From 884b560b6b44e8107565743bee688a2d5c93ff1c Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 7 Apr 2024 01:43:07 +0200 Subject: [PATCH 01/42] =?UTF-8?q?feat(json-crdt):=20=F0=9F=8E=B8=20improve?= =?UTF-8?q?=20LocalHistoryCrud=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/history/LocalHistoryCrud.ts | 14 +++++--- .../__tests__/LocalHistoryCrud.spec.ts | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/json-crdt/history/LocalHistoryCrud.ts b/src/json-crdt/history/LocalHistoryCrud.ts index b19ba39140..67d1048e24 100644 --- a/src/json-crdt/history/LocalHistoryCrud.ts +++ b/src/json-crdt/history/LocalHistoryCrud.ts @@ -30,9 +30,8 @@ export class LocalHistoryCrud implements LocalHistory { protected readonly locks: Locks, ) {} - public async create(collection: string[], log: Log): Promise<{id: string}> { + public async create(collection: string[], log: Log, id: string = genId()): Promise<{id: string}> { const blob = this.encode(log); - const id = genId(); await this.lock(collection, id, async () => { await this.crud.put([...collection, id], STATE_FILE_NAME, blob, {throwIf: 'exists'}); }); @@ -54,12 +53,17 @@ export class LocalHistoryCrud implements LocalHistory { const {frontier} = this.decoder.decode(blob, {format: 'seq.cbor', frontier: true}); return { log: frontier!, - cursor: '', + cursor: '1', }; } - public readHistory(collection: string[], id: string, cursor: string): Promise<{log: Log; cursor: string}> { - throw new Error('Method not implemented.'); + public async readHistory(collection: string[], id: string, cursor: string): Promise<{log: Log; cursor: string}> { + const blob = await this.crud.get([...collection, id], STATE_FILE_NAME); + const {history} = this.decoder.decode(blob, {format: 'seq.cbor', history: true}); + return { + log: history!, + cursor: '', + }; } public async update(collection: string[], id: string, patches: Patch[]): Promise { diff --git a/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts b/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts index cf6443657d..4faaa1adc1 100644 --- a/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts +++ b/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts @@ -60,3 +60,37 @@ test('can delete a document', async () => { expect((err as Error).message).toBe(`Collection /test/${id} does not exist`); } }); + +test('can update document', async () => { + const {local} = await setup(); + const model = Model.withLogicalClock(); + model.api.root({ + foo: 'spam', + }); + const log = Log.fromNewModel(model); + const {id} = await local.create(['test'], log); + const {log: log2} = await local.read(['test'], id); + log2.end.api.obj([]).set({ + bar: 'eggs', + }); + const patch = log2.end.api.flush(); + await local.update(['test'], id, [patch]); + const {log: log3} = await local.read(['test'], id); + expect(log3.end.view()).toStrictEqual({ + foo: 'spam', + bar: 'eggs', + }); +}); + +test('can delete document', async () => { + const {local} = await setup(); + const model = Model.withLogicalClock(); + model.api.root({ + foo: 'spam', + }); + const log = Log.fromNewModel(model); + const {id} = await local.create(['test'], log); + await local.read(['test'], id); + await local.delete(['test'], id); + expect(() => local.read(['test'], id)).rejects.toThrow(`Collection /test/${id} does not exist`); +}); From 339826f854b1db83c50400198e8c06422b2010d9 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 7 Apr 2024 14:53:40 +0200 Subject: [PATCH 02/42] =?UTF-8?q?chore(json-crdt):=20=F0=9F=A4=96=20change?= =?UTF-8?q?=20SessionHistory=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/history/SessionHistory.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/json-crdt/history/SessionHistory.ts b/src/json-crdt/history/SessionHistory.ts index 16622973ba..759ae9a507 100644 --- a/src/json-crdt/history/SessionHistory.ts +++ b/src/json-crdt/history/SessionHistory.ts @@ -5,6 +5,7 @@ import {ValNode} from '../nodes'; import {toSchema} from '../schema/toSchema'; import {Log} from '../log/Log'; import {RedoItem, UndoItem, UndoRedoStack} from './UndoRedoStack'; +import type {LocalHistory} from './types'; class Undo implements UndoItem { constructor(public readonly undo: () => Redo) {} @@ -15,7 +16,11 @@ class Redo implements RedoItem { } export class SessionHistory { - constructor(public readonly log: Log) {} + constructor( + public readonly collection: string[], + public readonly id: string, + protected readonly local: LocalHistory, + ) {} private readonly __onPatchRace = createRace(); From 0dd6756c3abfe5a19607fc9376284c01a675188d Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 7 Apr 2024 19:31:17 +0200 Subject: [PATCH 03/42] =?UTF-8?q?refactor(json-crdt):=20=F0=9F=92=A1=20mov?= =?UTF-8?q?e=20repo=20code=20to=20its=20own=20subfolder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LocalHistoryCrud.ts | 12 +-- src/json-crdt-repo/SessionHistory.ts | 78 +++++++++++++++++++ .../UndoRedoStack.ts | 0 .../__tests__/LocalHistoryCrud.spec.ts | 4 +- src/json-crdt-repo/remote/types.ts | 38 +++++++++ .../history => json-crdt-repo}/types.ts | 6 +- src/json-crdt/history/SessionHistory.ts | 77 ------------------ 7 files changed, 127 insertions(+), 88 deletions(-) rename src/{json-crdt/history => json-crdt-repo}/LocalHistoryCrud.ts (89%) create mode 100644 src/json-crdt-repo/SessionHistory.ts rename src/{json-crdt/history => json-crdt-repo}/UndoRedoStack.ts (100%) rename src/{json-crdt/history => json-crdt-repo}/__tests__/LocalHistoryCrud.spec.ts (96%) create mode 100644 src/json-crdt-repo/remote/types.ts rename src/{json-crdt/history => json-crdt-repo}/types.ts (91%) delete mode 100644 src/json-crdt/history/SessionHistory.ts diff --git a/src/json-crdt/history/LocalHistoryCrud.ts b/src/json-crdt-repo/LocalHistoryCrud.ts similarity index 89% rename from src/json-crdt/history/LocalHistoryCrud.ts rename to src/json-crdt-repo/LocalHistoryCrud.ts index 67d1048e24..1b0a6359c6 100644 --- a/src/json-crdt/history/LocalHistoryCrud.ts +++ b/src/json-crdt-repo/LocalHistoryCrud.ts @@ -1,11 +1,11 @@ -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; -import {CborDecoder} from '../../json-pack/cbor/CborDecoder'; -import {LogEncoder} from '../log/codec/LogEncoder'; -import {LogDecoder} from '../log/codec/LogDecoder'; +import {CborEncoder} from '../json-pack/cbor/CborEncoder'; +import {CborDecoder} from '../json-pack/cbor/CborDecoder'; +import {LogEncoder} from '../json-crdt/log/codec/LogEncoder'; +import {LogDecoder} from '../json-crdt/log/codec/LogDecoder'; import type {CrudApi} from 'memfs/lib/crud/types'; import type {Locks} from 'thingies/es2020/Locks'; -import type {Patch} from '../../json-crdt-patch'; -import type {Log} from '../log/Log'; +import type {Patch} from '../json-crdt-patch'; +import type {Log} from '../json-crdt/log/Log'; import type {LocalHistory} from './types'; export const genId = (octets: number = 8): string => { diff --git a/src/json-crdt-repo/SessionHistory.ts b/src/json-crdt-repo/SessionHistory.ts new file mode 100644 index 0000000000..6bbc7ef660 --- /dev/null +++ b/src/json-crdt-repo/SessionHistory.ts @@ -0,0 +1,78 @@ +import {createRace} from 'thingies/es2020/createRace'; +import {FanOutUnsubscribe} from 'thingies/es2020/fanout'; +import {InsValOp, Patch} from '../json-crdt-patch'; +import {ValNode} from '../json-crdt/nodes'; +import {toSchema} from '../json-crdt/schema/toSchema'; +import {Log} from '../json-crdt/log/Log'; +import {RedoItem, UndoItem, UndoRedoStack} from './UndoRedoStack'; +import type {LocalHistory} from './types'; + +class Undo implements UndoItem { + constructor(public readonly undo: () => Redo) {} +} + +class Redo implements RedoItem { + constructor(public readonly redo: () => Undo) {} +} + +export class SessionHistory { + constructor( + public readonly collection: string[], + public readonly id: string, + protected readonly local: LocalHistory, + ) {} + + private readonly __onPatchRace = createRace(); + + public attachUndoRedo(stack: UndoRedoStack): FanOutUnsubscribe { + // const onBeforePatch = (patch: Patch) => { + // this.__onPatchRace(() => { + // const undo = this.createUndo(patch); + // stack.push(undo); + // }); + // }; + // const unsubscribe = this.log.end.api.onBeforePatch.listen(onBeforePatch); + // return unsubscribe; + throw new Error('Method not implemented.'); + } + + public createUndo(patch: Patch): Undo { + const undoTasks: Array<() => void> = []; + const ops = patch.ops; + const length = ops.length; + for (let i = length - 1; i >= 0; i--) { + const op = ops[i]; + switch (op.name()) { + case 'ins_val': { + // const insOp = op as InsValOp; + // const valNode = this.log.end.index.get(insOp.obj); + // if (!(valNode instanceof ValNode)) throw new Error('INVALID_NODE'); + // const copy = toSchema(valNode.node()); + // const valNodeId = valNode.id; + // const task = () => { + // const end = this.log.end; + // const valNode = end.index.get(valNodeId); + // if (!valNode) return; + // end.api.wrap(valNode).asVal().set(copy); + // }; + // undoTasks.push(task); + } + } + } + const undo = new Undo(() => { + this.__onPatchRace(() => { + for (const task of undoTasks) task(); + }); + return new Redo(() => { + const undo = this.__onPatchRace(() => { + // // TODO: This line needs to be changed: + // const redoPatch = Patch.fromBinary(patch.toBinary()); + // this.log.end.api.builder.patch = redoPatch; + // return this.createUndo(redoPatch); + }); + return undo!; + }); + }); + return undo; + } +} diff --git a/src/json-crdt/history/UndoRedoStack.ts b/src/json-crdt-repo/UndoRedoStack.ts similarity index 100% rename from src/json-crdt/history/UndoRedoStack.ts rename to src/json-crdt-repo/UndoRedoStack.ts diff --git a/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts b/src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts similarity index 96% rename from src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts rename to src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts index 4faaa1adc1..234b206db3 100644 --- a/src/json-crdt/history/__tests__/LocalHistoryCrud.spec.ts +++ b/src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts @@ -2,8 +2,8 @@ import {memfs} from 'memfs'; import {NodeCrud} from 'memfs/lib/node-to-crud'; import {Locks} from 'thingies/es2020/Locks'; import {LocalHistoryCrud} from '../LocalHistoryCrud'; -import {Log} from '../../log/Log'; -import {Model} from '../../model'; +import {Log} from '../../json-crdt/log/Log'; +import {Model} from '../../json-crdt/model'; const setup = async () => { const {fs, vol} = memfs(); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts new file mode 100644 index 0000000000..53c8404e10 --- /dev/null +++ b/src/json-crdt-repo/remote/types.ts @@ -0,0 +1,38 @@ +import type {Patch} from '../../json-crdt-patch'; +import type {Model} from '../../json-crdt/model'; + +/** + * A history of patches that have been applied to a model, stored on the + * "remote": (1) server; (2) content addressable storage; or (3) somewhere in a + * peer-to-peer network. + * + * Cases: + * + * - `RemoteHistoryServer` + * - `RemoteHistoryServerIdempotent` + * - `RemoteHistoryCAS` + * - `RemoteHistoryP2P` + */ +export interface RemoteHistory { + create(id: string, patches: Patch[], start?: Model): Promise; + + /** + * Load latest state of the model, and any unmerged "tip" of patches + * it might have. + * + * @todo Maybe `state` and `tip` should be serialized to JSON? + */ + loadLatest(id: string): Promise<[cursor: Cursor, state: Model]>; + + loadAfter(id: string, cursor: Cursor): Promise<[cursor: Cursor, tip: Patch[]]>; + + loadBefore(id: string, cursor: Cursor): Promise<[cursor: Cursor, state: Model, tip: Patch[]]>; + + apply(id: string, patches: Patch[]): Promise; + + /** + * Subscribe to the latest changes to the model. + * @param callback + */ + subscribe(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void; +} diff --git a/src/json-crdt/history/types.ts b/src/json-crdt-repo/types.ts similarity index 91% rename from src/json-crdt/history/types.ts rename to src/json-crdt-repo/types.ts index 59fcce0054..556859e1e0 100644 --- a/src/json-crdt/history/types.ts +++ b/src/json-crdt-repo/types.ts @@ -1,6 +1,6 @@ -import type {Patch} from '../../json-crdt-patch'; -import type {Log} from '../log/Log'; -import type {Model} from '../model'; +import type {Patch} from '../json-crdt-patch'; +import type {Log} from '../json-crdt/log/Log'; +import type {Model} from '../json-crdt/model'; /** * A history of patches that have been applied to a model, stored on the diff --git a/src/json-crdt/history/SessionHistory.ts b/src/json-crdt/history/SessionHistory.ts deleted file mode 100644 index 759ae9a507..0000000000 --- a/src/json-crdt/history/SessionHistory.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {createRace} from 'thingies/es2020/createRace'; -import {FanOutUnsubscribe} from 'thingies/es2020/fanout'; -import {InsValOp, Patch} from '../../json-crdt-patch'; -import {ValNode} from '../nodes'; -import {toSchema} from '../schema/toSchema'; -import {Log} from '../log/Log'; -import {RedoItem, UndoItem, UndoRedoStack} from './UndoRedoStack'; -import type {LocalHistory} from './types'; - -class Undo implements UndoItem { - constructor(public readonly undo: () => Redo) {} -} - -class Redo implements RedoItem { - constructor(public readonly redo: () => Undo) {} -} - -export class SessionHistory { - constructor( - public readonly collection: string[], - public readonly id: string, - protected readonly local: LocalHistory, - ) {} - - private readonly __onPatchRace = createRace(); - - public attachUndoRedo(stack: UndoRedoStack): FanOutUnsubscribe { - const onBeforePatch = (patch: Patch) => { - this.__onPatchRace(() => { - const undo = this.createUndo(patch); - stack.push(undo); - }); - }; - const unsubscribe = this.log.end.api.onBeforePatch.listen(onBeforePatch); - return unsubscribe; - } - - public createUndo(patch: Patch): Undo { - const undoTasks: Array<() => void> = []; - const ops = patch.ops; - const length = ops.length; - for (let i = length - 1; i >= 0; i--) { - const op = ops[i]; - switch (op.name()) { - case 'ins_val': { - const insOp = op as InsValOp; - const valNode = this.log.end.index.get(insOp.obj); - if (!(valNode instanceof ValNode)) throw new Error('INVALID_NODE'); - const copy = toSchema(valNode.node()); - const valNodeId = valNode.id; - const task = () => { - const end = this.log.end; - const valNode = end.index.get(valNodeId); - if (!valNode) return; - end.api.wrap(valNode).asVal().set(copy); - }; - undoTasks.push(task); - } - } - } - const undo = new Undo(() => { - this.__onPatchRace(() => { - for (const task of undoTasks) task(); - }); - return new Redo(() => { - const undo = this.__onPatchRace(() => { - // TODO: This line needs to be changed: - const redoPatch = Patch.fromBinary(patch.toBinary()); - this.log.end.api.builder.patch = redoPatch; - return this.createUndo(redoPatch); - }); - return undo!; - }); - }); - return undo; - } -} From da4e1b01ecf5c2aaee7b0e7c30479d5b42ef615e Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:18:37 +0200 Subject: [PATCH 04/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20cleanupt=20packag?= =?UTF-8?q?e.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 44e31bb21c..bafc80d089 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "json-joy", "version": "11.2.0", - "description": "", + "description": "Collection of libraries for building collaborative editing apps.", "author": { "name": "streamich", "url": "https://github.com/streamich" @@ -13,8 +13,53 @@ "engines": { "node": ">=10.0" }, + "keywords": [ + "collaborative", + "multiplayer", + "local-first", + "localfirst", + "crdt", + "rdt", + "ot", + "operational-transformation", + "replicated", + "sync", + "synchronization", + "distributed-state", + "rpc", + "reactive-rpc", + "marshaling", + "serializations", + "json-patch", + "json-binary", + "json-brand", + "json-cli", + "json-clone", + "json-crdt-patch", + "json-crdt-extensions", + "json-crdt-peritext-ui", + "json-crdt", + "json-equal", + "json-expression", + "json-hash", + "json-ot", + "json-pack", + "json-patch-multicore", + "json-patch-ot", + "json-patch", + "json-pointer", + "json-random", + "json-schema", + "json-size", + "json-stable", + "json-text", + "json-type", + "json-type-value", + "json-walk" + ], "main": "lib/index.js", - "module": "esm/index.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "bin": { "jj": "./bin/jj.js", "json-pack": "./bin/json-pack.js", @@ -65,7 +110,6 @@ "publish-coverage-and-typedocs": "yarn typedoc && yarn coverage && yarn build:pages && yarn deploy:pages", "jj": "ts-node src/json-cli/jj.ts" }, - "keywords": [], "peerDependencies": { "quill-delta": "^5", "rxjs": "7", @@ -176,8 +220,6 @@ "yjs": "13.6.9", "ywasm": "0.16.10" }, - "types": "lib/index.d.ts", - "typings": "lib/index.d.ts", "jest": { "verbose": true, "testEnvironmentOptions": { From 2b6bcf052e9283bcd3bf91157cfaad069245fe5c Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:19:02 +0200 Subject: [PATCH 05/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20cleanup=20main=20?= =?UTF-8?q?index.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index d4c02daad6..c09f80ac22 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,18 @@ /** - * json-joy is a collection of modules. Import from reach module directly. + * json-joy is a collection of sub-libraries. Import from reach sub-library directly: * * ```ts - * // In Nodejs - * import {something} from 'json-joy/es2020/'; - * - * // In browser * import {something} from 'json-joy/lib/'; * ``` */ + +export type * from './json-binary/types'; +export type * from './json-brand/types'; +export type * from './json-crdt'; +export type * from './json-crdt-patch'; +export type * from './json-crdt-extensions'; +export type * from './json-expression/types'; +export type * from './json-pack/types'; +export type * from './json-patch/types'; +export type * from './json-pointer/types'; +export type * from './json-schema/types'; From 826d2cff56e7ed2bae46a14b6aada3f5a795caef Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:29:18 +0200 Subject: [PATCH 06/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20update=20release=20s?= =?UTF-8?q?cript=20and=20funding=20info?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 4 ++-- package.json | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81b0cbb276..a3a75ce95f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: - run: yarn demo:json-pointer - run: yarn test:reactive-rpc - name: Semantic Release - uses: cycjimmy/semantic-release-action@v4 + run: npx semantic-release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/package.json b/package.json index bafc80d089..81365cdcda 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ "homepage": "https://github.com/streamich/json-joy", "repository": "streamich/json-joy", "license": "Apache-2.0", - "funding": "https://github.com/sponsors/streamich", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, "engines": { "node": ">=10.0" }, From 090ce9ffb15ea8ed0586a753d7e2b1c74767460d Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:34:21 +0200 Subject: [PATCH 07/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20update=20build=20art?= =?UTF-8?q?ifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build output now is only es2020, which should serve most browsers and modern Node.js versions. BREAKING CHANGE: ๐Ÿงจ Only es2020 artifacts are published. --- .github/workflows/pr.yml | 2 +- package.json | 7 ++----- tsconfig.json | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 03cf8a4495..fb53146a35 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -36,7 +36,7 @@ jobs: node-version: ${{ matrix.node-version }} cache: yarn - run: yarn install --frozen-lockfile - - run: yarn build + - run: yarn build:all - run: yarn lint - run: yarn prettier:check - run: yarn test:cli:pointer diff --git a/package.json b/package.json index 81365cdcda..1aab75666e 100644 --- a/package.json +++ b/package.json @@ -87,11 +87,8 @@ "lint:fix": "yarn tslint --fix", "tslint": "tslint 'src/**/*.{js,jsx,ts,tsx}' -t verbose --project .", "clean": "rimraf lib es6 es2019 es2020 esm typedocs coverage gh-pages yarn-error.log", - "build:cjs": "tsc --project tsconfig.build.json", - "build:es6": "tsc --project tsconfig.build.json --module commonjs --target es6 --outDir es6", - "build:es2020": "tsc --project tsconfig.build.json --module commonjs --target es2020 --outDir es2020", - "build:esm": "tsc --project tsconfig.build.json --module ESNext --target ESNEXT --outDir esm", - "build:all": "concurrently \"yarn build:cjs\" \"yarn build:es6\" \"yarn build:es2020\" \"yarn build:esm\"", + "build:es2020": "tsc --project tsconfig.build.json --module commonjs --target es2020 --outDir lib", + "build:all": "concurrently \"yarn build:es2020\"", "build": "yarn build:es2020", "jest": "node -r ts-node/register ./node_modules/.bin/jest", "test": "jest --maxWorkers 7", diff --git a/tsconfig.json b/tsconfig.json index 789e5a07aa..dd0384430b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "es6", "module": "commonjs", "moduleResolution": "Node", - "removeComments": true, + "removeComments": false, "noImplicitAny": true, "allowJs": false, "allowSyntheticDefaultImports": true, From 6172bbabae901e1830ee6b8f33dd0144480f52c2 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:38:43 +0200 Subject: [PATCH 08/42] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20use=20/lib?= =?UTF-8?q?=20folder=20to=20import=20sub-libraries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/jj.js | 2 +- bin/json-pack-test.js | 2 +- bin/json-pack.js | 2 +- bin/json-patch-test.js | 2 +- bin/json-patch.js | 2 +- bin/json-pointer-test.js | 2 +- bin/json-pointer.js | 2 +- bin/json-unpack.js | 2 +- package.json | 5 +---- src/json-crdt/__bench__/profiler/automerge-paper.js | 4 ++-- src/json-crdt/__bench__/profiler/serialization.js | 2 +- src/json-equal/README.md | 4 ++-- src/json-expression/README.md | 4 ++-- src/json-hash/README.md | 2 +- src/json-ot/__bench__/ot-string.compose-and-transform.js | 4 ++-- src/json-type/README.md | 2 +- src/util/base64/README.md | 8 ++++---- src/util/base64/__bench__/decode.js | 6 +++--- src/util/base64/__bench__/encode.js | 2 +- src/util/buffers/__bench__/bench.decodeUtf8.ts | 2 +- 20 files changed, 29 insertions(+), 32 deletions(-) diff --git a/bin/jj.js b/bin/jj.js index a731488d33..775b5e7822 100755 --- a/bin/jj.js +++ b/bin/jj.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/jj"); +require("../lib/json-cli/jj"); diff --git a/bin/json-pack-test.js b/bin/json-pack-test.js index 46ec116e9c..ff7b364f1c 100755 --- a/bin/json-pack-test.js +++ b/bin/json-pack-test.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-pack-test"); +require("../lib/json-cli/json-pack-test"); diff --git a/bin/json-pack.js b/bin/json-pack.js index 1bbc68ce44..de89dc8a8e 100755 --- a/bin/json-pack.js +++ b/bin/json-pack.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-pack"); +require("../lib/json-cli/json-pack"); diff --git a/bin/json-patch-test.js b/bin/json-patch-test.js index 21022ab68b..2ed448b7fd 100755 --- a/bin/json-patch-test.js +++ b/bin/json-patch-test.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-patch-test"); +require("../lib/json-cli/json-patch-test"); diff --git a/bin/json-patch.js b/bin/json-patch.js index 3ab7511833..47fdb2b77f 100755 --- a/bin/json-patch.js +++ b/bin/json-patch.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-patch"); +require("../lib/json-cli/json-patch"); diff --git a/bin/json-pointer-test.js b/bin/json-pointer-test.js index 684a802347..89db474ae2 100755 --- a/bin/json-pointer-test.js +++ b/bin/json-pointer-test.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-pointer-test"); +require("../lib/json-cli/json-pointer-test"); diff --git a/bin/json-pointer.js b/bin/json-pointer.js index 946d18e710..25faf60073 100755 --- a/bin/json-pointer.js +++ b/bin/json-pointer.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-pointer"); +require("../lib/json-cli/json-pointer"); diff --git a/bin/json-unpack.js b/bin/json-unpack.js index aad20bc983..5f9c2e8de4 100755 --- a/bin/json-unpack.js +++ b/bin/json-unpack.js @@ -1,4 +1,4 @@ #!/usr/bin/env node // eslint-disable-next-line import/no-unassigned-import -require("../es2020/json-cli/json-unpack"); +require("../lib/json-cli/json-unpack"); diff --git a/package.json b/package.json index 1aab75666e..dccf7ab964 100644 --- a/package.json +++ b/package.json @@ -75,10 +75,7 @@ }, "files": [ "LICENSE", - "lib/", - "es6/", - "es2020/", - "esm/" + "lib/" ], "scripts": { "prettier": "prettier --ignore-path .gitignore --write \"src/**/*.{ts,tsx,js,jsx}\"", diff --git a/src/json-crdt/__bench__/profiler/automerge-paper.js b/src/json-crdt/__bench__/profiler/automerge-paper.js index 9e2f4d8b5d..b530a7ce1c 100644 --- a/src/json-crdt/__bench__/profiler/automerge-paper.js +++ b/src/json-crdt/__bench__/profiler/automerge-paper.js @@ -4,8 +4,8 @@ */ const {traces} = require('../../data/editing-traces'); -const {StrNode} = require('../../../es2020/json-crdt/types/str/StrNode'); -const {ts} = require('../../../es2020/json-crdt-patch/clock/logical'); +const {StrNode} = require('../../../lib/json-crdt/types/str/StrNode'); +const {ts} = require('../../../lib/json-crdt-patch/clock/logical'); const patches = traces.get('automerge-paper').txns.map((txn) => txn.patches[0]); const length = patches.length; diff --git a/src/json-crdt/__bench__/profiler/serialization.js b/src/json-crdt/__bench__/profiler/serialization.js index b4c190af29..67c94d76ef 100644 --- a/src/json-crdt/__bench__/profiler/serialization.js +++ b/src/json-crdt/__bench__/profiler/serialization.js @@ -3,7 +3,7 @@ * node --prof-process isolate-0xnnnnnnnnnnnn-v8.log */ -const {Model} = require('../../../es2020/json-crdt'); +const {Model} = require('../../../lib/json-crdt'); const json = require('../../data/json6'); const doc1 = Model.withLogicalClock(); diff --git a/src/json-equal/README.md b/src/json-equal/README.md index 1467ea9324..d82945245b 100644 --- a/src/json-equal/README.md +++ b/src/json-equal/README.md @@ -15,7 +15,7 @@ This library contains the fastest JSON deep comparison algorithms. ### `deepEqual` ```ts -import {deepEqual} from 'json-joy/{lib,es6,esm,es2020}/json-equal/deepEqual'; +import {deepEqual} from 'json-joy/lib/json-equal/deepEqual'; deepEqual(a, b); // true/false ``` @@ -24,7 +24,7 @@ deepEqual(a, b); // true/false ### `$$deepEqual` ```ts -import {$$deepEqual} from 'json-joy/{lib,es6,esm,es2020}/json-equal/$$deepEqual'; +import {$$deepEqual} from 'json-joy/lib/json-equal/$$deepEqual'; const js = $$deepEqual(a); const fn = eval(js); diff --git a/src/json-expression/README.md b/src/json-expression/README.md index b2be1148d4..f8a292b3cd 100644 --- a/src/json-expression/README.md +++ b/src/json-expression/README.md @@ -73,7 +73,7 @@ an order of magnitude faster. Evaluating expression immediately as-is. ```ts -import {evaluate} from 'json-joy/{lib,es2020}/json-expression'; +import {evaluate} from 'json-joy/lib/json-expression'; const expression = ['+', 1, ['$', '/foo']]; const data = {foo: 2}; @@ -84,7 +84,7 @@ evaluate(expression, {data}); // 3 Pre-compiling expression to an optimized function. ```ts -import {JsonExpressionCodegen} from 'json-joy/{lib,es2020}/json-expression'; +import {JsonExpressionCodegen} from 'json-joy/lib/json-expression'; const expression = ['+', 1, ['$', '/foo']]; const codegen = new JsonExpressionCodegen({expression}); diff --git a/src/json-hash/README.md b/src/json-hash/README.md index 7dedd52826..fe423a3792 100644 --- a/src/json-hash/README.md +++ b/src/json-hash/README.md @@ -6,7 +6,7 @@ with different key orders will have the same hash. Implements DJB2 hash function. ```ts -import {hash} from 'json-joy/es2020/json-hash'; +import {hash} from 'json-joy/lib/json-hash'; const num1 = hash({ foo: 1, diff --git a/src/json-ot/__bench__/ot-string.compose-and-transform.js b/src/json-ot/__bench__/ot-string.compose-and-transform.js index e0c2ffe510..9bbeec0796 100644 --- a/src/json-ot/__bench__/ot-string.compose-and-transform.js +++ b/src/json-ot/__bench__/ot-string.compose-and-transform.js @@ -1,11 +1,11 @@ const Benchmark = require('benchmark'); -const {validate, apply, compose, transform} = require('../../../es2020/json-ot/types/ot-string'); +const {validate, apply, compose, transform} = require('../../../lib/json-ot/types/ot-string'); const { validate: validate2, apply: apply2, compose: compose2, transform: transform2, -} = require('../../../es2020/json-ot/types/ot-string-irreversible'); +} = require('../../../lib/json-ot/types/ot-string-irreversible'); const {type} = require('ot-text'); const {type: type2} = require('ot-text-unicode'); const {delta: d} = require('./util'); diff --git a/src/json-type/README.md b/src/json-type/README.md index a5fd23655d..b53310e669 100644 --- a/src/json-type/README.md +++ b/src/json-type/README.md @@ -3,7 +3,7 @@ Type builder for JSON and MessagePack. ```ts -import {t} from 'json-joy/es2020/json-type'; +import {t} from 'json-joy/lib/json-type'; t.String(); // { __t: 'str' } t.String({const: 'add'}); // { __t: 'str', const: 'add' } diff --git a/src/util/base64/README.md b/src/util/base64/README.md index 49e6ce371f..ba368cec0a 100644 --- a/src/util/base64/README.md +++ b/src/util/base64/README.md @@ -15,7 +15,7 @@ Use encoder compatible with Node's Buffer: ```ts -import {toBase64} from 'json-joy/{lib,es2020}/util/base64'; +import {toBase64} from 'json-joy/lib/util/base64'; toBase64(new Uint8Array([1, 2, 3])); ``` @@ -23,7 +23,7 @@ toBase64(new Uint8Array([1, 2, 3])); Create your custom encoder: ```ts -import {createToBase64} from 'json-joy/{lib,es2020}/util/base64'; +import {createToBase64} from 'json-joy/lib/util/base64'; const encode = createToBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_'); @@ -75,7 +75,7 @@ Fastest is json-joy/util/base64 fromBase64(str) Use decoder compatible with Node's Buffer: ```ts -import {toBase64, fromBase64} from 'json-joy/{lib,es2020}/util/base64'; +import {toBase64, fromBase64} from 'json-joy/lib/util/base64'; fromBase64(toBase64(new Uint8Array([1, 2, 3]))); ``` @@ -83,7 +83,7 @@ fromBase64(toBase64(new Uint8Array([1, 2, 3]))); Create your custom encoder: ```ts -import {createFromBase64} from 'json-joy/{lib,es2020}/util/base64'; +import {createFromBase64} from 'json-joy/lib/util/base64'; const decoder = createFromBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_'); diff --git a/src/util/base64/__bench__/decode.js b/src/util/base64/__bench__/decode.js index 14bce78288..9b6de6dfa0 100644 --- a/src/util/base64/__bench__/decode.js +++ b/src/util/base64/__bench__/decode.js @@ -1,7 +1,7 @@ const Benchmark = require('benchmark'); -const toBase64 = require('../../../../es2020/util/base64').toBase64; -const {bufferToUint8Array} = require('../../../../es2020/util/buffers/bufferToUint8Array'); -const {fromBase64, createFromBase64} = require('../../../../es2020/util/base64'); +const toBase64 = require('../../../../lib/util/base64').toBase64; +const {bufferToUint8Array} = require('../../../../lib/util/buffers/bufferToUint8Array'); +const {fromBase64, createFromBase64} = require('../../../../lib/util/base64'); const {toByteArray} = require('base64-js'); const {decode: decodeJsBase64} = require('js-base64'); diff --git a/src/util/base64/__bench__/encode.js b/src/util/base64/__bench__/encode.js index c89d2931a1..07fbd4edde 100644 --- a/src/util/base64/__bench__/encode.js +++ b/src/util/base64/__bench__/encode.js @@ -1,5 +1,5 @@ const Benchmark = require('benchmark'); -const {toBase64, createToBase64} = require('../../../../es2020/util/base64'); +const {toBase64, createToBase64} = require('../../../../lib/util/base64'); const {fromByteArray} = require('base64-js'); const {encode: encodeJsBase64} = require('js-base64'); diff --git a/src/util/buffers/__bench__/bench.decodeUtf8.ts b/src/util/buffers/__bench__/bench.decodeUtf8.ts index 1255b2a3d8..0baee0ff6d 100644 --- a/src/util/buffers/__bench__/bench.decodeUtf8.ts +++ b/src/util/buffers/__bench__/bench.decodeUtf8.ts @@ -12,7 +12,7 @@ const prepare = (str: string) => { const runner = (v: number, name: string) => ({ name: `${name} (v${v})`, setup: () => { - const decode = require('../../../../es2020/util/buffers/utf8/decodeUtf8/v' + v).default; + const decode = require('../../../../lib/util/buffers/utf8/decodeUtf8/v' + v).default; return (data: any) => decode(data, 0, data.length); }, }); From 9dac25c3bf0a6be7ae582a7f2a388a0a4f965569 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:39:40 +0200 Subject: [PATCH 09/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20build=20PRs=20which?= =?UTF-8?q?=20target=20"next"=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index fb53146a35..f4c1ef4ef7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,7 +2,7 @@ name: Node.js CI on: pull_request: - branches: [ master ] + branches: [ master, next ] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} From 0d93f764790d5fc58adf8f5d547761e8df2f5b0b Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:45:47 +0200 Subject: [PATCH 10/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20allow=20"next"=20bra?= =?UTF-8?q?nch=20merging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3a75ce95f..6a42c14244 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Node.js CI on: push: - branches: [ master ] + branches: [ master, next ] jobs: release: From 789befdbb614c2570ebc037eae8896ea5ff8bffe Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 8 Apr 2024 10:53:37 +0200 Subject: [PATCH 11/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20use=20GitHub=20actio?= =?UTF-8?q?n=20for=20releasing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a42c14244..3e96b382d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: - run: yarn demo:json-pointer - run: yarn test:reactive-rpc - name: Semantic Release - run: npx semantic-release + uses: cycjimmy/semantic-release-action@v4 env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 07fb569e5bf5a37726f61482743ed9a8107645e2 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 8 Apr 2024 13:25:04 +0200 Subject: [PATCH 12/42] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20upgrade=20TypeScri?= =?UTF-8?q?pt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: ๐Ÿงจ Bump major --- package.json | 18 +- src/json-type-value/ObjectValue.ts | 2 +- src/json-type/type/index.ts | 1 + yarn.lock | 512 +++++++++++++++++------------ 4 files changed, 319 insertions(+), 214 deletions(-) diff --git a/package.json b/package.json index dccf7ab964..48f37e25bf 100644 --- a/package.json +++ b/package.json @@ -135,9 +135,9 @@ "@shelacek/ubjson": "^1.1.1", "@types/benchmark": "^2.1.2", "@types/jest": "^29.5.12", - "@types/quill": "^2.0.10", - "@types/react": "^18.2.7", - "@types/react-dom": "^18.2.4", + "@types/quill": "^2.0.14", + "@types/react": "^18.2.74", + "@types/react-dom": "^18.2.24", "ajv": "^8.11.0", "app-root-path": "^3.1.0", "axios": "^1.3.5", @@ -206,14 +206,14 @@ "tslib": "^2.6.2", "tslint": "^6.1.3", "tslint-config-common": "^1.6.2", - "typedoc": "^0.25.12", - "typescript": "^5.3.3", + "typedoc": "^0.25.13", + "typescript": "^5.4.4", "uWebSockets.js": "uNetworking/uWebSockets.js#v20.23.0", - "webpack": "^5.84.1", - "webpack-cli": "^5.1.1", - "webpack-dev-server": "^4.15.0", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4", "websocket": "^1.0.34", - "ws": "^8.14.2", + "ws": "^8.16.0", "yjs": "13.6.9", "ywasm": "0.16.10" }, diff --git a/src/json-type-value/ObjectValue.ts b/src/json-type-value/ObjectValue.ts index eb5f585286..85127538b5 100644 --- a/src/json-type-value/ObjectValue.ts +++ b/src/json-type-value/ObjectValue.ts @@ -71,7 +71,7 @@ export class ObjectValue> extends Value { const system = type.system; if (!system) throw new Error('NO_SYSTEM'); const extendedType = system.t.Object(...type.fields, field); - return new ObjectValue(extendedType, extendedData) as any; + return new ObjectValue(extendedType, extendedData as any) as any; } public prop( diff --git a/src/json-type/type/index.ts b/src/json-type/type/index.ts index 54fa1baa1e..f51a0d84b1 100644 --- a/src/json-type/type/index.ts +++ b/src/json-type/type/index.ts @@ -1,4 +1,5 @@ export * from './types'; +export * from './classes'; import {TypeBuilder} from './TypeBuilder'; diff --git a/yarn.lock b/yarn.lock index 2faa458875..52025ca36a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -662,6 +662,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -864,17 +872,17 @@ "@types/connect" "*" "@types/node" "*" -"@types/bonjour@^3.5.9": +"@types/bonjour@^3.5.13": version "3.5.13" resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" -"@types/connect-history-api-fallback@^1.3.5": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz#7793aa2160cef7db0ce5fe2b8aab621200f1a470" - integrity sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA== +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" @@ -902,7 +910,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -917,7 +925,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.13": +"@types/express@*", "@types/express@^4.17.21": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -1037,7 +1045,7 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8" integrity sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw== -"@types/quill@^2.0.10": +"@types/quill@^2.0.14": version "2.0.14" resolved "https://registry.yarnpkg.com/@types/quill/-/quill-2.0.14.tgz#78309020cccd6d9569bbba5733f4329a4ae76b47" integrity sha512-zvoXCRnc2Dl8g+7/9VSAmRWPN6oH+MVhTPizmCR+GJCITplZ5VRVzMs4+a/nOE3yzNwEZqylJJrMB07bwbM1/g== @@ -1050,14 +1058,14 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^18.2.4": - version "18.2.15" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.15.tgz#921af67f9ee023ac37ea84b1bc0cc40b898ea522" - integrity sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg== +"@types/react-dom@^18.2.24": + version "18.2.24" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.24.tgz#8dda8f449ae436a7a6e91efed8035d4ab03ff759" + integrity sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.2.7": +"@types/react@*": version "18.2.37" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.37.tgz#0f03af69e463c0f19a356c2660dbca5d19c44cae" integrity sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw== @@ -1066,10 +1074,18 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/react@^18.2.74": + version "18.2.74" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.74.tgz#2d52eb80e4e7c4ea8812c89181d6d590b53f958c" + integrity sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/scheduler@*": version "0.16.6" @@ -1089,14 +1105,14 @@ "@types/mime" "^1" "@types/node" "*" -"@types/serve-index@^1.9.1": +"@types/serve-index@^1.9.4": version "1.9.4" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.13.10": +"@types/serve-static@*": version "1.15.5" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== @@ -1105,7 +1121,16 @@ "@types/mime" "*" "@types/node" "*" -"@types/sockjs@^0.3.33": +"@types/serve-static@^1.15.5": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.36": version "0.3.36" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== @@ -1117,10 +1142,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== -"@types/ws@^8.5.5": - version "8.5.9" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c" - integrity sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg== +"@types/ws@^8.5.10": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" @@ -1136,10 +1161,10 @@ dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: "@webassemblyjs/helper-numbers" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" @@ -1154,10 +1179,10 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== "@webassemblyjs/helper-numbers@1.11.6": version "1.11.6" @@ -1173,15 +1198,15 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/ieee754@1.11.6": version "1.11.6" @@ -1202,59 +1227,59 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-api-error" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^2.1.1": @@ -1445,11 +1470,6 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1613,13 +1633,11 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -bonjour-service@^1.0.11: - version "1.1.1" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.1.tgz#960948fa0e0153f5d26743ab15baf8e33752c135" - integrity sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg== +bonjour-service@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== dependencies: - array-flatten "^2.1.2" - dns-equal "^1.0.0" fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" @@ -1650,7 +1668,17 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.14.5, browserslist@^4.21.9: +browserslist@^4.21.10: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +browserslist@^4.21.9: version "4.22.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== @@ -1704,6 +1732,13 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -1751,6 +1786,11 @@ caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz#aa68a64188903e98f36eb9c56e48fba0c1fe2a32" integrity sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw== +caniuse-lite@^1.0.30001587: + version "1.0.30001607" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz#b91e8e033f6bca4e13d3d45388d87fa88931d9a5" + integrity sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w== + cbor-extract@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.1.1.tgz#f154b31529fdb6b7c70fb3ca448f44eda96a1b42" @@ -1839,6 +1879,21 @@ chokidar@^3.5.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -2147,6 +2202,19 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -2163,10 +2231,10 @@ define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== delayed-stream@~1.0.0: version "1.0.0" @@ -2213,11 +2281,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - dns-packet@^5.2.2: version "5.6.1" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" @@ -2289,6 +2352,11 @@ electron-to-chromium@^1.4.535: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.588.tgz#d553f3c008e73488fb181fdf2601fdb0b1ffbb78" integrity sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w== +electron-to-chromium@^1.4.668: + version "1.4.729" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz#8477d21e2a50993781950885b2731d92ad532c00" + integrity sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA== + email-addresses@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-5.0.0.tgz#7ae9e7f58eef7d5e3e2c2c2d3ea49b78dc854fa6" @@ -2321,7 +2389,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: +enhanced-resolve@^5.0.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -2329,6 +2397,14 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" + integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -2901,7 +2977,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -2960,10 +3036,10 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" - integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== +html-entities@^2.4.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== html-escaper@^2.0.0: version "2.0.2" @@ -3169,7 +3245,7 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: +ipaddr.js@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== @@ -3193,10 +3269,10 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" @@ -3220,6 +3296,18 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-network-error@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3247,12 +3335,12 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== dependencies: - is-docker "^2.0.0" + is-inside-container "^1.0.0" isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" @@ -3809,7 +3897,7 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -launch-editor@^2.6.0: +launch-editor@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== @@ -3990,14 +4078,14 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.4.1, memfs@^3.4.3: +memfs@^3.4.1: version "3.6.0" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== dependencies: fs-monkey "^1.0.4" -memfs@^4.8.1: +memfs@^4.6.0, memfs@^4.8.1: version "4.8.1" resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.8.1.tgz#1e02c15c4397212a9a1b037fa4324c6f7dd45b47" integrity sha512-7q/AdPzf2WpwPlPL4v1kE2KsJsHl7EF4+hAeVzlyanr2+YnR21NVn9mDqo+7DEaKDRsQy8nvxPlKH4WqMtiO0w== @@ -4259,6 +4347,11 @@ node-releases@^2.0.13: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + nodemon@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.1.tgz#affe822a2c5f21354466b2fc8ae83277d27dadc7" @@ -4321,7 +4414,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -4347,14 +4440,15 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^8.0.9: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== +open@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" ot-text-unicode@^4.0.0: version "4.0.0" @@ -4389,12 +4483,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== dependencies: - "@types/retry" "0.12.0" + "@types/retry" "0.12.2" + is-network-error "^1.0.0" retry "^0.13.1" p-try@^2.0.0: @@ -4824,14 +4919,7 @@ retry@^0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rimraf@^5.0.0: +rimraf@^5.0.0, rimraf@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.5.tgz#9be65d2d6e683447d2e9013da2bf451139a61ccf" integrity sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A== @@ -4850,6 +4938,11 @@ rtl-css-js@^1.14.0: dependencies: "@babel/runtime" "^7.1.2" +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + rxjs@^7.5.5, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" @@ -4900,7 +4993,7 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: +schema-utils@^4.0.0, schema-utils@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== @@ -4920,7 +5013,7 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: +selfsigned@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== @@ -5325,18 +5418,18 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.10.0, terser@^5.16.8: +terser@^5.10.0: version "5.24.0" resolved "https://registry.yarnpkg.com/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364" integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw== @@ -5346,6 +5439,16 @@ terser@^5.10.0, terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" +terser@^5.26.0: + version "5.30.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2" + integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -5553,20 +5656,20 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedoc@^0.25.12: - version "0.25.12" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.12.tgz#f73f0a8d3731d418cc604d4230f95a857799e27a" - integrity sha512-F+qhkK2VoTweDXd1c42GS/By2DvI2uDF4/EpG424dTexSHdtCH52C6IcAvMA6jR3DzAWZjHpUOW+E02kyPNUNw== +typedoc@^0.25.13: + version "0.25.13" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.13.tgz#9a98819e3b2d155a6d78589b46fa4c03768f0922" + integrity sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ== dependencies: lunr "^2.3.9" marked "^4.3.0" minimatch "^9.0.3" shiki "^0.14.7" -typescript@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" - integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== +typescript@^5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.4.tgz#eb2471e7b0a5f1377523700a21669dce30c2d952" + integrity sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw== uWebSockets.js@uNetworking/uWebSockets.js#v20.23.0: version "20.23.0" @@ -5690,10 +5793,10 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -5705,7 +5808,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@^5.1.1: +webpack-cli@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== @@ -5724,52 +5827,53 @@ webpack-cli@^5.1.1: rechoir "^0.8.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== +webpack-dev-middleware@^7.1.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz#2af00538b6e4eda05f5afdd5d711dbebc05958f7" + integrity sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA== dependencies: colorette "^2.0.10" - memfs "^3.4.3" + memfs "^4.6.0" mime-types "^2.1.31" + on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.15.0: - version "4.15.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" - integrity sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.5" +webpack-dev-server@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz#cb6ea47ff796b9251ec49a94f24a425e12e3c9b8" + integrity sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" + bonjour-service "^1.2.1" + chokidar "^3.6.0" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^2.0.0" default-gateway "^6.0.3" express "^4.17.3" graceful-fs "^4.2.6" - html-entities "^2.3.2" + html-entities "^2.4.0" http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + rimraf "^5.0.5" + schema-utils "^4.2.0" + selfsigned "^2.4.1" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.13.0" + webpack-dev-middleware "^7.1.0" + ws "^8.16.0" webpack-merge@^5.7.3: version "5.10.0" @@ -5785,34 +5889,34 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.84.1: - version "5.89.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" - integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== +webpack@^5.91.0: + version "5.91.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.16.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -5884,10 +5988,10 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.13.0, ws@^8.14.2: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== +ws@^8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== y18n@^5.0.5: version "5.0.8" From bed8a4a51f424e5ead73aa4e5fdf855eeb760de0 Mon Sep 17 00:00:00 2001 From: streamich Date: Tue, 9 Apr 2024 19:04:43 +0200 Subject: [PATCH 13/42] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20use=20published=20?= =?UTF-8?q?json-pack=20and=20base64=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/index.ts | 5 +- src/json-binary/README.md | 18 - src/json-binary/__tests__/automated.spec.ts | 12 - src/json-binary/__tests__/stringify.spec.ts | 24 - src/json-binary/codec.ts | 124 ---- src/json-binary/constants.ts | 4 - src/json-binary/index.ts | 4 +- src/json-binary/types.ts | 20 - src/json-brand/README.md | 12 - src/json-brand/global.d.ts | 7 - src/json-brand/index.d.ts | 10 - src/json-brand/index.ts | 6 +- src/json-brand/types.ts | 9 - src/json-cli/json-pack.ts | 6 +- src/json-cli/json-unpack.ts | 4 +- src/json-clone/cloneBinary.ts | 2 +- src/json-crdt-patch/PatchBuilder.ts | 2 +- .../__demos__/PatchBuilder-operations.ts | 2 +- src/json-crdt-patch/codec/binary/Decoder.ts | 2 +- src/json-crdt-patch/codec/binary/Encoder.ts | 2 +- .../codec/compact-binary/decode.ts | 2 +- .../codec/compact-binary/encode.ts | 2 +- src/json-crdt-patch/codec/compact/decode.ts | 2 +- src/json-crdt-patch/codec/compact/encode.ts | 2 +- src/json-crdt-patch/codec/verbose/decode.ts | 2 +- src/json-crdt-patch/codec/verbose/encode.ts | 2 +- src/json-crdt-patch/util/binary/CrdtReader.ts | 2 +- src/json-crdt-patch/util/binary/CrdtWriter.ts | 2 +- .../__bench__/util/concurrent-trace.ts | 4 +- src/json-crdt/__bench__/util/fuzzer-traces.ts | 4 +- src/json-crdt/codec/indexed/binary/Decoder.ts | 2 +- src/json-crdt/codec/indexed/binary/Encoder.ts | 2 +- src/json-crdt/codec/sidecar/binary/Decoder.ts | 2 +- src/json-crdt/codec/sidecar/binary/Encoder.ts | 4 +- .../sidecar/binary/__tests__/Encoder.spec.ts | 2 +- .../__tests__/all-types-smoketest.spec.ts | 2 +- .../__tests__/automated-logical.spec.ts | 2 +- .../binary/__tests__/fuzzer-logical.spec.ts | 2 +- .../codec/structural/binary/Decoder.ts | 2 +- .../codec/structural/binary/Encoder.ts | 2 +- .../codec/structural/binary/ViewDecoder.ts | 2 +- .../structural/compact-binary/Decoder.ts | 2 +- .../structural/compact-binary/Encoder.ts | 2 +- .../codec/structural/verbose/Decoder.ts | 2 +- .../codec/structural/verbose/Encoder.ts | 2 +- src/json-crdt/history/LocalHistoryCrud.ts | 4 +- src/json-crdt/log/codec/LogDecoder.ts | 4 +- src/json-crdt/log/codec/LogEncoder.ts | 4 +- .../log/codec/__tests__/LogEncoder.spec.ts | 4 +- src/json-crdt/log/codec/logDecoderOpts.ts | 4 +- src/json-crdt/log/codec/logEncoderOpts.ts | 6 +- src/json-crdt/nodes/rga/AbstractRga.ts | 4 +- .../schema/__tests__/toSchema.spec.ts | 2 +- src/json-equal/$$deepEqual/v1.ts | 2 +- src/json-expression/codegen.ts | 4 +- src/json-hash/index.ts | 2 +- src/json-pack/JsonPackExtension.ts | 14 - src/json-pack/JsonPackValue.ts | 13 - src/json-pack/README.md | 426 ----------- .../__bench__/bench.bson.encoding.ts | 49 -- .../__bench__/bench.cbor-dag.encoding.ts | 57 -- .../__bench__/bench.cbor.decoding.ts | 59 -- .../__bench__/bench.cbor.encoding.ts | 58 -- src/json-pack/__bench__/bench.encoders.ts | 84 --- .../__bench__/bench.encoding.cbor.ts | 77 -- src/json-pack/__bench__/bench.encoding.ts | 139 ---- src/json-pack/__bench__/bench.ion.encoding.ts | 48 -- .../__bench__/bench.json.decoding.ts | 47 -- .../__bench__/bench.json.encoding.ts | 53 -- .../__bench__/bench.msgpack.decoding.ts | 82 --- .../__bench__/bench.msgpack.encoding.ts | 73 -- .../__bench__/bench.resp.decoding.ts | 70 -- .../__bench__/bench.resp.encoding.ts | 52 -- src/json-pack/__bench__/bench.shallow-read.ts | 118 --- src/json-pack/__bench__/bench.slice.ts | 36 - .../__bench__/bench.ubjson.decoding.ts | 44 -- .../__bench__/bench.ubjson.encoding.ts | 37 - src/json-pack/__bench__/bench.writer-size.ts | 71 -- .../__bench__/profiler/cbor-decoding.ts | 20 - src/json-pack/__bench__/profiler/slices.ts | 69 -- src/json-pack/__bench__/profiler/time.ts | 31 - src/json-pack/__bench__/profiler/truncate.ts | 37 - src/json-pack/__demos__/cbor.ts | 63 -- src/json-pack/__demos__/json.ts | 38 - src/json-pack/__demos__/msgpack.ts | 45 -- src/json-pack/__demos__/ubjson.ts | 38 - src/json-pack/bencode/BencodeDecoder.ts | 151 ---- src/json-pack/bencode/BencodeEncoder.ts | 164 ----- src/json-pack/bencode/README.md | 22 - .../bencode/__tests__/BencodeDecoder.spec.ts | 271 ------- .../bencode/__tests__/BencodeEncoder.spec.ts | 205 ------ .../bencode/__tests__/automated.spec.ts | 42 -- src/json-pack/bencode/types.ts | 1 - src/json-pack/bson/BsonEncoder.ts | 381 ---------- src/json-pack/bson/README.md | 56 -- .../bson/__tests__/BsonEncoder-values.spec.ts | 154 ---- .../bson/__tests__/BsonEncoder.spec.ts | 258 ------- src/json-pack/bson/__tests__/codec.spec.ts | 23 - src/json-pack/bson/index.ts | 2 - src/json-pack/bson/values.ts | 63 -- src/json-pack/cbor/CborDecoder.ts | 408 ----------- src/json-pack/cbor/CborDecoderBase.ts | 348 --------- src/json-pack/cbor/CborDecoderDag.ts | 9 - src/json-pack/cbor/CborEncoder.ts | 71 -- src/json-pack/cbor/CborEncoderDag.ts | 18 - src/json-pack/cbor/CborEncoderFast.ts | 333 --------- src/json-pack/cbor/CborEncoderStable.ts | 68 -- src/json-pack/cbor/README.md | 490 ------------- .../__tests__/CborDecoder.readLevel.spec.ts | 75 -- .../CborDecoder.shallow-reading.spec.ts | 183 ----- .../cbor/__tests__/CborDecoder.spec.ts | 408 ----------- .../__tests__/CborDecoder.validate.spec.ts | 55 -- .../cbor/__tests__/CborDecoderDag.spec.ts | 55 -- .../cbor/__tests__/CborEncoder.spec.ts | 413 ----------- .../cbor/__tests__/CborEncoderDag.spec.ts | 115 --- .../cbor/__tests__/CborEncoderStable.spec.ts | 227 ------ .../cbor/__tests__/cbor-js-testcases.ts | 101 --- src/json-pack/cbor/__tests__/cbor-js.spec.ts | 45 -- src/json-pack/cbor/__tests__/codec.spec.ts | 72 -- src/json-pack/cbor/__tests__/fuzzing.spec.ts | 50 -- .../shallow-read.genShallowRead.spec.ts | 132 ---- src/json-pack/cbor/constants.ts | 42 -- src/json-pack/cbor/shared.ts | 11 - src/json-pack/cbor/types.ts | 1 - src/json-pack/codecs/Codecs.ts | 16 - src/json-pack/codecs/cbor.ts | 17 - src/json-pack/codecs/json.ts | 17 - src/json-pack/codecs/msgpack.ts | 17 - src/json-pack/codecs/types.ts | 9 - src/json-pack/constants.ts | 5 - src/json-pack/ion/Import.ts | 49 -- src/json-pack/ion/IonEncoderFast.ts | 234 ------ src/json-pack/ion/README.md | 59 -- src/json-pack/ion/__tests__/Import.spec.ts | 47 -- .../ion/__tests__/IonEncoder.spec.ts | 212 ------ src/json-pack/ion/__tests__/automated.spec.ts | 15 - src/json-pack/ion/ast.ts | 185 ----- src/json-pack/ion/constants.ts | 25 - src/json-pack/ion/symbols.ts | 16 - src/json-pack/ion/types.ts | 1 - src/json-pack/json/JsonDecoder.ts | 683 ------------------ src/json-pack/json/JsonDecoderDag.ts | 133 ---- src/json-pack/json/JsonEncoder.ts | 282 -------- src/json-pack/json/JsonEncoderDag.ts | 59 -- src/json-pack/json/JsonEncoderStable.ts | 23 - src/json-pack/json/README.md | 117 --- .../json/__tests__/JsonDecoder.spec.ts | 440 ----------- .../json/__tests__/JsonDecoderDag.spec.ts | 72 -- .../json/__tests__/JsonEncoder.spec.ts | 182 ----- .../json/__tests__/JsonEncoderDag.spec.ts | 63 -- .../json/__tests__/automated.spec.ts | 39 - src/json-pack/json/__tests__/fuzzer.spec.ts | 34 - .../json/__tests__/memory-leaks.spec.ts | 37 - src/json-pack/json/types.ts | 1 - src/json-pack/json/util.ts | 13 - src/json-pack/msgpack/MsgPackDecoder.ts | 256 ------- src/json-pack/msgpack/MsgPackDecoderFast.ts | 181 ----- src/json-pack/msgpack/MsgPackEncoder.ts | 39 - src/json-pack/msgpack/MsgPackEncoderFast.ts | 324 --------- src/json-pack/msgpack/MsgPackEncoderStable.ts | 18 - .../msgpack/MsgPackToJsonConverter.ts | 241 ------ src/json-pack/msgpack/README.md | 143 ---- .../MsgPackDecoder.one-level.spec.ts | 311 -------- .../MsgPackDecoder.shallow-reading.spec.ts | 172 ----- .../__tests__/MsgPackDecoder.validate.spec.ts | 25 - .../__tests__/MsgPackDecoderFast.spec.ts | 301 -------- .../__tests__/MsgPackEncoder.codec.spec.ts | 89 --- .../msgpack/__tests__/MsgPackEncoder.spec.ts | 68 -- .../MsgPackEncoderFast.overwrite.spec.ts | 16 - .../__tests__/MsgPackEncoderFast.spec.ts | 293 -------- .../__tests__/MsgPackEncoderStable.spec.ts | 27 - .../__tests__/MsgPackToJsonConverter.spec.ts | 15 - src/json-pack/msgpack/__tests__/codec.spec.ts | 15 - .../msgpack/__tests__/decode.spec.ts | 239 ------ .../msgpack/__tests__/fuzzing.spec.ts | 19 - .../msgpack/__tests__/numbers.spec.ts | 80 -- .../shallow-read.genShallowRead.spec.ts | 130 ---- src/json-pack/msgpack/constants.ts | 6 - src/json-pack/msgpack/index.ts | 38 - src/json-pack/msgpack/shallow-read.ts | 113 --- src/json-pack/msgpack/types.ts | 15 - src/json-pack/msgpack/util.ts | 14 - src/json-pack/resp/README.md | 1 - src/json-pack/resp/RespDecoder.ts | 422 ----------- src/json-pack/resp/RespEncoder.ts | 503 ------------- src/json-pack/resp/RespEncoderLegacy.ts | 96 --- src/json-pack/resp/RespStreamingDecoder.ts | 114 --- .../resp/__tests__/RespDecoder.spec.ts | 235 ------ .../resp/__tests__/RespEncoder.spec.ts | 355 --------- .../resp/__tests__/RespEncoderLegacy.spec.ts | 52 -- .../__tests__/RespStreamingDecoder.spec.ts | 77 -- src/json-pack/resp/__tests__/codec.spec.ts | 43 -- src/json-pack/resp/__tests__/fuzzing.spec.ts | 17 - src/json-pack/resp/__tests__/skipping.spec.ts | 41 -- src/json-pack/resp/constants.ts | 27 - src/json-pack/resp/extensions.ts | 19 - src/json-pack/resp/index.ts | 6 - src/json-pack/types.ts | 59 -- src/json-pack/ubjson/README.md | 184 ----- src/json-pack/ubjson/UbjsonDecoder.ts | 124 ---- src/json-pack/ubjson/UbjsonEncoder.ts | 231 ------ .../ubjson/__tests__/UbjsonDecoder.spec.ts | 198 ----- .../ubjson/__tests__/UbjsonEncoder.spec.ts | 183 ----- .../ubjson/__tests__/automated.spec.ts | 35 - src/json-pack/ubjson/__tests__/fuzzer.spec.ts | 41 -- src/json-pack/util/CompressionTable.ts | 167 ----- src/json-pack/util/DecompressionTable.ts | 89 --- .../util/__tests__/CompressionTable.spec.ts | 69 -- .../util/__tests__/DecompressionTable.spec.ts | 41 -- src/json-pack/util/objKeyCmp.ts | 5 - src/json-patch/codec/binary/Decoder.ts | 4 +- src/json-patch/codec/binary/Encoder.ts | 2 +- .../binary/__tests__/message-pack.spec.ts | 2 +- src/json-patch/op/AbstractOp.ts | 2 +- src/json-patch/op/OpAdd.ts | 2 +- src/json-patch/op/OpAnd.ts | 2 +- src/json-patch/op/OpContains.ts | 2 +- src/json-patch/op/OpCopy.ts | 2 +- src/json-patch/op/OpDefined.ts | 2 +- src/json-patch/op/OpEnds.ts | 2 +- src/json-patch/op/OpExtend.ts | 2 +- src/json-patch/op/OpFlip.ts | 2 +- src/json-patch/op/OpIn.ts | 2 +- src/json-patch/op/OpInc.ts | 2 +- src/json-patch/op/OpLess.ts | 2 +- src/json-patch/op/OpMatches.ts | 2 +- src/json-patch/op/OpMerge.ts | 2 +- src/json-patch/op/OpMore.ts | 2 +- src/json-patch/op/OpMove.ts | 2 +- src/json-patch/op/OpNot.ts | 2 +- src/json-patch/op/OpOr.ts | 2 +- src/json-patch/op/OpRemove.ts | 2 +- src/json-patch/op/OpReplace.ts | 2 +- src/json-patch/op/OpSplit.ts | 2 +- src/json-patch/op/OpStarts.ts | 2 +- src/json-patch/op/OpStrDel.ts | 2 +- src/json-patch/op/OpStrIns.ts | 2 +- src/json-patch/op/OpTest.ts | 2 +- src/json-patch/op/OpTestString.ts | 2 +- src/json-patch/op/OpTestStringLen.ts | 2 +- src/json-patch/op/OpTestType.ts | 2 +- src/json-patch/op/OpType.ts | 2 +- src/json-patch/op/OpUndefined.ts | 2 +- src/json-random/RandomJson.ts | 2 +- src/json-size/__tests__/fuzz.spec.ts | 2 +- .../__tests__/msgpackSizeFast.spec.ts | 2 +- src/json-size/__tests__/testJsonSize.ts | 2 +- src/json-size/json.ts | 2 +- src/json-size/msgpackSizeFast.ts | 4 +- src/json-stable/index.ts | 2 +- src/json-type-cli/Cli.ts | 2 +- src/json-type-cli/codecs/cbor.ts | 6 +- src/json-type-cli/codecs/json.ts | 6 +- src/json-type-cli/codecs/json2.ts | 8 +- src/json-type-cli/codecs/json4.ts | 8 +- src/json-type-cli/codecs/msgpack.ts | 6 +- src/json-type-cli/codecs/ubjson.ts | 6 +- src/json-type-cli/defaultCodecs.ts | 2 +- src/json-type-cli/params/CliParamCmd.ts | 4 +- src/json-type-value/Value.ts | 2 +- src/json-type/__demos__/json-type.ts | 2 +- .../binary/BinaryEncoderCodegenContext.ts | 4 +- .../binary/CborEncoderCodegenContext.ts | 2 +- .../binary/JsonEncoderCodegenContext.ts | 2 +- .../MessagePackEncoderCodegenContext.ts | 2 +- .../CborEncoderCodegenContext.spec.ts | 8 +- .../JsonEncoderCodegenContext.spec.ts | 6 +- .../MessagePackEncoderCodegenContext.spec.ts | 8 +- .../json/JsonTextEncoderCodegenContext.ts | 4 +- src/json-type/codegen/types.ts | 2 +- src/json-type/type/classes/AbstractType.ts | 14 +- src/json-type/type/classes/AnyType.ts | 6 +- src/json-type/type/classes/ArrayType.ts | 4 +- src/json-type/type/classes/BinaryType.ts | 4 +- src/json-type/type/classes/BooleanType.ts | 4 +- src/json-type/type/classes/ConstType.ts | 4 +- src/json-type/type/classes/MapType.ts | 6 +- src/json-type/type/classes/NumberType.ts | 4 +- src/json-type/type/classes/ObjectType.ts | 6 +- src/json-type/type/classes/OrType.ts | 4 +- src/json-type/type/classes/RefType.ts | 6 +- src/json-type/type/classes/StringType.ts | 6 +- src/json-type/type/classes/TupleType.ts | 2 +- src/json-type/typescript/toText.ts | 2 +- .../e2e/uws/http/FetchRpcClient.spec.ts | 6 +- .../e2e/uws/http/StreamingRpcClient.spec.ts | 6 +- .../e2e/uws/ws/RpcPersistentClient.spec.ts | 6 +- .../browser/createBinaryWsRpcClient.ts | 4 +- .../browser/createJsonWsRpcClient.ts | 4 +- src/reactive-rpc/common/channel/channel.ts | 2 +- src/reactive-rpc/common/channel/mock.ts | 2 +- src/reactive-rpc/common/codec/RpcCodec.ts | 2 +- src/reactive-rpc/common/codec/RpcCodecs.ts | 2 +- .../codec/binary/BinaryRpcMessageCodec.ts | 4 +- .../__tests__/BinaryRpcMessageCodec.spec.ts | 4 +- .../codec/binary/__tests__/automated.spec.ts | 4 +- .../codec/binary/__tests__/decode.spec.ts | 8 +- .../codec/binary/__tests__/encode.spec.ts | 4 +- .../binary/__tests__/smoke-tests.spec.ts | 4 +- .../common/codec/binary/decode.ts | 4 +- .../codec/compact/CompactRpcMessageCodec.ts | 8 +- .../__tests__/CompactRpcMessageCodec.spec.ts | 4 +- .../codec/compact/__tests__/automated.spec.ts | 4 +- .../compact/__tests__/smoke-tests.spec.ts | 4 +- .../json-rpc-2/JsonRpc2RpcMessageCodec.ts | 8 +- .../json-rpc-2/__tests__/automated.spec.ts | 4 +- src/reactive-rpc/common/codec/types.ts | 2 +- src/reactive-rpc/common/messages/messages.ts | 10 +- src/reactive-rpc/common/messages/types.ts | 2 +- .../rpc/__tests__/RpcPersistentClient.spec.ts | 4 +- .../error/__tests__/RpcErrorType.spec.ts | 4 +- .../rpc/client/EncodedStaticRpcClient.ts | 2 +- .../common/rpc/client/FetchRpcClient.ts | 2 +- .../common/testing/buildE2eClient.ts | 4 +- src/reactive-rpc/server/context.ts | 6 +- src/reactive-rpc/server/http1/Http1Server.ts | 4 +- src/reactive-rpc/server/http1/context.ts | 4 +- src/reactive-rpc/server/uws/RpcApp.ts | 8 +- .../server/ws/codec/WsFrameDecoder.ts | 2 +- .../server/ws/codec/WsFrameEncoder.ts | 4 +- .../server/ws/server/WsServerConnection.ts | 4 +- .../__tests__/WsServerConnection.spec.ts | 4 +- src/util/base64/README.md | 91 --- src/util/base64/__bench__/decode.js | 77 -- src/util/base64/__bench__/encode.js | 98 --- .../base64/__tests__/decode-base64url.spec.ts | 20 - src/util/base64/__tests__/decode-bin.spec.ts | 31 - src/util/base64/__tests__/decode.spec.ts | 33 - .../base64/__tests__/encode-base64url.spec.ts | 23 - src/util/base64/__tests__/encode-bin.spec.ts | 38 - src/util/base64/__tests__/encode.spec.ts | 24 - src/util/base64/constants.ts | 2 - src/util/base64/createFromBase64.ts | 66 -- src/util/base64/createFromBase64Bin.ts | 73 -- src/util/base64/createToBase64.ts | 45 -- src/util/base64/createToBase64Bin.ts | 58 -- src/util/base64/createToBase64BinUint8.ts | 56 -- src/util/base64/fromBase64.ts | 10 - src/util/base64/fromBase64Bin.ts | 3 - src/util/base64/fromBase64Url.ts | 3 - src/util/base64/index.ts | 7 - src/util/base64/toBase64.ts | 12 - src/util/base64/toBase64Bin.ts | 3 - src/util/base64/toBase64Url.ts | 3 - src/util/buffers/Reader.ts | 105 --- src/util/buffers/Slice.ts | 12 - src/util/buffers/StreamingOctetReader.ts | 175 ----- src/util/buffers/StreamingReader.ts | 179 ----- src/util/buffers/Uint8ArrayCut.ts | 7 - src/util/buffers/Writer.ts | 269 ------- .../buffers/__bench__/bench.decodeUtf8.ts | 108 --- .../buffers/__tests__/StreamingReader.spec.ts | 57 -- src/util/buffers/__tests__/isFloat32.spec.ts | 9 - src/util/buffers/b.ts | 1 - src/util/buffers/bufferToUint8Array.ts | 1 - src/util/buffers/cmpUint8Array.ts | 6 - src/util/buffers/cmpUint8Array2.ts | 18 - src/util/buffers/cmpUint8Array3.ts | 19 - src/util/buffers/concat.ts | 31 - src/util/buffers/copy.ts | 5 - src/util/buffers/f16.ts | 16 - src/util/buffers/index.ts | 2 +- src/util/buffers/isArrayBuffer.ts | 3 - src/util/buffers/isFloat32.ts | 6 - src/util/buffers/isUint8Array.ts | 4 - src/util/buffers/printOctets.ts | 14 - src/util/buffers/strings.ts | 16 - src/util/buffers/toBuf.ts | 8 - src/util/buffers/toDataUri.ts | 7 - src/util/buffers/toUint8Array.ts | 10 - src/util/buffers/types.ts | 126 ---- src/util/buffers/utf8/CachedUtf8Decoder.ts | 54 -- .../buffers/utf8/__tests__/encode.spec.ts | 23 - .../buffers/utf8/__tests__/isUtf8.spec.ts | 54 -- src/util/buffers/utf8/decodeAscii.ts | 167 ----- src/util/buffers/utf8/decodeUtf8/README.md | 96 --- .../decodeUtf8/__tests__/decodeUtf8.spec.ts | 120 --- src/util/buffers/utf8/decodeUtf8/index.ts | 3 - src/util/buffers/utf8/decodeUtf8/v1.ts | 38 - src/util/buffers/utf8/decodeUtf8/v10.ts | 39 - src/util/buffers/utf8/decodeUtf8/v11.ts | 2 - src/util/buffers/utf8/decodeUtf8/v12.ts | 5 - src/util/buffers/utf8/decodeUtf8/v13.ts | 27 - src/util/buffers/utf8/decodeUtf8/v14.ts | 8 - src/util/buffers/utf8/decodeUtf8/v15.ts | 16 - src/util/buffers/utf8/decodeUtf8/v16.ts | 29 - src/util/buffers/utf8/decodeUtf8/v17.ts | 37 - src/util/buffers/utf8/decodeUtf8/v18.ts | 36 - src/util/buffers/utf8/decodeUtf8/v19.ts | 3 - src/util/buffers/utf8/decodeUtf8/v2.ts | 2 - src/util/buffers/utf8/decodeUtf8/v3.ts | 4 - src/util/buffers/utf8/decodeUtf8/v4.ts | 4 - src/util/buffers/utf8/decodeUtf8/v5.ts | 30 - src/util/buffers/utf8/decodeUtf8/v6.ts | 6 - src/util/buffers/utf8/decodeUtf8/v7.ts | 32 - src/util/buffers/utf8/decodeUtf8/v8.ts | 39 - src/util/buffers/utf8/decodeUtf8/v9.ts | 35 - src/util/buffers/utf8/encode.ts | 58 -- src/util/buffers/utf8/isUtf8.ts | 45 -- .../buffers/utf8/sharedCachedUtf8Decoder.ts | 3 - src/util/codegen/Codegen.ts | 296 -------- src/util/codegen/README.md | 17 - src/util/codegen/__tests__/Codegen.spec.ts | 31 - src/util/codegen/compile.ts | 7 - src/util/codegen/dynamicFunction.ts | 16 - src/util/codegen/index.ts | 4 +- src/util/codegen/switch.ts | 15 - src/util/codegen/types.ts | 38 - src/util/codegen/util/JsExpression.ts | 41 -- src/util/codegen/util/helpers.ts | 6 - src/util/codegen/util/normalizeAccessor.ts | 7 - src/util/router/codegen.ts | 4 +- src/util/router/router.ts | 2 +- src/util/router/tree.ts | 2 +- src/util/sort/insertion.ts | 22 +- src/util/sort/insertion2.ts | 23 +- src/util/strings/__tests__/asString.spec.ts | 33 - src/util/strings/__tests__/utf8.spec.ts | 26 - src/util/strings/__tests__/util.spec.ts | 54 -- src/util/strings/__tests__/wordWrap.spec.ts | 42 -- src/util/strings/asString.ts | 22 - src/util/strings/escape.ts | 134 ---- src/util/strings/flatstr.ts | 5 - src/util/strings/utf8.ts | 27 - src/util/strings/util.ts | 8 - src/util/strings/wordWrap.ts | 21 - src/web3/adl/hamt-crdt/Hamt.ts | 2 +- src/web3/adl/hamt-crdt/HamtFrame.ts | 2 +- src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts | 2 +- src/web3/codec/codecs/cbor.ts | 4 +- src/web3/codec/codecs/writer.ts | 2 +- src/web3/codec/types.ts | 2 +- src/web3/multiformats/Multihash.ts | 2 +- tsconfig.json | 1 - yarn.lock | 14 + 436 files changed, 299 insertions(+), 21718 deletions(-) delete mode 100644 src/json-binary/README.md delete mode 100644 src/json-binary/__tests__/automated.spec.ts delete mode 100644 src/json-binary/__tests__/stringify.spec.ts delete mode 100644 src/json-binary/codec.ts delete mode 100644 src/json-binary/constants.ts delete mode 100644 src/json-binary/types.ts delete mode 100644 src/json-brand/README.md delete mode 100644 src/json-brand/global.d.ts delete mode 100644 src/json-brand/index.d.ts delete mode 100644 src/json-brand/types.ts delete mode 100644 src/json-pack/JsonPackExtension.ts delete mode 100644 src/json-pack/JsonPackValue.ts delete mode 100644 src/json-pack/README.md delete mode 100644 src/json-pack/__bench__/bench.bson.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.cbor-dag.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.cbor.decoding.ts delete mode 100644 src/json-pack/__bench__/bench.cbor.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.encoders.ts delete mode 100644 src/json-pack/__bench__/bench.encoding.cbor.ts delete mode 100644 src/json-pack/__bench__/bench.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.ion.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.json.decoding.ts delete mode 100644 src/json-pack/__bench__/bench.json.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.msgpack.decoding.ts delete mode 100644 src/json-pack/__bench__/bench.msgpack.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.resp.decoding.ts delete mode 100644 src/json-pack/__bench__/bench.resp.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.shallow-read.ts delete mode 100644 src/json-pack/__bench__/bench.slice.ts delete mode 100644 src/json-pack/__bench__/bench.ubjson.decoding.ts delete mode 100644 src/json-pack/__bench__/bench.ubjson.encoding.ts delete mode 100644 src/json-pack/__bench__/bench.writer-size.ts delete mode 100644 src/json-pack/__bench__/profiler/cbor-decoding.ts delete mode 100644 src/json-pack/__bench__/profiler/slices.ts delete mode 100644 src/json-pack/__bench__/profiler/time.ts delete mode 100644 src/json-pack/__bench__/profiler/truncate.ts delete mode 100644 src/json-pack/__demos__/cbor.ts delete mode 100644 src/json-pack/__demos__/json.ts delete mode 100644 src/json-pack/__demos__/msgpack.ts delete mode 100644 src/json-pack/__demos__/ubjson.ts delete mode 100644 src/json-pack/bencode/BencodeDecoder.ts delete mode 100644 src/json-pack/bencode/BencodeEncoder.ts delete mode 100644 src/json-pack/bencode/README.md delete mode 100644 src/json-pack/bencode/__tests__/BencodeDecoder.spec.ts delete mode 100644 src/json-pack/bencode/__tests__/BencodeEncoder.spec.ts delete mode 100644 src/json-pack/bencode/__tests__/automated.spec.ts delete mode 100644 src/json-pack/bencode/types.ts delete mode 100644 src/json-pack/bson/BsonEncoder.ts delete mode 100644 src/json-pack/bson/README.md delete mode 100644 src/json-pack/bson/__tests__/BsonEncoder-values.spec.ts delete mode 100644 src/json-pack/bson/__tests__/BsonEncoder.spec.ts delete mode 100644 src/json-pack/bson/__tests__/codec.spec.ts delete mode 100644 src/json-pack/bson/index.ts delete mode 100644 src/json-pack/bson/values.ts delete mode 100644 src/json-pack/cbor/CborDecoder.ts delete mode 100644 src/json-pack/cbor/CborDecoderBase.ts delete mode 100644 src/json-pack/cbor/CborDecoderDag.ts delete mode 100644 src/json-pack/cbor/CborEncoder.ts delete mode 100644 src/json-pack/cbor/CborEncoderDag.ts delete mode 100644 src/json-pack/cbor/CborEncoderFast.ts delete mode 100644 src/json-pack/cbor/CborEncoderStable.ts delete mode 100644 src/json-pack/cbor/README.md delete mode 100644 src/json-pack/cbor/__tests__/CborDecoder.readLevel.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborDecoder.shallow-reading.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborDecoder.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborDecoder.validate.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborDecoderDag.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborEncoder.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborEncoderDag.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/CborEncoderStable.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/cbor-js-testcases.ts delete mode 100644 src/json-pack/cbor/__tests__/cbor-js.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/codec.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/fuzzing.spec.ts delete mode 100644 src/json-pack/cbor/__tests__/shallow-read.genShallowRead.spec.ts delete mode 100644 src/json-pack/cbor/constants.ts delete mode 100644 src/json-pack/cbor/shared.ts delete mode 100644 src/json-pack/cbor/types.ts delete mode 100644 src/json-pack/codecs/Codecs.ts delete mode 100644 src/json-pack/codecs/cbor.ts delete mode 100644 src/json-pack/codecs/json.ts delete mode 100644 src/json-pack/codecs/msgpack.ts delete mode 100644 src/json-pack/codecs/types.ts delete mode 100644 src/json-pack/constants.ts delete mode 100644 src/json-pack/ion/Import.ts delete mode 100644 src/json-pack/ion/IonEncoderFast.ts delete mode 100644 src/json-pack/ion/README.md delete mode 100644 src/json-pack/ion/__tests__/Import.spec.ts delete mode 100644 src/json-pack/ion/__tests__/IonEncoder.spec.ts delete mode 100644 src/json-pack/ion/__tests__/automated.spec.ts delete mode 100644 src/json-pack/ion/ast.ts delete mode 100644 src/json-pack/ion/constants.ts delete mode 100644 src/json-pack/ion/symbols.ts delete mode 100644 src/json-pack/ion/types.ts delete mode 100644 src/json-pack/json/JsonDecoder.ts delete mode 100644 src/json-pack/json/JsonDecoderDag.ts delete mode 100644 src/json-pack/json/JsonEncoder.ts delete mode 100644 src/json-pack/json/JsonEncoderDag.ts delete mode 100644 src/json-pack/json/JsonEncoderStable.ts delete mode 100644 src/json-pack/json/README.md delete mode 100644 src/json-pack/json/__tests__/JsonDecoder.spec.ts delete mode 100644 src/json-pack/json/__tests__/JsonDecoderDag.spec.ts delete mode 100644 src/json-pack/json/__tests__/JsonEncoder.spec.ts delete mode 100644 src/json-pack/json/__tests__/JsonEncoderDag.spec.ts delete mode 100644 src/json-pack/json/__tests__/automated.spec.ts delete mode 100644 src/json-pack/json/__tests__/fuzzer.spec.ts delete mode 100644 src/json-pack/json/__tests__/memory-leaks.spec.ts delete mode 100644 src/json-pack/json/types.ts delete mode 100644 src/json-pack/json/util.ts delete mode 100644 src/json-pack/msgpack/MsgPackDecoder.ts delete mode 100644 src/json-pack/msgpack/MsgPackDecoderFast.ts delete mode 100644 src/json-pack/msgpack/MsgPackEncoder.ts delete mode 100644 src/json-pack/msgpack/MsgPackEncoderFast.ts delete mode 100644 src/json-pack/msgpack/MsgPackEncoderStable.ts delete mode 100644 src/json-pack/msgpack/MsgPackToJsonConverter.ts delete mode 100644 src/json-pack/msgpack/README.md delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackDecoder.one-level.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackDecoder.shallow-reading.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackDecoder.validate.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackDecoderFast.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackEncoder.codec.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackEncoder.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackEncoderFast.overwrite.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackEncoderFast.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackEncoderStable.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/MsgPackToJsonConverter.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/codec.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/decode.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/fuzzing.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/numbers.spec.ts delete mode 100644 src/json-pack/msgpack/__tests__/shallow-read.genShallowRead.spec.ts delete mode 100644 src/json-pack/msgpack/constants.ts delete mode 100644 src/json-pack/msgpack/index.ts delete mode 100644 src/json-pack/msgpack/shallow-read.ts delete mode 100644 src/json-pack/msgpack/types.ts delete mode 100644 src/json-pack/msgpack/util.ts delete mode 100644 src/json-pack/resp/README.md delete mode 100644 src/json-pack/resp/RespDecoder.ts delete mode 100644 src/json-pack/resp/RespEncoder.ts delete mode 100644 src/json-pack/resp/RespEncoderLegacy.ts delete mode 100644 src/json-pack/resp/RespStreamingDecoder.ts delete mode 100644 src/json-pack/resp/__tests__/RespDecoder.spec.ts delete mode 100644 src/json-pack/resp/__tests__/RespEncoder.spec.ts delete mode 100644 src/json-pack/resp/__tests__/RespEncoderLegacy.spec.ts delete mode 100644 src/json-pack/resp/__tests__/RespStreamingDecoder.spec.ts delete mode 100644 src/json-pack/resp/__tests__/codec.spec.ts delete mode 100644 src/json-pack/resp/__tests__/fuzzing.spec.ts delete mode 100644 src/json-pack/resp/__tests__/skipping.spec.ts delete mode 100644 src/json-pack/resp/constants.ts delete mode 100644 src/json-pack/resp/extensions.ts delete mode 100644 src/json-pack/resp/index.ts delete mode 100644 src/json-pack/types.ts delete mode 100644 src/json-pack/ubjson/README.md delete mode 100644 src/json-pack/ubjson/UbjsonDecoder.ts delete mode 100644 src/json-pack/ubjson/UbjsonEncoder.ts delete mode 100644 src/json-pack/ubjson/__tests__/UbjsonDecoder.spec.ts delete mode 100644 src/json-pack/ubjson/__tests__/UbjsonEncoder.spec.ts delete mode 100644 src/json-pack/ubjson/__tests__/automated.spec.ts delete mode 100644 src/json-pack/ubjson/__tests__/fuzzer.spec.ts delete mode 100644 src/json-pack/util/CompressionTable.ts delete mode 100644 src/json-pack/util/DecompressionTable.ts delete mode 100644 src/json-pack/util/__tests__/CompressionTable.spec.ts delete mode 100644 src/json-pack/util/__tests__/DecompressionTable.spec.ts delete mode 100644 src/json-pack/util/objKeyCmp.ts delete mode 100644 src/util/base64/README.md delete mode 100644 src/util/base64/__bench__/decode.js delete mode 100644 src/util/base64/__bench__/encode.js delete mode 100644 src/util/base64/__tests__/decode-base64url.spec.ts delete mode 100644 src/util/base64/__tests__/decode-bin.spec.ts delete mode 100644 src/util/base64/__tests__/decode.spec.ts delete mode 100644 src/util/base64/__tests__/encode-base64url.spec.ts delete mode 100644 src/util/base64/__tests__/encode-bin.spec.ts delete mode 100644 src/util/base64/__tests__/encode.spec.ts delete mode 100644 src/util/base64/constants.ts delete mode 100644 src/util/base64/createFromBase64.ts delete mode 100644 src/util/base64/createFromBase64Bin.ts delete mode 100644 src/util/base64/createToBase64.ts delete mode 100644 src/util/base64/createToBase64Bin.ts delete mode 100644 src/util/base64/createToBase64BinUint8.ts delete mode 100644 src/util/base64/fromBase64.ts delete mode 100644 src/util/base64/fromBase64Bin.ts delete mode 100644 src/util/base64/fromBase64Url.ts delete mode 100644 src/util/base64/index.ts delete mode 100644 src/util/base64/toBase64.ts delete mode 100644 src/util/base64/toBase64Bin.ts delete mode 100644 src/util/base64/toBase64Url.ts delete mode 100644 src/util/buffers/Reader.ts delete mode 100644 src/util/buffers/Slice.ts delete mode 100644 src/util/buffers/StreamingOctetReader.ts delete mode 100644 src/util/buffers/StreamingReader.ts delete mode 100644 src/util/buffers/Uint8ArrayCut.ts delete mode 100644 src/util/buffers/Writer.ts delete mode 100644 src/util/buffers/__bench__/bench.decodeUtf8.ts delete mode 100644 src/util/buffers/__tests__/StreamingReader.spec.ts delete mode 100644 src/util/buffers/__tests__/isFloat32.spec.ts delete mode 100644 src/util/buffers/b.ts delete mode 100644 src/util/buffers/bufferToUint8Array.ts delete mode 100644 src/util/buffers/cmpUint8Array.ts delete mode 100644 src/util/buffers/cmpUint8Array2.ts delete mode 100644 src/util/buffers/cmpUint8Array3.ts delete mode 100644 src/util/buffers/concat.ts delete mode 100644 src/util/buffers/copy.ts delete mode 100644 src/util/buffers/f16.ts delete mode 100644 src/util/buffers/isArrayBuffer.ts delete mode 100644 src/util/buffers/isFloat32.ts delete mode 100644 src/util/buffers/isUint8Array.ts delete mode 100644 src/util/buffers/printOctets.ts delete mode 100644 src/util/buffers/strings.ts delete mode 100644 src/util/buffers/toBuf.ts delete mode 100644 src/util/buffers/toDataUri.ts delete mode 100644 src/util/buffers/toUint8Array.ts delete mode 100644 src/util/buffers/types.ts delete mode 100644 src/util/buffers/utf8/CachedUtf8Decoder.ts delete mode 100644 src/util/buffers/utf8/__tests__/encode.spec.ts delete mode 100644 src/util/buffers/utf8/__tests__/isUtf8.spec.ts delete mode 100644 src/util/buffers/utf8/decodeAscii.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/README.md delete mode 100644 src/util/buffers/utf8/decodeUtf8/__tests__/decodeUtf8.spec.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/index.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v1.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v10.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v11.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v12.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v13.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v14.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v15.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v16.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v17.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v18.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v19.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v2.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v3.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v4.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v5.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v6.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v7.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v8.ts delete mode 100644 src/util/buffers/utf8/decodeUtf8/v9.ts delete mode 100644 src/util/buffers/utf8/encode.ts delete mode 100644 src/util/buffers/utf8/isUtf8.ts delete mode 100644 src/util/buffers/utf8/sharedCachedUtf8Decoder.ts delete mode 100644 src/util/codegen/Codegen.ts delete mode 100644 src/util/codegen/README.md delete mode 100644 src/util/codegen/__tests__/Codegen.spec.ts delete mode 100644 src/util/codegen/compile.ts delete mode 100644 src/util/codegen/dynamicFunction.ts delete mode 100644 src/util/codegen/switch.ts delete mode 100644 src/util/codegen/types.ts delete mode 100644 src/util/codegen/util/JsExpression.ts delete mode 100644 src/util/codegen/util/helpers.ts delete mode 100644 src/util/codegen/util/normalizeAccessor.ts delete mode 100644 src/util/strings/__tests__/asString.spec.ts delete mode 100644 src/util/strings/__tests__/utf8.spec.ts delete mode 100644 src/util/strings/__tests__/util.spec.ts delete mode 100644 src/util/strings/__tests__/wordWrap.spec.ts delete mode 100644 src/util/strings/asString.ts delete mode 100644 src/util/strings/escape.ts delete mode 100644 src/util/strings/flatstr.ts delete mode 100644 src/util/strings/utf8.ts delete mode 100644 src/util/strings/util.ts delete mode 100644 src/util/strings/wordWrap.ts diff --git a/package.json b/package.json index 48f37e25bf..ef4d58c1df 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,8 @@ } }, "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/json-pack": "^1.0.1", "arg": "^5.0.2", "hyperdyperid": "^1.2.0", "multibase": "^4.0.6", diff --git a/src/index.ts b/src/index.ts index c09f80ac22..ccefde3bc6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,13 +6,12 @@ * ``` */ -export type * from './json-binary/types'; -export type * from './json-brand/types'; +export type * from '@jsonjoy.com/json-pack/lib/json-binary/types'; +export type * from '@jsonjoy.com/json-pack/lib/json-brand/types'; export type * from './json-crdt'; export type * from './json-crdt-patch'; export type * from './json-crdt-extensions'; export type * from './json-expression/types'; -export type * from './json-pack/types'; export type * from './json-patch/types'; export type * from './json-pointer/types'; export type * from './json-schema/types'; diff --git a/src/json-binary/README.md b/src/json-binary/README.md deleted file mode 100644 index edba4b14fc..0000000000 --- a/src/json-binary/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# json-binary - -A JSON serializer and parser which supports `Uint8Array` binary data. -Encodes binary data as Base64 encoded data URI strings. - -```ts -import * as JSONB from 'json-joy/lib/json-binary'; - -const data = { - foo: new Uint8Array([1, 2, 3]), -}; - -const json = JSONB.stringify(data); -// {"foo":"data:application/octet-stream;base64,AAECAw=="} - -const data2 = JSONB.parse(json); -// { foo: Uint8Array { 1, 2, 3 } } -``` diff --git a/src/json-binary/__tests__/automated.spec.ts b/src/json-binary/__tests__/automated.spec.ts deleted file mode 100644 index bd8f0a4e29..0000000000 --- a/src/json-binary/__tests__/automated.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {stringify, parse} from '..'; -import {documents} from '../../__tests__/json-documents'; -import {binaryDocuments} from '../../__tests__/binary-documents'; -import {msgPackDocuments} from '../../__tests__/msgpack-documents'; - -for (const document of [...documents, ...binaryDocuments, ...msgPackDocuments]) { - (document.only ? test.only : test)(document.name, () => { - const encoded = stringify(document.json); - const decoded = parse(encoded); - expect(decoded).toStrictEqual(document.json); - }); -} diff --git a/src/json-binary/__tests__/stringify.spec.ts b/src/json-binary/__tests__/stringify.spec.ts deleted file mode 100644 index f304981214..0000000000 --- a/src/json-binary/__tests__/stringify.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {stringify} from '..'; -import {binUriStart} from '../constants'; - -test('can stringify an empty buffer', () => { - const json = stringify(new Uint8Array(0)); - expect(json).toBe(`"${binUriStart}"`); -}); - -test('can stringify a short buffer', () => { - const json = stringify(new Uint8Array([0, 1, 2, 3])); - expect(json).toBe(`"${binUriStart}AAECAw=="`); -}); - -test('can stringify a short buffer in an object', () => { - const json = stringify({ - foo: new Uint8Array([0, 1, 2, 3]), - }); - expect(json).toBe(`{"foo":"${binUriStart}AAECAw=="}`); -}); - -test('can stringify a short buffer in an array', () => { - const json = stringify([null, 1, new Uint8Array([0, 1, 2, 3]), 'a']); - expect(json).toBe(`[null,1,"${binUriStart}AAECAw==","a"]`); -}); diff --git a/src/json-binary/codec.ts b/src/json-binary/codec.ts deleted file mode 100644 index 87df43276d..0000000000 --- a/src/json-binary/codec.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {JsonPackExtension, JsonPackValue} from '../json-pack/msgpack'; -import {fromBase64, toBase64} from '../util/base64'; -import {isUint8Array} from '../util/buffers/isUint8Array'; -import {binUriStart, msgPackExtStart, msgPackUriStart} from './constants'; -import {binary_string} from './types'; - -const binUriStartLength = binUriStart.length; -const msgPackUriStartLength = msgPackUriStart.length; -const msgPackExtStartLength = msgPackExtStart.length; -const minDataUri = Math.min(binUriStartLength, msgPackUriStartLength); - -const parseExtDataUri = (uri: string): JsonPackExtension => { - uri = uri.substring(msgPackExtStartLength); - const commaIndex = uri.indexOf(','); - if (commaIndex === -1) throw new Error('INVALID_EXT_DATA_URI'); - const typeString = uri.substring(0, commaIndex); - const buf = fromBase64(uri.substring(commaIndex + 1)); - return new JsonPackExtension(Number(typeString), buf); -}; - -/** - * Replaces strings with Uint8Arrays in-place. - */ -export const unwrapBinary = (value: unknown): unknown => { - if (!value) return value; - if (value instanceof Array) { - const len = value.length; - for (let i = 0; i < len; i++) { - const item = value[i]; - switch (typeof item) { - case 'object': { - unwrapBinary(item); - continue; - } - case 'string': { - if (item.length < minDataUri) continue; - if (item.substring(0, binUriStartLength) === binUriStart) - value[i] = fromBase64(item.substring(binUriStartLength)); - else if (item.substring(0, msgPackUriStartLength) === msgPackUriStart) - value[i] = new JsonPackValue(fromBase64(item.substring(msgPackUriStartLength))); - else if (item.substring(0, msgPackExtStartLength) === msgPackExtStart) value[i] = parseExtDataUri(item); - } - } - } - return value; - } - if (typeof value === 'object') { - for (const key in value) { - const item = (value as any)[key]; - switch (typeof item) { - case 'object': { - unwrapBinary(item); - continue; - } - case 'string': { - if (item.length < minDataUri) continue; - if (item.substring(0, binUriStartLength) === binUriStart) { - const buf = fromBase64(item.substring(binUriStartLength)); - (value as any)[key] = buf; - } else if (item.substring(0, msgPackUriStartLength) === msgPackUriStart) { - (value as any)[key] = new JsonPackValue(fromBase64(item.substring(msgPackUriStartLength))); - } else if (item.substring(0, msgPackExtStartLength) === msgPackExtStart) - (value as any)[key] = parseExtDataUri(item); - } - } - } - return value; - } - if (typeof value === 'string') { - if (value.length < minDataUri) return value; - if (value.substring(0, binUriStartLength) === binUriStart) return fromBase64(value.substring(binUriStartLength)); - if (value.substring(0, msgPackUriStartLength) === msgPackUriStart) - return new JsonPackValue(fromBase64(value.substring(msgPackUriStartLength))); - if (value.substring(0, msgPackExtStartLength) === msgPackExtStart) return parseExtDataUri(value); - else return value; - } - return value; -}; - -export const parse = (json: string): unknown => { - const parsed = JSON.parse(json); - return unwrapBinary(parsed); -}; - -export const stringifyBinary = (value: T): binary_string => - >(binUriStart + toBase64(value)); - -/** - * Replaces Uint8Arrays with strings, returns a new structure, - * without mutating the original. - */ -export const wrapBinary = (value: unknown): unknown => { - if (!value) return value; - if (isUint8Array(value)) return stringifyBinary(value); - if (value instanceof Array) { - const out: unknown[] = []; - const len = value.length; - for (let i = 0; i < len; i++) { - const item = value[i]; - out.push(!item || typeof item !== 'object' ? item : wrapBinary(item)); - } - return out; - } - if (value instanceof JsonPackValue) return msgPackUriStart + toBase64(value.val); - if (value instanceof JsonPackExtension) return msgPackExtStart + value.tag + ',' + toBase64(value.val); - if (typeof value === 'object') { - const out: {[key: string]: unknown} = {}; - for (const key in value) { - const item = (value as any)[key]; - out[key] = !item || typeof item !== 'object' ? item : wrapBinary(item); - } - return out; - } - return value; -}; - -type Stringify = - | ((value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number) => string) - | ((value: any, replacer?: (number | string)[] | null, space?: string | number) => string); - -export const stringify: Stringify = (value: unknown, replacer: any, space: any) => { - const wrapped = wrapBinary(value); - return JSON.stringify(wrapped, replacer, space); -}; diff --git a/src/json-binary/constants.ts b/src/json-binary/constants.ts deleted file mode 100644 index e5afe7a993..0000000000 --- a/src/json-binary/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const binUriStart = 'data:application/octet-stream;base64,'; -const msgPackUriHeader = 'data:application/msgpack;base64'; -export const msgPackUriStart = msgPackUriHeader + ','; -export const msgPackExtStart = msgPackUriHeader + ';ext='; diff --git a/src/json-binary/index.ts b/src/json-binary/index.ts index f36840daa2..4b173b2b67 100644 --- a/src/json-binary/index.ts +++ b/src/json-binary/index.ts @@ -1,3 +1 @@ -export * from './types'; -export * from './constants'; -export * from './codec'; +export * from '@jsonjoy.com/json-pack/lib/json-binary'; diff --git a/src/json-binary/types.ts b/src/json-binary/types.ts deleted file mode 100644 index 63877c7194..0000000000 --- a/src/json-binary/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type {MsgPack} from '../json-pack/msgpack'; -import type {CborUint8Array} from '../json-pack/cbor/types'; -import type {Brand} from '../util/types'; - -export type base64_string = Brand; -export type binary_string = Brand< - `data:application/octet-stream;base64,${base64_string}`, - T, - 'binary_string' ->; -export type cbor_string = Brand< - `data:application/cbor;base64,${base64_string>}`, - T, - 'cbor_string' ->; -export type msgpack_string = Brand< - `data:application/msgpack;base64,${base64_string>}`, - T, - 'msgpack_string' ->; diff --git a/src/json-brand/README.md b/src/json-brand/README.md deleted file mode 100644 index 1a99005394..0000000000 --- a/src/json-brand/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# json-brand - -TypeScript branded type for a JSON string. - -```ts -import {JSON, json} from 'json-joy/lib/json-brand'; - -const str = '{"hello": "world"}' as json<{hello: string}>; - -JSON.parse(str).hello; // OK -JSON.parse(str).foo; // Error: ... -``` diff --git a/src/json-brand/global.d.ts b/src/json-brand/global.d.ts deleted file mode 100644 index b9a6ec2575..0000000000 --- a/src/json-brand/global.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare global { - interface JSON { - parse(text: json, reviver?: (key: any, value: any) => any): T; - stringify(value: T, replacer?: (key: string, value: any) => any, space?: string | number): json; - stringify(value: T, replacer?: (number | string)[] | null, space?: string | number): json; - } -} diff --git a/src/json-brand/index.d.ts b/src/json-brand/index.d.ts deleted file mode 100644 index a6c51e2666..0000000000 --- a/src/json-brand/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type json = string & {__JSON__: T}; -export type json_string = json; - -export interface JSON { - parse(text: json, reviver?: (key: any, value: any) => any): T; - stringify(value: T, replacer?: (key: string, value: any) => any, space?: string | number): json; - stringify(value: T, replacer?: (number | string)[] | null, space?: string | number): json; -} - -export const JSON: JSON; diff --git a/src/json-brand/index.ts b/src/json-brand/index.ts index 2a85af098e..58890235f8 100644 --- a/src/json-brand/index.ts +++ b/src/json-brand/index.ts @@ -1,5 +1 @@ -import * as type from './types'; - -export const JSON = (typeof global !== 'undefined' ? global.JSON : window.JSON) as unknown as type.JSON; - -export type {json, json_string} from './types'; +export * from '@jsonjoy.com/json-pack/lib/json-brand'; diff --git a/src/json-brand/types.ts b/src/json-brand/types.ts deleted file mode 100644 index b3735b1ce4..0000000000 --- a/src/json-brand/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type json = json_string; - -export type json_string = string & {__BRAND__: 'JSON_STRING'; __TYPE__: T}; - -export interface JSON { - parse(text: json, reviver?: (key: any, value: any) => any): T; - stringify(value: T, replacer?: (key: string, value: any) => any, space?: string | number): json; - stringify(value: T, replacer?: (number | string)[] | null, space?: string | number): json; -} diff --git a/src/json-cli/json-pack.ts b/src/json-cli/json-pack.ts index d7ca1982e0..5283d112a4 100644 --- a/src/json-cli/json-pack.ts +++ b/src/json-cli/json-pack.ts @@ -1,7 +1,7 @@ import {readFileSync} from 'fs'; -import {MsgPackEncoder} from '../json-pack/msgpack'; -import {CborEncoder} from '../json-pack/cbor/CborEncoder'; -import * as JSONB from '../json-binary'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import * as JSONB from '@jsonjoy.com/json-pack/lib/json-binary'; import arg from 'arg'; const args = arg( diff --git a/src/json-cli/json-unpack.ts b/src/json-cli/json-unpack.ts index 1f493231c6..5cba8c0159 100644 --- a/src/json-cli/json-unpack.ts +++ b/src/json-cli/json-unpack.ts @@ -1,6 +1,6 @@ import {readFileSync} from 'fs'; -import {MsgPackDecoderFast} from '../json-pack/msgpack'; -import {CborDecoder} from '../json-pack/cbor/CborDecoder'; +import {MsgPackDecoderFast} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import * as JSONB from '../json-binary'; import arg from 'arg'; diff --git a/src/json-clone/cloneBinary.ts b/src/json-clone/cloneBinary.ts index 533f60a837..fc5b110ebd 100644 --- a/src/json-clone/cloneBinary.ts +++ b/src/json-clone/cloneBinary.ts @@ -1,4 +1,4 @@ -import {isUint8Array} from '../util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; const {isArray} = Array; const objectKeys = Object.keys; diff --git a/src/json-crdt-patch/PatchBuilder.ts b/src/json-crdt-patch/PatchBuilder.ts index 6da120f103..2116af5d00 100644 --- a/src/json-crdt-patch/PatchBuilder.ts +++ b/src/json-crdt-patch/PatchBuilder.ts @@ -16,7 +16,7 @@ import { NopOp, } from './operations'; import {IClock, ITimestampStruct, ITimespanStruct, ts, Timestamp} from './clock'; -import {isUint8Array} from '../util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; import {Patch} from './Patch'; import {ORIGIN} from './constants'; import {VectorDelayedValue} from './builder/Tuple'; diff --git a/src/json-crdt-patch/__demos__/PatchBuilder-operations.ts b/src/json-crdt-patch/__demos__/PatchBuilder-operations.ts index 9784ab6c1b..5e06bd8524 100644 --- a/src/json-crdt-patch/__demos__/PatchBuilder-operations.ts +++ b/src/json-crdt-patch/__demos__/PatchBuilder-operations.ts @@ -11,7 +11,7 @@ import {LogicalClock} from '../clock'; import * as verbose from '../codec/verbose'; import * as compact from '../codec/compact'; import * as binary from '../codec/binary'; -import * as cbor from '../../json-pack/cbor/shared'; +import * as cbor from '@jsonjoy.com/json-pack/lib/cbor/shared'; const clock = new LogicalClock(123, 456); const builder = new PatchBuilder(clock); diff --git a/src/json-crdt-patch/codec/binary/Decoder.ts b/src/json-crdt-patch/codec/binary/Decoder.ts index 7382fe65f2..e45b892e0b 100644 --- a/src/json-crdt-patch/codec/binary/Decoder.ts +++ b/src/json-crdt-patch/codec/binary/Decoder.ts @@ -3,7 +3,7 @@ import {interval, ITimespanStruct, ITimestampStruct, ClockVector, ServerClockVec import {Patch} from '../../Patch'; import {PatchBuilder} from '../../PatchBuilder'; import {SESSION} from '../../constants'; -import {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {JsonCrdtPatchOpcode} from '../../constants'; /** diff --git a/src/json-crdt-patch/codec/binary/Encoder.ts b/src/json-crdt-patch/codec/binary/Encoder.ts index 72106ddf23..f808f1b5a2 100644 --- a/src/json-crdt-patch/codec/binary/Encoder.ts +++ b/src/json-crdt-patch/codec/binary/Encoder.ts @@ -2,7 +2,7 @@ import * as operations from '../../operations'; import {JsonCrdtPatchOpcodeOverlay} from '../../constants'; import {CrdtWriter} from '../../util/binary/CrdtWriter'; import {ITimespanStruct, ITimestampStruct, Timestamp} from '../../clock'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import type {JsonCrdtPatchOperation, Patch} from '../../Patch'; /** diff --git a/src/json-crdt-patch/codec/compact-binary/decode.ts b/src/json-crdt-patch/codec/compact-binary/decode.ts index 547f66f3b9..a941ac4717 100644 --- a/src/json-crdt-patch/codec/compact-binary/decode.ts +++ b/src/json-crdt-patch/codec/compact-binary/decode.ts @@ -1,5 +1,5 @@ +import {decode as decodeJson, type CborUint8Array} from '@jsonjoy.com/json-pack/lib/cbor/shared'; import {decode as decodeCompact} from '../compact/decode'; -import {decode as decodeJson, type CborUint8Array} from '../../../json-pack/cbor/shared'; import type {Patch} from '../../Patch'; import type {CompactCodecPatch} from '../compact/types'; diff --git a/src/json-crdt-patch/codec/compact-binary/encode.ts b/src/json-crdt-patch/codec/compact-binary/encode.ts index 8c15e03fe6..cd7c072e9c 100644 --- a/src/json-crdt-patch/codec/compact-binary/encode.ts +++ b/src/json-crdt-patch/codec/compact-binary/encode.ts @@ -1,5 +1,5 @@ +import {encode as encodeJson, type CborUint8Array} from '@jsonjoy.com/json-pack/lib/cbor/shared'; import {encode as encodeCompact} from '../compact/encode'; -import {encode as encodeJson, type CborUint8Array} from '../../../json-pack/cbor/shared'; import type {Patch} from '../../Patch'; import type {CompactCodecPatch} from '../compact/types'; diff --git a/src/json-crdt-patch/codec/compact/decode.ts b/src/json-crdt-patch/codec/compact/decode.ts index 7c55674822..549622a08b 100644 --- a/src/json-crdt-patch/codec/compact/decode.ts +++ b/src/json-crdt-patch/codec/compact/decode.ts @@ -1,5 +1,5 @@ import {JsonCrdtPatchOpcode} from '../../constants'; -import {fromBase64} from '../../../util/base64/fromBase64'; +import {fromBase64} from '@jsonjoy.com/base64/lib/fromBase64'; import {ITimespanStruct, ITimestampStruct, ClockVector, ServerClockVector, Timespan, Timestamp} from '../../clock'; import {Patch} from '../../Patch'; import {PatchBuilder} from '../../PatchBuilder'; diff --git a/src/json-crdt-patch/codec/compact/encode.ts b/src/json-crdt-patch/codec/compact/encode.ts index 03486f6c40..2e9fee7409 100644 --- a/src/json-crdt-patch/codec/compact/encode.ts +++ b/src/json-crdt-patch/codec/compact/encode.ts @@ -2,7 +2,7 @@ import * as operations from '../../operations'; import {ITimespanStruct, ITimestampStruct, Timestamp} from '../../clock'; import {Patch} from '../../Patch'; import {JsonCrdtPatchOpcode, SESSION} from '../../constants'; -import {toBase64} from '../../../util/base64/toBase64'; +import {toBase64} from '@jsonjoy.com/base64/lib/toBase64'; import type * as types from './types'; const timestamp = (sid: number, ts: ITimestampStruct): types.CompactCodecTimestamp => { diff --git a/src/json-crdt-patch/codec/verbose/decode.ts b/src/json-crdt-patch/codec/verbose/decode.ts index 1d37d3d3a7..8d8d580b48 100644 --- a/src/json-crdt-patch/codec/verbose/decode.ts +++ b/src/json-crdt-patch/codec/verbose/decode.ts @@ -1,4 +1,4 @@ -import {fromBase64} from '../../../util/base64/fromBase64'; +import {fromBase64} from '@jsonjoy.com/base64/lib/fromBase64'; import {ts, ClockVector, ServerClockVector, tss, ITimestampStruct} from '../../clock'; import {SESSION} from '../../constants'; import {Patch} from '../../Patch'; diff --git a/src/json-crdt-patch/codec/verbose/encode.ts b/src/json-crdt-patch/codec/verbose/encode.ts index e169b813c8..877b3bff09 100644 --- a/src/json-crdt-patch/codec/verbose/encode.ts +++ b/src/json-crdt-patch/codec/verbose/encode.ts @@ -1,7 +1,7 @@ import * as operations from '../../operations'; import {Patch} from '../../Patch'; import {SESSION} from '../../constants'; -import {toBase64} from '../../../util/base64/toBase64'; +import {toBase64} from '@jsonjoy.com/base64/lib/toBase64'; import {ITimestampStruct, Timestamp} from '../../clock'; import type * as types from './types'; diff --git a/src/json-crdt-patch/util/binary/CrdtReader.ts b/src/json-crdt-patch/util/binary/CrdtReader.ts index 32a788df43..242a75e36c 100644 --- a/src/json-crdt-patch/util/binary/CrdtReader.ts +++ b/src/json-crdt-patch/util/binary/CrdtReader.ts @@ -1,4 +1,4 @@ -import {Reader} from '../../../util/buffers/Reader'; +import {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; /** @todo Rename file name. */ export class CrdtReader extends Reader { diff --git a/src/json-crdt-patch/util/binary/CrdtWriter.ts b/src/json-crdt-patch/util/binary/CrdtWriter.ts index a0476a608c..5c71546451 100644 --- a/src/json-crdt-patch/util/binary/CrdtWriter.ts +++ b/src/json-crdt-patch/util/binary/CrdtWriter.ts @@ -1,4 +1,4 @@ -import {Writer} from '../../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; export class CrdtWriter extends Writer { /** diff --git a/src/json-crdt/__bench__/util/concurrent-trace.ts b/src/json-crdt/__bench__/util/concurrent-trace.ts index 15f1fac4f7..333a950d3f 100644 --- a/src/json-crdt/__bench__/util/concurrent-trace.ts +++ b/src/json-crdt/__bench__/util/concurrent-trace.ts @@ -1,8 +1,8 @@ import * as path from 'path'; import * as fs from 'fs'; import {Patch} from '../../../json-crdt-patch'; -import {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; -import {JsonDecoder} from '../../../json-pack/json/JsonDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; export const loadConcurrentTrace = (traceName: string): [batch: Patch[], view: unknown] => { const root = path.resolve(__dirname, '..', '..', '..', '..'); diff --git a/src/json-crdt/__bench__/util/fuzzer-traces.ts b/src/json-crdt/__bench__/util/fuzzer-traces.ts index d0bc81b5c6..a359a1fd47 100644 --- a/src/json-crdt/__bench__/util/fuzzer-traces.ts +++ b/src/json-crdt/__bench__/util/fuzzer-traces.ts @@ -1,9 +1,9 @@ import * as path from 'path'; import * as fs from 'fs'; import {Patch} from '../../../json-crdt-patch'; -import {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Model} from '../../model'; -import {bufferToUint8Array} from '../../../util/buffers/bufferToUint8Array'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; export const loadFuzzerTrace = (traceName: string): [batch: Patch[], model: Model] => { const root = path.resolve(__dirname, '..', '..', '..', '..'); diff --git a/src/json-crdt/codec/indexed/binary/Decoder.ts b/src/json-crdt/codec/indexed/binary/Decoder.ts index 0d9fe1c01f..e419c411d1 100644 --- a/src/json-crdt/codec/indexed/binary/Decoder.ts +++ b/src/json-crdt/codec/indexed/binary/Decoder.ts @@ -4,7 +4,7 @@ import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtReader'; import {IndexedFields, FieldName, IndexedNodeFields} from './types'; import {ITimestampStruct, IClockVector, Timestamp, ClockVector} from '../../../../json-crdt-patch/clock'; import {Model, UNDEFINED} from '../../../model/Model'; -import {CborDecoderBase} from '../../../../json-pack/cbor/CborDecoderBase'; +import {CborDecoderBase} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import {CRDT_MAJOR} from '../../structural/binary/constants'; export class Decoder { diff --git a/src/json-crdt/codec/indexed/binary/Encoder.ts b/src/json-crdt/codec/indexed/binary/Encoder.ts index 01b158f1dd..3d3c5d55eb 100644 --- a/src/json-crdt/codec/indexed/binary/Encoder.ts +++ b/src/json-crdt/codec/indexed/binary/Encoder.ts @@ -1,7 +1,7 @@ import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; import {ClockTable} from '../../../../json-crdt-patch/codec/clock/ClockTable'; import {CrdtWriter} from '../../../../json-crdt-patch/util/binary/CrdtWriter'; -import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {Model} from '../../../model'; import * as nodes from '../../../nodes'; import {CRDT_MAJOR_OVERLAY} from '../../structural/binary/constants'; diff --git a/src/json-crdt/codec/sidecar/binary/Decoder.ts b/src/json-crdt/codec/sidecar/binary/Decoder.ts index 25b6e7689b..a7700e31cf 100644 --- a/src/json-crdt/codec/sidecar/binary/Decoder.ts +++ b/src/json-crdt/codec/sidecar/binary/Decoder.ts @@ -2,7 +2,7 @@ import {ClockDecoder} from '../../../../json-crdt-patch/codec/clock/ClockDecoder import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtReader'; import {ITimestampStruct} from '../../../../json-crdt-patch/clock'; import {Model, UNDEFINED} from '../../../model/Model'; -import {CborDecoderBase} from '../../../../json-pack/cbor/CborDecoderBase'; +import {CborDecoderBase} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import * as nodes from '../../../nodes'; import {CRDT_MAJOR} from '../../structural/binary/constants'; import {sort} from '../../../../util/sort/insertion'; diff --git a/src/json-crdt/codec/sidecar/binary/Encoder.ts b/src/json-crdt/codec/sidecar/binary/Encoder.ts index f937ae7ef5..3dd3afdb84 100644 --- a/src/json-crdt/codec/sidecar/binary/Encoder.ts +++ b/src/json-crdt/codec/sidecar/binary/Encoder.ts @@ -2,10 +2,10 @@ import * as nodes from '../../../nodes'; import {ClockEncoder} from '../../../../json-crdt-patch/codec/clock/ClockEncoder'; import {CrdtWriter} from '../../../../json-crdt-patch/util/binary/CrdtWriter'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; -import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {CRDT_MAJOR_OVERLAY} from '../../structural/binary/constants'; -import {sort} from '../../../../util/sort/insertion'; +import {sort} from '@jsonjoy.com/json-pack/lib/util/sort/insertion'; import {UNDEFINED} from '../../../model/Model'; import type {Model} from '../../../model'; diff --git a/src/json-crdt/codec/sidecar/binary/__tests__/Encoder.spec.ts b/src/json-crdt/codec/sidecar/binary/__tests__/Encoder.spec.ts index d04720cb45..c9c90a178d 100644 --- a/src/json-crdt/codec/sidecar/binary/__tests__/Encoder.spec.ts +++ b/src/json-crdt/codec/sidecar/binary/__tests__/Encoder.spec.ts @@ -1,5 +1,5 @@ import {s} from '../../../../../json-crdt-patch'; -import {CborDecoder} from '../../../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Model} from '../../../../model'; import {Encoder} from '../Encoder'; import {Decoder} from '../Decoder'; diff --git a/src/json-crdt/codec/sidecar/binary/__tests__/all-types-smoketest.spec.ts b/src/json-crdt/codec/sidecar/binary/__tests__/all-types-smoketest.spec.ts index f679991c9d..26be210bb6 100644 --- a/src/json-crdt/codec/sidecar/binary/__tests__/all-types-smoketest.spec.ts +++ b/src/json-crdt/codec/sidecar/binary/__tests__/all-types-smoketest.spec.ts @@ -2,7 +2,7 @@ import {Model} from '../../../../model'; import {runCodecAllTypesSmokeTests} from '../../../structural/verbose/__tests__/runCodecAllTypesSmokeTests'; import {Encoder} from '../Encoder'; import {Decoder} from '../Decoder'; -import {CborDecoder} from '../../../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; const encoder = new Encoder(); const decoder = new Decoder(); diff --git a/src/json-crdt/codec/sidecar/binary/__tests__/automated-logical.spec.ts b/src/json-crdt/codec/sidecar/binary/__tests__/automated-logical.spec.ts index e7e07cfa9b..637296a5d4 100644 --- a/src/json-crdt/codec/sidecar/binary/__tests__/automated-logical.spec.ts +++ b/src/json-crdt/codec/sidecar/binary/__tests__/automated-logical.spec.ts @@ -4,7 +4,7 @@ import {Encoder} from '../Encoder'; import {Decoder} from '../Decoder'; import {documents} from '../../../../../__tests__/json-documents'; import {binaryDocuments} from '../../../../../__tests__/binary-documents'; -import {CborDecoder} from '../../../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; for (const {name, json} of [...documents, ...binaryDocuments]) { describe('fresh encoder and decoder', () => { diff --git a/src/json-crdt/codec/sidecar/binary/__tests__/fuzzer-logical.spec.ts b/src/json-crdt/codec/sidecar/binary/__tests__/fuzzer-logical.spec.ts index 315b63d0bd..6812c84275 100644 --- a/src/json-crdt/codec/sidecar/binary/__tests__/fuzzer-logical.spec.ts +++ b/src/json-crdt/codec/sidecar/binary/__tests__/fuzzer-logical.spec.ts @@ -1,7 +1,7 @@ import {JsonCrdtFuzzer} from '../../../../__tests__/fuzzer/JsonCrdtFuzzer'; import {Encoder} from '../Encoder'; import {Decoder} from '../Decoder'; -import {CborDecoder} from '../../../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; const encoder = new Encoder(); const decoder = new Decoder(); diff --git a/src/json-crdt/codec/structural/binary/Decoder.ts b/src/json-crdt/codec/structural/binary/Decoder.ts index 1de5d32c76..bda7c00f5e 100644 --- a/src/json-crdt/codec/structural/binary/Decoder.ts +++ b/src/json-crdt/codec/structural/binary/Decoder.ts @@ -3,7 +3,7 @@ import {ClockDecoder} from '../../../../json-crdt-patch/codec/clock/ClockDecoder import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtReader'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; import {Model, UNDEFINED} from '../../../model/Model'; -import {CborDecoderBase} from '../../../../json-pack/cbor/CborDecoderBase'; +import {CborDecoderBase} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {CRDT_MAJOR} from './constants'; diff --git a/src/json-crdt/codec/structural/binary/Encoder.ts b/src/json-crdt/codec/structural/binary/Encoder.ts index 9734da685e..36ae0ffb14 100644 --- a/src/json-crdt/codec/structural/binary/Encoder.ts +++ b/src/json-crdt/codec/structural/binary/Encoder.ts @@ -2,7 +2,7 @@ import * as nodes from '../../../nodes'; import {ClockEncoder} from '../../../../json-crdt-patch/codec/clock/ClockEncoder'; import {CrdtWriter} from '../../../../json-crdt-patch/util/binary/CrdtWriter'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; -import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {CRDT_MAJOR_OVERLAY} from './constants'; import type {Model} from '../../../model'; diff --git a/src/json-crdt/codec/structural/binary/ViewDecoder.ts b/src/json-crdt/codec/structural/binary/ViewDecoder.ts index 187a447ede..cf60879c0b 100644 --- a/src/json-crdt/codec/structural/binary/ViewDecoder.ts +++ b/src/json-crdt/codec/structural/binary/ViewDecoder.ts @@ -1,5 +1,5 @@ +import {CborDecoderBase} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtReader'; -import {CborDecoderBase} from '../../../../json-pack/cbor/CborDecoderBase'; import {CRDT_MAJOR} from './constants'; export class ViewDecoder extends CborDecoderBase { diff --git a/src/json-crdt/codec/structural/compact-binary/Decoder.ts b/src/json-crdt/codec/structural/compact-binary/Decoder.ts index d3708ba0c0..72d66af5e1 100644 --- a/src/json-crdt/codec/structural/compact-binary/Decoder.ts +++ b/src/json-crdt/codec/structural/compact-binary/Decoder.ts @@ -1,5 +1,5 @@ import {Decoder as CompactDecoder} from '../compact/Decoder'; -import {decoder} from '../../../../json-pack/msgpack/util'; +import {decoder} from '@jsonjoy.com/json-pack/lib/msgpack/util'; import {Model} from '../../../model'; export class Decoder { diff --git a/src/json-crdt/codec/structural/compact-binary/Encoder.ts b/src/json-crdt/codec/structural/compact-binary/Encoder.ts index 55d8f001fa..0aabb7594f 100644 --- a/src/json-crdt/codec/structural/compact-binary/Encoder.ts +++ b/src/json-crdt/codec/structural/compact-binary/Encoder.ts @@ -1,5 +1,5 @@ import {Encoder as CompactEncoder} from '../compact/Encoder'; -import {encoderFull} from '../../../../json-pack/msgpack/util'; +import {encoderFull} from '@jsonjoy.com/json-pack/lib/msgpack/util'; import {Model} from '../../../model'; export class Encoder { diff --git a/src/json-crdt/codec/structural/verbose/Decoder.ts b/src/json-crdt/codec/structural/verbose/Decoder.ts index ce51d0b3d9..0e140edd9e 100644 --- a/src/json-crdt/codec/structural/verbose/Decoder.ts +++ b/src/json-crdt/codec/structural/verbose/Decoder.ts @@ -1,5 +1,5 @@ import * as nodes from '../../../nodes'; -import {fromBase64} from '../../../../util/base64/fromBase64'; +import {fromBase64} from '@jsonjoy.com/base64/lib/fromBase64'; import {ITimestampStruct, ts, ClockVector} from '../../../../json-crdt-patch/clock'; import {Model} from '../../../model'; import {SESSION} from '../../../../json-crdt-patch/constants'; diff --git a/src/json-crdt/codec/structural/verbose/Encoder.ts b/src/json-crdt/codec/structural/verbose/Encoder.ts index 34231ad036..99165cbc8f 100644 --- a/src/json-crdt/codec/structural/verbose/Encoder.ts +++ b/src/json-crdt/codec/structural/verbose/Encoder.ts @@ -1,5 +1,5 @@ import * as nodes from '../../../nodes'; -import {toBase64} from '../../../../util/base64/toBase64'; +import {toBase64} from '@jsonjoy.com/base64/lib/toBase64'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {ITimestampStruct, IClockVector, Timestamp} from '../../../../json-crdt-patch/clock'; import {Model} from '../../../model'; diff --git a/src/json-crdt/history/LocalHistoryCrud.ts b/src/json-crdt/history/LocalHistoryCrud.ts index b19ba39140..7baf866950 100644 --- a/src/json-crdt/history/LocalHistoryCrud.ts +++ b/src/json-crdt/history/LocalHistoryCrud.ts @@ -1,5 +1,5 @@ -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; -import {CborDecoder} from '../../json-pack/cbor/CborDecoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {LogEncoder} from '../log/codec/LogEncoder'; import {LogDecoder} from '../log/codec/LogDecoder'; import type {CrudApi} from 'memfs/lib/crud/types'; diff --git a/src/json-crdt/log/codec/LogDecoder.ts b/src/json-crdt/log/codec/LogDecoder.ts index d47d651947..132ad539cd 100644 --- a/src/json-crdt/log/codec/LogDecoder.ts +++ b/src/json-crdt/log/codec/LogDecoder.ts @@ -4,8 +4,8 @@ import {Patch} from '../../../json-crdt-patch'; import {FileModelEncoding} from './constants'; import {SESSION} from '../../../json-crdt-patch/constants'; import type * as types from './types'; -import type {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; -import type {JsonDecoder} from '../../../json-pack/json/JsonDecoder'; +import type {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; +import type {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; import type {Decoder as SidecarDecoder} from '../../codec/sidecar/binary/Decoder'; import type {Decoder as StructuralDecoderCompact} from '../../codec/structural/compact/Decoder'; import type {Decoder as StructuralDecoderVerbose} from '../../codec/structural/verbose/Decoder'; diff --git a/src/json-crdt/log/codec/LogEncoder.ts b/src/json-crdt/log/codec/LogEncoder.ts index 03330daef9..fdad92794f 100644 --- a/src/json-crdt/log/codec/LogEncoder.ts +++ b/src/json-crdt/log/codec/LogEncoder.ts @@ -1,8 +1,8 @@ import {Log} from '../Log'; import {FileModelEncoding} from './constants'; import type * as types from './types'; -import type {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import type {JsonEncoder} from '../../../json-pack/json/JsonEncoder'; +import type {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import type {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import type {Encoder as StructuralEncoderCompact} from '../../codec/structural/compact/Encoder'; import type {Encoder as StructuralEncoderVerbose} from '../../codec/structural/verbose/Encoder'; import type {Encoder as SidecarEncoder} from '../../codec/sidecar/binary/Encoder'; diff --git a/src/json-crdt/log/codec/__tests__/LogEncoder.spec.ts b/src/json-crdt/log/codec/__tests__/LogEncoder.spec.ts index dd090288c9..b5781dbe12 100644 --- a/src/json-crdt/log/codec/__tests__/LogEncoder.spec.ts +++ b/src/json-crdt/log/codec/__tests__/LogEncoder.spec.ts @@ -1,10 +1,10 @@ // import {s} from '../../../json-crdt-patch'; import {Model} from '../../../model'; -import {JsonDecoder} from '../../../../json-pack/json/JsonDecoder'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; import {logEncoderOpts} from '../logEncoderOpts'; import {LogEncoder} from '../LogEncoder'; import {Log} from '../../Log'; -import {CborDecoder} from '../../../../json-pack/cbor/CborDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; const setup = (view: unknown) => { const model = Model.withServerClock(); diff --git a/src/json-crdt/log/codec/logDecoderOpts.ts b/src/json-crdt/log/codec/logDecoderOpts.ts index 9fe19a5089..8be9cfcde4 100644 --- a/src/json-crdt/log/codec/logDecoderOpts.ts +++ b/src/json-crdt/log/codec/logDecoderOpts.ts @@ -1,8 +1,8 @@ import {Decoder as SidecarDecoder} from '../../codec/sidecar/binary/Decoder'; import {Decoder as StructuralDecoderCompact} from '../../codec/structural/compact/Decoder'; import {Decoder as StructuralDecoderVerbose} from '../../codec/structural/verbose/Decoder'; -import {JsonDecoder} from '../../../json-pack/json/JsonDecoder'; -import {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {decode as decodeCompact} from '../../../json-crdt-patch/codec/compact/decode'; import {decode as decodeVerbose} from '../../../json-crdt-patch/codec/verbose/decode'; import type {LogDecoderOpts} from './LogDecoder'; diff --git a/src/json-crdt/log/codec/logEncoderOpts.ts b/src/json-crdt/log/codec/logEncoderOpts.ts index 76f50d66d5..b826ed468f 100644 --- a/src/json-crdt/log/codec/logEncoderOpts.ts +++ b/src/json-crdt/log/codec/logEncoderOpts.ts @@ -1,11 +1,11 @@ -import {Writer} from '../../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {Encoder as SidecarEncoder} from '../../codec/sidecar/binary/Encoder'; import {Encoder as StructuralEncoderCompact} from '../../codec/structural/compact/Encoder'; import {Encoder as StructuralEncoderVerbose} from '../../codec/structural/verbose/Encoder'; import {encode as encodeCompact} from '../../../json-crdt-patch/codec/compact/encode'; import {encode as encodeVerbose} from '../../../json-crdt-patch/codec/verbose/encode'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import {JsonEncoder} from '../../../json-pack/json/JsonEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import type {LogEncoderOpts} from './LogEncoder'; const writer = new Writer(4096); diff --git a/src/json-crdt/nodes/rga/AbstractRga.ts b/src/json-crdt/nodes/rga/AbstractRga.ts index 909b15a45a..443cb33610 100644 --- a/src/json-crdt/nodes/rga/AbstractRga.ts +++ b/src/json-crdt/nodes/rga/AbstractRga.ts @@ -8,14 +8,14 @@ import { containsId, Timestamp, } from '../../../json-crdt-patch/clock'; -import {isUint8Array} from '../../../util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; import {rSplay, lSplay, llSplay, rrSplay, lrSplay, rlSplay} from '../../../util/trees/splay/util'; import {splay2} from '../../../util/trees/splay/util2'; import {insert2, remove2} from '../../../util/trees/util2'; import {ORIGIN} from '../../../json-crdt-patch/constants'; import {printTree} from '../../../util/print/printTree'; import {printBinary} from '../../../util/print/printBinary'; -import {printOctets} from '../../../util/buffers/printOctets'; +import {printOctets} from '@jsonjoy.com/json-pack/lib/util/buffers/printOctets'; /** * @category CRDT Node diff --git a/src/json-crdt/schema/__tests__/toSchema.spec.ts b/src/json-crdt/schema/__tests__/toSchema.spec.ts index 4850337d4f..a0ae310343 100644 --- a/src/json-crdt/schema/__tests__/toSchema.spec.ts +++ b/src/json-crdt/schema/__tests__/toSchema.spec.ts @@ -1,6 +1,6 @@ import {NodeBuilder, s, nodes} from '../../../json-crdt-patch'; import {deepEqual} from '../../../json-equal/deepEqual'; -import {cmpUint8Array} from '../../../util/buffers/cmpUint8Array'; +import {cmpUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array'; import {Model} from '../../model'; import {toSchema} from '../toSchema'; diff --git a/src/json-equal/$$deepEqual/v1.ts b/src/json-equal/$$deepEqual/v1.ts index 87a2c349bd..a240f9171c 100644 --- a/src/json-equal/$$deepEqual/v1.ts +++ b/src/json-equal/$$deepEqual/v1.ts @@ -1,4 +1,4 @@ -import {JavaScript} from '../../util/codegen'; +import {JavaScript} from '@jsonjoy.com/json-pack/lib/util/codegen'; const codegenValue = (doc: unknown, code: string[], r: number): number => { let rr = r; diff --git a/src/json-expression/codegen.ts b/src/json-expression/codegen.ts index 38de6526e1..b4427251ea 100644 --- a/src/json-expression/codegen.ts +++ b/src/json-expression/codegen.ts @@ -1,8 +1,8 @@ import * as util from './util'; -import {Codegen} from '../util/codegen/Codegen'; +import {Codegen} from '@jsonjoy.com/json-pack/lib/util/codegen/Codegen'; import {ExpressionResult, Literal} from './codegen-steps'; import {createEvaluate} from './createEvaluate'; -import {JavaScript} from '../util/codegen'; +import {JavaScript} from '@jsonjoy.com/json-pack/lib/util/codegen'; import {Vars} from './Vars'; import type * as types from './types'; diff --git a/src/json-hash/index.ts b/src/json-hash/index.ts index 85a3d9fe34..80dcc9c879 100644 --- a/src/json-hash/index.ts +++ b/src/json-hash/index.ts @@ -1,4 +1,4 @@ -import type {JsonValue} from '../json-pack/types'; +import type {JsonValue} from '@jsonjoy.com/json-pack/lib/types'; import {sort} from '../util/sort/insertion'; export const enum CONST { diff --git a/src/json-pack/JsonPackExtension.ts b/src/json-pack/JsonPackExtension.ts deleted file mode 100644 index e21ca138ae..0000000000 --- a/src/json-pack/JsonPackExtension.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * A wrapping for MessagePack extension or CBOR tag value. When encoder - * encounters {@link JsonPackExtension} it will encode it as a MessagePack - * extension or CBOR tag. Likewise, the decoder will - * decode extensions into {@link JsonPackExtension}. - * - * @category Value - */ -export class JsonPackExtension { - constructor( - public readonly tag: number, - public readonly val: T, - ) {} -} diff --git a/src/json-pack/JsonPackValue.ts b/src/json-pack/JsonPackValue.ts deleted file mode 100644 index cdf7f41dec..0000000000 --- a/src/json-pack/JsonPackValue.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Use this wrapper is you have a pre-encoded MessagePack or CBOR value and you would - * like to dump it into a the document as-is. The contents of `buf` will - * be written as is to the document. - * - * It also serves as CBOR simple value container. In which case the type of value - * `val` field is "number". - * - * @category Value - */ -export class JsonPackValue { - constructor(public readonly val: T) {} -} diff --git a/src/json-pack/README.md b/src/json-pack/README.md deleted file mode 100644 index 3e287204e1..0000000000 --- a/src/json-pack/README.md +++ /dev/null @@ -1,426 +0,0 @@ -# `json-pack`—JSON binary packing - -This library contains implementations of various JSON codecs into binary, -formats, such as MessagePack, CBOR, and Amazon Ion. - - -`json-pack` implements the fastest encoders and decoders for MessagePack and CBOR. - -- [__MessagePack__](./msgpack/README.md) -- [__CBOR__](./cbor/README.md) - - -## Benchmarks - -Encoding: - -``` -npx ts-node benchmarks/json-pack/bench.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.0.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 5,385,617 ops/sec ยฑ0.53% (100 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 2,254,954 ops/sec ยฑ0.83% (97 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 5,953,159 ops/sec ยฑ1.12% (92 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 6,248,036 ops/sec ยฑ0.29% (98 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 3,121,940 ops/sec ยฑ0.16% (99 runs sampled) -๐Ÿคž JSON.stringify() x 3,866,116 ops/sec ยฑ0.11% (101 runs sampled) -๐Ÿคž @msgpack/msgpack x 1,406,546 ops/sec ยฑ0.94% (93 runs sampled) -๐Ÿคž msgpackr x 2,404,916 ops/sec ยฑ3.22% (86 runs sampled) -๐Ÿคž cbor-x x 4,737,433 ops/sec ยฑ1.00% (97 runs sampled) -๐Ÿคž msgpack-lite x 987,201 ops/sec ยฑ2.84% (91 runs sampled) -๐Ÿคž msgpack5 x 197,867 ops/sec ยฑ3.65% (84 runs sampled) -๐Ÿคž messagepack x 171,865 ops/sec ยฑ4.44% (74 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 299,970 ops/sec ยฑ0.30% (97 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 211,651 ops/sec ยฑ0.18% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 429,535 ops/sec ยฑ3.38% (93 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 428,848 ops/sec ยฑ0.71% (97 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 322,982 ops/sec ยฑ0.67% (97 runs sampled) -๐Ÿคž JSON.stringify() x 306,828 ops/sec ยฑ1.94% (90 runs sampled) -๐Ÿคž @msgpack/msgpack x 199,937 ops/sec ยฑ5.52% (93 runs sampled) -๐Ÿคž msgpackr x 317,457 ops/sec ยฑ2.18% (90 runs sampled) -๐Ÿคž cbor-x x 401,854 ops/sec ยฑ3.20% (92 runs sampled) -๐Ÿคž msgpack-lite x 135,110 ops/sec ยฑ1.29% (94 runs sampled) -๐Ÿคž msgpack5 x 15,217 ops/sec ยฑ3.72% (85 runs sampled) -๐Ÿคž messagepack x 13,853 ops/sec ยฑ4.73% (71 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 87,312 ops/sec ยฑ1.10% (96 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 64,718 ops/sec ยฑ0.45% (96 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 134,615 ops/sec ยฑ0.19% (97 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 128,975 ops/sec ยฑ0.20% (98 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 103,325 ops/sec ยฑ1.62% (98 runs sampled) -๐Ÿคž JSON.stringify() x 101,067 ops/sec ยฑ1.36% (95 runs sampled) -๐Ÿคž @msgpack/msgpack x 61,715 ops/sec ยฑ0.22% (98 runs sampled) -๐Ÿคž msgpackr x 95,175 ops/sec ยฑ3.84% (95 runs sampled) -๐Ÿคž cbor-x x 111,658 ops/sec ยฑ1.34% (95 runs sampled) -๐Ÿคž msgpack-lite x 41,364 ops/sec ยฑ0.28% (100 runs sampled) -๐Ÿคž msgpack5 x 3,262 ops/sec ยฑ4.32% (71 runs sampled) -๐Ÿคž messagepack x 4,167 ops/sec ยฑ7.29% (65 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 5,687 ops/sec ยฑ1.92% (94 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 5,813 ops/sec ยฑ2.51% (97 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 5,749 ops/sec ยฑ0.67% (98 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 5,515 ops/sec ยฑ0.70% (98 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 5,027 ops/sec ยฑ0.19% (100 runs sampled) -๐Ÿคž JSON.stringify() x 7,687 ops/sec ยฑ0.87% (99 runs sampled) -๐Ÿคž @msgpack/msgpack x 3,379 ops/sec ยฑ2.20% (97 runs sampled) -๐Ÿคž msgpackr x 5,929 ops/sec ยฑ15.26% (96 runs sampled) -๐Ÿคž cbor-x x 5,032 ops/sec ยฑ5.17% (90 runs sampled) -๐Ÿคž msgpack-lite x 2,173 ops/sec ยฑ1.17% (97 runs sampled) -๐Ÿคž msgpack5 x 179 ops/sec ยฑ2.95% (68 runs sampled) -๐Ÿคž messagepack x 167 ops/sec ยฑ1.09% (79 runs sampled) -Fastest is ๐Ÿคž JSON.stringify() ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 213,447 ops/sec ยฑ3.31% (95 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 168,303 ops/sec ยฑ2.13% (95 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 275,511 ops/sec ยฑ0.40% (95 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 270,949 ops/sec ยฑ0.32% (97 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 210,525 ops/sec ยฑ0.66% (99 runs sampled) -๐Ÿคž JSON.stringify() x 200,767 ops/sec ยฑ0.19% (101 runs sampled) -๐Ÿคž @msgpack/msgpack x 163,665 ops/sec ยฑ0.81% (98 runs sampled) -๐Ÿคž msgpackr x 151,889 ops/sec ยฑ0.27% (96 runs sampled) -๐Ÿคž cbor-x x 191,010 ops/sec ยฑ0.44% (96 runs sampled) -๐Ÿคž msgpack-lite x 93,537 ops/sec ยฑ0.68% (99 runs sampled) -๐Ÿคž msgpack5 x 28,581 ops/sec ยฑ1.74% (93 runs sampled) -๐Ÿคž messagepack x 8,330 ops/sec ยฑ5.00% (61 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 147,755 ops/sec ยฑ0.23% (97 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 128,378 ops/sec ยฑ0.15% (96 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 298,037 ops/sec ยฑ0.73% (98 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 293,608 ops/sec ยฑ0.22% (97 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 244,864 ops/sec ยฑ3.92% (92 runs sampled) -๐Ÿคž JSON.stringify() x 165,819 ops/sec ยฑ1.72% (94 runs sampled) -๐Ÿคž @msgpack/msgpack x 79,127 ops/sec ยฑ1.43% (93 runs sampled) -๐Ÿคž msgpackr x 236,254 ops/sec ยฑ1.45% (94 runs sampled) -๐Ÿคž cbor-x x 206,835 ops/sec ยฑ1.26% (92 runs sampled) -๐Ÿคž msgpack-lite x 157,499 ops/sec ยฑ0.39% (98 runs sampled) -๐Ÿคž msgpack5 x 55,363 ops/sec ยฑ2.75% (88 runs sampled) -๐Ÿคž messagepack x 8,261 ops/sec ยฑ2.97% (72 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 51,334 ops/sec ยฑ0.16% (99 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 27,108 ops/sec ยฑ4.89% (90 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 374,042 ops/sec ยฑ6.39% (91 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 424,864 ops/sec ยฑ0.35% (97 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 363,465 ops/sec ยฑ1.91% (85 runs sampled) -๐Ÿคž JSON.stringify() x 59,793 ops/sec ยฑ0.14% (100 runs sampled) -๐Ÿคž @msgpack/msgpack x 57,373 ops/sec ยฑ0.13% (98 runs sampled) -๐Ÿคž msgpackr x 372,751 ops/sec ยฑ2.17% (90 runs sampled) -๐Ÿคž cbor-x x 389,277 ops/sec ยฑ1.60% (93 runs sampled) -๐Ÿคž msgpack-lite x 170,279 ops/sec ยฑ0.82% (97 runs sampled) -๐Ÿคž msgpack5 x 83,809 ops/sec ยฑ2.80% (83 runs sampled) -๐Ÿคž messagepack x 20,076 ops/sec ยฑ1.45% (87 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 1,577,757 ops/sec ยฑ0.16% (98 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,057,420 ops/sec ยฑ0.38% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 1,844,775 ops/sec ยฑ0.20% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 1,468,011 ops/sec ยฑ0.23% (98 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 1,240,577 ops/sec ยฑ0.19% (98 runs sampled) -๐Ÿคž JSON.stringify() x 1,852,916 ops/sec ยฑ0.20% (100 runs sampled) -๐Ÿคž @msgpack/msgpack x 781,414 ops/sec ยฑ0.42% (92 runs sampled) -๐Ÿคž msgpackr x 1,672,474 ops/sec ยฑ0.23% (99 runs sampled) -๐Ÿคž cbor-x x 1,351,338 ops/sec ยฑ0.20% (97 runs sampled) -๐Ÿคž msgpack-lite x 416,300 ops/sec ยฑ0.76% (96 runs sampled) -๐Ÿคž msgpack5 x 151,657 ops/sec ยฑ1.97% (91 runs sampled) -๐Ÿคž messagepack x 35,124 ops/sec ยฑ5.60% (61 runs sampled) -Fastest is ๐Ÿคž JSON.stringify() --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 1,708,133 ops/sec ยฑ1.09% (98 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,135,630 ops/sec ยฑ1.67% (95 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 2,658,037 ops/sec ยฑ1.33% (97 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 3,084,914 ops/sec ยฑ0.24% (101 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 1,620,958 ops/sec ยฑ2.15% (94 runs sampled) -๐Ÿคž JSON.stringify() x 1,602,303 ops/sec ยฑ0.24% (98 runs sampled) -๐Ÿคž @msgpack/msgpack x 997,885 ops/sec ยฑ1.70% (97 runs sampled) -๐Ÿคž msgpackr x 2,659,862 ops/sec ยฑ0.51% (96 runs sampled) -๐Ÿคž cbor-x x 3,116,954 ops/sec ยฑ0.89% (95 runs sampled) -๐Ÿคž msgpack-lite x 892,281 ops/sec ยฑ2.19% (92 runs sampled) -๐Ÿคž msgpack5 x 144,567 ops/sec ยฑ3.06% (88 runs sampled) -๐Ÿคž messagepack x 383,134 ops/sec ยฑ2.95% (74 runs sampled) -Fastest is ๐Ÿคž cbor-x ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿคž json-joy/json-pack JsonEncoder x 1,370,517 ops/sec ยฑ0.52% (98 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,016,856 ops/sec ยฑ0.16% (93 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 1,347,193 ops/sec ยฑ0.20% (96 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 1,353,358 ops/sec ยฑ0.20% (101 runs sampled) -๐Ÿคž json-joy/json-pack MsgPackEncoderFast x 1,130,418 ops/sec ยฑ0.14% (96 runs sampled) -๐Ÿคž JSON.stringify() x 1,549,669 ops/sec ยฑ0.49% (97 runs sampled) -๐Ÿคž @msgpack/msgpack x 871,477 ops/sec ยฑ0.92% (98 runs sampled) -๐Ÿคž msgpackr x 1,716,378 ops/sec ยฑ0.20% (99 runs sampled) -๐Ÿคž cbor-x x 1,951,639 ops/sec ยฑ0.16% (100 runs sampled) -๐Ÿคž msgpack-lite x 622,495 ops/sec ยฑ1.03% (96 runs sampled) -๐Ÿคž msgpack5 x 81,727 ops/sec ยฑ2.04% (91 runs sampled) -๐Ÿคž messagepack x 609,651 ops/sec ยฑ1.64% (89 runs sampled) -Fastest is ๐Ÿคž cbor-x -``` - -Decoding: - -``` -node benchmarks/json-pack/bench.decoding.js -=============================================================================== Benchmark: Decoding -Warmup: 1000x , Node.js: v16.14.2 , Arch: arm64 , CPU: Apple M1 --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ JSON.parse() x 3,506 ops/sec ยฑ0.19% (100 runs sampled) -๐Ÿ‘ sjson.parse() x 3,336 ops/sec ยฑ0.11% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 4,915 ops/sec ยฑ0.18% (100 runs sampled) -๐Ÿ‘ cbor-x x 4,747 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿ‘ cbor x 260 ops/sec ยฑ0.29% (90 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 5,506 ops/sec ยฑ0.48% (100 runs sampled) -๐Ÿ‘ msgpackr x 4,729 ops/sec ยฑ0.23% (101 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 4,096 ops/sec ยฑ0.25% (100 runs sampled) -๐Ÿ‘ msgpack5 x 920 ops/sec ยฑ0.34% (99 runs sampled) -๐Ÿ‘ msgpack-lite x 1,223 ops/sec ยฑ0.10% (100 runs sampled) -๐Ÿ‘ messagepack x 194 ops/sec ยฑ1.93% (73 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ JSON.parse() x 91,582 ops/sec ยฑ0.30% (100 runs sampled) -๐Ÿ‘ sjson.parse() x 84,411 ops/sec ยฑ0.16% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 94,618 ops/sec ยฑ0.27% (97 runs sampled) -๐Ÿ‘ cbor-x x 108,102 ops/sec ยฑ0.37% (101 runs sampled) -๐Ÿ‘ cbor x 4,845 ops/sec ยฑ0.79% (95 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 102,544 ops/sec ยฑ0.39% (99 runs sampled) -๐Ÿ‘ msgpackr x 111,668 ops/sec ยฑ0.16% (101 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 56,952 ops/sec ยฑ0.51% (97 runs sampled) -๐Ÿ‘ msgpack5 x 17,420 ops/sec ยฑ0.60% (101 runs sampled) -๐Ÿ‘ msgpack-lite x 20,536 ops/sec ยฑ0.23% (98 runs sampled) -๐Ÿ‘ messagepack x 3,247 ops/sec ยฑ2.30% (87 runs sampled) -Fastest is ๐Ÿ‘ msgpackr -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ JSON.parse() x 304,670 ops/sec ยฑ0.98% (97 runs sampled) -๐Ÿ‘ sjson.parse() x 283,259 ops/sec ยฑ0.20% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 298,666 ops/sec ยฑ0.19% (100 runs sampled) -๐Ÿ‘ cbor-x x 322,995 ops/sec ยฑ0.71% (97 runs sampled) -๐Ÿ‘ cbor x 14,391 ops/sec ยฑ0.88% (95 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 321,984 ops/sec ยฑ0.23% (100 runs sampled) -๐Ÿ‘ msgpackr x 328,671 ops/sec ยฑ0.31% (99 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 198,604 ops/sec ยฑ0.85% (96 runs sampled) -๐Ÿ‘ msgpack5 x 51,549 ops/sec ยฑ0.32% (99 runs sampled) -๐Ÿ‘ msgpack-lite x 67,171 ops/sec ยฑ0.19% (99 runs sampled) -๐Ÿ‘ messagepack x 9,464 ops/sec ยฑ1.95% (92 runs sampled) -Fastest is ๐Ÿ‘ msgpackr ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ JSON.parse() x 2,654,389 ops/sec ยฑ0.28% (98 runs sampled) -๐Ÿ‘ sjson.parse() x 2,325,941 ops/sec ยฑ0.21% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 3,357,402 ops/sec ยฑ0.31% (99 runs sampled) -๐Ÿ‘ cbor-x x 4,133,737 ops/sec ยฑ0.29% (101 runs sampled) -๐Ÿ‘ cbor x 112,776 ops/sec ยฑ5.79% (88 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 3,359,127 ops/sec ยฑ0.56% (98 runs sampled) -๐Ÿ‘ msgpackr x 3,436,592 ops/sec ยฑ0.35% (97 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 2,288,251 ops/sec ยฑ0.52% (94 runs sampled) -๐Ÿ‘ msgpack5 x 377,061 ops/sec ยฑ0.67% (96 runs sampled) -๐Ÿ‘ msgpack-lite x 872,569 ops/sec ยฑ0.31% (100 runs sampled) -๐Ÿ‘ messagepack x 116,422 ops/sec ยฑ1.84% (86 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ JSON.parse() x 270,312 ops/sec ยฑ0.57% (98 runs sampled) -๐Ÿ‘ sjson.parse() x 242,328 ops/sec ยฑ3.10% (97 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 81,403 ops/sec ยฑ0.42% (96 runs sampled) -๐Ÿ‘ cbor-x x 93,131 ops/sec ยฑ0.48% (99 runs sampled) -๐Ÿ‘ cbor x 8,760 ops/sec ยฑ0.93% (95 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 84,014 ops/sec ยฑ0.31% (96 runs sampled) -๐Ÿ‘ msgpackr x 91,477 ops/sec ยฑ0.77% (90 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 73,089 ops/sec ยฑ0.56% (89 runs sampled) -๐Ÿ‘ msgpack5 x 23,468 ops/sec ยฑ0.72% (97 runs sampled) -๐Ÿ‘ msgpack-lite x 34,630 ops/sec ยฑ0.48% (100 runs sampled) -๐Ÿ‘ messagepack x 6,161 ops/sec ยฑ1.77% (86 runs sampled) -Fastest is ๐Ÿ‘ JSON.parse() -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ JSON.parse() x 287,387 ops/sec ยฑ0.36% (99 runs sampled) -๐Ÿ‘ sjson.parse() x 192,836 ops/sec ยฑ0.40% (95 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 177,787 ops/sec ยฑ0.48% (98 runs sampled) -๐Ÿ‘ cbor-x x 320,303 ops/sec ยฑ0.51% (94 runs sampled) -๐Ÿ‘ cbor x 15,416 ops/sec ยฑ0.61% (94 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 179,625 ops/sec ยฑ0.59% (100 runs sampled) -๐Ÿ‘ msgpackr x 375,452 ops/sec ยฑ0.69% (94 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 36,544 ops/sec ยฑ0.75% (84 runs sampled) -๐Ÿ‘ msgpack5 x 54,428 ops/sec ยฑ0.46% (98 runs sampled) -๐Ÿ‘ msgpack-lite x 25,309 ops/sec ยฑ0.81% (75 runs sampled) -๐Ÿ‘ messagepack x 10,117 ops/sec ยฑ3.99% (82 runs sampled) -Fastest is ๐Ÿ‘ msgpackr --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ JSON.parse() x 117,335 ops/sec ยฑ3.32% (89 runs sampled) -๐Ÿ‘ sjson.parse() x 103,275 ops/sec ยฑ0.64% (94 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 74,140 ops/sec ยฑ7.50% (81 runs sampled) -๐Ÿ‘ cbor-x x 92,753 ops/sec ยฑ0.78% (96 runs sampled) -๐Ÿ‘ cbor x 24,292 ops/sec ยฑ27.70% (75 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 88,124 ops/sec ยฑ1.65% (90 runs sampled) -๐Ÿ‘ msgpackr x 94,352 ops/sec ยฑ0.91% (94 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 33,256 ops/sec ยฑ30.68% (71 runs sampled) -๐Ÿ‘ msgpack5 x 68,367 ops/sec ยฑ0.70% (95 runs sampled) -๐Ÿ‘ msgpack-lite x 14,764 ops/sec ยฑ2.04% (63 runs sampled) -๐Ÿ‘ messagepack x 17,522 ops/sec ยฑ28.57% (66 runs sampled) -Fastest is ๐Ÿ‘ JSON.parse() --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ JSON.parse() x 1,077,084 ops/sec ยฑ6.88% (77 runs sampled) -๐Ÿ‘ sjson.parse() x 837,130 ops/sec ยฑ2.70% (80 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 698,901 ops/sec ยฑ4.69% (88 runs sampled) -๐Ÿ‘ cbor-x x 1,182,303 ops/sec ยฑ0.39% (94 runs sampled) -๐Ÿ‘ cbor x 26,810 ops/sec ยฑ14.70% (73 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 742,562 ops/sec ยฑ5.06% (88 runs sampled) -๐Ÿ‘ msgpackr x 1,041,143 ops/sec ยฑ2.66% (85 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 440,652 ops/sec ยฑ1.38% (92 runs sampled) -๐Ÿ‘ msgpack5 x 133,387 ops/sec ยฑ1.14% (96 runs sampled) -๐Ÿ‘ msgpack-lite x 206,844 ops/sec ยฑ0.63% (97 runs sampled) -๐Ÿ‘ messagepack x 23,818 ops/sec ยฑ2.13% (94 runs sampled) -Fastest is ๐Ÿ‘ cbor-x,๐Ÿ‘ JSON.parse() --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ JSON.parse() x 1,747,460 ops/sec ยฑ0.61% (95 runs sampled) -๐Ÿ‘ sjson.parse() x 1,553,635 ops/sec ยฑ1.04% (93 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 2,289,002 ops/sec ยฑ0.93% (87 runs sampled) -๐Ÿ‘ cbor-x x 3,775,727 ops/sec ยฑ2.86% (82 runs sampled) -๐Ÿ‘ cbor x 77,650 ops/sec ยฑ4.32% (83 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 2,287,682 ops/sec ยฑ1.54% (80 runs sampled) -๐Ÿ‘ msgpackr x 3,391,489 ops/sec ยฑ0.59% (80 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 2,297,255 ops/sec ยฑ1.54% (78 runs sampled) -๐Ÿ‘ msgpack5 x 112,373 ops/sec ยฑ1.19% (91 runs sampled) -๐Ÿ‘ msgpack-lite x 1,378,387 ops/sec ยฑ0.84% (95 runs sampled) -๐Ÿ‘ messagepack x 1,174,740 ops/sec ยฑ0.97% (89 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ JSON.parse() x 1,303,300 ops/sec ยฑ2.26% (92 runs sampled) -๐Ÿ‘ sjson.parse() x 1,091,921 ops/sec ยฑ2.85% (86 runs sampled) -๐Ÿ‘ json-joy/json-pack CborDecoderBase x 1,203,319 ops/sec ยฑ2.12% (90 runs sampled) -๐Ÿ‘ cbor-x x 1,787,591 ops/sec ยฑ2.94% (74 runs sampled) -๐Ÿ‘ cbor x 45,127 ops/sec ยฑ24.11% (64 runs sampled) -๐Ÿ‘ json-joy/json-pack MsgPackDecoderFast x 1,283,322 ops/sec ยฑ1.93% (94 runs sampled) -๐Ÿ‘ msgpackr x 1,890,533 ops/sec ยฑ2.66% (90 runs sampled) -๐Ÿ‘ @msgpack/msgpack x 1,364,025 ops/sec ยฑ3.78% (67 runs sampled) -๐Ÿ‘ msgpack5 x 117,205 ops/sec ยฑ2.72% (90 runs sampled) -๐Ÿ‘ msgpack-lite x 1,316,133 ops/sec ยฑ0.74% (99 runs sampled) -๐Ÿ‘ messagepack x 733,566 ops/sec ยฑ1.55% (87 runs sampled) -Fastest is ๐Ÿ‘ msgpackr -``` - -Encoder comparison: - -``` -npx ts-node benchmarks/json-pack/bench.encoders.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.2.0 , Arch: arm64 , CPU: Apple M1 Max ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ CborEncoderFast x 6,319,117 ops/sec ยฑ0.11% (101 runs sampled) -๐Ÿ‘ CborEncoder x 6,001,443 ops/sec ยฑ0.15% (101 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 6,047,466 ops/sec ยฑ0.20% (99 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 5,493,093 ops/sec ยฑ0.10% (101 runs sampled) -๐Ÿ‘Ž JsonEncoder x 6,018,890 ops/sec ยฑ0.11% (97 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 6,545,118 ops/sec ยฑ0.10% (97 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 1,032,434 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 2,300,069 ops/sec ยฑ0.15% (100 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ CborEncoderFast x 460,125 ops/sec ยฑ0.14% (98 runs sampled) -๐Ÿ‘ CborEncoder x 439,506 ops/sec ยฑ0.18% (98 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 458,530 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 449,540 ops/sec ยฑ0.16% (100 runs sampled) -๐Ÿ‘Ž JsonEncoder x 303,410 ops/sec ยฑ0.12% (101 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 479,450 ops/sec ยฑ0.13% (99 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 68,000 ops/sec ยฑ0.11% (102 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 207,747 ops/sec ยฑ0.11% (98 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ CborEncoderFast x 133,608 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿ‘ CborEncoder x 128,019 ops/sec ยฑ0.13% (97 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 133,863 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 131,521 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘Ž JsonEncoder x 93,018 ops/sec ยฑ0.13% (98 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 140,969 ops/sec ยฑ0.15% (101 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 11,523 ops/sec ยฑ0.15% (101 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 63,389 ops/sec ยฑ0.13% (101 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ CborEncoderFast x 5,790 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿ‘ CborEncoder x 5,579 ops/sec ยฑ0.14% (100 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 6,005 ops/sec ยฑ0.13% (100 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 5,670 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘Ž JsonEncoder x 6,351 ops/sec ยฑ0.16% (101 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 6,248 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 1,868 ops/sec ยฑ0.21% (98 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 7,240 ops/sec ยฑ0.19% (99 runs sampled) -Fastest is ๐Ÿ‘Ž Buffer.from(JSON.stringify()) ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ CborEncoderFast x 283,371 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘ CborEncoder x 268,056 ops/sec ยฑ0.17% (96 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 285,224 ops/sec ยฑ0.17% (96 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 272,416 ops/sec ยฑ0.21% (98 runs sampled) -๐Ÿ‘Ž JsonEncoder x 234,921 ops/sec ยฑ0.21% (98 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 292,228 ops/sec ยฑ0.19% (95 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 63,456 ops/sec ยฑ0.14% (98 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 175,341 ops/sec ยฑ0.86% (93 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ CborEncoderFast x 280,167 ops/sec ยฑ0.20% (100 runs sampled) -๐Ÿ‘ CborEncoder x 283,404 ops/sec ยฑ0.20% (97 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 272,800 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 283,433 ops/sec ยฑ0.23% (98 runs sampled) -๐Ÿ‘Ž JsonEncoder x 147,390 ops/sec ยฑ0.16% (98 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 290,624 ops/sec ยฑ0.21% (98 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 25,452 ops/sec ยฑ0.17% (101 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 145,352 ops/sec ยฑ0.23% (99 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ CborEncoderFast x 394,386 ops/sec ยฑ0.53% (95 runs sampled) -๐Ÿ‘ CborEncoder x 394,442 ops/sec ยฑ0.49% (94 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 386,894 ops/sec ยฑ0.54% (95 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 394,019 ops/sec ยฑ0.50% (95 runs sampled) -๐Ÿ‘Ž JsonEncoder x 50,781 ops/sec ยฑ0.13% (97 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 396,184 ops/sec ยฑ0.57% (95 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 11,799 ops/sec ยฑ0.22% (99 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 28,742 ops/sec ยฑ0.11% (102 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder,๐Ÿ‘ CborEncoder,๐Ÿ‘ CborEncoderFast,๐Ÿ‘Ž MsgPackEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ CborEncoderFast x 1,816,742 ops/sec ยฑ0.16% (100 runs sampled) -๐Ÿ‘ CborEncoder x 1,831,503 ops/sec ยฑ0.22% (97 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 1,641,743 ops/sec ยฑ0.17% (101 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 1,694,803 ops/sec ยฑ0.17% (97 runs sampled) -๐Ÿ‘Ž JsonEncoder x 1,595,041 ops/sec ยฑ0.12% (99 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 1,779,112 ops/sec ยฑ0.24% (98 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 422,031 ops/sec ยฑ0.10% (101 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 1,001,976 ops/sec ยฑ0.24% (98 runs sampled) -Fastest is ๐Ÿ‘ CborEncoder --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ CborEncoderFast x 2,822,683 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿ‘ CborEncoder x 3,111,311 ops/sec ยฑ0.20% (97 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 2,918,971 ops/sec ยฑ0.14% (100 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 2,862,193 ops/sec ยฑ0.13% (100 runs sampled) -๐Ÿ‘Ž JsonEncoder x 1,706,584 ops/sec ยฑ0.18% (96 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 3,238,810 ops/sec ยฑ0.15% (97 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 545,885 ops/sec ยฑ0.16% (98 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 1,216,907 ops/sec ยฑ0.20% (98 runs sampled) -Fastest is ๐Ÿ‘Ž UbjsonEncoder ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ CborEncoderFast x 1,360,976 ops/sec ยฑ0.20% (96 runs sampled) -๐Ÿ‘ CborEncoder x 1,367,625 ops/sec ยฑ0.16% (101 runs sampled) -๐Ÿ‘Ž MsgPackEncoderFast x 1,753,202 ops/sec ยฑ0.19% (99 runs sampled) -๐Ÿ‘Ž MsgPackEncoder x 1,733,298 ops/sec ยฑ0.16% (100 runs sampled) -๐Ÿ‘Ž JsonEncoder x 1,411,382 ops/sec ยฑ0.27% (98 runs sampled) -๐Ÿ‘Ž UbjsonEncoder x 1,734,304 ops/sec ยฑ0.17% (101 runs sampled) -๐Ÿ‘Ž IonEncoderFast x 369,161 ops/sec ยฑ0.21% (97 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 1,092,623 ops/sec ยฑ0.15% (101 runs sampled) -Fastest is ๐Ÿ‘Ž MsgPackEncoderFast -``` - - -### Shallow reading - -``` -node benchmarks/json-pack/bench.shallow-read.js -=============================================================================== Benchmark: Encoding -Warmup: 10000x , Node.js: v16.14.2 , Arch: arm64 , CPU: Apple M1 -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ JSON.parse() x 314,451 ops/sec ยฑ0.24% (94 runs sampled) -๐Ÿ‘ msgpackr x 332,628 ops/sec ยฑ0.09% (99 runs sampled) -๐Ÿ‘ cbor-x x 326,509 ops/sec ยฑ0.05% (101 runs sampled) -๐Ÿ‘ MsgPackDecoder x 368,088 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿ‘ CborDecoder x 327,286 ops/sec ยฑ0.15% (101 runs sampled) -๐Ÿ‘ MsgPackDecoder.{findKey,findIndex}() x 1,815,090 ops/sec ยฑ0.07% (99 runs sampled) -๐Ÿ‘ MsgPackDecoder.find() x 1,797,098 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿ‘ genShallowReader()(MsgPackDecoder) x 2,085,260 ops/sec ยฑ0.19% (99 runs sampled) -Fastest is ๐Ÿ‘ genShallowReader()(MsgPackDecoder) -``` diff --git a/src/json-pack/__bench__/bench.bson.encoding.ts b/src/json-pack/__bench__/bench.bson.encoding.ts deleted file mode 100644 index 1c4da8b3a4..0000000000 --- a/src/json-pack/__bench__/bench.bson.encoding.ts +++ /dev/null @@ -1,49 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.bson.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {BsonEncoder} from '../bson/BsonEncoder'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads as payloads_} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; -import {BSON, EJSON} from 'bson'; - -const payloads = payloads_.map((p) => ({...p, data: {data: p.data}})); - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const buf = Buffer.from(data as Uint8Array | Buffer); - const json = JSON.parse(buf.toString()); - return deepEqual(payload, json); - }, - runners: [ - { - name: 'json-joy/json-pack BsonEncoder', - setup: () => { - const writer = new Writer(); - const encoder = new BsonEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'bson BSON.serialize()', - setup: () => { - return (json: any) => { - return BSON.serialize(json); - }; - }, - }, - { - name: 'bson Buffer.from(EJSON.stringify())', - setup: () => { - return (json: any) => { - return Buffer.from(EJSON.stringify(json)); - }; - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.cbor-dag.encoding.ts b/src/json-pack/__bench__/bench.cbor-dag.encoding.ts deleted file mode 100644 index 7fd47a3c94..0000000000 --- a/src/json-pack/__bench__/bench.cbor-dag.encoding.ts +++ /dev/null @@ -1,57 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.cbor-dag.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoderDag} from '../cbor/CborEncoderDag'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: 'json-joy/json-pack CborEncoder', - setup: () => { - const encoder = new CborEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack CborEncoderDag', - setup: () => { - const encoder = new CborEncoderDag(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'cborg', - setup: () => { - const {encode} = require('cborg'); - return (json: any) => encode(json); - }, - }, - { - name: 'cbor-x', - setup: () => { - const {encode} = require('cbor-x'); - return (json: any) => encode(json); - }, - }, - { - name: 'Buffer.from(JSON.stringify)', - setup: () => { - return (json: any) => Buffer.from(JSON.stringify(json)); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.cbor.decoding.ts b/src/json-pack/__bench__/bench.cbor.decoding.ts deleted file mode 100644 index 58ec20e257..0000000000 --- a/src/json-pack/__bench__/bench.cbor.decoding.ts +++ /dev/null @@ -1,59 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.cbor.decoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborDecoderBase} from '../cbor/CborDecoderBase'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const encoder = new CborEncoder(); - -const encodedPayloads = payloads.map((payload) => { - return { - ...payload, - data: encoder.encode(payload.data), - }; -}); - -const benchmark: IBenchmark = { - name: 'CBOR Decoding', - warmup: 1000, - payloads: encodedPayloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoderBase(); - const json = decoder.read(payload as Buffer); - return deepEqual(json, data); - }, - runners: [ - { - name: 'json-joy/json-pack CborDecoder', - setup: () => { - const decoder = new CborDecoderBase(); - return (data: any) => decoder.read(data); - }, - }, - { - name: 'cbor-x', - setup: () => { - const {decode} = require('cbor-x'); - return (data: any) => decode(data); - }, - }, - { - name: 'cborg', - setup: () => { - const {decode} = require('cborg'); - return (json: any) => decode(json); - }, - }, - { - name: 'cbor', - setup: () => { - const {decode} = require('cbor'); - return (data: any) => decode(data); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.cbor.encoding.ts b/src/json-pack/__bench__/bench.cbor.encoding.ts deleted file mode 100644 index 2cca85b733..0000000000 --- a/src/json-pack/__bench__/bench.cbor.encoding.ts +++ /dev/null @@ -1,58 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.cbor.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborEncoderFast} from '../cbor/CborEncoderFast'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {deepEqual} from '../../json-equal/deepEqual'; -import {payloads} from '../../__bench__/payloads'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads: payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: 'json-joy/json-pack CborEncoderFast', - setup: () => { - const encoder = new CborEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack CborEncoder', - setup: () => { - const encoder = new CborEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'cbor-x', - setup: () => { - const {encode} = require('cbor-x'); - return (json: any) => encode(json); - }, - }, - { - name: 'cborg', - setup: () => { - const {encode} = require('cborg'); - return (json: any) => encode(json); - }, - }, - { - name: 'cbor', - setup: () => { - const {encode} = require('cbor'); - return (json: any) => encode(json); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.encoders.ts b/src/json-pack/__bench__/bench.encoders.ts deleted file mode 100644 index bd3417390d..0000000000 --- a/src/json-pack/__bench__/bench.encoders.ts +++ /dev/null @@ -1,84 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.encoders.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborEncoderFast} from '../cbor/CborEncoderFast'; -import {MsgPackEncoderFast} from '../msgpack/MsgPackEncoderFast'; -import {MsgPackEncoder} from '../msgpack/MsgPackEncoder'; -import {JsonEncoder} from '../json/JsonEncoder'; -import {UbjsonEncoder} from '../ubjson/UbjsonEncoder'; -import {IonEncoderFast} from '../ion/IonEncoderFast'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; -import {Writer} from '../../util/buffers/Writer'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: 'CborEncoderFast', - setup: () => { - const encoder = new CborEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'CborEncoder', - setup: () => { - const encoder = new CborEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'MsgPackEncoderFast', - setup: () => { - const encoder = new MsgPackEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'MsgPackEncoder', - setup: () => { - const encoder = new MsgPackEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'JsonEncoder', - setup: () => { - const encoder = new JsonEncoder(new Writer()); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'UbjsonEncoder', - setup: () => { - const encoder = new UbjsonEncoder(new Writer()); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'IonEncoderFast', - setup: () => { - const encoder = new IonEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'Buffer.from(JSON.stringify())', - setup: () => { - return (json: any) => Buffer.from(JSON.stringify(json)); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.encoding.cbor.ts b/src/json-pack/__bench__/bench.encoding.cbor.ts deleted file mode 100644 index aeb2900c54..0000000000 --- a/src/json-pack/__bench__/bench.encoding.cbor.ts +++ /dev/null @@ -1,77 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.encoding.cbor.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoderFast} from '../cbor/CborEncoderFast'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {payloads} from '../../__bench__/payloads'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - runners: [ - { - name: 'JSON.stringify()', - setup: () => { - return (json: any) => JSON.stringify(json); - }, - }, - { - name: 'Buffer.from(JSON.stringify())', - setup: () => { - return (json: any) => Buffer.from(JSON.stringify(json)); - }, - }, - { - name: 'json-joy/json-pack CborEncoderFast', - setup: () => { - const encoder = new CborEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack CborEncoder', - setup: () => { - const encoder = new CborEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'cbor-x', - setup: () => { - const {encode} = require('cbor-x'); - return (json: any) => encode(json); - }, - }, - // { - // name: 'cbor', - // setup: () => { - // const {encode} = require('cbor'); - // return (json: any) => encode(json); - // }, - // }, - { - name: 'cbor-js', - setup: () => { - const {encode} = require('cbor-js'); - return (json: any) => encode(json); - }, - }, - { - name: 'cborg', - setup: () => { - const {encode} = require('cborg'); - return (json: any) => encode(json); - }, - }, - { - name: 'cbor-sync', - setup: () => { - const {encode} = require('cbor-sync'); - return (json: any) => encode(json); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.encoding.ts b/src/json-pack/__bench__/bench.encoding.ts deleted file mode 100644 index b1f48a898f..0000000000 --- a/src/json-pack/__bench__/bench.encoding.ts +++ /dev/null @@ -1,139 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {JsonEncoder} from '../json/JsonEncoder'; -import {UbjsonEncoder} from '../ubjson/UbjsonEncoder'; -import {CborEncoderFast} from '../cbor/CborEncoderFast'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads} from '../../__bench__/payloads'; -import {MsgPackEncoderFast} from '../msgpack'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - runners: [ - { - name: 'json-joy/json-pack JsonEncoder', - setup: () => { - const writer = new Writer(); - const encoder = new JsonEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack UbjsonEncoder', - setup: () => { - const writer = new Writer(); - const encoder = new UbjsonEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '@shelacek/ubjson', - setup: () => { - const {encode} = require('@shelacek/ubjson'); - return (json: any) => encode(json); - }, - }, - { - name: 'Buffer.from(JSON.stringify())', - setup: () => { - return (json: any) => Buffer.from(JSON.stringify(json)); - }, - }, - { - name: 'json-joy/json-pack CborEncoderFast', - setup: () => { - const encoder = new CborEncoderFast(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack CborEncoder', - setup: () => { - const encoder = new CborEncoder(); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'json-joy/json-pack MsgPackEncoderFast', - setup: () => { - const encoder = new MsgPackEncoderFast(); - const jsonPack4 = encoder.encode.bind(encoder); - return (json: any) => jsonPack4(json); - }, - }, - { - name: 'JSON.stringify()', - setup: () => { - return (json: any) => JSON.stringify(json); - }, - }, - { - name: '@msgpack/msgpack', - setup: () => { - const {encode} = require('@msgpack/msgpack'); - return (json: any) => encode(json); - }, - }, - { - name: 'msgpackr', - setup: () => { - const {Packr} = require('msgpackr'); - const packr = new Packr(); - return (json: any) => packr.pack(json); - }, - }, - { - name: 'cbor-x', - setup: () => { - const {encode} = require('cbor-x'); - return (json: any) => encode(json); - }, - }, - // { - // name: 'ion-js', - // setup: () => { - // const {makeBinaryWriter, dom} = require('ion-js'); - // return (json: any) => { - // const writer = makeBinaryWriter(); - // dom.Value.from(json).writeTo(writer); - // writer.close(); - // return writer.getBytes(); - // }; - // }, - // }, - { - name: 'msgpack-lite', - setup: () => { - const {encode} = require('msgpack-lite'); - return (json: any) => encode(json); - }, - }, - { - name: 'msgpack5', - setup: () => { - const {encode} = require('msgpack5')(); - return (json: any) => encode(json); - }, - }, - // { - // name: 'cbor', - // setup: () => { - // const {encode} = require('cbor'); - // return (json: any) => encode(json); - // }, - // }, - { - name: 'messagepack', - setup: () => { - const {encode} = require('messagepack'); - return (json: any) => encode(json); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.ion.encoding.ts b/src/json-pack/__bench__/bench.ion.encoding.ts deleted file mode 100644 index b59d8b6c28..0000000000 --- a/src/json-pack/__bench__/bench.ion.encoding.ts +++ /dev/null @@ -1,48 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.ion.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {IonEncoderFast} from '../ion/IonEncoderFast'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads} from '../../__bench__/payloads'; -import {load, makeBinaryWriter, dom} from 'ion-js'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoded = load(data as any); - const json = JSON.parse(JSON.stringify(decoded)); - return deepEqual(payload, json); - }, - runners: [ - { - name: 'json-joy/json-pack IonEncoderFast', - setup: () => { - const writer = new Writer(); - const encoder = new IonEncoderFast(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'ion-js', - setup: () => { - return (json: any) => { - const writer = makeBinaryWriter(); - dom.Value.from(json).writeTo(writer); - writer.close(); - return writer.getBytes(); - }; - }, - }, - // { - // name: 'Buffer.from(JSON.stringify())', - // setup: () => { - // return (json: any) => Buffer.from(JSON.stringify(json)); - // }, - // }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.json.decoding.ts b/src/json-pack/__bench__/bench.json.decoding.ts deleted file mode 100644 index e9acf61e59..0000000000 --- a/src/json-pack/__bench__/bench.json.decoding.ts +++ /dev/null @@ -1,47 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.json.decoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {JsonDecoder} from '../json/JsonDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const encodedPayloads = payloads.map((payload) => { - return { - ...payload, - data: Buffer.from(JSON.stringify(payload.data)), - }; -}); - -const benchmark: IBenchmark = { - name: 'Decoding JSON', - warmup: 1000, - payloads: encodedPayloads, - test: (payload: unknown, data: unknown): boolean => { - const json = JSON.parse((payload as Buffer).toString()); - return deepEqual(json, data); - }, - runners: [ - { - name: 'json-joy/json-pack JsonDecoder.decode()', - setup: () => { - const decoder = new JsonDecoder(); - return (json: any) => decoder.read(json); - }, - }, - { - name: 'Native JSON.parse(buf.toString())', - setup: () => { - return (buf: any) => JSON.parse(buf.toString()); - }, - }, - { - name: 'sjson.parse()', - setup: () => { - const sjson = require('secure-json-parse'); - return (buf: any) => sjson.parse(buf.toString()); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.json.encoding.ts b/src/json-pack/__bench__/bench.json.encoding.ts deleted file mode 100644 index a046b4fc78..0000000000 --- a/src/json-pack/__bench__/bench.json.encoding.ts +++ /dev/null @@ -1,53 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.json.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {JsonEncoder} from '../json/JsonEncoder'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; -const safeStringify = require('fast-safe-stringify'); - -const benchmark: IBenchmark = { - name: 'Encoding JSON', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const buf = Buffer.from(data as Uint8Array | Buffer); - const json = JSON.parse(buf.toString()); - return deepEqual(payload, json); - }, - runners: [ - { - name: 'json-joy/json-pack JsonEncoder.encode()', - setup: () => { - const writer = new Writer(); - const encoder = new JsonEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'Native Buffer.from(JSON.stringify())', - setup: () => { - return (json: any) => Buffer.from(JSON.stringify(json)); - }, - }, - { - name: 'fast-safe-stringify Buffer.from(stringify())', - setup: () => { - return (json: any) => { - return Buffer.from(safeStringify(json)); - }; - }, - }, - { - name: 'fast-safe-stringify Buffer.from(stableStringify())', - setup: () => { - return (json: any) => { - return Buffer.from(safeStringify.stableStringify(json)); - }; - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.msgpack.decoding.ts b/src/json-pack/__bench__/bench.msgpack.decoding.ts deleted file mode 100644 index 3541724ad3..0000000000 --- a/src/json-pack/__bench__/bench.msgpack.decoding.ts +++ /dev/null @@ -1,82 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.msgpack.decoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {MsgPackEncoderFast} from '../msgpack/MsgPackEncoderFast'; -import {MsgPackDecoderFast} from '../msgpack/MsgPackDecoderFast'; -import {MsgPackDecoder} from '../msgpack/MsgPackDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const encoder = new MsgPackEncoderFast(); - -const encodedPayloads = payloads.map((payload) => { - return { - ...payload, - data: encoder.encode(payload.data), - }; -}); - -const benchmark: IBenchmark = { - name: 'MessagePack Decoding', - warmup: 1000, - payloads: encodedPayloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new MsgPackDecoderFast(); - const json = decoder.read(payload as Buffer); - return deepEqual(json, data); - }, - runners: [ - { - name: 'json-joy/json-pack MsgPackDecoderFast', - setup: () => { - const decoder = new MsgPackDecoderFast(); - return (data: any) => decoder.read(data); - }, - }, - { - name: 'json-joy/json-pack MsgPackDecoder', - setup: () => { - const decoder = new MsgPackDecoder(); - return (data: any) => decoder.read(data); - }, - }, - { - name: 'msgpackr', - setup: () => { - const {unpack} = require('msgpackr'); - return (data: any) => unpack(data); - }, - }, - { - name: '@msgpack/msgpack', - setup: () => { - const {Decoder} = require('@msgpack/msgpack'); - const decoder = new Decoder(); - return (data: any) => decoder.decode(data); - }, - }, - { - name: 'msgpack-lite', - setup: () => { - const {decode} = require('msgpack-lite'); - return (data: any) => decode(data); - }, - }, - { - name: 'msgpack5', - setup: () => { - const {decode} = require('msgpack5')(); - return (data: any) => decode(data); - }, - }, - { - name: 'messagepack', - setup: () => { - const {decode} = require('messagepack'); - return (data: any) => decode(data); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.msgpack.encoding.ts b/src/json-pack/__bench__/bench.msgpack.encoding.ts deleted file mode 100644 index c4b74e9bd6..0000000000 --- a/src/json-pack/__bench__/bench.msgpack.encoding.ts +++ /dev/null @@ -1,73 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.msgpack.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {MsgPackEncoder} from '../msgpack/MsgPackEncoder'; -import {MsgPackEncoderFast} from '../msgpack/MsgPackEncoderFast'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const benchmark: IBenchmark = { - name: 'MessagePack Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: 'json-joy/json-pack MsgPackEncoderFast', - setup: () => { - const encoder = new MsgPackEncoderFast(); - return (data: any) => encoder.encode(data); - }, - }, - { - name: 'json-joy/json-pack MsgPackEncoder', - setup: () => { - const encoder = new MsgPackEncoder(); - return (data: any) => encoder.encode(data); - }, - }, - { - name: 'msgpackr', - setup: () => { - const {pack} = require('msgpackr'); - return (data: any) => pack(data); - }, - }, - { - name: '@msgpack/msgpack', - setup: () => { - const {Encoder} = require('@msgpack/msgpack'); - const encoder = new Encoder(); - return (data: any) => encoder.encode(data); - }, - }, - { - name: 'msgpack-lite', - setup: () => { - const {encode} = require('msgpack-lite'); - return (data: any) => encode(data); - }, - }, - { - name: 'msgpack5', - setup: () => { - const {encode} = require('msgpack5')(); - return (data: any) => encode(data); - }, - }, - { - name: 'messagepack', - setup: () => { - const {encode} = require('messagepack'); - return (data: any) => encode(data); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.resp.decoding.ts b/src/json-pack/__bench__/bench.resp.decoding.ts deleted file mode 100644 index 4725765732..0000000000 --- a/src/json-pack/__bench__/bench.resp.decoding.ts +++ /dev/null @@ -1,70 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.resp.decoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {RespEncoder} from '../resp/RespEncoder'; -import {RespDecoder} from '../resp/RespDecoder'; -import {RespStreamingDecoder} from '../resp/RespStreamingDecoder'; - -const encoder = new RespEncoder(); -const data = encoder.encode(['set', 'production:project-name:keys:foobarbaz', 'PX', 'NX', 'EX', 60000, 'KEEPTTL']); - -const benchmark: IBenchmark = { - name: 'Decoding RESP', - warmup: 1000, - payloads: [ - { - name: 'short array', - data, - }, - ], - runners: [ - { - name: 'json-joy/json-pack RespDecoder', - setup: () => { - const decoder = new RespDecoder(); - return (data: any) => { - decoder.read(data); - }; - }, - }, - { - name: 'json-joy/json-pack RespStreamingDecoder', - setup: () => { - const decoder = new RespStreamingDecoder(); - return (data: any) => { - decoder.push(data); - decoder.read(); - }; - }, - }, - { - name: 'redis-parser', - setup: () => { - const Parser = require('redis-parser'); - let result: unknown; - const parser = new Parser({ - returnReply(reply: any, b: any, c: any) { - result = reply; - }, - returnError(err: any) { - result = err; - }, - returnFatalError(err: any) { - result = err; - }, - returnBuffers: false, - stringNumbers: false, - }); - const parse = (uint8: Uint8Array): unknown => { - parser.execute(Buffer.from(uint8)); - return result; - }; - return (data: any) => { - parse(data); - }; - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.resp.encoding.ts b/src/json-pack/__bench__/bench.resp.encoding.ts deleted file mode 100644 index 199c2cb5fb..0000000000 --- a/src/json-pack/__bench__/bench.resp.encoding.ts +++ /dev/null @@ -1,52 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.resp.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {RespEncoder} from '../resp/RespEncoder'; -import encodeCommand from '@redis/client/dist/lib/client/RESP2/encoder'; - -const data = ['set', 'production:project-name:keys:foobarbaz', 'PX', 'NX', 'EX', '60000', 'KEEPTTL']; -const redisClientEncode = (cmd: string[]) => { - const list = encodeCommand(data); - return Buffer.from(list.join('')); -}; - -const benchmark: IBenchmark = { - name: 'Encoding RESP', - warmup: 1000, - payloads: [ - { - name: 'short array', - data, - }, - ], - runners: [ - { - name: 'json-joy/json-pack RespEncoder.encode()', - setup: () => { - const encoder = new RespEncoder(); - return (data: any) => { - encoder.encode(data); - }; - }, - }, - { - name: 'json-joy/json-pack RespEncoder.encodeCmd()', - setup: () => { - const encoder = new RespEncoder(); - return (data: any) => { - encoder.encodeCmd(data); - }; - }, - }, - { - name: '@redis/client', - setup: () => { - return (data: any) => { - redisClientEncode(data); - }; - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.shallow-read.ts b/src/json-pack/__bench__/bench.shallow-read.ts deleted file mode 100644 index a46959003d..0000000000 --- a/src/json-pack/__bench__/bench.shallow-read.ts +++ /dev/null @@ -1,118 +0,0 @@ -import {runBenchmark} from '../../__bench__/runBenchmark'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {MsgPackEncoderFast} from '../msgpack'; -import {MsgPackDecoder} from '../msgpack/MsgPackDecoder'; -import {genShallowReader} from '../msgpack/shallow-read'; - -const benchmark = { - name: 'Encoding', - warmup: 10000, - payloads: [ - { - name: (json: any) => `Typical object, ${JSON.stringify(json).length} bytes`, - data: require('../../__bench__/data/json2'), - test: () => 'Sports ๐Ÿ€', - }, - ], - runners: [ - { - name: 'JSON.parse()', - setup: (json: any) => { - const doc = JSON.stringify(json); - return () => { - const parsed = JSON.parse(doc); - return parsed[5]?.value?.json?.tags[1]; - }; - }, - }, - { - name: 'msgpackr', - setup: (json: any) => { - const {decode} = require('msgpackr'); - const encoder = new MsgPackEncoderFast(); - const doc = encoder.encode(json); - return () => { - const parsed = decode(doc); - return parsed[5]?.value?.json?.tags[1]; - }; - }, - }, - { - name: 'cbor-x', - setup: (json: any) => { - const {decode} = require('cbor-x'); - const encoder = new CborEncoder(); - const doc = encoder.encode(json); - return () => { - const parsed = decode(doc); - return parsed[5]?.value?.json?.tags[1]; - }; - }, - }, - { - name: 'MsgPackDecoder', - setup: (json: any) => { - const encoder = new MsgPackEncoderFast(); - const doc = encoder.encode(json); - const decoder = new MsgPackDecoder(); - return () => { - const parsed = decoder.decode(doc) as any; - return parsed[5]?.value?.json?.tags[1]; - }; - }, - }, - { - name: 'CborDecoder', - setup: (json: any) => { - const encoder = new CborEncoder(); - const doc = encoder.encode(json); - const decoder = new CborDecoder(); - return () => { - const parsed = decoder.decode(doc) as any; - return parsed[5]?.value?.json?.tags[1]; - }; - }, - }, - { - name: 'MsgPackDecoder.{findKey,findIndex}()', - setup: (json: any) => { - const encoder = new MsgPackEncoderFast(); - const doc = encoder.encode(json); - const decoder = new MsgPackDecoder(); - return () => { - decoder.reader.reset(doc); - return decoder.findIndex(5).findKey('value').findKey('json').findKey('tags').findIndex(1).val(); - }; - }, - }, - { - name: 'MsgPackDecoder.find()', - setup: (json: any) => { - const encoder = new MsgPackEncoderFast(); - const doc = encoder.encode(json); - const decoder = new MsgPackDecoder(); - return () => { - decoder.reader.reset(doc); - return decoder.find([5, 'value', 'json', 'tags', 1]).val(); - }; - }, - }, - { - name: 'genShallowReader()(MsgPackDecoder)', - setup: (json: any) => { - const encoder = new MsgPackEncoderFast(); - const doc = encoder.encode(json); - const fn = genShallowReader([5, 'value', 'json', 'tags', 1]); - const decoder = new MsgPackDecoder(); - return () => { - decoder.reader.reset(doc); - fn(decoder); - return decoder.val(); - }; - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.slice.ts b/src/json-pack/__bench__/bench.slice.ts deleted file mode 100644 index b837f5e77d..0000000000 --- a/src/json-pack/__bench__/bench.slice.ts +++ /dev/null @@ -1,36 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.slice.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const encoder = new CborEncoder(); - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: 'Uint8Array', - setup: () => { - return (json: any) => encoder.encode(json); - }, - }, - { - name: 'Slice', - setup: () => { - return (json: any) => encoder.encodeToSlice(json); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.ubjson.decoding.ts b/src/json-pack/__bench__/bench.ubjson.decoding.ts deleted file mode 100644 index d1f7cd4942..0000000000 --- a/src/json-pack/__bench__/bench.ubjson.decoding.ts +++ /dev/null @@ -1,44 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.ubjson.decoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {UbjsonEncoder} from '../ubjson/UbjsonEncoder'; -import {UbjsonDecoder} from '../ubjson/UbjsonDecoder'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; - -const encoder = new UbjsonEncoder(new Writer()); -const encodedPayloads = payloads.map((payload) => { - return { - ...payload, - data: encoder.encode(payload.data), - }; -}); - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads: encodedPayloads, - test: (payload: unknown, data: unknown): boolean => { - const encoded = encoder.encode(data); - return deepEqual(encoded, payload); - }, - runners: [ - { - name: 'json-joy/json-pack UbjsonDecoder', - setup: () => { - const decoder = new UbjsonDecoder(); - return (data: any) => decoder.read(data); - }, - }, - { - name: '@shelacek/ubjson', - setup: () => { - const {decode} = require('@shelacek/ubjson'); - return (data: any) => decode(data); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.ubjson.encoding.ts b/src/json-pack/__bench__/bench.ubjson.encoding.ts deleted file mode 100644 index 7724613379..0000000000 --- a/src/json-pack/__bench__/bench.ubjson.encoding.ts +++ /dev/null @@ -1,37 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.ubjson.encoding.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {UbjsonEncoder} from '../ubjson/UbjsonEncoder'; -import {Writer} from '../../util/buffers/Writer'; -import {payloads} from '../../__bench__/payloads'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - runners: [ - { - name: 'json-joy/json-pack UbjsonEncoder', - setup: () => { - const writer = new Writer(); - const encoder = new UbjsonEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '@shelacek/ubjson', - setup: () => { - const {encode} = require('@shelacek/ubjson'); - return (json: any) => encode(json); - }, - }, - // { - // name: 'Buffer.from(JSON.stringify())', - // setup: () => { - // return (json: any) => Buffer.from(JSON.stringify(json)); - // }, - // }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/bench.writer-size.ts b/src/json-pack/__bench__/bench.writer-size.ts deleted file mode 100644 index f8f249cb51..0000000000 --- a/src/json-pack/__bench__/bench.writer-size.ts +++ /dev/null @@ -1,71 +0,0 @@ -// npx ts-node src/json-pack/__bench__/bench.writer-size.ts - -import {runBenchmark, IBenchmark} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {payloads} from '../../__bench__/payloads'; -import {deepEqual} from '../../json-equal/deepEqual'; -import {Writer} from '../../util/buffers/Writer'; - -const benchmark: IBenchmark = { - name: 'Encoding', - warmup: 1000, - payloads, - test: (payload: unknown, data: unknown): boolean => { - const decoder = new CborDecoder(); - const decoded = decoder.read(data as any); - return deepEqual(decoded, payload); - }, - runners: [ - { - name: '1 MB', - setup: () => { - const writer = new Writer(1024 * 256 * 4); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '256 KB', - setup: () => { - const writer = new Writer(1024 * 256); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '64 KB', - setup: () => { - const writer = new Writer(1024 * 64); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '16 KB', - setup: () => { - const writer = new Writer(1024 * 16); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '4 KB', - setup: () => { - const writer = new Writer(1024 * 4); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - { - name: '1 KB', - setup: () => { - const writer = new Writer(1024); - const encoder = new CborEncoder(writer); - return (json: any) => encoder.encode(json); - }, - }, - ], -}; - -runBenchmark(benchmark); diff --git a/src/json-pack/__bench__/profiler/cbor-decoding.ts b/src/json-pack/__bench__/profiler/cbor-decoding.ts deleted file mode 100644 index ac8e3fe2cd..0000000000 --- a/src/json-pack/__bench__/profiler/cbor-decoding.ts +++ /dev/null @@ -1,20 +0,0 @@ -// NODE_ENV=production node --prof -r ts-node/register src/json-pack/__bench__/profiler/cbor-decoding.ts -// node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt - -import {CborEncoder} from '../../cbor/CborEncoder'; -import {CborDecoder} from '../../cbor/CborDecoder'; - -const payload = [ - 0, 1, 2, 333, -333, 44444, -55555, 556666, -6666666, 62343423432, 0.123, 0.0, -123.3434343, 127, 128, 129, 255, 256, - 257, 258, 1000, 1000, 1000, -222222, -22222, 0xff, 0xfe, 0x100, 0x101, - // 0xffff, 0xfffe, 0x10000, -0x7f, -0x80, -0x81, -0x100, -0x101, -0x10000, - // 0xffffffff, 0xfffffffe, 0x100000000, 0x100000001, 0xffffffffffffffff, - // 0xfffffffffffffffe, 0x10000000000000000, 0x10000000000000001, - // 0x100000000000000000, 0x100000000000000001, 0x1000000000000000000, -]; -const encoded = new CborEncoder().encode(payload); -const decoder = new CborDecoder(); - -for (let i = 0; i < 10e6; i++) { - decoder.read(encoded); -} diff --git a/src/json-pack/__bench__/profiler/slices.ts b/src/json-pack/__bench__/profiler/slices.ts deleted file mode 100644 index 32c8881ecd..0000000000 --- a/src/json-pack/__bench__/profiler/slices.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* tslint:disable no-console */ - -const iterations = 10000000; -const buf = new ArrayBuffer(1024 * 4); -const arr = new Uint8Array(buf); -const arr2 = Buffer.from(buf); -const arr3 = Buffer.allocUnsafe(1024 * 4); -const FastBuffer = (Buffer as any)[Symbol.species] as any; - -class Slice { - constructor( - public uint8: ArrayBuffer, - public start: number, - public end: number, - ) {} -} - -const res = { - end: (() => {}) as any, -}; - -console.time('res.end(buf, offset, length)'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(buf, pos, pos + 1); -} -console.timeEnd('res.end(buf, offset, length)'); - -console.time('new Slice()'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(new Slice(buf, pos, pos + 1)); -} -console.timeEnd('new Slice()'); - -console.time('new FastBuffer()'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(new FastBuffer(buf, pos, 1)); -} -console.timeEnd('new FastBuffer()'); - -console.time('new Uint8Array()'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(new Uint8Array(buf, pos, 1)); -} -console.timeEnd('new Uint8Array()'); - -console.time('Uint8Array.prototype.subarray()'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(arr.subarray(pos, pos + 1)); -} -console.timeEnd('Uint8Array.prototype.subarray()'); - -console.time('Buffer.prototype.subarray()'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(arr2.subarray(pos, pos + 1)); -} -console.timeEnd('Buffer.prototype.subarray()'); - -console.time('Buffer.prototype.subarray() - 2'); -for (let i = 0; i < iterations; i++) { - const pos = i % 1024; - res.end(arr3.subarray(pos, pos + 1)); -} -console.timeEnd('Buffer.prototype.subarray() - 2'); diff --git a/src/json-pack/__bench__/profiler/time.ts b/src/json-pack/__bench__/profiler/time.ts deleted file mode 100644 index 563ac5bc21..0000000000 --- a/src/json-pack/__bench__/profiler/time.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* tslint:disable no-console */ - -import {MsgPackEncoderFast} from '../../msgpack/MsgPackEncoderFast'; -import {Writer} from '../../../util/buffers/Writer'; - -const json = [1234]; - -const writer = new Writer(); -const encoder = new MsgPackEncoderFast(); - -const arr = new ArrayBuffer(1024 * 4); -const uint8 = new Uint8Array(arr); -const buf = Buffer.alloc(1024 * 4); - -console.time('loop'); -for (let i = 0; i < 10000000; i++) { - writer.utf8('asdf'); - writer.u8(123); - // writer.u8u32(123, 123123); - writer.reset(); - // writer.flush(); - // arr.slice(i % 1024, i % 1024 + 1); - // buf.slice(i % 1024, i % 1024 + 1); - // (buf as any).hexSlice(i % 1024, i % 1024 + 1); - // const pos = i % 1024; - // new Slice(uint8, pos, pos + 1); - // uint8.subarray(pos, pos + 1); - // new Uint8Array(arr.buffer, arr.byteOffset + pos, 1); - // arr.slice(pos, pos + 1); -} -console.timeEnd('loop'); diff --git a/src/json-pack/__bench__/profiler/truncate.ts b/src/json-pack/__bench__/profiler/truncate.ts deleted file mode 100644 index 8c8b0e9e3f..0000000000 --- a/src/json-pack/__bench__/profiler/truncate.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* tslint:disable no-console */ - -const i = 10000; -class MySlice { - constructor( - public uint8: ArrayBuffer, - public start: number, - public end: number, - ) {} -} - -console.time('new Slice()'); -for (let i = 0; i < iterations; i++) { - const buf = new ArrayBuffer(1024 * 4); - for (let j = 1024; j > 0; j--) { - new Slice(buf, 0, j); - } -} -console.timeEnd('new Slice()'); - -console.time('new Uint8Array()'); -for (let i = 0; i < iterations; i++) { - const buf = new ArrayBuffer(1024 * 4); - for (let j = 1024; j > 0; j--) { - new Uint8Array(buf, 0, j); - } -} -console.timeEnd('new Uint8Array()'); - -console.time('ArrayBuffer.prototype.resize'); -for (let i = 0; i < iterations; i++) { - const buf = new (ArrayBuffer as any)(1024 * 4, {maxByteLength: 1024 * 4}); - for (let j = 1024; j > 0; j--) { - buf.resize(j); - } -} -console.timeEnd('ArrayBuffer.prototype.resize'); diff --git a/src/json-pack/__demos__/cbor.ts b/src/json-pack/__demos__/cbor.ts deleted file mode 100644 index f7e44e7184..0000000000 --- a/src/json-pack/__demos__/cbor.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* tslint:disable no-console */ - -/** - * Run this demo with: - * - * npx nodemon -q -x npx ts-node src/json-pack/__demos__/cbor.ts - */ - -import {CborEncoder} from '../cbor/CborEncoder'; -import {CborDecoder} from '../cbor/CborDecoder'; -import {CborDecoderBase} from '../cbor/CborDecoderBase'; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); -const decoderBase = new CborDecoderBase(); - -const pojo = { - id: 123, - foo: 'bar', - tags: ['a', 'b', 'c'], - nested: { - a: 1, - b: 2, - level2: { - c: 3, - }, - }, -}; - -console.clear(); - -console.log('--------------------------------------------------'); -console.log('Encoding CBOR:'); -const encoded = encoder.encode(pojo); -console.log(encoded); - -console.log('--------------------------------------------------'); -console.log('Decoding CBOR:'); -const decoded = decoderBase.read(encoded); -console.log(decoded); - -console.log('--------------------------------------------------'); -console.log('Retrieving values without parsing:'); -decoder.reader.reset(encoded); -const id = decoder.find(['id']).val(); -decoder.reader.reset(encoded); -const foo = decoder.find(['foo']).val(); -decoder.reader.reset(encoded); -const secondTag = decoder.find(['tags', 1]).val(); -decoder.reader.reset(encoded); -const nested = decoder.find(['nested', 'level2', 'c']).val(); -console.log('id:', id, 'foo:', foo, 'secondTag:', secondTag, 'nested:', nested); - -console.log('--------------------------------------------------'); -console.log('Asserting by value type:'); -decoder.reader.reset(encoded); -const tagAsString = decoder.find(['tags', 1]).readPrimitiveOrVal(); -console.log({tagAsString}); - -console.log('--------------------------------------------------'); -console.log('Parsing only one level:'); -const decodedLevel = decoder.decodeLevel(encoded); -console.log(decodedLevel); diff --git a/src/json-pack/__demos__/json.ts b/src/json-pack/__demos__/json.ts deleted file mode 100644 index 44c4c63914..0000000000 --- a/src/json-pack/__demos__/json.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* tslint:disable no-console */ - -/** - * Run this demo with: - * - * npx nodemon -q -x npx ts-node src/json-pack/__demos__/json.ts - */ - -import {JsonEncoder} from '../json/JsonEncoder'; -import {JsonDecoder} from '../json/JsonDecoder'; -import {Writer} from '../../util/buffers/Writer'; - -const encoder = new JsonEncoder(new Writer()); -const decoder = new JsonDecoder(); - -const pojo = { - id: 123, - foo: 'bar', - tags: ['a', 'b', 'c'], - binary: new Uint8Array([1, 2, 3]), -}; - -console.clear(); - -console.log('--------------------------------------------------'); -console.log('Encoding JSON:'); -const encoded = encoder.encode(pojo); -console.log(encoded); - -console.log('--------------------------------------------------'); -console.log('Decoding JSON:'); -const decoded = decoder.read(encoded); -console.log(decoded); - -console.log('--------------------------------------------------'); -console.log('Binary data:'); -const blob = encoder.encode({binary: new Uint8Array([1, 2, 3])}); -console.log(Buffer.from(blob).toString()); diff --git a/src/json-pack/__demos__/msgpack.ts b/src/json-pack/__demos__/msgpack.ts deleted file mode 100644 index 552a932887..0000000000 --- a/src/json-pack/__demos__/msgpack.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* tslint:disable no-console */ - -/** - * Run this demo with: - * - * npx nodemon -q -x npx ts-node src/json-pack/__demos__/msgpack.ts - */ - -import {MsgPackEncoder} from '../msgpack/MsgPackEncoder'; -import {MsgPackDecoder} from '../msgpack/MsgPackDecoder'; - -const encoder = new MsgPackEncoder(); -const decoder = new MsgPackDecoder(); - -const pojo = { - id: 123, - foo: 'bar', - tags: ['a', 'b', 'c'], - nested: { - a: 1, - b: 2, - level2: { - c: 3, - }, - }, -}; - -console.clear(); - -console.log('--------------------------------------------------'); -console.log('Encoding MessagePack:'); -const encoded = encoder.encode(pojo); -console.log(encoded); - -console.log('--------------------------------------------------'); -console.log('Retrieving values without parsing:'); -decoder.reader.reset(encoded); -const id = decoder.find(['id']).val(); -decoder.reader.reset(encoded); -const foo = decoder.find(['foo']).val(); -decoder.reader.reset(encoded); -const secondTag = decoder.find(['tags', 1]).val(); -decoder.reader.reset(encoded); -const nested = decoder.find(['nested', 'level2', 'c']).val(); -console.log('id:', id, 'foo:', foo, 'secondTag:', secondTag, 'nested:', nested); diff --git a/src/json-pack/__demos__/ubjson.ts b/src/json-pack/__demos__/ubjson.ts deleted file mode 100644 index 4f1dfa70a7..0000000000 --- a/src/json-pack/__demos__/ubjson.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* tslint:disable no-console */ - -/** - * Run this demo with: - * - * npx nodemon -q -x npx ts-node src/json-pack/__demos__/ubjson.ts - */ - -import {UbjsonEncoder} from '../ubjson/UbjsonEncoder'; -import {UbjsonDecoder} from '../ubjson/UbjsonDecoder'; -import {Writer} from '../../util/buffers/Writer'; - -const encoder = new UbjsonEncoder(new Writer()); -const decoder = new UbjsonDecoder(); - -const pojo = { - id: 123, - foo: 'bar', - tags: ['a', 'b', 'c'], - binary: new Uint8Array([1, 2, 3]), -}; - -console.clear(); - -console.log('--------------------------------------------------'); -console.log('Encoding UBJSON:'); -const encoded = encoder.encode(pojo); -console.log(encoded); - -console.log('--------------------------------------------------'); -console.log('Decoding UBJSON:'); -const decoded = decoder.read(encoded); -console.log(decoded); - -console.log('--------------------------------------------------'); -console.log('Binary data:'); -const blob = encoder.encode({binary: new Uint8Array([1, 2, 3])}); -console.log(Buffer.from(blob).toString()); diff --git a/src/json-pack/bencode/BencodeDecoder.ts b/src/json-pack/bencode/BencodeDecoder.ts deleted file mode 100644 index ecfb352698..0000000000 --- a/src/json-pack/bencode/BencodeDecoder.ts +++ /dev/null @@ -1,151 +0,0 @@ -import {Reader} from '../../util/buffers/Reader'; -import type {BinaryJsonDecoder, PackValue} from '../types'; - -export class BencodeDecoder implements BinaryJsonDecoder { - public reader = new Reader(); - - public read(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.readAny(); - } - - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.readAny(); - } - - public readAny(): unknown { - const reader = this.reader; - const x = reader.x; - const uint8 = reader.uint8; - const char = uint8[x]; - switch (char) { - case 0x69: // i - return this.readNum(); - case 0x64: // d - return this.readObj(); - case 0x6c: // l - return this.readArr(); - case 0x66: // f - return this.readFalse(); - case 0x74: // t - return this.readTrue(); - case 110: // n - return this.readNull(); - case 117: // u - return this.readUndef(); - default: - if (char >= 48 && char <= 57) return this.readBin(); - } - throw new Error('INVALID_BENCODE'); - } - - public readNull(): null { - if (this.reader.u8() !== 0x6e) throw new Error('INVALID_BENCODE'); - return null; - } - - public readUndef(): undefined { - if (this.reader.u8() !== 117) throw new Error('INVALID_BENCODE'); - return undefined; - } - - public readTrue(): true { - if (this.reader.u8() !== 0x74) throw new Error('INVALID_BENCODE'); - return true; - } - - public readFalse(): false { - if (this.reader.u8() !== 0x66) throw new Error('INVALID_BENCODE'); - return false; - } - - public readBool(): unknown { - const reader = this.reader; - switch (reader.uint8[reader.x]) { - case 0x66: // f - return this.readFalse(); - case 0x74: // t - return this.readTrue(); - default: - throw new Error('INVALID_BENCODE'); - } - } - - public readNum(): number { - const reader = this.reader; - const startChar = reader.u8(); - if (startChar !== 0x69) throw new Error('INVALID_BENCODE'); - const u8 = reader.uint8; - let x = reader.x; - let numStr = ''; - let c = u8[x++]; - let i = 0; - while (c !== 0x65) { - numStr += String.fromCharCode(c); - c = u8[x++]; - if (i > 25) throw new Error('INVALID_BENCODE'); - i++; - } - if (!numStr) throw new Error('INVALID_BENCODE'); - reader.x = x; - return +numStr; - } - - public readStr(): string { - const bin = this.readBin(); - return new TextDecoder().decode(bin); - } - - public readBin(): Uint8Array { - const reader = this.reader; - const u8 = reader.uint8; - let lenStr = ''; - let x = reader.x; - let c = u8[x++]; - let i = 0; - while (c !== 0x3a) { - if (c < 48 || c > 57) throw new Error('INVALID_BENCODE'); - lenStr += String.fromCharCode(c); - c = u8[x++]; - if (i > 10) throw new Error('INVALID_BENCODE'); - i++; - } - reader.x = x; - const len = +lenStr; - const bin = reader.buf(len); - return bin; - } - - public readArr(): unknown[] { - const reader = this.reader; - if (reader.u8() !== 0x6c) throw new Error('INVALID_BENCODE'); - const arr: unknown[] = []; - const uint8 = reader.uint8; - while (true) { - const char = uint8[reader.x]; - if (char === 0x65) { - reader.x++; - return arr; - } - arr.push(this.readAny()); - } - } - - public readObj(): PackValue | Record | unknown { - const reader = this.reader; - if (reader.u8() !== 0x64) throw new Error('INVALID_BENCODE'); - const obj: Record = {}; - const uint8 = reader.uint8; - while (true) { - const char = uint8[reader.x]; - if (char === 0x65) { - reader.x++; - return obj; - } - const key = this.readStr(); - if (key === '__proto__') throw new Error('INVALID_KEY'); - obj[key] = this.readAny(); - } - } -} diff --git a/src/json-pack/bencode/BencodeEncoder.ts b/src/json-pack/bencode/BencodeEncoder.ts deleted file mode 100644 index 61ff9b76d9..0000000000 --- a/src/json-pack/bencode/BencodeEncoder.ts +++ /dev/null @@ -1,164 +0,0 @@ -import {utf8Size} from '../../util/strings/utf8'; -import {sort} from '../../util/sort/insertion'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder} from '../types'; - -export class BencodeEncoder implements BinaryJsonEncoder { - constructor(public readonly writer: IWriter & IWriterGrowable) {} - - public encode(value: unknown): Uint8Array { - const writer = this.writer; - writer.reset(); - this.writeAny(value); - return writer.flush(); - } - - /** - * Called when the encoder encounters a value that it does not know how to encode. - * - * @param value Some JavaScript value. - */ - public writeUnknown(value: unknown): void { - this.writeNull(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'boolean': - return this.writeBoolean(value); - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'object': { - if (value === null) return this.writeNull(); - const constructor = value.constructor; - switch (constructor) { - case Object: - return this.writeObj(value as Record); - case Array: - return this.writeArr(value as unknown[]); - case Uint8Array: - return this.writeBin(value as Uint8Array); - case Map: - return this.writeMap(value as Map); - case Set: - return this.writeSet(value as Set); - default: - return this.writeUnknown(value); - } - } - case 'bigint': { - return this.writeBigint(value); - } - case 'undefined': { - return this.writeUndef(); - } - default: - return this.writeUnknown(value); - } - } - - public writeNull(): void { - this.writer.u8(110); // 'n' - } - - public writeUndef(): void { - this.writer.u8(117); // 'u' - } - - public writeBoolean(bool: boolean): void { - this.writer.u8(bool ? 0x74 : 0x66); // 't' or 'f' - } - - public writeNumber(num: number): void { - const writer = this.writer; - writer.u8(0x69); // 'i' - writer.ascii(Math.round(num) + ''); - writer.u8(0x65); // 'e' - } - - public writeInteger(int: number): void { - const writer = this.writer; - writer.u8(0x69); // 'i' - writer.ascii(int + ''); - writer.u8(0x65); // 'e' - } - - public writeUInteger(uint: number): void { - this.writeInteger(uint); - } - - public writeFloat(float: number): void { - this.writeNumber(float); - } - - public writeBigint(int: bigint): void { - const writer = this.writer; - writer.u8(0x69); // 'i' - writer.ascii(int + ''); - writer.u8(0x65); // 'e' - } - - public writeBin(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.ascii(length + ''); - writer.u8(0x3a); // ':' - writer.buf(buf, length); - } - - public writeStr(str: string): void { - const writer = this.writer; - const length = utf8Size(str); - writer.ascii(length + ''); - writer.u8(0x3a); // ':' - writer.ensureCapacity(length); - writer.utf8(str); - } - - public writeAsciiStr(str: string): void { - const writer = this.writer; - writer.ascii(str.length + ''); - writer.u8(0x3a); // ':' - writer.ascii(str); - } - - public writeArr(arr: unknown[]): void { - const writer = this.writer; - writer.u8(0x6c); // 'l' - const length = arr.length; - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - writer.u8(0x65); // 'e' - } - - public writeObj(obj: Record): void { - const writer = this.writer; - writer.u8(0x64); // 'd' - const keys = sort(Object.keys(obj)); - const length = keys.length; - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - writer.u8(0x65); // 'e' - } - - public writeMap(obj: Map): void { - const writer = this.writer; - writer.u8(0x64); // 'd' - const keys = sort([...obj.keys()]); - const length = keys.length; - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key + ''); - this.writeAny(obj.get(key)); - } - writer.u8(0x65); // 'e' - } - - public writeSet(set: Set): void { - this.writeArr([...set.values()]); - } -} diff --git a/src/json-pack/bencode/README.md b/src/json-pack/bencode/README.md deleted file mode 100644 index 011fb12013..0000000000 --- a/src/json-pack/bencode/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Bencode codecs - -Implements [Bencode][bencode] encoder and decoder. - -[bencode]: https://en.wikipedia.org/wiki/Bencode - -Type coercion: - -- Strings and `Uint8Array` are encoded as Bencode byte strings, decoded as `Uint8Array`. -- `Object` and `Map` are encoded as Bencode dictionaries, decoded as `Object`. -- `Array` and `Set` are encoded as Bencode lists, decoded as `Array`. -- `number` and `bigint` are encoded as Bencode integers, decoded as `number`. -- Float `number` are rounded and encoded as Bencode integers, decoded as `number`. - - -## Extensions - -This codec extends the Bencode specification to support the following types: - -- `null` (encoded as `n`) -- `undefined` (encoded as `u`) -- `boolean` (encoded as `t` for `true` and `f` for `false`) diff --git a/src/json-pack/bencode/__tests__/BencodeDecoder.spec.ts b/src/json-pack/bencode/__tests__/BencodeDecoder.spec.ts deleted file mode 100644 index 122f1e3d7e..0000000000 --- a/src/json-pack/bencode/__tests__/BencodeDecoder.spec.ts +++ /dev/null @@ -1,271 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {BencodeEncoder} from '../BencodeEncoder'; -import {BencodeDecoder} from '../BencodeDecoder'; -import {utf8} from '../../../util/buffers/strings'; - -const decoder = new BencodeDecoder(); - -describe('null', () => { - test('null', () => { - const data = utf8`n`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(null); - }); -}); - -describe('undefined', () => { - test('undefined', () => { - const encoder = new BencodeEncoder(new Writer()); - const encoded = encoder.encode(undefined); - const decoded = decoder.read(encoded); - expect(decoded).toBe(undefined); - }); - - test('undefined in array', () => { - const encoder = new BencodeEncoder(new Writer()); - const encoded = encoder.encode({foo: [1, undefined, -1]}); - const decoded = decoder.read(encoded); - expect(decoded).toEqual({foo: [1, undefined, -1]}); - }); -}); - -describe('boolean', () => { - test('true', () => { - const data = utf8`t`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(true); - }); - - test('false', () => { - const data = utf8`f`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(false); - }); -}); - -describe('number', () => { - test('1', () => { - const data = utf8`i1e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(1); - }); - - test('12', () => { - const data = utf8`i12e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(12); - }); - - test('123', () => { - const data = utf8`i123e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(123); - }); - - test('1234', () => { - const data = utf8`i1234e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(1234); - }); - - test('12345', () => { - const data = utf8`i12345e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(12345); - }); - - test('123456', () => { - const data = utf8`i123456e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(123456); - }); - - test('-0.1234', () => { - const data = utf8`i-123e`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(-123); - }); -}); - -describe('string', () => { - test('empty string', () => { - const data = utf8`0:`; - const value = decoder.decode(data); - expect(value).toEqual(utf8``); - }); - - test('one char string', () => { - const data = utf8`1:a`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8`a`); - }); - - test('"hello world" string', () => { - const data = utf8`11:hello world`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8`hello world`); - }); - - test('string with emoji', () => { - const str = 'yes! - ๐Ÿ‘๐Ÿป๐Ÿ‘๐Ÿผ๐Ÿ‘๐Ÿฝ๐Ÿ‘๐Ÿพ๐Ÿ‘๐Ÿฟ'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('string with quote', () => { - const str = 'this is a "quote"'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('string with new line', () => { - const str = 'this is a \n new line'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('string with backslash', () => { - const str = 'this is a \\ backslash'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('a single backslash character', () => { - const str = '\\'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('string with tab', () => { - const str = 'this is a \t tab'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); - - test('string unicode characters', () => { - const str = '15\u00f8C'; - const buf = Buffer.from(str, 'utf-8'); - const data = utf8(`${buf.length}:${str}`); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(utf8(str)); - }); -}); - -describe('binary', () => { - test('empty buffer', () => { - const encoder = new BencodeEncoder(new Writer()); - const data = encoder.encode(new Uint8Array(0)); - decoder.reader.reset(data); - const value1 = decoder.readAny(); - expect(value1).toEqual(new Uint8Array(0)); - decoder.reader.reset(data); - const value2 = decoder.readBin(); - expect(value2).toEqual(new Uint8Array(0)); - }); - - test('a small buffer', () => { - const encoder = new BencodeEncoder(new Writer()); - const data = encoder.encode(new Uint8Array([4, 5, 6])); - decoder.reader.reset(data); - const value = decoder.readBin(); - expect(value).toEqual(new Uint8Array([4, 5, 6])); - }); -}); - -describe('array', () => { - test('empty array', () => { - const data = utf8`le`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([]); - }); - - test('array with one number element', () => { - const data = utf8`li1ee`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([1]); - }); - - test('array with strings', () => { - const data = utf8`l1:al1:be1:cl1:d1:eelee`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([utf8`a`, [utf8`b`], utf8`c`, [utf8`d`, utf8`e`], []]); - }); -}); - -describe('object', () => { - test('empty object', () => { - const data = utf8`de`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({}); - }); - - test('object with single key', () => { - const data = utf8`d3:foo3:bare`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({foo: utf8`bar`}); - }); - - test('nested object', () => { - const data = utf8`d0:dee`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({'': {}}); - }); - - test('complex nested object', () => { - const obj = { - a: 1, - b: true, - c: null, - d: [1, 2, 3], - e: { - f: utf8`foo`, - g: utf8`bar`, - h: { - i: utf8`baz`, - j: utf8`qux`, - }, - }, - }; - const data = utf8`d1:ai1e1:bt1:cn1:dli1ei2ei3ee1:ed1:f3:foo1:g3:bar1:hd1:i3:baz1:j3:quxeee`; - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(obj); - }); -}); diff --git a/src/json-pack/bencode/__tests__/BencodeEncoder.spec.ts b/src/json-pack/bencode/__tests__/BencodeEncoder.spec.ts deleted file mode 100644 index 566fb5a8be..0000000000 --- a/src/json-pack/bencode/__tests__/BencodeEncoder.spec.ts +++ /dev/null @@ -1,205 +0,0 @@ -import {utf8} from '../../../util/buffers/strings'; -import {Writer} from '../../../util/buffers/Writer'; -import {PackValue} from '../../types'; -import {BencodeEncoder} from '../BencodeEncoder'; - -const writer = new Writer(32); -const encoder = new BencodeEncoder(writer); - -const assertEncoder = (value: unknown, expected: Uint8Array) => { - const encoded = encoder.encode(value); - expect(encoded).toEqual(expected); -}; - -describe('null', () => { - test('null', () => { - assertEncoder(null, utf8`n`); - }); -}); - -describe('undefined', () => { - test('undefined', () => { - assertEncoder(undefined, utf8`u`); - }); -}); - -describe('boolean', () => { - test('true', () => { - assertEncoder(true, utf8`t`); - }); - - test('false', () => { - assertEncoder(false, utf8`f`); - }); -}); - -describe('number', () => { - test('integers', () => { - assertEncoder(0, utf8`i0e`); - assertEncoder(1, utf8`i1e`); - assertEncoder(-1, utf8`i-1e`); - assertEncoder(123, utf8`i123e`); - assertEncoder(-123, utf8`i-123e`); - assertEncoder(-12321321123, utf8`i-12321321123e`); - assertEncoder(+2321321123, utf8`i2321321123e`); - }); - - test('bigints', () => { - assertEncoder(BigInt('0'), utf8`i0e`); - assertEncoder(BigInt('1'), utf8`i1e`); - assertEncoder(BigInt('-1'), utf8`i-1e`); - assertEncoder(BigInt('123456'), utf8`i123456e`); - assertEncoder(BigInt('-123456'), utf8`i-123456e`); - }); - - test('floats', () => { - assertEncoder(0.0, utf8`i0e`); - assertEncoder(1.1, utf8`i1e`); - assertEncoder(-1.45, utf8`i-1e`); - assertEncoder(123.34, utf8`i123e`); - assertEncoder(-123.234, utf8`i-123e`); - assertEncoder(-12321.321123, utf8`i-12321e`); - assertEncoder(+2321321.123, utf8`i2321321e`); - }); -}); - -describe('string', () => { - test('empty string', () => { - assertEncoder('', utf8`0:`); - }); - - test('one char strings', () => { - assertEncoder('a', utf8`1:a`); - assertEncoder('b', utf8`1:b`); - assertEncoder('z', utf8`1:z`); - assertEncoder('~', utf8`1:~`); - assertEncoder('"', utf8`1:"`); - assertEncoder('\\', utf8`1:\\`); - assertEncoder('*', utf8`1:*`); - assertEncoder('@', utf8`1:@`); - assertEncoder('9', utf8`1:9`); - }); - - test('short strings', () => { - assertEncoder('abc', utf8`3:abc`); - assertEncoder('abc123', utf8`6:abc123`); - }); - - test('long strings', () => { - const txt = - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit.'; - assertEncoder(txt, utf8(`${txt.length}:${txt}`)); - }); -}); - -describe('binary', () => { - test('empty blob', () => { - assertEncoder(new Uint8Array(0), utf8`0:`); - }); - - test('small blob', () => { - assertEncoder(new Uint8Array([65]), utf8`1:A`); - }); -}); - -describe('array', () => { - test('empty array', () => { - assertEncoder([], utf8`le`); - }); - - test('array with one integer element', () => { - assertEncoder([1], utf8`li1ee`); - }); - - test('array with two integer elements', () => { - assertEncoder([1, 2], utf8`li1ei2ee`); - }); - - test('array of array', () => { - assertEncoder([[123]], utf8`lli123eee`); - }); - - test('array of various types', () => { - assertEncoder([0, 1.32, 'str', [1, 2, 3]], utf8`li0ei1e3:strli1ei2ei3eee`); - }); -}); - -describe('set', () => { - test('empty array', () => { - assertEncoder(new Set(), utf8`le`); - }); - - test('array with one integer element', () => { - assertEncoder(new Set([1]), utf8`li1ee`); - }); - - test('array with two integer elements', () => { - assertEncoder(new Set([1, 2]), utf8`li1ei2ee`); - }); - - test('array of array', () => { - assertEncoder(new Set([new Set([123])]), utf8`lli123eee`); - }); - - test('array of various types', () => { - assertEncoder(new Set([0, 1.32, 'str', new Set([1, 2, 3])]), utf8`li0ei1e3:strli1ei2ei3eee`); - }); -}); - -describe('object', () => { - test('empty object', () => { - assertEncoder({}, utf8`de`); - }); - - test('object with one key', () => { - assertEncoder({foo: 'bar'}, utf8`d3:foo3:bare`); - }); - - test('object with two keys (sorted)', () => { - assertEncoder({foo: 'bar', baz: 123}, utf8`d3:bazi123e3:foo3:bare`); - }); - - test('object with various nested types', () => { - assertEncoder( - { - str: 'qwerty', - num: 123, - arr: [1, 2, 3], - obj: {foo: 'bar'}, - }, - utf8`d3:arrli1ei2ei3ee3:numi123e3:objd3:foo3:bare3:str6:qwertye`, - ); - }); -}); - -describe('map', () => { - test('empty object', () => { - assertEncoder(new Map(), utf8`de`); - }); - - test('object with one key', () => { - assertEncoder(new Map([['foo', 'bar']]), utf8`d3:foo3:bare`); - }); - - test('object with two keys (sorted)', () => { - assertEncoder( - new Map([ - ['foo', 'bar'], - ['baz', 123], - ]), - utf8`d3:bazi123e3:foo3:bare`, - ); - }); - - test('object with various nested types', () => { - assertEncoder( - new Map([ - ['str', 'qwerty'], - ['num', 123], - ['arr', [1, 2, 3]], - ['obj', {foo: 'bar'}], - ]), - utf8`d3:arrli1ei2ei3ee3:numi123e3:objd3:foo3:bare3:str6:qwertye`, - ); - }); -}); diff --git a/src/json-pack/bencode/__tests__/automated.spec.ts b/src/json-pack/bencode/__tests__/automated.spec.ts deleted file mode 100644 index 858394c780..0000000000 --- a/src/json-pack/bencode/__tests__/automated.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {BencodeEncoder} from '../BencodeEncoder'; -import {BencodeDecoder} from '../BencodeDecoder'; -import {utf8} from '../../../util/buffers/strings'; - -const writer = new Writer(8); -const encoder = new BencodeEncoder(writer); -const decoder = new BencodeDecoder(); - -const documents: [value: unknown, name?: string][] = [ - [0], - [1], - [12345], - [-12345], - [-4444444444444444], - [true], - [false], - [null], - [undefined], - [utf8``, 'empty byte string'], - [utf8`hello`, '"hello" byte string'], - [{}, 'empty object'], - [[], 'empty array'], - [[1, -2, null, true, utf8`asdf`, false, utf8``, undefined], 'array with basic values'], - [[[[]]], 'triply nested arrays'], - [[1, [1, [1], 1], 1], 'nested arrays with values'], - [{a: {b: {c: {d: {foo: utf8`bar`}}}}}, 'nested objects'], -]; - -const assertEncoder = (value: unknown) => { - const encoded = encoder.encode(value); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(value); -}; - -describe('Sample JSON documents', () => { - for (const [value, name] of documents) { - test(name || String(value), () => { - assertEncoder(value); - }); - } -}); diff --git a/src/json-pack/bencode/types.ts b/src/json-pack/bencode/types.ts deleted file mode 100644 index 7bf66c87ec..0000000000 --- a/src/json-pack/bencode/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type BencodeUint8Array = Uint8Array & {__BRAND__: 'bencode'; __TYPE__: T}; diff --git a/src/json-pack/bson/BsonEncoder.ts b/src/json-pack/bson/BsonEncoder.ts deleted file mode 100644 index 17a669a45c..0000000000 --- a/src/json-pack/bson/BsonEncoder.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { - BsonBinary, - BsonDbPointer, - BsonDecimal128, - BsonFloat, - BsonInt32, - BsonInt64, - BsonJavascriptCode, - BsonMaxKey, - BsonMinKey, - BsonObjectId, - BsonTimestamp, -} from './values'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder} from '../types'; - -export class BsonEncoder implements BinaryJsonEncoder { - constructor(public readonly writer: IWriter & IWriterGrowable) {} - - public encode(value: unknown): Uint8Array { - const writer = this.writer; - writer.reset(); - this.writeAny(value); - return writer.flush(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'object': { - if (value === null) throw new Error('NOT_OBJ'); - return this.writeObj(>value); - } - } - throw new Error('NOT_OBJ'); - } - - public writeNull(): void { - throw new Error('Method not implemented.'); - } - - public writeUndef(): void { - throw new Error('Method not implemented.'); - } - - public writeBoolean(bool: boolean): void { - throw new Error('Method not implemented.'); - } - - public writeNumber(num: number): void { - throw new Error('Method not implemented.'); - } - - public writeInteger(int: number): void { - throw new Error('Method not implemented.'); - } - - public writeUInteger(uint: number): void { - throw new Error('Method not implemented.'); - } - - public writeInt32(int: number): void { - const writer = this.writer; - writer.ensureCapacity(4); - writer.view.setInt32(writer.x, int, true); - writer.x += 4; - } - - public writeInt64(int: number | bigint): void { - const writer = this.writer; - writer.ensureCapacity(8); - writer.view.setBigInt64(writer.x, BigInt(int), true); - writer.x += 8; - } - - public writeFloat(float: number): void { - const writer = this.writer; - writer.ensureCapacity(4); - writer.view.setFloat64(writer.x, float, true); - writer.x += 8; - } - - public writeBigInt(int: bigint): void { - throw new Error('Method not implemented.'); - } - - public writeBin(buf: Uint8Array): void { - const length = buf.length; - this.writeInt32(length); - const writer = this.writer; - writer.u8(0); - writer.buf(buf, length); - } - - public writeStr(str: string): void { - const writer = this.writer; - const length = str.length; - const maxSize = 4 + 1 + 4 * length; - writer.ensureCapacity(maxSize); - const x = writer.x; - this.writeInt32(length + 1); - const bytesWritten = writer.utf8(str); - writer.u8(0); - if (bytesWritten !== length) { - writer.view.setInt32(x, bytesWritten + 1, true); - } - } - - public writeAsciiStr(str: string): void { - throw new Error('Method not implemented.'); - } - - public writeArr(arr: unknown[]): void { - this.writeObj(arr as unknown as Record); - } - - public writeObj(obj: Record): void { - const writer = this.writer; - writer.ensureCapacity(8); - const x0 = writer.x0; - const dx = writer.x - x0; - writer.x += 4; - const keys = Object.keys(obj); - const length = keys.length; - for (let i = 0; i < length; i++) { - const key = keys[i]; - const value = obj[key]; - this.writeKey(key, value); - } - writer.u8(0); - const x = writer.x0 + dx; - const size = writer.x - x; - writer.view.setUint32(x, size, true); - } - - public writeCString(str: string): void { - const writer = this.writer; - const length = str.length; - writer.ensureCapacity(1 + 4 * length); - const uint8 = writer.uint8; - let x = writer.x; - let pos = 0; - while (pos < length) { - let value = str.charCodeAt(pos++); - if ((value & 0xffffff80) === 0) { - if (!value) break; - uint8[x++] = value; - continue; - } else if ((value & 0xfffff800) === 0) { - const octet = ((value >> 6) & 0x1f) | 0xc0; - if (!octet) break; - uint8[x++] = octet; - } else { - if (value >= 0xd800 && value <= 0xdbff) { - if (pos < length) { - const extra = str.charCodeAt(pos); - if ((extra & 0xfc00) === 0xdc00) { - pos++; - value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; - } - } - } - if ((value & 0xffff0000) === 0) { - const octet1 = ((value >> 12) & 0x0f) | 0xe0; - const octet2 = ((value >> 6) & 0x3f) | 0x80; - if (!octet1 || !octet2) throw new Error('INVALID_CSTRING'); - uint8[x++] = octet1; - uint8[x++] = octet2; - } else { - const octet1 = ((value >> 18) & 0x07) | 0xf0; - const octet2 = ((value >> 12) & 0x3f) | 0x80; - const octet3 = ((value >> 6) & 0x3f) | 0x80; - if (!octet1 || !octet2 || !octet3) throw new Error('INVALID_CSTRING'); - uint8[x++] = octet1; - uint8[x++] = octet2; - uint8[x++] = octet3; - } - } - const octet = (value & 0x3f) | 0x80; - if (!octet) break; - uint8[x++] = octet; - } - uint8[x++] = 0; - writer.x = x; - } - - public writeObjectId(id: BsonObjectId): void { - const writer = this.writer; - writer.ensureCapacity(12); - const uint8 = writer.uint8; - const x = writer.x; - const {timestamp, process, counter} = id; - uint8[x + 0] = timestamp >>> 24; - uint8[x + 1] = (timestamp >>> 16) & 0xff; - uint8[x + 2] = (timestamp >>> 8) & 0xff; - uint8[x + 3] = timestamp & 0xff; - uint8[x + 4] = process & 0xff; - uint8[x + 5] = (process >>> 8) & 0xff; - uint8[x + 6] = (process >>> 16) & 0xff; - uint8[x + 7] = (process >>> 24) & 0xff; - let lo32 = process | 0; - if (lo32 < 0) lo32 += 4294967296; - const hi32 = (process - lo32) / 4294967296; - uint8[x + 8] = hi32 & 0xff; - uint8[x + 9] = counter >>> 16; - uint8[x + 10] = (counter >>> 8) & 0xff; - uint8[x + 11] = counter & 0xff; - writer.x += 12; - } - - public writeKey(key: string, value: unknown): void { - const writer = this.writer; - switch (typeof value) { - case 'number': { - const isFloat = Math.floor(value) !== value; - if (isFloat) { - writer.u8(0x01); - this.writeCString(key); - this.writeFloat(value); - break; - } - if (value <= 2147483647 && value >= -2147483648) { - writer.u8(0x10); - this.writeCString(key); - this.writeInt32(value); - break; - } - writer.u8(0x12); - this.writeCString(key); - this.writeInt64(value); - break; - } - case 'string': { - writer.u8(0x02); - this.writeCString(key); - this.writeStr(value); - break; - } - case 'object': { - if (value === null) { - writer.u8(0x0a); - this.writeCString(key); - break; - } - const constructor = value.constructor; - switch (constructor) { - case Object: { - writer.u8(0x03); - this.writeCString(key); - this.writeObj(value as Record); - break; - } - case Array: { - writer.u8(0x04); - this.writeCString(key); - this.writeObj(value as Record); - break; - } - case Uint8Array: { - writer.u8(0x05); - this.writeCString(key); - this.writeBin(value as Uint8Array); - break; - } - case BsonObjectId: { - writer.u8(0x07); - this.writeCString(key); - this.writeObjectId(value as BsonObjectId); - break; - } - case Date: { - writer.u8(0x09); - this.writeCString(key); - writer.ensureCapacity(8); - writer.view.setBigUint64(writer.x, BigInt((value as Date).getTime()), true); - writer.x += 8; - break; - } - case RegExp: { - writer.u8(0x0b); - this.writeCString(key); - this.writeCString((value).source); - this.writeCString((value).flags); - break; - } - case BsonDbPointer: { - writer.u8(0x0c); - this.writeCString(key); - const pointer = value as BsonDbPointer; - this.writeStr(pointer.name); - this.writeObjectId(pointer.id); - break; - } - case BsonJavascriptCode: { - writer.u8(0x0d); - this.writeCString(key); - this.writeStr((value as BsonJavascriptCode).code); - break; - } - case BsonInt32: { - writer.u8(0x10); - this.writeCString(key); - this.writeInt32((value as BsonInt32).value); - break; - } - case BsonInt64: { - writer.u8(0x12); - this.writeCString(key); - this.writeInt64((value as BsonInt32).value); - break; - } - case BsonFloat: { - writer.u8(0x01); - this.writeCString(key); - this.writeFloat((value as BsonInt32).value); - break; - } - case BsonTimestamp: { - writer.u8(0x11); - this.writeCString(key); - const ts = value as BsonTimestamp; - this.writeInt32(ts.increment); - this.writeInt32(ts.timestamp); - break; - } - case BsonDecimal128: { - writer.u8(0x13); - this.writeCString(key); - const dec = value as BsonDecimal128; - if (dec.data.length !== 16) throw new Error('INVALID_DECIMAL128'); - writer.buf(dec.data, 16); - break; - } - case BsonMinKey: { - writer.u8(0xff); - this.writeCString(key); - break; - } - case BsonMaxKey: { - writer.u8(0x7f); - this.writeCString(key); - break; - } - case BsonBinary: { - writer.u8(0x05); - this.writeCString(key); - const bin = value as BsonBinary; - const length = bin.data.length; - this.writeInt32(length); - writer.u8(bin.subtype); - writer.buf(bin.data, length); - break; - } - default: { - writer.u8(0x03); - this.writeCString(key); - this.writeObj(value as Record); - break; - } - } - break; - } - case 'boolean': { - writer.u8(0x08); - this.writeCString(key); - writer.u8(+value); - break; - } - case 'undefined': { - writer.u8(0x06); - this.writeCString(key); - break; - } - case 'symbol': { - writer.u8(0x0e); - this.writeCString(key); - this.writeStr(value.description || ''); - break; - } - } - } -} diff --git a/src/json-pack/bson/README.md b/src/json-pack/bson/README.md deleted file mode 100644 index 1dc37ff8fe..0000000000 --- a/src/json-pack/bson/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# BSON - -Performant impelementation of [BSON][bson] (Binary JSON) for JavaScript. - -[bson]: https://bsonspec.org/ - - -## Benchmarks - -``` -npx ts-node benchmarks/json-pack/bench.bson.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.4.0 , Arch: arm64 , CPU: Apple M1 ------------------------------------------------------------------------------ Combined, 63374 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 4,604 ops/sec ยฑ0.12% (100 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 3,962 ops/sec ยฑ0.18% (100 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 1,439 ops/sec ยฑ0.19% (100 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 1,699 ops/sec ยฑ0.11% (100 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder ----------------------------------------------------------------------------- Small object, 53 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 4,464,852 ops/sec ยฑ0.47% (96 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 3,684,236 ops/sec ยฑ0.18% (100 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 884,917 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 1,153,616 ops/sec ยฑ0.16% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder ------------------------------------------------------------------------- Typical object, 1002 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 306,241 ops/sec ยฑ0.22% (100 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 368,051 ops/sec ยฑ0.17% (100 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 106,583 ops/sec ยฑ0.84% (99 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 126,497 ops/sec ยฑ0.12% (99 runs sampled) -Fastest is ๐Ÿ‘Ž json-joy/json-pack BsonEncoder --------------------------------------------------------------------------- Large object, 3750 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 91,646 ops/sec ยฑ0.76% (100 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 109,402 ops/sec ยฑ0.17% (100 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 35,037 ops/sec ยฑ0.19% (98 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 39,504 ops/sec ยฑ0.49% (101 runs sampled) -Fastest is ๐Ÿ‘Ž json-joy/json-pack BsonEncoder --------------------------------------------------------------------- Very large object, 45759 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 6,234 ops/sec ยฑ0.47% (99 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 4,824 ops/sec ยฑ0.20% (99 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 1,645 ops/sec ยฑ0.17% (101 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 2,696 ops/sec ยฑ0.66% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder ------------------------------------------------------------------- Object with many keys, 978 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 260,571 ops/sec ยฑ0.68% (96 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 243,776 ops/sec ยฑ0.42% (98 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 86,641 ops/sec ยฑ0.29% (100 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 81,730 ops/sec ยฑ0.13% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder -------------------------------------------------------------------------- String ladder, 4046 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder x 92,381 ops/sec ยฑ0.13% (100 runs sampled) -๐Ÿ‘Ž json-joy/json-pack BsonEncoder x 127,132 ops/sec ยฑ1.03% (90 runs sampled) -๐Ÿ‘Ž bson BSON.serialize() x 75,356 ops/sec ยฑ1.18% (94 runs sampled) -๐Ÿ‘ bson Buffer.from(EJSON.stringify()) x 47,308 ops/sec ยฑ0.08% (101 runs sampled) -Fastest is ๐Ÿ‘Ž json-joy/json-pack BsonEncoder -``` diff --git a/src/json-pack/bson/__tests__/BsonEncoder-values.spec.ts b/src/json-pack/bson/__tests__/BsonEncoder-values.spec.ts deleted file mode 100644 index 16f5d489c7..0000000000 --- a/src/json-pack/bson/__tests__/BsonEncoder-values.spec.ts +++ /dev/null @@ -1,154 +0,0 @@ -import {BSON, Decimal128, MinKey, MaxKey} from 'bson'; -import {Writer} from '../../../util/buffers/Writer'; -import {BsonEncoder} from '../BsonEncoder'; -import { - BsonBinary, - BsonDbPointer, - BsonFloat, - BsonInt32, - BsonInt64, - BsonJavascriptCode, - BsonJavascriptCodeWithScope, - BsonMaxKey, - BsonMinKey, - BsonObjectId, - BsonTimestamp, -} from '../values'; -import {BsonDecimal128} from '../values'; - -const writer = new Writer(8); -const encoder = new BsonEncoder(writer); - -describe('special value encoding', () => { - test('BsonObjectId', () => { - const value = { - foo: new BsonObjectId(0x01020304, 0x0102030405, 0x010203), - }; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - const objectId = decoded.foo; - expect(objectId.getTimestamp().getTime()).toBe(0x01020304 * 1000); - }); - - test('Date', () => { - const date = new Date(1689235374326); - const value = {date}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.date.getTime()).toBe(1689235374326); - }); - - test('RegExp', () => { - const reg = /foo/i; - const value = {reg}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.reg.source).toBe('foo'); - expect(decoded.reg.flags).toBe('i'); - }); - - test('BsonDbPointer', () => { - const id = new BsonObjectId(0x01020304, 0x0102030405, 0x010203); - const pointer = new BsonDbPointer('test', id); - const value = {pointer}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.pointer.collection).toBe('test'); - expect(decoded.pointer.oid.getTimestamp().getTime()).toBe(0x01020304 * 1000); - }); - - test('BsonJavascriptCode', () => { - const code = new BsonJavascriptCode('console.log("hello world")'); - const value = {code}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.code.code).toBe('console.log("hello world")'); - }); - - test('BsonJavascriptCodeWithScope', () => { - const code = new BsonJavascriptCodeWithScope('console.log("hello world")', {foo: 'bar'}); - const value = {code}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.code.code).toBe('console.log("hello world")'); - expect(decoded.code.scope).toStrictEqual({foo: 'bar'}); - }); - - test('Symbol', () => { - const symbol = Symbol('foo'); - const value = {symbol}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.symbol).toBe('foo'); - }); - - test('BsonInt32', () => { - const int = new BsonInt32(123); - const value = {int}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.int).toBe(123); - }); - - test('BsonInt64', () => { - const int = new BsonInt64(123); - const value = {int}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.int).toBe(123); - }); - - test('BsonFloat', () => { - const int = new BsonFloat(123); - const value = {int}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.int).toBe(123); - }); - - test('BsonTimestamp', () => { - const increment = 0x01020304; - const timestamp = 0x40302010; - const ts = new BsonTimestamp(increment, timestamp); - const value = {ts}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.ts.toExtendedJSON().$timestamp.t).toBe(timestamp); - expect(decoded.ts.toExtendedJSON().$timestamp.i).toBe(increment); - }); - - test('BsonDecimal128', () => { - const dec = new BsonDecimal128(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])); - const value = {dec}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.dec).toBeInstanceOf(Decimal128); - }); - - test('BsonMinKey and BsonMaxKey', () => { - const value = { - min: new BsonMinKey(), - max: new BsonMaxKey(), - }; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.min).toBeInstanceOf(MinKey); - expect(decoded.max).toBeInstanceOf(MaxKey); - }); - - test('BsonBinary', () => { - const value = { - bin1: new BsonBinary(0x00, new Uint8Array([1, 2, 3])), - bin2: new BsonBinary(0x01, new Uint8Array([1, 2, 3])), - bin3: new BsonBinary(0x80, new Uint8Array([1, 2, 3])), - }; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded.bin1.sub_type).toBe(0); - expect(decoded.bin2.sub_type).toBe(0x01); - expect(decoded.bin3.sub_type).toBe(0x80); - expect(decoded.bin1.buffer).toStrictEqual(Buffer.from([1, 2, 3])); - expect(decoded.bin2.buffer).toStrictEqual(Buffer.from([1, 2, 3])); - expect(decoded.bin3.buffer).toStrictEqual(Buffer.from([1, 2, 3])); - }); -}); diff --git a/src/json-pack/bson/__tests__/BsonEncoder.spec.ts b/src/json-pack/bson/__tests__/BsonEncoder.spec.ts deleted file mode 100644 index e1a86b6318..0000000000 --- a/src/json-pack/bson/__tests__/BsonEncoder.spec.ts +++ /dev/null @@ -1,258 +0,0 @@ -import {BSON} from 'bson'; -import {Writer} from '../../../util/buffers/Writer'; -import {BsonEncoder} from '../BsonEncoder'; - -const writer = new Writer(8); -const encoder = new BsonEncoder(writer); - -const assertEncoder = (value: unknown, expected: unknown = value) => { - if (!value || typeof value !== 'object' || value.constructor !== Object) expected = value = {value}; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded).toEqual(expected); -}; - -describe('undefined', () => { - test('undefined', () => { - assertEncoder(undefined as any); - }); -}); - -describe('null', () => { - test('null', () => { - assertEncoder(null); - }); -}); - -describe('boolean', () => { - test('true', () => { - assertEncoder(true); - }); - - test('false', () => { - assertEncoder(false); - }); -}); - -describe('number', () => { - const ints = [ - 0, 1, -1, 123, -123, 1234, 3333, -3467, -4444, 55555, -55565, 234234, -322324, 2147483647, -1147483647, 12321321123, - -12321321123, +2321321123, - ]; - for (const int of ints) { - test('integer ' + int, () => { - assertEncoder(int); - }); - } - - test('floats', () => { - assertEncoder(0.0); - assertEncoder(1.1); - assertEncoder(-1.45); - assertEncoder(123.34); - assertEncoder(-123.234); - assertEncoder(-12321.321123); - assertEncoder(+2321321.123); - }); -}); - -describe('string', () => { - test('empty string', () => { - assertEncoder(''); - }); - - test('one char strings', () => { - assertEncoder('a'); - assertEncoder('b'); - assertEncoder('z'); - assertEncoder('~'); - assertEncoder('"'); - assertEncoder('\\'); - assertEncoder('*'); - assertEncoder('@'); - assertEncoder('9'); - assertEncoder('โœ…'); - assertEncoder('๐Ÿ‘'); - }); - - test('short strings', () => { - assertEncoder('abc'); - assertEncoder('abc123'); - }); - - test('long strings', () => { - assertEncoder( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit.', - ); - }); - - test('unsafe character in the middle of a string', () => { - assertEncoder('...................".....................'); - }); - - test('unsafe character in the middle of a string - 2', () => { - assertEncoder('...................๐ŸŽ‰.....................'); - }); -}); - -describe('array', () => { - test('empty array', () => { - assertEncoder([]); - }); - - test('array with one element', () => { - assertEncoder([1]); - }); - - test('array with two elements', () => { - assertEncoder([1, 2]); - }); - - test('array of array', () => { - assertEncoder([[123]]); - }); - - test('array of various types', () => { - assertEncoder([0, 1.32, 'str', true, false, null, [1, 2, 3]]); - }); -}); - -describe('object', () => { - test('empty object', () => { - assertEncoder({}); - }); - - test('object with float key', () => { - assertEncoder({ - float: 123.456, - }); - }); - - test('object with int32 key', () => { - assertEncoder({ - int: 0x01020304, - }); - }); - - test('object with int64 key', () => { - assertEncoder({ - int64: 0x010203040506, - }); - }); - - test('object with one string key', () => { - assertEncoder({foo: 'bar'}); - }); - - test('object with two keys', () => { - assertEncoder({foo: 'bar', baz: 123}); - }); - - test('empty nested array', () => { - assertEncoder({ - foo: [], - }); - }); - - test('simple nested array', () => { - assertEncoder({ - foo: [1, 2, 3], - }); - }); - - test('one nested object', () => { - assertEncoder({ - foo: {}, - }); - }); - - test('nested objects', () => { - assertEncoder({ - foo: { - bar: { - baz: 123, - }, - }, - }); - }); - - test('binary Uint8Array', () => { - const value = { - foo: new Uint8Array([1, 2, 3]), - }; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - const buf = decoded.foo.buffer; - const uint8 = new Uint8Array(buf, buf.byteOffset, buf.byteLength); - expect(uint8).toEqual(value.foo); - }); - - test('undefined key', () => { - assertEncoder({ - foo: undefined, - }); - }); - - test('boolean keys', () => { - assertEncoder({ - true: true, - false: false, - }); - }); - - test('null keys', () => { - assertEncoder({ - null: null, - }); - }); - - test('symbol keys', () => { - const value = { - foo: Symbol('foo'), - }; - const encoded = encoder.encode(value); - const decoded = BSON.deserialize(encoded); - expect(decoded).toStrictEqual({foo: 'foo'}); - }); - - test('object with various nested types', () => { - assertEncoder({ - '': null, - null: false, - true: true, - str: 'asdfasdf ,asdf asdf asdf asdf asdf, asdflkasjdflakjsdflajskdlfkasdf', - num: 123, - arr: [1, 2, 3], - obj: {foo: 'bar'}, - obj2: {1: 2, 3: 4}, - }); - }); -}); - -describe('nested object', () => { - test('large array/object', () => { - assertEncoder({ - foo: [ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ], - }); - }); -}); diff --git a/src/json-pack/bson/__tests__/codec.spec.ts b/src/json-pack/bson/__tests__/codec.spec.ts deleted file mode 100644 index 89bf7f052d..0000000000 --- a/src/json-pack/bson/__tests__/codec.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {BSON} from 'bson'; -import {documents} from '../../../__tests__/json-documents'; -import {BsonEncoder} from '../BsonEncoder'; -import {Writer} from '../../../util/buffers/Writer'; - -const run = (encoder: BsonEncoder) => { - describe('JSON documents', () => { - for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - const json = t.json && typeof t.json === 'object' && t.json.constructor === Object ? t.json : {json: t.json}; - const encoded = encoder.encode(json); - const decoded = BSON.deserialize(encoded); - expect(decoded).toEqual(json); - }); - } - }); -}; - -describe('CbroEncoder', () => { - const writer = new Writer(32); - const encoder = new BsonEncoder(writer); - run(encoder); -}); diff --git a/src/json-pack/bson/index.ts b/src/json-pack/bson/index.ts deleted file mode 100644 index 680858d157..0000000000 --- a/src/json-pack/bson/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './values'; -export * from './BsonEncoder'; diff --git a/src/json-pack/bson/values.ts b/src/json-pack/bson/values.ts deleted file mode 100644 index 1ff8e6f6a5..0000000000 --- a/src/json-pack/bson/values.ts +++ /dev/null @@ -1,63 +0,0 @@ -export class BsonObjectId { - public constructor( - public timestamp: number, - public process: number, - public counter: number, - ) {} -} - -export class BsonDbPointer { - public constructor( - public name: string, - public id: BsonObjectId, - ) {} -} - -export class BsonJavascriptCode { - public constructor(public code: string) {} -} - -export class BsonSymbol { - public constructor(public symbol: string) {} -} - -export class BsonJavascriptCodeWithScope { - public constructor( - public code: string, - public scope: Record, - ) {} -} - -export class BsonInt32 { - public constructor(public value: number) {} -} - -export class BsonInt64 { - public constructor(public value: number) {} -} - -export class BsonFloat { - public constructor(public value: number) {} -} - -export class BsonTimestamp { - public constructor( - public increment: number, - public timestamp: number, - ) {} -} - -export class BsonDecimal128 { - public constructor(public data: Uint8Array) {} -} - -export class BsonMinKey {} - -export class BsonMaxKey {} - -export class BsonBinary { - public constructor( - public subtype: number, - public data: Uint8Array, - ) {} -} diff --git a/src/json-pack/cbor/CborDecoder.ts b/src/json-pack/cbor/CborDecoder.ts deleted file mode 100644 index 64b925799f..0000000000 --- a/src/json-pack/cbor/CborDecoder.ts +++ /dev/null @@ -1,408 +0,0 @@ -import {CONST, ERROR, MAJOR} from './constants'; -import {CborDecoderBase} from './CborDecoderBase'; -import {JsonPackValue} from '../JsonPackValue'; -import type {Path} from '../../json-pointer'; -import type {IReader, IReaderResettable} from '../../util/buffers'; - -export class CborDecoder< - R extends IReader & IReaderResettable = IReader & IReaderResettable, -> extends CborDecoderBase { - // -------------------------------------------------------------- Map reading - - public readAsMap(): Map { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - switch (major) { - case MAJOR.MAP: - return this.readMap(minor); - default: - throw ERROR.UNEXPECTED_MAJOR; - } - } - - public readMap(minor: number): Map { - const length = this.readMinorLen(minor); - if (length >= 0) return this.readMapRaw(length); - else return this.readMapIndef(); - } - - public readMapRaw(length: number): Map { - const map: Map = new Map(); - for (let i = 0; i < length; i++) { - const key = this.val(); - const value = this.val(); - map.set(key, value); - } - return map; - } - - public readMapIndef(): Map { - const map: Map = new Map(); - while (this.reader.peak() !== CONST.END) { - const key = this.val(); - if (this.reader.peak() === CONST.END) throw ERROR.UNEXPECTED_OBJ_BREAK; - const value = this.val(); - map.set(key, value); - } - this.reader.x++; - return map; - } - - // ----------------------------------------------------------- Value skipping - - public skipN(n: number): void { - for (let i = 0; i < n; i++) this.skipAny(); - } - public skipAny(): void { - this.skipAnyRaw(this.reader.u8()); - } - - public skipAnyRaw(octet: number): void { - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - switch (major) { - case MAJOR.UIN: - case MAJOR.NIN: - this.skipUNint(minor); - break; - case MAJOR.BIN: - this.skipBin(minor); - break; - case MAJOR.STR: - this.skipStr(minor); - break; - case MAJOR.ARR: - this.skipArr(minor); - break; - case MAJOR.MAP: - this.skipObj(minor); - break; - case MAJOR.TKN: - this.skipTkn(minor); - break; - case MAJOR.TAG: - this.skipTag(minor); - break; - } - } - - public skipMinorLen(minor: number): number { - if (minor <= 23) return minor; - switch (minor) { - case 24: - return this.reader.u8(); - case 25: - return this.reader.u16(); - case 26: - return this.reader.u32(); - case 27: - return Number(this.reader.u64()); - case 31: - return -1; - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - // --------------------------------------------------------- Integer skipping - - public skipUNint(minor: number): void { - if (minor <= 23) return; - switch (minor) { - case 24: - return this.reader.skip(1); - case 25: - return this.reader.skip(2); - case 26: - return this.reader.skip(4); - case 27: - return this.reader.skip(8); - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - // ---------------------------------------------------------- Binary skipping - - public skipBin(minor: number): void { - const length = this.skipMinorLen(minor); - if (length >= 0) this.reader.skip(length); - else { - while (this.reader.peak() !== CONST.END) this.skipBinChunk(); - this.reader.x++; - } - } - - public skipBinChunk(): void { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.BIN) throw ERROR.UNEXPECTED_BIN_CHUNK_MAJOR; - if (minor > 27) throw ERROR.UNEXPECTED_BIN_CHUNK_MINOR; - this.skipBin(minor); - } - - // ---------------------------------------------------------- String skipping - - public skipStr(minor: number): void { - const length = this.skipMinorLen(minor); - if (length >= 0) this.reader.skip(length); - else { - while (this.reader.peak() !== CONST.END) this.skipStrChunk(); - this.reader.x++; - } - } - - public skipStrChunk(): void { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.STR) throw ERROR.UNEXPECTED_STR_CHUNK_MAJOR; - if (minor > 27) throw ERROR.UNEXPECTED_STR_CHUNK_MINOR; - this.skipStr(minor); - } - - // ----------------------------------------------------------- Array skipping - - public skipArr(minor: number): void { - const length = this.skipMinorLen(minor); - if (length >= 0) this.skipN(length); - else { - while (this.reader.peak() !== CONST.END) this.skipAny(); - this.reader.x++; - } - } - - // ---------------------------------------------------------- Object skipping - - public skipObj(minor: number): void { - const length = this.readMinorLen(minor); - if (length >= 0) return this.skipN(length * 2); - else { - while (this.reader.peak() !== CONST.END) { - this.skipAny(); - if (this.reader.peak() === CONST.END) throw ERROR.UNEXPECTED_OBJ_BREAK; - this.skipAny(); - } - this.reader.x++; - } - } - - // ------------------------------------------------------------- Tag skipping - - public skipTag(minor: number): void { - const length = this.skipMinorLen(minor); - if (length < 0) throw ERROR.UNEXPECTED_MINOR; - this.skipAny(); - } - - // ----------------------------------------------------------- Token skipping - - public skipTkn(minor: number): void { - switch (minor) { - case 0xf8 & CONST.MINOR_MASK: - this.reader.skip(1); - return; - case 0xf9 & CONST.MINOR_MASK: - this.reader.skip(2); - return; - case 0xfa & CONST.MINOR_MASK: - this.reader.skip(4); - return; - case 0xfb & CONST.MINOR_MASK: - this.reader.skip(8); - return; - } - if (minor <= 23) return; - throw ERROR.UNEXPECTED_MINOR; - } - - // --------------------------------------------------------------- Validation - - /** - * Throws if at given offset in a buffer there is an invalid CBOR value, or - * if the value does not span the exact length specified in `size`. I.e. - * throws if: - * - * - The value is not a valid CBOR value. - * - The value is shorter than `size`. - * - The value is longer than `size`. - * - * @param value Buffer in which to validate CBOR value. - * @param offset Offset at which the value starts. - * @param size Expected size of the value. - */ - public validate(value: Uint8Array, offset: number = 0, size: number = value.length): void { - this.reader.reset(value); - this.reader.x = offset; - const start = offset; - this.skipAny(); - const end = this.reader.x; - if (end - start !== size) throw ERROR.INVALID_SIZE; - } - - // -------------------------------------------- One level reading - any value - - public decodeLevel(value: Uint8Array): unknown { - this.reader.reset(value); - return this.readLevel(); - } - - /** - * Decodes only one level of objects and arrays. Other values are decoded - * completely. - * - * @returns One level of decoded CBOR value. - */ - public readLevel(): unknown { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - switch (major) { - case MAJOR.ARR: - return this.readArrLevel(minor); - case MAJOR.MAP: - return this.readObjLevel(minor); - default: - return super.readAnyRaw(octet); - } - } - - /** - * Decodes primitive values, returns container values as `JsonPackValue`. - * - * @returns A primitive value, or CBOR container value as a blob. - */ - public readPrimitiveOrVal(): unknown | JsonPackValue { - const octet = this.reader.peak(); - const major = octet >> 5; - switch (major) { - case MAJOR.ARR: - case MAJOR.MAP: - return this.readAsValue(); - default: - return this.val(); - } - } - - public readAsValue(): JsonPackValue { - const reader = this.reader; - const start = reader.x; - this.skipAny(); - const end = reader.x; - return new JsonPackValue(reader.uint8.subarray(start, end)); - } - - // ----------------------------------------------- One level reading - object - - public readObjLevel(minor: number): Record { - const length = this.readMinorLen(minor); - if (length >= 0) return this.readObjRawLevel(length); - else return this.readObjIndefLevel(); - } - - public readObjRawLevel(length: number): Record { - const obj: Record = {}; - for (let i = 0; i < length; i++) { - const key = this.key(); - const value = this.readPrimitiveOrVal(); - obj[key] = value; - } - return obj; - } - - public readObjIndefLevel(): Record { - const obj: Record = {}; - while (this.reader.peak() !== CONST.END) { - const key = this.key(); - if (this.reader.peak() === CONST.END) throw ERROR.UNEXPECTED_OBJ_BREAK; - const value = this.readPrimitiveOrVal(); - obj[key] = value; - } - this.reader.x++; - return obj; - } - - // ------------------------------------------------ One level reading - array - - public readArrLevel(minor: number): unknown[] { - const length = this.readMinorLen(minor); - if (length >= 0) return this.readArrRawLevel(length); - return this.readArrIndefLevel(); - } - - public readArrRawLevel(length: number): unknown[] { - const arr: unknown[] = []; - for (let i = 0; i < length; i++) arr.push(this.readPrimitiveOrVal()); - return arr; - } - - public readArrIndefLevel(): unknown[] { - const arr: unknown[] = []; - while (this.reader.peak() !== CONST.END) arr.push(this.readPrimitiveOrVal()); - this.reader.x++; - return arr; - } - - // ---------------------------------------------------------- Shallow reading - - public readHdr(expectedMajor: number): number { - const octet = this.reader.u8(); - const major = octet >> 5; - if (major !== expectedMajor) throw ERROR.UNEXPECTED_MAJOR; - const minor = octet & CONST.MINOR_MASK; - if (minor < 24) return minor; - switch (minor) { - case 24: - return this.reader.u8(); - case 25: - return this.reader.u16(); - case 26: - return this.reader.u32(); - case 27: - return Number(this.reader.u64()); - case 31: - return -1; - } - throw ERROR.UNEXPECTED_MINOR; - } - - public readStrHdr(): number { - return this.readHdr(MAJOR.STR); - } - - public readObjHdr(): number { - return this.readHdr(MAJOR.MAP); - } - - public readArrHdr(): number { - return this.readHdr(MAJOR.ARR); - } - - public findKey(key: string): this { - const size = this.readObjHdr(); - for (let i = 0; i < size; i++) { - const k = this.key(); - if (k === key) return this; - this.skipAny(); - } - throw ERROR.KEY_NOT_FOUND; - } - - public findIndex(index: number): this { - const size = this.readArrHdr(); - if (index >= size) throw ERROR.INDEX_OUT_OF_BOUNDS; - for (let i = 0; i < index; i++) this.skipAny(); - return this; - } - - public find(path: Path): this { - for (let i = 0; i < path.length; i++) { - const segment = path[i]; - if (typeof segment === 'string') this.findKey(segment); - else this.findIndex(segment); - } - return this; - } -} diff --git a/src/json-pack/cbor/CborDecoderBase.ts b/src/json-pack/cbor/CborDecoderBase.ts deleted file mode 100644 index f99bf7f0a5..0000000000 --- a/src/json-pack/cbor/CborDecoderBase.ts +++ /dev/null @@ -1,348 +0,0 @@ -import {CONST, ERROR, MAJOR} from './constants'; -import {decodeF16} from '../../util/buffers/f16'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {JsonPackValue} from '../JsonPackValue'; -import {Reader} from '../../util/buffers/Reader'; -import sharedCachedUtf8Decoder from '../../util/buffers/utf8/sharedCachedUtf8Decoder'; -import type {CachedUtf8Decoder} from '../../util/buffers/utf8/CachedUtf8Decoder'; -import type {IReader, IReaderResettable} from '../../util/buffers'; -import type {BinaryJsonDecoder, PackValue} from '../types'; - -export class CborDecoderBase - implements BinaryJsonDecoder -{ - public constructor( - public reader: R = new Reader() as any, - public readonly keyDecoder: CachedUtf8Decoder = sharedCachedUtf8Decoder, - ) {} - - public read(uint8: Uint8Array): PackValue { - this.reader.reset(uint8); - return this.val() as PackValue; - } - - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.val(); - } - - // -------------------------------------------------------- Any value reading - - public val(): unknown { - const reader = this.reader; - const octet = reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major < MAJOR.ARR) { - if (major < MAJOR.BIN) return major === MAJOR.UIN ? this.readUint(minor) : this.readNint(minor); - else return major === MAJOR.BIN ? this.readBin(minor) : this.readStr(minor); - } else { - if (major < MAJOR.TAG) return major === MAJOR.ARR ? this.readArr(minor) : this.readObj(minor); - else return major === MAJOR.TAG ? this.readTag(minor) : this.readTkn(minor); - } - } - - public readAnyRaw(octet: number): unknown { - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major < MAJOR.ARR) { - if (major < MAJOR.BIN) return major === MAJOR.UIN ? this.readUint(minor) : this.readNint(minor); - else return major === MAJOR.BIN ? this.readBin(minor) : this.readStr(minor); - } else { - if (major < MAJOR.TAG) return major === MAJOR.ARR ? this.readArr(minor) : this.readObj(minor); - else return major === MAJOR.TAG ? this.readTag(minor) : this.readTkn(minor); - } - } - - public readMinorLen(minor: number): number { - if (minor < 24) return minor; - switch (minor) { - case 24: - return this.reader.u8(); - case 25: - return this.reader.u16(); - case 26: - return this.reader.u32(); - case 27: - return Number(this.reader.u64()); - case 31: - return -1; - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - // ----------------------------------------------------- Unsigned int reading - - public readUint(minor: number): number | bigint { - if (minor < 25) { - return minor === 24 ? this.reader.u8() : minor; - } else { - if (minor < 27) { - return minor === 25 ? this.reader.u16() : this.reader.u32(); - } else { - const num = this.reader.u64(); - return num > CONST.MAX_UINT ? num : Number(num); - } - } - } - - // ----------------------------------------------------- Negative int reading - - public readNint(minor: number): number | bigint { - if (minor < 25) { - return minor === 24 ? -this.reader.u8() - 1 : -minor - 1; - } else { - if (minor < 27) { - return minor === 25 ? -this.reader.u16() - 1 : -this.reader.u32() - 1; - } else { - const num = this.reader.u64(); - return num > CONST.MAX_UINT - 1 ? -num - BigInt(1) : -Number(num) - 1; - } - } - } - - // ----------------------------------------------------------- Binary reading - - public readBin(minor: number): Uint8Array { - const reader = this.reader; - if (minor <= 23) return reader.buf(minor); - switch (minor) { - case 24: - return reader.buf(reader.u8()); - case 25: - return reader.buf(reader.u16()); - case 26: - return reader.buf(reader.u32()); - case 27: - return reader.buf(Number(reader.u64())); - case 31: { - let size = 0; - const list: Uint8Array[] = []; - while (this.reader.peak() !== CONST.END) { - const uint8 = this.readBinChunk(); - size += uint8.length; - list.push(uint8); - } - this.reader.x++; - const res = new Uint8Array(size); - let offset = 0; - const length = list.length; - for (let i = 0; i < length; i++) { - const arr = list[i]; - res.set(arr, offset); - offset += arr.length; - } - return res; - } - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - public readBinChunk(): Uint8Array { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.BIN) throw ERROR.UNEXPECTED_BIN_CHUNK_MAJOR; - if (minor > 27) throw ERROR.UNEXPECTED_BIN_CHUNK_MINOR; - return this.readBin(minor); - } - - // ----------------------------------------------------------- String reading - - public readAsStr(): string { - const reader = this.reader; - const octet = reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.STR) throw ERROR.UNEXPECTED_STR_MAJOR; - return this.readStr(minor); - } - - public readStr(minor: number): string { - const reader = this.reader; - if (minor <= 23) return reader.utf8(minor); - switch (minor) { - case 24: - return reader.utf8(reader.u8()); - case 25: - return reader.utf8(reader.u16()); - case 26: - return reader.utf8(reader.u32()); - case 27: - return reader.utf8(Number(reader.u64())); - case 31: { - let str = ''; - while (reader.peak() !== CONST.END) str += this.readStrChunk(); - this.reader.x++; - return str; - } - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - public readStrLen(minor: number): number { - if (minor <= 23) return minor; - switch (minor) { - case 24: - return this.reader.u8(); - case 25: - return this.reader.u16(); - case 26: - return this.reader.u32(); - case 27: - return Number(this.reader.u64()); - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - public readStrChunk(): string { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.STR) throw ERROR.UNEXPECTED_STR_CHUNK_MAJOR; - if (minor > 27) throw ERROR.UNEXPECTED_STR_CHUNK_MINOR; - return this.readStr(minor); - } - - // ------------------------------------------------------------ Array reading - - public readArr(minor: number): unknown[] { - const length = this.readMinorLen(minor); - if (length >= 0) return this.readArrRaw(length); - return this.readArrIndef(); - } - - public readArrRaw(length: number): unknown[] { - const arr: unknown[] = []; - for (let i = 0; i < length; i++) arr.push(this.val()); - return arr; - } - - public readArrIndef(): unknown[] { - const arr: unknown[] = []; - while (this.reader.peak() !== CONST.END) arr.push(this.val()); - this.reader.x++; - return arr; - } - - // ----------------------------------------------------------- Object reading - - public readObj(minor: number): Record { - if (minor < 28) { - let length = minor; - switch (minor) { - case 24: - length = this.reader.u8(); - break; - case 25: - length = this.reader.u16(); - break; - case 26: - length = this.reader.u32(); - break; - case 27: - length = Number(this.reader.u64()); - break; - } - const obj: Record = {}; - for (let i = 0; i < length; i++) { - const key = this.key(); - if (key === '__proto__') throw ERROR.UNEXPECTED_OBJ_KEY; - const value = this.val(); - obj[key] = value; - } - return obj; - } else if (minor === 31) return this.readObjIndef(); - else throw ERROR.UNEXPECTED_MINOR; - } - - /** Remove this? */ - public readObjRaw(length: number): Record { - const obj: Record = {}; - for (let i = 0; i < length; i++) { - const key = this.key(); - const value = this.val(); - obj[key] = value; - } - return obj; - } - - public readObjIndef(): Record { - const obj: Record = {}; - while (this.reader.peak() !== CONST.END) { - const key = this.key(); - if (this.reader.peak() === CONST.END) throw ERROR.UNEXPECTED_OBJ_BREAK; - const value = this.val(); - obj[key] = value; - } - this.reader.x++; - return obj; - } - - public key(): string { - const octet = this.reader.u8(); - const major = octet >> 5; - const minor = octet & CONST.MINOR_MASK; - if (major !== MAJOR.STR) return String(this.readAnyRaw(octet)); - const length = this.readStrLen(minor); - if (length > 31) return this.reader.utf8(length); - const key = this.keyDecoder.decode(this.reader.uint8, this.reader.x, length); - this.reader.skip(length); - return key; - } - - // -------------------------------------------------------------- Tag reading - - public readTag(minor: number): JsonPackExtension | unknown { - if (minor <= 23) return this.readTagRaw(minor); - switch (minor) { - case 24: - return this.readTagRaw(this.reader.u8()); - case 25: - return this.readTagRaw(this.reader.u16()); - case 26: - return this.readTagRaw(this.reader.u32()); - case 27: - return this.readTagRaw(Number(this.reader.u64())); - default: - throw ERROR.UNEXPECTED_MINOR; - } - } - - public readTagRaw(tag: number): JsonPackExtension | unknown { - return new JsonPackExtension(tag, this.val()); - } - - // ------------------------------------------------------------ Token reading - - public readTkn(minor: number): number | true | false | null | undefined | JsonPackValue { - switch (minor) { - case 0xf4 & CONST.MINOR_MASK: - return false; - case 0xf5 & CONST.MINOR_MASK: - return true; - case 0xf6 & CONST.MINOR_MASK: - return null; - case 0xf7 & CONST.MINOR_MASK: - return undefined; - case 0xf8 & CONST.MINOR_MASK: - return new JsonPackValue(this.reader.u8()); - case 0xf9 & CONST.MINOR_MASK: - return this.f16(); - case 0xfa & CONST.MINOR_MASK: - return this.reader.f32(); - case 0xfb & CONST.MINOR_MASK: - return this.reader.f64(); - } - if (minor <= 23) return new JsonPackValue(minor); - throw ERROR.UNEXPECTED_MINOR; - } - - public f16(): number { - return decodeF16(this.reader.u16()); - } -} diff --git a/src/json-pack/cbor/CborDecoderDag.ts b/src/json-pack/cbor/CborDecoderDag.ts deleted file mode 100644 index e1ab74b909..0000000000 --- a/src/json-pack/cbor/CborDecoderDag.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {JsonPackExtension} from '../JsonPackExtension'; -import {CborDecoder} from './CborDecoder'; - -export class CborDecoderDag extends CborDecoder { - public readTagRaw(tag: number): JsonPackExtension | unknown { - const value = this.val(); - return tag === 42 ? new JsonPackExtension(tag, value) : value; - } -} diff --git a/src/json-pack/cbor/CborEncoder.ts b/src/json-pack/cbor/CborEncoder.ts deleted file mode 100644 index facf3d1568..0000000000 --- a/src/json-pack/cbor/CborEncoder.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {isFloat32} from '../../util/buffers/isFloat32'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {CborEncoderFast} from './CborEncoderFast'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import {JsonPackValue} from '../JsonPackValue'; - -export class CborEncoder extends CborEncoderFast { - /** - * Called when the encoder encounters a value that it does not know how to encode. - * - * @param value Some JavaScript value. - */ - public writeUnknown(value: unknown): void { - this.writeNull(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'boolean': - return this.writer.u8(0xf4 + +value); - case 'object': { - if (!value) return this.writer.u8(0xf6); - const constructor = value.constructor; - switch (constructor) { - case Object: - return this.writeObj(value as Record); - case Array: - return this.writeArr(value as unknown[]); - case Uint8Array: - return this.writeBin(value as Uint8Array); - case Map: - return this.writeMap(value as Map); - case JsonPackExtension: - return this.writeTag((value).tag, (value).val); - case JsonPackValue: - const buf = (value as JsonPackValue).val; - return this.writer.buf(buf, buf.length); - default: - return this.writeUnknown(value); - } - } - case 'undefined': - return this.writeUndef(); - case 'bigint': - return this.writeBigInt(value as bigint); - default: - return this.writeUnknown(value); - } - } - - public writeFloat(float: number): void { - if (isFloat32(float)) this.writer.u8f32(0xfa, float); - else this.writer.u8f64(0xfb, float); - } - - public writeMap(map: Map): void { - this.writeMapHdr(map.size); - map.forEach((value, key) => { - this.writeAny(key); - this.writeAny(value); - }); - } - - public writeUndef(): void { - this.writer.u8(0xf7); - } -} diff --git a/src/json-pack/cbor/CborEncoderDag.ts b/src/json-pack/cbor/CborEncoderDag.ts deleted file mode 100644 index 91294852d9..0000000000 --- a/src/json-pack/cbor/CborEncoderDag.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {CborEncoderStable} from './CborEncoderStable'; - -export class CborEncoderDag extends CborEncoderStable { - public writeUndef(): void { - this.writeNull(); - } - - public writeFloat(float: number): void { - if (float !== float) return this.writeNull(); - if (!Number.isFinite(float)) return this.writeNull(); - this.writer.u8f64(0xfb, float); - } - - public writeTag(tag: number, value: unknown): void { - if (tag === 42) this.writeTagHdr(tag); - this.writeAny(value); - } -} diff --git a/src/json-pack/cbor/CborEncoderFast.ts b/src/json-pack/cbor/CborEncoderFast.ts deleted file mode 100644 index b54ff07cc2..0000000000 --- a/src/json-pack/cbor/CborEncoderFast.ts +++ /dev/null @@ -1,333 +0,0 @@ -import {Writer} from '../../util/buffers/Writer'; -import {CONST, MAJOR_OVERLAY} from './constants'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder, StreamingBinaryJsonEncoder, TlvBinaryJsonEncoder} from '../types'; -import type {Slice} from '../../util/buffers/Slice'; - -const isSafeInteger = Number.isSafeInteger; - -/** - * Fast CBOR encoder supports only JSON values. Use regular `CborEncoder` if - * you need ability to encode all CBOR value types. - */ -export class CborEncoderFast - implements BinaryJsonEncoder, StreamingBinaryJsonEncoder, TlvBinaryJsonEncoder -{ - constructor(public readonly writer: W = new Writer() as any) {} - - public encode(value: unknown): Uint8Array { - this.writeAny(value); - return this.writer.flush(); - } - - public encodeToSlice(value: unknown): Slice { - this.writeAny(value); - return this.writer.flushSlice(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'boolean': - return this.writer.u8(0xf4 + +value); - case 'object': { - if (!value) return this.writer.u8(0xf6); - const constructor = value.constructor; - switch (constructor) { - case Array: - return this.writeArr(value as unknown[]); - default: - return this.writeObj(value as Record); - } - } - } - } - - public writeCbor(): void { - this.writer.u8u16(0xd9, 0xd9f7); - } - - public writeEnd(): void { - this.writer.u8(CONST.END); - } - - public writeNull(): void { - this.writer.u8(0xf6); - } - - public writeBoolean(bool: boolean): void { - if (bool) this.writer.u8(0xf5); - else this.writer.u8(0xf4); - } - - public writeNumber(num: number): void { - if (isSafeInteger(num)) this.writeInteger(num); - else if (typeof num === 'bigint') this.writeBigInt(num); - else this.writeFloat(num); - } - - public writeBigInt(int: bigint): void { - if (int >= 0) this.writeBigUint(int); - else this.writeBigSint(int); - } - - public writeBigUint(uint: bigint): void { - if (uint <= Number.MAX_SAFE_INTEGER) return this.writeUInteger(Number(uint)); - this.writer.u8u64(0x1b, uint); - } - - public writeBigSint(int: bigint): void { - if (int >= Number.MIN_SAFE_INTEGER) return this.encodeNint(Number(int)); - const uint = -BigInt(1) - int; - this.writer.u8u64(0x3b, uint); - } - - public writeInteger(int: number): void { - if (int >= 0) this.writeUInteger(int); - else this.encodeNint(int); - } - - public writeUInteger(uint: number): void { - const writer = this.writer; - writer.ensureCapacity(9); - const uint8 = writer.uint8; - let x = writer.x; - if (uint <= 23) { - uint8[x++] = MAJOR_OVERLAY.UIN + uint; - } else if (uint <= 0xff) { - uint8[x++] = 0x18; - uint8[x++] = uint; - } else if (uint <= 0xffff) { - uint8[x++] = 0x19; - writer.view.setUint16(x, uint); - x += 2; - } else if (uint <= 0xffffffff) { - uint8[x++] = 0x1a; - writer.view.setUint32(x, uint); - x += 4; - } else { - uint8[x++] = 0x1b; - writer.view.setBigUint64(x, BigInt(uint)); - x += 8; - } - writer.x = x; - } - - /** @deprecated Remove and use `writeNumber` instead. */ - public encodeNumber(num: number): void { - this.writeNumber(num); - } - - /** @deprecated Remove and use `writeInteger` instead. */ - public encodeInteger(int: number): void { - this.writeInteger(int); - } - - /** @deprecated */ - public encodeUint(uint: number): void { - this.writeUInteger(uint); - } - - public encodeNint(int: number): void { - const uint = -1 - int; - const writer = this.writer; - writer.ensureCapacity(9); - const uint8 = writer.uint8; - let x = writer.x; - if (uint < 24) { - uint8[x++] = MAJOR_OVERLAY.NIN + uint; - } else if (uint <= 0xff) { - uint8[x++] = 0x38; - uint8[x++] = uint; - } else if (uint <= 0xffff) { - uint8[x++] = 0x39; - writer.view.setUint16(x, uint); - x += 2; - } else if (uint <= 0xffffffff) { - uint8[x++] = 0x3a; - writer.view.setUint32(x, uint); - x += 4; - } else { - uint8[x++] = 0x3b; - writer.view.setBigUint64(x, BigInt(uint)); - x += 8; - } - writer.x = x; - } - - public writeFloat(float: number): void { - this.writer.u8f64(0xfb, float); - } - - public writeBin(buf: Uint8Array): void { - const length = buf.length; - this.writeBinHdr(length); - this.writer.buf(buf, length); - } - - public writeBinHdr(length: number): void { - const writer = this.writer; - if (length <= 23) writer.u8(MAJOR_OVERLAY.BIN + length); - else if (length <= 0xff) writer.u16((0x58 << 8) + length); - else if (length <= 0xffff) writer.u8u16(0x59, length); - else if (length <= 0xffffffff) writer.u8u32(0x5a, length); - else writer.u8u64(0x5b, length); - } - - public writeStr(str: string): void { - const writer = this.writer; - const length = str.length; - const maxSize = length * 4; - writer.ensureCapacity(5 + maxSize); - const uint8 = writer.uint8; - let lengthOffset: number = writer.x; - if (maxSize <= 23) writer.x++; - else if (maxSize <= 0xff) { - uint8[writer.x++] = 0x78; - lengthOffset = writer.x; - writer.x++; - } else if (maxSize <= 0xffff) { - uint8[writer.x++] = 0x79; - lengthOffset = writer.x; - writer.x += 2; - } else { - uint8[writer.x++] = 0x7a; - lengthOffset = writer.x; - writer.x += 4; - } - const bytesWritten = writer.utf8(str); - if (maxSize <= 23) uint8[lengthOffset] = MAJOR_OVERLAY.STR + bytesWritten; - else if (maxSize <= 0xff) uint8[lengthOffset] = bytesWritten; - else if (maxSize <= 0xffff) writer.view.setUint16(lengthOffset, bytesWritten); - else writer.view.setUint32(lengthOffset, bytesWritten); - } - - public writeStrHdr(length: number): void { - const writer = this.writer; - if (length <= 23) writer.u8(MAJOR_OVERLAY.STR + length); - else if (length <= 0xff) writer.u16((0x78 << 8) + length); - else if (length <= 0xffff) writer.u8u16(0x79, length); - else writer.u8u32(0x7a, length); - } - - public writeAsciiStr(str: string): void { - this.writeStrHdr(str.length); - this.writer.ascii(str); - } - - public writeArr(arr: unknown[]): void { - const length = arr.length; - this.writeArrHdr(length); - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - } - - public writeArrHdr(length: number): void { - const writer = this.writer; - if (length <= 23) writer.u8(MAJOR_OVERLAY.ARR + length); - else if (length <= 0xff) writer.u16((0x98 << 8) + length); - else if (length <= 0xffff) writer.u8u16(0x99, length); - else if (length <= 0xffffffff) writer.u8u32(0x9a, length); - else writer.u8u64(0x9b, length); - } - - public writeObj(obj: Record): void { - const keys = Object.keys(obj); - const length = keys.length; - this.writeObjHdr(length); - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } - - public writeObjHdr(length: number): void { - const writer = this.writer; - if (length <= 23) writer.u8(MAJOR_OVERLAY.MAP + length); - else if (length <= 0xff) writer.u16((0xb8 << 8) + length); - else if (length <= 0xffff) writer.u8u16(0xb9, length); - else if (length <= 0xffffffff) writer.u8u32(0xba, length); - else writer.u8u64(0xbb, length); - } - - public writeMapHdr(length: number): void { - this.writeObjHdr(length); - } - - public writeStartMap(): void { - this.writer.u8(0xbf); - } - - public writeTag(tag: number, value: unknown): void { - this.writeTagHdr(tag); - this.writeAny(value); - } - - public writeTagHdr(tag: number): void { - const writer = this.writer; - if (tag <= 23) writer.u8(MAJOR_OVERLAY.TAG + tag); - else if (tag <= 0xff) writer.u16((0xd8 << 8) + tag); - else if (tag <= 0xffff) writer.u8u16(0xd9, tag); - else if (tag <= 0xffffffff) writer.u8u32(0xda, tag); - else writer.u8u64(0xdb, tag); - } - - public writeTkn(value: number): void { - const writer = this.writer; - if (value <= 23) writer.u8(MAJOR_OVERLAY.TKN + value); - else if (value <= 0xff) writer.u16((0xf8 << 8) + value); - } - - // ------------------------------------------------------- Streaming encoding - - public writeStartStr(): void { - this.writer.u8(0x7f); - } - - public writeStrChunk(str: string): void { - throw new Error('Not implemented'); - } - - public writeEndStr(): void { - throw new Error('Not implemented'); - } - - public writeStartBin(): void { - this.writer.u8(0x5f); - } - - public writeBinChunk(buf: Uint8Array): void { - throw new Error('Not implemented'); - } - - public writeEndBin(): void { - throw new Error('Not implemented'); - } - - public writeStartArr(): void { - this.writer.u8(0x9f); - } - - public writeArrChunk(item: unknown): void { - throw new Error('Not implemented'); - } - - public writeEndArr(): void { - this.writer.u8(CONST.END); - } - - public writeStartObj(): void { - this.writer.u8(0xbf); - } - - public writeObjChunk(key: string, value: unknown): void { - throw new Error('Not implemented'); - } - - public writeEndObj(): void { - this.writer.u8(CONST.END); - } -} diff --git a/src/json-pack/cbor/CborEncoderStable.ts b/src/json-pack/cbor/CborEncoderStable.ts deleted file mode 100644 index 45359574c4..0000000000 --- a/src/json-pack/cbor/CborEncoderStable.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {CborEncoder} from './CborEncoder'; -import {sort} from '../../util/sort/insertion2'; -import {MAJOR_OVERLAY} from './constants'; -import {objKeyCmp} from '../util/objKeyCmp'; - -const strHeaderLength = (strSize: number): 1 | 2 | 3 | 5 => { - if (strSize <= 23) return 1; - else if (strSize <= 0xff) return 2; - else if (strSize <= 0xffff) return 3; - else return 5; -}; - -export class CborEncoderStable extends CborEncoder { - public writeObj(obj: Record): void { - const keys = Object.keys(obj); - sort(keys, objKeyCmp); - const length = keys.length; - this.writeObjHdr(length); - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } - - /** @todo This implementation might be even faster than the default one, verify that. */ - public writeStr(str: string): void { - const writer = this.writer; - const length = str.length; - const maxSize = length * 4; - writer.ensureCapacity(5 + maxSize); - const headerLengthGuess = strHeaderLength(length); - const x0 = writer.x; - const x1 = x0 + headerLengthGuess; - writer.x = x1; - const bytesWritten = writer.utf8(str); - const uint8 = writer.uint8; - const headerLength = strHeaderLength(bytesWritten); - if (headerLength !== headerLengthGuess) { - const shift = headerLength - headerLengthGuess; - uint8.copyWithin(x1 + shift, x1, x1 + bytesWritten); - } - switch (headerLength) { - case 1: - uint8[x0] = MAJOR_OVERLAY.STR + bytesWritten; - break; - case 2: - uint8[x0] = 0x78; - uint8[x0 + 1] = bytesWritten; - break; - case 3: { - uint8[x0] = 0x79; - writer.view.setUint16(x0 + 1, bytesWritten); - break; - } - case 5: { - uint8[x0] = 0x7a; - writer.view.setUint32(x0 + 1, bytesWritten); - break; - } - } - writer.x = x0 + headerLength + bytesWritten; - } - - public writeUndef(): void { - this.writeNull(); - } -} diff --git a/src/json-pack/cbor/README.md b/src/json-pack/cbor/README.md deleted file mode 100644 index a24fd750e6..0000000000 --- a/src/json-pack/cbor/README.md +++ /dev/null @@ -1,490 +0,0 @@ -# `json-pack` CBOR Codec - - -## Implementation details - -- Map keys are treated as strings. - - To decode a map with non-string keys, use `decoder.readAsMap()` method. - - When encoding JavaScript `Object`, map keys are encoded as strings. - - Full encoder supports `Map` object encoding, where keys can be any type. - - When decoding CBOR, map keys are decoded as strings. If a non-string value - is encountered, it is decoded and cast to a string. -- Half-precision `f16` floats are decoded to JavaScript `number`, however, - encoder does not support half-precision floats—floats are encoded as - `f32` or `f64`. - - -## Benchmarks - -### Encoding - -``` -npx ts-node benchmarks/json-pack/bench.encoding.cbor.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.1.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 2,339,523 ops/sec ยฑ0.50% (99 runs sampled) -๐Ÿคž JSON.stringify() x 3,802,757 ops/sec ยฑ0.17% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 6,132,816 ops/sec ยฑ0.43% (99 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 6,248,575 ops/sec ยฑ0.13% (98 runs sampled) -๐Ÿคž cbor-x x 4,924,643 ops/sec ยฑ0.31% (99 runs sampled) -๐Ÿคž cbor-js x 670,013 ops/sec ยฑ1.51% (80 runs sampled) -๐Ÿคž cborg x 777,829 ops/sec ยฑ0.16% (98 runs sampled) -๐Ÿคž cbor-sync x 444,785 ops/sec ยฑ3.07% (96 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 208,448 ops/sec ยฑ0.07% (101 runs sampled) -๐Ÿคž JSON.stringify() x 335,826 ops/sec ยฑ0.14% (101 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 468,552 ops/sec ยฑ0.31% (96 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 446,904 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿคž cbor-x x 400,380 ops/sec ยฑ0.89% (91 runs sampled) -๐Ÿคž cbor-js x 109,455 ops/sec ยฑ0.13% (98 runs sampled) -๐Ÿคž cborg x 60,584 ops/sec ยฑ0.10% (102 runs sampled) -๐Ÿคž cbor-sync x 75,523 ops/sec ยฑ0.21% (96 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 64,232 ops/sec ยฑ0.07% (99 runs sampled) -๐Ÿคž JSON.stringify() x 108,186 ops/sec ยฑ0.24% (101 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 135,553 ops/sec ยฑ0.11% (101 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 130,092 ops/sec ยฑ0.24% (100 runs sampled) -๐Ÿคž cbor-x x 110,045 ops/sec ยฑ0.63% (95 runs sampled) -๐Ÿคž cbor-js x 33,044 ops/sec ยฑ0.11% (102 runs sampled) -๐Ÿคž cborg x 18,516 ops/sec ยฑ0.13% (101 runs sampled) -๐Ÿคž cbor-sync x 25,829 ops/sec ยฑ0.43% (98 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 7,175 ops/sec ยฑ0.76% (98 runs sampled) -๐Ÿคž JSON.stringify() x 7,783 ops/sec ยฑ0.51% (101 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 5,759 ops/sec ยฑ0.53% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 5,569 ops/sec ยฑ0.43% (100 runs sampled) -๐Ÿคž cbor-x x 5,671 ops/sec ยฑ0.71% (94 runs sampled) -๐Ÿคž cbor-js x 2,513 ops/sec ยฑ0.40% (100 runs sampled) -๐Ÿคž cborg x 818 ops/sec ยฑ1.04% (92 runs sampled) -๐Ÿคž cbor-sync x 1,579 ops/sec ยฑ0.34% (98 runs sampled) -Fastest is ๐Ÿคž JSON.stringify() ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 182,418 ops/sec ยฑ0.69% (99 runs sampled) -๐Ÿคž JSON.stringify() x 166,880 ops/sec ยฑ5.89% (82 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 276,754 ops/sec ยฑ1.11% (99 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 272,113 ops/sec ยฑ0.77% (94 runs sampled) -๐Ÿคž cbor-x x 193,156 ops/sec ยฑ0.49% (96 runs sampled) -๐Ÿคž cbor-js x 73,180 ops/sec ยฑ0.38% (100 runs sampled) -๐Ÿคž cborg x 35,937 ops/sec ยฑ0.19% (95 runs sampled) -๐Ÿคž cbor-sync x 53,410 ops/sec ยฑ0.66% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoderFast -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 148,910 ops/sec ยฑ0.24% (98 runs sampled) -๐Ÿคž JSON.stringify() x 172,582 ops/sec ยฑ0.06% (102 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 276,018 ops/sec ยฑ0.64% (92 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 278,835 ops/sec ยฑ0.85% (92 runs sampled) -๐Ÿคž cbor-x x 209,737 ops/sec ยฑ0.44% (95 runs sampled) -๐Ÿคž cbor-js x 29,304 ops/sec ยฑ0.15% (101 runs sampled) -๐Ÿคž cborg x 61,577 ops/sec ยฑ0.10% (102 runs sampled) -๐Ÿคž cbor-sync x 73,548 ops/sec ยฑ2.14% (93 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder,๐Ÿคž json-joy/json-pack CborEncoderFast --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 28,860 ops/sec ยฑ0.06% (99 runs sampled) -๐Ÿคž JSON.stringify() x 59,800 ops/sec ยฑ0.07% (99 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 403,027 ops/sec ยฑ1.97% (93 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 415,001 ops/sec ยฑ1.38% (95 runs sampled) -๐Ÿคž cbor-x x 364,240 ops/sec ยฑ1.95% (85 runs sampled) -๐Ÿคž cbor-js x 13,370 ops/sec ยฑ0.11% (101 runs sampled) -๐Ÿคž cborg x 118,723 ops/sec ยฑ0.54% (99 runs sampled) -๐Ÿคž cbor-sync x 117,072 ops/sec ยฑ0.17% (94 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 1,016,012 ops/sec ยฑ0.12% (102 runs sampled) -๐Ÿคž JSON.stringify() x 1,828,820 ops/sec ยฑ0.15% (102 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 1,848,409 ops/sec ยฑ0.56% (99 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 1,860,103 ops/sec ยฑ0.18% (98 runs sampled) -๐Ÿคž cbor-x x 1,360,519 ops/sec ยฑ0.22% (98 runs sampled) -๐Ÿคž cbor-js x 367,320 ops/sec ยฑ0.25% (97 runs sampled) -๐Ÿคž cborg x 278,084 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿคž cbor-sync x 181,966 ops/sec ยฑ0.17% (92 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack CborEncoder --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 1,231,696 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿคž JSON.stringify() x 1,610,733 ops/sec ยฑ0.16% (100 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 2,775,684 ops/sec ยฑ0.17% (101 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 3,112,233 ops/sec ยฑ0.18% (100 runs sampled) -๐Ÿคž cbor-x x 3,264,422 ops/sec ยฑ0.14% (101 runs sampled) -๐Ÿคž cbor-js x 558,877 ops/sec ยฑ1.31% (89 runs sampled) -๐Ÿคž cborg x 296,104 ops/sec ยฑ0.14% (100 runs sampled) -๐Ÿคž cbor-sync x 379,437 ops/sec ยฑ0.28% (99 runs sampled) -Fastest is ๐Ÿคž cbor-x ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿคž Buffer.from(JSON.stringify()) x 1,101,690 ops/sec ยฑ0.17% (98 runs sampled) -๐Ÿคž JSON.stringify() x 1,560,523 ops/sec ยฑ0.14% (98 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoderFast x 1,352,703 ops/sec ยฑ0.24% (96 runs sampled) -๐Ÿคž json-joy/json-pack CborEncoder x 1,371,395 ops/sec ยฑ0.24% (101 runs sampled) -๐Ÿคž cbor-x x 1,975,990 ops/sec ยฑ0.19% (98 runs sampled) -๐Ÿคž cbor-js x 525,540 ops/sec ยฑ1.25% (91 runs sampled) -๐Ÿคž cborg x 227,011 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿคž cbor-sync x 418,451 ops/sec ยฑ0.30% (97 runs sampled) -Fastest is ๐Ÿคž cbor-x -``` - -Node 18: - -``` -npx ts-node benchmarks/json-pack/bench.cbor.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 Max ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 6,233,741 ops/sec ยฑ0.48% (97 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 6,284,071 ops/sec ยฑ0.52% (98 runs sampled) -๐Ÿ‘ cborg x 593,217 ops/sec ยฑ0.75% (98 runs sampled) -๐Ÿ‘ cbor-x x 4,360,950 ops/sec ยฑ0.61% (92 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 450,797 ops/sec ยฑ0.43% (94 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 465,790 ops/sec ยฑ0.39% (97 runs sampled) -๐Ÿ‘ cborg x 48,343 ops/sec ยฑ0.57% (99 runs sampled) -๐Ÿ‘ cbor-x x 414,580 ops/sec ยฑ0.38% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 132,873 ops/sec ยฑ0.37% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 134,572 ops/sec ยฑ0.49% (96 runs sampled) -๐Ÿ‘ cborg x 14,615 ops/sec ยฑ0.59% (96 runs sampled) -๐Ÿ‘ cbor-x x 114,106 ops/sec ยฑ0.46% (100 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 5,498 ops/sec ยฑ0.60% (97 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 5,474 ops/sec ยฑ1.15% (94 runs sampled) -๐Ÿ‘ cborg x 659 ops/sec ยฑ0.99% (92 runs sampled) -๐Ÿ‘ cbor-x x 5,635 ops/sec ยฑ0.76% (96 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 279,077 ops/sec ยฑ0.52% (96 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 279,231 ops/sec ยฑ0.35% (98 runs sampled) -๐Ÿ‘ cborg x 26,533 ops/sec ยฑ0.62% (95 runs sampled) -๐Ÿ‘ cbor-x x 194,635 ops/sec ยฑ0.58% (95 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder,๐Ÿ‘ json-joy/json-pack CborEncoderFast -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 295,817 ops/sec ยฑ0.61% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 293,260 ops/sec ยฑ0.37% (97 runs sampled) -๐Ÿ‘ cborg x 46,351 ops/sec ยฑ0.46% (99 runs sampled) -๐Ÿ‘ cbor-x x 221,037 ops/sec ยฑ0.49% (96 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoderFast --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 397,191 ops/sec ยฑ1.10% (93 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 393,080 ops/sec ยฑ0.86% (91 runs sampled) -๐Ÿ‘ cborg x 73,491 ops/sec ยฑ0.51% (98 runs sampled) -๐Ÿ‘ cbor-x x 386,859 ops/sec ยฑ0.82% (94 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoderFast --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 1,746,092 ops/sec ยฑ0.40% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 1,745,521 ops/sec ยฑ0.40% (99 runs sampled) -๐Ÿ‘ cborg x 198,683 ops/sec ยฑ0.57% (96 runs sampled) -๐Ÿ‘ cbor-x x 1,276,409 ops/sec ยฑ0.62% (93 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoderFast,๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 2,558,939 ops/sec ยฑ0.46% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 2,575,323 ops/sec ยฑ0.39% (95 runs sampled) -๐Ÿ‘ cborg x 230,191 ops/sec ยฑ0.40% (98 runs sampled) -๐Ÿ‘ cbor-x x 2,966,610 ops/sec ยฑ0.34% (97 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ json-joy/json-pack CborEncoderFast x 1,318,484 ops/sec ยฑ0.45% (100 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoder x 1,332,239 ops/sec ยฑ0.40% (100 runs sampled) -๐Ÿ‘ cborg x 168,853 ops/sec ยฑ0.42% (96 runs sampled) -๐Ÿ‘ cbor-x x 1,824,744 ops/sec ยฑ0.43% (95 runs sampled) -Fastest is ๐Ÿ‘ cbor-x -``` - -### Decoding - -``` -npx ts-node benchmarks/json-pack/bench.cbor.decoding.ts -========================================================================== Benchmark: CBOR Decoding -Warmup: 1000x , Node.js: v20.2.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Combined, 634613 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 3,869 ops/sec ยฑ0.18% (98 runs sampled) -๐Ÿ‘Ž cbor-x x 3,636 ops/sec ยฑ0.13% (100 runs sampled) -๐Ÿ‘ cborg x 1,848 ops/sec ยฑ0.27% (99 runs sampled) -๐Ÿ‘ cbor x 313 ops/sec ยฑ0.85% (95 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder ---------------------------------------------------------------------------- Small object, 274 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 4,547,927 ops/sec ยฑ0.13% (98 runs sampled) -๐Ÿ‘ cbor-x x 4,146,745 ops/sec ยฑ0.15% (94 runs sampled) -๐Ÿ‘ cborg x 1,979,229 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘ cbor x 133,271 ops/sec ยฑ2.51% (92 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder ------------------------------------------------------------------------- Typical object, 8253 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 373,571 ops/sec ยฑ0.33% (97 runs sampled) -๐Ÿ‘ cbor-x x 254,533 ops/sec ยฑ0.57% (99 runs sampled) -๐Ÿ‘ cborg x 121,327 ops/sec ยฑ0.36% (97 runs sampled) -๐Ÿ‘ cbor x 19,516 ops/sec ยฑ0.22% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder -------------------------------------------------------------------------- Large object, 34563 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 108,250 ops/sec ยฑ0.70% (96 runs sampled) -๐Ÿ‘ cbor-x x 86,146 ops/sec ยฑ0.32% (101 runs sampled) -๐Ÿ‘ cborg x 33,641 ops/sec ยฑ0.56% (93 runs sampled) -๐Ÿ‘ cbor x 6,383 ops/sec ยฑ0.58% (97 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder -------------------------------------------------------------------- Very large object, 437014 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 4,374 ops/sec ยฑ0.31% (94 runs sampled) -๐Ÿ‘Ž cbor-x x 3,943 ops/sec ยฑ0.30% (98 runs sampled) -๐Ÿ‘ cborg x 1,685 ops/sec ยฑ0.29% (79 runs sampled) -๐Ÿ‘ cbor x 310 ops/sec ยฑ0.15% (89 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder ------------------------------------------------------------------ Object with many keys, 7575 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 92,625 ops/sec ยฑ0.51% (95 runs sampled) -๐Ÿ‘Ž cbor-x x 91,511 ops/sec ยฑ0.94% (93 runs sampled) -๐Ÿ‘ cborg x 54,355 ops/sec ยฑ0.41% (97 runs sampled) -๐Ÿ‘ cbor x 13,289 ops/sec ยฑ1.41% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder,๐Ÿ‘Ž cbor-x ------------------------------------------------------------------------- String ladder, 35622 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 240,683 ops/sec ยฑ0.34% (100 runs sampled) -๐Ÿ‘ cbor-x x 324,927 ops/sec ยฑ0.40% (96 runs sampled) -๐Ÿ‘ cborg x 70,820 ops/sec ยฑ0.58% (95 runs sampled) -๐Ÿ‘ cbor x 24,792 ops/sec ยฑ0.76% (96 runs sampled) -Fastest is ๐Ÿ‘ cbor-x -------------------------------------------------------------------------- Long strings, 85228 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 96,957 ops/sec ยฑ0.50% (98 runs sampled) -๐Ÿ‘ cbor-x x 94,397 ops/sec ยฑ0.51% (94 runs sampled) -๐Ÿ‘ cborg x 69,925 ops/sec ยฑ6.38% (91 runs sampled) -๐Ÿ‘ cbor x 34,779 ops/sec ยฑ10.73% (79 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder -------------------------------------------------------------------------- Short strings, 1211 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 1,177,079 ops/sec ยฑ0.61% (94 runs sampled) -๐Ÿ‘ cbor-x x 1,070,770 ops/sec ยฑ1.19% (90 runs sampled) -๐Ÿ‘ cborg x 385,823 ops/sec ยฑ0.79% (94 runs sampled) -๐Ÿ‘ cbor x 53,147 ops/sec ยฑ0.91% (91 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborDecoder -------------------------------------------------------------------------------- Numbers, 1544 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 974,821 ops/sec ยฑ0.72% (98 runs sampled) -๐Ÿ‘ cbor-x x 1,576,220 ops/sec ยฑ0.68% (95 runs sampled) -๐Ÿ‘ cborg x 464,996 ops/sec ยฑ0.44% (94 runs sampled) -๐Ÿ‘ cbor x 34,161 ops/sec ยฑ0.76% (92 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ---------------------------------------------------------------------------------- Tokens, 530 bytes -๐Ÿ‘ json-joy/json-pack CborDecoder x 1,198,726 ops/sec ยฑ0.53% (96 runs sampled) -๐Ÿ‘ cbor-x x 1,927,307 ops/sec ยฑ0.67% (80 runs sampled) -๐Ÿ‘ cborg x 957,531 ops/sec ยฑ0.62% (98 runs sampled) -๐Ÿ‘ cbor x 44,276 ops/sec ยฑ10.58% (80 runs sampled) -Fastest is ๐Ÿ‘ cbor-x -``` - -### Other - -By writer buffer size: - -``` -npx ts-node benchmarks/json-pack/bench.writer-size.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 Max ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ 1 MB x 6,313,890 ops/sec ยฑ0.12% (101 runs sampled) -๐Ÿ‘ 256 KB x 6,289,685 ops/sec ยฑ0.11% (97 runs sampled) -๐Ÿ‘ 64 KB x 6,275,863 ops/sec ยฑ0.12% (100 runs sampled) -๐Ÿ‘ 16 KB x 6,254,832 ops/sec ยฑ0.24% (98 runs sampled) -๐Ÿ‘ 4 KB x 6,187,636 ops/sec ยฑ0.13% (99 runs sampled) -๐Ÿ‘ 1 KB x 5,890,157 ops/sec ยฑ0.14% (99 runs sampled) -Fastest is ๐Ÿ‘ 1 MB -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ 1 MB x 497,752 ops/sec ยฑ0.21% (100 runs sampled) -๐Ÿ‘ 256 KB x 495,574 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘ 64 KB x 494,724 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿ‘ 16 KB x 489,579 ops/sec ยฑ0.23% (97 runs sampled) -๐Ÿ‘ 4 KB x 455,526 ops/sec ยฑ0.34% (98 runs sampled) -๐Ÿ‘ 1 KB x 433,038 ops/sec ยฑ0.48% (97 runs sampled) -Fastest is ๐Ÿ‘ 1 MB --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ 1 MB x 140,580 ops/sec ยฑ0.39% (96 runs sampled) -๐Ÿ‘ 256 KB x 136,933 ops/sec ยฑ0.39% (92 runs sampled) -๐Ÿ‘ 64 KB x 139,697 ops/sec ยฑ0.27% (98 runs sampled) -๐Ÿ‘ 16 KB x 137,278 ops/sec ยฑ0.33% (98 runs sampled) -๐Ÿ‘ 4 KB x 130,838 ops/sec ยฑ0.19% (98 runs sampled) -๐Ÿ‘ 1 KB x 122,987 ops/sec ยฑ0.45% (94 runs sampled) -Fastest is ๐Ÿ‘ 1 MB --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ 1 MB x 5,883 ops/sec ยฑ0.12% (101 runs sampled) -๐Ÿ‘ 256 KB x 5,845 ops/sec ยฑ0.66% (91 runs sampled) -๐Ÿ‘ 64 KB x 5,783 ops/sec ยฑ0.26% (100 runs sampled) -๐Ÿ‘ 16 KB x 5,584 ops/sec ยฑ0.59% (94 runs sampled) -๐Ÿ‘ 4 KB x 5,648 ops/sec ยฑ0.35% (98 runs sampled) -๐Ÿ‘ 1 KB x 5,649 ops/sec ยฑ0.35% (95 runs sampled) -Fastest is ๐Ÿ‘ 1 MB,๐Ÿ‘ 256 KB ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ 1 MB x 282,535 ops/sec ยฑ0.34% (98 runs sampled) -๐Ÿ‘ 256 KB x 282,055 ops/sec ยฑ0.34% (95 runs sampled) -๐Ÿ‘ 64 KB x 286,786 ops/sec ยฑ0.22% (97 runs sampled) -๐Ÿ‘ 16 KB x 283,067 ops/sec ยฑ0.27% (97 runs sampled) -๐Ÿ‘ 4 KB x 281,647 ops/sec ยฑ0.24% (100 runs sampled) -๐Ÿ‘ 1 KB x 259,775 ops/sec ยฑ0.33% (96 runs sampled) -Fastest is ๐Ÿ‘ 64 KB -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ 1 MB x 308,326 ops/sec ยฑ0.23% (96 runs sampled) -๐Ÿ‘ 256 KB x 307,324 ops/sec ยฑ0.34% (100 runs sampled) -๐Ÿ‘ 64 KB x 305,368 ops/sec ยฑ0.23% (97 runs sampled) -๐Ÿ‘ 16 KB x 289,570 ops/sec ยฑ0.46% (99 runs sampled) -๐Ÿ‘ 4 KB x 270,486 ops/sec ยฑ0.52% (96 runs sampled) -๐Ÿ‘ 1 KB x 211,091 ops/sec ยฑ0.57% (95 runs sampled) -Fastest is ๐Ÿ‘ 1 MB,๐Ÿ‘ 256 KB --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ 1 MB x 446,622 ops/sec ยฑ0.48% (98 runs sampled) -๐Ÿ‘ 256 KB x 438,083 ops/sec ยฑ0.58% (94 runs sampled) -๐Ÿ‘ 64 KB x 421,277 ops/sec ยฑ0.50% (97 runs sampled) -๐Ÿ‘ 16 KB x 349,768 ops/sec ยฑ1.32% (93 runs sampled) -๐Ÿ‘ 4 KB x 350,886 ops/sec ยฑ0.76% (92 runs sampled) -๐Ÿ‘ 1 KB x 348,879 ops/sec ยฑ1.00% (92 runs sampled) -Fastest is ๐Ÿ‘ 1 MB --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ 1 MB x 2,003,291 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘ 256 KB x 2,002,815 ops/sec ยฑ0.30% (98 runs sampled) -๐Ÿ‘ 64 KB x 2,003,416 ops/sec ยฑ0.22% (98 runs sampled) -๐Ÿ‘ 16 KB x 1,973,326 ops/sec ยฑ0.31% (96 runs sampled) -๐Ÿ‘ 4 KB x 1,938,991 ops/sec ยฑ0.28% (98 runs sampled) -๐Ÿ‘ 1 KB x 1,815,441 ops/sec ยฑ0.24% (99 runs sampled) -Fastest is ๐Ÿ‘ 1 MB,๐Ÿ‘ 64 KB,๐Ÿ‘ 256 KB --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ 1 MB x 3,301,798 ops/sec ยฑ0.25% (99 runs sampled) -๐Ÿ‘ 256 KB x 3,284,645 ops/sec ยฑ0.30% (98 runs sampled) -๐Ÿ‘ 64 KB x 3,272,060 ops/sec ยฑ0.94% (96 runs sampled) -๐Ÿ‘ 16 KB x 3,317,569 ops/sec ยฑ0.25% (98 runs sampled) -๐Ÿ‘ 4 KB x 3,238,186 ops/sec ยฑ0.34% (96 runs sampled) -๐Ÿ‘ 1 KB x 3,017,336 ops/sec ยฑ0.68% (98 runs sampled) -Fastest is ๐Ÿ‘ 16 KB ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ 1 MB x 1,698,059 ops/sec ยฑ0.24% (101 runs sampled) -๐Ÿ‘ 256 KB x 1,644,210 ops/sec ยฑ0.70% (99 runs sampled) -๐Ÿ‘ 64 KB x 1,680,855 ops/sec ยฑ0.22% (97 runs sampled) -๐Ÿ‘ 16 KB x 1,651,801 ops/sec ยฑ0.35% (97 runs sampled) -๐Ÿ‘ 4 KB x 1,634,786 ops/sec ยฑ0.72% (95 runs sampled) -๐Ÿ‘ 1 KB x 1,633,724 ops/sec ยฑ0.25% (98 runs sampled) -Fastest is ๐Ÿ‘ 1 MB -``` - -Buffer vs Slice results: - -``` -npx ts-node benchmarks/json-pack/bench.slice.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 Max ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ Uint8Array x 6,375,191 ops/sec ยฑ0.29% (99 runs sampled) -๐Ÿ‘Ž Slice x 7,477,318 ops/sec ยฑ0.24% (99 runs sampled) -Fastest is ๐Ÿ‘Ž Slice -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ Uint8Array x 481,245 ops/sec ยฑ0.27% (95 runs sampled) -๐Ÿ‘Ž Slice x 487,881 ops/sec ยฑ0.24% (95 runs sampled) -Fastest is ๐Ÿ‘Ž Slice --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ Uint8Array x 139,034 ops/sec ยฑ0.28% (99 runs sampled) -๐Ÿ‘Ž Slice x 139,084 ops/sec ยฑ0.30% (93 runs sampled) -Fastest is ๐Ÿ‘Ž Slice,๐Ÿ‘ Uint8Array --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ Uint8Array x 5,992 ops/sec ยฑ0.17% (98 runs sampled) -๐Ÿ‘Ž Slice x 5,973 ops/sec ยฑ0.18% (101 runs sampled) -Fastest is ๐Ÿ‘ Uint8Array ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ Uint8Array x 283,511 ops/sec ยฑ0.21% (96 runs sampled) -๐Ÿ‘Ž Slice x 284,962 ops/sec ยฑ0.20% (100 runs sampled) -Fastest is ๐Ÿ‘Ž Slice -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ Uint8Array x 321,418 ops/sec ยฑ0.36% (97 runs sampled) -๐Ÿ‘Ž Slice x 324,213 ops/sec ยฑ0.34% (99 runs sampled) -Fastest is ๐Ÿ‘Ž Slice --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ Uint8Array x 417,711 ops/sec ยฑ0.72% (94 runs sampled) -๐Ÿ‘Ž Slice x 421,504 ops/sec ยฑ0.72% (94 runs sampled) -Fastest is ๐Ÿ‘Ž Slice --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ Uint8Array x 2,186,736 ops/sec ยฑ0.21% (97 runs sampled) -๐Ÿ‘Ž Slice x 2,283,908 ops/sec ยฑ0.26% (98 runs sampled) -Fastest is ๐Ÿ‘Ž Slice --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ Uint8Array x 3,305,268 ops/sec ยฑ0.21% (100 runs sampled) -๐Ÿ‘Ž Slice x 3,526,413 ops/sec ยฑ0.32% (97 runs sampled) -Fastest is ๐Ÿ‘Ž Slice ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ Uint8Array x 1,681,882 ops/sec ยฑ0.14% (100 runs sampled) -๐Ÿ‘Ž Slice x 1,721,419 ops/sec ยฑ0.35% (97 runs sampled) -Fastest is ๐Ÿ‘Ž Slice -``` - -### DAG-CBOR benchmarks - -``` -npx ts-node benchmarks/json-pack/bench.cbor-dag.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.4.0 , Arch: arm64 , CPU: Apple M1 ------------------------------------------------------------------------------ Combined, 63365 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 4,802 ops/sec ยฑ0.29% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 3,747 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘ cborg x 494 ops/sec ยฑ2.66% (74 runs sampled) -๐Ÿ‘ cbor-x x 4,119 ops/sec ยฑ0.29% (98 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 3,069 ops/sec ยฑ0.13% (101 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 5,373,104 ops/sec ยฑ0.64% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 5,046,824 ops/sec ยฑ0.37% (95 runs sampled) -๐Ÿ‘ cborg x 444,568 ops/sec ยฑ3.20% (85 runs sampled) -๐Ÿ‘ cbor-x x 3,876,636 ops/sec ยฑ0.54% (94 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 2,419,844 ops/sec ยฑ0.13% (97 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 444,693 ops/sec ยฑ0.24% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 395,237 ops/sec ยฑ0.55% (98 runs sampled) -๐Ÿ‘ cborg x 38,173 ops/sec ยฑ2.96% (89 runs sampled) -๐Ÿ‘ cbor-x x 369,911 ops/sec ยฑ0.20% (97 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 209,177 ops/sec ยฑ0.14% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 129,963 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 116,481 ops/sec ยฑ0.40% (97 runs sampled) -๐Ÿ‘ cborg x 11,650 ops/sec ยฑ2.91% (86 runs sampled) -๐Ÿ‘ cbor-x x 102,557 ops/sec ยฑ0.21% (96 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 63,205 ops/sec ยฑ0.11% (102 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 5,532 ops/sec ยฑ0.20% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 4,209 ops/sec ยฑ0.48% (99 runs sampled) -๐Ÿ‘ cborg x 563 ops/sec ยฑ2.88% (72 runs sampled) -๐Ÿ‘ cbor-x x 4,767 ops/sec ยฑ0.28% (99 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 6,769 ops/sec ยฑ0.19% (98 runs sampled) -Fastest is ๐Ÿ‘Ž Buffer.from(JSON.stringify) ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 263,890 ops/sec ยฑ0.26% (97 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 180,107 ops/sec ยฑ0.26% (98 runs sampled) -๐Ÿ‘ cborg x 25,011 ops/sec ยฑ2.62% (91 runs sampled) -๐Ÿ‘ cbor-x x 195,063 ops/sec ยฑ0.30% (97 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 192,690 ops/sec ยฑ0.19% (96 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder -------------------------------------------------------------------------- String ladder, 4037 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 204,028 ops/sec ยฑ0.20% (101 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 187,891 ops/sec ยฑ0.18% (97 runs sampled) -๐Ÿ‘ cborg x 30,417 ops/sec ยฑ3.11% (90 runs sampled) -๐Ÿ‘ cbor-x x 158,968 ops/sec ยฑ0.40% (100 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 56,748 ops/sec ยฑ0.09% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 407,500 ops/sec ยฑ0.21% (97 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 398,762 ops/sec ยฑ0.25% (98 runs sampled) -๐Ÿ‘ cborg x 86,854 ops/sec ยฑ2.66% (81 runs sampled) -๐Ÿ‘ cbor-x x 398,117 ops/sec ยฑ0.62% (98 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 28,748 ops/sec ยฑ0.40% (100 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 2,022,274 ops/sec ยฑ0.15% (100 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 1,543,637 ops/sec ยฑ0.16% (99 runs sampled) -๐Ÿ‘ cborg x 168,393 ops/sec ยฑ2.98% (88 runs sampled) -๐Ÿ‘ cbor-x x 1,348,931 ops/sec ยฑ0.51% (100 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 1,005,204 ops/sec ยฑ0.45% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack CborEncoder --------------------------------------------------------------------------------- Numbers, 331 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 1,290,404 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 1,293,654 ops/sec ยฑ0.12% (101 runs sampled) -๐Ÿ‘ cborg x 117,671 ops/sec ยฑ2.12% (92 runs sampled) -๐Ÿ‘ cbor-x x 1,547,093 ops/sec ยฑ0.19% (99 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 677,253 ops/sec ยฑ0.14% (99 runs sampled) -Fastest is ๐Ÿ‘ cbor-x ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ json-joy/json-pack CborEncoder x 1,525,319 ops/sec ยฑ0.37% (99 runs sampled) -๐Ÿ‘ json-joy/json-pack CborEncoderDag x 1,509,373 ops/sec ยฑ0.20% (98 runs sampled) -๐Ÿ‘ cborg x 225,699 ops/sec ยฑ1.00% (96 runs sampled) -๐Ÿ‘ cbor-x x 1,980,475 ops/sec ยฑ0.18% (99 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify) x 1,074,160 ops/sec ยฑ0.15% (97 runs sampled) -Fastest is ๐Ÿ‘ cbor-x -``` diff --git a/src/json-pack/cbor/__tests__/CborDecoder.readLevel.spec.ts b/src/json-pack/cbor/__tests__/CborDecoder.readLevel.spec.ts deleted file mode 100644 index 4a6e7d17f6..0000000000 --- a/src/json-pack/cbor/__tests__/CborDecoder.readLevel.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; -import {JsonPackValue} from '../../JsonPackValue'; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); - -test('decodes a primitive as is', () => { - const encoded = encoder.encode(1.1); - const decoded = decoder.decodeLevel(encoded); - expect(decoded).toBe(1.1); -}); - -test('decodes object with one level of values', () => { - const value = { - foo: 'bar', - baz: true, - }; - const encoded = encoder.encode(value); - const decoded = decoder.decodeLevel(encoded); - expect(decoded).toStrictEqual(value); -}); - -test('decodes nested objects and arrays as JsonPackValue, in object', () => { - const value = { - foo: 'bar', - baz: true, - arr: [1, 2, 3], - obj: { - a: 'b', - }, - }; - const encoded = encoder.encode(value); - const decoded = decoder.decodeLevel(encoded); - expect(decoded).toMatchObject({ - foo: 'bar', - baz: true, - arr: expect.any(JsonPackValue), - obj: expect.any(JsonPackValue), - }); - const arr = decoder.decode((decoded as any).arr.val); - expect(arr).toStrictEqual([1, 2, 3]); - const obj = decoder.decode((decoded as any).obj.val); - expect(obj).toStrictEqual({ - a: 'b', - }); -}); - -test('decodes array with one level of values', () => { - const value = [1, 'foo', true]; - const encoded = encoder.encode(value); - const decoded = decoder.decodeLevel(encoded); - expect(decoded).toStrictEqual(value); -}); - -test('decodes nested objects and arrays as JsonPackValue, in array', () => { - const value = [ - 1, - 'foo', - true, - [1, 2, 3], - { - a: 'b', - }, - ]; - const encoded = encoder.encode(value); - const decoded = decoder.decodeLevel(encoded); - expect(decoded).toMatchObject([1, 'foo', true, expect.any(JsonPackValue), expect.any(JsonPackValue)]); - const arr = decoder.decode((decoded as any)[3].val); - expect(arr).toStrictEqual([1, 2, 3]); - const obj = decoder.decode((decoded as any)[4].val); - expect(obj).toStrictEqual({ - a: 'b', - }); -}); diff --git a/src/json-pack/cbor/__tests__/CborDecoder.shallow-reading.spec.ts b/src/json-pack/cbor/__tests__/CborDecoder.shallow-reading.spec.ts deleted file mode 100644 index bc90ba2ce5..0000000000 --- a/src/json-pack/cbor/__tests__/CborDecoder.shallow-reading.spec.ts +++ /dev/null @@ -1,183 +0,0 @@ -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); - -describe('shallow reading values, without parsing the document', () => { - describe('reading object header', () => { - test('can read object size of empty oject', () => { - const encoded = encoder.encode({}); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(0); - }); - - test('can read small object size', () => { - const encoded = encoder.encode({foo: 'bar', a: 1, b: 2}); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(3); - }); - - test('medium size object size', () => { - const encoded = encoder.encode({ - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - }); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(17); - }); - - test('throws if value is not an object', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - expect(() => decoder.readObjHdr()).toThrowError(); - }); - }); - - describe('object key finding', () => { - test('can find object key', () => { - const encoded = encoder.encode({foo: 'bar'}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('foo').val(); - expect(decoded).toBe('bar'); - }); - - test('can find object key in the middle of the object', () => { - const encoded = encoder.encode({x: 123, y: 0, z: -1}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('y').val(); - expect(decoded).toBe(0); - }); - - test('can find object key at the end of the object', () => { - const encoded = encoder.encode({x: 123, y: 0, z: -1}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('z').val(); - expect(decoded).toBe(-1); - }); - }); - - describe('reading array header', () => { - test('can read array size of an empty array', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(0); - }); - - test('can read small array size', () => { - const encoded = encoder.encode(['bar', 1, 2]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(3); - }); - - test('medium size array size', () => { - const encoded = encoder.encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(17); - }); - - test('throws if value is not an array', () => { - const encoded = encoder.encode({}); - decoder.reader.reset(encoded); - expect(() => decoder.readArrHdr()).toThrowError(); - }); - }); - - describe('array index finding', () => { - test('can find value at beginning of array', () => { - const encoded = encoder.encode(['foobar']); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(0).val(); - expect(decoded).toBe('foobar'); - }); - - test('can find value in the middle of array', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(1).val(); - expect(decoded).toBe(2); - }); - - test('can find value at the end of array', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(2).val(); - expect(decoded).toBe(3); - }); - - test('throws if array index is out of bounds', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - expect(() => decoder.findIndex(3).val()).toThrowError(); - }); - - test('throws when reading value from an empty array', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - expect(() => decoder.findIndex(0).val()).toThrowError(); - }); - }); - - const doc = { - a: { - b: { - c: { - d: { - e: [1, 2, 3], - }, - hmm: [ - { - foo: 'bar', - }, - ], - }, - }, - }, - }; - - test('can shallow read a deeply nested value', () => { - const encoded = encoder.encode(doc); - - decoder.reader.reset(encoded); - const decoded1 = decoder.findKey('a').findKey('b').findKey('c').findKey('d').findKey('e').val(); - expect(decoded1).toStrictEqual([1, 2, 3]); - - decoder.reader.reset(encoded); - const decoded2 = decoder.findKey('a').findKey('b').findKey('c').findKey('d').findKey('e').findIndex(1).val(); - expect(decoded2).toBe(2); - - decoder.reader.reset(encoded); - const decoded3 = decoder.findKey('a').findKey('b').findKey('c').findKey('hmm').findIndex(0).findKey('foo').val(); - expect(decoded3).toBe('bar'); - }); - - describe('.find()', () => { - test('can find deeply nested value', () => { - const encoded = encoder.encode(doc); - decoder.reader.reset(encoded); - const decoded1 = decoder.find(['a', 'b', 'c', 'd', 'e', 1]).val(); - expect(decoded1).toStrictEqual(2); - }); - }); -}); diff --git a/src/json-pack/cbor/__tests__/CborDecoder.spec.ts b/src/json-pack/cbor/__tests__/CborDecoder.spec.ts deleted file mode 100644 index b525723e35..0000000000 --- a/src/json-pack/cbor/__tests__/CborDecoder.spec.ts +++ /dev/null @@ -1,408 +0,0 @@ -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; -import {JsonPackExtension} from '../../JsonPackExtension'; -import {JsonPackValue} from '../../JsonPackValue'; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); - -describe('unsigned integer', () => { - const uints: (number | bigint)[] = [ - 0, - 6, - 23, - 24, - 25, - 55, - 111, - 166, - 200, - 222, - 255, - 256, - 444, - 1111, - 22222, - 55555, - 0xffff, - 0x10000, - 0xffffff, - 0xffffff, - 0xfffffff, - 0xffffffff, - 0x100000000, - 0xfffffffffffff, - 0x1fffffffffffff, - BigInt('0x1ffffffffffffff'), - BigInt('0x1ffffffffffffffA'), - ]; - - for (const num of uints) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - const decoded = decoder.decode(encoded); - expect(decoded).toBe(num); - }); - } -}); - -describe('signed integer', () => { - const ints: (number | bigint)[] = [ - -1, - -2, - -4, - -16, - -23, - -24, - -26, - -123, - -4444, - -44444, - -66666, - -33333333, - -0xffff, - -0x10000, - -0xffffff, - -0xffffff, - -0xfffffff, - -0xffffffff, - -0x100000000, - -0xfffffffffffff, - -0x1fffffffffffff, - BigInt('-12312312312312312232'), - ]; - - for (const num of ints) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - const decoded = decoder.decode(encoded); - expect(decoded).toBe(num); - }); - } -}); - -describe('binary', () => { - const toUint8Array = (buf: Buffer): Uint8Array => { - const uint8 = new Uint8Array(buf.length); - buf.copy(uint8); - return uint8; - }; - - const buffers: Uint8Array[] = [ - new Uint8Array([]), - new Uint8Array([0]), - new Uint8Array([1, 2, 3]), - new Uint8Array([1, 2, 3, 4, 5]), - toUint8Array(Buffer.alloc(1)), - toUint8Array(Buffer.alloc(15)), - toUint8Array(Buffer.alloc(23)), - toUint8Array(Buffer.alloc(24)), - toUint8Array(Buffer.alloc(25)), - toUint8Array(Buffer.alloc(123)), - toUint8Array(Buffer.alloc(255)), - toUint8Array(Buffer.alloc(256, 2)), - toUint8Array(Buffer.alloc(1024, 3)), - toUint8Array(Buffer.alloc(66666, 5)), - ]; - - for (const val of buffers) { - test(`${String(val).substring(0, 80)}`, () => { - const encoded = encoder.encode(val); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual(val); - }); - } - - test('indefinite length binary', () => { - encoder.writer.reset(); - encoder.writeStartBin(); - encoder.writeBin(new Uint8Array([1, 2, 3])); - encoder.writeBin(new Uint8Array([4, 5, 6])); - encoder.writeBin(new Uint8Array([7, 8, 9])); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])); - }); -}); - -describe('strings', () => { - const strings: string[] = [ - '', - 'a', - 'b', - '๐Ÿ‘', - 'asdf', - 'asdfa adsf asdf a', - 'as ๐Ÿ‘ df', - 'asdf asfd asdf asdf as', - 'asdf asfd ๐Ÿ˜ฑ asdf asdf ๐Ÿ‘€ as', - 'asdf asfasdfasdf asdf asdf d ๐Ÿ˜ฑ asdf asdfasdf asdf asdf asdf asdf asdfasdf asdf asdfasdfasdf asdf asdf asdfasdf asdf asdf asdf asdf asdfasdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asfd asdf asdf asdf sdf asdf asdf ๐Ÿ‘€ as', - ]; - - for (const num of strings) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - const decoded = decoder.decode(encoded); - expect(decoded).toBe(num); - }); - } - - test('indefinite length string', () => { - encoder.writer.reset(); - encoder.writeStartStr(); - encoder.writeStr('abc'); - encoder.writeStr('def'); - encoder.writeStr('ghi'); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual('abcdefghi'); - }); -}); - -describe('arrays', () => { - const arrays: unknown[][] = [ - [], - [0], - [1, 2, 3], - ['qwerty'], - [1, 'a', -2], - [1, 'a', -2, 'qwerty'], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], - [[]], - [[1, 2, 3]], - [[[[[[[[]]]]]]]], - JSON.parse('['.repeat(20) + ']'.repeat(20)), - JSON.parse('['.repeat(50) + ']'.repeat(50)), - JSON.parse('[' + '1,'.repeat(50) + '2]'), - JSON.parse('[' + '1,'.repeat(150) + '2]'), - JSON.parse('[' + '1,'.repeat(250) + '2]'), - JSON.parse('[' + '1,'.repeat(350) + '2]'), - JSON.parse('[' + '1,'.repeat(1250) + '2]'), - JSON.parse('[' + '1,'.repeat(55250) + '2]'), - JSON.parse('[' + '1,'.repeat(77250) + '2]'), - ]; - - for (const val of arrays) { - test(`${JSON.stringify(val).substring(0, 80)} (${val.length})`, () => { - const encoded = encoder.encode(val); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual(val); - }); - } - - test('indefinite length array', () => { - encoder.writer.reset(); - encoder.writeStartArr(); - encoder.writeArr([1, 2, 3]); - encoder.writeArr([4, 5, 6]); - encoder.writeArr([7, 8, 9]); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]); - }); -}); - -describe('objects', () => { - const objects: Record[] = [ - {}, - {a: 'b'}, - {foo: 'bar'}, - {foo: 123}, - {foo: {}}, - {foo: {bar: {}}}, - {foo: {bar: {baz: {}}}}, - {foo: {bar: {baz: {quz: 'qux'}}}}, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - }, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - 23: 23, - }, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - 23: 23, - 24: 24, - }, - ]; - - for (const val of objects) { - test(`${JSON.stringify(val).substring(0, 80)} (${Object.keys(val).length})`, () => { - const encoded = encoder.encode(val); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual(val); - }); - } - - test('indefinite length object', () => { - encoder.writer.reset(); - encoder.writeStartMap(); - encoder.writeAny('foo'); - encoder.writeAny(123); - encoder.writeAny('bar'); - encoder.writeAny(4); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual({foo: 123, bar: 4}); - }); -}); - -describe('tags', () => { - const testTag = (tag: number, value: unknown) => { - test(`can encode a tag = ${tag}, value = ${value}`, () => { - encoder.writer.reset(); - encoder.writeTag(9, 123); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded) as JsonPackExtension; - expect(decoded.tag).toBe(9); - expect(decoded.val).toBe(123); - }); - }; - - testTag(1, 1); - testTag(5, []); - testTag(23, 'adsf'); - testTag(24, 'adsf asdf'); - testTag(125, {}); - testTag(1256, {foo: 'bar'}); - testTag(0xfffff, {foo: 'bar'}); - testTag(0xffffff, {foo: 'bar'}); - testTag(0xfffffffff, {foo: 'bar'}); -}); - -describe('tokens (simple values)', () => { - const testToken = (token: number) => { - test(`can encode a token = ${token}`, () => { - encoder.writer.reset(); - encoder.writeTkn(token); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded) as JsonPackValue; - expect(decoded.val).toBe(token); - }); - }; - - for (let i = 0; i <= 19; i++) testToken(i); - - const testNativeToken = (token: number, expected: unknown) => { - test(`can encode a token = ${token}`, () => { - encoder.writer.reset(); - encoder.writeTkn(token); - const encoded = encoder.writer.flush(); - const decoded = decoder.decode(encoded) as JsonPackValue; - expect(decoded).toBe(expected); - }); - }; - - testNativeToken(20, false); - testNativeToken(21, true); - testNativeToken(22, null); - testNativeToken(23, undefined); - - const testJsTokens = (token: unknown) => { - test(`can encode a token = ${token}`, () => { - const encoded = encoder.encode(token); - const decoded = decoder.decode(encoded); - expect(decoded).toBe(token); - }); - }; - - testJsTokens(false); - testJsTokens(true); - testJsTokens(null); - testJsTokens(undefined); -}); - -describe('maps', () => { - const maps: Map[] = [ - new Map(), - new Map([['foo', 'bar']]), - new Map([ - ['foo', 'bar'], - [1, 2], - [true, false], - [null, null], - ]), - ]; - - for (const map of maps) { - test(`{${[...map.entries()]}}`, () => { - const encoded = encoder.encode(map); - decoder.reader.reset(encoded); - const decoded = decoder.readAsMap(); - expect(decoded).toStrictEqual(map); - }); - } -}); diff --git a/src/json-pack/cbor/__tests__/CborDecoder.validate.spec.ts b/src/json-pack/cbor/__tests__/CborDecoder.validate.spec.ts deleted file mode 100644 index a07015fee0..0000000000 --- a/src/json-pack/cbor/__tests__/CborDecoder.validate.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); - -test('value is too short, buffer too long', () => { - const encoded = encoder.encode(1.1); - decoder.validate(encoded); - const corrupted = new Uint8Array(encoded.length + 1); - corrupted.set(encoded); - expect(() => decoder.validate(corrupted)).toThrow(); -}); - -test('value is truncated, buffer too short', () => { - const encoded = encoder.encode(1.1); - decoder.validate(encoded); - const corrupted = encoded.subarray(0, encoded.length - 1); - expect(() => decoder.validate(corrupted)).toThrow(); -}); - -test('validates valid indefinite map', () => { - encoder.writer.reset(); - encoder.writeStartMap(); - encoder.writeStr('foo'); - encoder.writeStr('bar'); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - decoder.validate(encoded); -}); - -test('value contents is corrupted, break between map key and value', () => { - encoder.writer.reset(); - encoder.writeStartMap(); - encoder.writeStr('foo'); - encoder.writeEnd(); - encoder.writeStr('bar'); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - expect(() => decoder.validate(encoded)).toThrow(); -}); - -test('value contents is corrupted, no value in indefinite map', () => { - encoder.writer.reset(); - encoder.writeStartMap(); - encoder.writeStr('foo'); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - expect(() => decoder.validate(encoded)).toThrow(); -}); - -test('invalid value', () => { - const encoded = new Uint8Array([0xff]); - expect(() => decoder.validate(encoded)).toThrow(); -}); diff --git a/src/json-pack/cbor/__tests__/CborDecoderDag.spec.ts b/src/json-pack/cbor/__tests__/CborDecoderDag.spec.ts deleted file mode 100644 index 9266aac553..0000000000 --- a/src/json-pack/cbor/__tests__/CborDecoderDag.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {CborEncoderStable} from '../CborEncoderStable'; -import {CborDecoderDag} from '../CborDecoderDag'; -import {JsonPackExtension} from '../../JsonPackExtension'; - -const writer = new Writer(1); -const encoder = new CborEncoderStable(writer); -const decoder = new CborDecoderDag(); - -describe('only extension = 42 is permitted', () => { - test('can decode a value with extension 42', () => { - const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(42, 'b')}); - const val = decoder.read(encoded); - expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - }); - - test('non-42 extensions are not processed', () => { - const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(43, 'b')}); - const val = decoder.read(encoded); - expect(val).toStrictEqual({a: 'a', b: 'b'}); - }); - - // test('can encode CID using inlined custom class', () => { - // class CID { - // constructor(public readonly value: string) {} - // } - // const encoder = new CborEncoderDag(writer); - // encoder.writeUnknown = (val: unknown): void => { - // if (val instanceof CID) encoder.writeTag(42, val.value); - // else throw new Error('Unknown value type'); - // }; - // const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(42, 'b')}); - // const val = decoder.read(encoded); - // expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - // const encoded2 = encoder.encode({a: 'a', b: new CID('b')}); - // const val2 = decoder.read(encoded2); - // expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - // }); - - // test('can throw on unknown custom class', () => { - // class CID { - // constructor(public readonly value: string) {} - // } - // class NotCID { - // constructor(public readonly value: string) {} - // } - // const encoder = new CborEncoderDag(writer); - // encoder.writeUnknown = (val: unknown): void => { - // if (val instanceof CID) encoder.writeTag(42, val.value); - // else throw new Error('Unknown value type'); - // }; - // const encoded1 = encoder.encode({a: 'a', b: new CID('b')}); - // expect(() => encoder.encode({a: 'a', b: new NotCID('b')})).toThrowError(new Error('Unknown value type')); - // }); -}); diff --git a/src/json-pack/cbor/__tests__/CborEncoder.spec.ts b/src/json-pack/cbor/__tests__/CborEncoder.spec.ts deleted file mode 100644 index fd98461b9b..0000000000 --- a/src/json-pack/cbor/__tests__/CborEncoder.spec.ts +++ /dev/null @@ -1,413 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonPackValue} from '../../JsonPackValue'; -import {CborEncoder} from '../CborEncoder'; -import {decode} from 'cbor'; - -const writer = new Writer(1); -const encoder = new CborEncoder(writer); - -describe('unsigned integer', () => { - const uints: (number | bigint)[] = [ - 0, - 6, - 23, - 24, - 25, - 55, - 111, - 166, - 200, - 222, - 255, - 256, - 444, - 1111, - 22222, - 55555, - 0xffff, - 0x10000, - 0xffffff, - 0xffffff, - 0xfffffff, - 0xffffffff, - 0x100000000, - 0xfffffffffffff, - 0x1fffffffffffff, - BigInt('0x1ffffffffffffff'), - BigInt('0x1ffffffffffffffA'), - ]; - - for (const num of uints) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - expect(decode(encoded)).toBe(num); - }); - } -}); - -describe('signed integer', () => { - const ints: (number | bigint)[] = [ - -1, - -2, - -4, - -16, - -23, - -24, - -26, - -123, - -4444, - -44444, - -66666, - -33333333, - -0xffff, - -0x10000, - -0xffffff, - -0xffffff, - -0xfffffff, - -0xffffffff, - -0x100000000, - -0xfffffffffffff, - -0x1fffffffffffff, - BigInt('-12312312312312312232'), - ]; - - for (const num of ints) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - expect(decode(encoded)).toBe(num); - }); - } -}); - -describe('floats', () => { - const floats: (number | bigint)[] = [0, 1, 0.0, 0.1, 123.4, 7.34, -123.123]; - - for (const num of floats) { - test(`${num}`, () => { - const encoded = encoder.encode(num); - expect(decode(encoded)).toBe(num); - }); - } -}); - -const toUint8Array = (buf: Buffer): Uint8Array => { - const uint8 = new Uint8Array(buf.length); - buf.copy(uint8); - return uint8; -}; - -describe('binary', () => { - const buffers: Uint8Array[] = [ - new Uint8Array([]), - new Uint8Array([0]), - new Uint8Array([1, 2, 3]), - new Uint8Array([1, 2, 3, 4, 5]), - toUint8Array(Buffer.alloc(1)), - toUint8Array(Buffer.alloc(15)), - toUint8Array(Buffer.alloc(23)), - toUint8Array(Buffer.alloc(24)), - toUint8Array(Buffer.alloc(25)), - toUint8Array(Buffer.alloc(123)), - toUint8Array(Buffer.alloc(255)), - toUint8Array(Buffer.alloc(256, 2)), - toUint8Array(Buffer.alloc(1024, 3)), - toUint8Array(Buffer.alloc(66666, 5)), - ]; - - for (const val of buffers) { - test(`${String(val).substring(0, 80)}`, () => { - const encoded = encoder.encode(val); - const decoded = decode(encoded) as Buffer; - const uint8 = new Uint8Array(decoded.length); - decoded.copy(uint8); - expect(uint8).toStrictEqual(val); - }); - } - - test('indefinite length binary', () => { - encoder.writer.reset(); - encoder.writeStartBin(); - encoder.writeBin(new Uint8Array([1, 2, 3])); - encoder.writeBin(new Uint8Array([4, 5, 6])); - encoder.writeBin(new Uint8Array([7, 8, 9])); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded) as Buffer; - expect(toUint8Array(decoded)).toStrictEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])); - }); -}); - -describe('strings', () => { - const strings: string[] = [ - '', - 'a', - 'b', - '๐Ÿ‘', - 'asdf', - 'asdfa adsf asdf a', - 'as ๐Ÿ‘ df', - 'asdf asfd asdf asdf as', - 'asdf asfd ๐Ÿ˜ฑ asdf asdf ๐Ÿ‘€ as', - 'asdf asfasdfasdf asdf asdf d ๐Ÿ˜ฑ asdf asdfasdf asdf asdf asdf asdf asdfasdf asdf asdfasdfasdf asdf asdf asdfasdf asdf asdf asdf asdf asdfasdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asfd asdf asdf asdf sdf asdf asdf ๐Ÿ‘€ as', - ]; - - for (const val of strings) { - test(`${JSON.stringify(val.substring(0, 80))} (${val.length})`, () => { - const encoded = encoder.encode(val); - expect(decode(encoded)).toBe(val); - }); - } - - test('indefinite length string', () => { - encoder.writeStartStr(); - encoder.writeStr('abc'); - encoder.writeStr('def'); - encoder.writeStr('ghi'); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded).toStrictEqual('abcdefghi'); - encoder.writeStartStr(); - encoder.writeStr('abc'); - encoder.writeStr('def'); - encoder.writeStr('ghi'); - encoder.writeEnd(); - const encoded2 = encoder.writer.flush(); - const decoded2 = decode(encoded2); - expect(decoded2).toStrictEqual('abcdefghi'); - }); -}); - -describe('arrays', () => { - const arrays: unknown[][] = [ - [], - [0], - [1, 2, 3], - ['qwerty'], - [1, 'a', -2], - [1, 'a', -2, 'qwerty'], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], - [[]], - [[1, 2, 3]], - [[[[[[[[]]]]]]]], - JSON.parse('['.repeat(20) + ']'.repeat(20)), - JSON.parse('['.repeat(50) + ']'.repeat(50)), - JSON.parse('[' + '1,'.repeat(50) + '2]'), - JSON.parse('[' + '1,'.repeat(150) + '2]'), - JSON.parse('[' + '1,'.repeat(250) + '2]'), - JSON.parse('[' + '1,'.repeat(350) + '2]'), - JSON.parse('[' + '1,'.repeat(1250) + '2]'), - JSON.parse('[' + '1,'.repeat(55250) + '2]'), - JSON.parse('[' + '1,'.repeat(77250) + '2]'), - ]; - - for (const val of arrays) { - test(`${JSON.stringify(val).substring(0, 80)} (${val.length})`, () => { - const encoded = encoder.encode(val); - expect(decode(encoded)).toStrictEqual(val); - }); - - test('indefinite length array', () => { - encoder.writer.reset(); - encoder.writeStartArr(); - encoder.writeArr([1, 2, 3]); - encoder.writeArr([4, 5, 6]); - encoder.writeArr([7, 8, 9]); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded).toStrictEqual([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]); - }); - } -}); - -describe('objects', () => { - const objects: Record[] = [ - {}, - {a: 'b'}, - {foo: 'bar'}, - {foo: 123}, - {foo: {}}, - {foo: {bar: {}}}, - {foo: {bar: {baz: {}}}}, - {foo: {bar: {baz: {quz: 'qux'}}}}, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - }, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - 23: 23, - }, - { - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - 23: 23, - 24: 24, - }, - ]; - - for (const val of objects) { - test(`${JSON.stringify(val).substring(0, 80)} (${Object.keys(val).length})`, () => { - const encoded = encoder.encode(val); - expect(decode(encoded)).toStrictEqual(val); - }); - } - - test('indefinite length object', () => { - encoder.writer.reset(); - encoder.writeStartMap(); - encoder.writeAny('foo'); - encoder.writeAny(123); - encoder.writeAny('bar'); - encoder.writeAny(4); - encoder.writeEnd(); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded).toStrictEqual({foo: 123, bar: 4}); - }); -}); - -describe('tags', () => { - const testTag = (tag: number, value: unknown) => { - test(`can encode a tag = ${tag}, value = ${value}`, () => { - encoder.writer.reset(); - encoder.writeTag(9, 123); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded.tag).toBe(9); - expect(decoded.value).toBe(123); - }); - }; - - testTag(1, 1); - testTag(5, []); - testTag(23, 'adsf'); - testTag(24, 'adsf asdf'); - testTag(125, {}); - testTag(1256, {foo: 'bar'}); - testTag(0xfffff, {foo: 'bar'}); - testTag(0xffffff, {foo: 'bar'}); - testTag(0xfffffffff, {foo: 'bar'}); -}); - -describe('tokens (simple values)', () => { - const testToken = (token: number) => { - test(`can encode a token = ${token}`, () => { - encoder.writer.reset(); - encoder.writeTkn(token); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded.value).toBe(token); - }); - }; - - for (let i = 0; i <= 19; i++) testToken(i); - - const testNativeToken = (token: number, expected: unknown) => { - test(`can encode a token = ${token}`, () => { - encoder.writer.reset(); - encoder.writeTkn(token); - const encoded = encoder.writer.flush(); - const decoded = decode(encoded); - expect(decoded).toBe(expected); - }); - }; - - testNativeToken(20, false); - testNativeToken(21, true); - testNativeToken(22, null); - testNativeToken(23, undefined); - - const testJsTokens = (token: unknown) => { - test(`can encode a token = ${token}`, () => { - const encoded = encoder.encode(token); - const decoded = decode(encoded); - expect(decoded).toBe(token); - }); - }; - - testJsTokens(false); - testJsTokens(true); - testJsTokens(null); - testJsTokens(undefined); -}); - -describe('JsonPackValue', () => { - test('can encode pre-packed value', () => { - const internal = encoder.encode({foo: 'bar'}); - const val = new JsonPackValue(internal); - const data = {boo: [1, val, 2]}; - const encoded = encoder.encode(data); - expect(decode(encoded)).toEqual({ - boo: [1, {foo: 'bar'}, 2], - }); - }); -}); diff --git a/src/json-pack/cbor/__tests__/CborEncoderDag.spec.ts b/src/json-pack/cbor/__tests__/CborEncoderDag.spec.ts deleted file mode 100644 index bde7883e02..0000000000 --- a/src/json-pack/cbor/__tests__/CborEncoderDag.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {CborEncoderDag} from '../CborEncoderDag'; -import {CborDecoder} from '../CborDecoder'; -import {JsonPackExtension} from '../../JsonPackExtension'; -import {CborDecoderDag} from '../CborDecoderDag'; - -const writer = new Writer(1); -const encoder = new CborEncoderDag(writer); -const decoder = new CborDecoder(); - -describe('special tokens are not permitted', () => { - test('undefined', () => { - const encoded = encoder.encode(undefined); - const val = decoder.read(encoded); - expect(val).toBe(null); - expect(encoded.length).toBe(1); - }); - - test('NaN', () => { - const encoded = encoder.encode(NaN); - const val = decoder.read(encoded); - expect(val).toBe(null); - expect(encoded.length).toBe(1); - }); - - test('+Infinity', () => { - const encoded = encoder.encode(+Infinity); - const val = decoder.read(encoded); - expect(val).toBe(null); - expect(encoded.length).toBe(1); - }); - - test('-Infinity', () => { - const encoded = encoder.encode(-Infinity); - const val = decoder.read(encoded); - expect(val).toBe(null); - expect(encoded.length).toBe(1); - }); -}); - -describe('only extension = 42 is permitted', () => { - test('can encode a value with extension 42', () => { - const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(42, 'b')}); - const val = decoder.read(encoded); - expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - }); - - test('non-42 extensions are not encoded', () => { - const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(43, 'b')}); - const val = decoder.read(encoded); - expect(val).toStrictEqual({a: 'a', b: 'b'}); - }); - - class CID { - constructor(public readonly value: string) {} - } - class NotCID { - constructor(public readonly value: string) {} - } - - class IpfsCborEncoder extends CborEncoderDag { - public writeUnknown(val: unknown): void { - if (val instanceof CID) this.writeTag(42, val.value); - else throw new Error('Unknown value type'); - } - } - - class IpfsCborDecoder extends CborDecoderDag { - public readTagRaw(tag: number): CID | unknown { - if (tag === 42) return new CID(this.val() as any); - throw new Error('UNKNOWN_TAG'); - } - } - - test('can encode CID using inlined custom class', () => { - const encoder = new IpfsCborEncoder(); - const encoded = encoder.encode({a: 'a', b: new JsonPackExtension(42, 'b')}); - const val = decoder.read(encoded); - expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - const encoded2 = encoder.encode({a: 'a', b: new CID('b')}); - const val2 = decoder.decode(encoded2); - expect(val).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - expect(val2).toStrictEqual({a: 'a', b: new JsonPackExtension(42, 'b')}); - }); - - test('can encode CID inside a nested array', () => { - const encoder = new IpfsCborEncoder(); - const decoder = new IpfsCborDecoder(); - const cid = new CID('my-cid'); - const data = [1, [2, [3, cid, 4], 5], 6]; - const encoded = encoder.encode(data); - const decoded = decoder.decode(encoded); - expect(decoded).toStrictEqual(data); - }); - - test('can throw on unknown custom class', () => { - const encoder = new IpfsCborEncoder(); - const encoded1 = encoder.encode({a: 'a', b: new CID('b')}); - expect(() => encoder.encode({a: 'a', b: new NotCID('b')})).toThrowError(new Error('Unknown value type')); - }); -}); - -describe('floats', () => { - test('always encodes floats as double precision 64 bits', () => { - const floats = [ - 0.1, 0.2, 0.3, 0.4, 0.5, -0.1, -0.2, -0.3, -0.4, -0.5, 1.1, 1.12, 1.123, 1.1234, 0.12, 0.123, 0.1234, - ]; - const sizes = new Set(); - for (const float of floats) { - const encoded = encoder.encode(float); - sizes.add(encoded.length); - } - expect(sizes.size).toBe(1); - }); -}); diff --git a/src/json-pack/cbor/__tests__/CborEncoderStable.spec.ts b/src/json-pack/cbor/__tests__/CborEncoderStable.spec.ts deleted file mode 100644 index 6eff27187b..0000000000 --- a/src/json-pack/cbor/__tests__/CborEncoderStable.spec.ts +++ /dev/null @@ -1,227 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {CborEncoderStable} from '../CborEncoderStable'; -import {encode} from 'cborg'; - -const writer = new Writer(1); -const encoder = new CborEncoderStable(writer); - -describe('objects', () => { - test('sorts keys lexicographically', () => { - const obj1 = { - a: 1, - b: 2, - }; - const encoded1 = encoder.encode(obj1); - const encoded2 = encode(obj1); - expect(encoded1).toStrictEqual(encoded2); - const obj2 = { - b: 2, - a: 1, - }; - const encoded3 = encoder.encode(obj2); - const encoded4 = encode(obj2); - expect(encoded3).toStrictEqual(encoded4); - expect(encoded1).toStrictEqual(encoded3); - }); - - test('sorts keys by length', () => { - const obj1 = { - aa: 1, - b: 2, - }; - const encoded1 = encoder.encode(obj1); - const encoded2 = encode(obj1); - expect(encoded1).toStrictEqual(encoded2); - const obj2 = { - b: 2, - aa: 1, - }; - const encoded3 = encoder.encode(obj2); - const encoded4 = encode(obj2); - expect(encoded3).toStrictEqual(encoded4); - expect(encoded1).toStrictEqual(encoded3); - }); -}); - -describe('floats', () => { - test('always encoded as 8 bytes', () => { - for (let i = 0; i < 100; i++) { - const val = Math.random() * 100000; - const encoded1 = encoder.encode(val); - const encoded2 = encode(val); - expect(encoded1).toStrictEqual(encoded2); - expect(encoded1.length).toBe(9); - } - }); -}); - -describe('numbers and bigints', () => { - const assertNumber = (val: number, length: number) => { - const encoded1 = encoder.encode(val); - const encoded2 = encode(val); - expect(encoded1).toStrictEqual(encoded2); - expect(encoded1.length).toBe(length); - const encoded3 = encoder.encode(BigInt(val)); - expect(encoded1).toStrictEqual(encoded3); - }; - - describe('positive', () => { - test('numbers up to 23 are encoded as one byte', () => { - for (let i = 0; i < 24; i++) { - assertNumber(i, 1); - } - }); - - test('numbers between 24 and 0xff are encoded as two bytes', () => { - assertNumber(24, 2); - assertNumber(0xff, 2); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (0xff - 24) + 24); - assertNumber(val, 2); - } - }); - - test('numbers between 0xff + 1 and 0xffff are encoded as three bytes', () => { - assertNumber(0xff + 1, 3); - assertNumber(0xffff, 3); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (0xffff - (0xff + 1)) + 0xff + 1); - assertNumber(val, 3); - } - }); - - test('numbers between 0xffff + 1 and 0xffffffff are encoded as five bytes', () => { - assertNumber(0xffff + 1, 5); - assertNumber(0xffffffff, 5); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (0xffffffff - (0xffff + 1)) + 0xffff + 1); - assertNumber(val, 5); - } - }); - - test('numbers between 0xffffffff + 1 and Number.MAX_SAFE_INTEGER are encoded as nine bytes', () => { - assertNumber(0xffffffff + 1, 9); - assertNumber(Number.MAX_SAFE_INTEGER, 9); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (Number.MAX_SAFE_INTEGER - (0xffffffff + 1)) + 0xffffffff + 1); - assertNumber(val, 9); - } - }); - }); - - describe('negative', () => { - test('numbers between -24 and -1 are encoded as one byte', () => { - assertNumber(-24, 1); - assertNumber(-1, 1); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (-1 - -24) + -24); - assertNumber(val, 1); - } - }); - - test('numbers between -0xff - 1 and -25 are encoded as two bytes', () => { - assertNumber(-0xff, 2); - assertNumber(-0xff - 1, 2); - assertNumber(-25, 2); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (-25 - -0xff) + -0xff); - assertNumber(val, 2); - } - }); - - test('numbers between -0xffff - 1 and -0xff - 2 are encoded as three bytes', () => { - assertNumber(-0xffff, 3); - assertNumber(-0xff - 2, 3); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (-0xff - 2 - -0xffff) + -0xffff); - assertNumber(val, 3); - } - }); - - test('numbers between -0xffffffff - 1 and -0xffff - 2 are encoded as five bytes', () => { - assertNumber(-0xffffffff, 5); - assertNumber(-0xffff - 2, 5); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (-0xffff - 2 - -0xffffffff) + -0xffffffff); - assertNumber(val, 5); - } - }); - - test('numbers between Number.MIN_SAFE_INTEGER and -0xffffffff - 2 are encoded as nine bytes', () => { - assertNumber(Number.MIN_SAFE_INTEGER, 9); - assertNumber(-0xffffffff - 2, 9); - for (let i = 0; i < 100; i++) { - const val = Math.round(Math.random() * (-0xffffffff - 2 - Number.MIN_SAFE_INTEGER) + Number.MIN_SAFE_INTEGER); - assertNumber(val, 9); - } - }); - }); -}); - -describe('strings', () => { - const assertString = (val: string, length: number) => { - const encoded1 = encoder.encode(val); - expect(encoded1.length).toBe(length); - }; - - test('strings shorter than 24 byte consume 1 byte header', () => { - assertString('', 1); - assertString('a', 2); - assertString('a'.repeat(4), 5); - assertString('a'.repeat(8), 9); - assertString('a'.repeat(16), 17); - assertString('a'.repeat(23), 24); - }); - - test('strings between 24 and 0xff bytes consume 2 byte header', () => { - assertString('b'.repeat(24), 26); - assertString('b'.repeat(0xff), 0xff + 2); - for (let i = 0; i < 5; i++) { - const len = Math.round(Math.random() * (0xff - 24) + 24); - assertString('b'.repeat(len), len + 2); - } - }); - - test('strings between 0xff + 1 and 0xffff bytes consume 3 byte header', () => { - assertString('c'.repeat(0xff + 1), 0xff + 1 + 3); - assertString('c'.repeat(0xffff), 0xffff + 3); - for (let i = 0; i < 10; i++) { - const len = Math.round(Math.random() * (0xffff - (0xff + 1)) + 0xff + 1); - assertString('c'.repeat(len), len + 3); - } - }); - - test('strings between over 0xffff + 1 bytes consume 5 byte header', () => { - assertString('d'.repeat(0xffff + 1), 0xffff + 1 + 5); - for (let i = 0; i < 10; i++) { - const len = Math.round(Math.random() * (0xfffff - (0xffff + 1)) + 0xffff + 1); - assertString('c'.repeat(len), len + 5); - } - }); -}); - -describe('recursion', () => { - test('can prevent recursive objects', () => { - const encoder = new (class extends CborEncoderStable { - private readonly objectSet = new Set(); - - public encode(value: unknown): Uint8Array { - this.objectSet.clear(); - return super.encode(value); - } - - public writeAny(value: unknown): void { - if (this.objectSet.has(value)) { - throw new Error('Recursive object'); - } - this.objectSet.add(value); - super.writeAny(value); - } - })(); - const obj1 = {a: 1}; - const obj2 = {b: 2}; - (obj1).b = obj2; - (obj2).a = obj1; - expect(() => encoder.encode(obj1)).toThrowError('Recursive object'); - }); -}); diff --git a/src/json-pack/cbor/__tests__/cbor-js-testcases.ts b/src/json-pack/cbor/__tests__/cbor-js-testcases.ts deleted file mode 100644 index 4d8b4e03d6..0000000000 --- a/src/json-pack/cbor/__tests__/cbor-js-testcases.ts +++ /dev/null @@ -1,101 +0,0 @@ -import {ERROR} from '../constants'; - -export type TestCase = [name: string, expected: string, value: unknown, binaryDifference?: boolean, error?: ERROR]; - -export const testcases: TestCase[] = [ - ['PositiveIntegerFix 0', '00', 0], - ['PositiveIntegerFix 1', '01', 1], - ['PositiveIntegerFix 10', '0a', 10], - ['PositiveIntegerFix 23', '17', 23], - ['PositiveIntegerFix 24', '1818', 24], - ['PositiveInteger8 25', '1819', 25], - ['PositiveInteger8 100', '1864', 100], - ['PositiveInteger16 1000', '1903e8', 1000], - ['PositiveInteger32 1000000', '1a000f4240', 1000000], - ['PositiveInteger64 1000000000000', '1b000000e8d4a51000', 1000000000000], - ['PositiveInteger64 9007199254740991', '1b001fffffffffffff', 9007199254740991], - ['PositiveInteger64 9007199254740992', '1b0020000000000000', BigInt(9007199254740992)], - ['PositiveInteger64 18446744073709551615', '1bffffffffffffffff', BigInt('18446744073709551615'), true], - ['NegativeIntegerFix -1', '20', -1], - ['NegativeIntegerFix -10', '29', -10], - ['NegativeIntegerFix -24', '37', -24], - ['NegativeInteger8 -25', '3818', -25], - ['NegativeInteger8 -26', '3819', -26], - ['NegativeInteger8 -100', '3863', -100], - ['NegativeInteger16 -1000', '3903e7', -1000], - ['NegativeInteger32 -1000000', '3a000f423f', -1000000], - ['NegativeInteger64 -1000000000000', '3b000000e8d4a50fff', -1000000000000], - ['NegativeInteger64 -9007199254740992', '3b001fffffffffffff', BigInt(-9007199254740992)], - ['NegativeInteger64 -18446744073709551616', '3bffffffffffffffff', BigInt('-18446744073709551616'), true], - ["String ''", '60', ''], - ["String 'a'", '6161', 'a'], - ["String 'IETF'", '6449455446', 'IETF'], - ["String '\"\\'", '62225c', '"\\'], - ["String '\u00fc' (U+00FC)", '62c3bc', '\u00fc'], - ["String '\u6c34' (U+6C34)", '63e6b0b4', '\u6c34'], - ["String '\ud800\udd51' (U+10151)", '64f0908591', '\ud800\udd51'], - ["String 'streaming'", '7f657374726561646d696e67ff', 'streaming', true], - ['Array []', '80', []], - ["Array ['a', {'b': 'c'}]", '826161a161626163', ['a', {b: 'c'}]], - ["Array ['a, {_ 'b': 'c'}]", '826161bf61626163ff', ['a', {b: 'c'}], true], - ['Array [1,2,3]', '83010203', [1, 2, 3]], - ['Array [1, [2, 3], [4, 5]]', '8301820203820405', [1, [2, 3], [4, 5]]], - ['Array [1, [2, 3], [_ 4, 5]]', '83018202039f0405ff', [1, [2, 3], [4, 5]], true], - ['Array [1, [_ 2, 3], [4, 5]]', '83019f0203ff820405', [1, [2, 3], [4, 5]], true], - [ - 'Array [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]', - '98190102030405060708090a0b0c0d0e0f101112131415161718181819', - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], - ], - [ - 'Array [_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]', - '9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff', - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], - true, - ], - ['Array [_ 1, [2, 3], [4, 5]]', '9f01820203820405ff', [1, [2, 3], [4, 5]], true], - ['Array [_ 1, [2, 3], [_ 4, 5]]', '9f018202039f0405ffff', [1, [2, 3], [4, 5]], true], - ['Array [_ ]', '9fff', [], true], - ['Object {}', 'a0', {}], - ['Object {1: 2, 3: 4}', 'a201020304', {1: 2, 3: 4}, true], - ["Object {'a': 1, 'b': [2, 3]}", 'a26161016162820203', {a: 1, b: [2, 3]}, true], - [ - "Object {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E'}", - 'a56161614161626142616361436164614461656145', - {a: 'A', b: 'B', c: 'C', d: 'D', e: 'E'}, - true, - ], - ["Object {_ 'a': 1, 'b': [_ 2, 3]}", 'bf61610161629f0203ffff', {a: 1, b: [2, 3]}, true], - ["Object {_ 'Fun': true, 'Amt': -2}", 'bf6346756ef563416d7421ff', {Fun: true, Amt: -2}, true], - ['Tag Self-describe CBOR 0', 'd9d9f700', 0, true], - ['false', 'f4', false], - ['true', 'f5', true], - ['null', 'f6', null], - ['undefined', 'f7', undefined], - ['UnassignedSimpleValue 255', 'f8ff', 0xff, true], - ['Float16 0.0', 'f90000', 0.0, true], - // ['Float16 -0.0', 'f98000', -0.0, true], - ['Float16 1.0', 'f93c00', 1.0, true], - ['Float16 1.5', 'f93e00', 1.5, true], - ['Float16 65504.0', 'f97bff', 65504.0, true], - ['Float16 5.960464477539063e-8', 'f90001', 5.960464477539063e-8, true], - ['Float16 0.00006103515625', 'f90400', 0.00006103515625, true], - ['Float16 -5.960464477539063e-8', 'f98001', -5.960464477539063e-8, true], - ['Float16 -4.0', 'f9c400', -4.0, true], - ['Float16 +Infinity', 'f97c00', Infinity, true], - ['Float16 NaN', 'f97e00', NaN, true], - ['Float16 -Infinity', 'f9fc00', -Infinity, true], - ['Float32 100000.0', 'fa47c35000', 100000.0, true], - ['Float32 3.4028234663852886e+38', 'fa7f7fffff', 3.4028234663852886e38, true], - ['Float32 +Infinity', 'fa7f800000', Infinity, true], - ['Float32 NaN', 'fa7fc00000', NaN, true], - ['Float32 -Infinity', 'faff800000', -Infinity, true], - ['Float64 1.1', 'fb3ff199999999999a', 1.1], - ['Float64 9007199254740994', 'fb4340000000000001', 9007199254740994], - ['Float64 1.0e+300', 'fb7e37e43c8800759c', 1.0e300], - ['Float64 -4.1', 'fbc010666666666666', -4.1], - ['Float64 -9007199254740994', 'fbc340000000000001', -9007199254740994], - ['Float64 +Infinity', 'fb7ff0000000000000', Infinity, true], - ['Float64 NaN', 'fb7ff8000000000000', NaN, true], - ['Float64 -Infinity', 'fbfff0000000000000', -Infinity, true], -]; diff --git a/src/json-pack/cbor/__tests__/cbor-js.spec.ts b/src/json-pack/cbor/__tests__/cbor-js.spec.ts deleted file mode 100644 index 2c797bb3a8..0000000000 --- a/src/json-pack/cbor/__tests__/cbor-js.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {testcases} from './cbor-js-testcases'; -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; -import {JsonPackExtension} from '../../JsonPackExtension'; -import {JsonPackValue} from '../../JsonPackValue'; - -const hex2arrayBuffer = (data: string): Uint8Array => { - const length = data.length / 2; - const ret = new Uint8Array(length); - for (let i = 0; i < length; ++i) { - ret[i] = parseInt(data.substr(i * 2, 2), 16); - } - return ret; -}; - -const run = (encoder: CborEncoder, decoder: CborDecoder) => { - describe('JSON documents', () => { - for (const [name, expected, value, binaryDifferences, error] of testcases) { - test(name, () => { - if (error === undefined) { - const expectedBuf = hex2arrayBuffer(expected); - const encoded = encoder.encode(value); - const decoded = decoder.decode(encoded); - if (!binaryDifferences) expect(encoded).toStrictEqual(expectedBuf); - expect(decoded).toStrictEqual(value); - const decoded2 = decoder.decode(expectedBuf); - const resultValue = - decoded2 instanceof JsonPackExtension - ? decoded2.val - : decoded2 instanceof JsonPackValue - ? decoded2.val - : decoded2; - expect(resultValue).toStrictEqual(value); - } else { - expect(() => decoder.decode(hex2arrayBuffer(expected))).toThrow(); - } - }); - } - }); -}; - -const encoder = new CborEncoder(); -const decoder = new CborDecoder(); - -run(encoder, decoder); diff --git a/src/json-pack/cbor/__tests__/codec.spec.ts b/src/json-pack/cbor/__tests__/codec.spec.ts deleted file mode 100644 index 63267aee27..0000000000 --- a/src/json-pack/cbor/__tests__/codec.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {CborEncoder} from '../CborEncoder'; -import {CborEncoderFast} from '../CborEncoderFast'; -import {CborEncoderStable} from '../CborEncoderStable'; -import {CborEncoderDag} from '../CborEncoderDag'; -import {CborDecoder} from '../CborDecoder'; -import {decode as deocode__} from 'cbor'; -import {documents} from '../../../__tests__/json-documents'; -import {binaryDocuments} from '../../../__tests__/binary-documents'; - -const decode = (x: Uint8Array) => deocode__(x); -const decoder = new CborDecoder(); -const run = (encoder: CborEncoderFast) => { - describe('JSON documents', () => { - for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - const encoded = encoder.encode(t.json); - const decoded = decode(encoded); - expect(decoded).toEqual(t.json); - expect(decoder.decode(encoded)).toEqual(t.json); - - // Skipping - decoder.reader.reset(encoded); - const start = decoder.reader.x; - decoder.skipAny(); - const end = decoder.reader.x; - const diff = end - start; - expect(diff).toEqual(encoded.length); - }); - } - }); -}; - -const runBinary = (encoder: CborEncoderFast) => { - describe('binary documents', () => { - for (const t of binaryDocuments) { - (t.only ? test.only : test)(t.name, () => { - const encoded = encoder.encode(t.json); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(t.json); - - // Skipping - decoder.reader.reset(encoded); - const start = decoder.reader.x; - decoder.skipAny(); - const end = decoder.reader.x; - const diff = end - start; - expect(diff).toEqual(encoded.length); - }); - } - }); -}; - -describe('CbroEncoder', () => { - const encoder = new CborEncoder(); - run(encoder); - runBinary(encoder); -}); - -describe('CbroEncoderFast', () => { - const encoderFast = new CborEncoderFast(); - run(encoderFast); -}); - -describe('CbroEncoderStable', () => { - const encoderFast = new CborEncoderStable(); - run(encoderFast); -}); - -describe('CborEncoderDag', () => { - const encoderFast = new CborEncoderDag(); - run(encoderFast); -}); diff --git a/src/json-pack/cbor/__tests__/fuzzing.spec.ts b/src/json-pack/cbor/__tests__/fuzzing.spec.ts deleted file mode 100644 index f2709aa065..0000000000 --- a/src/json-pack/cbor/__tests__/fuzzing.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {RandomJson} from '../../../json-random'; -import {CborEncoderFast} from '../CborEncoderFast'; -import {CborEncoder} from '../CborEncoder'; -import {CborEncoderStable} from '../CborEncoderStable'; -import {CborEncoderDag} from '../CborEncoderDag'; -import {CborDecoder} from '../CborDecoder'; - -const decoder = new CborDecoder(); - -describe('fuzzing', () => { - test('CborEncoderFast', () => { - const encoder = new CborEncoderFast(); - for (let i = 0; i < 200; i++) { - const value = RandomJson.generate(); - const encoded = encoder.encode(value); - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual(value); - } - }); - - test('CborEncoder', () => { - const encoder = new CborEncoder(); - for (let i = 0; i < 200; i++) { - const value = RandomJson.generate(); - const encoded = encoder.encode(value); - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual(value); - } - }); - - test('CborEncoderStable', () => { - const encoder = new CborEncoderStable(); - for (let i = 0; i < 200; i++) { - const value = RandomJson.generate(); - const encoded = encoder.encode(value); - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual(value); - } - }); - - test('CborEncoderDag', () => { - const encoder = new CborEncoderDag(); - for (let i = 0; i < 200; i++) { - const value = RandomJson.generate(); - const encoded = encoder.encode(value); - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual(value); - } - }); -}); diff --git a/src/json-pack/cbor/__tests__/shallow-read.genShallowRead.spec.ts b/src/json-pack/cbor/__tests__/shallow-read.genShallowRead.spec.ts deleted file mode 100644 index e68a278b19..0000000000 --- a/src/json-pack/cbor/__tests__/shallow-read.genShallowRead.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {genShallowReader} from '../../msgpack/shallow-read'; -import {CborEncoder} from '../CborEncoder'; -import {CborDecoder} from '../CborDecoder'; -import {Path} from '../../../json-pointer'; -import {Writer} from '../../../util/buffers/Writer'; - -const assetShallowRead = (doc: unknown, path: Path): void => { - const writer = new Writer(1); - const encoder = new CborEncoder(writer); - const encoded = encoder.encode(doc); - const decoder = new CborDecoder(); - decoder.reader.reset(encoded); - const res1 = decoder.find(path).reader.x; - // console.log(res1); - const fn = genShallowReader(path); - // console.log(fn.toString()); - decoder.reader.reset(encoded); - const res2 = fn(decoder as any); - // console.log(res2); - expect(res1).toBe(res2); -}; - -describe('genShallowRead', () => { - test('first-level object', () => { - const doc = { - bar: {}, - baz: 123, - gg: true, - }; - assetShallowRead(doc, ['bar']); - assetShallowRead(doc, ['baz']); - assetShallowRead(doc, ['gg']); - }); - - test('second-level object', () => { - const doc = { - a: { - bar: {}, - baz: 123, - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - assetShallowRead(doc, ['a']); - assetShallowRead(doc, ['a', 'bar']); - assetShallowRead(doc, ['a', 'baz']); - assetShallowRead(doc, ['a', 'gg']); - assetShallowRead(doc, ['b', 'mmmm']); - assetShallowRead(doc, ['b', 'mmmm', 's']); - assetShallowRead(doc, ['end']); - }); - - test('first-level array', () => { - const doc = [0]; - assetShallowRead(doc, [0]); - }); - - test('first-level array - 2', () => { - const doc = [1234, 'asdf', {}, null, false]; - assetShallowRead(doc, [0]); - assetShallowRead(doc, [1]); - assetShallowRead(doc, [2]); - assetShallowRead(doc, [3]); - assetShallowRead(doc, [4]); - }); - - test('throws when selector is out of bounds of array', () => { - const doc = [1234, 'asdf', {}, null, false]; - expect(() => assetShallowRead(doc, [5])).toThrowError(); - }); - - test('can read from complex nested document', () => { - const doc = { - a: { - bar: [ - { - a: 1, - 2: true, - asdf: false, - }, - 5, - ], - baz: ['a', 'b', 123], - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - assetShallowRead(doc, ['a']); - assetShallowRead(doc, ['a', 'bar', 0]); - assetShallowRead(doc, ['a', 'bar', 1]); - assetShallowRead(doc, ['a', 'bar', 0, 'a']); - assetShallowRead(doc, ['a', 'bar', 0, '2']); - assetShallowRead(doc, ['a', 'bar', 0, 'asdf']); - assetShallowRead(doc, ['b']); - assetShallowRead(doc, ['b', 'mmmm']); - assetShallowRead(doc, ['b', 'mmmm', 's']); - assetShallowRead(doc, ['end']); - }); - - test('should throw when key does not exist', () => { - const doc = { - a: { - bar: {}, - baz: 123, - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - const encoder = new CborEncoder(); - const encoded = encoder.encode(doc); - const decoder = new CborDecoder(); - decoder.reader.reset(encoded); - const fn = genShallowReader(['asdf']); - // console.log(fn.toString()); - expect(() => fn(decoder as any)).toThrowError(); - }); -}); diff --git a/src/json-pack/cbor/constants.ts b/src/json-pack/cbor/constants.ts deleted file mode 100644 index 86b3a5ae2b..0000000000 --- a/src/json-pack/cbor/constants.ts +++ /dev/null @@ -1,42 +0,0 @@ -export const enum MAJOR { - UIN = 0b000, - NIN = 0b001, - BIN = 0b010, - STR = 0b011, - ARR = 0b100, - MAP = 0b101, - TAG = 0b110, - TKN = 0b111, -} - -export const enum MAJOR_OVERLAY { - UIN = 0b000_00000, - NIN = 0b001_00000, - BIN = 0b010_00000, - STR = 0b011_00000, - ARR = 0b100_00000, - MAP = 0b101_00000, - TAG = 0b110_00000, - TKN = 0b111_00000, -} - -export const enum CONST { - MINOR_MASK = 0b11111, - MAX_UINT = 9007199254740991, - END = 0xff, -} - -export const enum ERROR { - UNEXPECTED_MAJOR, - UNEXPECTED_MINOR, - UNEXPECTED_BIN_CHUNK_MAJOR, - UNEXPECTED_BIN_CHUNK_MINOR, - UNEXPECTED_STR_CHUNK_MAJOR, - UNEXPECTED_STR_CHUNK_MINOR, - UNEXPECTED_OBJ_KEY, - UNEXPECTED_OBJ_BREAK, - INVALID_SIZE, - KEY_NOT_FOUND, - INDEX_OUT_OF_BOUNDS, - UNEXPECTED_STR_MAJOR, -} diff --git a/src/json-pack/cbor/shared.ts b/src/json-pack/cbor/shared.ts deleted file mode 100644 index 335f5f0783..0000000000 --- a/src/json-pack/cbor/shared.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {CborEncoder} from './CborEncoder'; -import {CborDecoder} from './CborDecoder'; -import {CborUint8Array} from './types'; - -export type {CborUint8Array}; - -export const encoder = new CborEncoder(); -export const decoder = new CborDecoder(); - -export const encode = (data: T): CborUint8Array => encoder.encode(data) as CborUint8Array; -export const decode = (blob: CborUint8Array): T => decoder.read(blob) as T; diff --git a/src/json-pack/cbor/types.ts b/src/json-pack/cbor/types.ts deleted file mode 100644 index d80243c3e2..0000000000 --- a/src/json-pack/cbor/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type CborUint8Array = Uint8Array & {__BRAND__: 'cbor'; __TYPE__: T}; diff --git a/src/json-pack/codecs/Codecs.ts b/src/json-pack/codecs/Codecs.ts deleted file mode 100644 index fa8c54f961..0000000000 --- a/src/json-pack/codecs/Codecs.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Writer} from '../../util/buffers/Writer'; -import {CborJsonValueCodec} from './cbor'; -import {JsonJsonValueCodec} from './json'; -import {MsgPackJsonValueCodec} from './msgpack'; - -export class Codecs { - public readonly cbor: CborJsonValueCodec; - public readonly msgpack: MsgPackJsonValueCodec; - public readonly json: JsonJsonValueCodec; - - constructor(public readonly writer: Writer) { - this.cbor = new CborJsonValueCodec(this.writer); - this.msgpack = new MsgPackJsonValueCodec(this.writer); - this.json = new JsonJsonValueCodec(this.writer); - } -} diff --git a/src/json-pack/codecs/cbor.ts b/src/json-pack/codecs/cbor.ts deleted file mode 100644 index 88f35dcd9f..0000000000 --- a/src/json-pack/codecs/cbor.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {CborDecoder} from '../cbor/CborDecoder'; -import {CborEncoder} from '../cbor/CborEncoder'; -import {EncodingFormat} from '../constants'; -import type {Writer} from '../../util/buffers/Writer'; -import type {JsonValueCodec} from './types'; - -export class CborJsonValueCodec implements JsonValueCodec { - public readonly id = 'cbor'; - public readonly format = EncodingFormat.Cbor; - public readonly encoder: CborEncoder; - public readonly decoder: CborDecoder; - - constructor(writer: Writer) { - this.encoder = new CborEncoder(writer); - this.decoder = new CborDecoder(); - } -} diff --git a/src/json-pack/codecs/json.ts b/src/json-pack/codecs/json.ts deleted file mode 100644 index 14f3297819..0000000000 --- a/src/json-pack/codecs/json.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {EncodingFormat} from '../constants'; -import {JsonEncoder} from '../json/JsonEncoder'; -import {JsonDecoder} from '../json/JsonDecoder'; -import type {Writer} from '../../util/buffers/Writer'; -import type {JsonValueCodec} from './types'; - -export class JsonJsonValueCodec implements JsonValueCodec { - public readonly id = 'json'; - public readonly format = EncodingFormat.Json; - public readonly encoder: JsonEncoder; - public readonly decoder: JsonDecoder; - - constructor(writer: Writer) { - this.encoder = new JsonEncoder(writer); - this.decoder = new JsonDecoder(); - } -} diff --git a/src/json-pack/codecs/msgpack.ts b/src/json-pack/codecs/msgpack.ts deleted file mode 100644 index 00356d6583..0000000000 --- a/src/json-pack/codecs/msgpack.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {EncodingFormat} from '../constants'; -import {MsgPackEncoder} from '../msgpack'; -import {MsgPackDecoder} from '../msgpack/MsgPackDecoder'; -import type {Writer} from '../../util/buffers/Writer'; -import type {JsonValueCodec} from './types'; - -export class MsgPackJsonValueCodec implements JsonValueCodec { - public readonly id = 'msgpack'; - public readonly format = EncodingFormat.MsgPack; - public readonly encoder: MsgPackEncoder; - public readonly decoder: MsgPackDecoder; - - constructor(writer: Writer) { - this.encoder = new MsgPackEncoder(writer); - this.decoder = new MsgPackDecoder(); - } -} diff --git a/src/json-pack/codecs/types.ts b/src/json-pack/codecs/types.ts deleted file mode 100644 index 40c0a51d7e..0000000000 --- a/src/json-pack/codecs/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type {EncodingFormat} from '../constants'; -import type {BinaryJsonDecoder, BinaryJsonEncoder} from '../types'; - -export interface JsonValueCodec { - id: string; - format: EncodingFormat; - encoder: BinaryJsonEncoder; - decoder: BinaryJsonDecoder; -} diff --git a/src/json-pack/constants.ts b/src/json-pack/constants.ts deleted file mode 100644 index 5fd85b0f78..0000000000 --- a/src/json-pack/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const enum EncodingFormat { - Cbor, - MsgPack, - Json, -} diff --git a/src/json-pack/ion/Import.ts b/src/json-pack/ion/Import.ts deleted file mode 100644 index cf240d53e6..0000000000 --- a/src/json-pack/ion/Import.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {AstNode, ObjAstNode, toAst} from './ast'; -import {SymbolTable} from './types'; - -export class Import { - public readonly offset: number; - public length: number; - protected readonly byText = new Map(); - - constructor( - public readonly parent: Import | null, - public readonly symbols: SymbolTable, - ) { - this.offset = parent ? parent.offset + parent.length : 1; - this.length = symbols.length; - for (let i = 0; i < symbols.length; i++) { - const symbol = symbols[i]; - this.byText.set(symbol, this.offset + i); - } - } - - public getId(symbol: string): number | undefined { - const id = this.byText.get(symbol); - if (id !== undefined) return id; - if (this.parent) this.parent.getId(symbol); - return undefined; - } - - public getText(id: number): string | undefined { - if (id < this.offset) return this.parent ? this.parent.getText(id) : undefined; - return this.symbols[id - this.offset]; - } - - public add(symbol: string): number { - let id = this.byText.get(symbol); - if (id !== undefined) return id; - const length = this.symbols.length; - id = this.offset + length; - this.symbols.push(symbol); - this.length++; - this.byText.set(symbol, id); - return id; - } - - public toAst(): ObjAstNode { - const map = new Map>(); - map.set(7, toAst(this.symbols, this)); - return new ObjAstNode(map); - } -} diff --git a/src/json-pack/ion/IonEncoderFast.ts b/src/json-pack/ion/IonEncoderFast.ts deleted file mode 100644 index dd19be1c8d..0000000000 --- a/src/json-pack/ion/IonEncoderFast.ts +++ /dev/null @@ -1,234 +0,0 @@ -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import {Writer} from '../../util/buffers/Writer'; -import { - AnnotationAstNode, - ArrAstNode, - AstNode, - BinAstNode, - BoolAstNode, - FloatAstNode, - NintAstNode, - NullAstNode, - ObjAstNode, - StrAstNode, - toAst, - UintAstNode, -} from './ast'; -import {TYPE_OVERLAY} from './constants'; -import {Import} from './Import'; -import {systemSymbolImport} from './symbols'; - -export class IonEncoderFast { - protected symbols?: Import; - - constructor(public readonly writer: IWriter & IWriterGrowable = new Writer()) {} - - public encode(value: unknown): Uint8Array { - this.writer.reset(); - this.symbols = new Import(systemSymbolImport, []); - const ast = toAst(value, this.symbols); - this.writeIvm(); - this.writeSymbolTable(); - this.writeAny(ast); - return this.writer.flush(); - } - - public writeAny(value: AstNode): void { - if (value instanceof NullAstNode) this.writer.u8(TYPE_OVERLAY.NULL + 15); - else if (value instanceof StrAstNode) this.writeStr(value); - else if (value instanceof UintAstNode) this.encodeUint(value); - else if (value instanceof NintAstNode) this.encodeNint(value); - else if (value instanceof ObjAstNode) this.writeObj(value); - else if (value instanceof ArrAstNode) this.writeArr(value); - else if (value instanceof FloatAstNode) this.writeFloat(value); - else if (value instanceof BoolAstNode) this.writeBool(value); - else if (value instanceof BinAstNode) this.writeBin(value); - } - - public writeIvm(): void { - this.writer.u32(0xe00100ea); - } - - public writeSymbolTable(): void { - if (!this.symbols?.length) return; - const node = new AnnotationAstNode(this.symbols!.toAst(), [3]); - this.writeAnnotations(node); - } - - public writeAnnotations(node: AnnotationAstNode): void { - const writer = this.writer; - if (node.len < 14) writer.u8(TYPE_OVERLAY.ANNO + node.len); - else { - writer.u8(TYPE_OVERLAY.ANNO + 14); - this.writeVUint(node.len); - } - this.writeVUint(node.annotationLen); - for (let i = 0; i < node.annotations.length; i++) this.writeVUint(node.annotations[i]); - this.writeAny(node.val); - } - - public writeBool(node: BoolAstNode): void { - this.writer.u8(TYPE_OVERLAY.BOOL + (node.val ? 1 : 0)); - } - - public encodeUint(node: UintAstNode): void { - const uint = node.val; - if (!uint) this.writer.u8(TYPE_OVERLAY.UINT); - else if (uint <= 0xff) this.writer.u16(((TYPE_OVERLAY.UINT + 1) << 8) + uint); - else if (uint <= 0xffff) this.writer.u8u16(TYPE_OVERLAY.UINT + 2, uint); - else if (uint <= 0xffffff) this.writer.u32(((TYPE_OVERLAY.UINT + 3) << 24) + uint); - else if (uint <= 0xffffffff) this.writer.u8u32(TYPE_OVERLAY.UINT + 4, uint); - else { - let lo = uint | 0; - if (lo < 0) lo += 4294967296; - let hi = uint - lo; - hi /= 4294967296; - if (uint <= 0xffffffffff) { - this.writer.u16(((TYPE_OVERLAY.UINT + 5) << 8) + hi); - this.writer.u32(lo); - } else if (uint <= 0xffffffffffff) { - this.writer.u8u16(TYPE_OVERLAY.UINT + 6, hi); - this.writer.u32(lo); - } else { - this.writer.u16(((TYPE_OVERLAY.UINT + 7) << 8) + (hi >> 16)); - this.writer.u16(hi & 0xffff); - this.writer.u32(lo); - } - } - } - - public encodeNint(node: NintAstNode): void { - const uint = -node.val; - if (uint <= 0xff) this.writer.u16(((TYPE_OVERLAY.NINT + 1) << 8) + uint); - else if (uint <= 0xffff) this.writer.u8u16(TYPE_OVERLAY.NINT + 2, uint); - else if (uint <= 0xffffff) this.writer.u32(((TYPE_OVERLAY.NINT + 3) << 24) + uint); - else if (uint <= 0xffffffff) this.writer.u8u32(TYPE_OVERLAY.NINT + 4, uint); - else { - let lo = uint | 0; - if (lo < 0) lo += 4294967296; - let hi = uint - lo; - hi /= 4294967296; - if (uint <= 0xffffffffff) { - this.writer.u16(((TYPE_OVERLAY.NINT + 5) << 8) + hi); - this.writer.u32(lo); - } else if (uint <= 0xffffffffffff) { - this.writer.u8u16(TYPE_OVERLAY.NINT + 6, hi); - this.writer.u32(lo); - } else { - this.writer.u16(((TYPE_OVERLAY.NINT + 7) << 8) + (hi >> 16)); - this.writer.u16(hi & 0xffff); - this.writer.u32(lo); - } - } - } - - public writeFloat(node: FloatAstNode): void { - this.writer.u8f64(TYPE_OVERLAY.FLOT + 8, node.val); - } - - public writeVUint(num: number): void { - const writer = this.writer; - if (num <= 0b1111111) { - writer.u8(0b10000000 + num); - } else if (num <= 0b1111111_1111111) { - writer.ensureCapacity(2); - const uint8 = writer.uint8; - uint8[writer.x++] = num >>> 7; - uint8[writer.x++] = 0b10000000 + (num & 0b01111111); - } else if (num <= 0b1111111_1111111_1111111) { - writer.ensureCapacity(3); - const uint8 = writer.uint8; - uint8[writer.x++] = num >>> 14; - uint8[writer.x++] = (num >>> 7) & 0b01111111; - uint8[writer.x++] = 0b10000000 + (num & 0b01111111); - } else if (num <= 0b1111111_1111111_1111111_1111111) { - writer.ensureCapacity(4); - const uint8 = writer.uint8; - uint8[writer.x++] = num >>> 21; - uint8[writer.x++] = (num >>> 14) & 0b01111111; - uint8[writer.x++] = (num >>> 7) & 0b01111111; - uint8[writer.x++] = 0b10000000 + (num & 0b01111111); - } else { - let lo32 = num | 0; - if (lo32 < 0) lo32 += 4294967296; - const hi32 = (num - lo32) / 4294967296; - if (num <= 0b1111111_1111111_1111111_1111111_1111111) { - writer.ensureCapacity(5); - const uint8 = writer.uint8; - uint8[writer.x++] = (hi32 << 4) | (num >>> 28); - uint8[writer.x++] = (num >>> 21) & 0b01111111; - uint8[writer.x++] = (num >>> 14) & 0b01111111; - uint8[writer.x++] = (num >>> 7) & 0b01111111; - uint8[writer.x++] = 0b10000000 + (num & 0b01111111); - } else if (num <= 0b1111111_1111111_1111111_1111111_1111111_1111111) { - writer.ensureCapacity(6); - const uint8 = writer.uint8; - uint8[writer.x++] = (hi32 >>> 3) & 0b1111; - uint8[writer.x++] = ((hi32 & 0b111) << 4) | (num >>> 28); - uint8[writer.x++] = (num >>> 21) & 0b01111111; - uint8[writer.x++] = (num >>> 14) & 0b01111111; - uint8[writer.x++] = (num >>> 7) & 0b01111111; - uint8[writer.x++] = 0b10000000 + (num & 0b01111111); - } - } - } - - public writeStr(node: StrAstNode): void { - const str = node.val; - const length = node.len; - const writer = this.writer; - if (length < 14) writer.u8(TYPE_OVERLAY.STRI + length); - else { - writer.u8(TYPE_OVERLAY.STRI + 14); - this.writeVUint(length); - } - writer.utf8(str); - } - - public writeBin(node: BinAstNode): void { - const buf = node.val; - const length = node.len; - const writer = this.writer; - if (length < 14) writer.u8(TYPE_OVERLAY.BINA + length); - else { - writer.u8(TYPE_OVERLAY.BINA + 14); - this.writeVUint(length); - } - writer.buf(buf, length); - } - - public writeArr(node: ArrAstNode): void { - const writer = this.writer; - const arr = node.val; - if (arr === null) { - writer.u8(TYPE_OVERLAY.LIST + 15); - return; - } - const length = node.len; - if (length < 14) writer.u8(TYPE_OVERLAY.LIST + length); - else { - writer.u8(TYPE_OVERLAY.LIST + 14); - this.writeVUint(length); - } - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - } - - public writeObj(node: ObjAstNode): void { - const writer = this.writer; - const arr = node.val; - if (arr === null) { - writer.u8(TYPE_OVERLAY.LIST + 15); - return; - } - const length = node.len; - if (length < 14) writer.u8(TYPE_OVERLAY.STRU + length); - else { - writer.u8(TYPE_OVERLAY.STRU + 14); - this.writeVUint(length); - } - node.val!.forEach((n, symbolId) => { - this.writeVUint(symbolId); - this.writeAny(n); - }); - } -} diff --git a/src/json-pack/ion/README.md b/src/json-pack/ion/README.md deleted file mode 100644 index 88a4bce328..0000000000 --- a/src/json-pack/ion/README.md +++ /dev/null @@ -1,59 +0,0 @@ -## Benchmarks - -Encoding: - -``` -npx ts-node benchmarks/json-pack/bench.ion.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 Max ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 1,021,876 ops/sec ยฑ0.47% (99 runs sampled) -๐Ÿ‘ ion-js x 27,391 ops/sec ยฑ2.69% (68 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 2,269,009 ops/sec ยฑ0.40% (99 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 69,443 ops/sec ยฑ0.35% (99 runs sampled) -๐Ÿ‘Ž ion-js x 3,723 ops/sec ยฑ3.07% (53 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 214,308 ops/sec ยฑ0.34% (98 runs sampled) -Fastest is ๐Ÿ‘Ž Buffer.from(JSON.stringify()) --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 11,696 ops/sec ยฑ0.33% (101 runs sampled) -๐Ÿ‘Ž ion-js x 1,213 ops/sec ยฑ2.93% (62 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 67,074 ops/sec ยฑ0.35% (96 runs sampled) -Fastest is ๐Ÿ‘Ž Buffer.from(JSON.stringify()) --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 1,892 ops/sec ยฑ0.43% (100 runs sampled) -๐Ÿ‘ ion-js x 65.56 ops/sec ยฑ3.14% (58 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 5,957 ops/sec ยฑ0.36% (97 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 64,855 ops/sec ยฑ0.32% (96 runs sampled) -๐Ÿ‘Ž ion-js x 2,299 ops/sec ยฑ4.32% (51 runs sampled) -๐Ÿ‘Ž Buffer.from(JSON.stringify()) x 174,044 ops/sec ยฑ0.32% (97 runs sampled) -Fastest is ๐Ÿ‘Ž Buffer.from(JSON.stringify()) -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 26,020 ops/sec ยฑ0.33% (99 runs sampled) -๐Ÿ‘ ion-js x 10,668 ops/sec ยฑ5.02% (80 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 129,722 ops/sec ยฑ0.35% (96 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘Ž json-joy/json-pack IonEncoderFast x 11,837 ops/sec ยฑ0.49% (96 runs sampled) -๐Ÿ‘ ion-js x 8,749 ops/sec ยฑ3.80% (85 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 29,769 ops/sec ยฑ0.36% (101 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 435,230 ops/sec ยฑ0.39% (96 runs sampled) -๐Ÿ‘ ion-js x 42,636 ops/sec ยฑ8.11% (66 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 1,013,889 ops/sec ยฑ0.46% (96 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 484,353 ops/sec ยฑ0.41% (97 runs sampled) -๐Ÿ‘ ion-js x 17,032 ops/sec ยฑ14.67% (70 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 1,196,228 ops/sec ยฑ0.40% (99 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ json-joy/json-pack IonEncoderFast x 328,346 ops/sec ยฑ0.45% (96 runs sampled) -๐Ÿ‘ ion-js x 55,922 ops/sec ยฑ4.56% (79 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 991,593 ops/sec ยฑ0.45% (97 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()) -``` diff --git a/src/json-pack/ion/__tests__/Import.spec.ts b/src/json-pack/ion/__tests__/Import.spec.ts deleted file mode 100644 index 5288aab6ea..0000000000 --- a/src/json-pack/ion/__tests__/Import.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {Import} from '../Import'; -import {systemSymbolImport, systemSymbolTable} from '../symbols'; - -test('can instantiate symbols to local symbol table import', () => { - const imp = new Import(systemSymbolImport, ['foo', 'bar']); - const fooId = imp.getId('foo'); - const barId = imp.getId('bar'); - expect(fooId).toBe(systemSymbolTable.length + 1); - expect(barId).toBe(systemSymbolTable.length + 2); - const barText = imp.getText(systemSymbolTable.length + 1); - const fooText = imp.getText(systemSymbolTable.length + 2); - expect(barText).toBe('foo'); - expect(fooText).toBe('bar'); -}); - -test('can add symbols to the local symbol table import', () => { - const imp = new Import(systemSymbolImport, ['foo', 'bar']); - imp.add('baz'); - imp.add('__proto__'); - const id1 = imp.getId('baz'); - const id2 = imp.getId('__proto__'); - expect(id1).toBe(systemSymbolTable.length + 3); - expect(id2).toBe(systemSymbolTable.length + 4); - const text1 = imp.getText(systemSymbolTable.length + 3); - const text2 = imp.getText(systemSymbolTable.length + 4); - expect(text1).toBe('baz'); - expect(text2).toBe('__proto__'); -}); - -test('returns ID of new local symbol', () => { - const imp = new Import(systemSymbolImport, []); - const id = imp.add('baz'); - expect(id).toBe(systemSymbolTable.length + 1); - const id2 = imp.getId('baz'); - expect(id2).toBe(systemSymbolTable.length + 1); -}); - -test('returns same ID when adding symbol with the same text', () => { - const imp = new Import(systemSymbolImport, []); - const id1 = imp.add('baz'); - const id2 = imp.add('bar'); - const id3 = imp.add('baz'); - expect(id1).toBe(id3); - expect(id1).not.toBe(id2); - expect(imp.add('bar')).toBe(id2); - expect(imp.add('bar')).toBe(id2); -}); diff --git a/src/json-pack/ion/__tests__/IonEncoder.spec.ts b/src/json-pack/ion/__tests__/IonEncoder.spec.ts deleted file mode 100644 index c7f55ee716..0000000000 --- a/src/json-pack/ion/__tests__/IonEncoder.spec.ts +++ /dev/null @@ -1,212 +0,0 @@ -import {IonEncoderFast} from '../IonEncoderFast'; -import {makeBinaryWriter, dom} from 'ion-js'; - -const encode = (value: unknown): Uint8Array => { - const writer = makeBinaryWriter(); - dom.Value.from(value)?.writeTo(writer); - writer.close(); - return writer.getBytes(); -}; - -const encoder = new IonEncoderFast(); - -describe('tokens', () => { - const tokens: unknown[] = [true, false, null]; - - for (const bool of tokens) { - test(`${bool}`, () => { - const encoded = encoder.encode(bool); - expect(encoded).toEqual(encode(bool)); - }); - } -}); - -describe('integers', () => { - const ints: number[] = [ - 0, - 1, - 2, - 3, - 128, - 254, - 255, - 256, - 257, - 65535, - 2 ** 16 - 2, - 2 ** 16 - 1, - 2 ** 16 - 0, - 2 ** 16 + 1, - 2 ** 16 + 2, - 2 ** 24 - 2, - 2 ** 24 - 1, - 2 ** 24 - 0, - 2 ** 24 + 1, - 2 ** 24 + 2, - 2 ** 32 - 2, - 2 ** 32 - 1, - 2 ** 32 - 0, - 2 ** 32 + 1, - 2 ** 32 + 2, - 2 ** 40 - 2, - 2 ** 40 - 0, - 2 ** 40 + 1, - 2 ** 40 + 2, - 2 ** 48 - 2, - 2 ** 48 - 1, - 2 ** 48 - 0, - 2 ** 48 + 1, - 2 ** 48 + 2, - 2 ** 53 - 1, - ]; - - for (const value of ints) { - test(`${value}`, () => { - const encoded = encoder.encode(value); - expect(encoded).toEqual(encode(value)); - }); - } - for (const value of ints) { - test(`${-value}`, () => { - const encoded = encoder.encode(-value); - expect(encoded).toEqual(encode(-value)); - }); - } -}); - -describe('floats', () => { - const values: number[] = [ - 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.123, 0.1234, 0.12345, 1.1, 123.123, 3.14, 3.14159, 4.23, 7.22, - ]; - - for (const value of values) { - test(`${value}`, () => { - const encoded = encoder.encode(value); - expect(encoded).toEqual(encode(value)); - }); - } - for (const value of values) { - test(`${-value}`, () => { - const encoded = encoder.encode(-value); - expect(encoded).toEqual(encode(-value)); - }); - } -}); - -describe('strings', () => { - const values: string[] = [ - '', - 'a', - 'ab', - 'abc', - 'abcd', - 'abcde', - 'abcdef', - 'abcdefg', - 'abcdefgh', - 'abcdefghi', - 'abcdefghij', - 'abcdefghijk', - 'abcdefghijkl', - 'abcdefghijklm', - 'abcdefghijklmn', - 'abcdefghijklmno', - 'abcdefghijklmnop', - 'abcdefghijklmnopq', - 'abcdefghijklmnopqr', - 'abcdefghijklmnopqrs', - 'abcdefghijklmnopqrst', - 'abcdefghijklmnopqrstu', - 'abcdefghijklmnopqrstuv', - 'abcdefghijklmnopqrstuvw', - 'abcdefghijklmnopqrstuvwx', - 'abcdefghijklmnopqrstuvwxy', - 'abcdefghijklmnopqrstuvwxyz', - '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567', - 'a'.repeat(20000), - ]; - - for (const value of values) { - test(`${value.substring(0, 80)}`, () => { - const encoded = encoder.encode(value); - const expected = encode(value); - // console.log(encoded); - // console.log(expected); - expect(encoded).toEqual(expected); - }); - } -}); - -describe('binary', () => { - const values: Uint8Array[] = [ - new Uint8Array(), - new Uint8Array([0]), - new Uint8Array([1, 2, 3]), - new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]), - new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]), - ]; - - for (const value of values) { - test(`${value}`, () => { - const encoded = encoder.encode(value); - const expected = encode(value); - // console.log(encoded); - // console.log(expected); - expect(encoded).toEqual(expected); - }); - } -}); - -describe('arrays', () => { - const values: unknown[][] = [ - [], - [''], - ['asdf'], - [0], - [0, 0, 0], - [0, 1], - [1, 2, 3, 4, 5, 6], - [1, 2, 3, 4, 5, 6, 7], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [[]], - [[1, 2, 3]], - [[1, 2, 3, 'x'], 'asdf', null, false, true], - ]; - - for (const value of values) { - test(`${JSON.stringify(value)}`, () => { - const encoded = encoder.encode(value); - expect(encoded).toEqual(encode(value)); - }); - } -}); - -describe('objects', () => { - const values: object[] = [ - {}, - {a: 1}, - {a: 'b', foo: 'bar'}, - {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16}, - { - foo: [ - 'bar', - 1, - null, - { - a: 'gg', - d: 123, - }, - ], - }, - ]; - - for (const value of values) { - test(`${JSON.stringify(value)}`, () => { - const encoded = encoder.encode(value); - const expected = encode(value); - // console.log(encoded); - // console.log(expected); - expect(encoded).toEqual(expected); - }); - } -}); diff --git a/src/json-pack/ion/__tests__/automated.spec.ts b/src/json-pack/ion/__tests__/automated.spec.ts deleted file mode 100644 index d317e93abf..0000000000 --- a/src/json-pack/ion/__tests__/automated.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {IonEncoderFast} from '../IonEncoderFast'; -import {load} from 'ion-js'; -import {documents} from '../../../__tests__/json-documents'; - -const encoder = new IonEncoderFast(); - -for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - const encoded = encoder.encode(t.json); - // console.log(encoded); - const decoded = load(encoded); - const pojo = JSON.parse(JSON.stringify(decoded)); - expect(pojo).toEqual(t.json); - }); -} diff --git a/src/json-pack/ion/ast.ts b/src/json-pack/ion/ast.ts deleted file mode 100644 index 9c56309f9e..0000000000 --- a/src/json-pack/ion/ast.ts +++ /dev/null @@ -1,185 +0,0 @@ -import {utf8Size} from '../../util/strings/utf8'; -import {Import} from './Import'; - -export interface AstNode { - /** Node value as JS value. */ - readonly val: T; - /** Node representation length. */ - readonly len: number; - /** Total length of the node. */ - byteLength(): number; -} - -export class NullAstNode implements AstNode { - public readonly val = null; - public readonly len = 1; - public byteLength(): number { - return 1; - } -} - -export class BoolAstNode implements AstNode { - public readonly len = 1; - constructor(public readonly val: boolean) {} - public byteLength(): number { - return 1; - } -} - -export class UintAstNode implements AstNode { - public readonly len: number; - constructor(public readonly val: number) { - if (!val) this.len = 0; - else if (val <= 0xff) this.len = 1; - else if (val <= 0xffff) this.len = 2; - else if (val <= 0xffffff) this.len = 3; - else if (val <= 0xffffffff) this.len = 4; - else if (val <= 0xffffffffff) this.len = 5; - else if (val <= 0xffffffffffff) this.len = 6; - else this.len = 7; - } - public byteLength(): number { - return 1 + this.len; - } -} - -export class NintAstNode implements AstNode { - public readonly len: number; - constructor(public readonly val: number) { - const uint = -val; - if (!uint) this.len = 0; - else if (uint <= 0xff) this.len = 1; - else if (uint <= 0xffff) this.len = 2; - else if (uint <= 0xffffff) this.len = 3; - else if (uint <= 0xffffffff) this.len = 4; - else if (uint <= 0xffffffffff) this.len = 5; - else if (uint <= 0xffffffffffff) this.len = 6; - else this.len = 7; - } - public byteLength(): number { - return 1 + this.len; - } -} - -export class FloatAstNode implements AstNode { - public readonly len: number = 8; - constructor(public readonly val: number) {} - public byteLength(): number { - return 1 + this.len; - } -} - -const vUintLen = (num: number): number => { - if (num <= 0b1111111) return 1; - else if (num <= 0b1111111_1111111) return 2; - else if (num <= 0b1111111_1111111_1111111) return 3; - else if (num <= 0b1111111_1111111_1111111_1111111) return 4; - else if (num <= 0b1111111_1111111_1111111_1111111_1111111) return 5; - else return 6; -}; - -export class StrAstNode implements AstNode { - public readonly len: number; - constructor(public readonly val: string) { - this.len = utf8Size(val); - } - public byteLength(): number { - return this.len < 14 ? 1 + this.len : 1 + vUintLen(this.len) + this.len; - } -} - -export class BinAstNode implements AstNode { - public readonly len: number; - constructor(public readonly val: Uint8Array) { - this.len = val.length; - } - public byteLength(): number { - return this.len < 14 ? 1 + this.len : 1 + vUintLen(this.len) + this.len; - } -} - -export class ArrAstNode implements AstNode[] | null> { - public readonly len: number; - constructor(public readonly val: AstNode[] | null) { - if (val === null) { - this.len = 1; - } else { - if (!val.length) this.len = 0; - else { - let elementLength = 0; - for (let i = 0; i < val.length; i++) elementLength += val[i].byteLength(); - this.len = elementLength; - } - } - } - public byteLength(): number { - return this.len < 14 ? 1 + this.len : 1 + vUintLen(this.len) + this.len; - } -} - -export class ObjAstNode implements AstNode> | null> { - public readonly len: number; - constructor(public readonly val: Map> | null) { - if (val === null) { - this.len = 1; - } else { - if (!val.size) this.len = 0; - else { - let len = 0; - val.forEach((node, symbolId) => { - len += vUintLen(symbolId) + node.byteLength(); - }); - this.len = len; - } - } - } - public byteLength(): number { - return this.len < 14 ? 1 + this.len : 1 + vUintLen(this.len) + this.len; - } -} - -export class AnnotationAstNode implements AstNode> { - public readonly len: number; - public readonly annotationLen: number; - constructor( - public readonly val: AstNode, - public readonly annotations: number[], - ) { - let len = 0; - for (let i = 0; i < annotations.length; i++) len += vUintLen(annotations[i]); - this.annotationLen = len; - len += vUintLen(len); - len += val.byteLength(); - this.len = len; - } - public byteLength(): number { - return this.len < 14 ? 1 + this.len : 1 + vUintLen(this.len) + this.len; - } -} - -const isSafeInteger = Number.isSafeInteger; - -export const toAst = (val: unknown, symbols: Import): AstNode => { - if (val === null) return new NullAstNode(); - if (val instanceof Array) return new ArrAstNode(val.map((el) => toAst(el, symbols))); - if (val instanceof Uint8Array) return new BinAstNode(val); - switch (typeof val) { - case 'boolean': - return new BoolAstNode(val); - case 'number': { - if (isSafeInteger(val)) return val >= 0 ? new UintAstNode(val) : new NintAstNode(val); - else return new FloatAstNode(val); - } - case 'string': - return new StrAstNode(val); - case 'object': { - const struct = new Map>(); - for (const key in val) { - const symbolId = symbols.add(key); - struct.set(symbolId, toAst((val as any)[key], symbols)); - } - return new ObjAstNode(struct); - } - } - throw new Error('UNKNOWN_TYPE'); -}; diff --git a/src/json-pack/ion/constants.ts b/src/json-pack/ion/constants.ts deleted file mode 100644 index 2729622b7e..0000000000 --- a/src/json-pack/ion/constants.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const enum TYPE { - NULL = 0b0000, - BOOL = 0b0001, - UINT = 0b0010, - NINT = 0b0011, - FLOT = 0b0100, - STRI = 0b1000, - LIST = 0b1011, - BINA = 0b1010, - STRU = 0b1101, - ANNO = 0b1110, -} - -export const enum TYPE_OVERLAY { - NULL = 0b0000_0000, - BOOL = 0b0001_0000, - UINT = 0b0010_0000, - NINT = 0b0011_0000, - FLOT = 0b0100_0000, - STRI = 0b1000_0000, - LIST = 0b1011_0000, - BINA = 0b1010_0000, - STRU = 0b1101_0000, - ANNO = 0b1110_0000, -} diff --git a/src/json-pack/ion/symbols.ts b/src/json-pack/ion/symbols.ts deleted file mode 100644 index 59c4c1206d..0000000000 --- a/src/json-pack/ion/symbols.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Import} from './Import'; -import {SymbolTable} from './types'; - -export const systemSymbolTable: SymbolTable = [ - '$ion', - '$ion_1_0', - '$ion_symbol_table', - 'name', - 'version', - 'imports', - 'symbols', - 'max_id', - '$ion_shared_symbol_table', -]; - -export const systemSymbolImport = new Import(null, systemSymbolTable); diff --git a/src/json-pack/ion/types.ts b/src/json-pack/ion/types.ts deleted file mode 100644 index 1cc84d3a45..0000000000 --- a/src/json-pack/ion/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type SymbolTable = string[]; diff --git a/src/json-pack/json/JsonDecoder.ts b/src/json-pack/json/JsonDecoder.ts deleted file mode 100644 index d595b5359e..0000000000 --- a/src/json-pack/json/JsonDecoder.ts +++ /dev/null @@ -1,683 +0,0 @@ -import {decodeUtf8} from '../../util/buffers/utf8/decodeUtf8'; -import {Reader} from '../../util/buffers/Reader'; -import {fromBase64Bin} from '../../util/base64/fromBase64Bin'; -import {findEndingQuote} from './util'; -import type {BinaryJsonDecoder, PackValue} from '../types'; - -const REGEX_REPLACE_ESCAPED_CHARS = /\\(b|f|n|r|t|"|\/|\\)/g; -const escapedCharReplacer = (char: string) => { - switch (char) { - case '\\b': - return '\b'; - case '\\f': - return '\f'; - case '\\n': - return '\n'; - case '\\r': - return '\r'; - case '\\t': - return '\t'; - case '\\"': - return '"'; - case '\\/': - return '/'; - case '\\\\': - return '\\'; - } - return char; -}; - -// Starts with data:application/octet-stream;base64, - 64 61 74 61 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6f 63 74 65 74 2d 73 74 72 65 61 6d 3b 62 61 73 65 36 34 2c -const hasBinaryPrefix = (u8: Uint8Array, x: number) => - u8[x] === 0x64 && - u8[x + 1] === 0x61 && - u8[x + 2] === 0x74 && - u8[x + 3] === 0x61 && - u8[x + 4] === 0x3a && - u8[x + 5] === 0x61 && - u8[x + 6] === 0x70 && - u8[x + 7] === 0x70 && - u8[x + 8] === 0x6c && - u8[x + 9] === 0x69 && - u8[x + 10] === 0x63 && - u8[x + 11] === 0x61 && - u8[x + 12] === 0x74 && - u8[x + 13] === 0x69 && - u8[x + 14] === 0x6f && - u8[x + 15] === 0x6e && - u8[x + 16] === 0x2f && - u8[x + 17] === 0x6f && - u8[x + 18] === 0x63 && - u8[x + 19] === 0x74 && - u8[x + 20] === 0x65 && - u8[x + 21] === 0x74 && - u8[x + 22] === 0x2d && - u8[x + 23] === 0x73 && - u8[x + 24] === 0x74 && - u8[x + 25] === 0x72 && - u8[x + 26] === 0x65 && - u8[x + 27] === 0x61 && - u8[x + 28] === 0x6d && - u8[x + 29] === 0x3b && - u8[x + 30] === 0x62 && - u8[x + 31] === 0x61 && - u8[x + 32] === 0x73 && - u8[x + 33] === 0x65 && - u8[x + 34] === 0x36 && - u8[x + 35] === 0x34 && - u8[x + 36] === 0x2c; - -// Matches "data:application/cbor,base64;9w==" -const isUndefined = (u8: Uint8Array, x: number) => - // u8[x++] === 0x22 && // " - // u8[x++] === 0x64 && // d - u8[x++] === 0x61 && // a - u8[x++] === 0x74 && // t - u8[x++] === 0x61 && // a - u8[x++] === 0x3a && // : - u8[x++] === 0x61 && // a - u8[x++] === 0x70 && // p - u8[x++] === 0x70 && // p - u8[x++] === 0x6c && // l - u8[x++] === 0x69 && // i - u8[x++] === 0x63 && // c - u8[x++] === 0x61 && // a - u8[x++] === 0x74 && // t - u8[x++] === 0x69 && // i - u8[x++] === 0x6f && // o - u8[x++] === 0x6e && // n - u8[x++] === 0x2f && // / - u8[x++] === 0x63 && // c - u8[x++] === 0x62 && // b - u8[x++] === 0x6f && // o - u8[x++] === 0x72 && // r - u8[x++] === 0x2c && // , - u8[x++] === 0x62 && // b - u8[x++] === 0x61 && // a - u8[x++] === 0x73 && // s - u8[x++] === 0x65 && // e - u8[x++] === 0x36 && // 6 - u8[x++] === 0x34 && // 4 - u8[x++] === 0x3b && // ; - u8[x++] === 0x39 && // 9 - u8[x++] === 0x77 && // w - u8[x++] === 0x3d && // = - u8[x++] === 0x3d && // = - u8[x++] === 0x22; // " - -const fromCharCode = String.fromCharCode; - -const readShortUtf8StrAndUnescape = (reader: Reader): string => { - const buf = reader.uint8; - const len = buf.length; - const points: number[] = []; - let x = reader.x; - let prev = 0; - while (x < len) { - let code = buf[x++]!; - if ((code & 0x80) === 0) { - if (prev === 92) { - switch (code) { - case 98: // \b - code = 8; - break; - case 102: // \f - code = 12; - break; - case 110: // \n - code = 10; - break; - case 114: // \r - code = 13; - break; - case 116: // \t - code = 9; - break; - case 34: // \" - code = 34; - break; - case 47: // \/ - code = 47; - break; - case 92: // \\ - code = 92; - break; - default: - throw new Error('Invalid JSON'); - } - prev = 0; - } else { - if (code === 34) break; - prev = code; - if (prev === 92) continue; - } - } else { - const octet2 = buf[x++]! & 0x3f; - if ((code & 0xe0) === 0xc0) { - code = ((code & 0x1f) << 6) | octet2; - } else { - const octet3 = buf[x++]! & 0x3f; - if ((code & 0xf0) === 0xe0) { - code = ((code & 0x1f) << 12) | (octet2 << 6) | octet3; - } else { - if ((code & 0xf8) === 0xf0) { - const octet4 = buf[x++]! & 0x3f; - let unit = ((code & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - const unit0 = ((unit >>> 10) & 0x3ff) | 0xd800; - unit = 0xdc00 | (unit & 0x3ff); - points.push(unit0); - code = unit; - } else { - code = unit; - } - } - } - } - } - points.push(code); - } - reader.x = x; - return fromCharCode.apply(String, points); -}; - -export class JsonDecoder implements BinaryJsonDecoder { - public reader = new Reader(); - - public read(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.readAny(); - } - - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.readAny(); - } - - public readAny(): unknown { - this.skipWhitespace(); - const reader = this.reader; - const x = reader.x; - const uint8 = reader.uint8; - const char = uint8[x]; - switch (char) { - case 34: { - // " - if (uint8[x + 1] === 0x64) { - // d - const bin = this.tryReadBin(); - if (bin) return bin; - if (isUndefined(uint8, x + 2)) { - reader.x = x + 35; - return undefined; - } - } - return this.readStr(); - } - case 91: // [ - return this.readArr(); - case 102: // f - return this.readFalse(); - case 110: // n - return this.readNull(); - case 116: // t - return this.readTrue(); - case 123: // { - return this.readObj(); - default: - if ((char >= 48 && char <= 57) || char === 45) return this.readNum(); - throw new Error('Invalid JSON'); - } - } - - public skipWhitespace(): void { - const reader = this.reader; - const uint8 = reader.uint8; - let x = reader.x; - let char: number = 0; - while (true) { - char = uint8[x]; - switch (char) { - case 32: // space - case 9: // tab - case 10: // line feed - case 13: // carriage return - x++; - continue; - default: - reader.x = x; - return; - } - } - } - - public readNull(): null { - if (this.reader.u32() !== 0x6e756c6c) throw new Error('Invalid JSON'); - return null; - } - - public readTrue(): true { - if (this.reader.u32() !== 0x74727565) throw new Error('Invalid JSON'); - return true; - } - - public readFalse(): false { - const reader = this.reader; - if (reader.u8() !== 0x66 || reader.u32() !== 0x616c7365) throw new Error('Invalid JSON'); - return false; - } - - public readBool(): unknown { - const reader = this.reader; - switch (reader.uint8[reader.x]) { - case 102: // f - return this.readFalse(); - case 116: // t - return this.readTrue(); - default: - throw new Error('Invalid JSON'); - } - } - - public readNum(): number { - const reader = this.reader; - const uint8 = reader.uint8; - let x = reader.x; - let c = uint8[x++]; - const c1 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c2 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c3 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c4 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c5 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c6 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c7 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c8 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c9 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c10 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c11 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c12 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c13 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c14 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c15 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c16 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c17 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c18 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c19 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c20 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode( - c1, - c2, - c3, - c4, - c5, - c6, - c7, - c8, - c9, - c10, - c11, - c12, - c13, - c14, - c15, - c16, - c17, - c18, - c19, - c20, - ); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c21 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode( - c1, - c2, - c3, - c4, - c5, - c6, - c7, - c8, - c9, - c10, - c11, - c12, - c13, - c14, - c15, - c16, - c17, - c18, - c19, - c20, - c21, - ); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c22 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode( - c1, - c2, - c3, - c4, - c5, - c6, - c7, - c8, - c9, - c10, - c11, - c12, - c13, - c14, - c15, - c16, - c17, - c18, - c19, - c20, - c21, - c22, - ); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c23 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode( - c1, - c2, - c3, - c4, - c5, - c6, - c7, - c8, - c9, - c10, - c11, - c12, - c13, - c14, - c15, - c16, - c17, - c18, - c19, - c20, - c21, - c22, - c23, - ); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - const c24 = c; - c = uint8[x++]; - if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) { - reader.x = x - 1; - const num = +fromCharCode( - c1, - c2, - c3, - c4, - c5, - c6, - c7, - c8, - c9, - c10, - c11, - c12, - c13, - c14, - c15, - c16, - c17, - c18, - c19, - c20, - c21, - c22, - c24, - ); - if (num !== num) throw new Error('Invalid JSON'); - return num; - } - throw new Error('Invalid JSON'); - } - - public readStr(): string { - const reader = this.reader; - const uint8 = reader.uint8; - const char = uint8[reader.x++]; - if (char !== 0x22) throw new Error('Invalid JSON'); - const x0 = reader.x; - const x1 = findEndingQuote(uint8, x0); - let str = decodeUtf8(uint8, x0, x1 - x0); - /** @todo perf: maybe faster is to first check if there are any escaped chars. */ - str = str.replace(REGEX_REPLACE_ESCAPED_CHARS, escapedCharReplacer); - reader.x = x1 + 1; - return str; - } - - public tryReadBin(): Uint8Array | undefined { - const reader = this.reader; - const u8 = reader.uint8; - let x = reader.x; - if (u8[x++] !== 0x22) return undefined; - const hasDataUrlPrefix = hasBinaryPrefix(u8, x); - if (!hasDataUrlPrefix) return undefined; - x += 37; - const x0 = x; - x = findEndingQuote(u8, x); - reader.x = x0; - const bin = fromBase64Bin(reader.view, x0, x - x0); - reader.x = x + 1; - return bin; - } - - public readBin(): Uint8Array { - const reader = this.reader; - const u8 = reader.uint8; - let x = reader.x; - if (u8[x++] !== 0x22) throw new Error('Invalid JSON'); - const hasDataUrlPrefix = hasBinaryPrefix(u8, x); - if (!hasDataUrlPrefix) throw new Error('Invalid JSON'); - x += 37; - const x0 = x; - x = findEndingQuote(u8, x); - reader.x = x0; - const bin = fromBase64Bin(reader.view, x0, x - x0); - reader.x = x + 1; - return bin; - } - - public readArr(): unknown[] { - const reader = this.reader; - if (reader.u8() !== 0x5b) throw new Error('Invalid JSON'); - const arr: unknown[] = []; - const uint8 = reader.uint8; - while (true) { - this.skipWhitespace(); - const char = uint8[reader.x]; - if (char === 0x5d) return reader.x++, arr; // ] - if (char === 0x2c) { - reader.x++; - continue; - } // , - arr.push(this.readAny()); - } - } - - public readObj(): PackValue | Record | unknown { - const reader = this.reader; - if (reader.u8() !== 0x7b) throw new Error('Invalid JSON'); - const obj: Record = {}; - const uint8 = reader.uint8; - while (true) { - this.skipWhitespace(); - let char = uint8[reader.x]; - if (char === 0x7d) return reader.x++, obj; // } - if (char === 0x2c) { - reader.x++; - continue; - } // , - char = uint8[reader.x++]; - if (char !== 0x22) throw new Error('Invalid JSON'); - const key = readShortUtf8StrAndUnescape(reader); - if (key === '__proto__') throw new Error('Invalid JSON'); - this.skipWhitespace(); - if (reader.u8() !== 0x3a) throw new Error('Invalid JSON'); - this.skipWhitespace(); - obj[key] = this.readAny(); - } - } -} diff --git a/src/json-pack/json/JsonDecoderDag.ts b/src/json-pack/json/JsonDecoderDag.ts deleted file mode 100644 index 0735810f2e..0000000000 --- a/src/json-pack/json/JsonDecoderDag.ts +++ /dev/null @@ -1,133 +0,0 @@ -import {JsonDecoder} from './JsonDecoder'; -import {findEndingQuote} from './util'; -import type {PackValue} from '../types'; -import {createFromBase64Bin} from '../../util/base64/createFromBase64Bin'; - -export const fromBase64Bin = createFromBase64Bin(undefined, ''); - -export class JsonDecoderDag extends JsonDecoder { - public readObj(): PackValue | Record | Uint8Array | unknown { - const bytes = this.tryReadBytes(); - if (bytes) return bytes; - const cid = this.tryReadCid(); - if (cid) return cid; - return super.readObj(); - } - - protected tryReadBytes(): Uint8Array | undefined { - const reader = this.reader; - const x = reader.x; - if (reader.u8() !== 0x7b) { - // { - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x22 || reader.u8() !== 0x2f || reader.u8() !== 0x22) { - // "/" - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x3a) { - // : - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x7b) { - // { - reader.x = x; - return; - } - this.skipWhitespace(); - if ( - reader.u8() !== 0x22 || - reader.u8() !== 0x62 || - reader.u8() !== 0x79 || - reader.u8() !== 0x74 || - reader.u8() !== 0x65 || - reader.u8() !== 0x73 || - reader.u8() !== 0x22 - ) { - // "bytes" - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x3a) { - // : - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x22) { - // " - reader.x = x; - return; - } - const bufStart = reader.x; - const bufEnd = findEndingQuote(reader.uint8, bufStart); - reader.x = 1 + bufEnd; - this.skipWhitespace(); - if (reader.u8() !== 0x7d) { - // } - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x7d) { - // } - reader.x = x; - return; - } - const bin = fromBase64Bin(reader.view, bufStart, bufEnd - bufStart); - return bin; - } - - protected tryReadCid(): undefined | unknown { - const reader = this.reader; - const x = reader.x; - if (reader.u8() !== 0x7b) { - // { - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x22 || reader.u8() !== 0x2f || reader.u8() !== 0x22) { - // "/" - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x3a) { - // : - reader.x = x; - return; - } - this.skipWhitespace(); - if (reader.u8() !== 0x22) { - // " - reader.x = x; - return; - } - const bufStart = reader.x; - const bufEnd = findEndingQuote(reader.uint8, bufStart); - reader.x = 1 + bufEnd; - this.skipWhitespace(); - if (reader.u8() !== 0x7d) { - // } - reader.x = x; - return; - } - const finalX = reader.x; - reader.x = bufStart; - const cid = reader.ascii(bufEnd - bufStart); - reader.x = finalX; - return this.readCid(cid); - } - - public readCid(cid: string): unknown { - return cid; - } -} diff --git a/src/json-pack/json/JsonEncoder.ts b/src/json-pack/json/JsonEncoder.ts deleted file mode 100644 index a57566005a..0000000000 --- a/src/json-pack/json/JsonEncoder.ts +++ /dev/null @@ -1,282 +0,0 @@ -import {toBase64Bin} from '../../util/base64/toBase64Bin'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder, StreamingBinaryJsonEncoder} from '../types'; - -export class JsonEncoder implements BinaryJsonEncoder, StreamingBinaryJsonEncoder { - constructor(public readonly writer: IWriter & IWriterGrowable) {} - - public encode(value: unknown): Uint8Array { - const writer = this.writer; - writer.reset(); - this.writeAny(value); - return writer.flush(); - } - - /** - * Called when the encoder encounters a value that it does not know how to encode. - * - * @param value Some JavaScript value. - */ - public writeUnknown(value: unknown): void { - this.writeNull(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'boolean': - return this.writeBoolean(value); - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'object': { - if (value === null) return this.writeNull(); - const constructor = value.constructor; - switch (constructor) { - case Object: - return this.writeObj(value as Record); - case Array: - return this.writeArr(value as unknown[]); - case Uint8Array: - return this.writeBin(value as Uint8Array); - default: - return this.writeUnknown(value); - } - } - case 'undefined': { - return this.writeUndef(); - } - default: - return this.writeUnknown(value); - } - } - - public writeNull(): void { - this.writer.u32(0x6e756c6c); // null - } - - public writeUndef(): void { - const writer = this.writer; - const length = 35; - writer.ensureCapacity(length); - // Write: "data:application/cbor,base64;9w==" - const view = writer.view; - let x = writer.x; - view.setUint32(x, 0x22_64_61_74); // "dat - x += 4; - view.setUint32(x, 0x61_3a_61_70); // a:ap - x += 4; - view.setUint32(x, 0x70_6c_69_63); // plic - x += 4; - view.setUint32(x, 0x61_74_69_6f); // atio - x += 4; - view.setUint32(x, 0x6e_2f_63_62); // n/cb - x += 4; - view.setUint32(x, 0x6f_72_2c_62); // or,b - x += 4; - view.setUint32(x, 0x61_73_65_36); // ase6 - x += 4; - view.setUint32(x, 0x34_3b_39_77); // 4;9w - x += 4; - view.setUint16(x, 0x3d_3d); // == - x += 2; - writer.uint8[x++] = 0x22; // " - writer.x = x; - } - - public writeBoolean(bool: boolean): void { - if (bool) - this.writer.u32(0x74727565); // true - else this.writer.u8u32(0x66, 0x616c7365); // false - } - - public writeNumber(num: number): void { - const str = num.toString(); - this.writer.ascii(str); - } - - public writeInteger(int: number): void { - this.writeNumber(int >> 0 === int ? int : Math.trunc(int)); - } - - public writeUInteger(uint: number): void { - this.writeInteger(uint < 0 ? -uint : uint); - } - - public writeFloat(float: number): void { - this.writeNumber(float); - } - - public writeBin(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.ensureCapacity(38 + 3 + (length << 1)); - // Write: "data:application/octet-stream;base64, - 22 64 61 74 61 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6f 63 74 65 74 2d 73 74 72 65 61 6d 3b 62 61 73 65 36 34 2c - const view = writer.view; - let x = writer.x; - view.setUint32(x, 0x22_64_61_74); // "dat - x += 4; - view.setUint32(x, 0x61_3a_61_70); // a:ap - x += 4; - view.setUint32(x, 0x70_6c_69_63); // plic - x += 4; - view.setUint32(x, 0x61_74_69_6f); // atio - x += 4; - view.setUint32(x, 0x6e_2f_6f_63); // n/oc - x += 4; - view.setUint32(x, 0x74_65_74_2d); // tet- - x += 4; - view.setUint32(x, 0x73_74_72_65); // stre - x += 4; - view.setUint32(x, 0x61_6d_3b_62); // am;b - x += 4; - view.setUint32(x, 0x61_73_65_36); // ase6 - x += 4; - view.setUint16(x, 0x34_2c); // 4, - x += 2; - x = toBase64Bin(buf, 0, length, view, x); - writer.uint8[x++] = 0x22; // " - writer.x = x; - } - - public writeStr(str: string): void { - const writer = this.writer; - const length = str.length; - writer.ensureCapacity(length * 4 + 2); - if (length < 256) { - let x = writer.x; - const uint8 = writer.uint8; - uint8[x++] = 0x22; // " - for (let i = 0; i < length; i++) { - const code = str.charCodeAt(i); - switch (code) { - case 34: // " - case 92: // \ - uint8[x++] = 0x5c; // \ - break; - } - if (code < 32 || code > 126) { - writer.utf8(JSON.stringify(str)); - return; - } else uint8[x++] = code; - } - uint8[x++] = 0x22; // " - writer.x = x; - return; - } - writer.utf8(JSON.stringify(str)); - } - - public writeAsciiStr(str: string): void { - const length = str.length; - const writer = this.writer; - writer.ensureCapacity(length * 2 + 2); - const uint8 = writer.uint8; - let x = writer.x; - uint8[x++] = 0x22; // " - for (let i = 0; i < length; i++) { - const code = str.charCodeAt(i); - switch (code) { - case 34: // " - case 92: // \ - uint8[x++] = 0x5c; // \ - break; - } - uint8[x++] = code; - } - uint8[x++] = 0x22; // " - writer.x = x; - } - - public writeArr(arr: unknown[]): void { - const writer = this.writer; - writer.u8(0x5b); // [ - const length = arr.length; - const last = length - 1; - for (let i = 0; i < last; i++) { - this.writeAny(arr[i]); - writer.u8(0x2c); // , - } - if (last >= 0) this.writeAny(arr[last]); - writer.u8(0x5d); // ] - } - - public writeArrSeparator(): void { - this.writer.u8(0x2c); // , - } - - public writeObj(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - const length = keys.length; - if (!length) return writer.u16(0x7b7d); // {} - writer.u8(0x7b); // { - for (let i = 0; i < length; i++) { - const key = keys[i]; - const value = obj[key]; - this.writeStr(key); - writer.u8(0x3a); // : - this.writeAny(value); - writer.u8(0x2c); // , - } - writer.uint8[writer.x - 1] = 0x7d; // } - } - - public writeObjSeparator(): void { - this.writer.u8(0x2c); // , - } - - public writeObjKeySeparator(): void { - this.writer.u8(0x3a); // : - } - - // ------------------------------------------------------- Streaming encoding - - public writeStartStr(): void { - throw new Error('Method not implemented.'); - } - - public writeStrChunk(str: string): void { - throw new Error('Method not implemented.'); - } - - public writeEndStr(): void { - throw new Error('Method not implemented.'); - } - - public writeStartBin(): void { - throw new Error('Method not implemented.'); - } - - public writeBinChunk(buf: Uint8Array): void { - throw new Error('Method not implemented.'); - } - - public writeEndBin(): void { - throw new Error('Method not implemented.'); - } - - public writeStartArr(): void { - this.writer.u8(0x5b); // [ - } - - public writeArrChunk(item: unknown): void { - throw new Error('Method not implemented.'); - } - - public writeEndArr(): void { - this.writer.u8(0x5d); // ] - } - - public writeStartObj(): void { - this.writer.u8(0x7b); // { - } - - public writeObjChunk(key: string, value: unknown): void { - throw new Error('Method not implemented.'); - } - - public writeEndObj(): void { - this.writer.u8(0x7d); // } - } -} diff --git a/src/json-pack/json/JsonEncoderDag.ts b/src/json-pack/json/JsonEncoderDag.ts deleted file mode 100644 index 3c7dd4f51b..0000000000 --- a/src/json-pack/json/JsonEncoderDag.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {JsonEncoderStable} from './JsonEncoderStable'; -import {createToBase64Bin} from '../../util/base64/createToBase64Bin'; - -const objBaseLength = '{"/":{"bytes":""}}'.length; -const cidBaseLength = '{"/":""}'.length; -const base64Encode = createToBase64Bin(undefined, ''); - -/** - * Base class for implementing DAG-JSON encoders. - * - * @see https://ipld.io/specs/codecs/dag-json/spec/ - */ -export class JsonEncoderDag extends JsonEncoderStable { - /** - * Encodes binary data as nested `["/", "bytes"]` object encoded in Base64 - * without padding. - * - * Example: - * - * ```json - * {"/":{"bytes":"aGVsbG8gd29ybGQ"}} - * ``` - * - * @param buf Binary data to write. - */ - public writeBin(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.ensureCapacity(objBaseLength + (length << 1)); - const view = writer.view; - const uint8 = writer.uint8; - let x = writer.x; - view.setUint32(x, 0x7b222f22); // {"/" - x += 4; - view.setUint32(x, 0x3a7b2262); // :{"b - x += 4; - view.setUint32(x, 0x79746573); // ytes - x += 4; - view.setUint16(x, 0x223a); // ": - x += 2; - uint8[x] = 0x22; // " - x += 1; - x = base64Encode(buf, 0, length, view, x); - view.setUint16(x, 0x227d); // "} - x += 2; - uint8[x] = 0x7d; // } - x += 1; - writer.x = x; - } - - public writeCid(cid: string): void { - const writer = this.writer; - writer.ensureCapacity(cidBaseLength + cid.length); - writer.u32(0x7b222f22); // {"/" - writer.u16(0x3a22); // :" - writer.ascii(cid); - writer.u16(0x227d); // "} - } -} diff --git a/src/json-pack/json/JsonEncoderStable.ts b/src/json-pack/json/JsonEncoderStable.ts deleted file mode 100644 index 203d07dcdf..0000000000 --- a/src/json-pack/json/JsonEncoderStable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {JsonEncoder} from './JsonEncoder'; -import {sort} from '../../util/sort/insertion2'; -import {objKeyCmp} from '../util/objKeyCmp'; - -export class JsonEncoderStable extends JsonEncoder { - public writeObj(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - sort(keys, objKeyCmp); - const length = keys.length; - if (!length) return writer.u16(0x7b7d); // {} - writer.u8(0x7b); // { - for (let i = 0; i < length; i++) { - const key = keys[i]; - const value = obj[key]; - this.writeStr(key); - writer.u8(0x3a); // : - this.writeAny(value); - writer.u8(0x2c); // , - } - writer.uint8[writer.x - 1] = 0x7d; // } - } -} diff --git a/src/json-pack/json/README.md b/src/json-pack/json/README.md deleted file mode 100644 index 77066c669b..0000000000 --- a/src/json-pack/json/README.md +++ /dev/null @@ -1,117 +0,0 @@ -## Benchmarks - -Encoding: - -``` -npx ts-node benchmarks/json-pack/bench.json.encoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 5,800,937 ops/sec ยฑ0.90% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 2,220,449 ops/sec ยฑ0.71% (97 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 1,998,965 ops/sec ยฑ0.68% (96 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 1,396,750 ops/sec ยฑ0.80% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 320,862 ops/sec ยฑ1.81% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 214,464 ops/sec ยฑ0.49% (100 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 187,439 ops/sec ยฑ0.68% (97 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 119,426 ops/sec ยฑ1.93% (93 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 87,901 ops/sec ยฑ1.22% (95 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 65,695 ops/sec ยฑ1.06% (96 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 56,424 ops/sec ยฑ1.80% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 38,689 ops/sec ยฑ1.77% (96 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 6,087 ops/sec ยฑ0.45% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 6,094 ops/sec ยฑ0.21% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 4,133 ops/sec ยฑ0.97% (98 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 1,813 ops/sec ยฑ0.26% (99 runs sampled) -Fastest is ๐Ÿ‘ Buffer.from(JSON.stringify()),๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 251,763 ops/sec ยฑ0.65% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 194,535 ops/sec ยฑ0.13% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 154,017 ops/sec ยฑ0.15% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 64,720 ops/sec ยฑ0.13% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 146,873 ops/sec ยฑ0.44% (99 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 127,235 ops/sec ยฑ0.46% (93 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 126,412 ops/sec ยฑ0.10% (101 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 126,018 ops/sec ยฑ0.21% (101 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 50,734 ops/sec ยฑ0.10% (99 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 29,757 ops/sec ยฑ0.32% (100 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 29,607 ops/sec ยฑ0.43% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 29,563 ops/sec ยฑ0.59% (97 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 1,597,067 ops/sec ยฑ0.14% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 979,318 ops/sec ยฑ1.18% (99 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 826,713 ops/sec ยฑ1.74% (93 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 815,531 ops/sec ยฑ3.65% (87 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 1,382,467 ops/sec ยฑ4.90% (78 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 1,009,130 ops/sec ยฑ1.66% (91 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 821,214 ops/sec ยฑ4.36% (88 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 886,689 ops/sec ยฑ0.33% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() x 1,357,017 ops/sec ยฑ0.38% (98 runs sampled) -๐Ÿ‘ Buffer.from(JSON.stringify()) x 965,756 ops/sec ยฑ0.19% (93 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify()) x 648,336 ops/sec ยฑ0.45% (96 runs sampled) -๐Ÿ‘ fast-safe-stringify + Buffer.from(safeStringify.stableStringify()) x 642,934 ops/sec ยฑ0.34% (97 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack JsonEncoder.encode() -``` - -Decoding: - -``` -npx ts-node benchmarks/json-pack/bench.json.decoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 ---------------------------------------------------------------------------- Small object, 175 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 1,149,110 ops/sec ยฑ0.16% (99 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 2,360,476 ops/sec ยฑ0.56% (94 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) ------------------------------------------------------------------------- Typical object, 3587 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 86,604 ops/sec ยฑ0.56% (98 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 245,029 ops/sec ยฑ1.28% (98 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) -------------------------------------------------------------------------- Large object, 13308 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 25,911 ops/sec ยฑ0.64% (102 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 67,049 ops/sec ยฑ0.15% (100 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) -------------------------------------------------------------------- Very large object, 162796 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 1,494 ops/sec ยฑ0.32% (100 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 3,557 ops/sec ยฑ0.33% (100 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) ------------------------------------------------------------------ Object with many keys, 3339 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 47,767 ops/sec ยฑ0.90% (100 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 280,836 ops/sec ยฑ2.21% (94 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) ------------------------------------------------------------------------- String ladder, 13302 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 60,041 ops/sec ยฑ1.26% (94 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 317,991 ops/sec ยฑ1.08% (98 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) -------------------------------------------------------------------------- Long strings, 30251 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 37,350 ops/sec ยฑ0.76% (98 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 44,679 ops/sec ยฑ0.40% (97 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) --------------------------------------------------------------------------- Short strings, 625 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 311,662 ops/sec ยฑ0.59% (97 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 1,131,918 ops/sec ยฑ1.40% (97 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) --------------------------------------------------------------------------------- Numbers, 434 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 631,451 ops/sec ยฑ0.23% (99 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 1,815,177 ops/sec ยฑ0.55% (94 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) --------------------------------------------------------------------------------- Tokens, 1182 bytes -๐Ÿ‘ json-joy/json-pack JsonDecoder.decode() x 1,312,357 ops/sec ยฑ0.55% (99 runs sampled) -๐Ÿ‘ Native JSON.parse(buf.toString()) x 1,385,641 ops/sec ยฑ2.35% (94 runs sampled) -Fastest is ๐Ÿ‘ Native JSON.parse(buf.toString()) -``` diff --git a/src/json-pack/json/__tests__/JsonDecoder.spec.ts b/src/json-pack/json/__tests__/JsonDecoder.spec.ts deleted file mode 100644 index d8946a6a42..0000000000 --- a/src/json-pack/json/__tests__/JsonDecoder.spec.ts +++ /dev/null @@ -1,440 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonDecoder} from '../JsonDecoder'; -import {JsonEncoder} from '../JsonEncoder'; - -const decoder = new JsonDecoder(); - -describe('null', () => { - test('null', () => { - const data = Buffer.from('null', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(null); - }); - - test('null with whitespace', () => { - const data = Buffer.from(' null', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(null); - }); - - test('null with more whitespace', () => { - const data = Buffer.from(' \n\n \n \t \r \r null \r \r \r\t\n', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(null); - }); -}); - -describe('undefined', () => { - test('undefined', () => { - const encoder = new JsonEncoder(new Writer()); - const encoded = encoder.encode(undefined); - const decoded = decoder.read(encoded); - expect(decoded).toBe(undefined); - }); - - test('undefined in array', () => { - const encoder = new JsonEncoder(new Writer()); - const encoded = encoder.encode({foo: [1, undefined, -1]}); - const decoded = decoder.read(encoded); - expect(decoded).toEqual({foo: [1, undefined, -1]}); - }); -}); - -describe('boolean', () => { - test('true', () => { - const data = Buffer.from('true', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(true); - }); - - test('true with whitespace', () => { - const data = Buffer.from('\n \t \r true\n \t \r ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(true); - }); - - test('false', () => { - const data = Buffer.from('false', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(false); - }); - - test('true with whitespace', () => { - const data = Buffer.from('\n \t \r false\n \t \r ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(false); - }); - - test('can read any boolean - 1', () => { - const data = Buffer.from('\n \t \r false\n \t \r ', 'utf-8'); - decoder.reader.reset(data); - decoder.skipWhitespace(); - const value = decoder.readBool(); - expect(value).toBe(false); - }); - - test('can read any boolean - 2', () => { - const data = Buffer.from('true ', 'utf-8'); - decoder.reader.reset(data); - decoder.skipWhitespace(); - const value = decoder.readBool(); - expect(value).toBe(true); - }); -}); - -describe('number', () => { - test('1', () => { - const data = Buffer.from('1', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(1); - }); - - test('12', () => { - const data = Buffer.from('12', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(12); - }); - - test('123', () => { - const data = Buffer.from('123', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(123); - }); - - test('1234', () => { - const data = Buffer.from('1234', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(1234); - }); - - test('12345', () => { - const data = Buffer.from('12345', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(12345); - }); - - test('123456', () => { - const data = Buffer.from('123456', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(123456); - }); - - test('-0.1234', () => { - const data = Buffer.from('-0.1234', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(-0.1234); - }); - - test('3n', () => { - const data = Buffer.from('3n', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(3); - }); - - test('with whitespace', () => { - const data = Buffer.from('\n \r 5.6 ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(5.6); - }); -}); - -describe('string', () => { - test('empty string', () => { - const data = Buffer.from('""', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(''); - }); - - test('empty string with whitespace', () => { - const data = Buffer.from(' \n \r \t "" \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(''); - }); - - test('one char string', () => { - const data = Buffer.from('"a"', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe('a'); - }); - - test('"hello world" string', () => { - const data = Buffer.from('"hello world"', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe('hello world'); - }); - - test('string with emoji', () => { - const str = 'yes! - ๐Ÿ‘๐Ÿป๐Ÿ‘๐Ÿผ๐Ÿ‘๐Ÿฝ๐Ÿ‘๐Ÿพ๐Ÿ‘๐Ÿฟ'; - const data = Buffer.from(' "yes! - ๐Ÿ‘๐Ÿป๐Ÿ‘๐Ÿผ๐Ÿ‘๐Ÿฝ๐Ÿ‘๐Ÿพ๐Ÿ‘๐Ÿฟ" ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('string with quote', () => { - const str = 'this is a "quote"'; - const data = Buffer.from(JSON.stringify(str), 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('string with new line', () => { - const str = 'this is a \n new line'; - const json = JSON.stringify(str); - const data = Buffer.from(json, 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('string with backslash', () => { - const str = 'this is a \\ backslash'; - const json = JSON.stringify(str); - const data = Buffer.from(json, 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('a single backslash character', () => { - const str = '\\'; - const json = JSON.stringify(str); - const data = Buffer.from(json, 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('string with tab', () => { - const str = 'this is a \t tab'; - const json = JSON.stringify(str); - const data = Buffer.from(json, 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe(str); - }); - - test('string unicode characters', () => { - const json = '"15\u00f8C"'; - const data = Buffer.from(json, 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toBe('15\u00f8C'); - }); -}); - -describe('binary', () => { - test('empty buffer', () => { - const encoder = new JsonEncoder(new Writer()); - const data = encoder.encode(new Uint8Array(0)); - decoder.reader.reset(data); - const value1 = decoder.readAny(); - expect(value1).toEqual(new Uint8Array(0)); - decoder.reader.reset(data); - const value2 = decoder.readBin(); - expect(value2).toEqual(new Uint8Array(0)); - }); - - test('a small buffer', () => { - const encoder = new JsonEncoder(new Writer()); - const data = encoder.encode(new Uint8Array([4, 5, 6])); - decoder.reader.reset(data); - const value = decoder.readBin(); - expect(value).toEqual(new Uint8Array([4, 5, 6])); - }); -}); - -describe('array', () => { - test('empty array', () => { - const data = Buffer.from('[]', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([]); - }); - - test('empty array with whitespace', () => { - const data = Buffer.from(' \n \r \t [] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([]); - }); - - test('array with one number element', () => { - const data = Buffer.from(' \n \r \t [1] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([1]); - }); - - test('array with one number element - 2', () => { - const data = Buffer.from(' \n \r \t [ -3.5e2\n] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([-3.5e2]); - }); - - test('array with one boolean', () => { - const data = Buffer.from(' \n \r \t [ true] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([true]); - }); - - test('array with one boolean - 2', () => { - const data = Buffer.from(' \n \r \t [false ] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([false]); - }); - - test('array with one null', () => { - const data = Buffer.from(' \n \r \t [null] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([null]); - }); - - test('array with multiple numbers', () => { - const data = Buffer.from(' \n \r \t [1, 2.2,-3.3 ] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([1, 2.2, -3.3]); - }); - - test('nested arrays', () => { - const data = Buffer.from(' \n \r \t [[],\n[ 4,\t5] , [null]] \n \r \t ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual([[], [4, 5], [null]]); - }); - - test('array with strings', () => { - const data = Buffer.from('["a", ["b"], "c", ["d", "e"], [ ] ]', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(['a', ['b'], 'c', ['d', 'e'], []]); - }); -}); - -describe('object', () => { - test('empty object', () => { - const data = Buffer.from('{}', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({}); - }); - - test('empty object with whitespace', () => { - const data = Buffer.from(' { } ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({}); - }); - - test('empty object with whitespace - 2', () => { - const data = Buffer.from(' {\n} ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({}); - }); - - test('object with single key', () => { - const data = Buffer.from(' { "foo" : "bar" } ', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({foo: 'bar'}); - }); - - test('nested object', () => { - const data = Buffer.from('{"":{}}', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({'': {}}); - }); - - test('nested object', () => { - const data = Buffer.from('{"":{}}', 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual({'': {}}); - }); - - test('complex nested object', () => { - const obj = { - a: 1, - b: true, - c: null, - d: [1, 2, 3], - e: { - f: 'foo', - g: 'bar', - h: { - i: 'baz', - j: 'qux', - }, - }, - }; - const data = Buffer.from(JSON.stringify(obj), 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(obj); - }); - - test('complex nested object - 2', () => { - const obj = { - '!Cq"G_f/]j': 'pG.HEFjh', - '<3-': [285717617.40402037, '! qiH14NE', 'YCu"<>)PWv[9ot', 591097389.6547585], - 'zJ49L}1A)M]': { - 'V0`*ei?8E': { - 'C8:yy': -2807878070964447, - '$^': 855760508.2633594, - 'ew5!f{>w/B zg': 'vGS', - 'oFaFl,&F{9J9!=h': 828843580.1490843, - }, - '5|': { - '?#^5`_ABY"': ["h'mHT-\\JK\\$", 'je{O<3l(', 'q'], - 'Z|gPbq,LZB9^$].8': ['mo"Ho'], - Sl45: 796047966.3180537, - "`_pz@ADh 'iYlc5V": 1128283461473140, - }, - 'y|#.;\\QpUx8T': -53172, - 'BGk-f#QZ_!)2Tup4': 87540156.63740477, - 'H5tl@md|9(-': 411281070.2708618, - }, - 'XH>)': 718476139.1743257, - 't$@`w': { - 'jQ$1y"9': null, - ่ฏถะณ่ฅฟ่ฏถๅฟ…่ฅฟ่ฏถ่ฅฟ่ฅฟ่ฏถ่ฏถ่ฅฟ่ฅฟ: 64094888.57050705, - }, - 'OWB@6%': "'bx8Fc", - '#vxKbXgF+$mIk': 919164616.3711811, - 'x!UZa*e@Rfz': '\\', - "tyae=ID>')Z5Bu?": 721968011.7405405, - }; - const data = Buffer.from(JSON.stringify(obj), 'utf-8'); - decoder.reader.reset(data); - const value = decoder.readAny(); - expect(value).toEqual(obj); - }); -}); diff --git a/src/json-pack/json/__tests__/JsonDecoderDag.spec.ts b/src/json-pack/json/__tests__/JsonDecoderDag.spec.ts deleted file mode 100644 index a0a8f7adbc..0000000000 --- a/src/json-pack/json/__tests__/JsonDecoderDag.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {utf8} from '../../../util/buffers/strings'; -import {JsonEncoderDag} from '../JsonEncoderDag'; -import {JsonDecoderDag} from '../JsonDecoderDag'; - -const writer = new Writer(16); -const encoder = new JsonEncoderDag(writer); -const decoder = new JsonDecoderDag(); - -describe('Bytes', () => { - test('can decode a simple buffer in object', () => { - const buf = utf8`hello world`; - const data = {foo: buf}; - const encoded = encoder.encode(data); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(data); - }); - - test('can decode buffers inside an array', () => { - const data = [0, utf8``, utf8`asdf`, 1]; - const encoded = encoder.encode(data); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(data); - }); - - test('can decode buffer with whitespace surrounding literals', () => { - const json = ' { "foo" : { "/" : { "bytes" : "aGVsbG8gd29ybGQ" } } } '; - const encoded = Buffer.from(json); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual({foo: utf8`hello world`}); - }); -}); - -describe('Cid', () => { - class CID { - constructor(public readonly value: string) {} - } - - class IpfsEncoder extends JsonEncoderDag { - public writeUnknown(value: unknown): void { - if (value instanceof CID) return this.writeCid(value.value); - else super.writeUnknown(value); - } - } - - class IpfsDecoder extends JsonDecoderDag { - public readCid(cid: string): unknown { - return new CID(cid); - } - } - - const encoder = new IpfsEncoder(writer); - const decoder = new IpfsDecoder(); - - test('can decode a single CID', () => { - const data = new CID('Qm'); - const encoded = encoder.encode(data); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(data); - }); - - test('can decode a CID in object and array', () => { - const data = { - foo: 'bar', - baz: new CID('Qm'), - qux: [new CID('bu'), 'quux'], - }; - const encoded = encoder.encode(data); - const decoded = decoder.decode(encoded); - expect(decoded).toEqual(data); - }); -}); diff --git a/src/json-pack/json/__tests__/JsonEncoder.spec.ts b/src/json-pack/json/__tests__/JsonEncoder.spec.ts deleted file mode 100644 index 3c5d031b83..0000000000 --- a/src/json-pack/json/__tests__/JsonEncoder.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {JsonEncoder} from '../JsonEncoder'; - -const writer = new Writer(16); -const encoder = new JsonEncoder(writer); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - const json = Buffer.from(encoded).toString('utf-8'); - // console.log('json', json); - const decoded = JSON.parse(json); - expect(decoded).toEqual(value); -}; - -describe('null', () => { - test('null', () => { - assertEncoder(null); - }); -}); - -describe('undefined', () => { - test('undefined', () => { - const encoded = encoder.encode(undefined); - const txt = Buffer.from(encoded).toString('utf-8'); - expect(txt).toBe('"data:application/cbor,base64;9w=="'); - }); - - test('undefined in object', () => { - const encoded = encoder.encode({foo: undefined}); - const txt = Buffer.from(encoded).toString('utf-8'); - expect(txt).toBe('{"foo":"data:application/cbor,base64;9w=="}'); - }); -}); - -describe('boolean', () => { - test('true', () => { - assertEncoder(true); - }); - - test('false', () => { - assertEncoder(false); - }); -}); - -describe('number', () => { - test('integers', () => { - assertEncoder(0); - assertEncoder(1); - assertEncoder(-1); - assertEncoder(123); - assertEncoder(-123); - assertEncoder(-12321321123); - assertEncoder(+2321321123); - }); - - test('floats', () => { - assertEncoder(0.0); - assertEncoder(1.1); - assertEncoder(-1.45); - assertEncoder(123.34); - assertEncoder(-123.234); - assertEncoder(-12321.321123); - assertEncoder(+2321321.123); - }); -}); - -describe('string', () => { - test('empty string', () => { - assertEncoder(''); - }); - - test('one char strings', () => { - assertEncoder('a'); - assertEncoder('b'); - assertEncoder('z'); - assertEncoder('~'); - assertEncoder('"'); - assertEncoder('\\'); - assertEncoder('*'); - assertEncoder('@'); - assertEncoder('9'); - assertEncoder('โœ…'); - assertEncoder('๐Ÿ‘'); - }); - - test('short strings', () => { - assertEncoder('abc'); - assertEncoder('abc123'); - }); - - test('long strings', () => { - assertEncoder( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit.', - ); - }); - - test('unsafe character in the middle of a string', () => { - assertEncoder('...................".....................'); - }); - - test('unsafe character in the middle of a string - 2', () => { - assertEncoder('...................๐ŸŽ‰.....................'); - }); -}); - -describe('array', () => { - test('empty array', () => { - assertEncoder([]); - }); - - test('array with one element', () => { - assertEncoder([1]); - }); - - test('array with two elements', () => { - assertEncoder([1, 2]); - }); - - test('array of array', () => { - assertEncoder([[123]]); - }); - - test('array of various types', () => { - assertEncoder([0, 1.32, 'str', true, false, null, [1, 2, 3]]); - }); -}); - -describe('object', () => { - test('empty object', () => { - assertEncoder({}); - }); - - test('object with one key', () => { - assertEncoder({foo: 'bar'}); - }); - - test('object with two keys', () => { - assertEncoder({foo: 'bar', baz: 123}); - }); - - test('object with various nested types', () => { - assertEncoder({ - '': null, - null: false, - true: true, - str: 'asdfasdf ,asdf asdf asdf asdf asdf, asdflkasjdflakjsdflajskdlfkasdf', - num: 123, - arr: [1, 2, 3], - obj: {foo: 'bar'}, - obj2: {1: 2, 3: 4}, - }); - }); -}); - -describe('nested object', () => { - test('large array/object', () => { - assertEncoder({ - foo: [ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ], - }); - }); -}); diff --git a/src/json-pack/json/__tests__/JsonEncoderDag.spec.ts b/src/json-pack/json/__tests__/JsonEncoderDag.spec.ts deleted file mode 100644 index 27a4c8bb58..0000000000 --- a/src/json-pack/json/__tests__/JsonEncoderDag.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {utf8} from '../../../util/buffers/strings'; -import {JsonEncoderDag} from '../JsonEncoderDag'; - -const writer = new Writer(16); -const encoder = new JsonEncoderDag(writer); - -describe('object', () => { - test('shorter and smaller keys are sorted earlier', () => { - const json = '{"aaaaaa":6,"aaaaab":7,"aaaaac":8,"aaaabb":9,"bbbbb":5,"cccc":4,"ddd":3,"ee":2,"f":1}'; - const data = JSON.parse(json); - const encoded = encoder.encode(data); - const json2 = Buffer.from(encoded).toString(); - expect(json2).toBe('{"f":1,"ee":2,"ddd":3,"cccc":4,"bbbbb":5,"aaaaaa":6,"aaaaab":7,"aaaaac":8,"aaaabb":9}'); - }); -}); - -describe('Bytes', () => { - test('can encode a simple buffer in object', () => { - const buf = utf8`hello world`; - const data = {foo: buf}; - const encoded = encoder.encode(data); - const json = Buffer.from(encoded).toString(); - expect(json).toBe('{"foo":{"/":{"bytes":"aGVsbG8gd29ybGQ"}}}'); - }); - - test('can encode a simple buffer in array', () => { - const buf = utf8`hello world`; - const data = [0, buf, 1]; - const encoded = encoder.encode(data); - const json = Buffer.from(encoded).toString(); - expect(json).toBe('[0,{"/":{"bytes":"aGVsbG8gd29ybGQ"}},1]'); - }); -}); - -describe('Cid', () => { - class CID { - constructor(public readonly value: string) {} - } - - class IpfsEncoder extends JsonEncoderDag { - public writeUnknown(value: unknown): void { - if (value instanceof CID) return this.writeCid(value.value); - else super.writeUnknown(value); - } - } - - const encoder = new IpfsEncoder(writer); - - test('can encode a CID as object key', () => { - const data = {id: new CID('QmXn5v3z')}; - const encoded = encoder.encode(data); - const json = Buffer.from(encoded).toString(); - expect(json).toBe('{"id":{"/":"QmXn5v3z"}}'); - }); - - test('can encode a CID in array', () => { - const data = ['a', new CID('b'), 'c']; - const encoded = encoder.encode(data); - const json = Buffer.from(encoded).toString(); - expect(json).toBe('["a",{"/":"b"},"c"]'); - }); -}); diff --git a/src/json-pack/json/__tests__/automated.spec.ts b/src/json-pack/json/__tests__/automated.spec.ts deleted file mode 100644 index 534c942243..0000000000 --- a/src/json-pack/json/__tests__/automated.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {JsonEncoder} from '../JsonEncoder'; -import {JsonEncoderStable} from '../JsonEncoderStable'; -import {JsonDecoder} from '../JsonDecoder'; -import {documents} from '../../../__tests__/json-documents'; -import {binaryDocuments} from '../../../__tests__/binary-documents'; - -const writer = new Writer(8); -const encoder = new JsonEncoder(writer); -const encoderStable = new JsonEncoderStable(writer); -const decoder = new JsonDecoder(); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - const encoded2 = encoderStable.encode(value); - // const json = Buffer.from(encoded).toString('utf-8'); - // console.log('json', json); - const decoded = decoder.decode(encoded); - const decoded2 = decoder.decode(encoded2); - expect(decoded).toEqual(value); - expect(decoded2).toEqual(value); -}; - -describe('Sample JSON documents', () => { - for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - assertEncoder(t.json as any); - }); - } -}); - -describe('Sample binary documents', () => { - for (const t of binaryDocuments) { - (t.only ? test.only : test)(t.name, () => { - assertEncoder(t.json as any); - }); - } -}); diff --git a/src/json-pack/json/__tests__/fuzzer.spec.ts b/src/json-pack/json/__tests__/fuzzer.spec.ts deleted file mode 100644 index e4b7661c0a..0000000000 --- a/src/json-pack/json/__tests__/fuzzer.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {RandomJson} from '../../../json-random'; -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {JsonDecoder} from '../JsonDecoder'; -import {JsonEncoder} from '../JsonEncoder'; - -const writer = new Writer(1); -const encoder = new JsonEncoder(writer); -const decoder = new JsonDecoder(); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - const json = Buffer.from(encoded).toString('utf-8'); - try { - decoder.reader.reset(encoded); - const decoded = decoder.readAny(); - // console.log('decoded', decoded); - expect(decoded).toEqual(value); - } catch (error) { - /* tslint:disable no-console */ - console.log('value', value); - console.log('JSON.stringify', JSON.stringify(value)); - console.log('JsonEncoder', json); - /* tslint:enable no-console */ - throw error; - } -}; - -test('fuzzing', () => { - for (let i = 0; i < 1000; i++) { - const json = RandomJson.generate(); - assertEncoder(json); - } -}, 50000); diff --git a/src/json-pack/json/__tests__/memory-leaks.spec.ts b/src/json-pack/json/__tests__/memory-leaks.spec.ts deleted file mode 100644 index 525cc350f9..0000000000 --- a/src/json-pack/json/__tests__/memory-leaks.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {JsonEncoder} from '../JsonEncoder'; -import {parse} from '../../../json-binary'; -import largeJson from '../../../__bench__/data/json-very-large-object'; - -const writer = new Writer(1024 * 64); -const encoder = new JsonEncoder(writer); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - const json = Buffer.from(encoded).toString('utf-8'); - // console.log('json', json); - const decoded = parse(json); - expect(decoded).toEqual(value); -}; - -describe('should keep writing buffer memory within limits', () => { - test('long string', () => { - for (let i = 0; i < 1000; i++) { - const value = { - foo: 'a'.repeat(Math.round(32000 * Math.random()) + 10), - }; - assertEncoder(value); - // console.log(writer.uint8.length); - expect(writer.uint8.length).toBeLessThan(1024 * 64 * 5 * 5); - } - }); - - test('large object', () => { - for (let i = 0; i < 100; i++) { - encoder.encode(largeJson); - // console.log(writer.uint8.length); - expect(writer.uint8.length).toBeLessThan(1024 * 64 * 5 * 5); - } - }); -}); diff --git a/src/json-pack/json/types.ts b/src/json-pack/json/types.ts deleted file mode 100644 index 109a72b60b..0000000000 --- a/src/json-pack/json/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type JsonUint8Array = Uint8Array & {__BRAND__: 'json'; __TYPE__: T}; diff --git a/src/json-pack/json/util.ts b/src/json-pack/json/util.ts deleted file mode 100644 index f708118e88..0000000000 --- a/src/json-pack/json/util.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const findEndingQuote = (uint8: Uint8Array, x: number): number => { - const len = uint8.length; - let char = uint8[x]; - let prev = 0; - while (x < len) { - if (char === 34 && prev !== 92) break; - if (char === 92 && prev === 92) prev = 0; - else prev = char; - char = uint8[++x]; - } - if (x === len) throw new Error('Invalid JSON'); - return x; -}; diff --git a/src/json-pack/msgpack/MsgPackDecoder.ts b/src/json-pack/msgpack/MsgPackDecoder.ts deleted file mode 100644 index 7da9929bed..0000000000 --- a/src/json-pack/msgpack/MsgPackDecoder.ts +++ /dev/null @@ -1,256 +0,0 @@ -import {JsonPackValue} from '.'; -import {MsgPackDecoderFast} from './MsgPackDecoderFast'; -import type {Path} from '../../json-pointer'; -import type {Reader} from '../../util/buffers/Reader'; - -/** - * @category Decoder - */ -export class MsgPackDecoder extends MsgPackDecoderFast { - // ---------------------------------------------------------- Skipping values - - /** - * Skips a whole JSON value and returns back the number of bytes - * that value consumed. - */ - public skipAny(): number { - const byte = this.reader.u8(); - if (byte >= 0xe0) return 1; // 0xe0 - if (byte <= 0xbf) { - if (byte < 0x90) { - if (byte <= 0b1111111) return 1; // 0x7f - return 1 + this.skipObj(byte & 0b1111); // 0x80, obj(1) - } else { - if (byte < 0xa0) return 1 + this.skipArr(byte & 0b1111); - // 0x90 - else return 1 + this.skip(byte & 0b11111); // 0xa0, str(1) - } - } - if (byte <= 0xd0) { - if (byte <= 0xc8) { - if (byte <= 0xc4) { - if (byte <= 0xc2) return byte === 0xc2 ? 1 : 1; - else return byte === 0xc4 ? 2 + this.skip(this.reader.u8()) : 1; - } else { - if (byte <= 0xc6) return byte === 0xc6 ? 5 + this.skip(this.reader.u32()) : 3 + this.skip(this.reader.u16()); - else return byte === 0xc8 ? 4 + this.skip(this.reader.u16()) : 3 + this.skip(this.reader.u8()); - } - } else { - return byte <= 0xcc - ? byte <= 0xca - ? byte === 0xca - ? 1 + this.skip(4) // f32 - : 1 + 1 + 4 + this.skip(this.reader.u32()) // ext32 - : byte === 0xcc - ? 1 + this.skip(1) // u8 - : 1 + this.skip(8) // f64 - : byte <= 0xce - ? byte === 0xce - ? 1 + this.skip(4) // u32 - : 1 + this.skip(2) // u16 - : byte === 0xd0 - ? 1 + this.skip(1) // i8 - : 1 + this.skip(8); // u64 - } - } else if (byte <= 0xd8) { - return byte <= 0xd4 - ? byte <= 0xd2 - ? byte === 0xd2 - ? 1 + this.skip(4) // i32 - : 1 + this.skip(2) // i16 - : byte === 0xd4 - ? 1 + this.skip(2) // ext1 - : 1 + this.skip(8) // i64 - : byte <= 0xd6 - ? byte === 0xd6 - ? 1 + this.skip(5) // ext4 - : 1 + this.skip(3) // ext2 - : byte === 0xd8 - ? 1 + this.skip(17) // ext16 - : 1 + this.skip(9); // ext8 - } else { - switch (byte) { - case 0xd9: - return 2 + this.skip(this.reader.u8()); // str8 - case 0xda: - return 3 + this.skip(this.reader.u16()); // str16 - case 0xdb: - return 5 + this.skip(this.reader.u32()); // str32 - case 0xdc: - return 3 + this.skipArr(this.reader.u16()); - case 0xdd: - return 5 + this.skipArr(this.reader.u32()); - case 0xde: - return 3 + this.skipObj(this.reader.u16()); - case 0xdf: - return 5 + this.skipObj(this.reader.u32()); - } - } - return 1; - } - - /** @ignore */ - protected skipArr(size: number): number { - let length = 0; - for (let i = 0; i < size; i++) length += this.skipAny(); - return length; - } - - /** @ignore */ - protected skipObj(size: number): number { - let length = 0; - for (let i = 0; i < size; i++) { - length += this.skipAny() + this.skipAny(); - } - return length; - } - - // -------------------------------------------------------- One level reading - - public readLevel(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.valOneLevel(); - } - - protected valOneLevel(): unknown { - const byte = this.reader.view.getUint8(this.reader.x); - const isMap = byte === 0xde || byte === 0xdf || byte >> 4 === 0b1000; - if (isMap) { - this.reader.x++; - const size = byte === 0xde ? this.reader.u16() : byte === 0xdf ? this.reader.u32() : byte & 0b1111; - const obj: Record = {}; - for (let i = 0; i < size; i++) { - const key = this.key(); - obj[key] = this.primitive(); - } - return obj; - } - const isArray = byte === 0xdc || byte === 0xdd || byte >> 4 === 0b1001; - if (isArray) { - this.reader.x++; - const size = byte === 0xdc ? this.reader.u16() : byte === 0xdd ? this.reader.u32() : byte & 0b1111; - const arr: unknown[] = []; - for (let i = 0; i < size; i++) arr.push(this.primitive()); - return arr; - } - return this.val(); - } - - /** - * @ignore - * @returns Returns a primitive value or {@link JsonPackValue} object, if the value - * is a "map" or an "arr". - */ - protected primitive(): unknown { - const reader = this.reader; - const byte = reader.view.getUint8(reader.x); - const isMapOrArray = byte === 0xde || byte === 0xdf || byte === 0xdc || byte === 0xdd || byte >> 5 === 0b100; - if (isMapOrArray) { - const length = this.skipAny(); - reader.x -= length; - const buf = reader.buf(length); - return new JsonPackValue(buf); - } - return this.val(); - } - - protected skip(length: number): number { - this.reader.x += length; - return length; - } - - // --------------------------------------------------------------- Validation - - /** - * Throws if at given offset in a buffer there is an invalid MessagePack - * value, or if the value does not span the exact length specified in `size`. - * I.e. throws if: - * - * - The value is not a valid MessagePack value. - * - The value is shorter than `size`. - * - The value is longer than `size`. - * - * @param value Buffer in which to validate MessagePack value. - * @param offset Offset at which the value starts. - * @param size Expected size of the value. - */ - public validate(value: Uint8Array, offset: number = 0, size: number = value.length): void { - this.reader.reset(value); - this.reader.x = offset; - const start = offset; - this.skipAny(); - const end = this.reader.x; - if (end - start !== size) throw new Error('INVALID_SIZE'); - } - - // ---------------------------------------------------------- Shallow reading - - public readObjHdr(): number { - const reader = this.reader; - const byte = reader.u8(); - const isFixMap = byte >> 4 === 0b1000; - if (isFixMap) return byte & 0b1111; - switch (byte) { - case 0xde: - return reader.u16(); - case 0xdf: - return reader.u32(); - } - throw new Error('NOT_OBJ'); - } - - public readStrHdr(): number { - const reader = this.reader; - const byte = reader.u8(); - if (byte >> 5 === 0b101) return byte & 0b11111; - switch (byte) { - case 0xd9: - return reader.u8(); - case 0xda: - return reader.u16(); - case 0xdb: - return reader.u32(); - } - throw new Error('NOT_STR'); - } - - public findKey(key: string): this { - const size = this.readObjHdr(); - for (let i = 0; i < size; i++) { - const k = this.key(); - if (k === key) return this; - this.skipAny(); - } - throw new Error('KEY_NOT_FOUND'); - } - - public readArrHdr(): number { - const reader = this.reader; - const byte = reader.u8(); - const isFixArr = byte >> 4 === 0b1001; - if (isFixArr) return byte & 0b1111; - switch (byte) { - case 0xdc: - return this.reader.u16(); - case 0xdd: - return this.reader.u32(); - } - throw new Error('NOT_ARR'); - } - - public findIndex(index: number): this { - const size = this.readArrHdr(); - if (index >= size) throw new Error('INDEX_OUT_OF_BOUNDS'); - for (let i = 0; i < index; i++) this.skipAny(); - return this; - } - - public find(path: Path): this { - for (let i = 0; i < path.length; i++) { - const segment = path[i]; - if (typeof segment === 'string') this.findKey(segment); - else this.findIndex(segment); - } - return this; - } -} diff --git a/src/json-pack/msgpack/MsgPackDecoderFast.ts b/src/json-pack/msgpack/MsgPackDecoderFast.ts deleted file mode 100644 index 140851da89..0000000000 --- a/src/json-pack/msgpack/MsgPackDecoderFast.ts +++ /dev/null @@ -1,181 +0,0 @@ -import {JsonPackExtension} from '../JsonPackExtension'; -import {Reader} from '../../util/buffers/Reader'; -import {ERROR} from '../cbor/constants'; -import sharedCachedUtf8Decoder from '../../util/buffers/utf8/sharedCachedUtf8Decoder'; -import type {BinaryJsonDecoder, PackValue} from '../types'; -import type {CachedUtf8Decoder} from '../../util/buffers/utf8/CachedUtf8Decoder'; - -/** - * @category Decoder - */ -export class MsgPackDecoderFast implements BinaryJsonDecoder { - public constructor( - public reader: R = new Reader() as R, - protected readonly keyDecoder: CachedUtf8Decoder = sharedCachedUtf8Decoder, - ) {} - - /** @deprecated */ - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.val(); - } - - public read(uint8: Uint8Array): PackValue { - this.reader.reset(uint8); - return this.val() as PackValue; - } - - public val(): unknown { - const reader = this.reader; - const byte = reader.u8(); - if (byte >= 0xe0) return byte - 0x100; // 0xe0 - if (byte <= 0xbf) { - if (byte < 0x90) { - if (byte <= 0b1111111) return byte; // 0x7f - return this.obj(byte & 0b1111); // 0x80 - } else { - if (byte < 0xa0) return this.arr(byte & 0b1111); - // 0x90 - else return reader.utf8(byte & 0b11111); // 0xa0 - } - } - if (byte <= 0xd0) { - if (byte <= 0xc8) { - if (byte <= 0xc4) { - if (byte <= 0xc2) return byte === 0xc2 ? false : byte === 0xc0 ? null : undefined; - else return byte === 0xc4 ? reader.buf(reader.u8()) : true; - } else { - if (byte <= 0xc6) return byte === 0xc6 ? reader.buf(reader.u32()) : reader.buf(reader.u16()); - else return byte === 0xc8 ? this.ext(reader.u16()) : this.ext(reader.u8()); - } - } else { - return byte <= 0xcc - ? byte <= 0xca - ? byte === 0xca - ? reader.f32() - : this.ext(reader.u32()) - : byte === 0xcc - ? reader.u8() - : reader.f64() - : byte <= 0xce - ? byte === 0xce - ? reader.u32() - : reader.u16() - : byte === 0xd0 - ? reader.i8() - : reader.u32() * 4294967296 + reader.u32(); - } - } else if (byte <= 0xd8) { - return byte <= 0xd4 - ? byte <= 0xd2 - ? byte === 0xd2 - ? reader.i32() - : reader.i16() - : byte === 0xd4 - ? this.ext(1) - : reader.i32() * 4294967296 + reader.u32() - : byte <= 0xd6 - ? byte === 0xd6 - ? this.ext(4) - : this.ext(2) - : byte === 0xd8 - ? this.ext(16) - : this.ext(8); - } else { - switch (byte) { - case 0xd9: - return reader.utf8(reader.u8()); - case 0xda: - return reader.utf8(reader.u16()); - case 0xdb: - return reader.utf8(reader.u32()); - case 0xdc: - return this.arr(reader.u16()); - case 0xdd: - return this.arr(reader.u32()); - case 0xde: - return this.obj(reader.u16()); - case 0xdf: - return this.obj(reader.u32()); - } - } - return undefined; - } - - public str(): unknown { - const reader = this.reader; - const byte = reader.u8(); - if (byte >> 5 === 0b101) return reader.utf8(byte & 0b11111); - switch (byte) { - case 0xd9: - return reader.utf8(reader.u8()); - case 0xda: - return reader.utf8(reader.u16()); - case 0xdb: - return reader.utf8(reader.u32()); - } - return undefined; - } - - /** @ignore */ - protected obj(size: number): object { - const obj: Record = {}; - for (let i = 0; i < size; i++) { - const key = this.key(); - if (key === '__proto__') throw ERROR.UNEXPECTED_OBJ_KEY; - obj[key] = this.val(); - } - return obj; - } - - /** @ignore */ - protected key(): string { - const reader = this.reader; - const byte = reader.view.getUint8(reader.x); - if (byte >= 0b10100000 && byte <= 0b10111111) { - const size = byte & 0b11111; - const key = this.keyDecoder.decode(reader.uint8, reader.x + 1, size); - reader.x += 1 + size; - return key; - } else if (byte === 0xd9) { - const size = reader.view.getUint8(reader.x + 1); - if (size < 32) { - const key = this.keyDecoder.decode(reader.uint8, reader.x + 2, size); - reader.x += 2 + size; - return key; - } - } - reader.x++; - switch (byte) { - case 0xd9: - return reader.utf8(reader.u8()); - case 0xda: - return reader.utf8(reader.u16()); - case 0xdb: - return reader.utf8(reader.u32()); - default: - return ''; - } - } - - /** @ignore */ - protected arr(size: number): unknown[] { - const arr: unknown[] = []; - for (let i = 0; i < size; i++) arr.push(this.val()); - return arr; - } - - /** @ignore */ - protected ext(size: number): JsonPackExtension { - const reader = this.reader; - const type = reader.u8(); - const end = reader.x + size; - const buf = reader.uint8.subarray(reader.x, end); - reader.x = end; - return new JsonPackExtension(type, buf); - } - - protected back(bytes: number) { - this.reader.x -= bytes; - } -} diff --git a/src/json-pack/msgpack/MsgPackEncoder.ts b/src/json-pack/msgpack/MsgPackEncoder.ts deleted file mode 100644 index 84320b529f..0000000000 --- a/src/json-pack/msgpack/MsgPackEncoder.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {MsgPackEncoderFast} from './MsgPackEncoderFast'; -import {isUint8Array} from '../../util/buffers/isUint8Array'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {JsonPackValue} from '../JsonPackValue'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import {MSGPACK} from './constants'; - -/** - * @category Encoder - */ -export class MsgPackEncoder< - W extends IWriter & IWriterGrowable = IWriter & IWriterGrowable, -> extends MsgPackEncoderFast { - public writeAny(value: unknown): void { - switch (value) { - case null: - return this.writer.u8(MSGPACK.NULL); - case false: - return this.writer.u8(MSGPACK.FALSE); - case true: - return this.writer.u8(MSGPACK.TRUE); - } - if (value instanceof Array) return this.encodeArray(value); - switch (typeof value) { - case 'number': - return this.encodeNumber(value); - case 'string': - return this.encodeString(value); - case 'object': { - if (value instanceof JsonPackValue) return this.writer.buf(value.val, value.val.length); - if (value instanceof JsonPackExtension) return this.encodeExt(value); - if (isUint8Array(value)) return this.encodeBinary(value); - return this.encodeObject(value as Record); - } - case 'undefined': - return this.writer.u8(MSGPACK.UNDEFINED); - } - } -} diff --git a/src/json-pack/msgpack/MsgPackEncoderFast.ts b/src/json-pack/msgpack/MsgPackEncoderFast.ts deleted file mode 100644 index 075e9f4798..0000000000 --- a/src/json-pack/msgpack/MsgPackEncoderFast.ts +++ /dev/null @@ -1,324 +0,0 @@ -import {IWriter, IWriterGrowable} from '../../util/buffers'; -import {Writer} from '../../util/buffers/Writer'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {BinaryJsonEncoder, TlvBinaryJsonEncoder} from '../types'; -import {IMessagePackEncoder} from './types'; - -/** - * @category Encoder - */ -export class MsgPackEncoderFast - implements IMessagePackEncoder, BinaryJsonEncoder, TlvBinaryJsonEncoder -{ - constructor(public readonly writer: W = new Writer() as any) {} - - /** - * Use this method to encode a JavaScript document into MessagePack format. - * - * @param json JSON value to encode. - * @returns Encoded memory buffer with MessagePack contents. - */ - public encode(json: unknown): Uint8Array { - this.writer.reset(); - this.writeAny(json); - return this.writer.flush(); - } - - /** @deprecated */ - public encodeAny(json: unknown): void { - this.writeAny(json); - } - - public writeAny(value: unknown): void { - switch (value) { - case null: - return this.writer.u8(0xc0); - case false: - return this.writer.u8(0xc2); - case true: - return this.writer.u8(0xc3); - } - if (value instanceof Array) return this.writeArr(value); - switch (typeof value) { - case 'number': - return this.writeNumber(value); - case 'string': - return this.writeStr(value); - case 'object': - return this.writeObj(value as Record); - } - } - - /** @deprecated */ - protected encodeFloat64(num: number): void { - this.writeFloat(num); - } - - public writeNull(): void { - return this.writer.u8(0xc0); - } - - public writeFloat(float: number): void { - this.writer.u8f64(0xcb, float); - } - - public u32(num: number): void { - const writer = this.writer; - this.writer.ensureCapacity(5); - const uint8 = writer.uint8; - if (num <= 0b1111111) { - uint8[writer.x++] = num; - // Commenting this out improves performance, there is not much space savings. - // } else if (num <= 0xff) { - // uint8[writer.x++] = 0xcc; - // uint8[writer.x++] = num; - } else if (num <= 0xffff) { - uint8[writer.x++] = 0xcd; - writer.view.setUint16(writer.x, num); - writer.x += 2; - } else if (num <= 0xffffffff) { - uint8[writer.x++] = 0xce; - writer.view.setUint32(writer.x, num); - writer.x += 4; - } else this.writeFloat(num); - } - - public n32(num: number): void { - const writer = this.writer; - this.writer.ensureCapacity(5); - const uint8 = writer.uint8; - if (num >= -0x20) { - uint8[writer.x++] = 0x100 + num; - // Commenting this out improves performance, there is not much space savings. - // } else if (num >= -0x80) { - // uint8[writer.x++] = 0xd0; - // uint8[writer.x++] = num + 0x100; - } else if (num >= -0x8000) { - uint8[writer.x++] = 0xd1; - writer.view.setInt16(writer.x, num); - writer.x += 2; - } else if (num >= -0x80000000) { - uint8[writer.x++] = 0xd2; - writer.view.setInt32(writer.x, num); - writer.x += 4; - } else this.writeFloat(num); - } - - /** @deprecated */ - public encodeNumber(num: number): void { - this.writeNumber(num); - } - - public writeNumber(num: number): void { - if (num >>> 0 === num) return this.u32(num); - if (num >> 0 === num) return this.n32(num); - this.writeFloat(num); - } - - public writeInteger(int: number): void { - if (int >= 0) - if (int <= 0xffffffff) return this.u32(int); - else if (int > -0x80000000) return this.n32(int); - this.writeFloat(int); - } - - public writeUInteger(uint: number): void { - if (uint <= 0xffffffff) return this.u32(uint); - this.writeFloat(uint); - } - - public encodeNull(): void { - this.writer.u8(0xc0); - } - - public encodeTrue(): void { - this.writer.u8(0xc3); - } - - public encodeFalse(): void { - this.writer.u8(0xc2); - } - - /** @deprecated */ - public encodeBoolean(bool: boolean): void { - this.writeBoolean(bool); - } - - public writeBoolean(bool: boolean): void { - if (bool) this.writer.u8(0xc3); - else this.writer.u8(0xc2); - } - - /** @deprecated */ - public encodeStringHeader(length: number): void { - this.writeStrHdr(length); - } - - public writeStrHdr(length: number): void { - if (length <= 0b11111) this.writer.u8(0b10100000 | length); - else if (length <= 0xff) this.writer.u16(0xd900 + length); - else if (length <= 0xffff) this.writer.u8u16(0xda, length); - else this.writer.u8u32(0xdb, length); - } - - /** @deprecated */ - public encodeString(str: string) { - this.writeStr(str); - } - - public writeStr(str: string): void { - const writer = this.writer; - const length = str.length; - const maxSize = length * 4; - writer.ensureCapacity(5 + maxSize); - const uint8 = writer.uint8; - let lengthOffset: number = writer.x; - if (maxSize <= 0b11111) writer.x++; - else if (maxSize <= 0xff) { - uint8[writer.x++] = 0xd9; - lengthOffset = writer.x; - writer.x++; - } else if (maxSize <= 0xffff) { - uint8[writer.x++] = 0xda; - lengthOffset = writer.x; - writer.x += 2; - } else { - uint8[writer.x++] = 0xdb; - lengthOffset = writer.x; - writer.x += 4; - } - const bytesWritten = this.writer.utf8(str); - if (maxSize <= 0b11111) uint8[lengthOffset] = 0b10100000 | bytesWritten; - else if (maxSize <= 0xff) uint8[lengthOffset] = bytesWritten; - else if (maxSize <= 0xffff) writer.view.setUint16(lengthOffset, bytesWritten); - else writer.view.setUint32(lengthOffset, bytesWritten); - } - - /** @deprecated */ - public encodeAsciiString(str: string) { - this.writeAsciiStr(str); - } - - public writeAsciiStr(str: string): void { - this.writeStrHdr(str.length); - this.writer.ascii(str); - } - - /** @deprecated */ - public encodeArrayHeader(length: number): void { - this.writeArrHdr(length); - } - - /** @deprecated */ - public encodeArray(arr: unknown[]): void { - this.writeArr(arr); - } - - public writeArrHdr(length: number): void { - if (length <= 0b1111) this.writer.u8(0b10010000 | length); - else if (length <= 0xffff) this.writer.u8u16(0xdc, length); - else if (length <= 0xffffffff) this.writer.u8u32(0xdd, length); - } - - public writeArr(arr: unknown[]): void { - const length = arr.length; - if (length <= 0b1111) this.writer.u8(0b10010000 | length); - else if (length <= 0xffff) this.writer.u8u16(0xdc, length); - else if (length <= 0xffffffff) this.writer.u8u32(0xdd, length); - // else return; - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - } - - /** @deprecated */ - public encodeObjectHeader(length: number): void { - this.writeObjHdr(length); - } - - /** @deprecated */ - public encodeObject(obj: Record): void { - this.writeObj(obj); - } - - public writeObjHdr(length: number): void { - if (length <= 0b1111) this.writer.u8(0b10000000 | length); - else if (length <= 0xffff) { - this.writer.u8u16(0xde, length); - } else if (length <= 0xffffffff) { - this.writer.u8u32(0xdf, length); - } - } - - public writeObj(obj: Record): void { - const keys = Object.keys(obj); - const length = keys.length; - this.writeObjHdr(length); - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } - - public encodeExtHeader(type: number, length: number) { - switch (length) { - case 1: - this.writer.u16((0xd4 << 8) | type); - break; - case 2: - this.writer.u16((0xd5 << 8) | type); - break; - case 4: - this.writer.u16((0xd6 << 8) | type); - break; - case 8: - this.writer.u16((0xd7 << 8) | type); - break; - case 16: - this.writer.u16((0xd8 << 8) | type); - break; - default: - if (length <= 0xff) { - this.writer.u16((0xc7 << 8) | length); - this.writer.u8(type); - } else if (length <= 0xffff) { - this.writer.u8u16(0xc8, length); - this.writer.u8(type); - } else if (length <= 0xffffffff) { - this.writer.u8u32(0xc9, length); - this.writer.u8(type); - } - } - } - - public encodeExt(ext: JsonPackExtension): void { - const {tag: type, val: buf} = ext; - const length = buf.length; - this.encodeExtHeader(type, length); - this.writer.buf(buf, length); - } - - /** @deprecated */ - public encodeBinaryHeader(length: number): void { - this.writeBinHdr(length); - } - - /** @deprecated */ - public encodeBinary(buf: Uint8Array): void { - this.writeBin(buf); - } - - public writeBinHdr(length: number): void { - if (length <= 0xff) this.writer.u16((0xc4 << 8) | length); - else if (length <= 0xffff) { - this.writer.u8u16(0xc5, length); - } else if (length <= 0xffffffff) { - this.writer.u8u32(0xc6, length); - } - } - - public writeBin(buf: Uint8Array): void { - const length = buf.length; - this.writeBinHdr(length); - this.writer.buf(buf, length); - } -} diff --git a/src/json-pack/msgpack/MsgPackEncoderStable.ts b/src/json-pack/msgpack/MsgPackEncoderStable.ts deleted file mode 100644 index 7b66a3051e..0000000000 --- a/src/json-pack/msgpack/MsgPackEncoderStable.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {sort} from '../../util/sort/insertion'; -import {MsgPackEncoderFast} from './MsgPackEncoderFast'; - -/** - * @category Encoder - */ -export class MsgPackEncoderStable extends MsgPackEncoderFast { - public writeObj(obj: Record): void { - const keys = sort(Object.keys(obj)); - const length = keys.length; - this.writeObjHdr(length); - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } -} diff --git a/src/json-pack/msgpack/MsgPackToJsonConverter.ts b/src/json-pack/msgpack/MsgPackToJsonConverter.ts deleted file mode 100644 index fa60d6edc3..0000000000 --- a/src/json-pack/msgpack/MsgPackToJsonConverter.ts +++ /dev/null @@ -1,241 +0,0 @@ -import {json_string} from '../../json-brand'; -import {asString} from '../../util/strings/asString'; -import {toDataUri} from '../../util/buffers/toDataUri'; - -/** - * @category Decoder - */ -export class MsgPackToJsonConverter { - /** @ignore */ - protected uint8 = new Uint8Array([]); - /** @ignore */ - protected view = new DataView(this.uint8.buffer); - /** @ignore */ - protected x = 0; - - public constructor() {} - - public reset(uint8: Uint8Array): void { - this.x = 0; - this.uint8 = uint8; - this.view = new DataView(uint8.buffer, uint8.byteOffset, uint8.length); - } - - /** - * Converts a MessagePack blob directly to JSON string. - * - * @param uint8 Binary data with MessagePack encoded value. - * @returns JSON string. - */ - public convert(uint8: Uint8Array): json_string { - this.reset(uint8); - return this.val() as json_string; - } - - /** @ignore */ - protected val(): string { - const byte = this.u8(); - if (byte >= 0xe0) return (byte - 0x100).toString(); // 0xe0 - if (byte <= 0xbf) { - if (byte < 0x90) { - if (byte <= 0b1111111) return byte.toString(); // 0x7f - return this.obj(byte & 0b1111); // 0x80 - } else { - if (byte < 0xa0) return this.arr(byte & 0b1111); - // 0x90 - else return this.str(byte & 0b11111); // 0xa0 - } - } - if (byte <= 0xd0) { - if (byte <= 0xc8) { - if (byte <= 0xc4) { - if (byte <= 0xc2) return byte === 0xc2 ? 'false' : 'null'; - else return byte === 0xc4 ? this.bin(this.u8()) : 'true'; - } else { - if (byte <= 0xc6) return byte === 0xc6 ? this.bin(this.u32()) : this.bin(this.u16()); - else return byte === 0xc8 ? this.ext(this.u16()) : this.ext(this.u8()); - } - } else { - return byte <= 0xcc - ? byte <= 0xca - ? byte === 0xca - ? this.f32().toString() - : this.ext(this.u32()) - : byte === 0xcc - ? this.u8().toString() - : this.f64().toString() - : byte <= 0xce - ? byte === 0xce - ? this.u32().toString() - : this.u16().toString() - : byte === 0xd0 - ? this.i8().toString() - : (this.u32() * 4294967296 + this.u32()).toString(); - } - } else if (byte <= 0xd8) { - return byte <= 0xd4 - ? byte <= 0xd2 - ? byte === 0xd2 - ? this.i32().toString() - : this.i16().toString() - : byte === 0xd4 - ? this.ext(1) - : (this.i32() * 4294967296 + this.i32()).toString() - : byte <= 0xd6 - ? byte === 0xd6 - ? this.ext(4) - : this.ext(2) - : byte === 0xd8 - ? this.ext(16) - : this.ext(8); - } else { - switch (byte) { - case 0xd9: - return this.str(this.u8()); - case 0xda: - return this.str(this.u16()); - case 0xdb: - return this.str(this.u32()); - case 0xdc: - return this.arr(this.u16()); - case 0xdd: - return this.arr(this.u32()); - case 0xde: - return this.obj(this.u16()); - case 0xdf: - return this.obj(this.u32()); - } - } - return ''; - } - - /** @ignore */ - protected str(size: number): string { - const uint8 = this.uint8; - const end = this.x + size; - let x = this.x; - let str = ''; - while (x < end) { - const b1 = uint8[x++]!; - if ((b1 & 0x80) === 0) { - str += String.fromCharCode(b1); - continue; - } else if ((b1 & 0xe0) === 0xc0) { - str += String.fromCharCode(((b1 & 0x1f) << 6) | (uint8[x++]! & 0x3f)); - } else if ((b1 & 0xf0) === 0xe0) { - str += String.fromCharCode(((b1 & 0x1f) << 12) | ((uint8[x++]! & 0x3f) << 6) | (uint8[x++]! & 0x3f)); - } else if ((b1 & 0xf8) === 0xf0) { - const b2 = uint8[x++]! & 0x3f; - const b3 = uint8[x++]! & 0x3f; - const b4 = uint8[x++]! & 0x3f; - let code = ((b1 & 0x07) << 0x12) | (b2 << 0x0c) | (b3 << 0x06) | b4; - if (code > 0xffff) { - code -= 0x10000; - str += String.fromCharCode(((code >>> 10) & 0x3ff) | 0xd800); - code = 0xdc00 | (code & 0x3ff); - } - str += String.fromCharCode(code); - } else { - str += String.fromCharCode(b1); - } - } - this.x = end; - return asString(str); - } - - /** @ignore */ - protected obj(size: number): json_string { - let str = '{'; - for (let i = 0; i < size; i++) { - if (i > 0) str += ','; - str += this.key(); - str += ':'; - str += this.val(); - } - return (str + '}') as json_string; - } - - /** @ignore */ - protected key(): json_string { - return this.val() as json_string; - } - - /** @ignore */ - protected arr(size: number): json_string { - let str = '['; - for (let i = 0; i < size; i++) { - if (i > 0) str += ','; - str += this.val(); - } - return (str + ']') as json_string; - } - - /** @ignore */ - protected bin(size: number): string { - const end = this.x + size; - const buf = this.uint8.subarray(this.x, end); - this.x = end; - return '"' + toDataUri(buf) + '"'; - } - - /** @ignore */ - protected ext(size: number): string { - const ext = this.u8(); - const end = this.x + size; - const buf = this.uint8.subarray(this.x, end); - this.x = end; - return '"' + toDataUri(buf, {ext}) + '"'; - } - - /** @ignore */ - protected u8(): number { - return this.view.getUint8(this.x++); - } - - /** @ignore */ - protected u16(): number { - const num = this.view.getUint16(this.x); - this.x += 2; - return num; - } - - /** @ignore */ - protected u32(): number { - const num = this.view.getUint32(this.x); - this.x += 4; - return num; - } - - /** @ignore */ - protected i8(): number { - return this.view.getInt8(this.x++); - } - - /** @ignore */ - protected i16(): number { - const num = this.view.getInt16(this.x); - this.x += 2; - return num; - } - - /** @ignore */ - protected i32(): number { - const num = this.view.getInt32(this.x); - this.x += 4; - return num; - } - - /** @ignore */ - protected f32(): number { - const pos = this.x; - this.x += 4; - return this.view.getFloat32(pos); - } - - /** @ignore */ - protected f64(): number { - const pos = this.x; - this.x += 8; - return this.view.getFloat64(pos); - } -} diff --git a/src/json-pack/msgpack/README.md b/src/json-pack/msgpack/README.md deleted file mode 100644 index 6845052622..0000000000 --- a/src/json-pack/msgpack/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# `json-pack` MessagePack Codec - -Fast and lean implementation of [MessagePack](https://github.com/msgpack/msgpack/blob/master/spec.md) codec. - -- Fastest implementation of MessagePack in JavaScript. -- Small footprint, small bundle size. -- Works in Node.js and browser. -- Supports binary fields. -- Supports extensions. -- Supports precomputed MessagePack values. - - -## Benchmarks - -Faster than built-in `JSON.stringify` and `JSON.parse`, and any other library. - -Encoding a 854 byte JSON object: - -``` -node benchmarks/json-pack.js -Size: 854 -json-joy/json-pack (v4) x 372,149 ops/sec ยฑ0.63% (96 runs sampled), 2687 ns/op -json-joy/json-pack (v3) x 273,234 ops/sec ยฑ0.74% (95 runs sampled), 3660 ns/op -json-joy/json-pack (v2) x 329,977 ops/sec ยฑ0.48% (95 runs sampled), 3031 ns/op -JSON.stringify x 303,455 ops/sec ยฑ0.94% (97 runs sampled), 3295 ns/op -@msgpack/msgpack x 211,446 ops/sec ยฑ0.75% (90 runs sampled), 4729 ns/op -msgpack-lite x 106,048 ops/sec ยฑ2.80% (90 runs sampled), 9430 ns/op -msgpack5 x 18,336 ops/sec ยฑ2.52% (84 runs sampled), 54537 ns/op -messagepack x 18,907 ops/sec ยฑ3.36% (81 runs sampled), 52890 ns/op -Fastest is json-joy/json-pack (v4) -``` - -Decoding a 584 byte JSON object: - -``` -node benchmarks/json-pack.Decoder.js -json-joy/json-pack x 258,215 ops/sec ยฑ0.97% (90 runs sampled), 3873 ns/op -JSON.parse x 224,616 ops/sec ยฑ0.72% (91 runs sampled), 4452 ns/op -@msgpack/msgpack x 196,799 ops/sec ยฑ0.74% (93 runs sampled), 5081 ns/op -msgpack x 62,323 ops/sec ยฑ0.74% (92 runs sampled), 16045 ns/op -msgpack-lite x 52,794 ops/sec ยฑ0.75% (92 runs sampled), 18941 ns/op -msgpack5 x 30,240 ops/sec ยฑ0.76% (93 runs sampled), 33069 ns/op -messagepack x 2,740 ops/sec ยฑ10.15% (49 runs sampled), 364983 ns/op -``` - - -## Usage - -### Basic usage - -Use `Encoder` to encode plain JSON values. - -```ts -import {Encoder, Decoder} from 'json-joy/{lib,es6,esm}/json-pack'; - -const encoder = new Encoder(); -const decoder = new Decoder(); -const buffer = encoder.encode({foo: 'bar'}); -const obj = decoder.decode(buffer); - -console.log(obj); // { foo: 'bar' } -``` - -Use `EncoderFull` to encode data that is more complex than plain JSON. For -example, encode binary data using `ArrayBuffer`: - -```ts -import {EncoderFull, Decoder} from 'json-joy/{lib,es6,esm}/json-pack'; - -const encoder = new EncoderFull(); -const decoder = new Decoder(); -const buffer = encoder.encode({foo: new Uint8Array([1, 2, 3]).buffer}); -const obj = decoder.decode(buffer); -console.log(obj); // { foo: ArrayBuffer { [1, 2, 3] } } -``` - - -### Pre-computed values - -You might have already encoded MessagePack value, to insert it into a bigger -MessagePack object as-is use `JsonPackValue` wrapper. - -```ts -import {EncoderFull, Decoder, JsonPackValue} from 'json-joy/{lib,es6,esm}/json-pack'; - -const encoder = new EncoderFull(); -const decoder = new Decoder(); -const buffer = encoder.encode({foo: 'bar'}); -const value = new JsonPackValue(buffer); -const buffer2 = encode({baz: value}); - -const obj = decoder.decode(buffer2); -console.log(obj); // { baz: { foo: 'bar' } } -``` - -### Extensions - -Use `JsonPackExtension` wrapper to encode extensions. - -```ts -import {EncoderFull, Decoder, JsonPackExtension} from 'json-joy/{lib,es6,esm}/json-pack'; - -const ext = new JsonPackExtension(1, new Uint8Array(8)); -const encoder = new EncoderFull(); -const decoder = new Decoder(); -const buffer = encoder.encode({foo: ext}); - -const obj = decoder.decode(buffe2); -console.log(obj); // { foo: JsonPackExtension } -``` - -### Decoding one level at a time - -You can use `.decodeOneLevel()` method to decode one level of objects or arrays -of Message Pack values at a time. Only the primitive values of the first level -are returned decoded, complex values—like objects and arrays—are -returned as `JsonPackValue` blobs. - -```ts -const msgPack = encoder.encode({ - a: 1, - b: [1], -}); -const decoded = decoder.decodeOneLevel(msgPack); -console.log(decoded); // { a: 1, b: JsonPackValue {} } -``` - -### Stable binary output - -Objects key order in JavaScript is not predictable, hence the same object can -result in different MessagePack blobs. Use `EncoderStable` to get stable -MessagePack blobs. - -```ts -import {EncoderStable} from 'json-joy/{lib,es6,esm}/json-pack'; - -const encoder = new EncoderStable(); - -const buf1 = encoder.encode({a: 1, b: 2}); -const buf2 = encoder.encode({b: 2, a: 1}); - -// buf1.equals(buf2) == true -``` diff --git a/src/json-pack/msgpack/__tests__/MsgPackDecoder.one-level.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackDecoder.one-level.spec.ts deleted file mode 100644 index 9a938c254b..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackDecoder.one-level.spec.ts +++ /dev/null @@ -1,311 +0,0 @@ -import {MsgPackDecoder} from '../MsgPackDecoder'; -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {JsonPackValue} from '../../JsonPackValue'; - -const encoder = new MsgPackEncoder(); -const decoder = new MsgPackDecoder(); -const encode = (x: unknown) => encoder.encode(x); -const decode = (x: Uint8Array, offset: number) => decoder.readLevel(x); - -describe('null', () => { - test('can decode null', () => { - const buf = encode(null); - const res = decode(buf, 0); - expect(res).toBe(null); - }); -}); - -describe('boolean', () => { - test('can decode false', () => { - const buf = encode(false); - const res = decode(buf, 0); - expect(res).toBe(false); - }); - - test('can decode true', () => { - const buf = encode(true); - const res = decode(buf, 0); - expect(res).toBe(true); - }); -}); - -describe('number', () => { - test('can decode positive fixint', () => { - const buf = new Uint8Array([123]); - const res = decode(buf, 0); - expect(res).toBe(123); - }); - - test('can decode positive fixint encoded at offset', () => { - const buf1 = new Uint8Array([0, 123]); - const buf2 = buf1.subarray(1); - const res = decode(buf2, 0); - expect(res).toBe(123); - }); - - test('can decode 0', () => { - const buf = encode(0); - const res = decode(buf, 0); - expect(res).toBe(0); - }); - - test('can decode negative fixint', () => { - const buf = encode(-1); - const res = decode(buf, 0); - expect(res).toBe(-1); - }); - - test('can decode negative fixint - 2', () => { - const buf = encode(-32); - const res = decode(buf, 0); - expect(res).toBe(-32); - }); - - test('can decode double', () => { - const buf = encode(3456.12345678902234); - const res = decode(buf, 0); - expect(res).toBe(3456.12345678902234); - }); - - test('can decode 8 byte negative int', () => { - const buf = encode(-4807526976); - const res = decode(buf, 0); - expect(res).toBe(-4807526976); - }); -}); - -describe('string', () => { - test('can decode empty string', () => { - const buf = encode(''); - const res = decode(buf, 0); - expect(res).toBe(''); - }); - - test('can decode short string', () => { - const buf = encode('abc'); - const res = decode(buf, 0); - expect(res).toBe('abc'); - }); - - test('can decode 31 char string', () => { - const buf = encode('1234567890123456789012345678901'); - const res = decode(buf, 0); - expect(res).toBe('1234567890123456789012345678901'); - }); - - test('can decode 32 char string', () => { - const buf = encode('12345678901234567890123456789012'); - const res = decode(buf, 0); - expect(res).toBe('12345678901234567890123456789012'); - }); - - test('can decode 255 char string', () => { - const str = 'a'.repeat(255); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode 256 char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode a long string', () => { - const arr = [218, 4, 192]; - for (let i = 0; i < 1216; i++) arr.push(101); - const uint8 = new Uint8Array(arr); - const res = decode(uint8, 0); - expect(res).toBe( - 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - ); - }); - - test('can decode 0xFFFF char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode 0xFFFF + 1 char string', () => { - const str = 'a'.repeat(0xffff + 1); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); -}); - -describe('array', () => { - test('can decode empty array', () => { - const buf = encode([]); - const res = decode(buf, 0); - expect(res).toEqual([]); - }); - - test('can decode one element array', () => { - const buf = encode(['abc']); - const res = decode(buf, 0); - expect(res).toEqual(['abc']); - }); - - test('can decode 15 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - const res = decode(buf, 0); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - }); - - test('can decode 16 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - const res = decode(buf, 0); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - }); - - test('can decode 255 element array', () => { - const arr = '3'.repeat(256).split('').map(Number); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF element array', () => { - const arr = '3'.repeat(0xffff).split('').map(Number); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF + 1 element array', () => { - const arr = '3'.repeat(0xffff + 1).split(''); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode nested array', () => { - const arr = [1, [2], 3]; - const buf = encode(arr); - const res = decode(buf, 0) as number[]; - expect(res[0]).toBe(1); - expect(res[1]).toStrictEqual(new JsonPackValue(encode([2]))); - expect(res[2]).toBe(3); - const arr2 = decoder.decode(encode(res)); - expect(arr2).toStrictEqual(arr); - }); - - test('can decode nested array - 2', () => { - const arr = [1, [2], [3, 4, [5]]]; - const buf = encode(arr); - const res = decode(buf, 0) as number[]; - expect(res[0]).toBe(1); - expect(res[1]).toStrictEqual(new JsonPackValue(encode([2]))); - expect(res[2]).toStrictEqual(new JsonPackValue(encode([3, 4, [5]]))); - const arr2 = decoder.decode(encode(res)); - expect(arr2).toStrictEqual(arr); - }); -}); - -describe('object', () => { - test('can decode empty object', () => { - const obj = {}; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode simple object', () => { - const obj = {foo: 'bar'}; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 14 key object', () => { - const obj: any = {}; - for (let i = 0; i < 15; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 15 key object', () => { - const obj: any = {}; - for (let i = 0; i < 15; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 16 key object', () => { - const obj: any = {}; - for (let i = 0; i < 16; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 32 key object', () => { - const obj: any = {}; - for (let i = 0; i < 32; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 255 key object', () => { - const obj: any = {}; - for (let i = 0; i < 255; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 256 key object', () => { - const obj: any = {}; - for (let i = 0; i < 256; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF + 1 key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff + 1; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode nested objects', () => { - const obj: any = { - a: {}, - b: [{}, {g: 123}], - c: 1, - d: 'asdf', - e: null, - f: false, - }; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toStrictEqual({ - c: 1, - d: 'asdf', - e: null, - f: false, - a: new JsonPackValue(encode({})), - b: new JsonPackValue(encode([{}, {g: 123}])), - }); - const obj2 = decoder.decode(encode(res)); - expect(obj2).toStrictEqual(obj); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackDecoder.shallow-reading.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackDecoder.shallow-reading.spec.ts deleted file mode 100644 index 69b3974418..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackDecoder.shallow-reading.spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {MsgPackDecoder} from '../MsgPackDecoder'; - -const encoder = new MsgPackEncoder(); -const decoder = new MsgPackDecoder(); - -describe('shallow reading values, without parsing the document', () => { - describe('reading object header', () => { - test('can read object size of empty oject', () => { - const encoded = encoder.encode({}); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(0); - }); - - test('can read small object size', () => { - const encoded = encoder.encode({foo: 'bar', a: 1, b: 2}); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(3); - }); - - test('medium size object size', () => { - const encoded = encoder.encode({ - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - }); - decoder.reader.reset(encoded); - const size = decoder.readObjHdr(); - expect(size).toBe(17); - }); - - test('throws if value is not an object', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - expect(() => decoder.readObjHdr()).toThrowError(); - }); - }); - - describe('object key finding', () => { - test('can find object key', () => { - const encoded = encoder.encode({foo: 'bar'}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('foo').val(); - expect(decoded).toBe('bar'); - }); - - test('can find object key in the middle of the object', () => { - const encoded = encoder.encode({x: 123, y: 0, z: -1}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('y').val(); - expect(decoded).toBe(0); - }); - - test('can find object key at the end of the object', () => { - const encoded = encoder.encode({x: 123, y: 0, z: -1}); - decoder.reader.reset(encoded); - const decoded = decoder.findKey('z').val(); - expect(decoded).toBe(-1); - }); - }); - - describe('reading array header', () => { - test('can read array size of an empty array', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(0); - }); - - test('can read small array size', () => { - const encoded = encoder.encode(['bar', 1, 2]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(3); - }); - - test('medium size array size', () => { - const encoded = encoder.encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]); - decoder.reader.reset(encoded); - const size = decoder.readArrHdr(); - expect(size).toBe(17); - }); - - test('throws if value is not an array', () => { - const encoded = encoder.encode({}); - decoder.reader.reset(encoded); - expect(() => decoder.readArrHdr()).toThrowError(); - }); - }); - - describe('array index finding', () => { - test('can find value at beginning of array', () => { - const encoded = encoder.encode(['foobar']); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(0).val(); - expect(decoded).toBe('foobar'); - }); - - test('can find value in the middle of array', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(1).val(); - expect(decoded).toBe(2); - }); - - test('can find value at the end of array', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - const decoded = decoder.findIndex(2).val(); - expect(decoded).toBe(3); - }); - - test('throws if array index is out of bounds', () => { - const encoded = encoder.encode([1, 2, 3]); - decoder.reader.reset(encoded); - expect(() => decoder.findIndex(3).val()).toThrowError(); - }); - - test('throws when reading value from an empty array', () => { - const encoded = encoder.encode([]); - decoder.reader.reset(encoded); - expect(() => decoder.findIndex(0).val()).toThrowError(); - }); - }); - - test('can shallow read a deeply nested value', () => { - const encoded = encoder.encode({ - a: { - b: { - c: { - d: { - e: [1, 2, 3], - }, - hmm: [ - { - foo: 'bar', - }, - ], - }, - }, - }, - }); - - decoder.reader.reset(encoded); - const decoded1 = decoder.findKey('a').findKey('b').findKey('c').findKey('d').findKey('e').val(); - expect(decoded1).toStrictEqual([1, 2, 3]); - - decoder.reader.reset(encoded); - const decoded2 = decoder.findKey('a').findKey('b').findKey('c').findKey('d').findKey('e').findIndex(1).val(); - expect(decoded2).toBe(2); - - decoder.reader.reset(encoded); - const decoded3 = decoder.findKey('a').findKey('b').findKey('c').findKey('hmm').findIndex(0).findKey('foo').val(); - expect(decoded3).toBe('bar'); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackDecoder.validate.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackDecoder.validate.spec.ts deleted file mode 100644 index 271a3228cb..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackDecoder.validate.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {MsgPackDecoder} from '../MsgPackDecoder'; - -const encoder = new MsgPackEncoder(); -const decoder = new MsgPackDecoder(); - -test('value is too short, buffer too long', () => { - const encoded = encoder.encode(1.1); - decoder.validate(encoded); - const corrupted = new Uint8Array(encoded.length + 1); - corrupted.set(encoded); - expect(() => decoder.validate(corrupted)).toThrow(); -}); - -test('value is truncated, buffer too short', () => { - const encoded = encoder.encode(1.1); - decoder.validate(encoded); - const corrupted = encoded.subarray(0, encoded.length - 1); - expect(() => decoder.validate(corrupted)).toThrow(); -}); - -test('invalid value', () => { - const encoded = new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); - expect(() => decoder.validate(encoded)).toThrow(); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackDecoderFast.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackDecoderFast.spec.ts deleted file mode 100644 index 0836acbafe..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackDecoderFast.spec.ts +++ /dev/null @@ -1,301 +0,0 @@ -import {NullObject} from '../../../util/NullObject'; -import {MsgPackDecoderFast} from '../MsgPackDecoderFast'; -import {MsgPackEncoderFast} from '../MsgPackEncoderFast'; - -const encoder = new MsgPackEncoderFast(); -const decoder = new MsgPackDecoderFast(); -const encode = (x: unknown) => encoder.encode(x); -const decode = (x: Uint8Array, offset: number) => decoder.decode(x); - -describe('null', () => { - test('can decode null', () => { - const buf = encode(null); - const res = decode(buf, 0); - expect(res).toBe(null); - }); -}); - -describe('boolean', () => { - test('can decode false', () => { - const buf = encode(false); - const res = decode(buf, 0); - expect(res).toBe(false); - }); - - test('can decode true', () => { - const buf = encode(true); - const res = decode(buf, 0); - expect(res).toBe(true); - }); -}); - -describe('number', () => { - test('can decode positive fixint', () => { - const buf = new Uint8Array([123]); - const res = decode(buf, 0); - expect(res).toBe(123); - }); - - test('can decode positive fixint encoded at offset', () => { - const buf1 = new Uint8Array([0, 123]); - const buf2 = buf1.subarray(1); - const res = decode(buf2, 0); - expect(res).toBe(123); - }); - - test('can decode 0', () => { - const buf = encode(0); - const res = decode(buf, 0); - expect(res).toBe(0); - }); - - test('can decode negative fixint', () => { - const buf = encode(-1); - const res = decode(buf, 0); - expect(res).toBe(-1); - }); - - test('can decode negative fixint - 2', () => { - const buf = encode(-32); - const res = decode(buf, 0); - expect(res).toBe(-32); - }); - - test('can decode double', () => { - const buf = encode(3456.12345678902234); - const res = decode(buf, 0); - expect(res).toBe(3456.12345678902234); - }); - - test('can decode 8 byte negative int', () => { - const buf = encode(-4807526976); - const res = decode(buf, 0); - expect(res).toBe(-4807526976); - }); -}); - -describe('string', () => { - test('can decode empty string', () => { - const buf = encode(''); - const res = decode(buf, 0); - expect(res).toBe(''); - }); - - test('can decode short string', () => { - const buf = encode('abc'); - const res = decode(buf, 0); - expect(res).toBe('abc'); - }); - - test('can decode 31 char string', () => { - const buf = encode('1234567890123456789012345678901'); - const res = decode(buf, 0); - expect(res).toBe('1234567890123456789012345678901'); - }); - - test('can decode 32 char string', () => { - const buf = encode('12345678901234567890123456789012'); - const res = decode(buf, 0); - expect(res).toBe('12345678901234567890123456789012'); - }); - - test('can decode 255 char string', () => { - const str = 'a'.repeat(255); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode 256 char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode a long string', () => { - const arr = [218, 4, 192]; - for (let i = 0; i < 1216; i++) arr.push(101); - const uint8 = new Uint8Array(arr); - const res = decode(uint8, 0); - expect(res).toBe( - 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - ); - }); - - test('can decode 0xFFFF char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); - - test('can decode 0xFFFF + 1 char string', () => { - const str = 'a'.repeat(0xffff + 1); - const buf = encode(str); - const res = decode(buf, 0); - expect(res).toBe(str); - }); -}); - -describe('array', () => { - test('can decode empty array', () => { - const buf = encode([]); - const res = decode(buf, 0); - expect(res).toEqual([]); - }); - - test('can decode one element array', () => { - const buf = encode(['abc']); - const res = decode(buf, 0); - expect(res).toEqual(['abc']); - }); - - test('can decode 15 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - const res = decode(buf, 0); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - }); - - test('can decode 16 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - const res = decode(buf, 0); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - }); - - test('can decode 255 element array', () => { - const arr = '3'.repeat(256).split('').map(Number); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF element array', () => { - const arr = '3'.repeat(0xffff).split('').map(Number); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF + 1 element array', () => { - const arr = '3'.repeat(0xffff + 1).split(''); - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode nested array', () => { - const arr = [1, [2], 3]; - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); - - test('can decode nested array - 2', () => { - const arr = [1, [2], [3, 4, [5]]]; - const buf = encode(arr); - const res = decode(buf, 0); - expect(res).toEqual(arr); - }); -}); - -describe('object', () => { - test('can decode empty object', () => { - const obj = {}; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode simple object', () => { - const obj = {foo: 'bar'}; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 14 key object', () => { - const obj: any = {}; - for (let i = 0; i < 15; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 15 key object', () => { - const obj: any = {}; - for (let i = 0; i < 15; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 16 key object', () => { - const obj: any = {}; - for (let i = 0; i < 16; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 32 key object', () => { - const obj: any = {}; - for (let i = 0; i < 32; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 255 key object', () => { - const obj: any = {}; - for (let i = 0; i < 255; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 256 key object', () => { - const obj: any = {}; - for (let i = 0; i < 256; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF + 1 key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff + 1; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('can decode nested objects', () => { - const obj: any = { - a: {}, - b: { - c: {}, - d: {g: 123}, - }, - }; - const buf = encode(obj); - const res = decode(buf, 0); - expect(res).toEqual(obj); - }); - - test('throws on __proto__ key', () => { - const obj = new NullObject(); - // tslint:disable-next-line: no-string-literal - obj['__proto__'] = 123; - const buf = encode(obj); - expect(() => decode(buf, 0)).toThrow(); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackEncoder.codec.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackEncoder.codec.spec.ts deleted file mode 100644 index 649b6ef876..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackEncoder.codec.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {JsonPackExtension} from '../../JsonPackExtension'; -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {MsgPackDecoderFast} from '..'; -import {documents} from '../../../__tests__/json-documents'; - -const encoder = new MsgPackEncoder(); -const encode = (x: unknown) => encoder.encode(x); -const decoder = new MsgPackDecoderFast(); -const decode = (a: Uint8Array) => decoder.decode(a); - -const tests: Array<{name: string; json: unknown}> = [ - ...documents, - { - name: 'simple ArrayBuffer', - json: new Uint8Array([1, 2, 3]), - }, - { - name: 'empty ArrayBuffer', - json: new Uint8Array([]), - }, - { - name: '255 byte ArrayBuffer', - json: new Uint8Array(255), - }, - { - name: '256 byte ArrayBuffer', - json: new Uint8Array(256), - }, - { - name: '0xFFFF byte ArrayBuffer', - json: new Uint8Array(0xffff), - }, - { - name: '0xFFFF + 1 byte ArrayBuffer', - json: new Uint8Array(0xffff + 1), - }, - { - name: '1 byte extension', - json: new JsonPackExtension(1, new Uint8Array([1])), - }, - { - name: '2 byte extension', - json: new JsonPackExtension(1, new Uint8Array([1, 1])), - }, - { - name: '4 byte extension', - json: new JsonPackExtension(6, new Uint8Array([1, 1, 2, 5])), - }, - { - name: '8 byte extension', - json: new JsonPackExtension(213, new Uint8Array([1, 1, 2, 5, 0, 0, 3, 3])), - }, - { - name: '16 byte extension', - json: new JsonPackExtension(0, new Uint8Array([1, 1, 2, 5, 0, 0, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2])), - }, - { - name: '10 byte extension', - json: new JsonPackExtension(10, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])), - }, - { - name: '255 byte extension', - json: new JsonPackExtension(10, new Uint8Array(255)), - }, - { - name: '256 byte extension', - json: new JsonPackExtension(11, new Uint8Array(256)), - }, - { - name: '0xFFFF byte extension', - json: new JsonPackExtension(12, new Uint8Array(0xffff)), - }, - { - name: '0xFFFF + 1 byte extension', - json: new JsonPackExtension(12, new Uint8Array(0xffff + 1)), - }, - { - name: '0xFFFFF byte extension', - json: new JsonPackExtension(12, new Uint8Array(0xfffff)), - }, -]; - -for (const t of tests) { - test(t.name, () => { - const buf = encode(t.json); - const res = decode(buf); - expect(res).toEqual(t.json); - }); -} diff --git a/src/json-pack/msgpack/__tests__/MsgPackEncoder.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackEncoder.spec.ts deleted file mode 100644 index f6beda07c7..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackEncoder.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {JsonPackExtension} from '../../JsonPackExtension'; -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {MsgPackDecoderFast} from '..'; -import {JsonPackValue} from '../../JsonPackValue'; - -const encoder = new MsgPackEncoder(); -const encode = (x: unknown) => encoder.encode(x); -const decoder = new MsgPackDecoderFast(); -const decode = (a: Uint8Array) => decoder.decode(a); - -describe('binary', () => { - test('can encode a simple Uin8Array', () => { - const data = {foo: new Uint8Array([3, 2, 1])}; - const arr = encode(data); - const res = decode(arr); - expect(res).toEqual(data); - expect((res as any).foo).toBeInstanceOf(Uint8Array); - }); -}); - -describe('extensions', () => { - test('can encode a 5 byte extension', () => { - const ext = new JsonPackExtension(33, new Uint8Array([1, 2, 3, 4, 5])); - const data = {foo: ext}; - const arr = encode(data); - const res = decode(arr); - expect(res).toEqual(data); - expect((res as any).foo.tag).toBe(33); - expect((res as any).foo.val).toEqual(new Uint8Array([1, 2, 3, 4, 5])); - expect((res as any).foo).toBeInstanceOf(JsonPackExtension); - }); - - test('can encode a 1 byte extension', () => { - const ext = new JsonPackExtension(32, new Uint8Array([5])); - const data = {foo: ext}; - const arr = encode(data); - const res = decode(arr); - expect(res).toEqual(data); - expect((res as any).foo.tag).toBe(32); - expect((res as any).foo.val).toEqual(new Uint8Array([5])); - expect((res as any).foo).toBeInstanceOf(JsonPackExtension); - }); - - test('can encode a 2 byte extension', () => { - const ext = new JsonPackExtension(32, new Uint8Array([5, 0])); - const data = {foo: ext}; - const arr = encode(data); - const res = decode(arr); - expect(res).toEqual(data); - expect((res as any).foo.tag).toBe(32); - expect((res as any).foo.val).toEqual(new Uint8Array([5, 0])); - expect((res as any).foo).toBeInstanceOf(JsonPackExtension); - }); -}); - -describe('pre-computed value', () => { - test('can encode a pre-computed value in an object', () => { - const data = {foo: new JsonPackValue(encode(['gaga']))}; - const arr = encode(data); - expect(arr).toEqual(encode({foo: ['gaga']})); - }); - - test('can encode a pre-computed value in an array', () => { - const data = {foo: [1, new JsonPackValue(encode(['gaga']))]}; - const arr = encode(data); - expect(arr).toEqual(encode({foo: [1, ['gaga']]})); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.overwrite.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.overwrite.spec.ts deleted file mode 100644 index 86638f589c..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.overwrite.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {MsgPackEncoderFast} from '../MsgPackEncoderFast'; -import {MsgPackDecoderFast} from '../MsgPackDecoderFast'; - -const encoder = new MsgPackEncoderFast(); -const decoder = new MsgPackDecoderFast(); -const encode = (x: unknown) => encoder.encode(x); -const decode = (arr: Uint8Array) => decoder.decode(arr); - -test('does not overwrite previous buffer', () => { - const buf1 = encode(true); - const buf2 = encode(false); - const val1 = decode(buf1); - const val2 = decode(buf2); - expect(val1).toBe(true); - expect(val2).toBe(false); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.spec.ts deleted file mode 100644 index f56f70e1d1..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackEncoderFast.spec.ts +++ /dev/null @@ -1,293 +0,0 @@ -import {MsgPackEncoderFast} from '..'; - -const {TextEncoder} = require('util'); -if (!global.TextEncoder) global.TextEncoder = TextEncoder; - -const encoder = new MsgPackEncoderFast(); -const encode = (x: unknown) => encoder.encode(x); - -describe('null', () => { - test('encodes null', () => { - const buf = encode(null); - expect([...new Uint8Array(buf)]).toEqual([0xc0]); - }); -}); - -describe('boolean', () => { - test('encodes false', () => { - const buf = encode(false); - expect([...new Uint8Array(buf)]).toEqual([0xc2]); - }); - - test('encodes true', () => { - const buf = encode(true); - expect([...new Uint8Array(buf)]).toEqual([0xc3]); - }); -}); - -describe('number', () => { - test('encodes positive fixint', () => { - const ints = [0, 1, 2, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000, 0x7f]; - for (const int of ints) expect([...new Uint8Array(encode(int))]).toEqual([int]); - }); - - test('encodes negative fixint', () => { - const ints = [-1, -2, -3, -4, -0b11110, -0b11111]; - const res = [ - 0xe0 | (-1 + 0x20), - 0xe0 | (-2 + 0x20), - 0xe0 | (-3 + 0x20), - 0xe0 | (-4 + 0x20), - 0xe0 | (-0b11110 + 0x20), - 0xe0 | (-0b11111 + 0x20), - ]; - for (let i = 0; i < ints.length; i++) expect([...new Uint8Array(encode(ints[i]))]).toEqual([res[i]]); - }); - - test('encodes doubles', () => { - const arr = encode(123.456789123123); - expect(arr.byteLength).toBe(9); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xcb); - expect(view.getFloat64(1)).toBe(123.456789123123); - }); - - // Skipped as due to optimization encoding this as float64 - test.skip('encodes large negative integer', () => { - const arr = encode(-4807526976); - expect(arr.byteLength).toBe(9); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xd3); - expect([...new Uint8Array(arr.buffer)]).toEqual([0xd3, 0xff, 0xff, 0xff, 0xfe, 0xe1, 0x72, 0xf5, 0xc0]); - }); -}); - -describe('string', () => { - test('encodes a zero length string', () => { - const buf = encode(''); - expect(buf.byteLength).toBe(1); - expect([...new Uint8Array(buf)]).toEqual([0b10100000]); - }); - - test('encodes a one char string', () => { - const buf = encode('a'); - expect(buf.byteLength).toBe(2); - expect([...new Uint8Array(buf)]).toEqual([0b10100001, 97]); - }); - - test('encodes a short string', () => { - const buf = encode('foo'); - expect(buf.byteLength).toBe(4); - expect([...new Uint8Array(buf)]).toEqual([0b10100011, 102, 111, 111]); - }); - - // Skipping these as for performance optimization strings are encoded as 4x longer then they could be. - test.skip('encodes 31 char string', () => { - const buf = encode('1234567890123456789012345678901'); - expect(buf.byteLength).toBe(32); - expect([...new Uint8Array(buf)]).toEqual([ - 0b10111111, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 48, 49, - ]); - }); - test.skip('encodes 255 char string', () => { - const buf = encode('a'.repeat(255)); - expect(buf.byteLength).toBe(257); - expect([...new Uint8Array(buf)]).toEqual([ - 0xd9, 255, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, - ]); - }); - test.skip('encodes 0xFFFF char string', () => { - const buf = encode('b'.repeat(0xffff)); - expect(buf.byteLength).toBe(0xffff + 3); - const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); - expect(view.getUint8(0)).toBe(0xda); - expect(view.getUint16(1)).toBe(0xffff); - }); - - // Skipping this test as due to optimizations, optimal encoding size is not used. - test.skip('encodes 2000 char string', () => { - const buf = encode('ab'.repeat(1000)); - expect(buf.byteLength).toBe(2003); - const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); - expect(view.getUint8(0)).toBe(0xda); - expect(view.getUint16(1)).toBe(2000); - }); - - test('encodes 0xFFFF + 1 char string', () => { - const buf = encode('d'.repeat(0xffff + 1)); - expect(buf.byteLength).toBe(0xffff + 1 + 5); - // const view = new DataView(buf); - // expect(view.getUint8(0)).toBe(0xdb); - // expect(view.getUint32(1)).toBe(0xFFFF + 1); - }); -}); - -describe('array', () => { - test('encodes empty array', () => { - const buf = encode([]); - expect(buf.byteLength).toBe(1); - expect([...new Uint8Array(buf)]).toEqual([0b10010000]); - }); - - test('encodes one element array', () => { - const buf = encode([1]); - expect(buf.byteLength).toBe(2); - expect([...new Uint8Array(buf)]).toEqual([0b10010001, 1]); - }); - - test('encodes three element array', () => { - const buf = encode([1, 2, 3]); - expect(buf.byteLength).toBe(4); - expect([...new Uint8Array(buf)]).toEqual([0b10010011, 1, 2, 3]); - }); - - test('encodes 15 element array', () => { - const arr = '1'.repeat(15).split('').map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(16); - expect([...new Uint8Array(buf)]).toEqual([0b10011111, ...arr]); - }); - - test('encodes 16 element array', () => { - const arr = '2'.repeat(16).split('').map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(19); - expect([...new Uint8Array(buf)]).toEqual([0xdc, 0, 16, ...arr]); - }); - - test('encodes 255 element array', () => { - const arr = '3'.repeat(255).split('').map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(1 + 2 + 255); - expect([...new Uint8Array(buf)]).toEqual([0xdc, 0, 255, ...arr]); - }); - - test('encodes 256 element array', () => { - const arr = '3'.repeat(256).split('').map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(1 + 2 + 256); - expect([...new Uint8Array(buf)]).toEqual([0xdc, 1, 0, ...arr]); - }); - - test('encodes 0xFFFF element array', () => { - const arr = '3'.repeat(0xffff).split('').map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(1 + 2 + 0xffff); - expect([...new Uint8Array(buf)]).toEqual([0xdc, 0xff, 0xff, ...arr]); - }); - - test('encodes 0xFFFF + 1 element array', () => { - const arr = '3' - .repeat(0xffff + 1) - .split('') - .map(Number); - const buf = encode(arr); - expect(buf.byteLength).toBe(1 + 4 + 0xffff + 1); - expect([...new Uint8Array(buf)]).toEqual([0xdd, 0, 1, 0, 0, ...arr]); - }); -}); - -describe('object', () => { - test('encodes empty object', () => { - const buf = encode({}); - expect(buf.byteLength).toBe(1); - expect([...new Uint8Array(buf)]).toEqual([0b10000000]); - }); - - test('encodes object with one key', () => { - const buf = encode({a: 1}); - expect(buf.byteLength).toBe(1 + 2 + 1); - expect([...new Uint8Array(buf)]).toEqual([0b10000001, 0b10100001, 97, 1]); - }); - - test('encodes object with 15 keys', () => { - const arr = encode({ - 1: 1, - 2: 1, - 3: 1, - 4: 1, - 5: 1, - 6: 1, - 7: 1, - 8: 1, - 9: 1, - 10: 1, - 11: 1, - 12: 1, - 13: 1, - 14: 1, - 15: 1, - }); - expect(arr.byteLength).toBe(1 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 4 + 4 + 4 + 4 + 4 + 4); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0b10001111); - }); - - test('encodes object with 16 keys', () => { - const arr = encode({ - 1: 1, - 2: 1, - 3: 1, - 4: 1, - 5: 1, - 6: 1, - 7: 1, - 8: 1, - 9: 1, - 10: 1, - 11: 1, - 12: 1, - 13: 1, - 14: 1, - 15: 1, - 16: 1, - }); - expect(arr.byteLength).toBe(1 + 2 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 4 + 4 + 4 + 4 + 4 + 4 + 4); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xde); - expect(view.getUint16(1)).toBe(16); - }); - - test('encodes object with 255 keys', () => { - const obj: any = {}; - for (let i = 0; i < 255; i++) obj[String(i)] = i; - const arr = encode(obj); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xde); - expect(view.getUint16(1)).toBe(255); - expect(view.getUint8(3)).toBe(0b10100001); - expect(view.getUint8(4)).toBe(48); - }); - - test('encodes object with 0xFFFF keys', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff; i++) obj[String(i)] = i; - const arr = encode(obj); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xde); - expect(view.getUint16(1)).toBe(0xffff); - expect(view.getUint8(3)).toBe(0b10100001); - expect(view.getUint8(4)).toBe(48); - }); - - test('encodes object with 0xFFFF + 1 keys', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff + 1; i++) obj[String(i)] = i; - const arr = encode(obj); - const view = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); - expect(view.getUint8(0)).toBe(0xdf); - expect(view.getUint32(1)).toBe(0xffff + 1); - expect(view.getUint8(5)).toBe(0b10100001); - expect(view.getUint8(6)).toBe(48); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackEncoderStable.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackEncoderStable.spec.ts deleted file mode 100644 index 3a36b7f761..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackEncoderStable.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {MsgPackEncoderStable} from '../MsgPackEncoderStable'; -import {MsgPackDecoderFast} from '../MsgPackDecoderFast'; - -const encoder = new MsgPackEncoderStable(); -const encode = (x: unknown) => encoder.encode(x); -const decoder = new MsgPackDecoderFast(); -const decode = (a: Uint8Array) => decoder.decode(a); - -test('encodes object the same regardless of key order', () => { - const data1 = {a: 1, b: 2}; - const data2 = {b: 2, a: 1}; - const arr1 = encode(data1); - const arr2 = encode(data2); - expect(arr1).toStrictEqual(arr2); - expect(decode(arr1)).toStrictEqual(decode(arr2)); - expect(arr1).toMatchInlineSnapshot(` - Uint8Array [ - 130, - 161, - 97, - 1, - 161, - 98, - 2, - ] - `); -}); diff --git a/src/json-pack/msgpack/__tests__/MsgPackToJsonConverter.spec.ts b/src/json-pack/msgpack/__tests__/MsgPackToJsonConverter.spec.ts deleted file mode 100644 index 20db34cf86..0000000000 --- a/src/json-pack/msgpack/__tests__/MsgPackToJsonConverter.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {documents} from '../../../__tests__/json-documents'; -import {MsgPackToJsonConverter} from '../MsgPackToJsonConverter'; -import {MsgPackEncoder} from '../MsgPackEncoder'; - -const encoder = new MsgPackEncoder(); -const converter = new MsgPackToJsonConverter(); - -for (const doc of documents) { - (doc.only ? test.only : test)(doc.name, () => { - const msgpack = encoder.encode(doc.json); - const json = converter.convert(msgpack); - const parsed = JSON.parse(json); - expect(parsed).toStrictEqual(doc.json); - }); -} diff --git a/src/json-pack/msgpack/__tests__/codec.spec.ts b/src/json-pack/msgpack/__tests__/codec.spec.ts deleted file mode 100644 index a334fa3707..0000000000 --- a/src/json-pack/msgpack/__tests__/codec.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {MsgPackEncoderFast, MsgPackDecoderFast} from '..'; -import {documents} from '../../../__tests__/json-documents'; - -const encoder = new MsgPackEncoderFast(); -const decoder = new MsgPackDecoderFast(); -const encode = (x: unknown) => encoder.encode(x); -const decode = (x: Uint8Array) => decoder.decode(x); - -for (const t of documents) { - test(t.name, () => { - const buf = encode(t.json); - const res = decode(buf); - expect(res).toEqual(t.json); - }); -} diff --git a/src/json-pack/msgpack/__tests__/decode.spec.ts b/src/json-pack/msgpack/__tests__/decode.spec.ts deleted file mode 100644 index 0c5be26c1f..0000000000 --- a/src/json-pack/msgpack/__tests__/decode.spec.ts +++ /dev/null @@ -1,239 +0,0 @@ -import {MsgPackEncoderFast, MsgPackDecoderFast} from '..'; - -const encoder = new MsgPackEncoderFast(); -const encode = (x: unknown) => encoder.encode(x); -const decoder = new MsgPackDecoderFast(); -const decode = (a: Uint8Array) => decoder.decode(a); - -describe('null', () => { - test('can decode null', () => { - const buf = encode(null); - const res = decode(buf); - expect(res).toBe(null); - }); -}); - -describe('boolean', () => { - test('can decode false', () => { - const buf = encode(false); - const res = decode(buf); - expect(res).toBe(false); - }); - - test('can decode true', () => { - const buf = encode(true); - const res = decode(buf); - expect(res).toBe(true); - }); -}); - -describe('number', () => { - test('can decode positive fixint', () => { - const buf = encode(123); - const res = decode(buf); - expect(res).toBe(123); - }); - - test('can decode 0', () => { - const buf = encode(0); - const res = decode(buf); - expect(res).toBe(0); - }); - - test('can decode negative fixint', () => { - const buf = encode(-1); - const res = decode(buf); - expect(res).toBe(-1); - }); - - test('can decode negative fixint - 2', () => { - const buf = encode(-32); - const res = decode(buf); - expect(res).toBe(-32); - }); - - test('can decode double', () => { - const buf = encode(3456.12345678902234); - const res = decode(buf); - expect(res).toBe(3456.12345678902234); - }); - - test('can decode 8 byte negative int', () => { - const buf = encode(-4807526976); - const res = decode(buf); - expect(res).toBe(-4807526976); - }); -}); - -describe('string', () => { - test('can decode empty string', () => { - const buf = encode(''); - const res = decode(buf); - expect(res).toBe(''); - }); - - test('can decode short string', () => { - const buf = encode('abc'); - const res = decode(buf); - expect(res).toBe('abc'); - }); - - test('can decode 31 char string', () => { - const buf = encode('1234567890123456789012345678901'); - const res = decode(buf); - expect(res).toBe('1234567890123456789012345678901'); - }); - - test('can decode 32 char string', () => { - const buf = encode('12345678901234567890123456789012'); - const res = decode(buf); - expect(res).toBe('12345678901234567890123456789012'); - }); - - test('can decode 255 char string', () => { - const str = 'a'.repeat(255); - const buf = encode(str); - const res = decode(buf); - expect(res).toBe(str); - }); - - test('can decode 256 char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf); - expect(res).toBe(str); - }); - - test('can decode 0xFFFF char string', () => { - const str = 'a'.repeat(256); - const buf = encode(str); - const res = decode(buf); - expect(res).toBe(str); - }); - - test('can decode 0xFFFF + 1 char string', () => { - const str = 'a'.repeat(0xffff + 1); - const buf = encode(str); - const res = decode(buf); - expect(res).toBe(str); - }); -}); - -describe('array', () => { - test('can decode empty array', () => { - const buf = encode([]); - const res = decode(buf); - expect(res).toEqual([]); - }); - - test('can decode one element array', () => { - const buf = encode(['abc']); - const res = decode(buf); - expect(res).toEqual(['abc']); - }); - - test('can decode 15 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - const res = decode(buf); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - }); - - test('can decode 16 element array', () => { - const buf = encode([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - const res = decode(buf); - expect(res).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - }); - - test('can decode 255 element array', () => { - const arr = '3'.repeat(256).split('').map(Number); - const buf = encode(arr); - const res = decode(buf); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF element array', () => { - const arr = '3'.repeat(0xffff).split('').map(Number); - const buf = encode(arr); - const res = decode(buf); - expect(res).toEqual(arr); - }); - - test('can decode 0xFFFF + 1 element array', () => { - const arr = '3'.repeat(0xffff + 1).split(''); - const buf = encode(arr); - const res = decode(buf); - expect(res).toEqual(arr); - }); -}); - -describe('object', () => { - test('can decode empty object', () => { - const obj = {}; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode simple object', () => { - const obj = {foo: 'bar'}; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 15 key object', () => { - const obj: any = {}; - for (let i = 0; i < 15; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 16 key object', () => { - const obj: any = {}; - for (let i = 0; i < 16; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 32 key object', () => { - const obj: any = {}; - for (let i = 0; i < 32; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 255 key object', () => { - const obj: any = {}; - for (let i = 0; i < 255; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 256 key object', () => { - const obj: any = {}; - for (let i = 0; i < 256; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); - - test('can decode 0xFFFF + 1 key object', () => { - const obj: any = {}; - for (let i = 0; i < 0xffff + 1; i++) obj[String(i)] = i; - const buf = encode(obj); - const res = decode(buf); - expect(res).toEqual(obj); - }); -}); diff --git a/src/json-pack/msgpack/__tests__/fuzzing.spec.ts b/src/json-pack/msgpack/__tests__/fuzzing.spec.ts deleted file mode 100644 index 403160dcce..0000000000 --- a/src/json-pack/msgpack/__tests__/fuzzing.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {encode} from '@msgpack/msgpack'; -import {RandomJson} from '../../../json-random'; -import {MsgPackEncoderFast} from '../MsgPackEncoderFast'; -import {MsgPackDecoderFast} from '../MsgPackDecoderFast'; - -const encoder1 = new MsgPackEncoderFast(); -const decoder1 = new MsgPackDecoderFast(); - -test('fuzzing', () => { - for (let i = 0; i < 200; i++) { - const value = RandomJson.generate(); - const encoded1 = encoder1.encode(value); - const decoded1 = decoder1.decode(encoded1); - const encoded2 = encode(value); - const decoded2 = decoder1.decode(encoded2); - expect(decoded1).toStrictEqual(value); - expect(decoded2).toStrictEqual(value); - } -}); diff --git a/src/json-pack/msgpack/__tests__/numbers.spec.ts b/src/json-pack/msgpack/__tests__/numbers.spec.ts deleted file mode 100644 index 90c1a46b49..0000000000 --- a/src/json-pack/msgpack/__tests__/numbers.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {MsgPackEncoderFast, MsgPackDecoderFast} from '..'; - -const encoder = new MsgPackEncoderFast(); -const encode = (x: unknown) => encoder.encode(x); -const decoder = new MsgPackDecoderFast(); -const decode = (a: Uint8Array) => decoder.decode(a); - -test('unsigned integers', () => { - let x1 = 0; - let x2 = 1; - for (let i = 0; i < 10000000000000000000; ) { - i = x1 + x2; - const buf = encode(i); - const res = decode(buf); - expect(res).toBe(i); - [x1, x2] = [x2, i]; - } -}); - -test('unsigned integers - 2', () => { - let x = 0; - for (let i = 0; i < 10000; i++) { - const buf = encode(x); - const res = decode(buf); - expect(res).toBe(x); - x += Math.round(1000 * Math.random()); - } -}); - -test('negative integers', () => { - let x1 = 0; - let x2 = -1; - for (let i = 0; i > -1000000000000000000; ) { - i = x1 + x2; - const buf = encode(i); - const res = decode(buf); - expect(res).toBe(i); - [x1, x2] = [x2, i]; - } -}); - -test('floats', () => { - let x = Math.random(); - for (let i = 0; i < 1000; i++) { - const buf = encode(x); - const res = decode(buf); - expect(res).toBe(x); - x = x * Math.random(); - } -}); - -test('floats - 2', () => { - let x = 1.001; - for (let i = 0; i < 10000; i++) { - const buf = encode(x); - const res = decode(buf); - expect(res).toBe(x); - x *= 1 + Math.random(); - } -}); - -test('floats - 3', () => { - let x = 0.1; - for (let i = 0; i < 10000; i++) { - const buf = encode(x); - const res = decode(buf); - expect(res).toBe(x); - x += 0.1; - } -}); - -test('floats - 4', () => { - let x = Math.random(); - for (let i = 0; i < 10000; i++) { - const buf = encode(x); - const res = decode(buf); - expect(res).toBe(x); - x += Math.random(); - } -}); diff --git a/src/json-pack/msgpack/__tests__/shallow-read.genShallowRead.spec.ts b/src/json-pack/msgpack/__tests__/shallow-read.genShallowRead.spec.ts deleted file mode 100644 index bc85687d59..0000000000 --- a/src/json-pack/msgpack/__tests__/shallow-read.genShallowRead.spec.ts +++ /dev/null @@ -1,130 +0,0 @@ -import {genShallowReader} from '../shallow-read'; -import {MsgPackEncoder} from '../MsgPackEncoder'; -import {MsgPackDecoder} from '../MsgPackDecoder'; -import {Path} from '../../../json-pointer'; - -const assetShallowRead = (doc: unknown, path: Path): void => { - const encoder = new MsgPackEncoder(); - const encoded = encoder.encode(doc); - const decoder = new MsgPackDecoder(); - decoder.reader.reset(encoded); - const res1 = decoder.find(path).reader.x; - // console.log(res1); - const fn = genShallowReader(path); - // console.log(fn.toString()); - decoder.reader.reset(encoded); - const res2 = fn(decoder); - // console.log(res2); - expect(res1).toBe(res2); -}; - -describe('genShallowRead', () => { - test('first-level object', () => { - const doc = { - bar: {}, - baz: 123, - gg: true, - }; - assetShallowRead(doc, ['bar']); - assetShallowRead(doc, ['baz']); - assetShallowRead(doc, ['gg']); - }); - - test('second-level object', () => { - const doc = { - a: { - bar: {}, - baz: 123, - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - assetShallowRead(doc, ['a']); - assetShallowRead(doc, ['a', 'bar']); - assetShallowRead(doc, ['a', 'baz']); - assetShallowRead(doc, ['a', 'gg']); - assetShallowRead(doc, ['b', 'mmmm']); - assetShallowRead(doc, ['b', 'mmmm', 's']); - assetShallowRead(doc, ['end']); - }); - - test('first-level array', () => { - const doc = [0]; - assetShallowRead(doc, [0]); - }); - - test('first-level array - 2', () => { - const doc = [1234, 'asdf', {}, null, false]; - assetShallowRead(doc, [0]); - assetShallowRead(doc, [1]); - assetShallowRead(doc, [2]); - assetShallowRead(doc, [3]); - assetShallowRead(doc, [4]); - }); - - test('throws when selector is out of bounds of array', () => { - const doc = [1234, 'asdf', {}, null, false]; - expect(() => assetShallowRead(doc, [5])).toThrowError(); - }); - - test('can read from complex nested document', () => { - const doc = { - a: { - bar: [ - { - a: 1, - 2: true, - asdf: false, - }, - 5, - ], - baz: ['a', 'b', 123], - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - assetShallowRead(doc, ['a']); - assetShallowRead(doc, ['a', 'bar', 0]); - assetShallowRead(doc, ['a', 'bar', 1]); - assetShallowRead(doc, ['a', 'bar', 0, 'a']); - assetShallowRead(doc, ['a', 'bar', 0, '2']); - assetShallowRead(doc, ['a', 'bar', 0, 'asdf']); - assetShallowRead(doc, ['b']); - assetShallowRead(doc, ['b', 'mmmm']); - assetShallowRead(doc, ['b', 'mmmm', 's']); - assetShallowRead(doc, ['end']); - }); - - test('should throw when key does not exist', () => { - const doc = { - a: { - bar: {}, - baz: 123, - gg: true, - }, - b: { - mmmm: { - s: true, - }, - }, - end: null, - }; - const encoder = new MsgPackEncoder(); - const encoded = encoder.encode(doc); - const decoder = new MsgPackDecoder(); - decoder.reader.reset(encoded); - const fn = genShallowReader(['asdf']); - // console.log(fn.toString()); - expect(() => fn(decoder)).toThrowError(); - }); -}); diff --git a/src/json-pack/msgpack/constants.ts b/src/json-pack/msgpack/constants.ts deleted file mode 100644 index 8e9e5aa2bb..0000000000 --- a/src/json-pack/msgpack/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const enum MSGPACK { - NULL = 0xc0, - UNDEFINED = 0xc1, - FALSE = 0xc2, - TRUE = 0xc3, -} diff --git a/src/json-pack/msgpack/index.ts b/src/json-pack/msgpack/index.ts deleted file mode 100644 index f63069b61a..0000000000 --- a/src/json-pack/msgpack/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * # `json-pack` MessagePack` - * - * Library for encoding and decoding JavaScript native structures to MessagePack - * format. - * - * Use `Encoder` to encode plain JSON values. - * - * ```ts - * import {Encoder, Decoder} from 'json-joy/{lib,es6,esm}/json-pack'; - * - * const encoder = new Encoder(); - * const decoder = new Decoder(); - * const buffer = encoder.encode({foo: 'bar'}); - * const obj = decoder.decode(buffer); - * - * console.log(obj); // { foo: 'bar' } - * ``` - * - * For more: - * - * - Use {@link Encoder} to encode only JSON values. - * - Use {@link EncoderFull} to also encode binary data, extensions and pre-computed MessagePack buffers. - * - To encode binary data use `Uint8Array`. - * - To encode an extension use {@link JsonPackExtension}. - * - To encode a pre-computed MessagePack value use {@link JsonPackValue}. - * - * @module - */ - -export {MsgPackEncoderFast} from './MsgPackEncoderFast'; -export {MsgPackEncoder} from './MsgPackEncoder'; -export {MsgPackEncoderStable} from './MsgPackEncoderStable'; -export {MsgPackDecoderFast} from './MsgPackDecoderFast'; -export {MsgPackToJsonConverter} from './MsgPackToJsonConverter'; -export {JsonPackValue} from '../JsonPackValue'; -export {JsonPackExtension} from '../JsonPackExtension'; -export * from './types'; diff --git a/src/json-pack/msgpack/shallow-read.ts b/src/json-pack/msgpack/shallow-read.ts deleted file mode 100644 index 5ecb09832e..0000000000 --- a/src/json-pack/msgpack/shallow-read.ts +++ /dev/null @@ -1,113 +0,0 @@ -import {Path} from '../../json-pointer'; -import {Codegen} from '../../util/codegen/Codegen'; -import type {MsgPackDecoder} from './MsgPackDecoder'; - -type Decoder = Pick; - -type Fn = (decoder: Decoder) => number; - -const toUtf8 = (str: string) => { - const arr: number[] = []; - const length = str.length; - let curr = 0; - while (curr < length) { - let value = str.charCodeAt(curr++); - if ((value & 0xffffff80) === 0) { - arr.push(value); - continue; - } else if ((value & 0xfffff800) === 0) { - arr.push(((value >> 6) & 0x1f) | 0xc0); - } else { - if (value >= 0xd800 && value <= 0xdbff) { - if (curr < length) { - const extra = str.charCodeAt(curr); - if ((extra & 0xfc00) === 0xdc00) { - curr++; - value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; - } - } - } - if ((value & 0xffff0000) === 0) { - arr.push(((value >> 12) & 0x0f) | 0xe0); - arr.push(((value >> 6) & 0x3f) | 0x80); - } else { - arr.push(((value >> 18) & 0x07) | 0xf0); - arr.push(((value >> 12) & 0x3f) | 0x80); - arr.push(((value >> 6) & 0x3f) | 0x80); - } - } - arr.push((value & 0x3f) | 0x80); - } - return arr; -}; - -export const genShallowReader = (path: Path): Fn => { - const codegen = new Codegen({ - args: ['dec'], - name: 'readShallow', - prologue: 'var r = dec.reader;', - epilogue: 'return r.x;', - }); - - for (let i = 0; i < path.length; i++) { - const step = path[i]; - switch (typeof step) { - case 'string': { - const rObj = codegen.getRegister(); - const rIter = codegen.getRegister(); - const rFound = codegen.getRegister(); - codegen.js(/* js */ `var ${rObj} = dec.readObjHdr();`); - codegen.js(/* js */ `var ${rFound} = false;`); - codegen.js(`for(var ${rIter} = 0; ${rIter} < ${rObj}; ${rIter}++) {`); - const utf8Arr = toUtf8(step); - const length = utf8Arr.length; - const rKey = codegen.getRegister(); - codegen.js(/* js */ `var ${rKey} = dec.readStrHdr();`); - codegen.js(/* js */ `if (${rKey} !== ${length}) { r.x += ${rKey}; dec.skipAny(); continue; };`); - while (utf8Arr.length > 0) { - if (utf8Arr.length >= 4) { - const word = utf8Arr.splice(0, 4); - const utf8Chunk = '0x' + word.map((x) => x.toString(16)).join(''); - codegen.js( - `if (r.u32() !== ${utf8Chunk}) { ${ - utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : '' - }dec.skipAny(); continue; }`, - ); - } else if (utf8Arr.length >= 2) { - const word = utf8Arr.splice(0, 2); - const utf8Chunk = '0x' + word.map((x) => x.toString(16)).join(''); - codegen.js( - `if (r.u16() !== ${utf8Chunk}) { ${ - utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : '' - }dec.skipAny(); continue; }`, - ); - } else { - const [octet] = utf8Arr.splice(0, 1); - codegen.js( - `if (r.u8() !== ${octet}) { ${ - utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : '' - }dec.skipAny(); continue; }`, - ); - } - } - codegen.js(`${rFound} = true;`); - codegen.js(`break;`); - codegen.js(`}`); - codegen.js(`if (!${rFound}) throw new Error('KEY_NOT_FOUND');`); - break; - } - case 'number': { - const rObj = codegen.getRegister(); - codegen.js(/* js */ `var ${rObj} = dec.readArrHdr();`); - codegen.js(/* js */ `if(${rObj} <= ${step}) throw new Error('INDEX_OUT_OF_BOUNDS');`); - for (let i = 0; i < step; i++) codegen.js(/* js */ `dec.skipAny();`); - break; - } - default: { - throw new Error('INVALID_PATH_STEP'); - } - } - } - - return codegen.compile(); -}; diff --git a/src/json-pack/msgpack/types.ts b/src/json-pack/msgpack/types.ts deleted file mode 100644 index 080434af3d..0000000000 --- a/src/json-pack/msgpack/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type {IWriter, IWriterGrowable} from '../../util/buffers'; - -export type MsgPack = Uint8Array & {__BRAND__: 'msgpack'; __TYPE__: T}; - -/** @deprecated */ -export interface IMessagePackEncoder { - writer: IWriter & IWriterGrowable; - encodeAny(value: unknown): void; - encodeNumber(num: number): void; - encodeString(str: string): void; - encodeArray(arr: unknown[]): void; - encodeArrayHeader(length: number): void; - encodeObject(obj: Record): void; - encodeObjectHeader(length: number): void; -} diff --git a/src/json-pack/msgpack/util.ts b/src/json-pack/msgpack/util.ts deleted file mode 100644 index eaaa9afce0..0000000000 --- a/src/json-pack/msgpack/util.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {MsgPackEncoderFast} from './MsgPackEncoderFast'; -import {MsgPackEncoder} from './MsgPackEncoder'; -import {MsgPackDecoderFast} from './MsgPackDecoderFast'; -import {MsgPack} from './types'; - -export const encoder = new MsgPackEncoderFast(); -export const encoderFull = new MsgPackEncoder(); -export const decoder = new MsgPackDecoderFast(); - -export const encode = (data: T): MsgPack => encoder.encode(data) as MsgPack; -export const encodeFull = (data: T): MsgPack => encoderFull.encode(data) as MsgPack; -export const decode = (blob: MsgPack): T => decoder.decode(blob) as T; - -export type {MsgPack}; diff --git a/src/json-pack/resp/README.md b/src/json-pack/resp/README.md deleted file mode 100644 index fe3e24eac9..0000000000 --- a/src/json-pack/resp/README.md +++ /dev/null @@ -1 +0,0 @@ -# RESP v2 and RESP3 codecs diff --git a/src/json-pack/resp/RespDecoder.ts b/src/json-pack/resp/RespDecoder.ts deleted file mode 100644 index 54cb42997a..0000000000 --- a/src/json-pack/resp/RespDecoder.ts +++ /dev/null @@ -1,422 +0,0 @@ -import {Reader} from '../../util/buffers/Reader'; -import {RESP} from './constants'; -import {RespAttributes, RespPush} from './extensions'; -import type {IReader, IReaderResettable} from '../../util/buffers'; -import type {BinaryJsonDecoder, PackValue} from '../types'; -import {isUtf8} from '../../util/buffers/utf8/isUtf8'; - -export class RespDecoder - implements BinaryJsonDecoder -{ - /** - * When set to true, the decoder will attempt to decode RESP Bulk strings - * (which are binary strings, i.e. Uint8Array) as UTF-8 strings. If the - * string is not valid UTF-8, it will be returned as a Uint8Array. - * - * You can toggle this setting at any time, before each call to `decode()` - * or `read()`, or other methods. - */ - public tryUtf8 = false; - - public constructor(public reader: R = new Reader() as any) {} - - public read(uint8: Uint8Array): PackValue { - this.reader.reset(uint8); - return this.val() as PackValue; - } - - /** @deprecated */ - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.val(); - } - - // -------------------------------------------------------- Any value reading - - public val(): unknown { - const reader = this.reader; - const type = reader.u8(); - switch (type) { - case RESP.INT: - return this.readInt(); - case RESP.FLOAT: - return this.readFloat(); - case RESP.STR_SIMPLE: - return this.readStrSimple(); - case RESP.STR_BULK: - return this.readStrBulk(); - case RESP.BOOL: - return this.readBool(); - case RESP.NULL: - return reader.skip(2), null; - case RESP.OBJ: - return this.readObj(); - case RESP.ARR: - return this.readArr(); - case RESP.STR_VERBATIM: - return this.readStrVerbatim(); - case RESP.PUSH: - return new RespPush(this.readArr() || []); - case RESP.BIG: - return this.readBigint(); - case RESP.SET: - return this.readSet(); - case RESP.ERR_SIMPLE: - return this.readErrSimple(); - case RESP.ERR_BULK: - return this.readErrBulk(); - case RESP.ATTR: - return new RespAttributes(this.readObj()); - } - throw new Error('UNKNOWN_TYPE'); - } - - protected readLength(): number { - const reader = this.reader; - let number: number = 0; - while (true) { - const c = reader.u8(); - if (c === RESP.R) return reader.skip(1), number; - number = number * 10 + (c - 48); - } - } - - public readCmd(): [cmd: string, ...args: Uint8Array[]] { - const reader = this.reader; - const type = reader.u8(); - if (type !== RESP.ARR) throw new Error('INVALID_COMMAND'); - const c = reader.peak(); - if (c === RESP.MINUS) throw new Error('INVALID_COMMAND'); - const length = this.readLength(); - if (length === 0) throw new Error('INVALID_COMMAND'); - const cmd = this.readAsciiAsStrBulk().toUpperCase(); - const args: [cmd: string, ...args: Uint8Array[]] = [cmd]; - this.tryUtf8 = false; - for (let i = 1; i < length; i++) { - const type = reader.u8(); - if (type !== RESP.STR_BULK) throw new Error('INVALID_COMMAND'); - args.push(this.readStrBulk() as Uint8Array); - } - return args; - } - - // ---------------------------------------------------------- Boolean reading - - public readBool(): boolean { - const reader = this.reader; - const c = reader.u8(); - reader.skip(2); // Skip "\r\n". - return c === 116; // t - } - - // ----------------------------------------------------------- Number reading - - public readInt(): number { - const reader = this.reader; - let negative = false; - let c = reader.u8(); - let number: number = 0; - if (c === RESP.MINUS) { - negative = true; - } else if (c !== RESP.PLUS) number = c - 48; - while (true) { - c = reader.u8(); - if (c === RESP.R) { - reader.skip(1); // Skip "\n". - return negative ? -number : number; - } - number = number * 10 + (c - 48); - } - } - - public readFloat(): number { - const reader = this.reader; - const x = reader.x; - while (true) { - const c = reader.u8(); - if (c !== RESP.R) continue; - const length = reader.x - x - 1; - reader.x = x; - const str = reader.ascii(length); - switch (length) { - case 3: - switch (str) { - case 'inf': - return reader.skip(2), Infinity; - case 'nan': - return reader.skip(2), NaN; - } - break; - case 4: - if (str === '-inf') { - return reader.skip(2), -Infinity; - } - break; - } - reader.skip(2); // Skip "\n". - return Number(str); - } - } - - public readBigint(): bigint { - const reader = this.reader; - const x = reader.x; - while (true) { - const c = reader.u8(); - if (c !== RESP.R) continue; - const length = reader.x - x; - reader.x = x; - const str = reader.ascii(length); - reader.skip(1); // Skip "\n". - return BigInt(str); - } - } - - // ----------------------------------------------------------- String reading - - public readStrSimple(): string { - const reader = this.reader; - const x = reader.x; - while (true) { - const c = reader.u8(); - if (c !== RESP.R) continue; - const size = reader.x - x - 1; - reader.x = x; - const str = reader.utf8(size); - reader.skip(2); // Skip "\r\n". - return str; - } - } - - public readStrBulk(): Uint8Array | string | null { - const reader = this.reader; - if (reader.peak() === RESP.MINUS) { - reader.skip(4); // Skip "-1\r\n". - return null; - } - const length = this.readLength(); - let res: Uint8Array | string; - if (this.tryUtf8 && isUtf8(reader.uint8, reader.x, length)) res = reader.utf8(length); - else res = reader.buf(length); - reader.skip(2); // Skip "\r\n". - return res; - } - - public readAsciiAsStrBulk(): string { - const reader = this.reader; - reader.skip(1); // Skip "$". - const length = this.readLength(); - const buf = reader.ascii(length); - reader.skip(2); // Skip "\r\n". - return buf; - } - - public readStrVerbatim(): string | Uint8Array { - const reader = this.reader; - const length = this.readLength(); - const u32 = reader.u32(); - const isTxt = u32 === 1954051130; // "txt:" - if (isTxt) { - const str = reader.utf8(length - 4); - reader.skip(2); // Skip "\r\n". - return str; - } - const buf = reader.buf(length - 4); - reader.skip(2); // Skip "\r\n". - return buf; - } - - // ------------------------------------------------------------ Error reading - - public readErrSimple(): Error { - const reader = this.reader; - const x = reader.x; - while (true) { - const c = reader.u8(); - if (c !== RESP.R) continue; - const size = reader.x - x - 1; - reader.x = x; - const str = reader.utf8(size); - reader.skip(2); // Skip "\r\n". - return new Error(str); - } - } - - public readErrBulk(): Error { - const reader = this.reader; - const length = this.readLength(); - const message = reader.utf8(length); - reader.skip(2); // Skip "\r\n". - return new Error(message); - } - - // ------------------------------------------------------------ Array reading - - public readArr(): unknown[] | null { - const reader = this.reader; - const c = reader.peak(); - if (c === RESP.MINUS) { - reader.skip(4); // Skip "-1\r\n". - return null; - } - const length = this.readLength(); - const arr: unknown[] = []; - for (let i = 0; i < length; i++) arr.push(this.val()); - return arr; - } - - public readSet(): Set { - const length = this.readLength(); - const set = new Set(); - for (let i = 0; i < length; i++) set.add(this.val()); - return set; - } - - // ----------------------------------------------------------- Object reading - - public readObj(): Record { - const length = this.readLength(); - const obj: Record = {}; - for (let i = 0; i < length; i++) { - const key = this.val() + ''; - obj[key] = this.val(); - } - return obj; - } - - // ----------------------------------------------------------------- Skipping - - public skipN(n: number): void { - for (let i = 0; i < n; i++) this.skipAny(); - } - - public skipAny(): void { - const reader = this.reader; - const type = reader.u8(); - switch (type) { - case RESP.INT: - return this.skipInt(); - case RESP.FLOAT: - return this.skipFloat(); - case RESP.STR_SIMPLE: - return this.skipStrSimple(); - case RESP.STR_BULK: - return this.skipStrBulk(); - case RESP.BOOL: - return this.skipBool(); - case RESP.NULL: - return reader.skip(2); - case RESP.OBJ: - return this.skipObj(); - case RESP.ARR: - return this.skipArr(); - case RESP.STR_VERBATIM: - return this.skipStrVerbatim(); - case RESP.PUSH: - return this.skipArr(); - case RESP.BIG: - return this.skipBigint(); - case RESP.SET: - return this.skipSet(); - case RESP.ERR_SIMPLE: - return this.skipErrSimple(); - case RESP.ERR_BULK: - return this.skipErrBulk(); - case RESP.ATTR: - return this.skipObj(); - } - throw new Error('UNKNOWN_TYPE'); - } - - public skipBool(): void { - this.reader.skip(3); - } - - public skipInt(): void { - const reader = this.reader; - while (true) { - if (reader.u8() !== RESP.R) continue; - reader.skip(1); // Skip "\n". - return; - } - } - - public skipFloat(): void { - const reader = this.reader; - while (true) { - if (reader.u8() !== RESP.R) continue; - reader.skip(1); // Skip "\n". - return; - } - } - - public skipBigint(): void { - const reader = this.reader; - while (true) { - if (reader.u8() !== RESP.R) continue; - reader.skip(1); // Skip "\n". - return; - } - } - - public skipStrSimple(): void { - const reader = this.reader; - while (true) { - if (reader.u8() !== RESP.R) continue; - reader.skip(1); // Skip "\n". - return; - } - } - - public skipStrBulk(): void { - const reader = this.reader; - if (reader.peak() === RESP.MINUS) { - reader.skip(4); // Skip "-1\r\n". - return; - } - reader.skip(this.readLength() + 2); // Skip "\r\n". - } - - public skipStrVerbatim(): void { - const length = this.readLength(); - this.reader.skip(length + 2); // Skip "\r\n". - } - - public skipErrSimple(): void { - const reader = this.reader; - while (true) { - if (reader.u8() !== RESP.R) continue; - reader.skip(1); // Skip "\n". - return; - } - } - - public skipErrBulk(): void { - const length = this.readLength(); - this.reader.skip(length + 2); // Skip "\r\n". - } - - public skipArr(): void { - const reader = this.reader; - const c = reader.peak(); - if (c === RESP.MINUS) { - reader.skip(4); // Skip "-1\r\n". - return; - } - const length = this.readLength(); - for (let i = 0; i < length; i++) this.skipAny(); - } - - public skipSet(): void { - const length = this.readLength(); - for (let i = 0; i < length; i++) this.skipAny(); - } - - public skipObj(): void { - const length = this.readLength(); - for (let i = 0; i < length; i++) { - this.skipAny(); - this.skipAny(); - } - } -} diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts deleted file mode 100644 index b534c2ac29..0000000000 --- a/src/json-pack/resp/RespEncoder.ts +++ /dev/null @@ -1,503 +0,0 @@ -import {Writer} from '../../util/buffers/Writer'; -import {RESP} from './constants'; -import {utf8Size} from '../../util/strings/utf8'; -import {RespAttributes, RespPush, RespVerbatimString} from './extensions'; -import {JsonPackExtension} from '../JsonPackExtension'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder, StreamingBinaryJsonEncoder, TlvBinaryJsonEncoder} from '../types'; -import type {Slice} from '../../util/buffers/Slice'; - -const REG_RN = /[\r\n]/; -const isSafeInteger = Number.isSafeInteger; - -/** - * Implements RESP3 encoding. - */ -export class RespEncoder - implements BinaryJsonEncoder, StreamingBinaryJsonEncoder, TlvBinaryJsonEncoder -{ - constructor(public readonly writer: W = new Writer() as any) {} - - public encode(value: unknown): Uint8Array { - this.writeAny(value); - return this.writer.flush(); - } - - public encodeToSlice(value: unknown): Slice { - this.writeAny(value); - return this.writer.flushSlice(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'boolean': - return this.writeBoolean(value); - case 'object': { - if (!value) return this.writeNull(); - if (value instanceof Array) return this.writeArr(value); - if (value instanceof Uint8Array) return this.writeBin(value); - if (value instanceof Error) return this.writeErr(value.message); - if (value instanceof Set) return this.writeSet(value); - if (value instanceof JsonPackExtension) { - if (value instanceof RespPush) return this.writePush(value.val); - if (value instanceof RespVerbatimString) return this.writeVerbatimStr('txt', value.val); - if (value instanceof RespAttributes) return this.writeAttr(value.val); - } - return this.writeObj(value as Record); - } - case 'undefined': - return this.writeUndef(); - case 'bigint': - return this.writeBigInt(value); - default: - return this.writeUnknown(value); - } - } - - protected writeLength(length: number): void { - const writer = this.writer; - if (length < 100) { - if (length < 10) { - writer.u8(length + 48); - return; - } - const octet1 = length % 10; - const octet2 = (length - octet1) / 10; - writer.u16(((octet2 + 48) << 8) + octet1 + 48); - return; - } - let digits = 1; - let pow = 10; - while (length >= pow) { - digits++; - pow *= 10; - } - writer.ensureCapacity(digits); - const uint8 = writer.uint8; - const x = writer.x; - const newX = x + digits; - let i = newX - 1; - while (i >= x) { - const remainder = length % 10; - uint8[i--] = remainder + 48; - length = (length - remainder) / 10; - } - writer.x = newX; - } - - public encodeCmd(args: unknown[]): Uint8Array { - this.writeCmd(args); - return this.writer.flush(); - } - - public writeCmd(args: unknown[]): void { - const length = args.length; - this.writeArrHdr(length); - for (let i = 0; i < length; i++) { - const arg = args[i]; - if (arg instanceof Uint8Array) this.writeBin(arg); - else this.writeBulkStrAscii(arg + ''); - } - } - - public encodeCmdUtf8(args: unknown[]): Uint8Array { - this.writeCmdUtf8(args); - return this.writer.flush(); - } - - public writeCmdUtf8(args: unknown[]): void { - const length = args.length; - this.writeArrHdr(length); - for (let i = 0; i < length; i++) this.writeArgUtf8(args[i]); - } - - public writeArgUtf8(arg: unknown): void { - if (arg instanceof Uint8Array) return this.writeBin(arg); - else this.writeBulkStr(arg + ''); - } - - public writeNull(): void { - this.writer.u8u16( - RESP.NULL, // _ - RESP.RN, // \r\n - ); - } - - public writeNullStr(): void { - this.writer.u8u32( - RESP.STR_BULK, // $ - 45 * 0x1000000 + // - - 49 * 0x10000 + // 1 - RESP.RN, // \r\n - ); - } - - public writeNullArr(): void { - this.writer.u8u32( - RESP.ARR, // * - 45 * 0x1000000 + // - - 49 * 0x10000 + // 1 - RESP.RN, // \r\n - ); - } - - public writeBoolean(bool: boolean): void { - this.writer.u32( - bool - ? RESP.BOOL * 0x1000000 + // # - 116 * 0x10000 + // t - RESP.RN // \r\n - : RESP.BOOL * 0x1000000 + // # - 102 * 0x10000 + // f - RESP.RN, // \r\n - ); - } - - public writeNumber(num: number): void { - if (isSafeInteger(num)) this.writeInteger(num); - else if (typeof num === 'bigint') this.writeBigInt(num); - else this.writeFloat(num); - } - - public writeBigInt(int: bigint): void { - const writer = this.writer; - writer.u8(RESP.BIG); // ( - writer.ascii(int + ''); - writer.u16(RESP.RN); // \r\n - } - - public writeInteger(int: number): void { - const writer = this.writer; - writer.u8(RESP.INT); // : - writer.ascii(int + ''); - writer.u16(RESP.RN); // \r\n - } - - public writeUInteger(uint: number): void { - this.writeInteger(uint); - } - - public writeFloat(float: number): void { - const writer = this.writer; - writer.u8(RESP.FLOAT); // , - switch (float) { - case Infinity: - writer.u8u16( - 105, // i - (110 << 8) | // n - 102, // f - ); - break; - case -Infinity: - writer.u32( - (45 * 0x1000000 + // - - 105 * 0x10000 + // i - (110 << 8)) | // n - 102, // f - ); - break; - default: - if (float !== float) - writer.u8u16( - 110, // n - (97 << 8) | // a - 110, // n - ); - else writer.ascii(float + ''); - break; - } - writer.u16(RESP.RN); // \r\n - } - - public writeBin(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.u8(RESP.STR_BULK); // $ - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - writer.buf(buf, length); - writer.u16(RESP.RN); // \r\n - } - - public writeBinHdr(length: number): void { - throw new Error('Not implemented'); - // Because then we also need `.writeBinBody()` which would emit trailing `\r\n`. - } - - public writeStr(str: string): void { - const length = str.length; - if (length < 64 && !REG_RN.test(str)) this.writeSimpleStr(str); - else this.writeVerbatimStr('txt', str); - } - - public writeStrHdr(length: number): void { - throw new Error('Not implemented'); - // Because then we also need `.writeBinBody()` which would emit trailing `\r\n`. - } - - public writeSimpleStr(str: string): void { - const writer = this.writer; - writer.u8(RESP.STR_SIMPLE); // + - writer.ensureCapacity(str.length << 2); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeSimpleStrAscii(str: string): void { - const writer = this.writer; - writer.u8(RESP.STR_SIMPLE); // + - writer.ascii(str); - writer.u16(RESP.RN); // \r\n - } - - public writeBulkStr(str: string): void { - const writer = this.writer; - const size = utf8Size(str); - writer.u8(RESP.STR_BULK); // $ - this.writeLength(size); - writer.u16(RESP.RN); // \r\n - writer.ensureCapacity(size); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeBulkStrAscii(str: string): void { - const writer = this.writer; - writer.u8(RESP.STR_BULK); // $ - this.writeLength(str.length); - writer.u16(RESP.RN); // \r\n - writer.ascii(str); - writer.u16(RESP.RN); // \r\n - } - - public writeAsciiStr(str: string): void { - const isSimple = !REG_RN.test(str); - if (isSimple) this.writeSimpleStr(str); - else this.writeBulkStrAscii(str); - } - - public writeVerbatimStr(encoding: string, str: string): void { - const writer = this.writer; - const size = utf8Size(str); - writer.u8(RESP.STR_VERBATIM); // = - this.writeLength(size + 4); - writer.u16(RESP.RN); // \r\n - writer.u32( - encoding.charCodeAt(0) * 0x1000000 + // t - (encoding.charCodeAt(1) << 16) + // x - (encoding.charCodeAt(2) << 8) + // t - 58, // : - ); - writer.ensureCapacity(size); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeErr(str: string): void { - if (str.length < 64 && !REG_RN.test(str)) this.writeSimpleErr(str); - else this.writeBulkErr(str); - } - - public writeSimpleErr(str: string): void { - const writer = this.writer; - writer.u8(RESP.ERR_SIMPLE); // - - writer.ensureCapacity(str.length << 2); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeBulkErr(str: string): void { - const writer = this.writer; - const size = utf8Size(str); - writer.u8(RESP.ERR_BULK); // ! - this.writeLength(size); - writer.u16(RESP.RN); // \r\n - writer.ensureCapacity(size); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeArr(arr: unknown[]): void { - const writer = this.writer; - const length = arr.length; - writer.u8(RESP.ARR); // * - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - } - - public writeArrHdr(length: number): void { - const writer = this.writer; - writer.u8(RESP.ARR); // * - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - } - - public writeObj(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - const length = keys.length; - writer.u8(RESP.OBJ); // % - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } - - public writeObjHdr(length: number): void { - const writer = this.writer; - writer.u8(RESP.OBJ); // % - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - } - - public writeAttr(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - const length = keys.length; - writer.u8(RESP.ATTR); // | - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - this.writeAny(obj[key]); - } - } - - public writeSet(set: Set): void { - const writer = this.writer; - const length = set.size; - writer.u8(RESP.SET); // ~ - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) set.forEach((value) => this.writeAny(value)); - } - - public writePush(elements: unknown[]): void { - const writer = this.writer; - const length = elements.length; - writer.u8(RESP.PUSH); // > - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) this.writeAny(elements[i]); - } - - /** - * Called when the encoder encounters a value that it does not know how to encode. - * - * @param value Some JavaScript value. - */ - public writeUnknown(value: unknown): void { - this.writeNull(); - } - - public writeUndef(): void { - this.writeNull(); - } - - protected writeRn(): void { - this.writer.u16(RESP.RN); // \r\n - } - - // ---------------------------------------------------------- Stream encoding - - public writeStartStr(): void { - this.writer.u32( - RESP.STR_BULK * 0x1000000 + // $ - (63 << 16) + // ? - RESP.RN, // \r\n - ); - } - - public writeStrChunk(str: string): void { - const writer = this.writer; - writer.u8(59); // ; - const size = utf8Size(str); - this.writeLength(size); - writer.u16(RESP.RN); // \r\n - writer.ensureCapacity(size); - writer.utf8(str); - writer.u16(RESP.RN); // \r\n - } - - public writeEndStr(): void { - this.writer.u32( - 59 * 0x1000000 + // ; - (48 << 16) + // 0 - RESP.RN, // \r\n - ); - } - - public writeStartBin(): void { - this.writer.u32( - 36 * 0x1000000 + // $ - (63 << 16) + // ? - RESP.RN, // \r\n - ); - } - - public writeBinChunk(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.u8(59); // ; - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - writer.buf(buf, length); - writer.u16(RESP.RN); // \r\n - } - - public writeEndBin(): void { - this.writer.u32( - 59 * 0x1000000 + // ; - (48 << 16) + // 0 - RESP.RN, // \r\n - ); - } - - public writeStartArr(): void { - this.writer.u32( - RESP.ARR * 0x1000000 + // * - (63 << 16) + // ? - RESP.RN, // \r\n - ); - } - - public writeArrChunk(item: unknown): void { - this.writeAny(item); - } - - public writeEndArr(): void { - this.writer.u8u16( - 46, // . - RESP.RN, // \r\n - ); - } - - public writeStartObj(): void { - this.writer.u32( - 37 * 0x1000000 + // % - (63 << 16) + // ? - RESP.RN, // \r\n - ); - } - - public writeObjChunk(key: string, value: unknown): void { - this.writeStr(key); - this.writeAny(value); - } - - public writeEndObj(): void { - this.writer.u8u16( - 46, // . - RESP.RN, // \r\n - ); - } -} diff --git a/src/json-pack/resp/RespEncoderLegacy.ts b/src/json-pack/resp/RespEncoderLegacy.ts deleted file mode 100644 index 81e848dbef..0000000000 --- a/src/json-pack/resp/RespEncoderLegacy.ts +++ /dev/null @@ -1,96 +0,0 @@ -import {RESP} from './constants'; -import {RespAttributes, RespPush, RespVerbatimString} from './extensions'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {RespEncoder} from './RespEncoder'; -import type {IWriter, IWriterGrowable} from '../../util/buffers'; - -const REG_RN = /[\r\n]/; -const isSafeInteger = Number.isSafeInteger; - -/** - * Implements RESP v2 encoding. - */ -export class RespEncoderLegacy extends RespEncoder { - public writeAny(value: unknown): void { - switch (typeof value) { - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'boolean': - return this.writeSimpleStr(value ? 'TRUE' : 'FALSE'); - case 'object': { - if (!value) return this.writeNull(); - if (value instanceof Array) return this.writeArr(value); - if (value instanceof Uint8Array) return this.writeBin(value); - if (value instanceof Error) return this.writeErr(value.message); - if (value instanceof Set) return this.writeSet(value); - if (value instanceof JsonPackExtension) { - if (value instanceof RespPush) return this.writeArr(value.val); - if (value instanceof RespVerbatimString) return this.writeStr(value.val); - if (value instanceof RespAttributes) return this.writeObj(value.val); - } - return this.writeObj(value as Record); - } - case 'undefined': - return this.writeUndef(); - case 'bigint': - return this.writeSimpleStrAscii(value + ''); - default: - return this.writeUnknown(value); - } - } - - public writeNumber(num: number): void { - if (isSafeInteger(num)) this.writeInteger(num); - else this.writeSimpleStrAscii(num + ''); - } - - public writeStr(str: string): void { - const length = str.length; - if (length < 64 && !REG_RN.test(str)) this.writeSimpleStr(str); - else this.writeBulkStr(str); - } - - public writeNull(): void { - this.writeNullArr(); - } - - public writeErr(str: string): void { - if (str.length < 64 && !REG_RN.test(str)) this.writeSimpleErr(str); - else this.writeBulkStr(str); - } - - public writeSet(set: Set): void { - this.writeArr([...set]); - } - - public writeArr(arr: unknown[]): void { - const writer = this.writer; - const length = arr.length; - writer.u8(RESP.ARR); // * - this.writeLength(length); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) { - const val = arr[i]; - if (val === null) this.writeNullStr(); - else this.writeAny(val); - } - } - - public writeObj(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - const length = keys.length; - writer.u8(RESP.ARR); // % - this.writeLength(length << 1); - writer.u16(RESP.RN); // \r\n - for (let i = 0; i < length; i++) { - const key = keys[i]; - this.writeStr(key); - const val = obj[key]; - if (val === null) this.writeNullStr(); - else this.writeAny(val); - } - } -} diff --git a/src/json-pack/resp/RespStreamingDecoder.ts b/src/json-pack/resp/RespStreamingDecoder.ts deleted file mode 100644 index c9116f2dce..0000000000 --- a/src/json-pack/resp/RespStreamingDecoder.ts +++ /dev/null @@ -1,114 +0,0 @@ -import {StreamingReader} from '../../util/buffers/StreamingReader'; -import {RespDecoder} from './RespDecoder'; - -/** - * Streaming decoder for RESP protocol. Can be used to decode data from - * a stream where messages are arbitrary split into chunks. - * - * Example: - * - * ```ts - * const decoder = new RespStreamingDecoder(); - * - * decoder.push(new Uint8Array([43, 49, 13, 10])); - * - * let val; - * while ((val = decoder.read()) !== undefined) { - * console.log(val); - * } - * ``` - */ -export class RespStreamingDecoder { - protected readonly reader = new StreamingReader(); - protected readonly decoder = new RespDecoder(this.reader); - - /** - * When set to true, the decoder will attempt to decode RESP Bulk strings - * (which are binary strings, i.e. Uint8Array) as UTF-8 strings. If the - * string is not valid UTF-8, it will be returned as a Uint8Array. - */ - public get tryUtf8(): boolean { - return this.decoder.tryUtf8; - } - public set tryUtf8(value: boolean) { - this.decoder.tryUtf8 = value; - } - - /** - * Add a chunk of data to be decoded. - * @param uint8 `Uint8Array` chunk of data to be decoded. - */ - public push(uint8: Uint8Array): void { - this.reader.push(uint8); - } - - /** - * Decode one value from the stream. If `undefined` is returned, then - * there is not enough data to decode or the stream is finished. - * - * There could be multiple values in the stream, so this method should be - * called in a loop until `undefined` is returned. - * - * @return Decoded value or `undefined` if there is not enough data to decode. - */ - public read(): unknown | undefined { - const reader = this.reader; - if (reader.size() === 0) return undefined; - const x = reader.x; - try { - const val = this.decoder.val(); - reader.consume(); - return val; - } catch (error) { - if (error instanceof RangeError) { - reader.x = x; - return undefined; - } else throw error; - } - } - - /** - * Decode only one RESP command from the stream, if the value is not a - * command, an error will be thrown. - * - * @returns Redis command and its arguments or `undefined` if there is - * not enough data to decode. - */ - public readCmd(): [cmd: string, ...args: Uint8Array[]] | undefined { - const reader = this.reader; - if (reader.size() === 0) return undefined; - const x = reader.x; - try { - const args = this.decoder.readCmd(); - reader.consume(); - return args; - } catch (error) { - if (error instanceof RangeError) { - reader.x = x; - return undefined; - } else throw error; - } - } - - /** - * Skips one value from the stream. If `undefined` is returned, then - * there is not enough data to skip or the stream is finished. - * @returns `null` if a value was skipped, `undefined` if there is not - * enough data to skip. - */ - public skip(): null | undefined { - const reader = this.reader; - if (reader.size() === 0) return undefined; - const x = reader.x; - try { - this.decoder.skipAny(); - reader.consume(); - return null; - } catch (error) { - if (error instanceof RangeError) { - reader.x = x; - return undefined; - } else throw error; - } - } -} diff --git a/src/json-pack/resp/__tests__/RespDecoder.spec.ts b/src/json-pack/resp/__tests__/RespDecoder.spec.ts deleted file mode 100644 index ab192c83b8..0000000000 --- a/src/json-pack/resp/__tests__/RespDecoder.spec.ts +++ /dev/null @@ -1,235 +0,0 @@ -import {RespEncoder} from '../RespEncoder'; -import {RespDecoder} from '../RespDecoder'; -import {bufferToUint8Array} from '../../../util/buffers/bufferToUint8Array'; -import {RespAttributes, RespPush} from '../extensions'; -import {Writer} from '../../../util/buffers/Writer'; -import {utf8} from '../../../util/buffers/strings'; - -const decode = (encoded: string | Uint8Array): unknown => { - const decoder = new RespDecoder(); - const buf = typeof encoded === 'string' ? bufferToUint8Array(Buffer.from(encoded)) : encoded; - const decoded = decoder.read(buf); - return decoded; -}; - -const encoder = new RespEncoder(new Writer(4)); -const assertCodec = (value: unknown, expected: unknown = value): void => { - const encoded = encoder.encode(value); - // console.log(Buffer.from(encoded).toString()); - const decoded = decode(encoded); - expect(decoded).toStrictEqual(expected); - const encoded2 = encoder.encode(value); - const decoded2 = decode(encoded2); - expect(decoded2).toStrictEqual(expected); -}; - -describe('nulls', () => { - test('null', () => { - assertCodec(null); - }); -}); - -describe('booleans', () => { - test('true', () => { - assertCodec(true); - }); - - test('false', () => { - assertCodec(false); - }); -}); - -describe('integers', () => { - test('zero', () => assertCodec(0)); - test('positive', () => assertCodec(123)); - test('negative', () => assertCodec(-2348934)); - test('positive with leading "+"', () => { - const decoded = decode(':+123\r\n'); - expect(decoded).toBe(123); - }); -}); - -describe('big ints', () => { - test('zero', () => assertCodec(BigInt('0'))); - test('positive', () => assertCodec(BigInt('123'))); - test('negative', () => assertCodec(BigInt('-2348934'))); - test('positive with leading "+"', () => { - const decoded = decode('(+123\r\n'); - expect(decoded).toEqual(BigInt('123')); - }); -}); - -describe('floats', () => { - test('positive', () => assertCodec([1.123])); - test('negative', () => assertCodec([-43.234435])); - test('negative', () => assertCodec([-5445e-10])); - test('negative', () => assertCodec([-5445e-20])); - test('negative', () => assertCodec([-5445e-30])); - test('inf', () => assertCodec([Infinity])); - test('-inf', () => assertCodec([-Infinity])); - test('nan', () => assertCodec([NaN])); - - test('decodes ",inf"', () => { - const decoded = decode(',inf\r\n'); - expect(decoded).toEqual(Infinity); - }); - - test('decodes ",-inf"', () => { - const decoded = decode(',-inf\r\n'); - expect(decoded).toEqual(-Infinity); - }); - - test('decodes ",nan"', () => { - const decoded = decode(',nan\r\n'); - expect(decoded).toEqual(NaN); - }); -}); - -const stringCases: [string, string][] = [ - ['empty string', ''], - ['short string', 'foo bar'], - ['short string with emoji', 'foo bar๐Ÿผ'], - ['short string with emoji and newline', 'foo bar\n๐Ÿผ'], - ['simple string with newline', 'foo\nbar'], -]; - -describe('strings', () => { - for (const [name, value] of stringCases) { - test(name, () => assertCodec(value)); - } - - describe('verbatim strings', () => { - test('example from docs', () => { - const encoded = '=15\r\ntxt:Some string\r\n'; - const decoded = decode(encoded); - expect(decoded).toBe('Some string'); - }); - }); -}); - -describe('binary', () => { - test('empty blob', () => assertCodec(new Uint8Array(0))); - test('small blob', () => assertCodec(new Uint8Array([1, 2, 3]))); - test('blob with new lines', () => assertCodec(new Uint8Array([1, 2, 3, 10, 13, 14, 64, 65]))); -}); - -describe('errors', () => { - for (const [name, value] of stringCases) { - test(name, () => assertCodec(new Error(value))); - } -}); - -const arrays: [string, unknown[]][] = [ - ['empty array', []], - ['simple array', [1, 2, 3]], - ['with strings', ['foo', 'bar']], - ['nested', [[]]], - ['surrounded by special strings', ['a\n', 'b๐Ÿ˜ฑ', [0, -1, 1], '\nasdf\r\n\r๐Ÿ’ช\nadsf']], -]; - -describe('arrays', () => { - for (const [name, value] of arrays) test(name, () => assertCodec(value)); -}); - -describe('sets', () => { - for (const [name, value] of arrays) test(name, () => assertCodec(new Set(value))); -}); - -describe('pushes', () => { - for (const [name, value] of arrays) test(name, () => assertCodec(new RespPush(value))); -}); - -const maps: [string, Record][] = [ - ['empty map', {}], - ['simple map', {foo: 'bar'}], - ['multiple keys', {foo: 'bar', baz: 'qux'}], - ['nested', {foo: {bar: 'baz'}}], - ['surrounded by special strings', {foo: 'bar', baz: 'qux', quux: ['a\n', 'b๐Ÿ˜ฑ', [0, -1, 1], '\nasdf\r\n\r๐Ÿ’ช\nadsf']}], - ['fuzzer 1', {a: 'b', 'a*|Avi5:7%7El': false}], - [ - 'fuzzer 2', - { - 'u.qSvG-7#j0tp1Z': [ - 'Mk9|s2<[-$k2sEq', - '.YyA', - ',g:V5el?o1', - ['/-=gfBa7@r'], - null, - 'x0"', - 899663861.7189225, - ['-yM}#tH>Z|0', '?x4c-M', 'V`Wjk', 962664739.7776917, 541764469.8786258], - 39815384.70374191, - '%J,TE6', - 867117612.5557965, - 432039764.7694767, - {'&3qo`uOc@]7c': -1199425724646684, '(3': 98978664.1896191}, - 941209461.4820778, - 444029027.33100927, - ], - ':xwsOx[u0:\\,': 116172902.03305908, - '=Em$Bo+t4': 118717435.20500576, - 'D3 hvV+uBsY^0': ' Mr!`Pjno;ME_', - 'l\\Wv1bs': null, - F: 175071663.912447, - 's-o}fQO2e': null, - 'K!q]': 'LBm,GEw,`BpQxIq', - "(:'-g`;x": 'r\\?K;AZWT1S:w0_-', - }, - ], -]; - -describe('objects', () => { - for (const [name, value] of maps) test(name, () => assertCodec(value)); - - describe('when .tryUtf8 = true', () => { - test('parses bulk strings as UTF8 strings', () => { - const encoded = '%1\r\n$3\r\nfoo\r\n$3\r\nbar\r\n'; - const decoder = new RespDecoder(); - decoder.tryUtf8 = true; - const decoded = decoder.read(Buffer.from(encoded)); - expect(decoded).toStrictEqual({foo: 'bar'}); - }); - - test('parses invalid UTF8 as Uint8Array', () => { - const encoded = encoder.encode({foo: new Uint8Array([0xc3, 0x28])}); - const decoder = new RespDecoder(); - decoder.tryUtf8 = true; - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual({foo: new Uint8Array([0xc3, 0x28])}); - }); - }); -}); - -describe('attributes', () => { - for (const [name, value] of maps) test(name, () => assertCodec(new RespAttributes(value))); -}); - -describe('nulls', () => { - test('can decode string null', () => { - const decoded = decode('$-1\r\n'); - expect(decoded).toBe(null); - }); - - test('can decode array null', () => { - const decoded = decode('*-1\r\n'); - expect(decoded).toBe(null); - }); -}); - -describe('commands', () => { - test('can decode a PING command', () => { - const encoded = encoder.encodeCmd(['PING']); - const decoder = new RespDecoder(); - decoder.reader.reset(encoded); - const decoded = decoder.readCmd(); - expect(decoded).toEqual(['PING']); - }); - - test('can decode a SET command', () => { - const encoded = encoder.encodeCmd(['SET', 'foo', 'bar']); - const decoder = new RespDecoder(); - decoder.reader.reset(encoded); - const decoded = decoder.readCmd(); - expect(decoded).toEqual(['SET', utf8`foo`, utf8`bar`]); - }); -}); diff --git a/src/json-pack/resp/__tests__/RespEncoder.spec.ts b/src/json-pack/resp/__tests__/RespEncoder.spec.ts deleted file mode 100644 index 9a1b1b306c..0000000000 --- a/src/json-pack/resp/__tests__/RespEncoder.spec.ts +++ /dev/null @@ -1,355 +0,0 @@ -import {bufferToUint8Array} from '../../../util/buffers/bufferToUint8Array'; -import {RespEncoder} from '../RespEncoder'; -import {RespVerbatimString} from '../extensions'; -const Parser = require('redis-parser'); - -const parse = (uint8: Uint8Array): unknown => { - let result: unknown; - const parser = new Parser({ - returnReply(reply: any, b: any, c: any) { - result = reply; - }, - returnError(err: any) { - result = err; - }, - returnFatalError(err: any) { - result = err; - }, - returnBuffers: false, - stringNumbers: false, - }); - parser.execute(Buffer.from(uint8)); - return result; -}; - -const toStr = (uint8: Uint8Array): string => { - return Buffer.from(uint8).toString(); -}; - -describe('strings', () => { - describe('.writeSimpleStr()', () => { - test('empty string', () => { - const encoder = new RespEncoder(); - encoder.writeSimpleStr(''); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('+\r\n'); - expect(parse(encoded)).toBe(''); - }); - - test('short string', () => { - const encoder = new RespEncoder(); - encoder.writeSimpleStr('abc!'); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('+abc!\r\n'); - expect(parse(encoded)).toBe('abc!'); - }); - }); - - describe('.writeBulkStr()', () => { - test('empty string', () => { - const encoder = new RespEncoder(); - encoder.writeBulkStr(''); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('$0\r\n\r\n'); - expect(parse(encoded)).toBe(''); - }); - - test('short string', () => { - const encoder = new RespEncoder(); - encoder.writeBulkStr('abc!'); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('$4\r\nabc!\r\n'); - expect(parse(encoded)).toBe('abc!'); - }); - }); - - describe('.writeVerbatimStr()', () => { - test('empty string', () => { - const encoder = new RespEncoder(); - encoder.writeVerbatimStr('txt', ''); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('=4\r\ntxt:\r\n'); - }); - - test('short string', () => { - const encoder = new RespEncoder(); - encoder.writeVerbatimStr('txt', 'asdf'); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('=8\r\ntxt:asdf\r\n'); - }); - - test('can encode verbatim string using RespVerbatimString', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new RespVerbatimString('asdf')); - expect(toStr(encoded)).toBe('=8\r\ntxt:asdf\r\n'); - }); - }); -}); - -describe('binary', () => { - test('empty blob', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Uint8Array(0)); - expect(toStr(encoded)).toBe('$0\r\n\r\n'); - expect(parse(encoded)).toBe(''); - }); - - test('small blob', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Uint8Array([65, 66])); - expect(toStr(encoded)).toBe('$2\r\nAB\r\n'); - expect(parse(encoded)).toBe('AB'); - }); -}); - -describe('.writeAsciiString()', () => { - test('can write "OK"', () => { - const encoder = new RespEncoder(); - encoder.writeAsciiStr('OK'); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('+OK\r\n'); - expect(parse(encoded)).toBe('OK'); - }); -}); - -describe('errors', () => { - test('can encode simple error', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Error('ERR')); - expect(toStr(encoded)).toBe('-ERR\r\n'); - expect(parse(encoded)).toBeInstanceOf(Error); - expect((parse(encoded) as any).message).toBe('ERR'); - }); - - test('can encode bulk error', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Error('a\nb')); - expect(toStr(encoded)).toBe('!3\r\na\nb\r\n'); - expect(parse(encoded)).toBeInstanceOf(Error); - }); -}); - -describe('integers', () => { - test('zero', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(0); - expect(toStr(encoded)).toBe(':0\r\n'); - expect(parse(encoded)).toBe(0); - }); - - test('positive integer', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(23423432543); - expect(toStr(encoded)).toBe(':23423432543\r\n'); - expect(parse(encoded)).toBe(23423432543); - }); - - test('negative integer', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(-11111111); - expect(toStr(encoded)).toBe(':-11111111\r\n'); - expect(parse(encoded)).toBe(-11111111); - }); -}); - -describe('arrays', () => { - test('empty array', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode([]); - expect(toStr(encoded)).toBe('*0\r\n'); - expect(parse(encoded)).toEqual([]); - }); - - test('array of numbers', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode([1, 2, 3]); - expect(toStr(encoded)).toBe('*3\r\n:1\r\n:2\r\n:3\r\n'); - expect(parse(encoded)).toEqual([1, 2, 3]); - }); - - test('array of strings and numbers', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode([1, 'abc', 3]); - expect(toStr(encoded)).toBe('*3\r\n:1\r\n+abc\r\n:3\r\n'); - expect(parse(encoded)).toEqual([1, 'abc', 3]); - }); -}); - -describe('nulls', () => { - test('a single null', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(null); - expect(toStr(encoded)).toBe('_\r\n'); - }); - - test('null in array', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode([1, 2, null]); - expect(toStr(encoded)).toBe('*3\r\n:1\r\n:2\r\n_\r\n'); - }); - - test('string null', () => { - const encoder = new RespEncoder(); - encoder.writeNullStr(); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('$-1\r\n'); - expect(parse(encoded)).toEqual(null); - }); - - test('array null', () => { - const encoder = new RespEncoder(); - encoder.writeNullArr(); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('*-1\r\n'); - expect(parse(encoded)).toEqual(null); - }); -}); - -describe('booleans', () => { - test('true', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(true); - expect(toStr(encoded)).toBe('#t\r\n'); - }); - - test('false', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(false); - expect(toStr(encoded)).toBe('#f\r\n'); - }); -}); - -describe('doubles', () => { - test('1.2', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(1.2); - expect(toStr(encoded)).toBe(',1.2\r\n'); - }); -}); - -describe('big numbers', () => { - test('12345678901234567890', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(BigInt('12345678901234567890')); - expect(toStr(encoded)).toBe('(12345678901234567890\r\n'); - }); -}); - -describe('objects', () => { - test('empty object', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode({}); - expect(toStr(encoded)).toBe('%0\r\n'); - }); - - test('simple object', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode({foo: 123}); - expect(toStr(encoded)).toBe('%1\r\n+foo\r\n:123\r\n'); - }); -}); - -describe('attributes', () => { - test('empty attributes', () => { - const encoder = new RespEncoder(); - encoder.writeAttr({}); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('|0\r\n'); - }); - - test('simple object', () => { - const encoder = new RespEncoder(); - encoder.writeAttr({foo: 123}); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('|1\r\n+foo\r\n:123\r\n'); - }); -}); - -describe('sets', () => { - test('empty set', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Set()); - expect(toStr(encoded)).toBe('~0\r\n'); - }); - - test('array of numbers', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encode(new Set([1])); - expect(toStr(encoded)).toBe('~1\r\n:1\r\n'); - }); -}); - -describe('pushes', () => { - test('empty push', () => { - const encoder = new RespEncoder(); - encoder.writePush([]); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('>0\r\n'); - }); - - test('two elements', () => { - const encoder = new RespEncoder(); - encoder.writePush([1, 32]); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('>2\r\n:1\r\n:32\r\n'); - }); -}); - -describe('streaming data', () => { - describe('strings', () => { - test('can write a streaming string', () => { - const encoder = new RespEncoder(); - encoder.writeStartStr(); - encoder.writeStrChunk('abc'); - encoder.writeStrChunk('def'); - encoder.writeEndStr(); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('$?\r\n;3\r\nabc\r\n;3\r\ndef\r\n;0\r\n'); - }); - }); - - describe('binary', () => { - test('can write a streaming binary', () => { - const encoder = new RespEncoder(); - encoder.writeStartBin(); - encoder.writeBinChunk(new Uint8Array([65])); - encoder.writeBinChunk(new Uint8Array([66])); - encoder.writeEndBin(); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('$?\r\n;1\r\nA\r\n;1\r\nB\r\n;0\r\n'); - }); - }); -}); - -describe('commands', () => { - describe('.writeCmd()', () => { - test('can encode a simple command', () => { - const encoder = new RespEncoder(); - encoder.writeCmd(['SET', 'foo', 'bar']); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n'); - }); - - test('casts numbers to strings', () => { - const encoder = new RespEncoder(); - encoder.writeCmd(['SET', 'foo', 123]); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\n123\r\n'); - }); - - test('can encode Uint8Array', () => { - const encoder = new RespEncoder(); - const encoded = encoder.encodeCmd([bufferToUint8Array(Buffer.from('SET')), 'foo', 123]); - expect(toStr(encoded)).toBe('*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\n123\r\n'); - }); - }); - - describe('.can encode emojis()', () => { - test('can encode a simple command', () => { - const encoder = new RespEncoder(); - encoder.writeCmdUtf8(['SET', 'foo ๐Ÿ‘', 'bar']); - const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('*3\r\n$3\r\nSET\r\n$8\r\nfoo ๐Ÿ‘\r\n$3\r\nbar\r\n'); - }); - }); -}); diff --git a/src/json-pack/resp/__tests__/RespEncoderLegacy.spec.ts b/src/json-pack/resp/__tests__/RespEncoderLegacy.spec.ts deleted file mode 100644 index a7d67763e2..0000000000 --- a/src/json-pack/resp/__tests__/RespEncoderLegacy.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {RespEncoderLegacy} from '../RespEncoderLegacy'; - -const encode = (value: unknown): string => { - const encoder = new RespEncoderLegacy(); - const encoded = encoder.encode(value); - return Buffer.from(encoded).toString(); -}; - -test('can encode simple strings', () => { - expect(encode('')).toBe('+\r\n'); - expect(encode('asdf')).toBe('+asdf\r\n'); -}); - -test('can encode simple errors', () => { - expect(encode(new Error('asdf'))).toBe('-asdf\r\n'); -}); - -test('can encode integers', () => { - expect(encode(0)).toBe(':0\r\n'); - expect(encode(123)).toBe(':123\r\n'); - expect(encode(-422469777)).toBe(':-422469777\r\n'); -}); - -test('can encode bulk strings', () => { - expect(encode('ab\nc')).toBe('$4\r\nab\nc\r\n'); - expect(encode(new Uint8Array([65]))).toBe('$1\r\nA\r\n'); -}); - -test('can encode arrays', () => { - expect(encode(['a', 1])).toBe('*2\r\n+a\r\n:1\r\n'); -}); - -test('encodes null as nullable array', () => { - expect(encode(null)).toBe('*-1\r\n'); -}); - -test('encodes null in nested structure as nullable string', () => { - expect(encode(['a', 'b', null])).toBe('*3\r\n+a\r\n+b\r\n$-1\r\n'); -}); - -test('encodes booleans as strings', () => { - expect(encode(true)).toBe('+TRUE\r\n'); - expect(encode(false)).toBe('+FALSE\r\n'); -}); - -test('encodes floats as strings', () => { - expect(encode(1.23)).toBe('+1.23\r\n'); -}); - -test('encodes objects as 2-tuple arrays', () => { - expect(encode({foo: 'bar'})).toBe('*2\r\n+foo\r\n+bar\r\n'); -}); diff --git a/src/json-pack/resp/__tests__/RespStreamingDecoder.spec.ts b/src/json-pack/resp/__tests__/RespStreamingDecoder.spec.ts deleted file mode 100644 index 112fd3a054..0000000000 --- a/src/json-pack/resp/__tests__/RespStreamingDecoder.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {RespStreamingDecoder} from '../RespStreamingDecoder'; -import {RespEncoder} from '../RespEncoder'; -import {concatList} from '../../../util/buffers/concat'; -import {documents} from '../../../__tests__/json-documents'; -import {utf8} from '../../../util/buffers/strings'; - -const encoder = new RespEncoder(); - -test('can decode simple string', () => { - const decoder = new RespStreamingDecoder(); - const encoded = encoder.encode('abc'); - expect(decoder.read()).toBe(undefined); - decoder.push(encoded); - expect(decoder.read()).toBe('abc'); - expect(decoder.read()).toBe(undefined); - expect(decoder.read()).toBe(undefined); -}); - -test('can stream one byte at a time', () => { - const decoder = new RespStreamingDecoder(); - const docs = [ - 1, - 123.1234, - -3, - true, - null, - false, - Infinity, - NaN, - -Infinity, - '', - 'abc', - 'a\nb', - 'a\rb', - 'emoji: ๐Ÿถ', - '๐Ÿ˜€', - '๐Ÿ˜€๐Ÿ˜€', - '๐Ÿ˜€๐Ÿ˜€๐Ÿ˜€', - new Error('123'), - new Error('\n'), - null, - {}, - [{foo: -43, bar: 'a\nb'}], - ]; - const encoded = docs.map((doc) => encoder.encode(doc)); - const decoded: unknown[] = []; - const bufs = concatList(encoded); - for (let i = 0; i < bufs.length; i++) { - decoder.push(new Uint8Array([bufs[i]])); - const read = decoder.read(); - if (read !== undefined) decoded.push(read); - } - expect(decoded).toEqual(docs); -}); - -test('can stream 49 bytes at a time', () => { - const decoder = new RespStreamingDecoder(); - const docs = documents; - const encoded = docs.map((doc) => encoder.encode(doc)); - const decoded: unknown[] = []; - const bufs = concatList(encoded); - for (let i = 0; i < bufs.length; i += 49) { - const max = Math.min(bufs.length, i + 49); - decoder.push(new Uint8Array(bufs.slice(i, max))); - let read: any; - while ((read = decoder.read()) !== undefined) decoded.push(read); - } - expect(decoded).toEqual(docs); -}); - -test('can decode a command', () => { - const encoded = encoder.encodeCmd(['SET', 'foo', 'bar']); - const decoder = new RespStreamingDecoder(); - decoder.push(encoded); - const decoded = decoder.readCmd(); - expect(decoded).toEqual(['SET', utf8`foo`, utf8`bar`]); -}); diff --git a/src/json-pack/resp/__tests__/codec.spec.ts b/src/json-pack/resp/__tests__/codec.spec.ts deleted file mode 100644 index 7ed84fb5d5..0000000000 --- a/src/json-pack/resp/__tests__/codec.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {RespEncoder} from '../RespEncoder'; -import {RespDecoder} from '../RespDecoder'; -import {documents} from '../../../__tests__/json-documents'; -import {binaryDocuments} from '../../../__tests__/binary-documents'; - -const run = (encoder: RespEncoder, decoder: RespDecoder) => { - describe('JSON documents', () => { - for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - const encoded = encoder.encode(t.json); - const decoded = decoder.read(encoded); - expect(decoded).toEqual(t.json); - }); - } - }); -}; - -const runBinary = (encoder: RespEncoder, decoder: RespDecoder) => { - describe('binary documents', () => { - for (const t of binaryDocuments) { - (t.only ? test.only : test)(t.name, () => { - const encoded = encoder.encode(t.json); - const decoded = decoder.read(encoded); - expect(decoded).toEqual(t.json); - }); - } - }); -}; - -describe('dedicated codecs', () => { - const encoder = new RespEncoder(); - const decoder = new RespDecoder(); - run(encoder, decoder); - runBinary(encoder, decoder); -}); - -const encoder = new RespEncoder(); -const decoder = new RespDecoder(); - -describe('shared codecs', () => { - run(encoder, decoder); - runBinary(encoder, decoder); -}); diff --git a/src/json-pack/resp/__tests__/fuzzing.spec.ts b/src/json-pack/resp/__tests__/fuzzing.spec.ts deleted file mode 100644 index 548d396ae5..0000000000 --- a/src/json-pack/resp/__tests__/fuzzing.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {RandomJson} from '../../../json-random'; -import {RespEncoder} from '../RespEncoder'; -import {RespDecoder} from '../RespDecoder'; - -const encoder = new RespEncoder(); -const decoder = new RespDecoder(); - -describe('fuzzing', () => { - test('CborEncoderFast', () => { - for (let i = 0; i < 2000; i++) { - const value = JSON.parse(JSON.stringify(RandomJson.generate())); - const encoded = encoder.encode(value); - const decoded = decoder.read(encoded); - expect(decoded).toStrictEqual(value); - } - }); -}); diff --git a/src/json-pack/resp/__tests__/skipping.spec.ts b/src/json-pack/resp/__tests__/skipping.spec.ts deleted file mode 100644 index da040c0b8a..0000000000 --- a/src/json-pack/resp/__tests__/skipping.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {RespEncoder} from '../RespEncoder'; -import {RespDecoder} from '../RespDecoder'; -import {RespStreamingDecoder} from '../RespStreamingDecoder'; -import {documents} from '../../../__tests__/json-documents'; -import {binaryDocuments} from '../../../__tests__/binary-documents'; - -const docs = [...documents, ...binaryDocuments]; - -const encoder = new RespEncoder(); -const decoder = new RespDecoder(); -const streamingDecoder = new RespStreamingDecoder(); - -describe('skipping', () => { - describe('RespDecoder', () => { - for (const t of docs) { - (t.only ? test.only : test)(t.name, () => { - encoder.writeAny(t.json); - encoder.writeAny({foo: 'bar'}); - const encoded = encoder.writer.flush(); - decoder.reader.reset(encoded); - decoder.skipAny(); - const decoded = decoder.val(); - expect(decoded).toEqual({foo: 'bar'}); - }); - } - }); - - describe('RespStreamingDecoder', () => { - for (const t of docs) { - (t.only ? test.only : test)(t.name, () => { - encoder.writeAny(t.json); - encoder.writeAny({foo: 'bar'}); - const encoded = encoder.writer.flush(); - streamingDecoder.push(encoded); - streamingDecoder.skip(); - const decoded = streamingDecoder.read(); - expect(decoded).toEqual({foo: 'bar'}); - }); - } - }); -}); diff --git a/src/json-pack/resp/constants.ts b/src/json-pack/resp/constants.ts deleted file mode 100644 index c00eb24274..0000000000 --- a/src/json-pack/resp/constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const enum RESP { - // Human readable separators - R = 0x0d, // \r - N = 0x0a, // \n - RN = 0x0d0a, // \r\n - - // Data types - NULL = 95, // _ - BOOL = 35, // # - INT = 58, // : - BIG = 40, // ( - FLOAT = 44, // , - STR_SIMPLE = 43, // + - STR_BULK = 36, // $ - STR_VERBATIM = 61, // = - ERR_SIMPLE = 45, // - - ERR_BULK = 33, // ! - ARR = 42, // * - SET = 126, // ~ - OBJ = 37, // % - PUSH = 62, // > - ATTR = 124, // | - - // Special chars - PLUS = 43, // + - MINUS = 45, // - -} diff --git a/src/json-pack/resp/extensions.ts b/src/json-pack/resp/extensions.ts deleted file mode 100644 index c179e4e359..0000000000 --- a/src/json-pack/resp/extensions.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {JsonPackExtension} from '../JsonPackExtension'; - -export class RespPush extends JsonPackExtension { - constructor(public readonly val: unknown[]) { - super(1, val); - } -} - -export class RespAttributes extends JsonPackExtension> { - constructor(public readonly val: Record) { - super(2, val); - } -} - -export class RespVerbatimString extends JsonPackExtension { - constructor(public readonly val: string) { - super(3, val); - } -} diff --git a/src/json-pack/resp/index.ts b/src/json-pack/resp/index.ts deleted file mode 100644 index 82c1e20632..0000000000 --- a/src/json-pack/resp/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './constants'; -export * from './extensions'; -export * from './RespEncoder'; -export * from './RespEncoderLegacy'; -export * from './RespDecoder'; -export * from './RespStreamingDecoder'; diff --git a/src/json-pack/types.ts b/src/json-pack/types.ts deleted file mode 100644 index 150ad9340b..0000000000 --- a/src/json-pack/types.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type {IReader, IReaderResettable, IWriter, IWriterGrowable} from '../util/buffers'; -import type {JsonPackExtension} from './JsonPackExtension'; -import type {JsonPackValue} from './JsonPackValue'; - -export type JsonPrimitive = string | number | bigint | boolean | null; -export type JsonValue = JsonPrimitive | JsonArray | JsonObject; -type JsonArray = JsonValue[] | readonly JsonValue[]; -type JsonObject = {[key: string]: JsonValue} | Readonly<{[key: string]: JsonValue}>; - -export type TypedJsonValue = T & JsonValue; - -export type PackPrimitive = JsonPrimitive | undefined | Uint8Array | JsonPackValue | JsonPackExtension | bigint; -export type PackValue = PackPrimitive | PackArray | PackObject; -type PackArray = PackValue[] | readonly PackValue[]; -type PackObject = {[key: string]: PackValue} | Readonly<{[key: string]: PackValue}>; - -export interface BinaryJsonEncoder { - encode(value: unknown): Uint8Array; - writer: IWriter & IWriterGrowable; - writeAny(value: unknown): void; - writeNull(): void; - writeBoolean(bool: boolean): void; - writeNumber(num: number): void; - writeInteger(int: number): void; - writeUInteger(uint: number): void; - writeFloat(float: number): void; - writeBin(buf: Uint8Array): void; - writeAsciiStr(str: string): void; - writeStr(str: string): void; - writeArr(arr: unknown[]): void; - writeObj(obj: Record): void; -} - -export interface StreamingBinaryJsonEncoder { - writeStartStr(): void; - writeStrChunk(str: string): void; - writeEndStr(): void; - writeStartBin(): void; - writeBinChunk(buf: Uint8Array): void; - writeEndBin(): void; - writeStartArr(): void; - writeArrChunk(item: unknown): void; - writeEndArr(): void; - writeStartObj(): void; - writeObjChunk(key: string, value: unknown): void; - writeEndObj(): void; -} - -export interface TlvBinaryJsonEncoder { - writeBinHdr(length: number): void; - writeArrHdr(length: number): void; - writeObjHdr(length: number): void; -} - -export interface BinaryJsonDecoder { - decode(uint8: Uint8Array): unknown; - reader: IReader & IReaderResettable; - read(uint8: Uint8Array): unknown; -} diff --git a/src/json-pack/ubjson/README.md b/src/json-pack/ubjson/README.md deleted file mode 100644 index 4d305e52cc..0000000000 --- a/src/json-pack/ubjson/README.md +++ /dev/null @@ -1,184 +0,0 @@ -## Limitations of the UBJSON format - -- Does not have a native "binary" type representation. Instead, octets are - encoded as a typed arrays of fixed length. Such encoding is reserved for - JavaScript Typed arrays. The `Uint8Array` array is encoded as fixed length - fixed type array of type `U`. -- UBJSON requires big-endian encoding of binary data, however, JavaScript - Typed arrays are always little-endian, because Intel and ARM CPUs are - little-endian. This means that the binary data must be converted to big-endian - before encoding and after decoding. To avoid this transcoding performance - penalty, only `Uint8Array` type is supported. - - -## Benchmarks - -`json-joy` implementation of UBJSON is about an order of magnitude faster than `@shelacek/ubjson`. - -### Encoding - -Node v20: - -``` -npx ts-node benchmarks/json-pack/bench.encoding.ubjson.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v20.1.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 6,086,774 ops/sec ยฑ0.49% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 249,763 ops/sec ยฑ0.90% (91 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 2,247,813 ops/sec ยฑ0.09% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 467,602 ops/sec ยฑ0.43% (100 runs sampled) -๐Ÿคž @shelacek/ubjson x 21,679 ops/sec ยฑ0.63% (93 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 205,665 ops/sec ยฑ0.07% (101 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 139,415 ops/sec ยฑ0.09% (98 runs sampled) -๐Ÿคž @shelacek/ubjson x 6,835 ops/sec ยฑ0.75% (80 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 63,793 ops/sec ยฑ0.07% (101 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 6,328 ops/sec ยฑ0.13% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 445 ops/sec ยฑ0.43% (77 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 7,131 ops/sec ยฑ0.44% (99 runs sampled) -Fastest is ๐Ÿคž Buffer.from(JSON.stringify()) ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 291,303 ops/sec ยฑ0.78% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 15,442 ops/sec ยฑ1.08% (86 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 183,711 ops/sec ยฑ0.82% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 272,762 ops/sec ยฑ0.56% (93 runs sampled) -๐Ÿคž @shelacek/ubjson x 27,051 ops/sec ยฑ1.11% (87 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 145,414 ops/sec ยฑ0.50% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 424,816 ops/sec ยฑ0.74% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 90,009 ops/sec ยฑ0.69% (93 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 28,931 ops/sec ยฑ0.08% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 2,147,028 ops/sec ยฑ0.23% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 63,720 ops/sec ยฑ0.82% (92 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,015,356 ops/sec ยฑ0.12% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 3,039,077 ops/sec ยฑ0.15% (98 runs sampled) -๐Ÿคž @shelacek/ubjson x 381,464 ops/sec ยฑ0.16% (97 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,197,582 ops/sec ยฑ0.11% (102 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 1,661,503 ops/sec ยฑ0.19% (101 runs sampled) -๐Ÿคž @shelacek/ubjson x 272,256 ops/sec ยฑ0.11% (101 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,075,468 ops/sec ยฑ0.18% (101 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -``` - -Node v18: - -``` -npx ts-node benchmarks/json-pack/bench.encoding.ubjson.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 ----------------------------------------------------------------------------- Small object, 44 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 6,702,065 ops/sec ยฑ1.34% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 244,890 ops/sec ยฑ0.83% (88 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 2,272,407 ops/sec ยฑ0.20% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -------------------------------------------------------------------------- Typical object, 993 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 499,534 ops/sec ยฑ0.37% (101 runs sampled) -๐Ÿคž @shelacek/ubjson x 21,968 ops/sec ยฑ0.55% (95 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 198,487 ops/sec ยฑ5.53% (90 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Large object, 3741 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 101,614 ops/sec ยฑ6.22% (71 runs sampled) -๐Ÿคž @shelacek/ubjson x 6,928 ops/sec ยฑ4.39% (86 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 63,549 ops/sec ยฑ2.57% (95 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------- Very large object, 45750 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 6,548 ops/sec ยฑ0.26% (99 runs sampled) -๐Ÿคž @shelacek/ubjson x 441 ops/sec ยฑ1.05% (80 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 5,973 ops/sec ยฑ1.06% (97 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder ------------------------------------------------------------------- Object with many keys, 969 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 299,428 ops/sec ยฑ1.96% (95 runs sampled) -๐Ÿคž @shelacek/ubjson x 15,818 ops/sec ยฑ1.29% (86 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 188,231 ops/sec ยฑ0.82% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -------------------------------------------------------------------------- String ladder, 3398 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 303,012 ops/sec ยฑ2.13% (97 runs sampled) -๐Ÿคž @shelacek/ubjson x 28,397 ops/sec ยฑ1.71% (86 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 126,743 ops/sec ยฑ1.43% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Long strings, 7011 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 434,614 ops/sec ยฑ0.73% (97 runs sampled) -๐Ÿคž @shelacek/ubjson x 74,697 ops/sec ยฑ5.70% (91 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 30,070 ops/sec ยฑ0.10% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------- Short strings, 170 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 1,818,725 ops/sec ยฑ0.64% (98 runs sampled) -๐Ÿคž @shelacek/ubjson x 63,728 ops/sec ยฑ1.30% (88 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,007,266 ops/sec ยฑ0.59% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder --------------------------------------------------------------------------------- Numbers, 136 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 4,132,602 ops/sec ยฑ0.42% (100 runs sampled) -๐Ÿคž @shelacek/ubjson x 361,219 ops/sec ยฑ0.78% (99 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 1,119,393 ops/sec ยฑ0.14% (100 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder ---------------------------------------------------------------------------------- Tokens, 308 bytes -๐Ÿคž json-joy/json-pack UbjsonEncoder x 1,907,200 ops/sec ยฑ0.25% (100 runs sampled) -๐Ÿคž @shelacek/ubjson x 258,382 ops/sec ยฑ0.52% (100 runs sampled) -๐Ÿคž Buffer.from(JSON.stringify()) x 971,885 ops/sec ยฑ0.81% (99 runs sampled) -Fastest is ๐Ÿคž json-joy/json-pack UbjsonEncoder -``` - -### Decoding - -Node v18: - -``` -npx ts-node benchmarks/json-pack/bench.ubjson.decoding.ts -=============================================================================== Benchmark: Encoding -Warmup: 1000x , Node.js: v18.16.0 , Arch: arm64 , CPU: Apple M1 ---------------------------------------------------------------------------- Small object, 331 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 2,615,977 ops/sec ยฑ0.16% (101 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 536,500 ops/sec ยฑ1.09% (96 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder ------------------------------------------------------------------------- Typical object, 8911 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 235,867 ops/sec ยฑ0.29% (100 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 56,058 ops/sec ยฑ1.43% (97 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder -------------------------------------------------------------------------- Large object, 36678 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 73,598 ops/sec ยฑ0.78% (99 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 18,320 ops/sec ยฑ0.58% (99 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder -------------------------------------------------------------------- Very large object, 474391 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 3,197 ops/sec ยฑ0.10% (100 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 932 ops/sec ยฑ1.42% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder ------------------------------------------------------------------ Object with many keys, 8314 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 98,536 ops/sec ยฑ1.03% (98 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 35,345 ops/sec ยฑ0.57% (100 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder ------------------------------------------------------------------------- String ladder, 36555 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 250,466 ops/sec ยฑ5.04% (93 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 68,201 ops/sec ยฑ2.84% (91 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder -------------------------------------------------------------------------- Long strings, 85535 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 102,333 ops/sec ยฑ2.35% (96 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 79,448 ops/sec ยฑ0.70% (95 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder -------------------------------------------------------------------------- Short strings, 1556 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 899,484 ops/sec ยฑ0.44% (96 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 156,232 ops/sec ยฑ2.08% (95 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder --------------------------------------------------------------------------------- Numbers, 790 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 3,313,595 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 430,527 ops/sec ยฑ0.76% (95 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder ---------------------------------------------------------------------------------- Tokens, 471 bytes -๐Ÿ‘ json-joy/json-pack UbjsonDecoder x 1,879,654 ops/sec ยฑ0.20% (95 runs sampled) -๐Ÿ‘ @shelacek/ubjson x 322,744 ops/sec ยฑ0.39% (98 runs sampled) -Fastest is ๐Ÿ‘ json-joy/json-pack UbjsonDecoder -``` diff --git a/src/json-pack/ubjson/UbjsonDecoder.ts b/src/json-pack/ubjson/UbjsonDecoder.ts deleted file mode 100644 index 927eb47120..0000000000 --- a/src/json-pack/ubjson/UbjsonDecoder.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {Reader} from '../../util/buffers/Reader'; -import {JsonPackExtension} from '../JsonPackExtension'; -import {ERROR} from '../cbor/constants'; -import type {BinaryJsonDecoder, PackValue} from '../types'; - -export class UbjsonDecoder implements BinaryJsonDecoder { - public reader = new Reader(); - - public read(uint8: Uint8Array): PackValue { - this.reader.reset(uint8); - return this.readAny(); - } - - public decode(uint8: Uint8Array): unknown { - this.reader.reset(uint8); - return this.readAny(); - } - - public readAny(): PackValue { - const reader = this.reader; - const octet = reader.u8(); - switch (octet) { - case 0x5a: - return null; - case 0x54: - return true; - case 0x46: - return false; - case 0x55: - return reader.u8(); - case 0x69: - return reader.i8(); - case 0x49: { - const int = reader.view.getInt16(reader.x, false); - reader.x += 2; - return int; - } - case 0x6c: { - const int = reader.view.getInt32(reader.x, false); - reader.x += 4; - return int; - } - case 0x64: { - const num = reader.view.getFloat32(reader.x, false); - reader.x += 4; - return num; - } - case 0x44: { - const num = reader.view.getFloat64(reader.x, false); - reader.x += 8; - return num; - } - case 0x4c: { - const num = reader.view.getBigInt64(reader.x, false); - reader.x += 8; - return num; - } - case 0x53: - return reader.utf8(+(this.readAny() as number)); - case 0x43: - return String.fromCharCode(reader.u8()); - case 0x5b: { - const uint8 = reader.uint8; - const x = reader.x; - if (uint8[x] === 0x24 && uint8[x + 1] === 0x55 && uint8[x + 2] === 0x23) { - reader.x += 3; - const size = +(this.readAny() as number); - return reader.buf(size); - } - let type: number = -1; - if (uint8[x] === 0x24) { - reader.x++; - type = reader.u8(); - } - let count: number = -1; - if (uint8[x] === 0x23) { - reader.x++; - count = reader.u8(); - } - if (uint8[x] === 0x24) { - reader.x++; - type = reader.u8(); - } - if (count >= 0) { - let wordSize: number = 1; - switch (type) { - case 0x49: - wordSize = 2; - break; - case 0x6c: - case 0x64: - wordSize = 4; - break; - case 0x44: - case 0x4c: - wordSize = 8; - break; - } - return new JsonPackExtension(type, reader.buf(count * wordSize)); - } else { - const arr: PackValue[] = []; - while (uint8[reader.x] !== 0x5d) arr.push(this.readAny()); - reader.x++; - return arr; - } - } - case 0x7b: { - const uint8 = reader.uint8; - const obj: Record = {}; - while (uint8[reader.x] !== 0x7d) { - const keySize = +(this.readAny() as number); - const key = reader.utf8(keySize); - if (key === '__proto__') throw ERROR.UNEXPECTED_OBJ_KEY; - obj[key] = this.readAny(); - } - reader.x++; - return obj; - } - case 0x4e: - return undefined; - } - return; - } -} diff --git a/src/json-pack/ubjson/UbjsonEncoder.ts b/src/json-pack/ubjson/UbjsonEncoder.ts deleted file mode 100644 index 575be1d3fa..0000000000 --- a/src/json-pack/ubjson/UbjsonEncoder.ts +++ /dev/null @@ -1,231 +0,0 @@ -import type {IWriter, IWriterGrowable} from '../../util/buffers'; -import type {BinaryJsonEncoder, StreamingBinaryJsonEncoder} from '../types'; - -export class UbjsonEncoder implements BinaryJsonEncoder, StreamingBinaryJsonEncoder { - constructor(public readonly writer: IWriter & IWriterGrowable) {} - - public encode(value: unknown): Uint8Array { - const writer = this.writer; - writer.reset(); - this.writeAny(value); - return writer.flush(); - } - - public writeAny(value: unknown): void { - switch (typeof value) { - case 'boolean': - return this.writeBoolean(value); - case 'number': - return this.writeNumber(value as number); - case 'string': - return this.writeStr(value); - case 'object': { - if (value === null) return this.writeNull(); - const constructor = value.constructor; - switch (constructor) { - case Array: - return this.writeArr(value as unknown[]); - case Uint8Array: - return this.writeBin(value as Uint8Array); - default: - return this.writeObj(value as Record); - } - } - case 'bigint': - return this.writeBigInt(value as bigint); - case 'undefined': - return this.writeUndef(); - default: - return this.writeNull(); - } - } - - public writeNull(): void { - this.writer.u8(0x5a); - } - - public writeUndef(): void { - this.writer.u8(0x4e); - } - - public writeBoolean(bool: boolean): void { - this.writer.u8(bool ? 0x54 : 0x46); - } - - public writeNumber(num: number): void { - if (num >> 0 === num) return this.writeInteger(num); - this.writeFloat(num); - } - - public writeInteger(int: number): void { - const writer = this.writer; - if (int <= 0xff && 0 <= int) writer.u16(0x5500 | int); - else if (int <= 127 && -128 <= int) { - writer.u16(0x6900); - writer.view.setInt8(writer.x - 1, int); - } else if (int <= 32767 && -32768 <= int) { - writer.ensureCapacity(3); - writer.u8(0x49); - writer.view.setInt16(writer.x, int, false); - writer.x += 2; - } else if (int <= 2147483647 && -2147483648 <= int) { - writer.ensureCapacity(5); - writer.u8(0x6c); - writer.view.setInt32(writer.x, int, false); - writer.x += 4; - } - } - - public writeUInteger(uint: number): void { - const writer = this.writer; - if (uint < 0xff) writer.u16(0x5500 + uint); - } - - public writeFloat(float: number): void { - const writer = this.writer; - writer.ensureCapacity(9); - const view = writer.view; - const x = writer.x; - view.setUint8(x, 0x44); - view.setFloat64(x + 1, float, false); - writer.x = x + 9; - } - - public writeBigInt(int: bigint): void { - const writer = this.writer; - writer.ensureCapacity(9); - const view = writer.view; - const x = writer.x; - view.setUint8(x, 0x4c); - view.setBigInt64(x + 1, int, false); - writer.x = x + 9; - } - - public writeBin(buf: Uint8Array): void { - const writer = this.writer; - const length = buf.length; - writer.u32(0x5b_24_55_23); // "[$U#" - this.writeInteger(length); - writer.buf(buf, length); - } - - public writeStr(str: string): void { - const length = str.length; - const maxLength = length * 4; - const capacity = maxLength + 1 + 5; // 1 for string type, 5 for length. - const writer = this.writer; - writer.ensureCapacity(capacity); - const uint8 = writer.uint8; - uint8[writer.x++] = 0x53; - const x = writer.x; - const oneByteLength = maxLength < 0xff; - if (oneByteLength) { - uint8[writer.x++] = 0x55; - writer.x++; - } else { - uint8[writer.x++] = 0x6c; - writer.x += 4; - } - const size = writer.utf8(str); - if (oneByteLength) uint8[x + 1] = size; - else writer.view.setUint32(x + 1, size); - } - - public writeAsciiStr(str: string): void { - this.writeStr(str); - } - - public writeArr(arr: unknown[]): void { - const writer = this.writer; - writer.u8(0x5b); - const length = arr.length; - for (let i = 0; i < length; i++) this.writeAny(arr[i]); - writer.u8(0x5d); - } - - public writeObj(obj: Record): void { - const writer = this.writer; - const keys = Object.keys(obj); - const length = keys.length; - writer.u8(0x7b); - for (let i = 0; i < length; i++) { - const key = keys[i]; - const value = obj[key]; - this.writeKey(key); - this.writeAny(value); - } - writer.u8(0x7d); - } - - public writeKey(str: string): void { - const length = str.length; - const maxLength = length * 4; - const capacity = maxLength + 5; // 5 for int. - const writer = this.writer; - writer.ensureCapacity(capacity); - const uint8 = writer.uint8; - const x = writer.x; - const oneByteLength = maxLength < 0xff; - if (oneByteLength) { - uint8[writer.x++] = 0x55; - writer.x++; - } else { - uint8[writer.x++] = 0x6c; - writer.x += 4; - } - const size = writer.utf8(str); - if (oneByteLength) uint8[x + 1] = size; - else writer.view.setUint32(x + 1, size); - } - - // ------------------------------------------------------- Streaming encoding - - public writeStartStr(): void { - throw new Error('Method not implemented.'); - } - - public writeStrChunk(str: string): void { - throw new Error('Method not implemented.'); - } - - public writeEndStr(): void { - throw new Error('Method not implemented.'); - } - - public writeStartBin(): void { - throw new Error('Method not implemented.'); - } - - public writeBinChunk(buf: Uint8Array): void { - throw new Error('Method not implemented.'); - } - - public writeEndBin(): void { - throw new Error('Method not implemented.'); - } - - public writeStartArr(): void { - this.writer.u8(0x5b); - } - - public writeArrChunk(item: unknown): void { - this.writeAny(item); - } - - public writeEndArr(): void { - this.writer.u8(0x5d); - } - - public writeStartObj(): void { - this.writer.u8(0x7b); - } - - public writeObjChunk(key: string, value: unknown): void { - this.writeKey(key); - this.writeAny(value); - } - - public writeEndObj(): void { - this.writer.u8(0x7d); - } -} diff --git a/src/json-pack/ubjson/__tests__/UbjsonDecoder.spec.ts b/src/json-pack/ubjson/__tests__/UbjsonDecoder.spec.ts deleted file mode 100644 index 1799727d07..0000000000 --- a/src/json-pack/ubjson/__tests__/UbjsonDecoder.spec.ts +++ /dev/null @@ -1,198 +0,0 @@ -import {encode} from '@shelacek/ubjson'; -import {Writer} from '../../../util/buffers/Writer'; -import {PackValue} from '../../types'; -import {UbjsonEncoder} from '../UbjsonEncoder'; -import {UbjsonDecoder} from '../UbjsonDecoder'; -import {NullObject} from '../../../util/NullObject'; - -const encoder = new UbjsonEncoder(new Writer(8)); -const decoder = new UbjsonDecoder(); - -const assertEncoder = (value: PackValue, optimize = false) => { - const encoded1 = new Uint8Array(encode(value, {optimizeArrays: optimize, optimizeObjects: optimize})); - const encoded2 = encoder.encode(value); - // console.log(encoded1); - // console.log(encoded2); - const decoded1 = decoder.read(encoded1); - const decoded2 = decoder.read(encoded2); - expect(decoded1).toEqual(value); - expect(decoded2).toEqual(value); -}; - -describe('undefined', () => { - test('undefined', () => { - assertEncoder(undefined as any); - }); -}); - -describe('null', () => { - test('null', () => { - assertEncoder(null); - }); -}); - -describe('boolean', () => { - test('true', () => { - assertEncoder(true); - }); - - test('false', () => { - assertEncoder(false); - }); -}); - -describe('number', () => { - const ints = [ - 0, 1, -1, 123, -123, 1234, 3333, -3467, -4444, 55555, -55565, 234234, -322324, 2147483647, -1147483647, 12321321123, - -12321321123, +2321321123, - ]; - for (const int of ints) { - test('integer ' + int, () => { - assertEncoder(int); - }); - } - - test('floats', () => { - assertEncoder(0.0); - assertEncoder(1.1); - assertEncoder(-1.45); - assertEncoder(123.34); - assertEncoder(-123.234); - assertEncoder(-12321.321123); - assertEncoder(+2321321.123); - }); -}); - -describe('string', () => { - test('empty string', () => { - assertEncoder(''); - }); - - test('one char strings', () => { - assertEncoder('a'); - assertEncoder('b'); - assertEncoder('z'); - assertEncoder('~'); - assertEncoder('"'); - assertEncoder('\\'); - assertEncoder('*'); - assertEncoder('@'); - assertEncoder('9'); - assertEncoder('โœ…'); - assertEncoder('๐Ÿ‘'); - }); - - test('short strings', () => { - assertEncoder('abc'); - assertEncoder('abc123'); - }); - - test('long strings', () => { - assertEncoder( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit.', - ); - }); - - test('unsafe character in the middle of a string', () => { - assertEncoder('...................".....................'); - }); - - test('unsafe character in the middle of a string - 2', () => { - assertEncoder('...................๐ŸŽ‰.....................'); - }); -}); - -describe('binary', () => { - test('empty buffer', () => { - assertEncoder(new Uint8Array(0), true); - }); - - test('small buffer', () => { - assertEncoder(new Uint8Array([1, 2, 3]), true); - }); -}); - -describe('array', () => { - test('empty array', () => { - assertEncoder([]); - }); - - test('array with one element', () => { - assertEncoder([1]); - }); - - test('array with two elements', () => { - assertEncoder([1, 2]); - }); - - test('array of array', () => { - assertEncoder([[123]]); - }); - - test('array of various types', () => { - assertEncoder([0, 1.32, 'str', true, false, null, [1, 2, 3]]); - }); -}); - -describe('object', () => { - test('empty object', () => { - assertEncoder({}); - }); - - test('object with one key', () => { - assertEncoder({foo: 'bar'}); - }); - - test('object with two keys', () => { - assertEncoder({foo: 'bar', baz: 123}); - }); - - test('object with various nested types', () => { - assertEncoder({ - '': null, - null: false, - true: true, - str: 'asdfasdf ,asdf asdf asdf asdf asdf, asdflkasjdflakjsdflajskdlfkasdf', - num: 123, - arr: [1, 2, 3], - obj: {foo: 'bar'}, - obj2: {1: 2, 3: 4}, - }); - }); - - test('throws on __proto__ key', () => { - const obj = new NullObject(); - // tslint:disable-next-line: no-string-literal - obj['__proto__'] = 123; - const buf = encoder.encode(obj); - expect(() => decoder.read(buf)).toThrow(); - }); -}); - -describe('nested object', () => { - test('large array/object', () => { - assertEncoder({ - foo: [ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ], - }); - }); -}); diff --git a/src/json-pack/ubjson/__tests__/UbjsonEncoder.spec.ts b/src/json-pack/ubjson/__tests__/UbjsonEncoder.spec.ts deleted file mode 100644 index 7fd4ffe5b7..0000000000 --- a/src/json-pack/ubjson/__tests__/UbjsonEncoder.spec.ts +++ /dev/null @@ -1,183 +0,0 @@ -import {decode} from '@shelacek/ubjson'; -import {Writer} from '../../../util/buffers/Writer'; -import {PackValue} from '../../types'; -import {UbjsonEncoder} from '../UbjsonEncoder'; - -const writer = new Writer(16); -const encoder = new UbjsonEncoder(writer); - -const assertEncoder = (value: PackValue, expected: PackValue = value) => { - const encoded1 = encoder.encode(value); - const decoded = decode(encoded1, {useTypedArrays: true}); - expect(decoded).toEqual(expected); -}; - -describe('undefined', () => { - test('undefined', () => { - assertEncoder(undefined as any); - }); -}); - -describe('null', () => { - test('null', () => { - assertEncoder(null); - }); -}); - -describe('boolean', () => { - test('true', () => { - assertEncoder(true); - }); - - test('false', () => { - assertEncoder(false); - }); -}); - -describe('number', () => { - const ints = [ - 0, 1, -1, 123, -123, 1234, 3333, -3467, -4444, 55555, -55565, 234234, -322324, 2147483647, -1147483647, 12321321123, - -12321321123, +2321321123, - ]; - for (const int of ints) { - test('integer ' + int, () => { - assertEncoder(int); - }); - } - - test('floats', () => { - assertEncoder(0.0); - assertEncoder(1.1); - assertEncoder(-1.45); - assertEncoder(123.34); - assertEncoder(-123.234); - assertEncoder(-12321.321123); - assertEncoder(+2321321.123); - }); -}); - -describe('string', () => { - test('empty string', () => { - assertEncoder(''); - }); - - test('one char strings', () => { - assertEncoder('a'); - assertEncoder('b'); - assertEncoder('z'); - assertEncoder('~'); - assertEncoder('"'); - assertEncoder('\\'); - assertEncoder('*'); - assertEncoder('@'); - assertEncoder('9'); - assertEncoder('โœ…'); - assertEncoder('๐Ÿ‘'); - }); - - test('short strings', () => { - assertEncoder('abc'); - assertEncoder('abc123'); - }); - - test('long strings', () => { - assertEncoder( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit.', - ); - }); - - test('unsafe character in the middle of a string', () => { - assertEncoder('...................".....................'); - }); - - test('unsafe character in the middle of a string - 2', () => { - assertEncoder('...................๐ŸŽ‰.....................'); - }); -}); - -describe('binary', () => { - test('empty buffer', () => { - assertEncoder(new Uint8Array(0)); - }); - - test('small buffer', () => { - assertEncoder(new Uint8Array([1, 2, 3])); - }); -}); - -describe('array', () => { - test('empty array', () => { - assertEncoder([]); - }); - - test('array with one element', () => { - assertEncoder([1]); - }); - - test('array with two elements', () => { - assertEncoder([1, 2]); - }); - - test('array of array', () => { - assertEncoder([[123]]); - }); - - test('array of various types', () => { - assertEncoder([0, 1.32, 'str', true, false, null, [1, 2, 3]]); - }); -}); - -describe('object', () => { - test('empty object', () => { - assertEncoder({}); - }); - - test('object with one key', () => { - assertEncoder({foo: 'bar'}); - }); - - test('object with two keys', () => { - assertEncoder({foo: 'bar', baz: 123}); - }); - - test('object with various nested types', () => { - assertEncoder({ - '': null, - null: false, - true: true, - str: 'asdfasdf ,asdf asdf asdf asdf asdf, asdflkasjdflakjsdflajskdlfkasdf', - num: 123, - arr: [1, 2, 3], - obj: {foo: 'bar'}, - obj2: {1: 2, 3: 4}, - }); - }); -}); - -describe('nested object', () => { - test('large array/object', () => { - assertEncoder({ - foo: [ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ], - }); - }); -}); diff --git a/src/json-pack/ubjson/__tests__/automated.spec.ts b/src/json-pack/ubjson/__tests__/automated.spec.ts deleted file mode 100644 index 718cc6c58b..0000000000 --- a/src/json-pack/ubjson/__tests__/automated.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {UbjsonEncoder} from '../UbjsonEncoder'; -import {UbjsonDecoder} from '../UbjsonDecoder'; -import {documents} from '../../../__tests__/json-documents'; -import {binaryDocuments} from '../../../__tests__/binary-documents'; - -const writer = new Writer(8); -const encoder = new UbjsonEncoder(writer); -const decoder = new UbjsonDecoder(); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - // const json = Buffer.from(encoded).toString('utf-8'); - // console.log('json', json); - decoder.reader.reset(encoded); - const decoded = decoder.readAny(); - expect(decoded).toEqual(value); -}; - -describe('Sample JSON documents', () => { - for (const t of documents) { - (t.only ? test.only : test)(t.name, () => { - assertEncoder(t.json as any); - }); - } -}); - -describe('Sample binary documents', () => { - for (const t of binaryDocuments) { - (t.only ? test.only : test)(t.name, () => { - assertEncoder(t.json as any); - }); - } -}); diff --git a/src/json-pack/ubjson/__tests__/fuzzer.spec.ts b/src/json-pack/ubjson/__tests__/fuzzer.spec.ts deleted file mode 100644 index d6830d5135..0000000000 --- a/src/json-pack/ubjson/__tests__/fuzzer.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {RandomJson} from '../../../json-random'; -import {Writer} from '../../../util/buffers/Writer'; -import {JsonValue} from '../../types'; -import {UbjsonEncoder} from '../UbjsonEncoder'; -import {UbjsonDecoder} from '../UbjsonDecoder'; - -const writer = new Writer(2); -const encoder = new UbjsonEncoder(writer); -const decoder = new UbjsonDecoder(); - -const assertEncoder = (value: JsonValue) => { - const encoded = encoder.encode(value); - const json = Buffer.from(encoded).toString('utf-8'); - try { - decoder.reader.reset(encoded); - const decoded = decoder.readAny(); - // console.log('decoded', decoded); - expect(decoded).toEqual(value); - } catch (error) { - /* tslint:disable no-console */ - console.log('value', value); - console.log('JSON.stringify', JSON.stringify(value)); - console.log('JsonEncoder', json); - /* tslint:enable no-console */ - throw error; - } -}; - -test('fuzzing', () => { - for (let i = 0; i < 1000; i++) { - const json = RandomJson.generate(); - assertEncoder(json); - } -}, 50000); - -test('big ints', () => { - for (let i = 0; i < 10; i++) { - const int = BigInt(Math.round(Math.random() * Number.MAX_SAFE_INTEGER)); - assertEncoder(int); - } -}); diff --git a/src/json-pack/util/CompressionTable.ts b/src/json-pack/util/CompressionTable.ts deleted file mode 100644 index d8218cb83c..0000000000 --- a/src/json-pack/util/CompressionTable.ts +++ /dev/null @@ -1,167 +0,0 @@ -import {JsonPackExtension} from '../JsonPackExtension'; - -const isSafeInteger = Number.isSafeInteger; - -export class CompressionTable { - public static create(value: unknown): CompressionTable { - const table = new CompressionTable(); - table.walk(value); - table.finalize(); - return table; - } - - protected integers = new Set(); - protected nonIntegers = new Set(); - - protected table: unknown[] = []; - protected map: Map = new Map(); - - public addInteger(int: number): void { - this.integers.add(int); - } - - public addLiteral(value: number | string | unknown): void { - if (isSafeInteger(value)) { - this.addInteger(value as number); - return; - } - this.nonIntegers.add(value); - } - - public walk(value: unknown): void { - switch (typeof value) { - case 'object': { - if (!value) return this.addLiteral(null); - const constructor = value.constructor; - switch (constructor) { - case Object: { - const obj = value as Record; - for (const key in obj) { - this.addLiteral(key); - this.walk(obj[key]); - } - break; - } - case Array: { - const arr = value as unknown[]; - const len = arr.length; - for (let i = 0; i < len; i++) this.walk(arr[i]); - break; - } - case Map: { - const map = value as Map; - map.forEach((value, key) => { - this.walk(key); - this.walk(value); - }); - break; - } - case Set: { - const set = value as Set; - set.forEach((value) => { - this.walk(value); - }); - break; - } - case JsonPackExtension: { - const ext = value as JsonPackExtension; - this.addInteger(ext.tag); - this.walk(ext.val); - } - } - return; - } - default: - return this.addLiteral(value); - } - } - - public finalize(): void { - const integers = Array.from(this.integers); - integers.sort((a, b) => a - b); - const len = integers.length; - const table = this.table; - const map = this.map; - if (len > 0) { - const first = integers[0]; - table.push(first); - map.set(first, 0); - let last = first; - for (let i = 1; i < len; i++) { - const int = integers[i]; - table.push(int - last); - map.set(int, i); - last = int; - } - } - const nonIntegers = Array.from(this.nonIntegers); - nonIntegers.sort(); - const lenNonIntegers = nonIntegers.length; - for (let i = 0; i < lenNonIntegers; i++) { - const value = nonIntegers[i]; - table.push(value); - map.set(value, len + i); - } - this.integers.clear(); - this.nonIntegers.clear(); - } - - public getIndex(value: unknown): number { - const index = this.map.get(value); - if (index === undefined) throw new Error(`Value [${value}] not found in compression table.`); - return index; - } - - public getTable(): unknown[] { - return this.table; - } - - public compress(value: unknown): unknown { - switch (typeof value) { - case 'object': { - if (!value) return this.getIndex(null); - const constructor = value.constructor; - switch (constructor) { - case Object: { - const obj = value as Record; - const newObj: Record = {}; - for (const key in obj) newObj[this.getIndex(key)] = this.compress(obj[key]); - return newObj; - } - case Array: { - const arr = value as unknown[]; - const newArr: unknown[] = []; - const len = arr.length; - for (let i = 0; i < len; i++) newArr.push(this.compress(arr[i])); - return newArr; - } - case Map: { - const map = value as Map; - const newMap = new Map(); - map.forEach((value, key) => { - newMap.set(this.compress(key), this.compress(value)); - }); - return newMap; - } - case Set: { - const set = value as Set; - const newSet = new Set(); - set.forEach((value) => { - newSet.add(this.compress(value)); - }); - break; - } - case JsonPackExtension: { - const ext = value as JsonPackExtension; - const newExt = new JsonPackExtension(this.getIndex(ext.tag), this.compress(ext.val)); - return newExt; - } - } - throw new Error('UNEXPECTED_OBJECT'); - } - default: { - return this.getIndex(value); - } - } - } -} diff --git a/src/json-pack/util/DecompressionTable.ts b/src/json-pack/util/DecompressionTable.ts deleted file mode 100644 index c1b40cf9b1..0000000000 --- a/src/json-pack/util/DecompressionTable.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {JsonPackExtension} from '../JsonPackExtension'; - -const isSafeInteger = Number.isSafeInteger; - -export class DecompressionTable { - protected readonly table: unknown[] = []; - - public importTable(rleTable: unknown[]) { - const length = rleTable.length; - if (!length) return; - const table = this.table; - const first = rleTable[0]; - table.push(first); - let i = 1; - if (isSafeInteger(first)) { - let prev: number = first; - let value: unknown; - while (i < length) { - value = rleTable[i]; - if (isSafeInteger(value)) { - prev = prev + value; - table.push(prev); - i++; - } else { - break; - } - } - } - while (i < length) table.push(rleTable[i++]); - } - - public getLiteral(index: number): unknown { - const table = this.table; - // if (index < 0 || index >= table.length) throw new Error('OUT_OF_BOUNDS'); - return table[index]; - } - - public decompress(value: unknown): unknown { - switch (typeof value) { - case 'number': { - return this.getLiteral(value); - } - case 'object': { - if (!value) return null; - const constructor = value.constructor; - switch (constructor) { - case Object: { - const obj = value as Record; - const newObj: Record = {}; - for (const key in obj) newObj[String(this.getLiteral(Number(key)))] = this.decompress(obj[key]); - return newObj; - } - case Array: { - const arr = value as unknown[]; - const newArr: unknown[] = []; - const len = arr.length; - for (let i = 0; i < len; i++) newArr.push(this.decompress(arr[i])); - return newArr; - } - case Map: { - const map = value as Map; - const newMap = new Map(); - map.forEach((value, key) => { - newMap.set(this.decompress(key), this.decompress(value)); - }); - return newMap; - } - case Set: { - const set = value as Set; - const newSet = new Set(); - set.forEach((value) => { - newSet.add(this.decompress(value)); - }); - break; - } - case JsonPackExtension: { - const ext = value as JsonPackExtension; - const newExt = new JsonPackExtension(Number(this.getLiteral(ext.tag)), this.decompress(ext.val)); - return newExt; - } - } - return value; - } - default: { - return value; - } - } - } -} diff --git a/src/json-pack/util/__tests__/CompressionTable.spec.ts b/src/json-pack/util/__tests__/CompressionTable.spec.ts deleted file mode 100644 index 6d3e5231fc..0000000000 --- a/src/json-pack/util/__tests__/CompressionTable.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import {CompressionTable} from '../CompressionTable'; - -describe('.walk()', () => { - test('create a compression table from a primitive value', () => { - const table = CompressionTable.create(42).getTable(); - expect(table).toEqual([42]); - }); - - test('collects literals from object', () => { - const json = { - foo: 'bar', - baz: 42, - gg: 'foo', - true: false, - }; - const table = CompressionTable.create(json).getTable(); - expect(table).toEqual([42, 'bar', 'baz', false, 'foo', 'gg', 'true']); - }); - - test('run-length encodes integers', () => { - const json = { - foo: [-3, 12, 42, 12345], - baz: 42, - }; - const table = CompressionTable.create(json).getTable(); - expect(table).toEqual([-3, 15, 30, 12303, 'baz', 'foo']); - }); - - test('run-length encodes integers - 2', () => { - const json = { - foo: [5, 1, 2, 4, 8, 16, 17, 22], - baz: -1.5, - }; - const table = CompressionTable.create(json).getTable(); - expect(table).toEqual([1, 1, 2, 1, 3, 8, 1, 5, -1.5, 'baz', 'foo']); - }); -}); - -describe('.compress()', () => { - test('replaces literals with indices', () => { - const json = { - foo: 'bar', - baz: 42, - gg: 'foo', - true: false, - }; - const table = CompressionTable.create(json); - const compressed = table.compress(json); - expect(compressed).toEqual({'2': 0, '4': 1, '5': 4, '6': 3}); - }); - - test('can share compression table across two documents', () => { - const json1 = { - foo: 'bar', - }; - const json2 = { - foo: [0, 0, 5, 5], - }; - const table = new CompressionTable(); - table.walk(json1); - table.walk(json2); - table.finalize(); - const compressed1 = table.compress(json1); - const compressed2 = table.compress(json2); - expect(table.getTable()).toEqual([0, 5, 'bar', 'foo']); - expect(compressed1).toEqual({'3': 2}); - expect(compressed2).toEqual({'3': [0, 0, 1, 1]}); - }); -}); diff --git a/src/json-pack/util/__tests__/DecompressionTable.spec.ts b/src/json-pack/util/__tests__/DecompressionTable.spec.ts deleted file mode 100644 index 86b24bbea4..0000000000 --- a/src/json-pack/util/__tests__/DecompressionTable.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {CompressionTable} from '../CompressionTable'; -import {DecompressionTable} from '../DecompressionTable'; - -describe('.importTable()', () => { - test('can import back compression table', () => { - const json = { - a: [-10, -5, 5, 100], - b: [true, false, null, null], - c: 'c', - }; - const table = CompressionTable.create(json); - const decompressionTable = new DecompressionTable(); - decompressionTable.importTable(table.getTable()); - expect(decompressionTable.getLiteral(0)).toBe(-10); - expect(decompressionTable.getLiteral(1)).toBe(-5); - expect(decompressionTable.getLiteral(2)).toBe(5); - expect(decompressionTable.getLiteral(3)).toBe(100); - expect(decompressionTable.getLiteral(table.getIndex(true))).toBe(true); - expect(decompressionTable.getLiteral(table.getIndex(false))).toBe(false); - expect(decompressionTable.getLiteral(table.getIndex(null))).toBe(null); - expect(decompressionTable.getLiteral(table.getIndex('a'))).toBe('a'); - expect(decompressionTable.getLiteral(table.getIndex('b'))).toBe('b'); - expect(decompressionTable.getLiteral(table.getIndex('c'))).toBe('c'); - }); -}); - -describe('.decompress()', () => { - test('can decompress a document', () => { - const json = { - a: [-10, -5, 5, 100], - b: [true, false, null, null], - c: 'c', - }; - const table = CompressionTable.create(json); - const compressed = table.compress(json); - const decompressionTable = new DecompressionTable(); - decompressionTable.importTable(table.getTable()); - const decompressed = decompressionTable.decompress(compressed); - expect(decompressed).toEqual(json); - }); -}); diff --git a/src/json-pack/util/objKeyCmp.ts b/src/json-pack/util/objKeyCmp.ts deleted file mode 100644 index 18da990c7d..0000000000 --- a/src/json-pack/util/objKeyCmp.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const objKeyCmp = (a: string, b: string): number => { - const len1 = a.length; - const len2 = b.length; - return len1 === len2 ? (a > b ? 1 : -1) : len1 - len2; -}; diff --git a/src/json-patch/codec/binary/Decoder.ts b/src/json-patch/codec/binary/Decoder.ts index 0a26e6f770..8f747dc0bc 100644 --- a/src/json-patch/codec/binary/Decoder.ts +++ b/src/json-patch/codec/binary/Decoder.ts @@ -31,13 +31,13 @@ import { OpType, OpUndefined, } from '../../op'; -import {MsgPackDecoderFast} from '../../../json-pack/msgpack/MsgPackDecoderFast'; +import {MsgPackDecoderFast} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoderFast'; import {OPCODE} from '../../constants'; import {Path} from '../../../json-pointer'; import {JsonPatchTypes} from '../json/types'; import {createMatcherDefault} from '../../util'; import type {JsonPatchOptions} from '../../types'; -import type {Reader} from '../../../util/buffers/Reader'; +import type {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; export class Decoder extends MsgPackDecoderFast { constructor(private readonly options: JsonPatchOptions) { diff --git a/src/json-patch/codec/binary/Encoder.ts b/src/json-patch/codec/binary/Encoder.ts index 513ec1536d..9eabd67a54 100644 --- a/src/json-patch/codec/binary/Encoder.ts +++ b/src/json-patch/codec/binary/Encoder.ts @@ -1,4 +1,4 @@ -import {MsgPackEncoderFast as EncoderMessagePack} from '../../../json-pack/msgpack/MsgPackEncoderFast'; +import {MsgPackEncoderFast as EncoderMessagePack} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoderFast'; import {Op} from '../../op'; export class Encoder extends EncoderMessagePack { diff --git a/src/json-patch/codec/binary/__tests__/message-pack.spec.ts b/src/json-patch/codec/binary/__tests__/message-pack.spec.ts index afef963c2e..40c555643d 100644 --- a/src/json-patch/codec/binary/__tests__/message-pack.spec.ts +++ b/src/json-patch/codec/binary/__tests__/message-pack.spec.ts @@ -5,7 +5,7 @@ import {encode as encodeCompact} from '../../compact'; import { MsgPackEncoderFast as MsgPackEncoder, MsgPackDecoderFast as MsgPackDecoder, -} from '../../../../json-pack/msgpack'; +} from '@jsonjoy.com/json-pack/lib/msgpack'; const msgPackEncoder = new MsgPackEncoder(); const msgPackDecoder = new MsgPackDecoder(); diff --git a/src/json-patch/op/AbstractOp.ts b/src/json-patch/op/AbstractOp.ts index e17cf6f7f3..65eb39d1df 100644 --- a/src/json-patch/op/AbstractOp.ts +++ b/src/json-patch/op/AbstractOp.ts @@ -3,7 +3,7 @@ import type {Path} from '../../json-pointer'; import type {OpType} from '../opcodes'; import type {Operation} from '../types'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; export abstract class AbstractOp { public readonly from?: Path; diff --git a/src/json-patch/op/OpAdd.ts b/src/json-patch/op/OpAdd.ts index b43b738912..6cb0f37295 100644 --- a/src/json-patch/op/OpAdd.ts +++ b/src/json-patch/op/OpAdd.ts @@ -4,7 +4,7 @@ import {OperationAdd} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {clone as deepClone} from '../../json-clone/clone'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpAnd.ts b/src/json-patch/op/OpAnd.ts index d5bb76d7b3..c61ec9b3a3 100644 --- a/src/json-patch/op/OpAnd.ts +++ b/src/json-patch/op/OpAnd.ts @@ -5,7 +5,7 @@ import {OperationAnd, PredicateOperation} from '../types'; import {OPCODE} from '../constants'; import {Path, formatJsonPointer} from '../../json-pointer'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpContains.ts b/src/json-patch/op/OpContains.ts index 94d33ff41d..d89fd3fd56 100644 --- a/src/json-patch/op/OpContains.ts +++ b/src/json-patch/op/OpContains.ts @@ -4,7 +4,7 @@ import {AbstractPredicateOp} from './AbstractPredicateOp'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpCopy.ts b/src/json-patch/op/OpCopy.ts index 340b67e32b..8103b38430 100644 --- a/src/json-patch/op/OpCopy.ts +++ b/src/json-patch/op/OpCopy.ts @@ -5,7 +5,7 @@ import {Path, find, formatJsonPointer} from '../../json-pointer'; import {OpAdd} from './OpAdd'; import {clone as deepClone} from '../../json-clone/clone'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpDefined.ts b/src/json-patch/op/OpDefined.ts index 43ae0465cb..40964220d9 100644 --- a/src/json-patch/op/OpDefined.ts +++ b/src/json-patch/op/OpDefined.ts @@ -4,7 +4,7 @@ import {OperationDefined} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpEnds.ts b/src/json-patch/op/OpEnds.ts index 206edfe56d..e1d4e391c4 100644 --- a/src/json-patch/op/OpEnds.ts +++ b/src/json-patch/op/OpEnds.ts @@ -4,7 +4,7 @@ import {OperationEnds} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpExtend.ts b/src/json-patch/op/OpExtend.ts index a4de9b747a..059f40b573 100644 --- a/src/json-patch/op/OpExtend.ts +++ b/src/json-patch/op/OpExtend.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationExtend} from '../types'; import {find, isArrayReference, isObjectReference, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; const {isArray} = Array; diff --git a/src/json-patch/op/OpFlip.ts b/src/json-patch/op/OpFlip.ts index 41c52bb3ab..a18fcafff7 100644 --- a/src/json-patch/op/OpFlip.ts +++ b/src/json-patch/op/OpFlip.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationFlip} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpIn.ts b/src/json-patch/op/OpIn.ts index 2b520abe10..385f5bae55 100644 --- a/src/json-patch/op/OpIn.ts +++ b/src/json-patch/op/OpIn.ts @@ -5,7 +5,7 @@ import {AbstractPredicateOp} from './AbstractPredicateOp'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; import {deepEqual} from '../../json-equal/deepEqual'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpInc.ts b/src/json-patch/op/OpInc.ts index 74f1acf6c0..47a1b12faa 100644 --- a/src/json-patch/op/OpInc.ts +++ b/src/json-patch/op/OpInc.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationInc} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpLess.ts b/src/json-patch/op/OpLess.ts index affe7307ea..7e4cb4d4c3 100644 --- a/src/json-patch/op/OpLess.ts +++ b/src/json-patch/op/OpLess.ts @@ -4,7 +4,7 @@ import {OperationLess} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpMatches.ts b/src/json-patch/op/OpMatches.ts index 0c070c9da3..da69abb740 100644 --- a/src/json-patch/op/OpMatches.ts +++ b/src/json-patch/op/OpMatches.ts @@ -4,7 +4,7 @@ import {AbstractPredicateOp} from './AbstractPredicateOp'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpMerge.ts b/src/json-patch/op/OpMerge.ts index 8a3b34336c..318e1b1b35 100644 --- a/src/json-patch/op/OpMerge.ts +++ b/src/json-patch/op/OpMerge.ts @@ -4,7 +4,7 @@ import {OperationMerge} from '../types'; import {find, isArrayReference, Path, formatJsonPointer} from '../../json-pointer'; import {isTextNode, isElementNode} from '../util'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpMore.ts b/src/json-patch/op/OpMore.ts index 9c96eea25a..42a46173a7 100644 --- a/src/json-patch/op/OpMore.ts +++ b/src/json-patch/op/OpMore.ts @@ -4,7 +4,7 @@ import {OperationMore} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpMove.ts b/src/json-patch/op/OpMove.ts index 5e5d487d38..082e9ea8f6 100644 --- a/src/json-patch/op/OpMove.ts +++ b/src/json-patch/op/OpMove.ts @@ -5,7 +5,7 @@ import {OpRemove} from './OpRemove'; import {OpAdd} from './OpAdd'; import {Path, toPath, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpNot.ts b/src/json-patch/op/OpNot.ts index ae9dcfaf1a..4211069e74 100644 --- a/src/json-patch/op/OpNot.ts +++ b/src/json-patch/op/OpNot.ts @@ -5,7 +5,7 @@ import {OperationNot, PredicateOperation} from '../types'; import {OPCODE} from '../constants'; import {Path, formatJsonPointer} from '../../json-pointer'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpOr.ts b/src/json-patch/op/OpOr.ts index c409b5eca8..cfc01dd9c7 100644 --- a/src/json-patch/op/OpOr.ts +++ b/src/json-patch/op/OpOr.ts @@ -5,7 +5,7 @@ import {OperationOr, PredicateOperation} from '../types'; import {OPCODE} from '../constants'; import {Path, formatJsonPointer} from '../../json-pointer'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpRemove.ts b/src/json-patch/op/OpRemove.ts index a5f9b53be8..c5f7674981 100644 --- a/src/json-patch/op/OpRemove.ts +++ b/src/json-patch/op/OpRemove.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationRemove} from '../types'; import {find, isObjectReference, isArrayReference, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpReplace.ts b/src/json-patch/op/OpReplace.ts index 1b6fa8695f..ffe9541c5f 100644 --- a/src/json-patch/op/OpReplace.ts +++ b/src/json-patch/op/OpReplace.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationReplace} from '../types'; import {find, isObjectReference, isArrayReference, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpSplit.ts b/src/json-patch/op/OpSplit.ts index ea7256b392..32b75d74fe 100644 --- a/src/json-patch/op/OpSplit.ts +++ b/src/json-patch/op/OpSplit.ts @@ -4,7 +4,7 @@ import {OperationSplit, SlateNode, SlateTextNode, SlateElementNode} from '../typ import {find, isObjectReference, isArrayReference, Path, formatJsonPointer} from '../../json-pointer'; import {isTextNode, isElementNode} from '../util'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; type Composable = string | number | SlateNode; diff --git a/src/json-patch/op/OpStarts.ts b/src/json-patch/op/OpStarts.ts index ad77ab4c3e..2ec2fee2d8 100644 --- a/src/json-patch/op/OpStarts.ts +++ b/src/json-patch/op/OpStarts.ts @@ -4,7 +4,7 @@ import {OperationStarts} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-patch/op/OpStrDel.ts b/src/json-patch/op/OpStrDel.ts index 67605d19dc..2efffb5722 100644 --- a/src/json-patch/op/OpStrDel.ts +++ b/src/json-patch/op/OpStrDel.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationStrDel} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpStrIns.ts b/src/json-patch/op/OpStrIns.ts index 6d56ba259c..0a714d07f0 100644 --- a/src/json-patch/op/OpStrIns.ts +++ b/src/json-patch/op/OpStrIns.ts @@ -3,7 +3,7 @@ import {AbstractOp} from './AbstractOp'; import {OperationStrIns} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpTest.ts b/src/json-patch/op/OpTest.ts index 71e2d31e37..3535c308d2 100644 --- a/src/json-patch/op/OpTest.ts +++ b/src/json-patch/op/OpTest.ts @@ -5,7 +5,7 @@ import {AbstractPredicateOp} from './AbstractPredicateOp'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; import {deepEqual} from '../../json-equal/deepEqual'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch diff --git a/src/json-patch/op/OpTestString.ts b/src/json-patch/op/OpTestString.ts index de7df7602f..a12d020a1e 100644 --- a/src/json-patch/op/OpTestString.ts +++ b/src/json-patch/op/OpTestString.ts @@ -4,7 +4,7 @@ import {OperationTestString} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpTestStringLen.ts b/src/json-patch/op/OpTestStringLen.ts index 5e302cf0ea..0cf7d346df 100644 --- a/src/json-patch/op/OpTestStringLen.ts +++ b/src/json-patch/op/OpTestStringLen.ts @@ -4,7 +4,7 @@ import {OperationTestStringLen} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Patch Extended diff --git a/src/json-patch/op/OpTestType.ts b/src/json-patch/op/OpTestType.ts index 4ec5d2c93e..b7cdc442f3 100644 --- a/src/json-patch/op/OpTestType.ts +++ b/src/json-patch/op/OpTestType.ts @@ -4,7 +4,7 @@ import {OperationTestType, JsTypes, JsonPatchTypes} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; const {isArray} = Array; diff --git a/src/json-patch/op/OpType.ts b/src/json-patch/op/OpType.ts index 49f701d800..9ce4b9b512 100644 --- a/src/json-patch/op/OpType.ts +++ b/src/json-patch/op/OpType.ts @@ -4,7 +4,7 @@ import {OperationType, JsonPatchTypes} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; const {isArray} = Array; diff --git a/src/json-patch/op/OpUndefined.ts b/src/json-patch/op/OpUndefined.ts index 4d34bf8a24..bd41a7f2d5 100644 --- a/src/json-patch/op/OpUndefined.ts +++ b/src/json-patch/op/OpUndefined.ts @@ -4,7 +4,7 @@ import {OperationUndefined} from '../types'; import {find, Path, formatJsonPointer} from '../../json-pointer'; import {OPCODE} from '../constants'; import {AbstractOp} from './AbstractOp'; -import type {IMessagePackEncoder} from '../../json-pack/msgpack'; +import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; /** * @category JSON Predicate diff --git a/src/json-random/RandomJson.ts b/src/json-random/RandomJson.ts index ba37c709ff..aa4c00fee9 100644 --- a/src/json-random/RandomJson.ts +++ b/src/json-random/RandomJson.ts @@ -1,4 +1,4 @@ -import type {JsonValue} from '../json-pack/types'; +import type {JsonValue} from '@jsonjoy.com/json-pack/lib/types'; /** @ignore */ export type NodeType = 'null' | 'boolean' | 'number' | 'string' | 'binary' | 'array' | 'object'; diff --git a/src/json-size/__tests__/fuzz.spec.ts b/src/json-size/__tests__/fuzz.spec.ts index aa1c2f01fd..5a067411e2 100644 --- a/src/json-size/__tests__/fuzz.spec.ts +++ b/src/json-size/__tests__/fuzz.spec.ts @@ -1,6 +1,6 @@ import {jsonSize} from '..'; import {RandomJson} from '../../json-random/RandomJson'; -import {utf8Size} from '../../util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; const random = new RandomJson(); const iterations = 100; diff --git a/src/json-size/__tests__/msgpackSizeFast.spec.ts b/src/json-size/__tests__/msgpackSizeFast.spec.ts index d35951abd3..50db96fd3c 100644 --- a/src/json-size/__tests__/msgpackSizeFast.spec.ts +++ b/src/json-size/__tests__/msgpackSizeFast.spec.ts @@ -1,4 +1,4 @@ -import {JsonPackExtension, JsonPackValue} from '../../json-pack/msgpack'; +import {JsonPackExtension, JsonPackValue} from '@jsonjoy.com/json-pack/lib/msgpack'; import {msgpackSizeFast} from '../msgpackSizeFast'; test('computes size of single values', () => { diff --git a/src/json-size/__tests__/testJsonSize.ts b/src/json-size/__tests__/testJsonSize.ts index 1240a9a6b4..eeae004be2 100644 --- a/src/json-size/__tests__/testJsonSize.ts +++ b/src/json-size/__tests__/testJsonSize.ts @@ -1,4 +1,4 @@ -import {utf8Size} from '../../util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; export const testJsonSize = ( jsonSize: (val: unknown) => number, diff --git a/src/json-size/json.ts b/src/json-size/json.ts index 9405609e3f..c75f7b41d5 100644 --- a/src/json-size/json.ts +++ b/src/json-size/json.ts @@ -1,4 +1,4 @@ -import {utf8Size} from '../util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; const numberSize = (num: number) => { const isInteger = num === Math.round(num); diff --git a/src/json-size/msgpackSizeFast.ts b/src/json-size/msgpackSizeFast.ts index ae6bc5b37a..8ddd87a2a5 100644 --- a/src/json-size/msgpackSizeFast.ts +++ b/src/json-size/msgpackSizeFast.ts @@ -1,5 +1,5 @@ -import {JsonPackExtension, JsonPackValue} from '../json-pack/msgpack'; -import {isUint8Array} from '../util/buffers/isUint8Array'; +import {JsonPackExtension, JsonPackValue} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; const arraySize = (arr: unknown[]): number => { let size = 2; diff --git a/src/json-stable/index.ts b/src/json-stable/index.ts index 613d76c643..74a0e51be4 100644 --- a/src/json-stable/index.ts +++ b/src/json-stable/index.ts @@ -1,4 +1,4 @@ -import {escape} from '../util/strings/escape'; +import {escape} from '@jsonjoy.com/json-pack/lib/util/strings/escape'; import {sort} from '../util/sort/insertion'; import type {json_string} from '../json-brand'; diff --git a/src/json-type-cli/Cli.ts b/src/json-type-cli/Cli.ts index f073724a6f..83dedaecca 100644 --- a/src/json-type-cli/Cli.ts +++ b/src/json-type-cli/Cli.ts @@ -1,7 +1,7 @@ import {parseArgs} from 'node:util'; import {TypeSystem} from '../json-type/system/TypeSystem'; import {ObjectValueCaller} from '../reactive-rpc/common/rpc/caller/ObjectValueCaller'; -import {bufferToUint8Array} from '../util/buffers/bufferToUint8Array'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; import {formatError} from './util'; import {defineBuiltinRoutes} from './methods'; import {defaultParams} from './defaultParams'; diff --git a/src/json-type-cli/codecs/cbor.ts b/src/json-type-cli/codecs/cbor.ts index 50e797c4b0..6174b2fbbf 100644 --- a/src/json-type-cli/codecs/cbor.ts +++ b/src/json-type-cli/codecs/cbor.ts @@ -1,6 +1,6 @@ -import {CborDecoder} from '../../json-pack/cbor/CborDecoder'; -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; -import type {Writer} from '../../util/buffers/Writer'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecCbor implements CliCodec<'cbor'> { diff --git a/src/json-type-cli/codecs/json.ts b/src/json-type-cli/codecs/json.ts index 14db037a96..fad83b3069 100644 --- a/src/json-type-cli/codecs/json.ts +++ b/src/json-type-cli/codecs/json.ts @@ -1,6 +1,6 @@ -import {JsonDecoder} from '../../json-pack/json/JsonDecoder'; -import {JsonEncoder} from '../../json-pack/json/JsonEncoder'; -import type {Writer} from '../../util/buffers/Writer'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecJson implements CliCodec<'json'> { diff --git a/src/json-type-cli/codecs/json2.ts b/src/json-type-cli/codecs/json2.ts index 00f48fcf5b..d9df7f99d7 100644 --- a/src/json-type-cli/codecs/json2.ts +++ b/src/json-type-cli/codecs/json2.ts @@ -1,7 +1,7 @@ -import {JsonDecoder} from '../../json-pack/json/JsonDecoder'; -import {JsonEncoder} from '../../json-pack/json/JsonEncoder'; -import {bufferToUint8Array} from '../../util/buffers/bufferToUint8Array'; -import type {Writer} from '../../util/buffers/Writer'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import type {CliCodec} from '../types'; /** diff --git a/src/json-type-cli/codecs/json4.ts b/src/json-type-cli/codecs/json4.ts index 6ea7aaf074..1d290ec3b4 100644 --- a/src/json-type-cli/codecs/json4.ts +++ b/src/json-type-cli/codecs/json4.ts @@ -1,7 +1,7 @@ -import {JsonDecoder} from '../../json-pack/json/JsonDecoder'; -import {JsonEncoder} from '../../json-pack/json/JsonEncoder'; -import type {Writer} from '../../util/buffers/Writer'; -import {bufferToUint8Array} from '../../util/buffers/bufferToUint8Array'; +import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; import type {CliCodec} from '../types'; /** diff --git a/src/json-type-cli/codecs/msgpack.ts b/src/json-type-cli/codecs/msgpack.ts index 08dd401ced..95cb5ab8ef 100644 --- a/src/json-type-cli/codecs/msgpack.ts +++ b/src/json-type-cli/codecs/msgpack.ts @@ -1,6 +1,6 @@ -import {MsgPackEncoder} from '../../json-pack/msgpack'; -import {MsgPackDecoder} from '../../json-pack/msgpack/MsgPackDecoder'; -import type {Writer} from '../../util/buffers/Writer'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecMsgpack implements CliCodec<'msgpack'> { diff --git a/src/json-type-cli/codecs/ubjson.ts b/src/json-type-cli/codecs/ubjson.ts index a9fe737242..bad4036c81 100644 --- a/src/json-type-cli/codecs/ubjson.ts +++ b/src/json-type-cli/codecs/ubjson.ts @@ -1,6 +1,6 @@ -import {UbjsonDecoder} from '../../json-pack/ubjson/UbjsonDecoder'; -import {UbjsonEncoder} from '../../json-pack/ubjson/UbjsonEncoder'; -import type {Writer} from '../../util/buffers/Writer'; +import {UbjsonDecoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonDecoder'; +import {UbjsonEncoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonEncoder'; +import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecUbjson implements CliCodec<'ubjson'> { diff --git a/src/json-type-cli/defaultCodecs.ts b/src/json-type-cli/defaultCodecs.ts index aeb6f210bb..c6511a13d8 100644 --- a/src/json-type-cli/defaultCodecs.ts +++ b/src/json-type-cli/defaultCodecs.ts @@ -1,4 +1,4 @@ -import {Writer} from '../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {CliCodecs} from './CliCodecs'; import {CliCodecCbor} from './codecs/cbor'; import {CliCodecJson} from './codecs/json'; diff --git a/src/json-type-cli/params/CliParamCmd.ts b/src/json-type-cli/params/CliParamCmd.ts index 3488bfdc29..f685c40746 100644 --- a/src/json-type-cli/params/CliParamCmd.ts +++ b/src/json-type-cli/params/CliParamCmd.ts @@ -1,8 +1,8 @@ import {applyPatch} from '../../json-patch'; import {spawn} from 'child_process'; import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import {bufferToUint8Array} from '../../util/buffers/bufferToUint8Array'; -import {listToUint8} from '../../util/buffers/concat'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; import type {Cli} from '../Cli'; import type {CliParam, CliParamInstance} from '../types'; diff --git a/src/json-type-value/Value.ts b/src/json-type-value/Value.ts index 165a67f0bb..b89c621053 100644 --- a/src/json-type-value/Value.ts +++ b/src/json-type-value/Value.ts @@ -1,4 +1,4 @@ -import type {JsonValueCodec} from '../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {ResolveType, Type} from '../json-type'; export class Value { diff --git a/src/json-type/__demos__/json-type.ts b/src/json-type/__demos__/json-type.ts index d96276c6c9..6a416d75a1 100644 --- a/src/json-type/__demos__/json-type.ts +++ b/src/json-type/__demos__/json-type.ts @@ -6,7 +6,7 @@ /* tslint:disable no-console */ -import {EncodingFormat} from '../../json-pack/constants'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; import {TypeSystem} from '../../json-type'; console.clear(); diff --git a/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts b/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts index 62bb5d5480..b21d988fe8 100644 --- a/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts +++ b/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts @@ -1,11 +1,11 @@ import {Codegen, CodegenStepExecJs} from '../../../util/codegen'; import {WriteBlobStep} from '../WriteBlobStep'; -import {concat} from '../../../util/buffers/concat'; +import {concat} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; import {Value} from '../../../json-type-value/Value'; import type {TypeSystem} from '../../system'; import type {Type} from '../../type'; import type {CompiledBinaryEncoder} from '../types'; -import type {BinaryJsonEncoder} from '../../../json-pack/types'; +import type {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; type Step = WriteBlobStep | CodegenStepExecJs; diff --git a/src/json-type/codegen/binary/CborEncoderCodegenContext.ts b/src/json-type/codegen/binary/CborEncoderCodegenContext.ts index 325edea30a..94923f4412 100644 --- a/src/json-type/codegen/binary/CborEncoderCodegenContext.ts +++ b/src/json-type/codegen/binary/CborEncoderCodegenContext.ts @@ -1,4 +1,4 @@ -import type {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; +import type {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {BinaryEncoderCodegenContext, type BinaryEncoderCodegenContextOptions} from './BinaryEncoderCodegenContext'; export interface CborEncoderCodegenContextOptions extends BinaryEncoderCodegenContextOptions {} diff --git a/src/json-type/codegen/binary/JsonEncoderCodegenContext.ts b/src/json-type/codegen/binary/JsonEncoderCodegenContext.ts index a1c193c0b6..bea35022d1 100644 --- a/src/json-type/codegen/binary/JsonEncoderCodegenContext.ts +++ b/src/json-type/codegen/binary/JsonEncoderCodegenContext.ts @@ -1,4 +1,4 @@ -import type {JsonEncoder} from '../../../json-pack/json/JsonEncoder'; +import type {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import {BinaryEncoderCodegenContext, type BinaryEncoderCodegenContextOptions} from './BinaryEncoderCodegenContext'; export interface JsonEncoderCodegenContextOptions extends BinaryEncoderCodegenContextOptions {} diff --git a/src/json-type/codegen/binary/MessagePackEncoderCodegenContext.ts b/src/json-type/codegen/binary/MessagePackEncoderCodegenContext.ts index 00c917413e..0583134e39 100644 --- a/src/json-type/codegen/binary/MessagePackEncoderCodegenContext.ts +++ b/src/json-type/codegen/binary/MessagePackEncoderCodegenContext.ts @@ -1,4 +1,4 @@ -import type {MsgPackEncoder} from '../../../json-pack/msgpack/MsgPackEncoder'; +import type {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoder'; import {BinaryEncoderCodegenContext, type BinaryEncoderCodegenContextOptions} from './BinaryEncoderCodegenContext'; export interface MessagePackEncoderCodegenContextOptions extends BinaryEncoderCodegenContextOptions {} diff --git a/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts index 9e773fafbe..2c56ad4c02 100644 --- a/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts @@ -1,10 +1,10 @@ import {TypeSystem} from '../../../system'; -import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder'; -import {CborDecoder} from '../../../../json-pack/cbor/CborDecoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; -import {EncodingFormat} from '../../../../json-pack/constants'; -import {Writer} from '../../../../util/buffers/Writer'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; const writer = new Writer(1); const encoder = new CborEncoder(writer); diff --git a/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts index eb713de584..5ef8d2067d 100644 --- a/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts @@ -1,9 +1,9 @@ import {TypeSystem} from '../../../system'; import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; -import {EncodingFormat} from '../../../../json-pack/constants'; -import {JsonEncoder} from '../../../../json-pack/json/JsonEncoder'; -import {Writer} from '../../../../util/buffers/Writer'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {parse} from '../../../../json-binary'; const encoder = new JsonEncoder(new Writer(16)); diff --git a/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts index 59bf233cbf..e7f4cba553 100644 --- a/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts @@ -1,10 +1,10 @@ import {TypeSystem} from '../../../system'; -import {MsgPackEncoder} from '../../../../json-pack/msgpack/MsgPackEncoder'; -import {MsgPackDecoder} from '../../../../json-pack/msgpack/MsgPackDecoder'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoder'; +import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder'; import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; -import {EncodingFormat} from '../../../../json-pack/constants'; -import {Writer} from '../../../../util/buffers/Writer'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; const writer = new Writer(64); const encoder = new MsgPackEncoder(writer); diff --git a/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts b/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts index 1d8d8d66de..21b3a9d705 100644 --- a/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts +++ b/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts @@ -1,6 +1,6 @@ import {Codegen, CodegenStepExecJs} from '../../../util/codegen'; -import {asString} from '../../../util/strings/asString'; -import {toBase64} from '../../../util/base64'; +import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; +import {toBase64} from '@jsonjoy.com/base64/lib/toBase64'; import type {TypeSystem} from '../../system'; import type {Type} from '../../type'; import type {json_string} from '../../../json-brand'; diff --git a/src/json-type/codegen/types.ts b/src/json-type/codegen/types.ts index 4bb281d82a..ac8f6dd65a 100644 --- a/src/json-type/codegen/types.ts +++ b/src/json-type/codegen/types.ts @@ -1,3 +1,3 @@ -import {BinaryJsonEncoder} from '../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; export type CompiledBinaryEncoder = (value: unknown, encoder: BinaryJsonEncoder) => void; diff --git a/src/json-type/type/classes/AbstractType.ts b/src/json-type/type/classes/AbstractType.ts index 12fdc3d5c8..14232589c0 100644 --- a/src/json-type/type/classes/AbstractType.ts +++ b/src/json-type/type/classes/AbstractType.ts @@ -18,23 +18,23 @@ import { JsonEncoderCodegenContext, JsonEncoderCodegenContextOptions, } from '../../codegen/binary/JsonEncoderCodegenContext'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import { MessagePackEncoderCodegenContext, MessagePackEncoderCodegenContextOptions, } from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {MsgPackEncoder} from '../../../json-pack/msgpack'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; import {lazy} from '../../../util/lazyFunction'; -import {EncodingFormat} from '../../../json-pack/constants'; -import {JsonEncoder} from '../../../json-pack/json/JsonEncoder'; -import {Writer} from '../../../util/buffers/Writer'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import { CapacityEstimatorCodegenContext, CapacityEstimatorCodegenContextOptions, CompiledCapacityEstimator, } from '../../codegen/capacity/CapacityEstimatorCodegenContext'; -import {JsonValueCodec} from '../../../json-pack/codecs/types'; +import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type * as jsonSchema from '../../../json-schema'; import type {BaseType} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; diff --git a/src/json-type/type/classes/AnyType.ts b/src/json-type/type/classes/AnyType.ts index 1bd35812a2..0efd7d4ec6 100644 --- a/src/json-type/type/classes/AnyType.ts +++ b/src/json-type/type/classes/AnyType.ts @@ -7,10 +7,10 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {EncodingFormat} from '../../../json-pack/constants'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; diff --git a/src/json-type/type/classes/ArrayType.ts b/src/json-type/type/classes/ArrayType.ts index ab20ab3a39..f1b1dd969e 100644 --- a/src/json-type/type/classes/ArrayType.ts +++ b/src/json-type/type/classes/ArrayType.ts @@ -8,9 +8,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/BinaryType.ts b/src/json-type/type/classes/BinaryType.ts index bb5edd39a1..dc98969d19 100644 --- a/src/json-type/type/classes/BinaryType.ts +++ b/src/json-type/type/classes/BinaryType.ts @@ -10,9 +10,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/BooleanType.ts b/src/json-type/type/classes/BooleanType.ts index 0bbd56f726..9697fa2aef 100644 --- a/src/json-type/type/classes/BooleanType.ts +++ b/src/json-type/type/classes/BooleanType.ts @@ -8,9 +8,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/ConstType.ts b/src/json-type/type/classes/ConstType.ts index b674173f44..533ff44380 100644 --- a/src/json-type/type/classes/ConstType.ts +++ b/src/json-type/type/classes/ConstType.ts @@ -9,9 +9,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {maxEncodingCapacity} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/MapType.ts b/src/json-type/type/classes/MapType.ts index f75afc0497..61355bcb8a 100644 --- a/src/json-type/type/classes/MapType.ts +++ b/src/json-type/type/classes/MapType.ts @@ -1,7 +1,7 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; import {printTree} from '../../../util/print/printTree'; -import {asString} from '../../../util/strings/asString'; +import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; import {validateTType} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; @@ -10,9 +10,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/NumberType.ts b/src/json-type/type/classes/NumberType.ts index 4b63f9c855..fed3fd990f 100644 --- a/src/json-type/type/classes/NumberType.ts +++ b/src/json-type/type/classes/NumberType.ts @@ -8,9 +8,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/ObjectType.ts b/src/json-type/type/classes/ObjectType.ts index 4bb3881893..f1b5822449 100644 --- a/src/json-type/type/classes/ObjectType.ts +++ b/src/json-type/type/classes/ObjectType.ts @@ -1,17 +1,17 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; import {printTree} from '../../../util/print/printTree'; -import {asString} from '../../../util/strings/asString'; +import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; import {validateTType, validateWithValidator} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; import {ValidationError} from '../../constants'; -import {normalizeAccessor} from '../../../util/codegen/util/normalizeAccessor'; +import {normalizeAccessor} from '@jsonjoy.com/json-pack/lib/util/codegen/util/normalizeAccessor'; import {canSkipObjectKeyUndefinedCheck} from '../../codegen/validator/util'; import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderCodegenContext'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead, maxEncodingCapacity} from '../../../json-size'; diff --git a/src/json-type/type/classes/OrType.ts b/src/json-type/type/classes/OrType.ts index 97d37bc7ac..28c01bda53 100644 --- a/src/json-type/type/classes/OrType.ts +++ b/src/json-type/type/classes/OrType.ts @@ -8,9 +8,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {JsonExpressionCodegen} from '../../../json-expression'; import {operatorsMap} from '../../../json-expression/operators'; diff --git a/src/json-type/type/classes/RefType.ts b/src/json-type/type/classes/RefType.ts index be1033e66f..8eaa3dea86 100644 --- a/src/json-type/type/classes/RefType.ts +++ b/src/json-type/type/classes/RefType.ts @@ -8,10 +8,10 @@ import {CompiledBinaryEncoder} from '../../codegen/types'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {EncodingFormat} from '../../../json-pack/constants'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; diff --git a/src/json-type/type/classes/StringType.ts b/src/json-type/type/classes/StringType.ts index 697622f645..fda53ff6b3 100644 --- a/src/json-type/type/classes/StringType.ts +++ b/src/json-type/type/classes/StringType.ts @@ -1,6 +1,6 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; -import {asString} from '../../../util/strings/asString'; +import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; import {validateMinMax, validateTType, validateWithValidator} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; @@ -9,9 +9,9 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; -import {BinaryJsonEncoder} from '../../../json-pack/types'; +import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; diff --git a/src/json-type/type/classes/TupleType.ts b/src/json-type/type/classes/TupleType.ts index 3aed7324af..5adea83dd8 100644 --- a/src/json-type/type/classes/TupleType.ts +++ b/src/json-type/type/classes/TupleType.ts @@ -7,7 +7,7 @@ import {ValidationError} from '../../constants'; import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderCodegenContext'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; -import {JsExpression} from '../../../util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; diff --git a/src/json-type/typescript/toText.ts b/src/json-type/typescript/toText.ts index 4e3710eb57..9c40ba6dd9 100644 --- a/src/json-type/typescript/toText.ts +++ b/src/json-type/typescript/toText.ts @@ -1,4 +1,4 @@ -import {wordWrap} from '../../util/strings/wordWrap'; +import {wordWrap} from '@jsonjoy.com/json-pack/lib/util/strings/wordWrap'; import {TsIdentifier, TsNode, TsParameter} from './types'; import {TAB, isSimpleType, normalizeKey} from './util'; diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts index b989653b43..037105b016 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts @@ -5,10 +5,10 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApiTests'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '../../../../../util/buffers/Writer'; -import {Codecs} from '../../../../../json-pack/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '../../../../../json-pack/codecs/types'; +import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import {FetchRpcClient} from '../../../../common/rpc/client/FetchRpcClient'; if (process.env.TEST_E2E) { diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts index 924d564e86..ec55936d1d 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts @@ -6,10 +6,10 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApi import {StreamingRpcClient} from '../../../../common'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '../../../../../util/buffers/Writer'; -import {Codecs} from '../../../../../json-pack/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '../../../../../json-pack/codecs/types'; +import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; if (process.env.TEST_E2E) { const codecs = new RpcCodecs(new Codecs(new Writer()), new RpcMessageCodecs()); diff --git a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts index 73c63d20f8..381b56665f 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts @@ -6,10 +6,10 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApi import WebSocket from 'ws'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '../../../../../util/buffers/Writer'; -import {Codecs} from '../../../../../json-pack/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '../../../../../json-pack/codecs/types'; +import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import {RpcCodec} from '../../../../common/codec/RpcCodec'; import {RpcPersistentClient, WebSocketChannel} from '../../../../common'; diff --git a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts b/src/reactive-rpc/browser/createBinaryWsRpcClient.ts index dbe9cec554..457c21fc88 100644 --- a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts +++ b/src/reactive-rpc/browser/createBinaryWsRpcClient.ts @@ -1,5 +1,5 @@ -import {CborJsonValueCodec} from '../../json-pack/codecs/cbor'; -import {Writer} from '../../util/buffers/Writer'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RpcPersistentClient, WebSocketChannel} from '../common'; import {RpcCodec} from '../common/codec/RpcCodec'; import {BinaryRpcMessageCodec} from '../common/codec/binary'; diff --git a/src/reactive-rpc/browser/createJsonWsRpcClient.ts b/src/reactive-rpc/browser/createJsonWsRpcClient.ts index ec8cdb07b7..d1515cbe9e 100644 --- a/src/reactive-rpc/browser/createJsonWsRpcClient.ts +++ b/src/reactive-rpc/browser/createJsonWsRpcClient.ts @@ -1,5 +1,5 @@ -import {JsonJsonValueCodec} from '../../json-pack/codecs/json'; -import {Writer} from '../../util/buffers/Writer'; +import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RpcPersistentClient, WebSocketChannel} from '../common'; import {RpcCodec} from '../common/codec/RpcCodec'; import {CompactRpcMessageCodec} from '../common/codec/compact'; diff --git a/src/reactive-rpc/common/channel/channel.ts b/src/reactive-rpc/common/channel/channel.ts index 10690944f1..b153f6bfd8 100644 --- a/src/reactive-rpc/common/channel/channel.ts +++ b/src/reactive-rpc/common/channel/channel.ts @@ -1,6 +1,6 @@ import type {WebSocketBase, CloseEventBase} from './types'; import {Subject, ReplaySubject, BehaviorSubject, Observable, from} from 'rxjs'; -import {toUint8Array} from '../../../util/buffers/toUint8Array'; +import {toUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/toUint8Array'; import {delay, filter, map, skip, switchMap, take, takeUntil, tap} from 'rxjs/operators'; export const enum ChannelState { diff --git a/src/reactive-rpc/common/channel/mock.ts b/src/reactive-rpc/common/channel/mock.ts index f1799db9ca..ad6f978679 100644 --- a/src/reactive-rpc/common/channel/mock.ts +++ b/src/reactive-rpc/common/channel/mock.ts @@ -1,5 +1,5 @@ import {WebSocketState} from './constants'; -import {utf8Size} from '../../../util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; export interface CreateWebSocketMockParams { onClose: (code?: number, reason?: string) => void; diff --git a/src/reactive-rpc/common/codec/RpcCodec.ts b/src/reactive-rpc/common/codec/RpcCodec.ts index 7dba4e63d6..cf93a6e3a4 100644 --- a/src/reactive-rpc/common/codec/RpcCodec.ts +++ b/src/reactive-rpc/common/codec/RpcCodec.ts @@ -1,6 +1,6 @@ import type {RpcSpecifier} from '../rpc'; import type {ReactiveRpcMessage} from '../messages'; -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {RpcMessageCodec} from './types'; export class RpcCodec { diff --git a/src/reactive-rpc/common/codec/RpcCodecs.ts b/src/reactive-rpc/common/codec/RpcCodecs.ts index 9116692c02..580ebc261b 100644 --- a/src/reactive-rpc/common/codec/RpcCodecs.ts +++ b/src/reactive-rpc/common/codec/RpcCodecs.ts @@ -1,4 +1,4 @@ -import {Codecs} from '../../../json-pack/codecs/Codecs'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodecs} from './RpcMessageCodecs'; export class RpcCodecs { diff --git a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts b/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts index 54028225a7..10485a5e04 100644 --- a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts +++ b/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts @@ -1,9 +1,9 @@ import {RpcMessageFormat} from '../constants'; import {decode} from './decode'; import * as msg from '../../messages'; -import type {Uint8ArrayCut} from '../../../../util/buffers/Uint8ArrayCut'; +import type {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '../../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; export class BinaryRpcMessageCodec implements RpcMessageCodec { id = 'rx.binary'; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts index c93c2344e7..e67bc5121e 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts @@ -1,5 +1,5 @@ -import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RequestCompleteMessage} from '../../../messages'; import {BinaryRpcMessageCodec} from '../BinaryRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts index b1c0c6bf94..49424177ad 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts @@ -1,6 +1,6 @@ import {BinaryRpcMessageCodec} from '..'; -import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {messages} from '../../../messages/__tests__/fixtures'; const codec = new BinaryRpcMessageCodec(); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts index b4cc363232..e406657c7f 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts @@ -11,10 +11,10 @@ import { } from '../../../messages'; import {RpcValue} from '../../../messages/Value'; import {decode} from '../decode'; -import {Reader} from '../../../../../util/buffers/Reader'; -import {Uint8ArrayCut} from '../../../../../util/buffers/Uint8ArrayCut'; -import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; +import {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; const codec = new CborJsonValueCodec(new Writer(64)); const encoder = codec.encoder; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts index 82f9306803..789373112e 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts @@ -6,8 +6,8 @@ import { ResponseDataMessage, } from '../../../messages'; import {RpcValue} from '../../../messages/Value'; -import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; const cborCodec = new CborJsonValueCodec(new Writer(64)); const encoder = cborCodec.encoder; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts index bfcc6ab7a0..8a2f839f23 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts @@ -1,6 +1,6 @@ import {BinaryRpcMessageCodec} from '..'; -import {Writer} from '../../../../../util/buffers/Writer'; -import {Codecs} from '../../../../../json-pack/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import { NotificationMessage, ReactiveRpcMessage, diff --git a/src/reactive-rpc/common/codec/binary/decode.ts b/src/reactive-rpc/common/codec/binary/decode.ts index 8d16aab109..7c5e3b5236 100644 --- a/src/reactive-rpc/common/codec/binary/decode.ts +++ b/src/reactive-rpc/common/codec/binary/decode.ts @@ -1,4 +1,4 @@ -import {Uint8ArrayCut} from '../../../../util/buffers/Uint8ArrayCut'; +import {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; import { NotificationMessage, ReactiveRpcMessage, @@ -13,7 +13,7 @@ import { } from '../../messages'; import {RpcValue} from '../../messages/Value'; import {BinaryMessageType} from './constants'; -import type {Reader} from '../../../../util/buffers/Reader'; +import type {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; export const decode = (reader: Reader): ReactiveRpcMessage => { const word = reader.u32(); diff --git a/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts b/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts index fa510b0ec2..0a0c790e60 100644 --- a/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts +++ b/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts @@ -3,11 +3,11 @@ import {RpcError, RpcErrorCodes} from '../../rpc/caller/error'; import * as msg from '../../messages'; import {CompactMessageType} from './constants'; import {RpcValue} from '../../messages/Value'; -import {CborEncoder} from '../../../../json-pack/cbor/CborEncoder'; -import {MsgPackEncoder} from '../../../../json-pack/msgpack'; -import {JsonEncoder} from '../../../../json-pack/json/JsonEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '../../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type * as types from './types'; const fromJson = (arr: unknown | unknown[] | types.CompactMessage): msg.ReactiveRpcMessage => { diff --git a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts index f28f6ff441..c1a2450534 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts @@ -1,5 +1,5 @@ -import {MsgPackJsonValueCodec} from '../../../../../json-pack/codecs/msgpack'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {MsgPackJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/msgpack'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RequestCompleteMessage} from '../../../messages'; import {CompactRpcMessageCodec} from '../CompactRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts index 5e02238d7a..4be20127eb 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts @@ -1,7 +1,7 @@ import {compactMessages} from './compact-messages'; import {CompactRpcMessageCodec} from '..'; -import {CborJsonValueCodec} from '../../../../../json-pack/codecs/cbor'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {messages} from '../../../messages/__tests__/fixtures'; const codec = new CompactRpcMessageCodec(); diff --git a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts index 0b1e4ef90a..bcb531186e 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts @@ -1,6 +1,6 @@ import {CompactRpcMessageCodec} from '..'; -import {Writer} from '../../../../../util/buffers/Writer'; -import {Codecs} from '../../../../../json-pack/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import { NotificationMessage, ReactiveRpcMessage, diff --git a/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts b/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts index 10bc98eaca..d5d2ab7237 100644 --- a/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts +++ b/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts @@ -1,13 +1,13 @@ import {RpcMessageFormat} from '../constants'; import {RpcError} from '../../rpc/caller/error'; import {RpcValue} from '../../messages/Value'; -import {EncodingFormat} from '../../../../json-pack/constants'; -import {TlvBinaryJsonEncoder} from '../../../../json-pack/types'; -import {JsonJsonValueCodec} from '../../../../json-pack/codecs/json'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {TlvBinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; +import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; import * as msg from '../../messages'; import * as schema from './schema'; import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '../../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; export class JsonRpc2RpcMessageCodec implements RpcMessageCodec { id = 'json2.verbose'; diff --git a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts index 4a2e536937..fd548252c5 100644 --- a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts @@ -1,6 +1,6 @@ import {JsonRpc2RpcMessageCodec} from '..'; -import {JsonJsonValueCodec} from '../../../../../json-pack/codecs/json'; -import {Writer} from '../../../../../util/buffers/Writer'; +import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {ReactiveRpcMessage} from '../../../messages'; import {messages} from '../../../messages/__tests__/fixtures'; diff --git a/src/reactive-rpc/common/codec/types.ts b/src/reactive-rpc/common/codec/types.ts index 10b91fa9f7..543f62638b 100644 --- a/src/reactive-rpc/common/codec/types.ts +++ b/src/reactive-rpc/common/codec/types.ts @@ -1,4 +1,4 @@ -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {ReactiveRpcMessage} from '../messages'; import type {RpcMessageFormat} from './constants'; diff --git a/src/reactive-rpc/common/messages/messages.ts b/src/reactive-rpc/common/messages/messages.ts index e6af90e2ba..309e232b32 100644 --- a/src/reactive-rpc/common/messages/messages.ts +++ b/src/reactive-rpc/common/messages/messages.ts @@ -1,12 +1,12 @@ import {BinaryMessageType} from '../codec/binary/constants'; import {CompactMessageType} from '../codec/compact/constants'; import {validateId, validateMethod} from '../rpc/validation'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import {MsgPackEncoder} from '../../../json-pack/msgpack'; -import {JsonEncoder} from '../../../json-pack/json/JsonEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import type {RpcValue} from './Value'; -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; -import type {BinaryJsonEncoder} from '../../../json-pack/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; +import type {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import type * as cmsg from '../codec/compact/types'; import type {Message} from './types'; diff --git a/src/reactive-rpc/common/messages/types.ts b/src/reactive-rpc/common/messages/types.ts index ad460f182e..2c36fd2741 100644 --- a/src/reactive-rpc/common/messages/types.ts +++ b/src/reactive-rpc/common/messages/types.ts @@ -1,4 +1,4 @@ -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type * as msg from './messages'; /** diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts index 3ae307e766..9eefc5dd2c 100644 --- a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts +++ b/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts @@ -5,8 +5,8 @@ import {RequestCompleteMessage} from '../..'; import {until} from '../../../../__tests__/util'; import {RpcValue} from '../../messages/Value'; import {RpcCodec} from '../../codec/RpcCodec'; -import {Codecs} from '../../../../json-pack/codecs/Codecs'; -import {Writer} from '../../../../util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RpcMessageCodecs} from '../../codec/RpcMessageCodecs'; test('on remote method execution, sends message over WebSocket only once', async () => { diff --git a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts b/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts index 3cb8b62dad..db3f7cc264 100644 --- a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts +++ b/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts @@ -1,5 +1,5 @@ -import {Codecs} from '../../../../../../json-pack/codecs/Codecs'; -import {Writer} from '../../../../../../util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {RpcError} from '../RpcError'; import {RpcErrorType} from '../RpcErrorType'; diff --git a/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts b/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts index 04eb5feb46..3f36acae67 100644 --- a/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts +++ b/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts @@ -1,7 +1,7 @@ import * as msg from '../../messages'; import {StaticRpcClient} from './StaticRpcClient'; import {RpcMessageCodec} from '../../codec/types'; -import {JsonValueCodec} from '../../../../json-pack/codecs/types'; +import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {Observable} from 'rxjs'; import type {RpcClient} from './types'; diff --git a/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts b/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts index 9082e6a873..eed94b78fb 100644 --- a/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts +++ b/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts @@ -1,7 +1,7 @@ import {StaticRpcClient, StaticRpcClientOptions} from './StaticRpcClient'; import {EncodedStaticRpcClient} from './EncodedStaticRpcClient'; import type {RpcMessageCodec} from '../../codec/types'; -import type {JsonValueCodec} from '../../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {Observable} from 'rxjs'; import type {RpcClient} from './types'; diff --git a/src/reactive-rpc/common/testing/buildE2eClient.ts b/src/reactive-rpc/common/testing/buildE2eClient.ts index 9cf624db8a..9a003f8783 100644 --- a/src/reactive-rpc/common/testing/buildE2eClient.ts +++ b/src/reactive-rpc/common/testing/buildE2eClient.ts @@ -1,6 +1,6 @@ -import {Codecs} from '../../../json-pack/codecs/Codecs'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {Fuzzer} from '../../../util/Fuzzer'; -import {Writer} from '../../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {ConnectionContext} from '../../server/context'; import {RpcCodecs} from '../codec/RpcCodecs'; import {RpcMessageCodecs} from '../codec/RpcMessageCodecs'; diff --git a/src/reactive-rpc/server/context.ts b/src/reactive-rpc/server/context.ts index d4f7a6618a..33c656c8ab 100644 --- a/src/reactive-rpc/server/context.ts +++ b/src/reactive-rpc/server/context.ts @@ -1,7 +1,7 @@ import {NullObject} from '../../util/NullObject'; -import {copy} from '../../util/buffers/copy'; -import {listToUint8} from '../../util/buffers/concat'; -import type {JsonValueCodec} from '../../json-pack/codecs/types'; +import {copy} from '@jsonjoy.com/json-pack/lib/util/buffers/copy'; +import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {RpcMessageCodec} from '../common/codec/types'; import type {RpcApp} from './uws/RpcApp'; import type {HttpRequest, HttpResponse} from './uws/types'; diff --git a/src/reactive-rpc/server/http1/Http1Server.ts b/src/reactive-rpc/server/http1/Http1Server.ts index 04fca023d4..1d9a4342e1 100644 --- a/src/reactive-rpc/server/http1/Http1Server.ts +++ b/src/reactive-rpc/server/http1/Http1Server.ts @@ -1,8 +1,9 @@ import * as http from 'http'; import * as net from 'net'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {WsServerConnection} from '../ws/server/WsServerConnection'; import {WsFrameEncoder} from '../ws/codec/WsFrameEncoder'; -import {Writer} from '../../../util/buffers/Writer'; import {RouteMatcher} from '../../../util/router/codegen'; import {Router} from '../../../util/router'; import {Printable} from '../../../util/print/types'; @@ -11,7 +12,6 @@ import {PayloadTooLarge} from './errors'; import {findTokenInText, setCodecs} from './util'; import {Http1ConnectionContext, WsConnectionContext} from './context'; import {RpcCodecs} from '../../common/codec/RpcCodecs'; -import {Codecs} from '../../../json-pack/codecs/Codecs'; import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; import {NullObject} from '../../../util/NullObject'; diff --git a/src/reactive-rpc/server/http1/context.ts b/src/reactive-rpc/server/http1/context.ts index ad1acf19e5..85e4063e3c 100644 --- a/src/reactive-rpc/server/http1/context.ts +++ b/src/reactive-rpc/server/http1/context.ts @@ -1,7 +1,7 @@ import {getBody} from './util'; -import {listToUint8} from '../../../util/buffers/concat'; +import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; import type * as http from 'http'; -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {RpcMessageCodec} from '../../common/codec/types'; import type {WsServerConnection} from '../ws/server/WsServerConnection'; diff --git a/src/reactive-rpc/server/uws/RpcApp.ts b/src/reactive-rpc/server/uws/RpcApp.ts index 8357dc8eb3..0aaac8af39 100644 --- a/src/reactive-rpc/server/uws/RpcApp.ts +++ b/src/reactive-rpc/server/uws/RpcApp.ts @@ -6,14 +6,14 @@ import {ConnectionContext} from '../context'; import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; import {RpcValue} from '../../common/messages/Value'; import {RpcCodecs} from '../../common/codec/RpcCodecs'; -import {Codecs} from '../../../json-pack/codecs/Codecs'; -import {Writer} from '../../../util/buffers/Writer'; -import {copy} from '../../../util/buffers/copy'; +import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {copy} from '@jsonjoy.com/json-pack/lib/util/buffers/copy'; import {type ReactiveRpcMessage, RpcMessageStreamProcessor, ReactiveRpcClientMessage} from '../../common'; import type * as types from './types'; import type {RouteHandler} from './types'; import type {RpcCaller} from '../../common/rpc/caller/RpcCaller'; -import type {JsonValueCodec} from '../../../json-pack/codecs/types'; +import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; const HDR_BAD_REQUEST = Buffer.from('400 Bad Request', 'utf8'); const HDR_NOT_FOUND = Buffer.from('404 Not Found', 'utf8'); diff --git a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts index e9bedbf767..7025a4485d 100644 --- a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts +++ b/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts @@ -1,4 +1,4 @@ -import {StreamingOctetReader} from '../../../../util/buffers/StreamingOctetReader'; +import {StreamingOctetReader} from '@jsonjoy.com/json-pack/lib/util/buffers/StreamingOctetReader'; import {WsFrameOpcode} from './constants'; import {WsFrameDecodingError} from './errors'; import {WsCloseFrame, WsFrameHeader, WsPingFrame, WsPongFrame} from './frames'; diff --git a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts index b706d1acf0..f92ddbbc4a 100644 --- a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts +++ b/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts @@ -1,7 +1,7 @@ -import {Writer} from '../../../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {WsFrameOpcode} from './constants'; -import type {IWriter, IWriterGrowable} from '../../../../util/buffers'; import {WsFrameEncodingError} from './errors'; +import type {IWriter, IWriterGrowable} from '@jsonjoy.com/json-pack/lib/util/buffers'; const maskBuf = new Uint8Array(4); const maskBufView = new DataView(maskBuf.buffer, maskBuf.byteOffset, maskBuf.byteLength); diff --git a/src/reactive-rpc/server/ws/server/WsServerConnection.ts b/src/reactive-rpc/server/ws/server/WsServerConnection.ts index cdd092136e..0919f0487c 100644 --- a/src/reactive-rpc/server/ws/server/WsServerConnection.ts +++ b/src/reactive-rpc/server/ws/server/WsServerConnection.ts @@ -1,8 +1,8 @@ import * as crypto from 'crypto'; import * as stream from 'stream'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; import {WsCloseFrame, WsFrameDecoder, WsFrameHeader, WsFrameOpcode, WsPingFrame, WsPongFrame} from '../codec'; -import {utf8Size} from '../../../../util/strings/utf8'; -import {listToUint8} from '../../../../util/buffers/concat'; import type {WsFrameEncoder} from '../codec/WsFrameEncoder'; export type WsServerConnectionSocket = stream.Duplex; diff --git a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts b/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts index 4b50147b73..e6aeee348e 100644 --- a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts +++ b/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts @@ -3,8 +3,8 @@ import {WsServerConnection} from '../WsServerConnection'; import {WsFrameEncoder} from '../../codec/WsFrameEncoder'; import {until} from 'thingies'; import {WsFrameOpcode} from '../../codec'; -import {bufferToUint8Array} from '../../../../../util/buffers/bufferToUint8Array'; -import {listToUint8} from '../../../../../util/buffers/concat'; +import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; const setup = () => { const socket = new stream.PassThrough(); diff --git a/src/util/base64/README.md b/src/util/base64/README.md deleted file mode 100644 index ba368cec0a..0000000000 --- a/src/util/base64/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Base64 - - -## Encoder - -- Implements Base64 encoding algorithm compatible with Node's Buffer. -- Isomorphic—it can be used in, both, Node and the browser. -- Faster than the Node's implementation for short blobs, smaller than 40 bytes. -- Uses Node's implementation for long blobs, if available. Hence, it also works - in browser, but in Node environment will perform faster for short strings. - - -### Usage - -Use encoder compatible with Node's Buffer: - -```ts -import {toBase64} from 'json-joy/lib/util/base64'; - -toBase64(new Uint8Array([1, 2, 3])); -``` - -Create your custom encoder: - -```ts -import {createToBase64} from 'json-joy/lib/util/base64'; - -const encode = createToBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_'); - -encode(new Uint8Array([1, 2, 3])); -``` - - -### Benchmark - -Below benchmark encodes random binary blobs of sizes 8, 16, 32, 64, 128, 256, 512, and 1024 byes. -`json-joy/util/base64` is faster, because for short strings (less than 40 chars) it uses a -native JavaScript implementation, which is faster and also works in browsers. For blobs larger -than 40 chars, it falls back to Node `Buffer` implementation, if available. - -Encoding: - -``` -node benchmarks/util/base64/encode.js -json-joy/util/base64 toBase64(uint8) x 1,257,419 ops/sec ยฑ1.19% (93 runs sampled), 795 ns/op -json-joy/util/base64 createToBase64()(uint8) x 868,953 ops/sec ยฑ0.96% (96 runs sampled), 1151 ns/op -js-base64 x 974,991 ops/sec ยฑ0.73% (94 runs sampled), 1026 ns/op -fast-base64-encode x 428,545 ops/sec ยฑ1.67% (96 runs sampled), 2333 ns/op -base64-js x 295,165 ops/sec ยฑ1.59% (98 runs sampled), 3388 ns/op -Buffer.from(uint8).toString('base64'); x 973,173 ops/sec ยฑ0.65% (95 runs sampled), 1028 ns/op -Fastest is json-joy/util/base64 toBase64(uint8) -``` - -Decoding: - -``` -node benchmarks/util/base64/decode.js -json-joy/util/base64 fromBase64(str) x 602,268 ops/sec ยฑ1.09% (88 runs sampled), 1660 ns/op -json-joy/util/base64 createFromBase64()(str) x 392,345 ops/sec ยฑ0.96% (91 runs sampled), 2549 ns/op -Buffer.from(str, 'base64') x 498,609 ops/sec ยฑ1.66% (93 runs sampled), 2006 ns/op -base64-js x 439,246 ops/sec ยฑ0.94% (89 runs sampled), 2277 ns/op -js-base64 x 151,694 ops/sec ยฑ0.51% (99 runs sampled), 6592 ns/op -Fastest is json-joy/util/base64 fromBase64(str) -``` - - -## Decoder - -- Uses Node.js built-in `Buffer`, if available. -- When `Buffer` is not available, uses JavaScript implementation. - - -### Usage - -Use decoder compatible with Node's Buffer: - -```ts -import {toBase64, fromBase64} from 'json-joy/lib/util/base64'; - -fromBase64(toBase64(new Uint8Array([1, 2, 3]))); -``` - -Create your custom encoder: - -```ts -import {createFromBase64} from 'json-joy/lib/util/base64'; - -const decoder = createFromBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_'); - -decoder(toBase64(new Uint8Array([1, 2, 3]))); -``` diff --git a/src/util/base64/__bench__/decode.js b/src/util/base64/__bench__/decode.js deleted file mode 100644 index 9b6de6dfa0..0000000000 --- a/src/util/base64/__bench__/decode.js +++ /dev/null @@ -1,77 +0,0 @@ -const Benchmark = require('benchmark'); -const toBase64 = require('../../../../lib/util/base64').toBase64; -const {bufferToUint8Array} = require('../../../../lib/util/buffers/bufferToUint8Array'); -const {fromBase64, createFromBase64} = require('../../../../lib/util/base64'); -const {toByteArray} = require('base64-js'); -const {decode: decodeJsBase64} = require('js-base64'); - -const fromBase642 = createFromBase64(); - -const generateBlob = (length) => { - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -const str4 = toBase64(generateBlob(4)); -const str8 = toBase64(generateBlob(8)); -const str16 = toBase64(generateBlob(16)); -const str24 = toBase64(generateBlob(24)); -const str32 = toBase64(generateBlob(32)); -const str64 = toBase64(generateBlob(64)); -const str128 = toBase64(generateBlob(128)); -const str256 = toBase64(generateBlob(256)); - -const suite = new Benchmark.Suite(); - -const encoders = [ - { - name: `json-joy/util/base64 fromBase64(str)`, - decode: (str) => fromBase64(str), - }, - { - name: `json-joy/util/base64 createFromBase64()(str)`, - decode: (str) => fromBase642(str), - }, - { - name: `Buffer.from(str, 'base64')`, - decode: (str) => bufferToUint8Array(Buffer.from(str, 'base64')), - }, - { - name: `base64-js`, - decode: (str) => toByteArray(str), - }, - { - name: `js-base64`, - decode: (str) => decodeJsBase64(str), - }, -]; - -for (const encoder of encoders) { - // Warm up - for (let i = 0; i < 100000; i++) { - encoder.decode(str8); - encoder.decode(str256); - } - suite.add(encoder.name, () => { - encoder.decode(str4); - encoder.decode(str8); - encoder.decode(str16); - encoder.decode(str24); - encoder.decode(str32); - encoder.decode(str64); - encoder.decode(str128); - encoder.decode(str256); - }); -} - -suite - .on('cycle', function (event) { - console.log(String(event.target) + `, ${Math.round(1000000000 / event.target.hz)} ns/op`); - }) - .on('complete', function () { - console.log('Fastest is ' + this.filter('fastest').map('name')); - }) - .run(); diff --git a/src/util/base64/__bench__/encode.js b/src/util/base64/__bench__/encode.js deleted file mode 100644 index 07fbd4edde..0000000000 --- a/src/util/base64/__bench__/encode.js +++ /dev/null @@ -1,98 +0,0 @@ -const Benchmark = require('benchmark'); -const {toBase64, createToBase64} = require('../../../../lib/util/base64'); -const {fromByteArray} = require('base64-js'); -const {encode: encodeJsBase64} = require('js-base64'); - -const toBase64Native = createToBase64(); - -const generateBlob = (length) => { - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -const arr8 = generateBlob(9); -const arr16 = generateBlob(17); -const arr32 = generateBlob(33); -const arr64 = generateBlob(65); -const arr128 = generateBlob(127); -const arr256 = generateBlob(257); -const arr512 = generateBlob(513); -const arr1024 = generateBlob(1025); - -// fast-base64-encode -const table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); -const fastBase64Encode = (source) => { - let out = ''; - let tmp; - const length = source.byteLength; - const extraLength = length % 3; - const baseLength = length - extraLength; - for (let i = 0; i < baseLength; i += 3) { - tmp = ((source[i] & 0xff) << 16) | ((source[i + 1] & 0xff) << 8) | (source[i + 2] & 0xff); - out += table[(tmp >> 18) & 0x3f] + table[(tmp >> 12) & 0x3f] + table[(tmp >> 6) & 0x3f] + table[tmp & 0x3f]; - } - if (extraLength) { - if (extraLength === 1) { - tmp = source[baseLength] & 0xff; - out += table[tmp >> 2] + table[(tmp << 4) & 0x3f] + '=='; - } else { - tmp = ((source[baseLength] & 0xff) << 8) | (source[baseLength + 1] & 0xff); - out += table[tmp >> 10] + table[(tmp >> 4) & 0x3f] + table[(tmp << 2) & 0x3f] + '='; - } - } - return out; -}; - -const suite = new Benchmark.Suite(); - -const encoders = [ - { - name: `json-joy/util/base64 toBase64(uint8)`, - encode: (uint8) => toBase64(uint8), - }, - { - name: `json-joy/util/base64 createToBase64()(uint8)`, - encode: (uint8) => toBase64Native(uint8, uint8.length), - }, - { - name: `js-base64`, - encode: (uint8) => encodeJsBase64(uint8), - }, - { - name: `fast-base64-encode`, - encode: (uint8) => fastBase64Encode(uint8), - }, - { - name: `base64-js`, - encode: (uint8) => fromByteArray(uint8), - }, - { - name: `Buffer.from(uint8).toString('base64');`, - encode: (uint8) => Buffer.from(uint8).toString('base64'), - }, -]; - -for (const encoder of encoders) { - suite.add(encoder.name, () => { - encoder.encode(arr8); - encoder.encode(arr16); - encoder.encode(arr32); - encoder.encode(arr64); - encoder.encode(arr128); - // encoder.encode(arr256); - // encoder.encode(arr512); - // encoder.encode(arr1024); - }); -} - -suite - .on('cycle', function (event) { - console.log(String(event.target) + `, ${Math.round(1000000000 / event.target.hz)} ns/op`); - }) - .on('complete', function () { - console.log('Fastest is ' + this.filter('fastest').map('name')); - }) - .run(); diff --git a/src/util/base64/__tests__/decode-base64url.spec.ts b/src/util/base64/__tests__/decode-base64url.spec.ts deleted file mode 100644 index 3344250fc3..0000000000 --- a/src/util/base64/__tests__/decode-base64url.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {toBase64} from '../toBase64'; -import {fromBase64Url} from '../fromBase64Url'; - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100); - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const encoded = toBase64(blob).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); - const decoded2 = fromBase64Url(encoded); - expect(decoded2).toEqual(blob); - } -}); diff --git a/src/util/base64/__tests__/decode-bin.spec.ts b/src/util/base64/__tests__/decode-bin.spec.ts deleted file mode 100644 index ba0c5ffba9..0000000000 --- a/src/util/base64/__tests__/decode-bin.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {toBase64Bin} from '../toBase64Bin'; -import {fromBase64Bin} from '../fromBase64Bin'; - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100); - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const dest = new Uint8Array(blob.length * 4); - const length = toBase64Bin(blob, 0, blob.length, new DataView(dest.buffer), 0); - const encoded = dest.subarray(0, length); - const view = new DataView(encoded.buffer); - const decoded = fromBase64Bin(view, 0, encoded.length); - let padding = 0; - if (encoded.length > 0 && view.getUint8(encoded.length - 1) === 0x3d) padding++; - if (encoded.length > 1 && view.getUint8(encoded.length - 2) === 0x3d) padding++; - const decoded2 = fromBase64Bin(view, 0, encoded.length - padding); - // console.log('blob', blob); - // console.log('encoded', encoded); - // console.log('decoded', decoded); - expect(decoded).toEqual(blob); - expect(decoded2).toEqual(blob); - } -}); diff --git a/src/util/base64/__tests__/decode.spec.ts b/src/util/base64/__tests__/decode.spec.ts deleted file mode 100644 index 273857a466..0000000000 --- a/src/util/base64/__tests__/decode.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {toBase64} from '../toBase64'; -import {fromBase64} from '../fromBase64'; -import {createFromBase64} from '../createFromBase64'; - -const fromBase64_2 = createFromBase64(); - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100); - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const encoded = toBase64(blob); - const decoded1 = fromBase64_2(encoded); - const decoded2 = fromBase64(encoded); - expect(decoded1).toEqual(blob); - expect(decoded2).toEqual(blob); - } -}); - -test('handles invalid values', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const encoded = toBase64(blob); - expect(() => fromBase64_2(encoded + '!!!!')).toThrowError(new Error('INVALID_BASE64_STRING')); - } -}); diff --git a/src/util/base64/__tests__/encode-base64url.spec.ts b/src/util/base64/__tests__/encode-base64url.spec.ts deleted file mode 100644 index 1d2880f21b..0000000000 --- a/src/util/base64/__tests__/encode-base64url.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {toBase64Url} from '../toBase64Url'; - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100) + 1; - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const expected = Buffer.from(blob).toString('base64'); - const base64url = toBase64Url(blob, blob.length); - let encoded = base64url.replace(/-/g, '+').replace(/_/g, '/'); - const mod = encoded.length % 4; - if (mod === 2) encoded += '=='; - else if (mod === 3) encoded += '='; - expect(encoded).toEqual(expected); - } -}); diff --git a/src/util/base64/__tests__/encode-bin.spec.ts b/src/util/base64/__tests__/encode-bin.spec.ts deleted file mode 100644 index 6ce22c6bdd..0000000000 --- a/src/util/base64/__tests__/encode-bin.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {toBase64} from '../toBase64'; -import {createToBase64Bin} from '../createToBase64Bin'; -import {createToBase64BinUint8} from '../createToBase64BinUint8'; -import {bufferToUint8Array} from '../../buffers/bufferToUint8Array'; -import {copy} from '../../buffers/copy'; - -const encode = createToBase64Bin('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', '='); -const encodeUint8 = createToBase64BinUint8('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', '='); -const encodeNoPadding = createToBase64Bin('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100) + 1; - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const result = bufferToUint8Array(Buffer.from(toBase64(blob))); - const binWithBuffer = new Uint8Array(result.length + 3); - encode(blob, 0, blob.length, new DataView(binWithBuffer.buffer), 3); - const dupe = copy(blob); - encodeNoPadding(blob, 0, blob.length, new DataView(binWithBuffer.buffer), 3); - expect(dupe).toEqual(blob); - const dupe2 = copy(blob); - encodeUint8(blob, 0, blob.length, binWithBuffer, 3); - expect(dupe2).toEqual(blob); - const encoded = binWithBuffer.subarray(3); - // console.log(result); - // console.log(binWithBuffer); - // console.log(encoded); - expect(result).toEqual(encoded); - } -}); diff --git a/src/util/base64/__tests__/encode.spec.ts b/src/util/base64/__tests__/encode.spec.ts deleted file mode 100644 index 74a4c6231b..0000000000 --- a/src/util/base64/__tests__/encode.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {toBase64} from '../toBase64'; -import {createToBase64} from '../createToBase64'; - -const encode2 = createToBase64(); - -const generateBlob = (): Uint8Array => { - const length = Math.floor(Math.random() * 100) + 1; - const uint8 = new Uint8Array(length); - for (let i = 0; i < length; i++) { - uint8[i] = Math.floor(Math.random() * 256); - } - return uint8; -}; - -test('works', () => { - for (let i = 0; i < 100; i++) { - const blob = generateBlob(); - const result = toBase64(blob); - const result2 = encode2(blob, blob.byteLength); - const expected = Buffer.from(blob).toString('base64'); - expect(result).toBe(expected); - expect(result2).toBe(expected); - } -}); diff --git a/src/util/base64/constants.ts b/src/util/base64/constants.ts deleted file mode 100644 index 46b1926330..0000000000 --- a/src/util/base64/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -export const hasBuffer = typeof Buffer === 'function' && typeof Buffer.from === 'function'; diff --git a/src/util/base64/createFromBase64.ts b/src/util/base64/createFromBase64.ts deleted file mode 100644 index 5f4922f4e4..0000000000 --- a/src/util/base64/createFromBase64.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {alphabet} from './constants'; - -const E = '='; - -export const createFromBase64 = (chars: string = alphabet, noPadding: boolean = false) => { - if (chars.length !== 64) throw new Error('chars must be 64 characters long'); - let max = 0; - for (let i = 0; i < chars.length; i++) max = Math.max(max, chars.charCodeAt(i)); - const table: number[] = []; - for (let i = 0; i <= max; i += 1) table[i] = -1; - for (let i = 0; i < chars.length; i++) table[chars.charCodeAt(i)] = i; - - return (encoded: string): Uint8Array => { - if (!encoded) return new Uint8Array(0); - let length = encoded.length; - if (noPadding) { - const mod = length % 4; - if (mod === 2) { - encoded += '=='; - length += 2; - } else if (mod === 3) { - encoded += '='; - length += 1; - } - } - if (length % 4 !== 0) throw new Error('Base64 string length must be a multiple of 4'); - const mainLength = encoded[length - 1] !== E ? length : length - 4; - let bufferLength = (length >> 2) * 3; - let padding = 0; - if (encoded[length - 2] === E) { - padding = 2; - bufferLength -= 2; - } else if (encoded[length - 1] === E) { - padding = 1; - bufferLength -= 1; - } - const buf = new Uint8Array(bufferLength); - let j = 0; - let i = 0; - for (; i < mainLength; i += 4) { - const sextet0 = table[encoded.charCodeAt(i)]; - const sextet1 = table[encoded.charCodeAt(i + 1)]; - const sextet2 = table[encoded.charCodeAt(i + 2)]; - const sextet3 = table[encoded.charCodeAt(i + 3)]; - if (sextet0 < 0 || sextet1 < 0 || sextet2 < 0 || sextet3 < 0) throw new Error('INVALID_BASE64_STRING'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - buf[j + 1] = (sextet1 << 4) | (sextet2 >> 2); - buf[j + 2] = (sextet2 << 6) | sextet3; - j += 3; - } - if (padding === 2) { - const sextet0 = table[encoded.charCodeAt(mainLength)]; - const sextet1 = table[encoded.charCodeAt(mainLength + 1)]; - if (sextet0 < 0 || sextet1 < 0) throw new Error('INVALID_BASE64_STRING'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - } else if (padding === 1) { - const sextet0 = table[encoded.charCodeAt(mainLength)]; - const sextet1 = table[encoded.charCodeAt(mainLength + 1)]; - const sextet2 = table[encoded.charCodeAt(mainLength + 2)]; - if (sextet0 < 0 || sextet1 < 0 || sextet2 < 0) throw new Error('INVALID_BASE64_STRING'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - buf[j + 1] = (sextet1 << 4) | (sextet2 >> 2); - } - return buf; - }; -}; diff --git a/src/util/base64/createFromBase64Bin.ts b/src/util/base64/createFromBase64Bin.ts deleted file mode 100644 index ed9b018cb9..0000000000 --- a/src/util/base64/createFromBase64Bin.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {alphabet} from './constants'; - -export const createFromBase64Bin = (chars: string = alphabet, pad: string = '=') => { - if (chars.length !== 64) throw new Error('chars must be 64 characters long'); - let max = 0; - for (let i = 0; i < chars.length; i++) max = Math.max(max, chars.charCodeAt(i)); - const table: number[] = []; - for (let i = 0; i <= max; i += 1) table[i] = -1; - for (let i = 0; i < chars.length; i++) table[chars.charCodeAt(i)] = i; - - const doExpectPadding = pad.length === 1; - const PAD = doExpectPadding ? pad.charCodeAt(0) : 0; - - return (view: DataView, offset: number, length: number): Uint8Array => { - if (!length) return new Uint8Array(0); - let padding = 0; - if (length % 4 !== 0) { - padding = 4 - (length % 4); - length += padding; - } else { - const end = offset + length; - const last = end - 1; - if (view.getUint8(last) === PAD) { - padding = 1; - if (length > 1 && view.getUint8(last - 1) === PAD) padding = 2; - } - } - if (length % 4 !== 0) throw new Error('Base64 string length must be a multiple of 4'); - const mainEnd = offset + length - (padding ? 4 : 0); - const bufferLength = (length >> 2) * 3 - padding; - const buf = new Uint8Array(bufferLength); - let j = 0; - let i = offset; - for (; i < mainEnd; i += 4) { - const word = view.getUint32(i); - const octet0 = word >>> 24; - const octet1 = (word >>> 16) & 0xff; - const octet2 = (word >>> 8) & 0xff; - const octet3 = word & 0xff; - const sextet0 = table[octet0]; - const sextet1 = table[octet1]; - const sextet2 = table[octet2]; - const sextet3 = table[octet3]; - if (sextet0 < 0 || sextet1 < 0 || sextet2 < 0 || sextet3 < 0) throw new Error('INVALID_BASE64_SEQ'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - buf[j + 1] = (sextet1 << 4) | (sextet2 >> 2); - buf[j + 2] = (sextet2 << 6) | sextet3; - j += 3; - } - if (!padding) return buf; - if (padding === 1) { - const word = view.getUint16(mainEnd); - const octet0 = word >> 8; - const octet1 = word & 0xff; - const octet2 = view.getUint8(mainEnd + 2); - const sextet0 = table[octet0]; - const sextet1 = table[octet1]; - const sextet2 = table[octet2]; - if (sextet0 < 0 || sextet1 < 0 || sextet2 < 0) throw new Error('INVALID_BASE64_SEQ'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - buf[j + 1] = (sextet1 << 4) | (sextet2 >> 2); - return buf; - } - const word = view.getUint16(mainEnd); - const octet0 = word >> 8; - const octet1 = word & 0xff; - const sextet0 = table[octet0]; - const sextet1 = table[octet1]; - if (sextet0 < 0 || sextet1 < 0) throw new Error('INVALID_BASE64_SEQ'); - buf[j] = (sextet0 << 2) | (sextet1 >> 4); - return buf; - }; -}; diff --git a/src/util/base64/createToBase64.ts b/src/util/base64/createToBase64.ts deleted file mode 100644 index 83681524b4..0000000000 --- a/src/util/base64/createToBase64.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {flatstr} from '../strings/flatstr'; -import {alphabet} from './constants'; - -export const createToBase64 = (chars: string = alphabet, pad: string = '=') => { - if (chars.length !== 64) throw new Error('chars must be 64 characters long'); - - const table = chars.split(''); - const table2: string[] = []; - - for (const c1 of table) { - for (const c2 of table) { - const two = flatstr(c1 + c2); - table2.push(two); - } - } - - const E: string = pad; - const EE: string = flatstr(pad + pad); - - return (uint8: Uint8Array, length: number): string => { - let out = ''; - const extraLength = length % 3; - const baseLength = length - extraLength; - for (let i = 0; i < baseLength; i += 3) { - const o1 = uint8[i]; - const o2 = uint8[i + 1]; - const o3 = uint8[i + 2]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = ((o2 & 0b1111) << 8) | o3; - out += table2[v1] + table2[v2]; - } - if (!extraLength) return out; - if (extraLength === 1) { - const o1 = uint8[baseLength]; - out += table2[o1 << 4] + EE; - } else { - const o1 = uint8[baseLength]; - const o2 = uint8[baseLength + 1]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = (o2 & 0b1111) << 2; - out += table2[v1] + table[v2] + E; - } - return out; - }; -}; diff --git a/src/util/base64/createToBase64Bin.ts b/src/util/base64/createToBase64Bin.ts deleted file mode 100644 index 95e3a92758..0000000000 --- a/src/util/base64/createToBase64Bin.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {alphabet} from './constants'; - -export const createToBase64Bin = (chars: string = alphabet, pad: string = '=') => { - if (chars.length !== 64) throw new Error('chars must be 64 characters long'); - - const table = chars.split('').map((c) => c.charCodeAt(0)); - const table2: number[] = []; - - for (const c1 of table) { - for (const c2 of table) { - const two = (c1 << 8) + c2; - table2.push(two); - } - } - - const doAddPadding = pad.length === 1; - const E: number = doAddPadding ? pad.charCodeAt(0) : 0; - const EE: number = doAddPadding ? (E << 8) | E : 0; - - return (uint8: Uint8Array, start: number, length: number, dest: DataView, offset: number): number => { - const extraLength = length % 3; - const baseLength = length - extraLength; - for (; start < baseLength; start += 3) { - const o1 = uint8[start]; - const o2 = uint8[start + 1]; - const o3 = uint8[start + 2]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = ((o2 & 0b1111) << 8) | o3; - dest.setInt32(offset, (table2[v1] << 16) + table2[v2]); - offset += 4; - } - if (extraLength === 1) { - const o1 = uint8[baseLength]; - if (doAddPadding) { - dest.setInt32(offset, (table2[o1 << 4] << 16) + EE); - offset += 4; - } else { - dest.setInt16(offset, table2[o1 << 4]); - offset += 2; - } - } else if (extraLength) { - const o1 = uint8[baseLength]; - const o2 = uint8[baseLength + 1]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = (o2 & 0b1111) << 2; - if (doAddPadding) { - dest.setInt32(offset, (table2[v1] << 16) + (table[v2] << 8) + E); - offset += 4; - } else { - dest.setInt16(offset, table2[v1]); - offset += 2; - dest.setInt8(offset, table[v2]); - offset += 1; - } - } - return offset; - }; -}; diff --git a/src/util/base64/createToBase64BinUint8.ts b/src/util/base64/createToBase64BinUint8.ts deleted file mode 100644 index efa77615b0..0000000000 --- a/src/util/base64/createToBase64BinUint8.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {alphabet} from './constants'; - -export const createToBase64BinUint8 = (chars: string = alphabet, pad: string = '=') => { - if (chars.length !== 64) throw new Error('chars must be 64 characters long'); - - const table = chars.split('').map((c) => c.charCodeAt(0)); - const table2: number[] = []; - - for (const c1 of table) { - for (const c2 of table) { - const two = (c1 << 8) + c2; - table2.push(two); - } - } - - const PAD: number = pad.length === 1 ? pad.charCodeAt(0) : 0; - - return (uint8: Uint8Array, start: number, length: number, dest: Uint8Array, offset: number): number => { - const extraLength = length % 3; - const baseLength = length - extraLength; - for (; start < baseLength; start += 3) { - const o1 = uint8[start]; - const o2 = uint8[start + 1]; - const o3 = uint8[start + 2]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = ((o2 & 0b1111) << 8) | o3; - let u16 = table2[v1]; - dest[offset++] = u16 >> 8; - dest[offset++] = u16; - u16 = table2[v2]; - dest[offset++] = u16 >> 8; - dest[offset++] = u16; - } - if (extraLength === 1) { - const o1 = uint8[baseLength]; - const u16 = table2[o1 << 4]; - dest[offset++] = u16 >> 8; - dest[offset++] = u16; - if (PAD) { - dest[offset++] = PAD; - dest[offset++] = PAD; - } - } else if (extraLength) { - const o1 = uint8[baseLength]; - const o2 = uint8[baseLength + 1]; - const v1 = (o1 << 4) | (o2 >> 4); - const v2 = (o2 & 0b1111) << 2; - const u16 = table2[v1]; - dest[offset++] = u16 >> 8; - dest[offset++] = u16; - dest[offset++] = table[v2]; - if (PAD) dest[offset++] = PAD; - } - return offset; - }; -}; diff --git a/src/util/base64/fromBase64.ts b/src/util/base64/fromBase64.ts deleted file mode 100644 index f9a6bb8c7e..0000000000 --- a/src/util/base64/fromBase64.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {bufferToUint8Array} from '../buffers/bufferToUint8Array'; -import {hasBuffer} from './constants'; -import {createFromBase64} from './createFromBase64'; - -const fromBase64Cpp = hasBuffer ? (encoded: string) => bufferToUint8Array(Buffer.from(encoded, 'base64')) : null; -const fromBase64Native = createFromBase64(); - -export const fromBase64 = !fromBase64Cpp - ? fromBase64Native - : (encoded: string): Uint8Array => (encoded.length > 48 ? fromBase64Cpp(encoded) : fromBase64Native(encoded)); diff --git a/src/util/base64/fromBase64Bin.ts b/src/util/base64/fromBase64Bin.ts deleted file mode 100644 index 678898d5ed..0000000000 --- a/src/util/base64/fromBase64Bin.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {createFromBase64Bin} from './createFromBase64Bin'; - -export const fromBase64Bin = createFromBase64Bin(); diff --git a/src/util/base64/fromBase64Url.ts b/src/util/base64/fromBase64Url.ts deleted file mode 100644 index 12be59f819..0000000000 --- a/src/util/base64/fromBase64Url.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {createFromBase64} from './createFromBase64'; - -export const fromBase64Url = createFromBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', true); diff --git a/src/util/base64/index.ts b/src/util/base64/index.ts deleted file mode 100644 index 0c865ce8c8..0000000000 --- a/src/util/base64/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './createToBase64'; -export * from './createToBase64Bin'; -export * from './createFromBase64'; -export * from './toBase64'; -export * from './toBase64Bin'; -export * from './fromBase64'; -export * from './fromBase64Bin'; diff --git a/src/util/base64/toBase64.ts b/src/util/base64/toBase64.ts deleted file mode 100644 index be2688b3e2..0000000000 --- a/src/util/base64/toBase64.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {hasBuffer} from './constants'; -import {createToBase64} from './createToBase64'; - -const encodeSmall = createToBase64(); - -export const toBase64 = !hasBuffer - ? (uint8: Uint8Array) => encodeSmall(uint8, uint8.length) - : (uint8: Uint8Array): string => { - const length = uint8.length; - if (length <= 48) return encodeSmall(uint8, length); - return Buffer.from(uint8).toString('base64'); - }; diff --git a/src/util/base64/toBase64Bin.ts b/src/util/base64/toBase64Bin.ts deleted file mode 100644 index 012f2c24a9..0000000000 --- a/src/util/base64/toBase64Bin.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {createToBase64Bin} from './createToBase64Bin'; - -export const toBase64Bin = createToBase64Bin(); diff --git a/src/util/base64/toBase64Url.ts b/src/util/base64/toBase64Url.ts deleted file mode 100644 index 4188ca42d0..0000000000 --- a/src/util/base64/toBase64Url.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {createToBase64} from './createToBase64'; - -export const toBase64Url = createToBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', ''); diff --git a/src/util/buffers/Reader.ts b/src/util/buffers/Reader.ts deleted file mode 100644 index 160e9cdc83..0000000000 --- a/src/util/buffers/Reader.ts +++ /dev/null @@ -1,105 +0,0 @@ -import {decodeUtf8} from './utf8/decodeUtf8'; -import type {IReader, IReaderResettable} from './types'; - -export class Reader implements IReader, IReaderResettable { - public uint8 = new Uint8Array([]); - public view = new DataView(this.uint8.buffer); - public x = 0; - - public reset(uint8: Uint8Array): void { - this.x = 0; - this.uint8 = uint8; - this.view = new DataView(uint8.buffer, uint8.byteOffset, uint8.length); - } - - public peak(): number { - return this.view.getUint8(this.x); - } - - public skip(length: number): void { - this.x += length; - } - - public buf(size: number): Uint8Array { - const end = this.x + size; - const bin = this.uint8.subarray(this.x, end); - this.x = end; - return bin; - } - - public u8(): number { - return this.uint8[this.x++]; - // return this.view.getUint8(this.x++); - } - - public i8(): number { - return this.view.getInt8(this.x++); - } - - public u16(): number { - // const num = this.view.getUint16(this.x); - // this.x += 2; - // return num; - let x = this.x; - const num = (this.uint8[x++] << 8) + this.uint8[x++]; - this.x = x; - return num; - } - - public i16(): number { - const num = this.view.getInt16(this.x); - this.x += 2; - return num; - } - - public u32(): number { - const num = this.view.getUint32(this.x); - this.x += 4; - return num; - } - - public i32(): number { - const num = this.view.getInt32(this.x); - this.x += 4; - return num; - } - - public u64(): bigint { - const num = this.view.getBigUint64(this.x); - this.x += 8; - return num; - } - - public i64(): bigint { - const num = this.view.getBigInt64(this.x); - this.x += 8; - return num; - } - - public f32(): number { - const pos = this.x; - this.x += 4; - return this.view.getFloat32(pos); - } - - public f64(): number { - const pos = this.x; - this.x += 8; - return this.view.getFloat64(pos); - } - - public utf8(size: number): string { - const start = this.x; - this.x += size; - return decodeUtf8(this.uint8, start, size); - } - - public ascii(length: number): string { - const uint8 = this.uint8; - let str = ''; - const end = this.x + length; - for (let i = this.x; i < end; i++) str += String.fromCharCode(uint8[i]); - this.x = end; - return str; - } -} diff --git a/src/util/buffers/Slice.ts b/src/util/buffers/Slice.ts deleted file mode 100644 index c0287b831f..0000000000 --- a/src/util/buffers/Slice.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class Slice { - constructor( - public readonly uint8: Uint8Array, - public readonly view: DataView, - public readonly start: number, - public readonly end: number, - ) {} - - public subarray(): Uint8Array { - return this.uint8.subarray(this.start, this.end); - } -} diff --git a/src/util/buffers/StreamingOctetReader.ts b/src/util/buffers/StreamingOctetReader.ts deleted file mode 100644 index b254a75647..0000000000 --- a/src/util/buffers/StreamingOctetReader.ts +++ /dev/null @@ -1,175 +0,0 @@ -const fromCharCode = String.fromCharCode; - -export class StreamingOctetReader { - protected readonly chunks: Uint8Array[] = []; - - /** Total size of all chunks. */ - protected chunkSize: number = 0; - - protected x: number = 0; - - public size(): number { - return this.chunkSize - this.x; - } - - public push(chunk: Uint8Array): void { - this.chunks.push(chunk); - this.chunkSize += chunk.length; - } - - protected assertSize(size: number): void { - if (size > this.size()) throw new RangeError('OUT_OF_BOUNDS'); - } - - public u8(): number { - this.assertSize(1); - const chunk = this.chunks[0]!; - let x = this.x; - const octet = chunk[x++]; - if (x === chunk.length) { - this.chunks.shift(); - this.chunkSize -= chunk.length; - x = 0; - } - this.x = x; - return octet; - } - - public u32(): number { - const octet0 = this.u8(); - const octet1 = this.u8(); - const octet2 = this.u8(); - const octet3 = this.u8(); - return (octet0 * 0x1000000 + (octet1 << 16) + (octet2 << 8)) | octet3; - } - - public copy(size: number, dst: Uint8Array, pos: number): void { - if (!size) return; - this.assertSize(size); - const chunk0 = this.chunks[0]!; - const size0 = Math.min(chunk0.length - this.x, size); - dst.set(chunk0.subarray(this.x, this.x + size0), pos); - size -= size0; - if (size <= 0) { - this.skipUnsafe(size0); - return; - } - let chunkIndex = 1; - while (size > 0) { - const chunk1 = this.chunks[chunkIndex]!; - const size1 = Math.min(chunk1.length, size); - dst.set(chunk1.subarray(0, size1), pos + size0); - size -= size1; - chunkIndex++; - } - this.skipUnsafe(size); - } - - public copyXor( - size: number, - dst: Uint8Array, - pos: number, - mask: [number, number, number, number], - maskIndex: number, - ): void { - if (!size) return; - this.assertSize(size); - const chunk0 = this.chunks[0]!; - let x = this.x; - const size0 = Math.min(chunk0.length - x, size); - const end = x + size0; - for (; x < end; ) dst[pos++] = chunk0[x++] ^ mask[maskIndex++ % 4]; - size -= size0; - if (size <= 0) { - this.skipUnsafe(size0); - return; - } - let chunkIndex = 1; - while (size > 0) { - const chunk1 = this.chunks[chunkIndex++]!; - const size1 = Math.min(chunk1.length, size); - for (let x = 0; x < size1; ) dst[pos++] = chunk1[x++] ^ mask[maskIndex++ % 4]; - size -= size1; - } - this.skipUnsafe(size); - } - - public buf(size: number): Uint8Array { - this.assertSize(size); - const buf = new Uint8Array(size); - this.copy(size, buf, 0); - return buf; - } - - public bufXor(size: number, mask: [number, number, number, number], maskIndex: number): Uint8Array { - this.assertSize(size); - const buf = new Uint8Array(size); - this.copyXor(size, buf, 0, mask, maskIndex); - return buf; - } - - public skipUnsafe(n: number): void { - if (!n) return; - const chunk = this.chunks[0]!; - const chunkLength = chunk.length; - const remaining = chunkLength - this.x; - if (remaining > n) { - this.x = this.x + n; - return; - } - this.x = 0; - this.chunks.shift(); - this.chunkSize -= chunkLength; - n -= remaining; - this.skipUnsafe(n); - } - - public skip(n: number): void { - this.assertSize(n); - this.skipUnsafe(n); - } - - public peak(): number { - this.assertSize(1); - return this.chunks[0]![this.x]; - } - - public utf8(length: number, mask: [number, number, number, number], maskIndex: number): string { - this.assertSize(length); - let i = 0; - const points: number[] = []; - while (i < length) { - let code = this.u8() ^ mask[maskIndex++ % 4]; - i++; - if ((code & 0x80) !== 0) { - const octet2 = (this.u8() ^ mask[maskIndex++ % 4]) & 0x3f; - i++; - if ((code & 0xe0) === 0xc0) { - code = ((code & 0x1f) << 6) | octet2; - } else { - const octet3 = (this.u8() ^ mask[maskIndex++ % 4]) & 0x3f; - i++; - if ((code & 0xf0) === 0xe0) { - code = ((code & 0x1f) << 12) | (octet2 << 6) | octet3; - } else { - if ((code & 0xf8) === 0xf0) { - const octet4 = (this.u8() ^ mask[maskIndex++ % 4]) & 0x3f; - i++; - let unit = ((code & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - const unit0 = ((unit >>> 10) & 0x3ff) | 0xd800; - code = 0xdc00 | (unit & 0x3ff); - points.push(unit0); - } else { - code = unit; - } - } - } - } - } - points.push(code); - } - return fromCharCode.apply(String, points); - } -} diff --git a/src/util/buffers/StreamingReader.ts b/src/util/buffers/StreamingReader.ts deleted file mode 100644 index 1695b73711..0000000000 --- a/src/util/buffers/StreamingReader.ts +++ /dev/null @@ -1,179 +0,0 @@ -import {Writer} from './Writer'; -import {decodeUtf8} from './utf8/decodeUtf8'; -import type {IReader, IReaderResettable} from './types'; - -export class StreamingReader implements IReader, IReaderResettable { - protected readonly writer: Writer; - - /** - * Offset from the start of the buffer (x0 in Writer). - */ - protected dx = 0; - - constructor(allocSize: number = 16 * 1024) { - this.writer = new Writer(allocSize); - } - - /** - * Returns the number of bytes remaining in the buffer. - */ - public size(): number { - return this.writer.x - this.x; - } - - /** - * Assert that there is enough data in the buffer to read `size` bytes. - * - * @param size Number of bytes to read. - */ - protected assertSize(size: number): void { - if (size > this.size()) throw new RangeError('OUT_OF_BOUNDS'); - } - - /** - * Add a chunk of data to be decoded. The chunk is copied into the - * internal buffer, so you can reuse the chunk after calling this method; or - * this chunk can be neutered by the caller. - * - * @param uint8 `Uint8Array` chunk of data to be decoded. - */ - public push(uint8: Uint8Array): void { - this.writer.buf(uint8, uint8.length); - } - - /** - * Mark the current position as consumed. This will free up memory - * for reuse. - */ - public consume(): void { - this.writer.x0 += this.dx; - this.dx = 0; - } - - // ------------------------------------------------------------------ IReader - - public get uint8(): Uint8Array { - return this.writer.uint8; - } - - public get view(): DataView { - return this.writer.view; - } - - public get x(): number { - return this.writer.x0 + this.dx; - } - - public set x(x: number) { - this.dx = x - this.writer.x0; - } - - public peak(): number { - this.assertSize(1); - return this.view.getUint8(this.x); - } - - public skip(length: number): void { - this.assertSize(length); - this.x += length; - } - - public buf(size: number): Uint8Array { - this.assertSize(size); - const end = this.x + size; - const bin = this.uint8.subarray(this.x, end); - this.x = end; - return bin; - } - - public u8(): number { - this.assertSize(1); - return this.view.getUint8(this.x++); - } - - public i8(): number { - this.assertSize(1); - return this.view.getInt8(this.x++); - } - - public u16(): number { - this.assertSize(2); - const num = this.view.getUint16(this.x); - this.x += 2; - return num; - } - - public i16(): number { - this.assertSize(2); - const num = this.view.getInt16(this.x); - this.x += 2; - return num; - } - - public u32(): number { - this.assertSize(4); - const num = this.view.getUint32(this.x); - this.x += 4; - return num; - } - - public i32(): number { - this.assertSize(4); - const num = this.view.getInt32(this.x); - this.x += 4; - return num; - } - - public u64(): bigint { - this.assertSize(8); - const num = this.view.getBigUint64(this.x); - this.x += 8; - return num; - } - - public i64(): bigint { - this.assertSize(8); - const num = this.view.getBigInt64(this.x); - this.x += 8; - return num; - } - - public f32(): number { - this.assertSize(4); - const pos = this.x; - this.x += 4; - return this.view.getFloat32(pos); - } - - public f64(): number { - this.assertSize(8); - const pos = this.x; - this.x += 8; - return this.view.getFloat64(pos); - } - - public utf8(size: number): string { - this.assertSize(size); - const start = this.x; - this.x += size; - return decodeUtf8(this.uint8, start, size); - } - - public ascii(length: number): string { - this.assertSize(length); - const uint8 = this.uint8; - let str = ''; - const end = this.x + length; - for (let i = this.x; i < end; i++) str += String.fromCharCode(uint8[i]); - this.x = end; - return str; - } - - // -------------------------------------------------------- IReaderResettable - - public reset(uint8: Uint8Array): void { - this.dx = 0; - this.writer.reset(); - this.push(uint8); - } -} diff --git a/src/util/buffers/Uint8ArrayCut.ts b/src/util/buffers/Uint8ArrayCut.ts deleted file mode 100644 index b57bfd6d00..0000000000 --- a/src/util/buffers/Uint8ArrayCut.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class Uint8ArrayCut { - constructor( - public readonly uint8: Uint8Array, - public readonly start: number, - public readonly size: number, - ) {} -} diff --git a/src/util/buffers/Writer.ts b/src/util/buffers/Writer.ts deleted file mode 100644 index 8b96de2361..0000000000 --- a/src/util/buffers/Writer.ts +++ /dev/null @@ -1,269 +0,0 @@ -import {Slice} from './Slice'; -import {IWriterGrowable, IWriter} from './types'; - -const EMPTY_UINT8 = new Uint8Array([]); -const EMPTY_VIEW = new DataView(EMPTY_UINT8.buffer); - -const hasBuffer = typeof Buffer === 'function'; -const utf8Write = hasBuffer - ? (Buffer.prototype.utf8Write as (this: Uint8Array, str: string, pos: number, maxLength: number) => number) - : null; -const from = hasBuffer ? Buffer.from : null; -const textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null; - -/** - * Encoder class provides an efficient way to encode binary data. It grows the - * internal memory buffer automatically as more space is required. It is useful - * in cases when it is not known in advance the size of memory needed. - */ -export class Writer implements IWriter, IWriterGrowable { - /** @ignore */ - public uint8: Uint8Array; - /** @ignore */ - public view: DataView = EMPTY_VIEW; - /** @ignore */ - public x0: number = 0; - /** @ignore */ - public x: number = 0; - protected size: number; - - /** - * @param allocSize Number of bytes to allocate at a time when buffer ends. - */ - constructor(public allocSize: number = 64 * 1024) { - this.uint8 = new Uint8Array(allocSize); - this.size = allocSize; - this.view = new DataView(this.uint8.buffer); - } - - /** @ignore */ - protected grow(size: number) { - const x0 = this.x0; - const x = this.x; - const oldUint8 = this.uint8; - const newUint8 = new Uint8Array(size); - const view = new DataView(newUint8.buffer); - const activeSlice = oldUint8.subarray(x0, x); - newUint8.set(activeSlice, 0); - this.x = x - x0; - this.x0 = 0; - this.uint8 = newUint8; - this.size = size; - this.view = view; - } - - /** - * Make sure the internal buffer has enough space to write the specified number - * of bytes, otherwise resize the internal buffer to accommodate for more size. - * - * @param capacity Number of bytes. - */ - public ensureCapacity(capacity: number) { - const byteLength = this.size; - const remaining = byteLength - this.x; - if (remaining < capacity) { - const total = byteLength - this.x0; - const required = capacity - remaining; - const totalRequired = total + required; - this.grow(totalRequired <= this.allocSize ? this.allocSize : totalRequired * 2); - } - } - - /** @todo Consider renaming to "skip"? */ - public move(capacity: number) { - this.ensureCapacity(capacity); - this.x += capacity; - } - - public reset() { - this.x0 = this.x; - } - - /** - * Allocates a new {@link ArrayBuffer}, useful when the underlying - * {@link ArrayBuffer} cannot be shared between threads. - * - * @param size Size of memory to allocate. - */ - public newBuffer(size: number) { - const uint8 = (this.uint8 = new Uint8Array(size)); - this.size = size; - this.view = new DataView(uint8.buffer); - this.x = this.x0 = 0; - } - - /** - * @returns Encoded memory buffer contents. - */ - public flush(): Uint8Array { - const result = this.uint8.subarray(this.x0, this.x); - this.x0 = this.x; - return result; - } - - public flushSlice(): Slice { - const slice = new Slice(this.uint8, this.view, this.x0, this.x); - this.x0 = this.x; - return slice; - } - - public u8(char: number) { - this.ensureCapacity(1); - this.uint8[this.x++] = char; - } - - public u16(word: number) { - this.ensureCapacity(2); - this.view.setUint16(this.x, word); - this.x += 2; - } - - public u32(dword: number) { - this.ensureCapacity(4); - this.view.setUint32(this.x, dword); - this.x += 4; - } - - public i32(dword: number) { - this.ensureCapacity(4); - this.view.setInt32(this.x, dword); - this.x += 4; - } - - public u64(qword: number | bigint) { - this.ensureCapacity(8); - this.view.setBigUint64(this.x, BigInt(qword)); - this.x += 8; - } - - public f64(float: number) { - this.ensureCapacity(8); - this.view.setFloat64(this.x, float); - this.x += 8; - } - - public u8u16(u8: number, u16: number) { - this.ensureCapacity(3); - let x = this.x; - this.uint8[x++] = u8; - this.uint8[x++] = u16 >>> 8; - this.uint8[x++] = u16 & 0xff; - this.x = x; - } - - public u8u32(u8: number, u32: number) { - this.ensureCapacity(5); - let x = this.x; - this.uint8[x++] = u8; - this.view.setUint32(x, u32); - this.x = x + 4; - } - - public u8u64(u8: number, u64: number | bigint) { - this.ensureCapacity(9); - let x = this.x; - this.uint8[x++] = u8; - this.view.setBigUint64(x, BigInt(u64)); - this.x = x + 8; - } - - public u8f32(u8: number, f32: number) { - this.ensureCapacity(5); - let x = this.x; - this.uint8[x++] = u8; - this.view.setFloat32(x, f32); - this.x = x + 4; - } - - public u8f64(u8: number, f64: number) { - this.ensureCapacity(9); - let x = this.x; - this.uint8[x++] = u8; - this.view.setFloat64(x, f64); - this.x = x + 8; - } - - public buf(buf: Uint8Array, length: number): void { - this.ensureCapacity(length); - const x = this.x; - this.uint8.set(buf, x); - this.x = x + length; - } - - /** - * Encodes string as UTF-8. You need to call .ensureCapacity(str.length * 4) - * before calling - * - * @param str String to encode as UTF-8. - * @returns The number of bytes written - */ - public utf8(str: string): number { - const maxLength = str.length * 4; - if (maxLength < 168) return this.utf8Native(str); - if (utf8Write) { - const writeLength = utf8Write.call(this.uint8, str, this.x, maxLength); - this.x += writeLength; - return writeLength; - } else if (from) { - const uint8 = this.uint8; - const offset = uint8.byteOffset + this.x; - const buf = from(uint8.buffer).subarray(offset, offset + maxLength); - const writeLength = buf.write(str, 0, maxLength, 'utf8'); - this.x += writeLength; - return writeLength; - } else if (maxLength > 1024 && textEncoder) { - const writeLength = textEncoder!.encodeInto(str, this.uint8.subarray(this.x, this.x + maxLength)).written!; - this.x += writeLength; - return writeLength; - } - return this.utf8Native(str); - } - - public utf8Native(str: string): number { - const length = str.length; - const uint8 = this.uint8; - let offset = this.x; - let pos = 0; - while (pos < length) { - let value = str.charCodeAt(pos++); - if ((value & 0xffffff80) === 0) { - uint8[offset++] = value; - continue; - } else if ((value & 0xfffff800) === 0) { - uint8[offset++] = ((value >> 6) & 0x1f) | 0xc0; - } else { - if (value >= 0xd800 && value <= 0xdbff) { - if (pos < length) { - const extra = str.charCodeAt(pos); - if ((extra & 0xfc00) === 0xdc00) { - pos++; - value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; - } - } - } - if ((value & 0xffff0000) === 0) { - uint8[offset++] = ((value >> 12) & 0x0f) | 0xe0; - uint8[offset++] = ((value >> 6) & 0x3f) | 0x80; - } else { - uint8[offset++] = ((value >> 18) & 0x07) | 0xf0; - uint8[offset++] = ((value >> 12) & 0x3f) | 0x80; - uint8[offset++] = ((value >> 6) & 0x3f) | 0x80; - } - } - uint8[offset++] = (value & 0x3f) | 0x80; - } - const writeLength = offset - this.x; - this.x = offset; - return writeLength; - } - - public ascii(str: string): void { - const length = str.length; - this.ensureCapacity(length); - const uint8 = this.uint8; - let x = this.x; - let pos = 0; - while (pos < length) uint8[x++] = str.charCodeAt(pos++); - this.x = x; - } -} diff --git a/src/util/buffers/__bench__/bench.decodeUtf8.ts b/src/util/buffers/__bench__/bench.decodeUtf8.ts deleted file mode 100644 index 0baee0ff6d..0000000000 --- a/src/util/buffers/__bench__/bench.decodeUtf8.ts +++ /dev/null @@ -1,108 +0,0 @@ -// yarn build && npx ts-node src/util/buffers/__bench__/bench.decodeUtf8.ts - -import {runBenchmark} from '../../../__bench__/runBenchmark'; - -const prepare = (str: string) => { - const buf = Buffer.from(str); - const arr = new Uint8Array(buf.length); - for (let i = 0; i < buf.length; i++) arr[i] = buf[i]; - return arr; -}; - -const runner = (v: number, name: string) => ({ - name: `${name} (v${v})`, - setup: () => { - const decode = require('../../../../lib/util/buffers/utf8/decodeUtf8/v' + v).default; - return (data: any) => decode(data, 0, data.length); - }, -}); - -const benchmark = { - name: 'decodeUtf8', - warmup: 1000, - payloads: [ - // { - // name: (buf) => `Single character, ${buf.length} bytes`, - // data: prepare('a'), - // test: () => 'a', - // }, - // { - // name: (buf) => `"Hello", ${buf.length} bytes`, - // data: prepare('Hello'), - // test: () => 'Hello', - // }, - // { - // name: (buf) => `Short text with emoji, ${buf.length} bytes`, - // data: prepare('Hi, Mike ๐Ÿ‘‹!'), - // test: () => 'Hi, Mike ๐Ÿ‘‹!', - // }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('a'.repeat(2)), - test: () => 'a'.repeat(2), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('a'.repeat(4)), - test: () => 'a'.repeat(4), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('a'.repeat(8)), - test: () => 'a'.repeat(8), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(3)), - test: () => 'abcd'.repeat(3), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(4)), - test: () => 'abcd'.repeat(4), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(8)), - test: () => 'abcd'.repeat(8), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(16)), - test: () => 'abcd'.repeat(16), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(32)), - test: () => 'abcd'.repeat(32), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(64)), - test: () => 'abcd'.repeat(64), - }, - { - name: (buf: any) => `Repeating characters, ${buf.length} bytes`, - data: prepare('abcd'.repeat(128)), - test: () => 'abcd'.repeat(128), - }, - ], - runners: [ - // runner(1, 'JS with buffering in array'), - // runner(2, 'Buffer.prototype.utf8Slice'), - // runner(3, 'Buffer.from(arr).slice'), - // runner(4, 'Buffer.from(arr).subarray'), - // runner(5, 'JS with string concatenation'), - // runner(6, 'TextDecoder'), - // runner(7, 'JS with buffering in array, no flushing'), - // runner(8, 'JS with buffering in array, small buffer'), - // runner(9, 'JS with buffering in array, variable reuse'), - // runner(10, 'JS with string concatenation, variable reuse'), - runner(19, 'json-pack-napi'), - runner(11, 'utf8Slice'), - // runner(12, 'from(arr).subarray'), - // runner(13, 'composition'), - ], -}; - -runBenchmark(benchmark); diff --git a/src/util/buffers/__tests__/StreamingReader.spec.ts b/src/util/buffers/__tests__/StreamingReader.spec.ts deleted file mode 100644 index 420dbfa311..0000000000 --- a/src/util/buffers/__tests__/StreamingReader.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import {StreamingReader} from '../StreamingReader'; - -test('can push and read', () => { - const reader = new StreamingReader(4); - reader.push(new Uint8Array([1, 2, 3])); - expect(reader.u8()).toBe(1); - reader.push(new Uint8Array([4, 5, 6])); - expect(reader.u8()).toBe(2); - expect(reader.u8()).toBe(3); - expect(reader.u8()).toBe(4); - expect(reader.u8()).toBe(5); - expect(reader.u8()).toBe(6); -}); - -test('throws RangeError when reading out of bounds', () => { - const reader = new StreamingReader(2); - reader.push(new Uint8Array([1, 2, 3])); - reader.u16(); - reader.u8(); - expect(() => reader.u8()).toThrow(RangeError); -}); - -test('throws RangeError when reading out of bounds - 2', () => { - const reader = new StreamingReader(); - reader.push(new Uint8Array([1, 2, 3])); - reader.u16(); - expect(() => reader.u16()).toThrow(RangeError); -}); - -test('throws RangeError when reading out of bounds - 3', () => { - const reader = new StreamingReader(4); - reader.push(new Uint8Array([1, 2, 3])); - reader.u16(); - reader.push(new Uint8Array([4, 5])); - expect(() => reader.u32()).toThrow(RangeError); -}); - -test('size shrinks as data is read', () => { - const reader = new StreamingReader(4); - expect(reader.size()).toBe(0); - reader.push(new Uint8Array([1, 2, 3])); - expect(reader.size()).toBe(3); - expect(reader.u8()).toBe(1); - expect(reader.size()).toBe(2); - reader.push(new Uint8Array([4, 5, 6])); - expect(reader.size()).toBe(5); - expect(reader.u8()).toBe(2); - expect(reader.size()).toBe(4); - expect(reader.u8()).toBe(3); - expect(reader.size()).toBe(3); - expect(reader.u8()).toBe(4); - expect(reader.size()).toBe(2); - expect(reader.u8()).toBe(5); - expect(reader.size()).toBe(1); - expect(reader.u8()).toBe(6); - expect(reader.size()).toBe(0); -}); diff --git a/src/util/buffers/__tests__/isFloat32.spec.ts b/src/util/buffers/__tests__/isFloat32.spec.ts deleted file mode 100644 index 34a21049f3..0000000000 --- a/src/util/buffers/__tests__/isFloat32.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {isFloat32} from '../isFloat32'; - -test('returns true for a float32', () => { - expect(isFloat32(1.5)).toBe(true); -}); - -test('returns true for a float64', () => { - expect(isFloat32(1.1)).toBe(false); -}); diff --git a/src/util/buffers/b.ts b/src/util/buffers/b.ts deleted file mode 100644 index 003f912314..0000000000 --- a/src/util/buffers/b.ts +++ /dev/null @@ -1 +0,0 @@ -export const b = (...args: number[]) => new Uint8Array(args); diff --git a/src/util/buffers/bufferToUint8Array.ts b/src/util/buffers/bufferToUint8Array.ts deleted file mode 100644 index 63598b6434..0000000000 --- a/src/util/buffers/bufferToUint8Array.ts +++ /dev/null @@ -1 +0,0 @@ -export const bufferToUint8Array = (buf: Buffer): Uint8Array => new Uint8Array(buf.buffer, buf.byteOffset, buf.length); diff --git a/src/util/buffers/cmpUint8Array.ts b/src/util/buffers/cmpUint8Array.ts deleted file mode 100644 index 694c0275e9..0000000000 --- a/src/util/buffers/cmpUint8Array.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const cmpUint8Array = (a: Uint8Array, b: Uint8Array): boolean => { - const length = a.length; - if (length !== b.length) return false; - for (let i = 0; i < length; i++) if (a[i] !== b[i]) return false; - return true; -}; diff --git a/src/util/buffers/cmpUint8Array2.ts b/src/util/buffers/cmpUint8Array2.ts deleted file mode 100644 index 344ec1e6f2..0000000000 --- a/src/util/buffers/cmpUint8Array2.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Compares two `Uint8Arrays` byte-by-byte. Returns a negative number if `a` is - * less than `b`, a positive number if `a` is greater than `b`, or 0 if `a` is - * equal to `b`. - * - * @returns A negative number if a is less than b, a positive number if a is - * greater than b, or 0 if a is equal to b. - */ -export const cmpUint8Array2 = (a: Uint8Array, b: Uint8Array): number => { - const len1 = a.length; - const len2 = b.length; - const len = Math.min(len1, len2); - for (let i = 0; i < len; i++) { - const diffChar = a[i] - b[i]; - if (diffChar !== 0) return diffChar; - } - return len1 - len2; -}; diff --git a/src/util/buffers/cmpUint8Array3.ts b/src/util/buffers/cmpUint8Array3.ts deleted file mode 100644 index 950c358f7e..0000000000 --- a/src/util/buffers/cmpUint8Array3.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Compares two `Uint8Arrays`, first by length, then by each byte. Returns a - * negative number if `a` is less than `b`, a positive number if `a` is greater - * than `b`, or 0 if `a` is equal to `b`. - * - * @returns A negative number if a is less than b, a positive number if a is - * greater than b, or 0 if a is equal to b. - */ -export const cmpUint8Array3 = (a: Uint8Array, b: Uint8Array): number => { - const len1 = a.length; - const len2 = b.length; - const diff = len1 - len2; - if (diff !== 0) return diff; - for (let i = 0; i < len1; i++) { - const diff = a[i] - b[i]; - if (diff !== 0) return diff; - } - return 0; -}; diff --git a/src/util/buffers/concat.ts b/src/util/buffers/concat.ts deleted file mode 100644 index 205bad1d4b..0000000000 --- a/src/util/buffers/concat.ts +++ /dev/null @@ -1,31 +0,0 @@ -export const concat = (a: Uint8Array, b: Uint8Array): Uint8Array => { - const res = new Uint8Array(a.length + b.length); - res.set(a); - res.set(b, a.length); - return res; -}; - -export const concatList = (list: Uint8Array[]): Uint8Array => { - const length = list.length; - let size = 0, - offset = 0; - for (let i = 0; i < length; i++) size += list[i].length; - const res = new Uint8Array(size); - for (let i = 0; i < length; i++) { - const item = list[i]; - res.set(item, offset); - offset += item.length; - } - return res; -}; - -export const listToUint8 = (list: Uint8Array[]): Uint8Array => { - switch (list.length) { - case 0: - return new Uint8Array(0); - case 1: - return list[0]; - default: - return concatList(list); - } -}; diff --git a/src/util/buffers/copy.ts b/src/util/buffers/copy.ts deleted file mode 100644 index d779133967..0000000000 --- a/src/util/buffers/copy.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const copy = (arr: T): T => { - const dupe = new Uint8Array(arr.length) as T; - dupe.set(arr); - return dupe; -}; diff --git a/src/util/buffers/f16.ts b/src/util/buffers/f16.ts deleted file mode 100644 index 51a4150413..0000000000 --- a/src/util/buffers/f16.ts +++ /dev/null @@ -1,16 +0,0 @@ -const pow = Math.pow; - -export const decodeF16 = (binary: number): number => { - const exponent = (binary & 0x7c00) >> 10; - const fraction = binary & 0x03ff; - return ( - (binary >> 15 ? -1 : 1) * - (exponent - ? exponent === 0x1f - ? fraction - ? NaN - : Infinity - : pow(2, exponent - 15) * (1 + fraction / 0x400) - : 6.103515625e-5 * (fraction / 0x400)) - ); -}; diff --git a/src/util/buffers/index.ts b/src/util/buffers/index.ts index fcb073fefc..7d1d7e6433 100644 --- a/src/util/buffers/index.ts +++ b/src/util/buffers/index.ts @@ -1 +1 @@ -export * from './types'; +export * from '@jsonjoy.com/json-pack/lib/util/buffers'; diff --git a/src/util/buffers/isArrayBuffer.ts b/src/util/buffers/isArrayBuffer.ts deleted file mode 100644 index 998e906535..0000000000 --- a/src/util/buffers/isArrayBuffer.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const isArrayBuffer = (value: unknown): value is ArrayBuffer => { - return value instanceof ArrayBuffer || toString.call(value) === '[object ArrayBuffer]'; -}; diff --git a/src/util/buffers/isFloat32.ts b/src/util/buffers/isFloat32.ts deleted file mode 100644 index 26e382346d..0000000000 --- a/src/util/buffers/isFloat32.ts +++ /dev/null @@ -1,6 +0,0 @@ -const view = new DataView(new ArrayBuffer(4)); - -export const isFloat32 = (n: number): boolean => { - view.setFloat32(0, n); - return n === view.getFloat32(0); -}; diff --git a/src/util/buffers/isUint8Array.ts b/src/util/buffers/isUint8Array.ts deleted file mode 100644 index b86a8b0659..0000000000 --- a/src/util/buffers/isUint8Array.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const isUint8Array = - typeof Buffer === 'function' - ? (x: unknown): x is Uint8Array => x instanceof Uint8Array || Buffer.isBuffer(x) - : (x: unknown): x is Uint8Array => x instanceof Uint8Array; diff --git a/src/util/buffers/printOctets.ts b/src/util/buffers/printOctets.ts deleted file mode 100644 index 9e5c01fd3e..0000000000 --- a/src/util/buffers/printOctets.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const printOctets = (octets: Uint8Array, max: number = 16): string => { - let str = ''; - if (!octets.length) return str; - if (octets[0] < 16) str += '0'; - str += octets[0].toString(16); - for (let i = 1; i < octets.length && i < max; i++) { - const n = octets[i]; - str += ' '; - if (n < 16) str += '0'; - str += n.toString(16); - } - if (octets.length > max) str += `โ€ฆ (${octets.length - max} more)`; - return str; -}; diff --git a/src/util/buffers/strings.ts b/src/util/buffers/strings.ts deleted file mode 100644 index f4ac5b7378..0000000000 --- a/src/util/buffers/strings.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {bufferToUint8Array} from './bufferToUint8Array'; - -export const ascii = (txt: TemplateStringsArray | string | [string]): Uint8Array => { - if (typeof txt === 'string') return ascii([txt]); - [txt] = txt; - const len = txt.length; - const res = new Uint8Array(len); - for (let i = 0; i < len; i++) res[i] = txt.charCodeAt(i); - return res; -}; - -export const utf8 = (txt: TemplateStringsArray | [string] | string): Uint8Array => { - if (typeof txt === 'string') return utf8([txt]); - [txt] = txt; - return bufferToUint8Array(Buffer.from(txt, 'utf8')); -}; diff --git a/src/util/buffers/toBuf.ts b/src/util/buffers/toBuf.ts deleted file mode 100644 index 576f8a517f..0000000000 --- a/src/util/buffers/toBuf.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {encode} from './utf8/encode'; - -export const toBuf = (str: string): Uint8Array => { - const maxLength = str.length * 4; - const arr = new Uint8Array(maxLength); - const strBufferLength = encode(arr, str, 0, maxLength); - return arr.slice(0, strBufferLength); -}; diff --git a/src/util/buffers/toDataUri.ts b/src/util/buffers/toDataUri.ts deleted file mode 100644 index b5f26dab12..0000000000 --- a/src/util/buffers/toDataUri.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {toBase64} from '../base64'; - -export const toDataUri = (buf: Uint8Array, params?: Record): string => { - let uri = 'data:application/octet-stream;base64'; - for (const key in params) uri += `;${key}=${params[key]}`; - return uri + ',' + toBase64(buf); -}; diff --git a/src/util/buffers/toUint8Array.ts b/src/util/buffers/toUint8Array.ts deleted file mode 100644 index 8e2c916816..0000000000 --- a/src/util/buffers/toUint8Array.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const toUint8Array = (data: unknown): Uint8Array => { - if (data instanceof Uint8Array) return data; - if (data instanceof ArrayBuffer) return new Uint8Array(data); - if (Array.isArray(data)) return new Uint8Array(data); - if (typeof Buffer === 'function') { - if (Buffer.isBuffer(data)) return data; - return Buffer.from(data as any); - } - throw new Error('UINT8ARRAY_INCOMPATIBLE'); -}; diff --git a/src/util/buffers/types.ts b/src/util/buffers/types.ts deleted file mode 100644 index ae29727a81..0000000000 --- a/src/util/buffers/types.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type {Slice} from './Slice'; - -export interface IWriter { - /** - * Uint8Array view of the current memory buffer. - */ - uint8: Uint8Array; - - /** - * DataView view of the current memory buffer. - */ - view: DataView; - - /** - * Position where last flush happened. - */ - x0: number; - - /** - * Current position in the internal buffer. - */ - x: number; - - u8(char: number): void; - u16(word: number): void; - u32(dword: number): void; - i32(dword: number): void; - u64(qword: number | bigint): void; - u8u16(u8: number, u16: number): void; - u8u32(u8: number, u32: number): void; - u8u64(u8: number, u64: number | bigint): void; - u8f32(u8: number, f64: number): void; - u8f64(u8: number, f64: number): void; - f64(dword: number): void; - - /** - * Write contents of a buffer. - * - * @param buf Buffer to copy from. - * @param length Number of octets to copy. - */ - buf(buf: Uint8Array, length: number): void; - - /** - * Write string as UTF-8. You need to call .ensureCapacity(str.length * 4) - * before calling - * - * @param str JavaScript string to encode as UTF-8 byte sequence. - */ - utf8(str: string): number; - - ascii(str: string): void; -} - -export interface IWriterGrowable { - /** @deprecated */ - reset(): void; - - /** - * Calling this method might reset the internal buffer. So, your references - * (such as `x`, `uint8`, `view`) to the internal buffer might become invalid. - * - * @param capacity How many octets to ensure are available after `x`. - */ - ensureCapacity(capacity: number): void; - move(length: number): void; - flush(): Uint8Array; - flushSlice(): Slice; - newBuffer(size: number): void; -} - -export interface IReaderBase { - /** Get current byte value without advancing the cursor. */ - peak(): number; - - /** Advance the cursor given number of octets. */ - skip(length: number): void; - - /** - * Create a new Uint8Array view of provided length starting at - * the current cursor position. - */ - buf(size: number): Uint8Array; - - u8(): number; - i8(): number; - u16(): number; - i16(): number; - u32(): number; - u64(): bigint; - i64(): bigint; - i32(): number; - f32(): number; - f64(): number; - - /** - * Decode a UTF-8 string. - * - * @param size Length of the string. - */ - utf8(size: number): string; - - ascii(length: number): string; -} - -export interface IReader extends IReaderBase { - /** - * Uint8Array view of the current memory buffer. - */ - uint8: Uint8Array; - - /** - * DataView view of the current memory buffer. - */ - view: DataView; - - /** - * Cursor in the current memory buffer. - */ - x: number; -} - -export interface IReaderResettable { - /** Set a new underlying buffer and reset cursor position to 0. */ - reset(uint8: Uint8Array): void; -} diff --git a/src/util/buffers/utf8/CachedUtf8Decoder.ts b/src/util/buffers/utf8/CachedUtf8Decoder.ts deleted file mode 100644 index 01dd36f9b7..0000000000 --- a/src/util/buffers/utf8/CachedUtf8Decoder.ts +++ /dev/null @@ -1,54 +0,0 @@ -import decodeUtf8 from './decodeUtf8/v10'; -import {randomU32} from 'hyperdyperid/lib/randomU32'; - -class CacheItem { - constructor( - public readonly bytes: Uint8Array, - public readonly value: string, - ) {} -} - -const enum CONST { - MAX_CACHED_STR_LEN = 31, - MAX_RECORDS_PER_SIZE = 16, -} - -export class CachedUtf8Decoder { - private readonly caches: CacheItem[][]; - - constructor() { - this.caches = []; - for (let i = 0; i < CONST.MAX_CACHED_STR_LEN; i++) this.caches.push([]); - } - - private get(bytes: Uint8Array, offset: number, size: number): string | null { - const records = this.caches[size - 1]!; - const len = records.length; - FIND_CHUNK: for (let i = 0; i < len; i++) { - const record = records[i]; - const recordBytes = record.bytes; - for (let j = 0; j < size; j++) if (recordBytes[j] !== bytes[offset + j]) continue FIND_CHUNK; - return record.value; - } - return null; - } - - private store(bytes: Uint8Array, value: string): void { - const records = this.caches[bytes.length - 1]!; - const record = new CacheItem(bytes, value); - const length = records.length; - if (length >= CONST.MAX_RECORDS_PER_SIZE) records[randomU32(0, CONST.MAX_RECORDS_PER_SIZE - 1)] = record; - else records.push(record); - } - - public decode(bytes: Uint8Array, offset: number, size: number): string { - if (!size) return ''; - const cachedValue = this.get(bytes, offset, size); - if (cachedValue !== null) return cachedValue; - const value = decodeUtf8(bytes, offset, size); - // Ensure to copy a slice of bytes because the byte may be NodeJS Buffer and Buffer#slice() returns a reference to its internal ArrayBuffer. - const copy = Uint8Array.prototype.slice.call(bytes, offset, offset + size); - this.store(copy, value); - return value; - } -} diff --git a/src/util/buffers/utf8/__tests__/encode.spec.ts b/src/util/buffers/utf8/__tests__/encode.spec.ts deleted file mode 100644 index 5aeea13300..0000000000 --- a/src/util/buffers/utf8/__tests__/encode.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {RandomJson} from '../../../../json-random'; -import {encodeUtf8Write, encodeFrom, encodeNative, encodeTe, encode, EncodeString} from '../encode'; - -describe('can encode JavaScript string as UTF-8 string', () => { - const runEncodingTest = (name: string, encode: EncodeString) => { - test(name, () => { - const arr = new Uint8Array(50); - for (let i = 0; i < 1000; i++) { - const str = RandomJson.genString(10); - const len = encode(arr, str, str.length, str.length * 4); - const slice = arr.subarray(10, 10 + len); - const decoded = Buffer.from(slice).toString(); - expect(decoded).toBe(str); - } - }); - }; - - runEncodingTest('encodeUtf8Write', encodeUtf8Write); - runEncodingTest('encodeFrom', encodeFrom); - runEncodingTest('encodeNative', encodeNative); - runEncodingTest('encodeTe', encodeTe); - runEncodingTest('encode', encode); -}); diff --git a/src/util/buffers/utf8/__tests__/isUtf8.spec.ts b/src/util/buffers/utf8/__tests__/isUtf8.spec.ts deleted file mode 100644 index 6ea5aea23e..0000000000 --- a/src/util/buffers/utf8/__tests__/isUtf8.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {isUtf8} from '../isUtf8'; - -describe('returns true for valid UTF8', () => { - const strings = [ - '', - 'hello', - 'hello world', - 'emoji: ๐Ÿค”', - 'russian: ะŸั€ะธะฒะตั‚', - 'chinese: ไฝ ๅฅฝ', - 'japanese: ใ“ใ‚“ใซใกใฏ', - 'korean: ์•ˆ๋…•ํ•˜์„ธ์š”', - 'arabic: ู…ุฑุญุจุง', - 'hebrew: ืฉืœื•ื', - 'greek: ฮณฮตฮนฮฑ ฯƒฮฑฯ‚', - 'bulgarian: ะ—ะดั€ะฐะฒะตะนั‚ะต', - 'hindi: เคจเคฎเคธเฅเคคเฅ‡', - 'thai: เธชเธงเธฑเธชเธ”เธต', - 'special chars: !@#$%^&*()_+{}|:"<>?`-=[]\\;\',./', - ]; - for (const str of strings) { - test(str, () => { - const buf = Buffer.from(str); - expect(isUtf8(buf, 0, buf.length)).toBe(true); - }); - } -}); - -describe('returns false for non-UTF8 sequences', () => { - const strings: [name: string, Uint8Array][] = [ - ['two octets', Buffer.from([0xc3, 0x28])], - ['three octets', Buffer.from([0xe2, 0x82, 0x28])], - ['four octets', Buffer.from([0xf0, 0x90, 0x82, 0x28])], - ['five octets', Buffer.from([0xf8, 0x88, 0x82, 0x82, 0x28])], - ['six octets', Buffer.from([0xfc, 0x84, 0x82, 0x82, 0x82, 0x28])], - ]; - for (const [name, str] of strings) { - test(name, () => { - expect(isUtf8(str, 0, str.length)).toBe(false); - }); - } -}); - -describe('returns true for valid non-UTF8 sequences in the middle of buffer', () => { - const strings: [name: string, str: Uint8Array, from: number, length: number][] = [ - ['mid char valid', Buffer.from([0xc3, 0x28, 64, 0xc3, 0x28]), 2, 1], - ]; - for (const [name, str, from, length] of strings) { - test(name, () => { - expect(isUtf8(str, from, length)).toBe(true); - expect(isUtf8(str, 0, from + length)).toBe(false); - }); - } -}); diff --git a/src/util/buffers/utf8/decodeAscii.ts b/src/util/buffers/utf8/decodeAscii.ts deleted file mode 100644 index cb84c50de8..0000000000 --- a/src/util/buffers/utf8/decodeAscii.ts +++ /dev/null @@ -1,167 +0,0 @@ -const fromCharCode = String.fromCharCode; - -/** This code was borrowed form cbor-x under the MIT license. */ - -// MIT License - -// Copyright (c) 2020 Kris Zyp - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -export const decodeAscii = (src: Uint8Array, position: number, length: number): string | undefined => { - const bytes = []; - for (let i = 0; i < length; i++) { - const byte = src[position++]; - if (byte & 0x80) return; - bytes.push(byte); - } - return fromCharCode.apply(String, bytes); -}; - -export const decodeAsciiMax15 = (src: Uint8Array, position: number, length: number): string | undefined => { - if (length < 4) { - if (length < 2) { - if (length === 0) return ''; - else { - const a = src[position++]; - if ((a & 0x80) > 1) { - position -= 1; - return; - } - return fromCharCode(a); - } - } else { - const a = src[position++]; - const b = src[position++]; - if ((a & 0x80) > 0 || (b & 0x80) > 0) { - position -= 2; - return; - } - if (length < 3) return fromCharCode(a, b); - const c = src[position++]; - if ((c & 0x80) > 0) { - position -= 3; - return; - } - return fromCharCode(a, b, c); - } - } else { - const a = src[position++]; - const b = src[position++]; - const c = src[position++]; - const d = src[position++]; - if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) { - position -= 4; - return; - } - if (length < 6) { - if (length === 4) return fromCharCode(a, b, c, d); - else { - const e = src[position++]; - if ((e & 0x80) > 0) { - position -= 5; - return; - } - return fromCharCode(a, b, c, d, e); - } - } else if (length < 8) { - const e = src[position++]; - const f = src[position++]; - if ((e & 0x80) > 0 || (f & 0x80) > 0) { - position -= 6; - return; - } - if (length < 7) return fromCharCode(a, b, c, d, e, f); - const g = src[position++]; - if ((g & 0x80) > 0) { - position -= 7; - return; - } - return fromCharCode(a, b, c, d, e, f, g); - } else { - const e = src[position++]; - const f = src[position++]; - const g = src[position++]; - const h = src[position++]; - if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) { - position -= 8; - return; - } - if (length < 10) { - if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h); - else { - const i = src[position++]; - if ((i & 0x80) > 0) { - position -= 9; - return; - } - return fromCharCode(a, b, c, d, e, f, g, h, i); - } - } else if (length < 12) { - const i = src[position++]; - const j = src[position++]; - if ((i & 0x80) > 0 || (j & 0x80) > 0) { - position -= 10; - return; - } - if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j); - const k = src[position++]; - if ((k & 0x80) > 0) { - position -= 11; - return; - } - return fromCharCode(a, b, c, d, e, f, g, h, i, j, k); - } else { - const i = src[position++]; - const j = src[position++]; - const k = src[position++]; - const l = src[position++]; - if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) { - position -= 12; - return; - } - if (length < 14) { - if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l); - else { - const m = src[position++]; - if ((m & 0x80) > 0) { - position -= 13; - return; - } - return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m); - } - } else { - const m = src[position++]; - const n = src[position++]; - if ((m & 0x80) > 0 || (n & 0x80) > 0) { - position -= 14; - return; - } - if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n); - const o = src[position++]; - if ((o & 0x80) > 0) { - position -= 15; - return; - } - return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); - } - } - } - } -}; diff --git a/src/util/buffers/utf8/decodeUtf8/README.md b/src/util/buffers/utf8/decodeUtf8/README.md deleted file mode 100644 index dbdea0c7be..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# `decodeUtf8` - -Decodes UTF-8 text from `Uint8Array` buffer to JavaScript string (UTF-16). - -- `Buffer.from(arr).subarray` (v4) is always faster than `Buffer.from(arr).slice` (v3). -- `Buffer.prototype.utf8Slice` (v2) is always faster than `Buffer.from(arr).subarray` (v4). - - But `Buffer.prototype.utf8Slice` (v2) is Node's internal implementation detail, so we need - to be able to fall back to `Buffer.from(arr).subarray` (v4). -- "JS with string concatenation" (v5) is fastest for tiny strings. -- "JS with buffering in array" (v1) is fastest for medium sized strings. -- `TextDecoder` (v6) is slower than any `Buffer` methods. - - However, `Buffer` is not available on the browser (and possibly other environments). So, - for long strings would be beneficial to use `TextDecoder` (v6). -- (v10) seems to be faster than (v5). -- (v9) seems to be the fastest out of the buffering approaches. - - -Benchmarks: - -``` -node benchmarks/util/buffers/bench.decodeUtf8.js -============================================================================= Benchmark: decodeUtf8 -Warming up each runner 1000x -------------------------------------------------------------------------- Single character, 1 bytes -๐Ÿ‘ JS with buffering in array (v1) x 45,587,314 ops/sec ยฑ0.35% (91 runs sampled) -๐Ÿ‘ Buffer.prototype.utf8Slice (v2) x 9,669,294 ops/sec ยฑ0.08% (95 runs sampled) -๐Ÿ‘ Buffer.from(arr).slice (v3) x 6,522,838 ops/sec ยฑ0.18% (93 runs sampled) -๐Ÿ‘ Buffer.from(arr).subarray (v4) x 7,004,453 ops/sec ยฑ0.17% (99 runs sampled) -๐Ÿ‘ JS with string concatenation (v5) x 157,127,790 ops/sec ยฑ0.08% (98 runs sampled) -๐Ÿ‘ TextDecoder (v6) x 1,405,754 ops/sec ยฑ1.57% (84 runs sampled) -Fastest is ๐Ÿ‘ JS with string concatenation (v5) ----------------------------------------------------------------------------------- "Hello", 5 bytes -๐Ÿ‘ JS with buffering in array (v1) x 26,293,330 ops/sec ยฑ0.17% (98 runs sampled) -๐Ÿ‘ Buffer.prototype.utf8Slice (v2) x 9,595,299 ops/sec ยฑ0.13% (97 runs sampled) -๐Ÿ‘ Buffer.from(arr).slice (v3) x 6,597,346 ops/sec ยฑ0.27% (90 runs sampled) -๐Ÿ‘ Buffer.from(arr).subarray (v4) x 6,913,974 ops/sec ยฑ0.17% (98 runs sampled) -๐Ÿ‘ JS with string concatenation (v5) x 33,109,095 ops/sec ยฑ0.17% (100 runs sampled) -๐Ÿ‘ TextDecoder (v6) x 1,393,950 ops/sec ยฑ1.31% (87 runs sampled) -Fastest is ๐Ÿ‘ JS with string concatenation (v5) -------------------------------------------------------------------- Short text with emoji, 14 bytes -๐Ÿ‘ JS with buffering in array (v1) x 13,746,402 ops/sec ยฑ0.61% (94 runs sampled) -๐Ÿ‘ Buffer.prototype.utf8Slice (v2) x 8,573,654 ops/sec ยฑ0.13% (99 runs sampled) -๐Ÿ‘ Buffer.from(arr).slice (v3) x 6,003,418 ops/sec ยฑ0.42% (97 runs sampled) -๐Ÿ‘ Buffer.from(arr).subarray (v4) x 6,163,374 ops/sec ยฑ0.35% (99 runs sampled) -๐Ÿ‘ JS with string concatenation (v5) x 7,468,848 ops/sec ยฑ0.26% (99 runs sampled) -๐Ÿ‘ TextDecoder (v6) x 1,358,357 ops/sec ยฑ1.32% (72 runs sampled) -Fastest is ๐Ÿ‘ JS with buffering in array (v1) ---------------------------------------------------------------------- Repeating characters, 8 bytes -๐Ÿ‘ JS with buffering in array (v1) x 18,606,797 ops/sec ยฑ0.37% (99 runs sampled) -๐Ÿ‘ Buffer.prototype.utf8Slice (v2) x 9,210,861 ops/sec ยฑ0.26% (99 runs sampled) -๐Ÿ‘ Buffer.from(arr).slice (v3) x 6,398,227 ops/sec ยฑ0.23% (96 runs sampled) -๐Ÿ‘ Buffer.from(arr).subarray (v4) x 6,820,514 ops/sec ยฑ0.22% (99 runs sampled) -๐Ÿ‘ JS with string concatenation (v5) x 17,943,107 ops/sec ยฑ0.35% (94 runs sampled) -๐Ÿ‘ TextDecoder (v6) x 1,448,300 ops/sec ยฑ1.36% (75 runs sampled) -Fastest is ๐Ÿ‘ JS with buffering in array (v1) --------------------------------------------------------------------- Repeating characters, 16 bytes -๐Ÿคž JS with buffering in array (v1) x 12,181,356 ops/sec ยฑ0.26% (100 runs sampled) -๐Ÿคž Buffer.prototype.utf8Slice (v2) x 9,254,890 ops/sec ยฑ0.25% (97 runs sampled) -๐Ÿคž Buffer.from(arr).slice (v3) x 6,407,754 ops/sec ยฑ0.20% (99 runs sampled) -๐Ÿคž Buffer.from(arr).subarray (v4) x 6,738,914 ops/sec ยฑ0.25% (98 runs sampled) -๐Ÿคž JS with string concatenation (v5) x 9,530,473 ops/sec ยฑ0.21% (101 runs sampled) -๐Ÿคž TextDecoder (v6) x 1,456,139 ops/sec ยฑ1.28% (77 runs sampled) -Fastest is ๐Ÿคž JS with buffering in array (v1) --------------------------------------------------------------------- Repeating characters, 32 bytes -๐Ÿคž JS with buffering in array (v1) x 6,327,652 ops/sec ยฑ0.24% (100 runs sampled) -๐Ÿคž Buffer.prototype.utf8Slice (v2) x 8,958,249 ops/sec ยฑ0.22% (99 runs sampled) -๐Ÿคž Buffer.from(arr).slice (v3) x 6,217,455 ops/sec ยฑ0.23% (98 runs sampled) -๐Ÿคž Buffer.from(arr).subarray (v4) x 6,500,127 ops/sec ยฑ0.18% (101 runs sampled) -๐Ÿคž JS with string concatenation (v5) x 5,647,992 ops/sec ยฑ0.14% (99 runs sampled) -๐Ÿคž TextDecoder (v6) x 1,452,152 ops/sec ยฑ1.26% (79 runs sampled) -Fastest is ๐Ÿคž Buffer.prototype.utf8Slice (v2) --------------------------------------------------------------------- Repeating characters, 64 bytes -๐Ÿคž JS with buffering in array (v1) x 3,141,539 ops/sec ยฑ0.23% (99 runs sampled) -๐Ÿคž Buffer.prototype.utf8Slice (v2) x 8,898,315 ops/sec ยฑ0.21% (99 runs sampled) -๐Ÿคž Buffer.from(arr).slice (v3) x 5,947,900 ops/sec ยฑ0.24% (99 runs sampled) -๐Ÿคž Buffer.from(arr).subarray (v4) x 6,380,096 ops/sec ยฑ0.17% (102 runs sampled) -๐Ÿคž JS with string concatenation (v5) x 3,027,083 ops/sec ยฑ0.15% (96 runs sampled) -๐Ÿคž TextDecoder (v6) x 1,387,153 ops/sec ยฑ0.86% (85 runs sampled) -Fastest is ๐Ÿคž Buffer.prototype.utf8Slice (v2) -------------------------------------------------------------------- Repeating characters, 128 bytes -๐Ÿคž JS with buffering in array (v1) x 1,525,792 ops/sec ยฑ0.18% (98 runs sampled) -๐Ÿคž Buffer.prototype.utf8Slice (v2) x 8,600,267 ops/sec ยฑ0.17% (98 runs sampled) -๐Ÿคž Buffer.from(arr).slice (v3) x 5,676,294 ops/sec ยฑ0.16% (98 runs sampled) -๐Ÿคž Buffer.from(arr).subarray (v4) x 6,014,855 ops/sec ยฑ0.23% (100 runs sampled) -๐Ÿคž JS with string concatenation (v5) x 1,612,844 ops/sec ยฑ0.10% (100 runs sampled) -๐Ÿคž TextDecoder (v6) x 1,304,084 ops/sec ยฑ1.14% (86 runs sampled) -Fastest is ๐Ÿคž Buffer.prototype.utf8Slice (v2) -------------------------------------------------------------------- Repeating characters, 256 bytes -๐Ÿคž JS with buffering in array (v1) x 673,037 ops/sec ยฑ0.08% (98 runs sampled) -๐Ÿคž Buffer.prototype.utf8Slice (v2) x 7,934,918 ops/sec ยฑ0.26% (99 runs sampled) -๐Ÿคž Buffer.from(arr).slice (v3) x 4,803,526 ops/sec ยฑ0.25% (98 runs sampled) -๐Ÿคž Buffer.from(arr).subarray (v4) x 5,007,603 ops/sec ยฑ0.27% (94 runs sampled) -๐Ÿคž JS with string concatenation (v5) x 816,504 ops/sec ยฑ0.19% (101 runs sampled) -๐Ÿคž TextDecoder (v6) x 1,123,970 ops/sec ยฑ0.90% (92 runs sampled) -Fastest is ๐Ÿคž Buffer.prototype.utf8Slice (v2) -``` diff --git a/src/util/buffers/utf8/decodeUtf8/__tests__/decodeUtf8.spec.ts b/src/util/buffers/utf8/decodeUtf8/__tests__/decodeUtf8.spec.ts deleted file mode 100644 index 2a74db7bdb..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/__tests__/decodeUtf8.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import v1 from '../v1'; -import v2 from '../v2'; -import v3 from '../v3'; -import v4 from '../v4'; -import v5 from '../v5'; -import v6 from '../v6'; -import v7 from '../v7'; -import v8 from '../v8'; -import v9 from '../v9'; -import v10 from '../v10'; -import v11 from '../v11'; -import v12 from '../v12'; -import v13 from '../v13'; -import v14 from '../v14'; -import v15 from '../v15'; -import v16 from '../v16'; -import v17 from '../v17'; -import v18 from '../v18'; -import v19 from '../v19'; - -type Decoder = (buf: Uint8Array, start: number, length: number) => string; - -const runTests = (name: string, decodeUtf8: Decoder) => { - describe(name, () => { - test('can decode basic string', () => { - const arr = new ArrayBuffer(8); - const buf = new Uint8Array(arr); - buf[0] = 1; - buf[1] = 0x61; - buf[2] = 0x62; - buf[3] = 0x63; - buf[4] = 0x64; - buf[5] = 0x65; - buf[6] = 0x66; - buf[7] = 1; - const uint8 = buf.subarray(1, 7); - const str = decodeUtf8(uint8, 1, 2); - expect(str).toBe('bc'); - }); - - test('can decode emoji', () => { - const arr = new ArrayBuffer(8); - const buf = new Uint8Array(arr); - buf[0] = 1; - buf[1] = 0x61; - buf[2] = 0xf0; - buf[3] = 0x9f; - buf[4] = 0x98; - buf[5] = 0x80; - buf[6] = 0x62; - buf[7] = 1; - const uint8 = buf.subarray(1, 7); - const str = decodeUtf8(uint8, 1, 4); - expect(str).toBe('๐Ÿ˜€'); - }); - - test('can decode a long string', () => { - const str = 'a'.repeat(33333); - const buf = Buffer.from(str); - const arr = new Uint8Array(buf.length); - for (let i = 0; i < arr.length; i++) arr[i] = buf[i]; - const str2 = decodeUtf8(arr, 0, arr.length); - expect(str2).toBe(str); - }); - - test('can decode real-world sentence', () => { - const str = '๐Ÿ’ฏ RรซactQuill v2 ReactQuill 2 is here, baby! And it brings a fรผll port to TypeScript and React 16+.'; - const buf = Buffer.from(str); - const arr = new Uint8Array(buf.length); - for (let i = 0; i < arr.length; i++) arr[i] = buf[i]; - const str2 = decodeUtf8(arr, 0, arr.length); - expect(str2).toBe(str); - }); - - test('can decode various types of characters', () => { - const alphabet = [ - // 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - // 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - // 'u', 'v', 'w', 'x', 'y', 'z', - // 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - // 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - // 'U', 'V', 'W', 'X', 'Y', 'Z', - // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - // '-', '_', '.', ',', ';', '!', '@', '#', '$', '%', - // '^', '&', '*', '\\', '/', '(', ')', '+', '=', '\n', - // '๐Ÿ‘', '๐Ÿป', '๐Ÿ˜›', 'รค', 'รถ', 'รผ', 'รŸ', 'ะฐ', 'ะฑ', 'ะฒ', - 'ะณ', - '่ฏถ', - 'ๅฟ…', - '่ฅฟ', - ]; - const str = alphabet.join(''); - const buf = Buffer.from(str); - const arr = new Uint8Array(buf.length); - for (let i = 0; i < arr.length; i++) arr[i] = buf[i]; - const str2 = decodeUtf8(arr, 0, arr.length); - expect(str2).toBe(str); - }); - }); -}; - -runTests('v1', v1); -runTests('v2', v2); -runTests('v3', v3); -runTests('v4', v4); -runTests('v5', v5); -runTests('v6', v6); -runTests('v7', v7); -runTests('v8', v8); -runTests('v9', v9); -runTests('v10', v10); -runTests('v11', v11); -runTests('v12', v12); -runTests('v13', v13); -runTests('v14', v14); -runTests('v15', v15); -runTests('v16', v16); -runTests('v17', v17); -runTests('v18', v18); -runTests('v19', v19); diff --git a/src/util/buffers/utf8/decodeUtf8/index.ts b/src/util/buffers/utf8/decodeUtf8/index.ts deleted file mode 100644 index 7e9349894f..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import decodeUtf8 from './v16'; - -export {decodeUtf8}; diff --git a/src/util/buffers/utf8/decodeUtf8/v1.ts b/src/util/buffers/utf8/decodeUtf8/v1.ts deleted file mode 100644 index d9123022b4..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v1.ts +++ /dev/null @@ -1,38 +0,0 @@ -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - const units: Array = []; - let result = ''; - while (offset < end) { - const byte1 = buf[offset++]!; - if ((byte1 & 0x80) === 0) { - units.push(byte1); - } else if ((byte1 & 0xe0) === 0xc0) { - const byte2 = buf[offset++]! & 0x3f; - units.push(((byte1 & 0x1f) << 6) | byte2); - } else if ((byte1 & 0xf0) === 0xe0) { - const byte2 = buf[offset++]! & 0x3f; - const byte3 = buf[offset++]! & 0x3f; - units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3); - } else if ((byte1 & 0xf8) === 0xf0) { - const byte2 = buf[offset++]! & 0x3f; - const byte3 = buf[offset++]! & 0x3f; - const byte4 = buf[offset++]! & 0x3f; - let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4; - if (unit > 0xffff) { - unit -= 0x10000; - units.push(((unit >>> 10) & 0x3ff) | 0xd800); - unit = 0xdc00 | (unit & 0x3ff); - } - units.push(unit); - } else { - units.push(byte1); - } - if (units.length >= 1000) { - result += String.fromCharCode(...units); - units.length = 0; - } - } - if (units.length > 0) result += String.fromCharCode(...units); - return result; -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v10.ts b/src/util/buffers/utf8/decodeUtf8/v10.ts deleted file mode 100644 index ebf8b03d0d..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v10.ts +++ /dev/null @@ -1,39 +0,0 @@ -const fromCharCode = String.fromCharCode; - -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - let str = ''; - while (offset < end) { - const octet1 = buf[offset++]!; - if ((octet1 & 0x80) === 0) { - str += fromCharCode(octet1); - continue; - } - const octet2 = buf[offset++]! & 0x3f; - if ((octet1 & 0xe0) === 0xc0) { - str += fromCharCode(((octet1 & 0x1f) << 6) | octet2); - continue; - } - const octet3 = buf[offset++]! & 0x3f; - if ((octet1 & 0xf0) === 0xe0) { - str += fromCharCode(((octet1 & 0x1f) << 12) | (octet2 << 6) | octet3); - continue; - } - if ((octet1 & 0xf8) === 0xf0) { - const octet4 = buf[offset++]! & 0x3f; - let unit = ((octet1 & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - const unit0 = ((unit >>> 10) & 0x3ff) | 0xd800; - unit = 0xdc00 | (unit & 0x3ff); - str += fromCharCode(unit0, unit); - } else { - str += fromCharCode(unit); - } - } else { - str += fromCharCode(octet1); - } - } - return str; -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v11.ts b/src/util/buffers/utf8/decodeUtf8/v11.ts deleted file mode 100644 index f3beea70cd..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v11.ts +++ /dev/null @@ -1,2 +0,0 @@ -const utf8Slice = Buffer.prototype.utf8Slice; -export default (buf: Uint8Array, start: number, length: number): string => utf8Slice.call(buf, start, start + length); diff --git a/src/util/buffers/utf8/decodeUtf8/v12.ts b/src/util/buffers/utf8/decodeUtf8/v12.ts deleted file mode 100644 index 4a31f6dfc0..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v12.ts +++ /dev/null @@ -1,5 +0,0 @@ -const from = Buffer.from; -export default (arr: Uint8Array, start: number, length: number): string => - from(arr) - .subarray(start, start + length) - .toString(); diff --git a/src/util/buffers/utf8/decodeUtf8/v13.ts b/src/util/buffers/utf8/decodeUtf8/v13.ts deleted file mode 100644 index de26b1dc64..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v13.ts +++ /dev/null @@ -1,27 +0,0 @@ -import v10 from './v10'; - -let decode = v10; - -const hasBuffer = typeof Buffer !== 'undefined'; -const utf8Slice = hasBuffer ? Buffer.prototype.utf8Slice : null; - -if (utf8Slice) { - decode = (buf: Uint8Array, start: number, length: number): string => - length <= 10 ? v10(buf, start, length) : utf8Slice.call(buf, start, start + length); -} else { - const from = hasBuffer ? Buffer.from : null; - if (from) { - decode = (buf: Uint8Array, start: number, length: number): string => - length < 30 - ? v10(buf, start, length) - : from(buf) - .subarray(start, start + length) - .toString(); - } else if (typeof TextDecoder !== 'undefined') { - const decoder = new TextDecoder(); - decode = (buf: Uint8Array, start: number, length: number): string => - length < 150 ? v10(buf, start, length) : decoder.decode(buf.subarray(start, start + length)); - } -} - -export default decode; diff --git a/src/util/buffers/utf8/decodeUtf8/v14.ts b/src/util/buffers/utf8/decodeUtf8/v14.ts deleted file mode 100644 index d9062ad933..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v14.ts +++ /dev/null @@ -1,8 +0,0 @@ -import v10 from './v10'; - -const hasBuffer = typeof Buffer !== 'undefined'; -const utf8Slice = hasBuffer ? Buffer.prototype.utf8Slice : null; - -export default utf8Slice - ? (buf: Uint8Array, start: number, length: number): string => utf8Slice.call(buf, start, start + length) - : v10; diff --git a/src/util/buffers/utf8/decodeUtf8/v15.ts b/src/util/buffers/utf8/decodeUtf8/v15.ts deleted file mode 100644 index b5557c0631..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v15.ts +++ /dev/null @@ -1,16 +0,0 @@ -import v10 from './v10'; - -const hasBuffer = typeof Buffer !== 'undefined'; -const utf8Slice = hasBuffer ? Buffer.prototype.utf8Slice : null; -const from = hasBuffer ? Buffer.from : null; - -export default (buf: Uint8Array, start: number, length: number): string => { - const end = start + length; - return length > 8 - ? utf8Slice - ? utf8Slice.call(buf, start, end) - : from - ? from(buf).subarray(start, end).toString('utf8') - : v10(buf, start, length) - : v10(buf, start, length); -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v16.ts b/src/util/buffers/utf8/decodeUtf8/v16.ts deleted file mode 100644 index c95f699fdc..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v16.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {decodeAscii, decodeAsciiMax15} from '../decodeAscii'; -import v18 from './v18'; - -type Decoder = (buf: Uint8Array, start: number, length: number) => string; - -const hasBuffer = typeof Buffer !== 'undefined'; -const utf8Slice = hasBuffer ? Buffer.prototype.utf8Slice : null; -const from = hasBuffer ? Buffer.from : null; - -const shortDecoder: Decoder = (buf, start, length) => decodeAsciiMax15(buf, start, length) ?? v18(buf, start, length); - -const midDecoder: Decoder = (buf, start, length) => decodeAscii(buf, start, length) ?? v18(buf, start, length); - -const longDecoder: Decoder = utf8Slice - ? (buf, start, length) => utf8Slice.call(buf, start, start + length) - : from - ? (buf, start, length) => - from(buf) - .subarray(start, start + length) - .toString('utf8') - : v18; - -const decoder: Decoder = (buf, start, length): string => { - if (length < 16) return shortDecoder(buf, start, length); - if (length < 32) return midDecoder(buf, start, length); - return longDecoder(buf, start, length); -}; - -export default decoder; diff --git a/src/util/buffers/utf8/decodeUtf8/v17.ts b/src/util/buffers/utf8/decodeUtf8/v17.ts deleted file mode 100644 index 604ac7e022..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v17.ts +++ /dev/null @@ -1,37 +0,0 @@ -const fromCharCode = String.fromCharCode; - -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - let str = ''; - while (offset < end) { - let code = buf[offset++]!; - if ((code & 0x80) !== 0) { - const octet2 = buf[offset++]! & 0x3f; - if ((code & 0xe0) === 0xc0) { - code = ((code & 0x1f) << 6) | octet2; - } else { - const octet3 = buf[offset++]! & 0x3f; - if ((code & 0xf0) === 0xe0) { - code = ((code & 0x1f) << 12) | (octet2 << 6) | octet3; - } else { - if ((code & 0xf8) === 0xf0) { - const octet4 = buf[offset++]! & 0x3f; - let unit = ((code & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - const unit0 = ((unit >>> 10) & 0x3ff) | 0xd800; - unit = 0xdc00 | (unit & 0x3ff); - str += fromCharCode(unit0); - code = unit; - } else { - code = unit; - } - } - } - } - } - str += fromCharCode(code); - } - return str; -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v18.ts b/src/util/buffers/utf8/decodeUtf8/v18.ts deleted file mode 100644 index e1b518e004..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v18.ts +++ /dev/null @@ -1,36 +0,0 @@ -const fromCharCode = String.fromCharCode; - -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - const points: number[] = []; - while (offset < end) { - let code = buf[offset++]!; - if ((code & 0x80) !== 0) { - const octet2 = buf[offset++]! & 0x3f; - if ((code & 0xe0) === 0xc0) { - code = ((code & 0x1f) << 6) | octet2; - } else { - const octet3 = buf[offset++]! & 0x3f; - if ((code & 0xf0) === 0xe0) { - code = ((code & 0x1f) << 12) | (octet2 << 6) | octet3; - } else { - if ((code & 0xf8) === 0xf0) { - const octet4 = buf[offset++]! & 0x3f; - let unit = ((code & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - const unit0 = ((unit >>> 10) & 0x3ff) | 0xd800; - code = 0xdc00 | (unit & 0x3ff); - points.push(unit0); - } else { - code = unit; - } - } - } - } - } - points.push(code); - } - return fromCharCode.apply(String, points); -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v19.ts b/src/util/buffers/utf8/decodeUtf8/v19.ts deleted file mode 100644 index 2bbc39dc2b..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v19.ts +++ /dev/null @@ -1,3 +0,0 @@ -const {readUtf8} = require('json-pack-napi'); - -export default readUtf8; diff --git a/src/util/buffers/utf8/decodeUtf8/v2.ts b/src/util/buffers/utf8/decodeUtf8/v2.ts deleted file mode 100644 index c6612407b9..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v2.ts +++ /dev/null @@ -1,2 +0,0 @@ -export default (buf: Uint8Array, start: number, length: number): string => - Buffer.prototype.utf8Slice.call(buf, start, start + length); diff --git a/src/util/buffers/utf8/decodeUtf8/v3.ts b/src/util/buffers/utf8/decodeUtf8/v3.ts deleted file mode 100644 index fb8adea3ec..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v3.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default (arr: Uint8Array, start: number, length: number): string => - Buffer.from(arr) - .slice(start, start + length) - .toString(); diff --git a/src/util/buffers/utf8/decodeUtf8/v4.ts b/src/util/buffers/utf8/decodeUtf8/v4.ts deleted file mode 100644 index 23b26780c4..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v4.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default (arr: Uint8Array, start: number, length: number): string => - Buffer.from(arr) - .subarray(start, start + length) - .toString(); diff --git a/src/util/buffers/utf8/decodeUtf8/v5.ts b/src/util/buffers/utf8/decodeUtf8/v5.ts deleted file mode 100644 index c00b6ca8ec..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v5.ts +++ /dev/null @@ -1,30 +0,0 @@ -export default (uint8: Uint8Array, start: number, length: number): string => { - const end = start + length; - let x = start; - let str = ''; - while (x < end) { - const b1 = uint8[x++]!; - if ((b1 & 0x80) === 0) { - str += String.fromCharCode(b1); - continue; - } else if ((b1 & 0xe0) === 0xc0) { - str += String.fromCharCode(((b1 & 0x1f) << 6) | (uint8[x++]! & 0x3f)); - } else if ((b1 & 0xf0) === 0xe0) { - str += String.fromCharCode(((b1 & 0x1f) << 12) | ((uint8[x++]! & 0x3f) << 6) | (uint8[x++]! & 0x3f)); - } else if ((b1 & 0xf8) === 0xf0) { - const b2 = uint8[x++]! & 0x3f; - const b3 = uint8[x++]! & 0x3f; - const b4 = uint8[x++]! & 0x3f; - let code = ((b1 & 0x07) << 0x12) | (b2 << 0x0c) | (b3 << 0x06) | b4; - if (code > 0xffff) { - code -= 0x10000; - str += String.fromCharCode(((code >>> 10) & 0x3ff) | 0xd800); - code = 0xdc00 | (code & 0x3ff); - } - str += String.fromCharCode(code); - } else { - str += String.fromCharCode(b1); - } - } - return str; -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v6.ts b/src/util/buffers/utf8/decodeUtf8/v6.ts deleted file mode 100644 index 88acf9be18..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v6.ts +++ /dev/null @@ -1,6 +0,0 @@ -const sharedTextDecoder = new TextDecoder(); - -export default (uint8: Uint8Array, start: number, length: number): string => { - const stringBytes = uint8.subarray(start, start + length); - return sharedTextDecoder.decode(stringBytes); -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v7.ts b/src/util/buffers/utf8/decodeUtf8/v7.ts deleted file mode 100644 index 32ff0da65a..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v7.ts +++ /dev/null @@ -1,32 +0,0 @@ -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - const codes: Array = []; - while (offset < end) { - const octet1 = buf[offset++]!; - if ((octet1 & 0x80) === 0) { - codes.push(octet1); - } else if ((octet1 & 0xe0) === 0xc0) { - const octet2 = buf[offset++]! & 0x3f; - codes.push(((octet1 & 0x1f) << 6) | octet2); - } else if ((octet1 & 0xf0) === 0xe0) { - const octet2 = buf[offset++]! & 0x3f; - const octet3 = buf[offset++]! & 0x3f; - codes.push(((octet1 & 0x1f) << 12) | (octet2 << 6) | octet3); - } else if ((octet1 & 0xf8) === 0xf0) { - const octet2 = buf[offset++]! & 0x3f; - const octet3 = buf[offset++]! & 0x3f; - const octet4 = buf[offset++]! & 0x3f; - let unit = ((octet1 & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - codes.push(((unit >>> 10) & 0x3ff) | 0xd800); - unit = 0xdc00 | (unit & 0x3ff); - } - codes.push(unit); - } else { - codes.push(octet1); - } - } - return String.fromCharCode(...codes); -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v8.ts b/src/util/buffers/utf8/decodeUtf8/v8.ts deleted file mode 100644 index 28b77f6de0..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v8.ts +++ /dev/null @@ -1,39 +0,0 @@ -// String.fromCharCode(...units) flushing happens more often. -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - const units: Array = []; - let result = ''; - while (offset < end) { - const byte1 = buf[offset++]!; - if ((byte1 & 0x80) === 0) { - units.push(byte1); - } else if ((byte1 & 0xe0) === 0xc0) { - const byte2 = buf[offset++]! & 0x3f; - units.push(((byte1 & 0x1f) << 6) | byte2); - } else if ((byte1 & 0xf0) === 0xe0) { - const byte2 = buf[offset++]! & 0x3f; - const byte3 = buf[offset++]! & 0x3f; - units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3); - } else if ((byte1 & 0xf8) === 0xf0) { - const byte2 = buf[offset++]! & 0x3f; - const byte3 = buf[offset++]! & 0x3f; - const byte4 = buf[offset++]! & 0x3f; - let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4; - if (unit > 0xffff) { - unit -= 0x10000; - units.push(((unit >>> 10) & 0x3ff) | 0xd800); - unit = 0xdc00 | (unit & 0x3ff); - } - units.push(unit); - } else { - units.push(byte1); - } - if (units.length >= 8) { - result += String.fromCharCode(...units); - units.length = 0; - } - } - if (units.length > 0) result += String.fromCharCode(...units); - return result; -}; diff --git a/src/util/buffers/utf8/decodeUtf8/v9.ts b/src/util/buffers/utf8/decodeUtf8/v9.ts deleted file mode 100644 index 975c03e0f9..0000000000 --- a/src/util/buffers/utf8/decodeUtf8/v9.ts +++ /dev/null @@ -1,35 +0,0 @@ -export default (buf: Uint8Array, start: number, length: number): string => { - let offset = start; - const end = offset + length; - const codes: Array = []; - while (offset < end) { - const octet1 = buf[offset++]!; - if ((octet1 & 0x80) === 0) { - codes.push(octet1); - continue; - } - const octet2 = buf[offset++]! & 0x3f; - if ((octet1 & 0xe0) === 0xc0) { - codes.push(((octet1 & 0x1f) << 6) | octet2); - continue; - } - const octet3 = buf[offset++]! & 0x3f; - if ((octet1 & 0xf0) === 0xe0) { - codes.push(((octet1 & 0x1f) << 12) | (octet2 << 6) | octet3); - continue; - } - if ((octet1 & 0xf8) === 0xf0) { - const octet4 = buf[offset++]! & 0x3f; - let unit = ((octet1 & 0x07) << 0x12) | (octet2 << 0x0c) | (octet3 << 0x06) | octet4; - if (unit > 0xffff) { - unit -= 0x10000; - codes.push(((unit >>> 10) & 0x3ff) | 0xd800); - unit = 0xdc00 | (unit & 0x3ff); - } - codes.push(unit); - } else { - codes.push(octet1); - } - } - return String.fromCharCode(...codes); -}; diff --git a/src/util/buffers/utf8/encode.ts b/src/util/buffers/utf8/encode.ts deleted file mode 100644 index dedb236782..0000000000 --- a/src/util/buffers/utf8/encode.ts +++ /dev/null @@ -1,58 +0,0 @@ -const hasBuffer = typeof Buffer !== undefined; -const utf8Write = hasBuffer - ? (Buffer.prototype.utf8Write as (this: Uint8Array, str: string, pos: number, maxLength: number) => number) - : null; -const from = hasBuffer ? Buffer.from : null; - -export type EncodeString = (arr: Uint8Array, str: string, pos: number, maxLength: number) => number; - -export const encodeUtf8Write: EncodeString = (arr: Uint8Array, str: string, pos: number, maxLength: number): number => - utf8Write!.call(arr, str, pos, maxLength); - -export const encodeFrom: EncodeString = (arr: Uint8Array, str: string, pos: number, maxLength: number): number => { - const offset = arr.byteOffset + pos; - const buf = from!(arr.buffer).subarray(offset, offset + maxLength); - return buf.write(str, 0, maxLength, 'utf8'); -}; - -export const encodeNative: EncodeString = (arr: Uint8Array, str: string, pos: number, maxLength: number): number => { - const length = str.length; - const start = pos; - let curr = 0; - while (curr < length) { - let value = str.charCodeAt(curr++); - if ((value & 0xffffff80) === 0) { - arr[pos++] = value; - continue; - } else if ((value & 0xfffff800) === 0) { - arr[pos++] = ((value >> 6) & 0x1f) | 0xc0; - } else { - if (value >= 0xd800 && value <= 0xdbff) { - if (curr < length) { - const extra = str.charCodeAt(curr); - if ((extra & 0xfc00) === 0xdc00) { - curr++; - value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; - } - } - } - if ((value & 0xffff0000) === 0) { - arr[pos++] = ((value >> 12) & 0x0f) | 0xe0; - arr[pos++] = ((value >> 6) & 0x3f) | 0x80; - } else { - arr[pos++] = ((value >> 18) & 0x07) | 0xf0; - arr[pos++] = ((value >> 12) & 0x3f) | 0x80; - arr[pos++] = ((value >> 6) & 0x3f) | 0x80; - } - } - arr[pos++] = (value & 0x3f) | 0x80; - } - return pos - start; -}; - -const textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null; - -export const encodeTe: EncodeString = (arr: Uint8Array, str: string, pos: number, maxLength: number): number => - textEncoder!.encodeInto(str, arr.subarray(pos, pos + maxLength)).written!; - -export const encode = utf8Write ? encodeUtf8Write : from ? encodeFrom : encodeNative; diff --git a/src/util/buffers/utf8/isUtf8.ts b/src/util/buffers/utf8/isUtf8.ts deleted file mode 100644 index 5b846f0e31..0000000000 --- a/src/util/buffers/utf8/isUtf8.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Validates that the given data is valid UTF-8 text. - * @param buf Data to check. - * @returns True if the data is valid UTF-8. - */ -export const isUtf8 = (buf: Uint8Array, from: number, length: number): boolean => { - const to = from + length; - while (from < to) { - const c = buf[from]; - if (c <= 0x7f) { - from++; - continue; - } - if (c >= 0xc2 && c <= 0xdf) { - if (buf[from + 1] >> 6 === 2) { - from += 2; - continue; - } else return false; - } - const c1 = buf[from + 1]; - if ( - ((c === 0xe0 && c1 >= 0xa0 && c1 <= 0xbf) || (c === 0xed && c1 >= 0x80 && c1 <= 0x9f)) && - buf[from + 2] >> 6 === 2 - ) { - from += 3; - continue; - } - if (((c >= 0xe1 && c <= 0xec) || (c >= 0xee && c <= 0xef)) && c1 >> 6 === 2 && buf[from + 2] >> 6 === 2) { - from += 3; - continue; - } - if ( - ((c === 0xf0 && c1 >= 0x90 && c1 <= 0xbf) || - (c >= 0xf1 && c <= 0xf3 && c1 >> 6 === 2) || - (c === 0xf4 && c1 >= 0x80 && c1 <= 0x8f)) && - buf[from + 2] >> 6 === 2 && - buf[from + 3] >> 6 === 2 - ) { - from += 4; - continue; - } - return false; - } - return true; -}; diff --git a/src/util/buffers/utf8/sharedCachedUtf8Decoder.ts b/src/util/buffers/utf8/sharedCachedUtf8Decoder.ts deleted file mode 100644 index 86b2d4ad03..0000000000 --- a/src/util/buffers/utf8/sharedCachedUtf8Decoder.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {CachedUtf8Decoder} from './CachedUtf8Decoder'; - -export default new CachedUtf8Decoder(); diff --git a/src/util/codegen/Codegen.ts b/src/util/codegen/Codegen.ts deleted file mode 100644 index ab929b1a44..0000000000 --- a/src/util/codegen/Codegen.ts +++ /dev/null @@ -1,296 +0,0 @@ -import {compileClosure} from '.'; -import type {JavaScriptLinked} from './types'; - -/** - * Inline JavaScript statements that are executed in main function body. - */ -export class CodegenStepExecJs { - constructor(public readonly js: string) {} -} - -/** - * A step can be `CodegenStepExecJs` or some application specific step, which - * will later will need to be converted to `CodegenStepExecJs`. - */ -type JsonSerializerStep = CodegenStepExecJs | unknown; - -/** - * Configuration options for {@link Codegen} instances. - */ -export interface CodegenOptions> { - /** - * Inline JavaScript string that represents the arguments that will be passed - * to the main function body. Defaults to "r0", i.e. the first register. - */ - args?: string[]; - - /** - * Name of the generated function. - */ - name?: string; - - /** - * Inline JavaScript statements, that execute at the beginning of the main - * function body. - */ - prologue?: string; - - /** - * Inline JavaScript statements, that execute at the end of the main - * function body. - */ - epilogue?: string | (() => string); - - /** - * Converts all steps to `CodegenStepExecJs`. - */ - processSteps?: (steps: JsonSerializerStep[]) => CodegenStepExecJs[]; - - /** - * Predefined list of dependencies that can be linked on demand. Dependency is - * linked with the name of the property and is linked only once. - */ - linkable?: Linkable; -} - -export type CodegenGenerateOptions = Pick; - -/** - * A helper class which helps with building JavaScript code for a single - * function. It keeps track of external dependencies, internally generated - * constants, and execution steps, which at the end are all converted to - * to an executable JavaScript function. - * - * The final output is a JavaScript function enclosed in a closure: - * - * ```js - * (function(d1, d2, d3) { - * var c1 = something; - * var c2 = something; - * var c3 = something; - * return function(r0) { - * var r1 = something; - * var r2 = something; - * var r3 = something; - * return something; - * } - * }) - * ``` - * - * Where `d*` are the external dependencies, `c*` are the internal constants, - * and `r*` are the local immutable infinite registers. - */ -export class Codegen< - Fn extends (...deps: any[]) => any = (...deps: unknown[]) => unknown, - Linkable = Record, -> { - /** @ignore */ - protected steps: JsonSerializerStep[] = []; - - /** @ignore */ - public options: Required>; - - constructor(opts: CodegenOptions) { - this.options = { - args: ['r0'], - name: '', - prologue: '', - epilogue: '', - processSteps: (steps) => steps.filter((step) => step instanceof CodegenStepExecJs) as CodegenStepExecJs[], - linkable: {} as Linkable, - ...opts, - }; - this.registerCounter = this.options.args.length; - } - - /** - * Add one or more JavaScript statements to the main function body. - */ - public js(js: string): void { - this.steps.push(new CodegenStepExecJs(js)); - } - - public var(expression?: string): string { - const r = this.getRegister(); - if (expression) this.js('var ' + r + ' = ' + expression + ';'); - else this.js('var ' + r + ';'); - return r; - } - - public if(condition: string, then: () => void, otherwise?: () => void): void { - this.js('if (' + condition + ') {'); - then(); - if (otherwise) { - this.js('} else {'); - otherwise(); - } - this.js('}'); - } - - public switch( - expression: string, - cases: [match: string | number | boolean | null, block: () => void, noBreak?: boolean][], - def?: () => void, - ): void { - this.js('switch (' + expression + ') {'); - for (const [match, block, noBreak] of cases) { - this.js('case ' + match + ': {'); - block(); - if (!noBreak) this.js('break;'); - this.js('}'); - } - if (def) { - this.js('default: {'); - def(); - this.js('}'); - } - this.js('}'); - } - - public return(expression: string): void { - this.js('return ' + expression + ';'); - } - - /** - * Add any application specific execution step. Steps of `unknown` type - * later need to converted to `CodegenStepExecJs` steps in the `.processStep` - * callback. - * - * @param step A step in function execution logic. - */ - public step(step: unknown): void { - this.steps.push(step); - } - - protected registerCounter: number; - - /** - * Codegen uses the idea of infinite registers. It starts with `0` and - * increments it by one for each new register. Best practice is to use - * a new register for each new variable and keep them immutable. - * - * Usage: - * - * ```js - * const r = codegen.getRegister(); - * codegen.js(`const ${r} = 1;`); - * ``` - * - * @returns a unique identifier for a variable. - */ - public getRegister(): string { - return `r${this.registerCounter++}`; - } - public r(): string { - return this.getRegister(); - } - - /** @ignore */ - protected dependencies: unknown[] = []; - protected dependencyNames: string[] = []; - - /** - * Allows to wire up dependencies to the generated code. - * - * @param dep Any JavaScript dependency, could be a function, an object, - * or anything else. - * @param name Optional name of the dependency. If not provided, a unique - * name will be generated, which starts with `d` and a counter - * appended. - * @returns Returns the dependency name, a code symbol which can be used as - * variable name. - */ - public linkDependency(dep: unknown, name: string = 'd' + this.dependencies.length): string { - this.dependencies.push(dep); - this.dependencyNames.push(name); - return name; - } - - /** - * Sames as {@link Codegen#linkDependency}, but allows to wire up multiple - * dependencies at once. - */ - public linkDependencies(deps: unknown[]): string[] { - return deps.map((dep) => this.linkDependency(dep)); - } - - protected linked: {[key: string]: 1} = {}; - - /** - * Link a dependency from the pre-defined `options.linkable` object. This method - * can be called many times with the same dependency name, the dependency will - * be linked only once. - * - * @param name Linkable dependency name. - */ - public link(name: keyof Linkable): void { - if (this.linked[name as string]) return; - this.linked[name as string] = 1; - this.linkDependency(this.options.linkable[name], name as string); - } - - /** @ignore */ - protected constants: string[] = []; - protected constantNames: string[] = []; - - /** - * Allows to encode any code or value in the closure of the generated - * function. - * - * @param constant Any JavaScript value in string form. - * @param name Optional name of the constant. If not provided, a unique - * name will be generated, which starts with `c` and a counter - * appended. - * @returns Returns the constant name, a code symbol which can be used as - * variable name. - */ - public addConstant(constant: string, name: string = 'c' + this.constants.length): string { - this.constants.push(constant); - this.constantNames.push(name); - return name; - } - - /** - * Sames as {@link Codegen#addConstant}, but allows to create multiple - * constants at once. - */ - public addConstants(constants: string[]): string[] { - return constants.map((constant) => this.addConstant(constant)); - } - - /** - * Returns generated JavaScript code with the dependency list. - * - * ```js - * const code = codegen.generate(); - * const fn = eval(code.js)(...code.deps); - * const result = fn(...args); - * ``` - */ - public generate(opts: CodegenGenerateOptions = {}): JavaScriptLinked { - const {name, args, prologue, epilogue} = {...this.options, ...opts}; - const steps = this.options.processSteps(this.steps); - const js = `(function(${this.dependencyNames.join(', ')}) { -${this.constants.map((constant, index) => `var ${this.constantNames[index]} = (${constant});`).join('\n')} -return ${name ? `function ${name}` : 'function'}(${args.join(',')}){ -${prologue} -${steps.map((step) => (step as CodegenStepExecJs).js).join('\n')} -${typeof epilogue === 'function' ? epilogue() : epilogue || ''} -}})`; - // console.log(js); - return { - deps: this.dependencies, - js: js as JavaScriptLinked['js'], - }; - } - - /** - * Compiles the generated JavaScript code into a function. - * - * @returns JavaScript function ready for execution. - */ - public compile(opts?: CodegenGenerateOptions): Fn { - const closure = this.generate(opts); - return compileClosure(closure); - } -} diff --git a/src/util/codegen/README.md b/src/util/codegen/README.md deleted file mode 100644 index 5a3951e1af..0000000000 --- a/src/util/codegen/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# util/codegen - -This folder contains utilities for generating code. It is sometimes possible to -generate an optimized function that will execute significantly faster given -a "schema", or "template", of execution. - -Some examples: - -- Deep equality comparison function: if we know one object in advance we can - generate an optimized function which accepts a single object. It is - implemented in `json-equal` library. -- JSON Patch execution: if we know the JSON Patch in advance, we can generate - an optimized function which applies the JSON patch in the most efficient way. - It is implemented in `json-patch` library. -- Given a `json-type` schema of a JSON object, it is possible to generate - optimized functions for validation and serialization of objects according to - that schema. diff --git a/src/util/codegen/__tests__/Codegen.spec.ts b/src/util/codegen/__tests__/Codegen.spec.ts deleted file mode 100644 index 4183da60e5..0000000000 --- a/src/util/codegen/__tests__/Codegen.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {CodegenStepExecJs} from '..'; -import {Codegen} from '../Codegen'; - -test('can generate a simple function', () => { - const codegen = new Codegen({ - name: 'foobar', - args: ['a', 'b'], - prologue: 'var res = 0;', - epilogue: 'return res;', - processSteps: (steps) => { - return steps.map((step) => { - if (typeof step === 'number') { - return new CodegenStepExecJs(`a += ${step};`); - } else return step; - }) as CodegenStepExecJs[]; - }, - }); - codegen.step(4); - const [c1, c2] = codegen.addConstants(['1', '2']); - codegen.js(`b += ${c1} + ${c2};`); - const byTwo = (num: number) => 2 * num; - codegen.linkDependency(byTwo, 'byTwo'); - codegen.js(`res += byTwo(a) + byTwo(b);`); - const code = codegen.generate(); - const fn = codegen.compile(); - // console.log(code.js); - expect(code.deps).toStrictEqual([byTwo]); - expect(typeof code.js).toBe('string'); - expect(fn(1, 2)).toBe(20); - expect(fn.name).toBe('foobar'); -}); diff --git a/src/util/codegen/compile.ts b/src/util/codegen/compile.ts deleted file mode 100644 index a1c0bbbbe2..0000000000 --- a/src/util/codegen/compile.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {JavaScriptLinked} from '.'; -import {JavaScript} from './types'; - -// tslint:disable-next-line -export const compile = (js: JavaScript): T => eval(js); - -export const compileClosure = (fn: JavaScriptLinked): T => compile(fn.js)(...fn.deps); diff --git a/src/util/codegen/dynamicFunction.ts b/src/util/codegen/dynamicFunction.ts deleted file mode 100644 index 38a622a0b7..0000000000 --- a/src/util/codegen/dynamicFunction.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Wraps a function into a proxy function with the same signature, but which can - * be re-implemented by the user at runtime. - * - * @param implementation Initial implementation. - * @returns Proxy function and implementation setter. - */ -export const dynamicFunction = any>( - implementation: F, -): [fn: F, set: (fn: F) => void] => { - const proxy = ((...args) => implementation(...args)) as F; - const set = (f: F) => { - implementation = f; - }; - return [proxy, set]; -}; diff --git a/src/util/codegen/index.ts b/src/util/codegen/index.ts index 3a96aef3c2..41fa667fe6 100644 --- a/src/util/codegen/index.ts +++ b/src/util/codegen/index.ts @@ -1,3 +1 @@ -export * from './types'; -export * from './compile'; -export * from './Codegen'; +export * from '@jsonjoy.com/json-pack/lib/util/codegen'; diff --git a/src/util/codegen/switch.ts b/src/util/codegen/switch.ts deleted file mode 100644 index 3c98a124fc..0000000000 --- a/src/util/codegen/switch.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {dynamicFunction} from './dynamicFunction'; - -/** - * Switcher for code generation. It first executes "evaluation" function - * 3 times, and then generates optimized code. - */ -export const createSwitch = any>(fn: F, codegen: () => F): F => { - let counter = 0; - const [proxy, set] = dynamicFunction((...args) => { - if (counter > 2) set(codegen()); - counter++; - return fn(...args); - }); - return proxy as F; -}; diff --git a/src/util/codegen/types.ts b/src/util/codegen/types.ts deleted file mode 100644 index b9bcd8a44f..0000000000 --- a/src/util/codegen/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type {Brand} from '../types'; - -/** - * Represents a string which contains JavaScript code, which can be - * executed by the `eval` function. - * - * ```ts - * const code: JavaScript<() => {}> = `() => {}`; - * const fn = eval(code); // () => {} - * ``` - */ -export type JavaScript = Brand; - -/** - * Represents a string which contains JavaScript code, which is enclosed - * in a JavaScript closure function. The dependencies can be "linked" to - * the JavaScript code, by executing the outer closure function with the - * list of dependencies as arguments. - * - * ```ts - * const multBy: JavaScriptClosure<(x: number) => number, [by: number]> = - * 'function(by) { return function (x) { return x * by }}'; - * - * const multBy3 = eval(multBy)(3); - * - * multBy3(5); // 15 - * ``` - */ -export type JavaScriptClosure = JavaScript<(...deps: D) => Js>; - -/** - * Represents a {@link JavaScriptClosure} with a fixed list of dependencies, - * that can be linked to the JavaScript code-generated closure. - */ -export interface JavaScriptLinked { - deps: Dependencies; - js: JavaScriptClosure; -} diff --git a/src/util/codegen/util/JsExpression.ts b/src/util/codegen/util/JsExpression.ts deleted file mode 100644 index 7ddbd4e604..0000000000 --- a/src/util/codegen/util/JsExpression.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * JsExpression monad allows to write JS expression as strings which depend on each - * other and tracks whether an expression was used or not. - * - * ```ts - * const expr = new JsExpression(() => 'r0'); - * const subExpr = expr.chain((expr) => `${expr}["key"]`); - * - * expr.wasUsed; // false - * subExpr.use(); // r0["key"] - * expr.wasUsed; // true - * subExpr.wasUsed; // true - * ``` - */ -export class JsExpression { - private _wasUsed: boolean = false; - private _expression?: string; - private _listeners: ((expr: string) => void)[] = []; - - constructor(private expression: () => string) {} - - public get wasUsed(): boolean { - return this._wasUsed; - } - - public use(): string { - if (this._wasUsed) return this._expression!; - this._wasUsed = true; - const expression = (this._expression = this.expression()); - for (const listener of this._listeners) listener(expression); - return expression; - } - - public chain(use: (expr: string) => string): JsExpression { - return new JsExpression(() => use(this.use())); - } - - public addListener(listener: (expr: string) => void): void { - this._listeners.push(listener); - } -} diff --git a/src/util/codegen/util/helpers.ts b/src/util/codegen/util/helpers.ts deleted file mode 100644 index 8558b290da..0000000000 --- a/src/util/codegen/util/helpers.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const emitStringMatch = (expression: string, offset: string, match: string) => { - const conditions: string[] = []; - for (let i = 0; i < match.length; i++) - conditions.push(`${match.charCodeAt(i)} === ${expression}.charCodeAt(${offset} + ${i})`); - return conditions.join(' && '); -}; diff --git a/src/util/codegen/util/normalizeAccessor.ts b/src/util/codegen/util/normalizeAccessor.ts deleted file mode 100644 index 8bfa283fb7..0000000000 --- a/src/util/codegen/util/normalizeAccessor.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const normalizeAccessor = (accessor: string): string => { - if (/^[a-z_][a-z_0-9]*$/i.test(accessor)) { - return '.' + accessor; - } else { - return `[${JSON.stringify(accessor)}]`; - } -}; diff --git a/src/util/router/codegen.ts b/src/util/router/codegen.ts index cff9b898f8..96e516fca3 100644 --- a/src/util/router/codegen.ts +++ b/src/util/router/codegen.ts @@ -1,5 +1,5 @@ -import {Codegen} from '../codegen'; -import {JsExpression} from '../codegen/util/JsExpression'; +import {Codegen} from '@jsonjoy.com/json-pack/lib/util/codegen'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import type {Match} from './router'; export type RouteMatcher = (route: string) => undefined | Match; diff --git a/src/util/router/router.ts b/src/util/router/router.ts index 5ffaf74023..6b0daee5d0 100644 --- a/src/util/router/router.ts +++ b/src/util/router/router.ts @@ -1,4 +1,4 @@ -import {JsExpression} from '../codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; import {printTree} from '../print/printTree'; import {Printable} from '../print/types'; import {RouteMatcher, RouterCodegenCtx, RouterCodegenOpts} from './codegen'; diff --git a/src/util/router/tree.ts b/src/util/router/tree.ts index b6ea5b49ae..d32484a128 100644 --- a/src/util/router/tree.ts +++ b/src/util/router/tree.ts @@ -1,4 +1,4 @@ -import {emitStringMatch} from '../codegen/util/helpers'; +import {emitStringMatch} from '@jsonjoy.com/json-pack/lib/util/codegen/util/helpers'; import {printTree} from '../print/printTree'; import {Printable} from '../print/types'; import {RadixTree} from '../trees/radix/RadixTree'; diff --git a/src/util/sort/insertion.ts b/src/util/sort/insertion.ts index ff12dfb289..69d45d86c7 100644 --- a/src/util/sort/insertion.ts +++ b/src/util/sort/insertion.ts @@ -1,21 +1 @@ -/** - * Insertion sort, should be faster than built-int sort for small arrays. - * - * @todo Move this to `thingies` package. - * - * @param arr Array to sort. - * @returns Returns the same array instance. - */ -export const sort = (arr: T[]): T[] => { - const length = arr.length; - for (let i = 1; i < length; i++) { - const currentValue = arr[i]; - let position = i; - while (position !== 0 && arr[position - 1] > currentValue) { - arr[position] = arr[position - 1]; - position--; - } - arr[position] = currentValue; - } - return arr; -}; +export * from '@jsonjoy.com/json-pack/lib/util/sort/insertion'; diff --git a/src/util/sort/insertion2.ts b/src/util/sort/insertion2.ts index 8525b7e350..dbf23491f1 100644 --- a/src/util/sort/insertion2.ts +++ b/src/util/sort/insertion2.ts @@ -1,22 +1 @@ -/** - * Insertion sort, should be faster than built-int sort for small arrays. - * - * @todo Move this to `thingies` package. - * - * @param arr Array to sort. - * @param comparator Comparator function. - * @returns Returns the same array instance. - */ -export const sort = (arr: T[], comparator: (a: T, b: T) => number): T[] => { - const length = arr.length; - for (let i = 1; i < length; i++) { - const currentValue = arr[i]; - let position = i; - while (position !== 0 && comparator(arr[position - 1], currentValue) > 0) { - arr[position] = arr[position - 1]; - position--; - } - arr[position] = currentValue; - } - return arr; -}; +export * from '@jsonjoy.com/json-pack/lib/util/sort/insertion2'; diff --git a/src/util/strings/__tests__/asString.spec.ts b/src/util/strings/__tests__/asString.spec.ts deleted file mode 100644 index 80d924eb8e..0000000000 --- a/src/util/strings/__tests__/asString.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {asString} from '../asString'; - -const check = (str: string) => { - expect(asString(str)).toBe(JSON.stringify(str)); - expect(JSON.parse(asString(str))).toBe(str); -}; - -const generateStr = (): string => { - let str = ''; - for (let i = 0; i < 5; i++) str += String.fromCodePoint(Math.round(Math.random() * 0x6ffff)); - return str; -}; - -test('encodes the same as JSON.stringify()', () => { - check(''); - check('"'); - check("'"); - check('asdf'); - check('asdfasdfasdfasdfsadfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfsadfasdfasdfasdf'); - check('๐Ÿป'); - check('๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ'); - check('ะ›ะตะบัะธะปะพะณะพั'); - check('\b'); - check('\b\t\0'); - check('\0'); - check('\f'); - check('\r'); - check('\n'); -}); - -test('encodes the same as JSON.stringify(), autogenerated', () => { - for (let i = 0; i < 10000; i++) check(generateStr()); -}); diff --git a/src/util/strings/__tests__/utf8.spec.ts b/src/util/strings/__tests__/utf8.spec.ts deleted file mode 100644 index 9bbdaf5783..0000000000 --- a/src/util/strings/__tests__/utf8.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {utf8Size} from '../utf8'; - -describe('utf8Size', () => { - describe('computes correct size', () => { - const check = (str: string) => { - expect(utf8Size(str)).toBe(Buffer.from(str).byteLength); - }; - - test('encodes the same as JSON.stringify()', () => { - check(''); - check('"'); - check("'"); - check('asdf'); - check('asdfasdfasdfasdfsadfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfsadfasdfasdfasdf'); - check('๐Ÿป'); - check('๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ'); - check('ะ›ะตะบัะธะปะพะณะพั'); - check('\b'); - check('\b\t\0'); - check('\0'); - check('\f'); - check('\r'); - check('\n'); - }); - }); -}); diff --git a/src/util/strings/__tests__/util.spec.ts b/src/util/strings/__tests__/util.spec.ts deleted file mode 100644 index 2ae776bdf1..0000000000 --- a/src/util/strings/__tests__/util.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {isLetter, isPunctuation, isWhitespace} from '../util'; - -describe('isLetter()', () => { - it('should return true for letters', () => { - expect(isLetter('a')).toBe(true); - expect(isLetter('z')).toBe(true); - expect(isLetter('รฆ')).toBe(true); - expect(isLetter('ะฑ')).toBe(true); - expect(isLetter('A')).toBe(true); - }); - - it('should return true for numbers', () => { - expect(isLetter('0')).toBe(true); - expect(isLetter('1')).toBe(true); - expect(isLetter('9')).toBe(true); - }); - - it('should return false for non-letters', () => { - expect(isLetter('!')).toBe(false); - expect(isLetter(' ')).toBe(false); - expect(isLetter(' ')).toBe(false); - }); -}); - -describe('isPunctuation()', () => { - it('should return true for punctuation', () => { - expect(isPunctuation('.')).toBe(true); - expect(isPunctuation(',')).toBe(true); - expect(isPunctuation('?')).toBe(true); - expect(isPunctuation('!')).toBe(true); - expect(isPunctuation('โ€ฆ')).toBe(true); - }); - - it('should return false for non-punctuation', () => { - expect(isPunctuation('a')).toBe(false); - expect(isPunctuation('1')).toBe(false); - expect(isPunctuation(' ')).toBe(false); - }); -}); - -describe('isWhitespace()', () => { - it('should return true for whitespace', () => { - expect(isWhitespace(' ')).toBe(true); - expect(isWhitespace('\t')).toBe(true); - expect(isWhitespace('\n')).toBe(true); - expect(isWhitespace('\r')).toBe(true); - }); - - it('should return false for non-whitespace', () => { - expect(isWhitespace('a')).toBe(false); - expect(isWhitespace('1')).toBe(false); - expect(isWhitespace('.')).toBe(false); - }); -}); diff --git a/src/util/strings/__tests__/wordWrap.spec.ts b/src/util/strings/__tests__/wordWrap.spec.ts deleted file mode 100644 index c19d576740..0000000000 --- a/src/util/strings/__tests__/wordWrap.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {wordWrap} from '../wordWrap'; - -test('does not format a short line', () => { - expect(wordWrap('Hello')).toStrictEqual(['Hello']); -}); - -const text = - 'Acclaimed Harvard professor and entrepreneur Dr. David Sinclair believes that we will see human life expectancy increase to at least 100 years within this century. A world in which humans live significantly longer will have a major impact on economies, policies, healthcare, education, ethics, and more. Sinclair joined Bridgewater Portfolio Strategist Atul Lele to discuss the science and societal, political, systemic and ethical implications of humans living significantly longer lives.'; - -test('wraps long text', () => { - const result = wordWrap(text); - expect(result).toMatchInlineSnapshot(` - [ - "Acclaimed Harvard professor and entrepreneur Dr. ", - "David Sinclair believes that we will see human ", - "life expectancy increase to at least 100 years ", - "within this century. A world in which humans live ", - "significantly longer will have a major impact on ", - "economies, policies, healthcare, education, ", - "ethics, and more. Sinclair joined Bridgewater ", - "Portfolio Strategist Atul Lele to discuss the ", - "science and societal, political, systemic and ", - "ethical implications of humans living ", - "significantly longer lives.", - ] - `); -}); - -test('can specify line width', () => { - const result = wordWrap(text, {width: 80}); - expect(result).toMatchInlineSnapshot(` - [ - "Acclaimed Harvard professor and entrepreneur Dr. David Sinclair believes that we ", - "will see human life expectancy increase to at least 100 years within this ", - "century. A world in which humans live significantly longer will have a major ", - "impact on economies, policies, healthcare, education, ethics, and more. Sinclair ", - "joined Bridgewater Portfolio Strategist Atul Lele to discuss the science and ", - "societal, political, systemic and ethical implications of humans living ", - "significantly longer lives.", - ] - `); -}); diff --git a/src/util/strings/asString.ts b/src/util/strings/asString.ts deleted file mode 100644 index 34de018ea1..0000000000 --- a/src/util/strings/asString.ts +++ /dev/null @@ -1,22 +0,0 @@ -const stringify = JSON.stringify; - -/** Serialize text as a JSON string value. */ -export const asString = (str: string) => { - const length = str.length; - if (length > 41) return stringify(str); - let result = ''; - let last = 0; - let found = false; - let point = 255; - for (let i = 0; i < length && point >= 32; i++) { - point = str.charCodeAt(i); - if (point >= 0xd800 && point <= 0xdfff) return stringify(str); - if (point === 34 || point === 92) { - result += str.slice(last, i) + '\\'; - last = i; - found = true; - } - } - if (point < 32) return stringify(str); - return '"' + (!found ? str : result + str.slice(last)) + '"'; -}; diff --git a/src/util/strings/escape.ts b/src/util/strings/escape.ts deleted file mode 100644 index ad3d60ef7b..0000000000 --- a/src/util/strings/escape.ts +++ /dev/null @@ -1,134 +0,0 @@ -// License: https://github.com/BridgeAR/safe-stable-stringify/blob/78891ff37c6e8936118b8fa47ed59dd761c3208a/LICENSE - -const strEscapeSequencesRegExp = - /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/; -const strEscapeSequencesReplacer = - /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/g; -const meta = [ - '\\u0000', - '\\u0001', - '\\u0002', - '\\u0003', - '\\u0004', - '\\u0005', - '\\u0006', - '\\u0007', - '\\b', - '\\t', - '\\n', - '\\u000b', - '\\f', - '\\r', - '\\u000e', - '\\u000f', - '\\u0010', - '\\u0011', - '\\u0012', - '\\u0013', - '\\u0014', - '\\u0015', - '\\u0016', - '\\u0017', - '\\u0018', - '\\u0019', - '\\u001a', - '\\u001b', - '\\u001c', - '\\u001d', - '\\u001e', - '\\u001f', - '', - '', - '\\"', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '\\\\', -]; - -const esc_ = (str: string): string => { - if (str.length === 2) return str[0] + '\\u' + str.charCodeAt(1).toString(16); - const charCode = str.charCodeAt(0); - return meta.length > charCode ? meta[charCode] : '\\u' + charCode.toString(16); -}; - -export const escape = (str: string): string => { - let point, - last = 0, - result = ''; - if (str.length < 5000 && !strEscapeSequencesRegExp.test(str)) return str; - if (str.length > 100) return str.replace(strEscapeSequencesReplacer, esc_); - for (let i = 0; i < str.length; i++) { - point = str.charCodeAt(i); - if (point === 34 || point === 92 || point < 32) { - result += str.slice(last, i) + meta[point]; - last = i + 1; - } else if (point >= 0xd800 && point <= 0xdfff) { - if (point <= 0xdbff && i + 1 < str.length) { - point = str.charCodeAt(i + 1); - if (point >= 0xdc00 && point <= 0xdfff) { - i++; - continue; - } - } - result += str.slice(last, i) + '\\u' + point.toString(16); - last = i + 1; - } - } - result += str.slice(last); - return result; -}; diff --git a/src/util/strings/flatstr.ts b/src/util/strings/flatstr.ts deleted file mode 100644 index 1479ac271a..0000000000 --- a/src/util/strings/flatstr.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const flatstr = (s: string): string => { - (s) | 0; - Number(s); - return s; -}; diff --git a/src/util/strings/utf8.ts b/src/util/strings/utf8.ts deleted file mode 100644 index 9631bb33b4..0000000000 --- a/src/util/strings/utf8.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Given a JavaScript string, computes how many bytes it will take to encode - * that string in UTF-8. - * - * @param str JavaScript string. - * @returns Length in bytes if encoded as UTF-8. - */ -export function utf8Size(str: string): number { - const length = str.length; - let size = 0; - let pos = 0; - while (pos < length) { - let value = str.charCodeAt(pos++); - if ((value & 0xffffff80) === 0) { - size++; - continue; - } else if ((value & 0xfffff800) === 0) size += 2; - else { - if (value >= 0xd800 && value <= 0xdbff && pos < length) { - const extra = str.charCodeAt(pos); - if ((extra & 0xfc00) === 0xdc00) value = (pos++, ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000); - } - size += 3 + +((value & 0xffff0000) !== 0); - } - } - return size; -} diff --git a/src/util/strings/util.ts b/src/util/strings/util.ts deleted file mode 100644 index a75f5804f5..0000000000 --- a/src/util/strings/util.ts +++ /dev/null @@ -1,8 +0,0 @@ -const LETTER_REGEX = /(\p{Letter}|\d)/u; -const WHITESPACE_REGEX = /\s/; - -export type CharPredicate = (char: string) => boolean; - -export const isLetter: CharPredicate = (char: string) => LETTER_REGEX.test(char[0]); -export const isWhitespace: CharPredicate = (char: string) => WHITESPACE_REGEX.test(char[0]); -export const isPunctuation: CharPredicate = (char: string) => !isLetter(char) && !isWhitespace(char); diff --git a/src/util/strings/wordWrap.ts b/src/util/strings/wordWrap.ts deleted file mode 100644 index ab6b61aee2..0000000000 --- a/src/util/strings/wordWrap.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface WrapOptions { - width?: number; -} - -const lineMap = (line: string) => - line.slice(-1) === '\n' ? line.slice(0, line.length - 1).replace(/[ \t]*$/gm, '') : line; -const lineReduce = (acc: string[], line: string) => { - acc.push(...line.split('\n')); - return acc; -}; - -export const wordWrap = (str: string, options: WrapOptions = {}): string[] => { - if (!str) return []; - - const width = options.width || 50; - const regexString = '.{1,' + width + '}([\\s\u200B]+|$)|[^\\s\u200B]+?([\\s\u200B]+|$)'; - const re = new RegExp(regexString, 'g'); - const lines = (str.match(re) || []).map(lineMap).reduce(lineReduce, [] as string[]); - - return lines; -}; diff --git a/src/web3/adl/hamt-crdt/Hamt.ts b/src/web3/adl/hamt-crdt/Hamt.ts index 1097349c54..98992c9022 100644 --- a/src/web3/adl/hamt-crdt/Hamt.ts +++ b/src/web3/adl/hamt-crdt/Hamt.ts @@ -4,7 +4,7 @@ import {HamtFrame} from './HamtFrame'; import * as hlc from '../../hlc'; import {Cid} from '../../multiformats'; import {sha256} from '../../crypto'; -import {toBuf} from '../../../util/buffers/toBuf'; +import {toBuf} from '@jsonjoy.com/json-pack/lib/util/buffers/toBuf'; import type {CidCasStruct} from '../../store/cas/CidCasStruct'; import type * as types from './types'; diff --git a/src/web3/adl/hamt-crdt/HamtFrame.ts b/src/web3/adl/hamt-crdt/HamtFrame.ts index d7db93f788..87cab2b8b4 100644 --- a/src/web3/adl/hamt-crdt/HamtFrame.ts +++ b/src/web3/adl/hamt-crdt/HamtFrame.ts @@ -1,5 +1,5 @@ import {Defer} from 'thingies/es2020/Defer'; -import {cmpUint8Array2} from '../../../util/buffers/cmpUint8Array2'; +import {cmpUint8Array2} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array2'; import {cmpDto} from '../../hlc'; import {CidCasStruct} from '../../store/cas/CidCasStruct'; import {Cid} from '../../multiformats'; diff --git a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts index 0fb09c947d..b52311c47b 100644 --- a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts +++ b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts @@ -1,4 +1,4 @@ -import {b} from '../../../../util/buffers/b'; +import {b} from '@jsonjoy.com/json-pack/lib/util/buffers/b'; import {HlcFactory} from '../../../hlc'; import {CidCasMemory} from '../../../store/cas/CidCasMemory'; import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; diff --git a/src/web3/codec/codecs/cbor.ts b/src/web3/codec/codecs/cbor.ts index b5a6dbdcf4..96f18fc171 100644 --- a/src/web3/codec/codecs/cbor.ts +++ b/src/web3/codec/codecs/cbor.ts @@ -1,5 +1,5 @@ -import {CborEncoderDag} from '../../../json-pack/cbor/CborEncoderDag'; -import {CborDecoderDag} from '../../../json-pack/cbor/CborDecoderDag'; +import {CborEncoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoderDag'; +import {CborDecoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderDag'; import {Cid} from '../../multiformats'; import {writer} from './writer'; import type {IpldCodec} from '../types'; diff --git a/src/web3/codec/codecs/writer.ts b/src/web3/codec/codecs/writer.ts index fd58d8bb9f..195e12d84c 100644 --- a/src/web3/codec/codecs/writer.ts +++ b/src/web3/codec/codecs/writer.ts @@ -1,3 +1,3 @@ -import {Writer} from '../../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; export const writer = new Writer(); diff --git a/src/web3/codec/types.ts b/src/web3/codec/types.ts index c02c6ae304..8c72eaf954 100644 --- a/src/web3/codec/types.ts +++ b/src/web3/codec/types.ts @@ -1,4 +1,4 @@ -import type {BinaryJsonDecoder, BinaryJsonEncoder} from '../../json-pack/types'; +import type {BinaryJsonDecoder, BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; export interface IpldCodec { name: string; diff --git a/src/web3/multiformats/Multihash.ts b/src/web3/multiformats/Multihash.ts index 30afd81bfb..d6ef9af470 100644 --- a/src/web3/multiformats/Multihash.ts +++ b/src/web3/multiformats/Multihash.ts @@ -1,4 +1,4 @@ -import {cmpUint8Array} from '../../util/buffers/cmpUint8Array'; +import {cmpUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array'; import {crypto} from '../crypto'; import * as uvint from '../util/uvint'; import {Multicodec} from './constants'; diff --git a/tsconfig.json b/tsconfig.json index dd0384430b..6da30a9f98 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,7 +39,6 @@ ], "typedocOptions": { "entryPoints": [ - "src/binary-rx/index.ts", "src/json-crdt/index.ts", "src/json-crdt/codec/structural/binary/index.ts", "src/json-crdt/codec/structural/compact/index.ts", diff --git a/yarn.lock b/yarn.lock index 52025ca36a..d3e099929b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -670,6 +670,20 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsonjoy.com/base64@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.1.tgz#a717fd8840f7bad49c7fe66cc65db8bcfc4c4dc5" + integrity sha512-LnFjVChaGY8cZVMwAIMjvA1XwQjZ/zIXHyh28IyJkyNkzof4Dkm1+KN9UIm3lHhREH4vs7XwZ0NpkZKnwOtEfg== + +"@jsonjoy.com/json-pack@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.0.1.tgz#ab324209c99d1b0ccf7a247ce81a45658ec31283" + integrity sha512-a8nau3pCEmoyCR3O3ih5XVxhFhs1+PPphNAbTN1P66TOvPItWQ9NyGE78Rc8YgoyWBPwwyGW1C60sS8/Ji3L8g== + dependencies: + "@jsonjoy.com/base64" "^1.1.1" + hyperdyperid "^1.2.0" + thingies "^1.20.0" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" From b0272d9ec08e521d455d5c4a963a7ea10b95c757 Mon Sep 17 00:00:00 2001 From: streamich Date: Tue, 9 Apr 2024 19:39:45 +0200 Subject: [PATCH 14/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20fix=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/msgpack-documents.ts | 4 ++-- src/json-crdt-patch/__bench__/bench.encoding.ts | 2 +- src/json-crdt/__bench__/bench.codecs.decoding.ts | 4 ++-- src/json-crdt/__bench__/bench.codecs.encoding.ts | 2 +- src/json-crdt/__bench__/bench.json.encoding.ts | 2 +- src/json-crdt/__bench__/util/structural-editors.ts | 4 ++-- src/json-crdt/__tests__/fuzzer/generate-trace.ts | 4 ++-- src/json-size/__bench__/json-size.ts | 2 +- src/json-type/__bench__/encode.ts | 8 ++++---- src/server/__bench__/ping.bench.ts | 6 +++--- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/__tests__/msgpack-documents.ts b/src/__tests__/msgpack-documents.ts index a2f1260905..4cad435e7b 100644 --- a/src/__tests__/msgpack-documents.ts +++ b/src/__tests__/msgpack-documents.ts @@ -1,5 +1,5 @@ -import {JsonPackExtension, JsonPackValue} from '../json-pack/msgpack'; -import {encodeFull} from '../json-pack/msgpack/util'; +import {JsonPackExtension, JsonPackValue} from '@jsonjoy.com/json-pack/lib/msgpack'; +import {encodeFull} from '@jsonjoy.com/json-pack/lib/msgpack/util'; export interface JsonDocument { name: string; diff --git a/src/json-crdt-patch/__bench__/bench.encoding.ts b/src/json-crdt-patch/__bench__/bench.encoding.ts index ca782387c6..23daf72e19 100644 --- a/src/json-crdt-patch/__bench__/bench.encoding.ts +++ b/src/json-crdt-patch/__bench__/bench.encoding.ts @@ -9,7 +9,7 @@ import {PatchBuilder} from '../PatchBuilder'; import {encode as encodeBinary} from '../codec/binary'; import {encode as encodeCompact} from '../codec/compact-binary'; import {encode as encodeJson} from '../codec/verbose'; -import {encode as encodeCbor} from '../../json-pack/cbor/shared'; +import {encode as encodeCbor} from '@jsonjoy.com/json-pack/lib/cbor/shared'; const createPatch = (json: any) => { const clock = new LogicalClock(123456, 0); diff --git a/src/json-crdt/__bench__/bench.codecs.decoding.ts b/src/json-crdt/__bench__/bench.codecs.decoding.ts index d2dd26ac47..8e03d052e0 100644 --- a/src/json-crdt/__bench__/bench.codecs.decoding.ts +++ b/src/json-crdt/__bench__/bench.codecs.decoding.ts @@ -10,8 +10,8 @@ import {Encoder as IndexedEncoder} from '../codec/indexed/binary/Encoder'; import {Decoder as IndexedDecoder} from '../codec/indexed/binary/Decoder'; import {Encoder as SidecarEncoder} from '../codec/sidecar/binary/Encoder'; import {Decoder as SidecarDecoder} from '../codec/sidecar/binary/Decoder'; -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; -import {CborDecoder} from '../../json-pack/cbor/CborDecoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Model} from '../model'; const cborEncoder = new CborEncoder(); diff --git a/src/json-crdt/__bench__/bench.codecs.encoding.ts b/src/json-crdt/__bench__/bench.codecs.encoding.ts index 0770575c3c..18bdc5d7a5 100644 --- a/src/json-crdt/__bench__/bench.codecs.encoding.ts +++ b/src/json-crdt/__bench__/bench.codecs.encoding.ts @@ -6,7 +6,7 @@ import {Encoder as CompactEncoder} from '../codec/structural/compact/Encoder'; import {Encoder as VerboseEncoder} from '../codec/structural/verbose/Encoder'; import {Encoder as IndexedEncoder} from '../codec/indexed/binary/Encoder'; import {Encoder as SidecarEncoder} from '../codec/sidecar/binary/Encoder'; -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {Model} from '../model'; const cborEncoder = new CborEncoder(); diff --git a/src/json-crdt/__bench__/bench.json.encoding.ts b/src/json-crdt/__bench__/bench.json.encoding.ts index 60a654dd36..d9f31decc1 100644 --- a/src/json-crdt/__bench__/bench.json.encoding.ts +++ b/src/json-crdt/__bench__/bench.json.encoding.ts @@ -2,7 +2,7 @@ import {payloads} from '../../__bench__/payloads'; import {IBenchmark, runBenchmarkAndSave} from '../../__bench__/runBenchmark'; -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {Model} from '../model'; const benchmark: IBenchmark = { diff --git a/src/json-crdt/__bench__/util/structural-editors.ts b/src/json-crdt/__bench__/util/structural-editors.ts index deb279a760..cb7c5e0fae 100644 --- a/src/json-crdt/__bench__/util/structural-editors.ts +++ b/src/json-crdt/__bench__/util/structural-editors.ts @@ -11,8 +11,8 @@ import {Encoder as IndexedEncoder} from '../../codec/indexed/binary/Encoder'; import {Decoder as IndexedDecoder} from '../../codec/indexed/binary/Decoder'; import {Encoder as SidecarEncoder} from '../../codec/sidecar/binary/Encoder'; import {Decoder as SidecarDecoder} from '../../codec/sidecar/binary/Decoder'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import {CborDecoder} from '../../../json-pack/cbor/CborDecoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; const cborEncoder = new CborEncoder(); const cborDecoder = new CborDecoder(); diff --git a/src/json-crdt/__tests__/fuzzer/generate-trace.ts b/src/json-crdt/__tests__/fuzzer/generate-trace.ts index 88307e8f54..2d07cedee8 100644 --- a/src/json-crdt/__tests__/fuzzer/generate-trace.ts +++ b/src/json-crdt/__tests__/fuzzer/generate-trace.ts @@ -4,8 +4,8 @@ import {Patch} from '../../../json-crdt-patch'; import {Model} from '../../model'; import {JsonCrdtFuzzer} from './JsonCrdtFuzzer'; -import {CborEncoder} from '../../../json-pack/cbor/CborEncoder'; -import {Writer} from '../../../util/buffers/Writer'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import * as fs from 'fs'; const sessionNum = 100; diff --git a/src/json-size/__bench__/json-size.ts b/src/json-size/__bench__/json-size.ts index 8547c0e6f1..c806dd5b77 100644 --- a/src/json-size/__bench__/json-size.ts +++ b/src/json-size/__bench__/json-size.ts @@ -3,7 +3,7 @@ // npx ts-node src/json-size/__bench__/json-size.ts import * as Benchmark from 'benchmark'; -import {utf8Size} from '../../util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; import {jsonSize, jsonSizeApprox} from '../json'; import {jsonSizeFast} from '../jsonSizeFast'; import {msgpackSizeFast} from '../msgpackSizeFast'; diff --git a/src/json-type/__bench__/encode.ts b/src/json-type/__bench__/encode.ts index 2ff6dca6a5..1b6c2b4945 100644 --- a/src/json-type/__bench__/encode.ts +++ b/src/json-type/__bench__/encode.ts @@ -1,11 +1,11 @@ /* tslint:disable no-console */ import {TypeSystem} from '..'; -import {CborEncoder} from '../../json-pack/cbor/CborEncoder'; -import {JsonEncoder} from '../../json-pack/json/JsonEncoder'; +import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; +import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import {CompiledBinaryEncoder} from '../codegen/types'; -import {EncodingFormat} from '../../json-pack/constants'; -import {Writer} from '../../util/buffers/Writer'; +import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; const system = new TypeSystem(); const {t} = system; diff --git a/src/server/__bench__/ping.bench.ts b/src/server/__bench__/ping.bench.ts index 6a8fce31f8..50ea60a779 100644 --- a/src/server/__bench__/ping.bench.ts +++ b/src/server/__bench__/ping.bench.ts @@ -4,11 +4,11 @@ import {Suite} from 'benchmark'; import {RpcPersistentClient, WebSocketChannel} from '../../reactive-rpc/common'; -import {Writer} from '../../util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; import {BinaryRpcMessageCodec} from '../../reactive-rpc/common/codec/binary'; import {CompactRpcMessageCodec} from '../../reactive-rpc/common/codec/compact'; -import {CborJsonValueCodec} from '../../json-pack/codecs/cbor'; -import {JsonJsonValueCodec} from '../../json-pack/codecs/json'; +import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; +import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; import {RpcCodec} from '../../reactive-rpc/common/codec/RpcCodec'; import {WebSocket} from 'ws'; From a96e1a5a833b003ea6c68072a1f74cdd828af630 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 00:30:46 +0200 Subject: [PATCH 15/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20bump=20json-pack?= =?UTF-8?q?=20and=20cleanup=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 36 +---- yarn.lock | 379 +++------------------------------------------------ 2 files changed, 23 insertions(+), 392 deletions(-) diff --git a/package.json b/package.json index ef4d58c1df..e982712f82 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ }, "dependencies": { "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/json-pack": "^1.0.1", + "@jsonjoy.com/json-pack": "^1.0.2", "arg": "^5.0.2", "hyperdyperid": "^1.2.0", "multibase": "^4.0.6", @@ -132,9 +132,6 @@ "@automerge/automerge": "2.1.7", "@collabs/collabs": "0.13.4", "@exodus/schemasafe": "^1.0.0-rc.6", - "@msgpack/msgpack": "^3.0.0-beta2", - "@redis/client": "^1.5.12", - "@shelacek/ubjson": "^1.1.1", "@types/benchmark": "^2.1.2", "@types/jest": "^29.5.12", "@types/quill": "^2.0.14", @@ -143,52 +140,28 @@ "ajv": "^8.11.0", "app-root-path": "^3.1.0", "axios": "^1.3.5", - "base64-js": "^1.5.1", "benchmark": "^2.1.4", - "bson": "^5.4.0", - "cbor": "^8.1.0", - "cbor-js": "^0.1.0", - "cbor-sync": "^1.0.4", - "cbor-x": "^1.3.0", - "cborg": "^2.0.3", "concurrently": "^8.0.1", "diamond-types-node": "1.0.2", "editing-traces": "https://github.com/streamich/editing-traces#6494020428530a6e382378b98d1d7e31334e2d7b", "eventsource": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "fast-equals": "^5.0.1", "fast-json-patch": "^3.0.0-1", - "fast-safe-stringify": "^2.1.1", - "fast-stable-stringify": "^1.0.0", - "fastest-stable-stringify": "^2.0.2", "find-my-way": "^7.6.0", "fork-ts-checker-webpack-plugin": "^8.0.0", "gh-pages": "^5.0.0", "html-webpack-plugin": "^5.5.1", "husky": "^8.0.3", - "ion-js": "^4.3.0", "isomorphic-ws": "^5.0.0", "jest": "^29.7.0", - "js-base64": "^3.7.2", "js-sdsl": "^4.4.0", - "jsbi": "^4.3.0", "json-crdt-traces": "https://github.com/streamich/json-crdt-traces#ec825401dc05cbb74b9e0b3c4d6527399f54d54d", "json-logic-js": "^2.0.1", - "json-pack-napi": "^0.0.2", - "load-script": "^2.0.0", - "lodash": "^4.17.21", "loro-crdt": "^0.4.1", - "markdown-it": "^13.0.1", "memfs": "^4.8.1", - "messagepack": "^1.1.12", - "msgpack-lite": "^0.1.26", - "msgpack5": "^6.0.2", - "msgpackr": "^1.6.0", "nodemon": "^3.0.0", "ot-text": "^1.0.2", "ot-text-unicode": "^4.0.0", "p4-css": "^1.5.1", - "pako": "^2.0.4", "prettier": "^3.2.5", "pretty-quick": "^3.1.3", "quill-delta": "^5.0.0", @@ -198,8 +171,6 @@ "rimraf": "^5.0.0", "rope.js": "0.1.0", "rxjs": "^7.5.5", - "safe-stable-stringify": "^2.3.1", - "secure-json-parse": "^2.4.0", "sorted-btree": "^1.8.1", "tinybench": "^2.4.0", "ts-jest": "^29.1.2", @@ -254,24 +225,19 @@ "scopes": [ "", "demo", - "json-binary", - "json-brand", "json-cli", "json-clone", "json-crdt-patch", "json-crdt-extensions", "json-crdt-peritext-ui", "json-crdt", - "json-equal", "json-expression", "json-hash", "json-ot", - "json-pack", "json-patch-multicore", "json-patch-ot", "json-patch", "json-pointer", - "json-random", "json-schema", "json-size", "json-stable", diff --git a/yarn.lock b/yarn.lock index d3e099929b..1901ae0f7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -317,36 +317,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cbor-extract/cbor-extract-darwin-arm64@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.1.1.tgz#5721f6dd3feae0b96d23122853ce977e0671b7a6" - integrity sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA== - -"@cbor-extract/cbor-extract-darwin-x64@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.1.1.tgz#c25e7d0133950d87d101d7b3afafea8d50d83f5f" - integrity sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw== - -"@cbor-extract/cbor-extract-linux-arm64@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.1.1.tgz#48f78e7d8f0fcc84ed074b6bfa6d15dd83187c63" - integrity sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ== - -"@cbor-extract/cbor-extract-linux-arm@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.1.1.tgz#7507d346389cb682e44fab8fae9534edd52e2e41" - integrity sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ== - -"@cbor-extract/cbor-extract-linux-x64@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.1.1.tgz#b7c1d2be61c58ec18d58afbad52411ded63cd4cd" - integrity sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA== - -"@cbor-extract/cbor-extract-win32-x64@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.1.1.tgz#21b11a1a3f18c3e7d62fd5f87438b7ed2c64c1f7" - integrity sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw== - "@collabs/collabs@0.13.4": version "0.13.4" resolved "https://registry.yarnpkg.com/@collabs/collabs/-/collabs-0.13.4.tgz#74bfa91c7e9e4c3e720437d75b0bee4270c997df" @@ -675,55 +645,28 @@ resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.1.tgz#a717fd8840f7bad49c7fe66cc65db8bcfc4c4dc5" integrity sha512-LnFjVChaGY8cZVMwAIMjvA1XwQjZ/zIXHyh28IyJkyNkzof4Dkm1+KN9UIm3lHhREH4vs7XwZ0NpkZKnwOtEfg== -"@jsonjoy.com/json-pack@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.0.1.tgz#ab324209c99d1b0ccf7a247ce81a45658ec31283" - integrity sha512-a8nau3pCEmoyCR3O3ih5XVxhFhs1+PPphNAbTN1P66TOvPItWQ9NyGE78Rc8YgoyWBPwwyGW1C60sS8/Ji3L8g== +"@jsonjoy.com/json-pack@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.0.2.tgz#d7c8c284db828b29eebb9082134251a8216ec5cc" + integrity sha512-4KMApTgb1Hvjz9Ue7unziJ1xNy3k6d2erp0hz1iXryXsf6LEM3KwN6YrfbqT0vqkUO8Tu+CSnvMia9cWX6YGVw== dependencies: "@jsonjoy.com/base64" "^1.1.1" + "@jsonjoy.com/util" "^1.0.0" hyperdyperid "^1.2.0" thingies "^1.20.0" +"@jsonjoy.com/util@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.0.0.tgz#417a4e6c112501ac98e3a4e695f5ffa23ba01b2d" + integrity sha512-uTa/IyBC3/xTC5/qwMnY79ZqCA6ENQgsM6nzuAISa7pYiiUn+U4JIXoyaHEApX6k8KvTCBDNJs+k8U03bw5ffQ== + dependencies: + hyperdyperid "^1.2.0" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@msgpack/msgpack@^3.0.0-beta2": - version "3.0.0-beta2" - resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz#5bccee30f84df220b33905e3d8249ba96deca0b7" - integrity sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw== - -"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" - integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== - -"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" - integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== - -"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" - integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== - -"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" - integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== - -"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" - integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== - -"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" - integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== - "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -787,20 +730,6 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@redis/client@^1.5.12": - version "1.5.12" - resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.12.tgz#4c387727992152aea443b869de0ebb697f899187" - integrity sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A== - dependencies: - cluster-key-slot "1.1.2" - generic-pool "3.9.0" - yallist "4.0.0" - -"@shelacek/ubjson@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@shelacek/ubjson/-/ubjson-1.1.1.tgz#949d9e6fb5cf2ada4197de6f8430fa7894d61ed4" - integrity sha512-2Vi46ct3IWzh8eAz9IZ0NulIRtJbGgJDppatJvufB0fHwasRSfklBoQrVrCutF24fSuRvTvZVebjtcIHOxvzWw== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1469,11 +1398,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" @@ -1590,7 +1514,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1613,22 +1537,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bl@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" - integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== - dependencies: - buffer "^6.0.3" - inherits "^2.0.4" - readable-stream "^3.4.0" - body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -1716,24 +1624,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bson@^5.4.0: - version "5.5.1" - resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.1.tgz#f5849d405711a7f23acdda9a442375df858e6833" - integrity sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - bufferutil@^4.0.1: version "4.0.8" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" @@ -1805,49 +1700,6 @@ caniuse-lite@^1.0.30001587: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz#b91e8e033f6bca4e13d3d45388d87fa88931d9a5" integrity sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w== -cbor-extract@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.1.1.tgz#f154b31529fdb6b7c70fb3ca448f44eda96a1b42" - integrity sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA== - dependencies: - node-gyp-build-optional-packages "5.0.3" - optionalDependencies: - "@cbor-extract/cbor-extract-darwin-arm64" "2.1.1" - "@cbor-extract/cbor-extract-darwin-x64" "2.1.1" - "@cbor-extract/cbor-extract-linux-arm" "2.1.1" - "@cbor-extract/cbor-extract-linux-arm64" "2.1.1" - "@cbor-extract/cbor-extract-linux-x64" "2.1.1" - "@cbor-extract/cbor-extract-win32-x64" "2.1.1" - -cbor-js@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cbor-js/-/cbor-js-0.1.0.tgz#c80ce6120f387e8faa74370dfda21d965b8fc7f9" - integrity sha512-7sQ/TvDZPl7csT1Sif9G0+MA0I0JOVah8+wWlJVQdVEgIbCzlN/ab3x+uvMNsc34TUvO6osQTAmB2ls80JX6tw== - -cbor-sync@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cbor-sync/-/cbor-sync-1.0.4.tgz#5a11a1ab75c2a14d1af1b237fd84aa8c1593662f" - integrity sha512-GWlXN4wiz0vdWWXBU71Dvc1q3aBo0HytqwAZnXF1wOwjqNnDWA1vZ1gDMFLlqohak31VQzmhiYfiCX5QSSfagA== - -cbor-x@^1.3.0: - version "1.5.6" - resolved "https://registry.yarnpkg.com/cbor-x/-/cbor-x-1.5.6.tgz#cbc5a8267bcd89a559d32339fe7ec442bc3b3862" - integrity sha512-+TXdnDNdr8JH5GQRoAhjdT/5s5N+b71s2Nz8DpDRyuWx0uzMj8JTR3AqqMTBO/1HtUBHZpmK1enD2ViXFx0Nug== - optionalDependencies: - cbor-extract "^2.1.1" - -cbor@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" - integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== - dependencies: - nofilter "^3.1.0" - -cborg@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cborg/-/cborg-2.0.5.tgz#b5393c8b1843d5c1a61f2b79b4c9f752052a4d44" - integrity sha512-xVW1rSIw1ZXbkwl2XhJ7o/jAv0vnVoQv/QlfQxV8a7V5PlA4UU/AcIiXqmpyybwNWy/GPQU1m/aBVNIWr7/T0w== - chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1948,11 +1800,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -cluster-key-slot@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" - integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2424,11 +2271,6 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - envinfo@^7.7.3: version "7.11.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" @@ -2534,11 +2376,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -event-lite@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.3.tgz#3dfe01144e808ac46448f0c19b4ab68e403a901d" - integrity sha512-8qz9nOz5VeD2z96elrEKD2U433+L3DWdUdDkOINLGOJvx1GsMBbMn0aCeu28y8/e85A6mCigBiFlYMnTBEGlSw== - eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -2659,11 +2496,6 @@ fast-diff@^1.3.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-equals@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d" - integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== - fast-json-patch@^3.0.0-1: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947" @@ -2686,16 +2518,6 @@ fast-querystring@^1.0.0: dependencies: fast-decode-uri-component "^1.0.1" -fast-safe-stringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - -fast-stable-stringify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" - integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== - fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -2720,11 +2542,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" @@ -2878,11 +2695,6 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -generic-pool@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" - integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3177,11 +2989,6 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.8, ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -3221,7 +3028,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3239,21 +3046,11 @@ inline-style-prefixer@^6.0.0: css-in-js-utils "^3.1.0" fast-loops "^1.1.3" -int64-buffer@^0.1.9: - version "0.1.10" - resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423" - integrity sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA== - interpret@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -ion-js@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ion-js/-/ion-js-4.3.0.tgz#ea511b94a3a555ac6e2e58832c7b66c41c302a0f" - integrity sha512-8yhU1yx6Uia109cMbiDQDQS0InGJj8BaXckjyaN+5vCzL0e8qEXzLVRy579TmNThBDq8IfUJfqe0565Syh+0JA== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -3356,7 +3153,7 @@ is-wsl@^3.1.0: dependencies: is-inside-container "^1.0.0" -isarray@^1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== @@ -3810,11 +3607,6 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -js-base64@^3.7.2: - version "3.7.5" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" - integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA== - js-sdsl@^4.4.0: version "4.4.2" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847" @@ -3833,11 +3625,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsbi@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" - integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -3852,14 +3639,6 @@ json-logic-js@^2.0.1: resolved "https://registry.yarnpkg.com/json-logic-js/-/json-logic-js-2.0.2.tgz#b613e095f5e598cb78f7b9a2bbf638e74cf98158" integrity sha512-ZBtBdMJieqQcH7IX/LaBsr5pX+Y5JIW+EhejtM3Ffg2jdN9Iwf+Ht6TbHnvAZ/YtwyuhPaCBlnvzrwVeWdvGDQ== -json-pack-napi@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/json-pack-napi/-/json-pack-napi-0.0.2.tgz#57a3535aab2c851725612fe1b81c4173cf10d6da" - integrity sha512-NOkouHWWiZhCcmnE/w6MkV8JNhhW8kln6VvWcq0egX++XLayHpZtcGkfm5d+RxVuSQuTjIkwHyez49yLJgzs+Q== - dependencies: - bindings "^1.5.0" - node-addon-api "^6.1.0" - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -3936,18 +3715,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -linkify-it@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" - integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== - dependencies: - uc.micro "^1.0.1" - -load-script@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-script/-/load-script-2.0.0.tgz#40821aaa59e9bbe7be2e28b6ab053e6f44330fa1" - integrity sha512-km6cyoPW4rM22JMGb+SHUKPMZVDpUaMpMAKrv8UHWllIxc/qjgMGHD91nY+5hM+/NFs310OZ2pqQeJKs7HqWPA== - loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -4061,17 +3828,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -markdown-it@^13.0.1: - version "13.0.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" - integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== - dependencies: - argparse "^2.0.1" - entities "~3.0.1" - linkify-it "^4.0.1" - mdurl "^1.0.1" - uc.micro "^1.0.5" - marked@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" @@ -4082,11 +3838,6 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4116,11 +3867,6 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -messagepack@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/messagepack/-/messagepack-1.1.12.tgz#64284d4c8f325582a8b0dafbdb2e550f01d9dcf6" - integrity sha512-pNB6K4q4VMLRXdvlGZkTtQhmKFntvLisnOQnL0VhKpZooL8B8Wsv5TXuidIJil0bCH6V172p3+Onfyow0usPYQ== - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -4212,47 +3958,6 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msgpack-lite@^0.1.26: - version "0.1.26" - resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89" - integrity sha512-SZ2IxeqZ1oRFGo0xFGbvBJWMp3yLIY9rlIJyxy8CGrwZn1f0ZK4r6jV/AM1r0FZMDUkWkglOk/eeKIL9g77Nxw== - dependencies: - event-lite "^0.1.1" - ieee754 "^1.1.8" - int64-buffer "^0.1.9" - isarray "^1.0.0" - -msgpack5@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/msgpack5/-/msgpack5-6.0.2.tgz#46d2a9fdbe9c1b71ae7ae43f656fe266115d5e7e" - integrity sha512-kBSpECAWslrciRF3jy6HkMckNa14j3VZwNUUe1ONO/yihs19MskiFnsWXm0Q0aPkDYDBRFvTKkEuEDY+HVxBvQ== - dependencies: - bl "^5.0.0" - inherits "^2.0.3" - readable-stream "^3.0.0" - safe-buffer "^5.1.2" - -msgpackr-extract@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" - integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== - dependencies: - node-gyp-build-optional-packages "5.0.7" - optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" - -msgpackr@^1.6.0: - version "1.9.9" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.9.9.tgz#ec71e37beb8729280847f683cb0a340eb35ce70f" - integrity sha512-sbn6mioS2w0lq1O6PpGtsv6Gy8roWM+o3o4Sqjd6DudrL/nOugY+KyJUimoWzHnf9OkO0T6broHFnYE/R05t9A== - optionalDependencies: - msgpackr-extract "^3.0.2" - multibase@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" @@ -4326,26 +4031,11 @@ node-abort-controller@^3.0.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== -node-addon-api@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" - integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== - node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build-optional-packages@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" - integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== - -node-gyp-build-optional-packages@5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" - integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== - node-gyp-build@^4.3.0: version "4.7.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.1.tgz#cd7d2eb48e594874053150a9418ac85af83ca8f7" @@ -4382,11 +4072,6 @@ nodemon@^3.0.0: touch "^3.1.0" undefsafe "^2.0.5" -nofilter@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" - integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== - nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -4518,11 +4203,6 @@ p4-css@^1.5.1: dependencies: nano-css "^5.3.0" -pako@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" - integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== - param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -4821,7 +4501,7 @@ readable-stream@^2.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.0, readable-stream@^3.0.6, readable-stream@^3.4.0: +readable-stream@^3.0.6: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -4969,7 +4649,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4981,11 +4661,6 @@ safe-regex2@^2.0.0: dependencies: ret "~0.2.0" -safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -5017,11 +4692,6 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -secure-json-parse@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" - integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -5689,11 +5359,6 @@ uWebSockets.js@uNetworking/uWebSockets.js#v20.23.0: version "20.23.0" resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/49f8f1eb54435e4c3e3e436571e93d1f06aaabbb" -uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" @@ -6017,16 +5682,16 @@ yaeti@^0.0.6: resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== -yallist@4.0.0, yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" From 963046770f2a6ff5c655606522eff87e37225a50 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 00:50:19 +0200 Subject: [PATCH 16/42] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20new=20pack?= =?UTF-8?q?age=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/index.ts | 2 +- src/json-brand/index.ts | 1 - src/json-clone/cloneBinary.ts | 2 +- src/json-crdt-patch/PatchBuilder.ts | 2 +- src/json-crdt-patch/util/binary/CrdtReader.ts | 2 +- src/json-crdt-patch/util/binary/CrdtWriter.ts | 2 +- src/json-crdt/codec/sidecar/binary/Decoder.ts | 2 +- src/json-crdt/codec/sidecar/binary/Encoder.ts | 2 +- src/json-crdt/log/codec/logEncoderOpts.ts | 2 +- src/json-crdt/nodes/rga/AbstractRga.ts | 4 +- src/json-equal/$$deepEqual/v1.ts | 2 +- src/json-expression/codegen.ts | 4 +- src/json-expression/types.ts | 2 +- src/json-hash/index.ts | 2 +- src/json-patch/applyPatch/v3.ts | 2 +- src/json-patch/codec/binary/Decoder.ts | 2 +- src/json-patch/codegen/apply.ts | 2 +- src/json-patch/codegen/ops/add.ts | 2 +- src/json-patch/codegen/ops/starts.ts | 2 +- src/json-patch/codegen/ops/test.ts | 2 +- src/json-patch/validate.ts | 2 +- src/json-pointer/codegen/find.ts | 2 +- src/json-pointer/codegen/findRef.ts | 4 +- src/json-pointer/find.ts | 2 +- src/json-pointer/findByPointer/v1.ts | 2 +- src/json-pointer/findByPointer/v2.ts | 2 +- src/json-pointer/findByPointer/v3.ts | 2 +- src/json-pointer/findByPointer/v4.ts | 2 +- src/json-pointer/findByPointer/v5.ts | 2 +- src/json-pointer/findByPointer/v6.ts | 2 +- src/json-pointer/get.ts | 2 +- src/json-size/json.ts | 2 +- src/json-size/msgpackSizeFast.ts | 2 +- src/json-stable/index.ts | 6 +- src/json-type-cli/Cli.ts | 2 +- src/json-type-cli/codecs/cbor.ts | 2 +- src/json-type-cli/codecs/json.ts | 2 +- src/json-type-cli/codecs/json2.ts | 4 +- src/json-type-cli/codecs/json4.ts | 4 +- src/json-type-cli/codecs/msgpack.ts | 2 +- src/json-type-cli/codecs/ubjson.ts | 2 +- src/json-type-cli/defaultCodecs.ts | 2 +- src/json-type-cli/params/CliParamCmd.ts | 4 +- .../binary/BinaryEncoderCodegenContext.ts | 4 +- .../CapacityEstimatorCodegenContext.ts | 2 +- .../json/JsonTextEncoderCodegenContext.ts | 6 +- .../validator/ValidatorCodegenContext.ts | 2 +- src/json-type/schema/schema.ts | 2 +- src/json-type/type/classes/AbstractType.ts | 8 +-- src/json-type/type/classes/AnyType.ts | 2 +- src/json-type/type/classes/ArrayType.ts | 4 +- src/json-type/type/classes/BinaryType.ts | 4 +- src/json-type/type/classes/BooleanType.ts | 4 +- src/json-type/type/classes/ConstType.ts | 4 +- src/json-type/type/classes/MapType.ts | 6 +- src/json-type/type/classes/NumberType.ts | 4 +- src/json-type/type/classes/ObjectType.ts | 8 +-- src/json-type/type/classes/OrType.ts | 4 +- src/json-type/type/classes/RefType.ts | 4 +- src/json-type/type/classes/StringType.ts | 6 +- src/json-type/type/classes/TupleType.ts | 4 +- src/json-type/typescript/toText.ts | 2 +- .../browser/createBinaryWsRpcClient.ts | 2 +- .../browser/createJsonWsRpcClient.ts | 2 +- src/reactive-rpc/common/channel/channel.ts | 2 +- src/reactive-rpc/common/channel/mock.ts | 2 +- .../codec/binary/BinaryRpcMessageCodec.ts | 2 +- .../common/codec/binary/decode.ts | 4 +- .../common/testing/buildE2eClient.ts | 4 +- src/reactive-rpc/server/context.ts | 6 +- src/reactive-rpc/server/http1/Http1Server.ts | 4 +- src/reactive-rpc/server/http1/context.ts | 2 +- src/reactive-rpc/server/uws/RpcApp.ts | 4 +- .../server/ws/codec/WsFrameDecoder.ts | 2 +- .../server/ws/codec/WsFrameEncoder.ts | 4 +- .../server/ws/server/WsServerConnection.ts | 4 +- src/util/Fuzzer.ts | 66 ------------------- src/util/NullObject.ts | 2 - src/util/buffers/index.ts | 1 - src/util/codegen/index.ts | 1 - src/util/hasOwnProperty.ts | 5 -- src/util/isEmpty.ts | 4 -- src/util/lazyFunction.ts | 8 --- src/util/router/codegen.ts | 4 +- src/util/router/router.ts | 2 +- src/util/router/tree.ts | 2 +- src/util/sort/insertion.ts | 1 - src/util/sort/insertion2.ts | 1 - src/util/types.ts | 9 --- src/web3/adl/hamt-crdt/Hamt.ts | 2 +- src/web3/adl/hamt-crdt/HamtFrame.ts | 2 +- src/web3/codec/codecs/writer.ts | 2 +- src/web3/multiformats/Multihash.ts | 2 +- 94 files changed, 121 insertions(+), 219 deletions(-) delete mode 100644 src/json-brand/index.ts delete mode 100644 src/util/Fuzzer.ts delete mode 100644 src/util/NullObject.ts delete mode 100644 src/util/buffers/index.ts delete mode 100644 src/util/codegen/index.ts delete mode 100644 src/util/hasOwnProperty.ts delete mode 100644 src/util/isEmpty.ts delete mode 100644 src/util/lazyFunction.ts delete mode 100644 src/util/sort/insertion.ts delete mode 100644 src/util/sort/insertion2.ts delete mode 100644 src/util/types.ts diff --git a/package.json b/package.json index e982712f82..3c5421ec5e 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "dependencies": { "@jsonjoy.com/base64": "^1.1.1", "@jsonjoy.com/json-pack": "^1.0.2", + "@jsonjoy.com/util": "^1.0.0", "arg": "^5.0.2", "hyperdyperid": "^1.2.0", "multibase": "^4.0.6", diff --git a/src/index.ts b/src/index.ts index ccefde3bc6..6d549e2889 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ */ export type * from '@jsonjoy.com/json-pack/lib/json-binary/types'; -export type * from '@jsonjoy.com/json-pack/lib/json-brand/types'; +export type * from '@jsonjoy.com/util/lib/json-brand/types'; export type * from './json-crdt'; export type * from './json-crdt-patch'; export type * from './json-crdt-extensions'; diff --git a/src/json-brand/index.ts b/src/json-brand/index.ts deleted file mode 100644 index 58890235f8..0000000000 --- a/src/json-brand/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@jsonjoy.com/json-pack/lib/json-brand'; diff --git a/src/json-clone/cloneBinary.ts b/src/json-clone/cloneBinary.ts index fc5b110ebd..4d5725902c 100644 --- a/src/json-clone/cloneBinary.ts +++ b/src/json-clone/cloneBinary.ts @@ -1,4 +1,4 @@ -import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/util/lib/buffers/isUint8Array'; const {isArray} = Array; const objectKeys = Object.keys; diff --git a/src/json-crdt-patch/PatchBuilder.ts b/src/json-crdt-patch/PatchBuilder.ts index 2116af5d00..d05ed079e8 100644 --- a/src/json-crdt-patch/PatchBuilder.ts +++ b/src/json-crdt-patch/PatchBuilder.ts @@ -16,7 +16,7 @@ import { NopOp, } from './operations'; import {IClock, ITimestampStruct, ITimespanStruct, ts, Timestamp} from './clock'; -import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/util/lib/buffers/isUint8Array'; import {Patch} from './Patch'; import {ORIGIN} from './constants'; import {VectorDelayedValue} from './builder/Tuple'; diff --git a/src/json-crdt-patch/util/binary/CrdtReader.ts b/src/json-crdt-patch/util/binary/CrdtReader.ts index 242a75e36c..8a7622e00d 100644 --- a/src/json-crdt-patch/util/binary/CrdtReader.ts +++ b/src/json-crdt-patch/util/binary/CrdtReader.ts @@ -1,4 +1,4 @@ -import {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; +import {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; /** @todo Rename file name. */ export class CrdtReader extends Reader { diff --git a/src/json-crdt-patch/util/binary/CrdtWriter.ts b/src/json-crdt-patch/util/binary/CrdtWriter.ts index 5c71546451..e835be947e 100644 --- a/src/json-crdt-patch/util/binary/CrdtWriter.ts +++ b/src/json-crdt-patch/util/binary/CrdtWriter.ts @@ -1,4 +1,4 @@ -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; export class CrdtWriter extends Writer { /** diff --git a/src/json-crdt/codec/sidecar/binary/Decoder.ts b/src/json-crdt/codec/sidecar/binary/Decoder.ts index a7700e31cf..157b91021d 100644 --- a/src/json-crdt/codec/sidecar/binary/Decoder.ts +++ b/src/json-crdt/codec/sidecar/binary/Decoder.ts @@ -5,7 +5,7 @@ import {Model, UNDEFINED} from '../../../model/Model'; import {CborDecoderBase} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderBase'; import * as nodes from '../../../nodes'; import {CRDT_MAJOR} from '../../structural/binary/constants'; -import {sort} from '../../../../util/sort/insertion'; +import {sort} from '@jsonjoy.com/util/lib/sort/insertion'; import {SESSION} from '../../../../json-crdt-patch/constants'; export class Decoder { diff --git a/src/json-crdt/codec/sidecar/binary/Encoder.ts b/src/json-crdt/codec/sidecar/binary/Encoder.ts index 3dd3afdb84..fed981e30d 100644 --- a/src/json-crdt/codec/sidecar/binary/Encoder.ts +++ b/src/json-crdt/codec/sidecar/binary/Encoder.ts @@ -5,7 +5,7 @@ import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {CRDT_MAJOR_OVERLAY} from '../../structural/binary/constants'; -import {sort} from '@jsonjoy.com/json-pack/lib/util/sort/insertion'; +import {sort} from '@jsonjoy.com/util/lib/sort/insertion'; import {UNDEFINED} from '../../../model/Model'; import type {Model} from '../../../model'; diff --git a/src/json-crdt/log/codec/logEncoderOpts.ts b/src/json-crdt/log/codec/logEncoderOpts.ts index b826ed468f..f485163abd 100644 --- a/src/json-crdt/log/codec/logEncoderOpts.ts +++ b/src/json-crdt/log/codec/logEncoderOpts.ts @@ -1,4 +1,4 @@ -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Encoder as SidecarEncoder} from '../../codec/sidecar/binary/Encoder'; import {Encoder as StructuralEncoderCompact} from '../../codec/structural/compact/Encoder'; import {Encoder as StructuralEncoderVerbose} from '../../codec/structural/verbose/Encoder'; diff --git a/src/json-crdt/nodes/rga/AbstractRga.ts b/src/json-crdt/nodes/rga/AbstractRga.ts index 443cb33610..b8eb36e8df 100644 --- a/src/json-crdt/nodes/rga/AbstractRga.ts +++ b/src/json-crdt/nodes/rga/AbstractRga.ts @@ -8,14 +8,14 @@ import { containsId, Timestamp, } from '../../../json-crdt-patch/clock'; -import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/util/lib/buffers/isUint8Array'; import {rSplay, lSplay, llSplay, rrSplay, lrSplay, rlSplay} from '../../../util/trees/splay/util'; import {splay2} from '../../../util/trees/splay/util2'; import {insert2, remove2} from '../../../util/trees/util2'; import {ORIGIN} from '../../../json-crdt-patch/constants'; import {printTree} from '../../../util/print/printTree'; import {printBinary} from '../../../util/print/printBinary'; -import {printOctets} from '@jsonjoy.com/json-pack/lib/util/buffers/printOctets'; +import {printOctets} from '@jsonjoy.com/util/lib/buffers/printOctets'; /** * @category CRDT Node diff --git a/src/json-equal/$$deepEqual/v1.ts b/src/json-equal/$$deepEqual/v1.ts index a240f9171c..c775924f41 100644 --- a/src/json-equal/$$deepEqual/v1.ts +++ b/src/json-equal/$$deepEqual/v1.ts @@ -1,4 +1,4 @@ -import {JavaScript} from '@jsonjoy.com/json-pack/lib/util/codegen'; +import {JavaScript} from '@jsonjoy.com/util/lib/codegen'; const codegenValue = (doc: unknown, code: string[], r: number): number => { let rr = r; diff --git a/src/json-expression/codegen.ts b/src/json-expression/codegen.ts index b4427251ea..8629421049 100644 --- a/src/json-expression/codegen.ts +++ b/src/json-expression/codegen.ts @@ -1,8 +1,8 @@ import * as util from './util'; -import {Codegen} from '@jsonjoy.com/json-pack/lib/util/codegen/Codegen'; +import {Codegen} from '@jsonjoy.com/util/lib/codegen/Codegen'; import {ExpressionResult, Literal} from './codegen-steps'; import {createEvaluate} from './createEvaluate'; -import {JavaScript} from '@jsonjoy.com/json-pack/lib/util/codegen'; +import {JavaScript} from '@jsonjoy.com/util/lib/codegen'; import {Vars} from './Vars'; import type * as types from './types'; diff --git a/src/json-expression/types.ts b/src/json-expression/types.ts index 984dd0278f..1bc1b2bfca 100644 --- a/src/json-expression/types.ts +++ b/src/json-expression/types.ts @@ -1,4 +1,4 @@ -import type {JavaScript} from '../util/codegen'; +import type {JavaScript} from '@jsonjoy.com/util/lib/codegen'; import type {Vars} from './Vars'; import type {ExpressionResult} from './codegen-steps'; diff --git a/src/json-hash/index.ts b/src/json-hash/index.ts index 80dcc9c879..d9657f0f19 100644 --- a/src/json-hash/index.ts +++ b/src/json-hash/index.ts @@ -1,5 +1,5 @@ import type {JsonValue} from '@jsonjoy.com/json-pack/lib/types'; -import {sort} from '../util/sort/insertion'; +import {sort} from '@jsonjoy.com/util/lib/sort/insertion'; export const enum CONST { START_STATE = 5381, diff --git a/src/json-patch/applyPatch/v3.ts b/src/json-patch/applyPatch/v3.ts index 4401f8c9fd..10b312394a 100644 --- a/src/json-patch/applyPatch/v3.ts +++ b/src/json-patch/applyPatch/v3.ts @@ -3,7 +3,7 @@ import {Operation} from '../types'; import {findByPointer, unescapeComponent} from '../../json-pointer'; import {deepEqual} from '../../json-equal/deepEqual'; import type {ApplyPatchOptions, OpResult, PatchResult} from './types'; -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; const {isArray} = Array; diff --git a/src/json-patch/codec/binary/Decoder.ts b/src/json-patch/codec/binary/Decoder.ts index 8f747dc0bc..d10a9e8c41 100644 --- a/src/json-patch/codec/binary/Decoder.ts +++ b/src/json-patch/codec/binary/Decoder.ts @@ -37,7 +37,7 @@ import {Path} from '../../../json-pointer'; import {JsonPatchTypes} from '../json/types'; import {createMatcherDefault} from '../../util'; import type {JsonPatchOptions} from '../../types'; -import type {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; +import type {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; export class Decoder extends MsgPackDecoderFast { constructor(private readonly options: JsonPatchOptions) { diff --git a/src/json-patch/codegen/apply.ts b/src/json-patch/codegen/apply.ts index 0ed7c0b26b..5c32aa7c9c 100644 --- a/src/json-patch/codegen/apply.ts +++ b/src/json-patch/codegen/apply.ts @@ -5,7 +5,7 @@ import {AbstractPredicateOp} from '../op'; import {ApplyPatchOptions} from '../applyPatch/types'; import type {JsonPatchOptions} from '..'; import type {ApplyFn} from './types'; -import {compile, JavaScriptLinked, JavaScript} from '../../util/codegen'; +import {compile, JavaScriptLinked, JavaScript} from '@jsonjoy.com/util/lib/codegen'; import {codegenOp} from './codegenOp'; export const apply = (patch: readonly Operation[], applyOptions: ApplyPatchOptions, doc: unknown): unknown => { diff --git a/src/json-patch/codegen/ops/add.ts b/src/json-patch/codegen/ops/add.ts index f4b1c5d866..c417e7ea7a 100644 --- a/src/json-patch/codegen/ops/add.ts +++ b/src/json-patch/codegen/ops/add.ts @@ -1,5 +1,5 @@ import {OpAdd} from '../../op'; -import {JavaScriptLinked, compileClosure, JavaScript} from '../../../util/codegen'; +import {JavaScriptLinked, compileClosure, JavaScript} from '@jsonjoy.com/util/lib/codegen'; import type {ApplyFn} from '../types'; import {$findRef} from '../../../json-pointer/codegen/findRef'; diff --git a/src/json-patch/codegen/ops/starts.ts b/src/json-patch/codegen/ops/starts.ts index a5d7b7d4e1..992b2665c5 100644 --- a/src/json-patch/codegen/ops/starts.ts +++ b/src/json-patch/codegen/ops/starts.ts @@ -1,6 +1,6 @@ import {OpStarts} from '../../op'; import {$$find} from '../../../json-pointer/codegen/find'; -import {JavaScriptLinked, compileClosure, JavaScript} from '../../../util/codegen'; +import {JavaScriptLinked, compileClosure, JavaScript} from '@jsonjoy.com/util/lib/codegen'; import {predicateOpWrapper} from '../util'; import type {ApplyFn} from '../types'; diff --git a/src/json-patch/codegen/ops/test.ts b/src/json-patch/codegen/ops/test.ts index 5ab5d49f0e..e7db89eafc 100644 --- a/src/json-patch/codegen/ops/test.ts +++ b/src/json-patch/codegen/ops/test.ts @@ -1,7 +1,7 @@ import {OpTest} from '../../op'; import {$$find} from '../../../json-pointer/codegen/find'; import {$$deepEqual} from '../../../json-equal/$$deepEqual'; -import {JavaScriptLinked, compileClosure, JavaScript} from '../../../util/codegen'; +import {JavaScriptLinked, compileClosure, JavaScript} from '@jsonjoy.com/util/lib/codegen'; import {predicateOpWrapper} from '../util'; import type {ApplyFn} from '../types'; diff --git a/src/json-patch/validate.ts b/src/json-patch/validate.ts index a091fc8064..f209344ff4 100644 --- a/src/json-patch/validate.ts +++ b/src/json-patch/validate.ts @@ -23,7 +23,7 @@ import { JsonPatchTypes, } from './types'; import {validateJsonPointer} from '../json-pointer/validate'; -import {hasOwnProperty} from '../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; export const validateOperations = (ops: Operation[], allowMatchesOp: boolean = false) => { if (!Array.isArray(ops)) throw new Error('Not a array.'); diff --git a/src/json-pointer/codegen/find.ts b/src/json-pointer/codegen/find.ts index 6ab93e30fd..9898ba2866 100644 --- a/src/json-pointer/codegen/find.ts +++ b/src/json-pointer/codegen/find.ts @@ -1,4 +1,4 @@ -import {JavaScript} from '../../util/codegen'; +import {JavaScript} from '@jsonjoy.com/util/lib/codegen'; import {Path} from '../types'; export const $$find = (path: Path): JavaScript<(doc: unknown) => unknown> => { diff --git a/src/json-pointer/codegen/findRef.ts b/src/json-pointer/codegen/findRef.ts index e09285009d..a5c9162cba 100644 --- a/src/json-pointer/codegen/findRef.ts +++ b/src/json-pointer/codegen/findRef.ts @@ -1,7 +1,7 @@ import type {Reference} from '../find'; import type {Path} from '../types'; -import {JavaScriptLinked, compileClosure} from '../../util/codegen'; -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {JavaScriptLinked, compileClosure} from '@jsonjoy.com/util/lib/codegen'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; type Fn = (val: unknown) => Reference; diff --git a/src/json-pointer/find.ts b/src/json-pointer/find.ts index a030b41920..b76b1758b6 100644 --- a/src/json-pointer/find.ts +++ b/src/json-pointer/find.ts @@ -1,6 +1,6 @@ /* tslint:disable no-string-throw */ -import {hasOwnProperty} from '../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import type {Path} from './types'; export interface Reference { diff --git a/src/json-pointer/findByPointer/v1.ts b/src/json-pointer/findByPointer/v1.ts index 7c55f410b7..4a5b58ad90 100644 --- a/src/json-pointer/findByPointer/v1.ts +++ b/src/json-pointer/findByPointer/v1.ts @@ -1,4 +1,4 @@ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {isValidIndex, unescapeComponent} from '../util'; diff --git a/src/json-pointer/findByPointer/v2.ts b/src/json-pointer/findByPointer/v2.ts index 1ecb87eb7a..da56108071 100644 --- a/src/json-pointer/findByPointer/v2.ts +++ b/src/json-pointer/findByPointer/v2.ts @@ -1,4 +1,4 @@ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {isValidIndex, unescapeComponent} from '../util'; diff --git a/src/json-pointer/findByPointer/v3.ts b/src/json-pointer/findByPointer/v3.ts index 9058801fbd..3f9cff0174 100644 --- a/src/json-pointer/findByPointer/v3.ts +++ b/src/json-pointer/findByPointer/v3.ts @@ -1,4 +1,4 @@ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {isInteger, unescapeComponent} from '../util'; diff --git a/src/json-pointer/findByPointer/v4.ts b/src/json-pointer/findByPointer/v4.ts index 2c582a0453..e18e6f2005 100644 --- a/src/json-pointer/findByPointer/v4.ts +++ b/src/json-pointer/findByPointer/v4.ts @@ -1,4 +1,4 @@ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {unescapeComponent} from '../util'; diff --git a/src/json-pointer/findByPointer/v5.ts b/src/json-pointer/findByPointer/v5.ts index 9f5bea2a11..bf8f1c59ee 100644 --- a/src/json-pointer/findByPointer/v5.ts +++ b/src/json-pointer/findByPointer/v5.ts @@ -1,6 +1,6 @@ /* tslint:disable no-string-throw */ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {unescapeComponent} from '../util'; diff --git a/src/json-pointer/findByPointer/v6.ts b/src/json-pointer/findByPointer/v6.ts index 3b51c33bde..2f745194a8 100644 --- a/src/json-pointer/findByPointer/v6.ts +++ b/src/json-pointer/findByPointer/v6.ts @@ -1,6 +1,6 @@ /* tslint:disable no-string-throw */ -import {hasOwnProperty} from '../../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import {Reference} from '../find'; import {unescapeComponent} from '../util'; diff --git a/src/json-pointer/get.ts b/src/json-pointer/get.ts index 07e3d72bb6..e683a41be2 100644 --- a/src/json-pointer/get.ts +++ b/src/json-pointer/get.ts @@ -1,4 +1,4 @@ -import {hasOwnProperty} from '../util/hasOwnProperty'; +import {hasOwnProperty} from '@jsonjoy.com/util/lib/hasOwnProperty'; import type {Path} from './types'; export const get = (val: unknown, path: Path): unknown | undefined => { diff --git a/src/json-size/json.ts b/src/json-size/json.ts index c75f7b41d5..7b2c3bfefd 100644 --- a/src/json-size/json.ts +++ b/src/json-size/json.ts @@ -1,4 +1,4 @@ -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; const numberSize = (num: number) => { const isInteger = num === Math.round(num); diff --git a/src/json-size/msgpackSizeFast.ts b/src/json-size/msgpackSizeFast.ts index 8ddd87a2a5..933aea8936 100644 --- a/src/json-size/msgpackSizeFast.ts +++ b/src/json-size/msgpackSizeFast.ts @@ -1,5 +1,5 @@ import {JsonPackExtension, JsonPackValue} from '@jsonjoy.com/json-pack/lib/msgpack'; -import {isUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/isUint8Array'; +import {isUint8Array} from '@jsonjoy.com/util/lib/buffers/isUint8Array'; const arraySize = (arr: unknown[]): number => { let size = 2; diff --git a/src/json-stable/index.ts b/src/json-stable/index.ts index 74a0e51be4..28c767561d 100644 --- a/src/json-stable/index.ts +++ b/src/json-stable/index.ts @@ -1,6 +1,6 @@ -import {escape} from '@jsonjoy.com/json-pack/lib/util/strings/escape'; -import {sort} from '../util/sort/insertion'; -import type {json_string} from '../json-brand'; +import {escape} from '@jsonjoy.com/util/lib/strings/escape'; +import {sort} from '@jsonjoy.com/util/lib/sort/insertion'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; const getKeys = Object.keys; diff --git a/src/json-type-cli/Cli.ts b/src/json-type-cli/Cli.ts index 83dedaecca..ce042eb9d0 100644 --- a/src/json-type-cli/Cli.ts +++ b/src/json-type-cli/Cli.ts @@ -1,7 +1,7 @@ import {parseArgs} from 'node:util'; import {TypeSystem} from '../json-type/system/TypeSystem'; import {ObjectValueCaller} from '../reactive-rpc/common/rpc/caller/ObjectValueCaller'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; import {formatError} from './util'; import {defineBuiltinRoutes} from './methods'; import {defaultParams} from './defaultParams'; diff --git a/src/json-type-cli/codecs/cbor.ts b/src/json-type-cli/codecs/cbor.ts index 6174b2fbbf..20078fe649 100644 --- a/src/json-type-cli/codecs/cbor.ts +++ b/src/json-type-cli/codecs/cbor.ts @@ -1,6 +1,6 @@ import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecCbor implements CliCodec<'cbor'> { diff --git a/src/json-type-cli/codecs/json.ts b/src/json-type-cli/codecs/json.ts index fad83b3069..e6823a8df6 100644 --- a/src/json-type-cli/codecs/json.ts +++ b/src/json-type-cli/codecs/json.ts @@ -1,6 +1,6 @@ import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecJson implements CliCodec<'json'> { diff --git a/src/json-type-cli/codecs/json2.ts b/src/json-type-cli/codecs/json2.ts index d9df7f99d7..3a369a0967 100644 --- a/src/json-type-cli/codecs/json2.ts +++ b/src/json-type-cli/codecs/json2.ts @@ -1,7 +1,7 @@ import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import type {CliCodec} from '../types'; /** diff --git a/src/json-type-cli/codecs/json4.ts b/src/json-type-cli/codecs/json4.ts index 1d290ec3b4..d2607f4bd5 100644 --- a/src/json-type-cli/codecs/json4.ts +++ b/src/json-type-cli/codecs/json4.ts @@ -1,7 +1,7 @@ import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; import type {CliCodec} from '../types'; /** diff --git a/src/json-type-cli/codecs/msgpack.ts b/src/json-type-cli/codecs/msgpack.ts index 95cb5ab8ef..bd49f2f003 100644 --- a/src/json-type-cli/codecs/msgpack.ts +++ b/src/json-type-cli/codecs/msgpack.ts @@ -1,6 +1,6 @@ import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecMsgpack implements CliCodec<'msgpack'> { diff --git a/src/json-type-cli/codecs/ubjson.ts b/src/json-type-cli/codecs/ubjson.ts index bad4036c81..ae0d57f84f 100644 --- a/src/json-type-cli/codecs/ubjson.ts +++ b/src/json-type-cli/codecs/ubjson.ts @@ -1,6 +1,6 @@ import {UbjsonDecoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonDecoder'; import {UbjsonEncoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonEncoder'; -import type {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import type {CliCodec} from '../types'; export class CliCodecUbjson implements CliCodec<'ubjson'> { diff --git a/src/json-type-cli/defaultCodecs.ts b/src/json-type-cli/defaultCodecs.ts index c6511a13d8..944c58be3b 100644 --- a/src/json-type-cli/defaultCodecs.ts +++ b/src/json-type-cli/defaultCodecs.ts @@ -1,4 +1,4 @@ -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {CliCodecs} from './CliCodecs'; import {CliCodecCbor} from './codecs/cbor'; import {CliCodecJson} from './codecs/json'; diff --git a/src/json-type-cli/params/CliParamCmd.ts b/src/json-type-cli/params/CliParamCmd.ts index f685c40746..791e9ab369 100644 --- a/src/json-type-cli/params/CliParamCmd.ts +++ b/src/json-type-cli/params/CliParamCmd.ts @@ -1,8 +1,8 @@ import {applyPatch} from '../../json-patch'; import {spawn} from 'child_process'; import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; -import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; +import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; import type {Cli} from '../Cli'; import type {CliParam, CliParamInstance} from '../types'; diff --git a/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts b/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts index b21d988fe8..e8a395d29b 100644 --- a/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts +++ b/src/json-type/codegen/binary/BinaryEncoderCodegenContext.ts @@ -1,6 +1,6 @@ -import {Codegen, CodegenStepExecJs} from '../../../util/codegen'; +import {Codegen, CodegenStepExecJs} from '@jsonjoy.com/util/lib/codegen'; import {WriteBlobStep} from '../WriteBlobStep'; -import {concat} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {concat} from '@jsonjoy.com/util/lib/buffers/concat'; import {Value} from '../../../json-type-value/Value'; import type {TypeSystem} from '../../system'; import type {Type} from '../../type'; diff --git a/src/json-type/codegen/capacity/CapacityEstimatorCodegenContext.ts b/src/json-type/codegen/capacity/CapacityEstimatorCodegenContext.ts index 636b64af7a..0e5fee9ef4 100644 --- a/src/json-type/codegen/capacity/CapacityEstimatorCodegenContext.ts +++ b/src/json-type/codegen/capacity/CapacityEstimatorCodegenContext.ts @@ -1,4 +1,4 @@ -import {Codegen, CodegenStepExecJs} from '../../../util/codegen'; +import {Codegen, CodegenStepExecJs} from '@jsonjoy.com/util/lib/codegen'; import {maxEncodingCapacity} from '../../../json-size'; import {Value} from '../../../json-type-value/Value'; import type {TypeSystem} from '../../system'; diff --git a/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts b/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts index 21b3a9d705..2e500aad09 100644 --- a/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts +++ b/src/json-type/codegen/json/JsonTextEncoderCodegenContext.ts @@ -1,9 +1,9 @@ -import {Codegen, CodegenStepExecJs} from '../../../util/codegen'; -import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; +import {Codegen, CodegenStepExecJs} from '@jsonjoy.com/util/lib/codegen'; +import {asString} from '@jsonjoy.com/util/lib/strings/asString'; import {toBase64} from '@jsonjoy.com/base64/lib/toBase64'; import type {TypeSystem} from '../../system'; import type {Type} from '../../type'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; export type JsonEncoderFn = (value: T) => json_string; diff --git a/src/json-type/codegen/validator/ValidatorCodegenContext.ts b/src/json-type/codegen/validator/ValidatorCodegenContext.ts index 00eb93c190..065eeaa091 100644 --- a/src/json-type/codegen/validator/ValidatorCodegenContext.ts +++ b/src/json-type/codegen/validator/ValidatorCodegenContext.ts @@ -1,4 +1,4 @@ -import {Codegen} from '../../../util/codegen'; +import {Codegen} from '@jsonjoy.com/util/lib/codegen'; import {ValidationError, ValidationErrorMessage} from '../../constants'; import type {TypeSystem} from '../../system'; import type {Type} from '../../type'; diff --git a/src/json-type/schema/schema.ts b/src/json-type/schema/schema.ts index ba1545995e..c2cb50f900 100644 --- a/src/json-type/schema/schema.ts +++ b/src/json-type/schema/schema.ts @@ -1,5 +1,5 @@ import type {Observable} from 'rxjs'; -import type {Mutable} from '../../util/types'; +import type {Mutable} from '@jsonjoy.com/util/lib/types'; import type {Display, Identifiable} from './common'; import type {Expr} from '../../json-expression'; diff --git a/src/json-type/type/classes/AbstractType.ts b/src/json-type/type/classes/AbstractType.ts index 14232589c0..5d5c7288ba 100644 --- a/src/json-type/type/classes/AbstractType.ts +++ b/src/json-type/type/classes/AbstractType.ts @@ -19,16 +19,16 @@ import { JsonEncoderCodegenContextOptions, } from '../../codegen/binary/JsonEncoderCodegenContext'; import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import { MessagePackEncoderCodegenContext, MessagePackEncoderCodegenContextOptions, } from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; -import {lazy} from '../../../util/lazyFunction'; +import {lazy} from '@jsonjoy.com/util/lib/lazyFunction'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import { CapacityEstimatorCodegenContext, CapacityEstimatorCodegenContextOptions, @@ -38,7 +38,7 @@ import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type * as jsonSchema from '../../../json-schema'; import type {BaseType} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; import type {Validators} from './types'; diff --git a/src/json-type/type/classes/AnyType.ts b/src/json-type/type/classes/AnyType.ts index 0efd7d4ec6..77a4e69133 100644 --- a/src/json-type/type/classes/AnyType.ts +++ b/src/json-type/type/classes/AnyType.ts @@ -7,7 +7,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; diff --git a/src/json-type/type/classes/ArrayType.ts b/src/json-type/type/classes/ArrayType.ts index f1b1dd969e..4cb4da2c82 100644 --- a/src/json-type/type/classes/ArrayType.ts +++ b/src/json-type/type/classes/ArrayType.ts @@ -8,7 +8,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -20,7 +20,7 @@ import {NumberType} from './NumberType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/BinaryType.ts b/src/json-type/type/classes/BinaryType.ts index dc98969d19..b3784f9658 100644 --- a/src/json-type/type/classes/BinaryType.ts +++ b/src/json-type/type/classes/BinaryType.ts @@ -10,7 +10,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -19,7 +19,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/BooleanType.ts b/src/json-type/type/classes/BooleanType.ts index 9697fa2aef..281fdc8d75 100644 --- a/src/json-type/type/classes/BooleanType.ts +++ b/src/json-type/type/classes/BooleanType.ts @@ -8,7 +8,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -16,7 +16,7 @@ import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; import type * as jtd from '../../jtd/types'; diff --git a/src/json-type/type/classes/ConstType.ts b/src/json-type/type/classes/ConstType.ts index 533ff44380..a78898fd20 100644 --- a/src/json-type/type/classes/ConstType.ts +++ b/src/json-type/type/classes/ConstType.ts @@ -9,7 +9,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -17,7 +17,7 @@ import {maxEncodingCapacity} from '../../../json-size'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; import type * as jtd from '../../jtd/types'; diff --git a/src/json-type/type/classes/MapType.ts b/src/json-type/type/classes/MapType.ts index 61355bcb8a..f891a56668 100644 --- a/src/json-type/type/classes/MapType.ts +++ b/src/json-type/type/classes/MapType.ts @@ -1,7 +1,7 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; import {printTree} from '../../../util/print/printTree'; -import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; +import {asString} from '@jsonjoy.com/util/lib/strings/asString'; import {validateTType} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; @@ -10,7 +10,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -19,7 +19,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/NumberType.ts b/src/json-type/type/classes/NumberType.ts index fed3fd990f..5d6d441713 100644 --- a/src/json-type/type/classes/NumberType.ts +++ b/src/json-type/type/classes/NumberType.ts @@ -8,7 +8,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -16,7 +16,7 @@ import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; import type * as jtd from '../../jtd/types'; diff --git a/src/json-type/type/classes/ObjectType.ts b/src/json-type/type/classes/ObjectType.ts index f1b5822449..38e22ece42 100644 --- a/src/json-type/type/classes/ObjectType.ts +++ b/src/json-type/type/classes/ObjectType.ts @@ -1,17 +1,17 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; import {printTree} from '../../../util/print/printTree'; -import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; +import {asString} from '@jsonjoy.com/util/lib/strings/asString'; import {validateTType, validateWithValidator} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; import {ValidationError} from '../../constants'; -import {normalizeAccessor} from '@jsonjoy.com/json-pack/lib/util/codegen/util/normalizeAccessor'; +import {normalizeAccessor} from '@jsonjoy.com/util/lib/codegen/util/normalizeAccessor'; import {canSkipObjectKeyUndefinedCheck} from '../../codegen/validator/util'; import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderCodegenContext'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead, maxEncodingCapacity} from '../../../json-size'; @@ -19,7 +19,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, SchemaOfObjectFields, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/OrType.ts b/src/json-type/type/classes/OrType.ts index 28c01bda53..5ba41f3f9f 100644 --- a/src/json-type/type/classes/OrType.ts +++ b/src/json-type/type/classes/OrType.ts @@ -8,7 +8,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -20,7 +20,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/RefType.ts b/src/json-type/type/classes/RefType.ts index 8eaa3dea86..d2222174d6 100644 --- a/src/json-type/type/classes/RefType.ts +++ b/src/json-type/type/classes/RefType.ts @@ -8,7 +8,7 @@ import {CompiledBinaryEncoder} from '../../codegen/types'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; @@ -17,7 +17,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/type/classes/StringType.ts b/src/json-type/type/classes/StringType.ts index fda53ff6b3..41e6396ed7 100644 --- a/src/json-type/type/classes/StringType.ts +++ b/src/json-type/type/classes/StringType.ts @@ -1,6 +1,6 @@ import * as schema from '../../schema'; import {RandomJson} from '../../../json-random'; -import {asString} from '@jsonjoy.com/json-pack/lib/util/strings/asString'; +import {asString} from '@jsonjoy.com/util/lib/strings/asString'; import {validateMinMax, validateTType, validateWithValidator} from '../../schema/validate'; import {ValidatorCodegenContext} from '../../codegen/validator/ValidatorCodegenContext'; import {ValidationPath} from '../../codegen/validator/types'; @@ -9,7 +9,7 @@ import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderC import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; import {BinaryEncoderCodegenContext} from '../../codegen/binary/BinaryEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; @@ -17,7 +17,7 @@ import {MaxEncodingOverhead} from '../../../json-size'; import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; import type * as jtd from '../../jtd/types'; diff --git a/src/json-type/type/classes/TupleType.ts b/src/json-type/type/classes/TupleType.ts index 5adea83dd8..b01496a247 100644 --- a/src/json-type/type/classes/TupleType.ts +++ b/src/json-type/type/classes/TupleType.ts @@ -7,7 +7,7 @@ import {ValidationError} from '../../constants'; import {JsonTextEncoderCodegenContext} from '../../codegen/json/JsonTextEncoderCodegenContext'; import {CborEncoderCodegenContext} from '../../codegen/binary/CborEncoderCodegenContext'; import {JsonEncoderCodegenContext} from '../../codegen/binary/JsonEncoderCodegenContext'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {MessagePackEncoderCodegenContext} from '../../codegen/binary/MessagePackEncoderCodegenContext'; import {CapacityEstimatorCodegenContext} from '../../codegen/capacity/CapacityEstimatorCodegenContext'; import {MaxEncodingOverhead} from '../../../json-size'; @@ -15,7 +15,7 @@ import {AbstractType} from './AbstractType'; import type * as jsonSchema from '../../../json-schema'; import type {SchemaOf, Type} from '../types'; import type {TypeSystem} from '../../system/TypeSystem'; -import type {json_string} from '../../../json-brand'; +import type {json_string} from '@jsonjoy.com/util/lib/json-brand'; import type * as ts from '../../typescript/types'; import type {TypeExportContext} from '../../system/TypeExportContext'; diff --git a/src/json-type/typescript/toText.ts b/src/json-type/typescript/toText.ts index 9c40ba6dd9..6604f083ad 100644 --- a/src/json-type/typescript/toText.ts +++ b/src/json-type/typescript/toText.ts @@ -1,4 +1,4 @@ -import {wordWrap} from '@jsonjoy.com/json-pack/lib/util/strings/wordWrap'; +import {wordWrap} from '@jsonjoy.com/util/lib/strings/wordWrap'; import {TsIdentifier, TsNode, TsParameter} from './types'; import {TAB, isSimpleType, normalizeKey} from './util'; diff --git a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts b/src/reactive-rpc/browser/createBinaryWsRpcClient.ts index 457c21fc88..78cfc585c7 100644 --- a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts +++ b/src/reactive-rpc/browser/createBinaryWsRpcClient.ts @@ -1,5 +1,5 @@ import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RpcPersistentClient, WebSocketChannel} from '../common'; import {RpcCodec} from '../common/codec/RpcCodec'; import {BinaryRpcMessageCodec} from '../common/codec/binary'; diff --git a/src/reactive-rpc/browser/createJsonWsRpcClient.ts b/src/reactive-rpc/browser/createJsonWsRpcClient.ts index d1515cbe9e..0478532008 100644 --- a/src/reactive-rpc/browser/createJsonWsRpcClient.ts +++ b/src/reactive-rpc/browser/createJsonWsRpcClient.ts @@ -1,5 +1,5 @@ import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RpcPersistentClient, WebSocketChannel} from '../common'; import {RpcCodec} from '../common/codec/RpcCodec'; import {CompactRpcMessageCodec} from '../common/codec/compact'; diff --git a/src/reactive-rpc/common/channel/channel.ts b/src/reactive-rpc/common/channel/channel.ts index b153f6bfd8..f31ecb37be 100644 --- a/src/reactive-rpc/common/channel/channel.ts +++ b/src/reactive-rpc/common/channel/channel.ts @@ -1,6 +1,6 @@ import type {WebSocketBase, CloseEventBase} from './types'; import {Subject, ReplaySubject, BehaviorSubject, Observable, from} from 'rxjs'; -import {toUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/toUint8Array'; +import {toUint8Array} from '@jsonjoy.com/util/lib/buffers/toUint8Array'; import {delay, filter, map, skip, switchMap, take, takeUntil, tap} from 'rxjs/operators'; export const enum ChannelState { diff --git a/src/reactive-rpc/common/channel/mock.ts b/src/reactive-rpc/common/channel/mock.ts index ad6f978679..e6ef9c15bc 100644 --- a/src/reactive-rpc/common/channel/mock.ts +++ b/src/reactive-rpc/common/channel/mock.ts @@ -1,5 +1,5 @@ import {WebSocketState} from './constants'; -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; export interface CreateWebSocketMockParams { onClose: (code?: number, reason?: string) => void; diff --git a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts b/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts index 10485a5e04..d8b3d2a236 100644 --- a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts +++ b/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts @@ -1,7 +1,7 @@ import {RpcMessageFormat} from '../constants'; import {decode} from './decode'; import * as msg from '../../messages'; -import type {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; +import type {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; import type {RpcMessageCodec} from '../types'; import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; diff --git a/src/reactive-rpc/common/codec/binary/decode.ts b/src/reactive-rpc/common/codec/binary/decode.ts index 7c5e3b5236..db38628a90 100644 --- a/src/reactive-rpc/common/codec/binary/decode.ts +++ b/src/reactive-rpc/common/codec/binary/decode.ts @@ -1,4 +1,4 @@ -import {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; +import {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; import { NotificationMessage, ReactiveRpcMessage, @@ -13,7 +13,7 @@ import { } from '../../messages'; import {RpcValue} from '../../messages/Value'; import {BinaryMessageType} from './constants'; -import type {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; +import type {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; export const decode = (reader: Reader): ReactiveRpcMessage => { const word = reader.u32(); diff --git a/src/reactive-rpc/common/testing/buildE2eClient.ts b/src/reactive-rpc/common/testing/buildE2eClient.ts index 9a003f8783..937a200fb4 100644 --- a/src/reactive-rpc/common/testing/buildE2eClient.ts +++ b/src/reactive-rpc/common/testing/buildE2eClient.ts @@ -1,6 +1,6 @@ import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Fuzzer} from '../../../util/Fuzzer'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {ConnectionContext} from '../../server/context'; import {RpcCodecs} from '../codec/RpcCodecs'; import {RpcMessageCodecs} from '../codec/RpcMessageCodecs'; diff --git a/src/reactive-rpc/server/context.ts b/src/reactive-rpc/server/context.ts index 33c656c8ab..7b48210ae6 100644 --- a/src/reactive-rpc/server/context.ts +++ b/src/reactive-rpc/server/context.ts @@ -1,6 +1,6 @@ -import {NullObject} from '../../util/NullObject'; -import {copy} from '@jsonjoy.com/json-pack/lib/util/buffers/copy'; -import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {NullObject} from '@jsonjoy.com/util/lib/NullObject'; +import {copy} from '@jsonjoy.com/util/lib/buffers/copy'; +import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {RpcMessageCodec} from '../common/codec/types'; import type {RpcApp} from './uws/RpcApp'; diff --git a/src/reactive-rpc/server/http1/Http1Server.ts b/src/reactive-rpc/server/http1/Http1Server.ts index 1d9a4342e1..b00ef387d8 100644 --- a/src/reactive-rpc/server/http1/Http1Server.ts +++ b/src/reactive-rpc/server/http1/Http1Server.ts @@ -1,6 +1,6 @@ import * as http from 'http'; import * as net from 'net'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {WsServerConnection} from '../ws/server/WsServerConnection'; import {WsFrameEncoder} from '../ws/codec/WsFrameEncoder'; @@ -13,7 +13,7 @@ import {findTokenInText, setCodecs} from './util'; import {Http1ConnectionContext, WsConnectionContext} from './context'; import {RpcCodecs} from '../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; -import {NullObject} from '../../../util/NullObject'; +import {NullObject} from '@jsonjoy.com/util/lib/NullObject'; export type Http1Handler = (ctx: Http1ConnectionContext) => void | Promise; export type Http1NotFoundHandler = (res: http.ServerResponse, req: http.IncomingMessage) => void; diff --git a/src/reactive-rpc/server/http1/context.ts b/src/reactive-rpc/server/http1/context.ts index 85e4063e3c..d1f3513db9 100644 --- a/src/reactive-rpc/server/http1/context.ts +++ b/src/reactive-rpc/server/http1/context.ts @@ -1,5 +1,5 @@ import {getBody} from './util'; -import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; import type * as http from 'http'; import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; import type {RpcMessageCodec} from '../../common/codec/types'; diff --git a/src/reactive-rpc/server/uws/RpcApp.ts b/src/reactive-rpc/server/uws/RpcApp.ts index 0aaac8af39..f633bb0708 100644 --- a/src/reactive-rpc/server/uws/RpcApp.ts +++ b/src/reactive-rpc/server/uws/RpcApp.ts @@ -7,8 +7,8 @@ import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; import {RpcValue} from '../../common/messages/Value'; import {RpcCodecs} from '../../common/codec/RpcCodecs'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; -import {copy} from '@jsonjoy.com/json-pack/lib/util/buffers/copy'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; +import {copy} from '@jsonjoy.com/util/lib/buffers/copy'; import {type ReactiveRpcMessage, RpcMessageStreamProcessor, ReactiveRpcClientMessage} from '../../common'; import type * as types from './types'; import type {RouteHandler} from './types'; diff --git a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts index 7025a4485d..8c97f3448d 100644 --- a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts +++ b/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts @@ -1,4 +1,4 @@ -import {StreamingOctetReader} from '@jsonjoy.com/json-pack/lib/util/buffers/StreamingOctetReader'; +import {StreamingOctetReader} from '@jsonjoy.com/util/lib/buffers/StreamingOctetReader'; import {WsFrameOpcode} from './constants'; import {WsFrameDecodingError} from './errors'; import {WsCloseFrame, WsFrameHeader, WsPingFrame, WsPongFrame} from './frames'; diff --git a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts index f92ddbbc4a..1c3628b5eb 100644 --- a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts +++ b/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts @@ -1,7 +1,7 @@ -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {WsFrameOpcode} from './constants'; import {WsFrameEncodingError} from './errors'; -import type {IWriter, IWriterGrowable} from '@jsonjoy.com/json-pack/lib/util/buffers'; +import type {IWriter, IWriterGrowable} from '@jsonjoy.com/util/lib/buffers'; const maskBuf = new Uint8Array(4); const maskBufView = new DataView(maskBuf.buffer, maskBuf.byteOffset, maskBuf.byteLength); diff --git a/src/reactive-rpc/server/ws/server/WsServerConnection.ts b/src/reactive-rpc/server/ws/server/WsServerConnection.ts index 0919f0487c..1112093472 100644 --- a/src/reactive-rpc/server/ws/server/WsServerConnection.ts +++ b/src/reactive-rpc/server/ws/server/WsServerConnection.ts @@ -1,7 +1,7 @@ import * as crypto from 'crypto'; import * as stream from 'stream'; -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; -import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; +import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; import {WsCloseFrame, WsFrameDecoder, WsFrameHeader, WsFrameOpcode, WsPingFrame, WsPongFrame} from '../codec'; import type {WsFrameEncoder} from '../codec/WsFrameEncoder'; diff --git a/src/util/Fuzzer.ts b/src/util/Fuzzer.ts deleted file mode 100644 index 694149ba73..0000000000 --- a/src/util/Fuzzer.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {randomBytes} from 'crypto'; - -function xoshiro128ss(a: number, b: number, c: number, d: number) { - return () => { - const t = b << 9; - let r = b * 5; - r = ((r << 7) | (r >>> 25)) * 9; - c ^= a; - d ^= b; - b ^= c; - a ^= d; - c ^= t; - d = (d << 11) | (d >>> 21); - return (r >>> 0) / 4294967296; - }; -} - -export class Fuzzer { - public static randomInt(min: number, max: number): number { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - public static randomInt2([min, max]: [min: number, max: number]): number { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - /** @deprecated */ - public static pick(elements: T[]): T { - return elements[Math.floor(Math.random() * elements.length)]; - } - - /** @deprecated */ - public static repeat(times: number, callback: () => T): T[] { - const result: T[] = []; - for (let i = 0; i < times; i++) result.push(callback()); - return result; - } - - public readonly seed: Buffer; - public readonly random: () => number; - - constructor(seed?: Buffer) { - this.seed = seed = seed || randomBytes(4 * 4); - let i = 0; - const a = (seed[i++] << 24) | (seed[i++] << 16) | (seed[i++] << 8) | seed[i++]; - const b = (seed[i++] << 24) | (seed[i++] << 16) | (seed[i++] << 8) | seed[i++]; - const c = (seed[i++] << 24) | (seed[i++] << 16) | (seed[i++] << 8) | seed[i++]; - const d = (seed[i++] << 24) | (seed[i++] << 16) | (seed[i++] << 8) | seed[i++]; - this.random = xoshiro128ss(a, b, c, d); - Math.random = this.random; - } - - public readonly randomInt = (min: number, max: number): number => { - return Math.floor(Math.random() * (max - min + 1)) + min; - }; - - public readonly pick = (elements: T[]): T => { - return elements[Math.floor(Math.random() * elements.length)]; - }; - - public readonly repeat = (times: number, callback: () => T): T[] => { - const result: T[] = []; - for (let i = 0; i < times; i++) result.push(callback()); - return result; - }; -} diff --git a/src/util/NullObject.ts b/src/util/NullObject.ts deleted file mode 100644 index 707a2f0cc0..0000000000 --- a/src/util/NullObject.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const NullObject = function NullObject() {} as any as new () => Record; -NullObject.prototype = Object.create(null); diff --git a/src/util/buffers/index.ts b/src/util/buffers/index.ts deleted file mode 100644 index 7d1d7e6433..0000000000 --- a/src/util/buffers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@jsonjoy.com/json-pack/lib/util/buffers'; diff --git a/src/util/codegen/index.ts b/src/util/codegen/index.ts deleted file mode 100644 index 41fa667fe6..0000000000 --- a/src/util/codegen/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@jsonjoy.com/json-pack/lib/util/codegen'; diff --git a/src/util/hasOwnProperty.ts b/src/util/hasOwnProperty.ts deleted file mode 100644 index 668c13cb9d..0000000000 --- a/src/util/hasOwnProperty.ts +++ /dev/null @@ -1,5 +0,0 @@ -const has = Object.prototype.hasOwnProperty; - -export function hasOwnProperty(obj: object, key: string): boolean { - return has.call(obj, key); -} diff --git a/src/util/isEmpty.ts b/src/util/isEmpty.ts deleted file mode 100644 index cb3f76edb1..0000000000 --- a/src/util/isEmpty.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const isEmpty = (obj: object): boolean => { - for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) return false; - return true; -}; diff --git a/src/util/lazyFunction.ts b/src/util/lazyFunction.ts deleted file mode 100644 index ec5afd7ba9..0000000000 --- a/src/util/lazyFunction.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const lazy = any>(factory: () => T): T => { - let generated: T | undefined; - const fn = (...args: any[]) => { - if (!generated) generated = factory(); - return generated.apply(null, args); - }; - return fn as T; -}; diff --git a/src/util/router/codegen.ts b/src/util/router/codegen.ts index 96e516fca3..da38825e7e 100644 --- a/src/util/router/codegen.ts +++ b/src/util/router/codegen.ts @@ -1,5 +1,5 @@ -import {Codegen} from '@jsonjoy.com/json-pack/lib/util/codegen'; -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {Codegen} from '@jsonjoy.com/util/lib/codegen'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import type {Match} from './router'; export type RouteMatcher = (route: string) => undefined | Match; diff --git a/src/util/router/router.ts b/src/util/router/router.ts index 6b0daee5d0..143104fe3d 100644 --- a/src/util/router/router.ts +++ b/src/util/router/router.ts @@ -1,4 +1,4 @@ -import {JsExpression} from '@jsonjoy.com/json-pack/lib/util/codegen/util/JsExpression'; +import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; import {printTree} from '../print/printTree'; import {Printable} from '../print/types'; import {RouteMatcher, RouterCodegenCtx, RouterCodegenOpts} from './codegen'; diff --git a/src/util/router/tree.ts b/src/util/router/tree.ts index d32484a128..9e209cde0c 100644 --- a/src/util/router/tree.ts +++ b/src/util/router/tree.ts @@ -1,4 +1,4 @@ -import {emitStringMatch} from '@jsonjoy.com/json-pack/lib/util/codegen/util/helpers'; +import {emitStringMatch} from '@jsonjoy.com/util/lib/codegen/util/helpers'; import {printTree} from '../print/printTree'; import {Printable} from '../print/types'; import {RadixTree} from '../trees/radix/RadixTree'; diff --git a/src/util/sort/insertion.ts b/src/util/sort/insertion.ts deleted file mode 100644 index 69d45d86c7..0000000000 --- a/src/util/sort/insertion.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@jsonjoy.com/json-pack/lib/util/sort/insertion'; diff --git a/src/util/sort/insertion2.ts b/src/util/sort/insertion2.ts deleted file mode 100644 index dbf23491f1..0000000000 --- a/src/util/sort/insertion2.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@jsonjoy.com/json-pack/lib/util/sort/insertion2'; diff --git a/src/util/types.ts b/src/util/types.ts deleted file mode 100644 index 726479dc00..0000000000 --- a/src/util/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type Mutable = { - -readonly [P in keyof T]: T[P]; -}; - -export type Brand = S & {__TYPE__: T; __BRAND__: B}; - -export type MaybeArray = T | T[]; - -export type Ensure = T extends Ext ? T : never; diff --git a/src/web3/adl/hamt-crdt/Hamt.ts b/src/web3/adl/hamt-crdt/Hamt.ts index 98992c9022..8d5edc957d 100644 --- a/src/web3/adl/hamt-crdt/Hamt.ts +++ b/src/web3/adl/hamt-crdt/Hamt.ts @@ -4,7 +4,7 @@ import {HamtFrame} from './HamtFrame'; import * as hlc from '../../hlc'; import {Cid} from '../../multiformats'; import {sha256} from '../../crypto'; -import {toBuf} from '@jsonjoy.com/json-pack/lib/util/buffers/toBuf'; +import {toBuf} from '@jsonjoy.com/util/lib/buffers/toBuf'; import type {CidCasStruct} from '../../store/cas/CidCasStruct'; import type * as types from './types'; diff --git a/src/web3/adl/hamt-crdt/HamtFrame.ts b/src/web3/adl/hamt-crdt/HamtFrame.ts index 87cab2b8b4..14b62e32c0 100644 --- a/src/web3/adl/hamt-crdt/HamtFrame.ts +++ b/src/web3/adl/hamt-crdt/HamtFrame.ts @@ -1,5 +1,5 @@ import {Defer} from 'thingies/es2020/Defer'; -import {cmpUint8Array2} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array2'; +import {cmpUint8Array2} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array2'; import {cmpDto} from '../../hlc'; import {CidCasStruct} from '../../store/cas/CidCasStruct'; import {Cid} from '../../multiformats'; diff --git a/src/web3/codec/codecs/writer.ts b/src/web3/codec/codecs/writer.ts index 195e12d84c..8636e0f104 100644 --- a/src/web3/codec/codecs/writer.ts +++ b/src/web3/codec/codecs/writer.ts @@ -1,3 +1,3 @@ -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; export const writer = new Writer(); diff --git a/src/web3/multiformats/Multihash.ts b/src/web3/multiformats/Multihash.ts index d6ef9af470..367af4a8e7 100644 --- a/src/web3/multiformats/Multihash.ts +++ b/src/web3/multiformats/Multihash.ts @@ -1,4 +1,4 @@ -import {cmpUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array'; +import {cmpUint8Array} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array'; import {crypto} from '../crypto'; import * as uvint from '../util/uvint'; import {Multicodec} from './constants'; From 0ca861a4a2df4db4c799d0fc641bef0667613365 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 00:57:51 +0200 Subject: [PATCH 17/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20imports?= =?UTF-8?q?=20in=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts | 2 +- src/json-crdt/__bench__/util/fuzzer-traces.ts | 2 +- src/json-crdt/__tests__/fuzzer/Picker.ts | 2 +- src/json-crdt/__tests__/fuzzer/SessionLogical.ts | 2 +- src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts | 2 +- src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts | 2 +- src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts | 2 +- src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts | 2 +- src/json-crdt/schema/__tests__/toSchema.spec.ts | 2 +- .../ot-binary-irreversible/__tests__/BinaryOtFuzzer.ts | 2 +- src/json-ot/types/ot-json/__tests__/fuzzer/JsonOtFuzzer.ts | 2 +- .../ot-string-irreversible/__tests__/StringOtFuzzer.ts | 2 +- src/json-ot/types/ot-string/__tests__/StringOtFuzzer.ts | 2 +- src/json-size/__tests__/fuzz.spec.ts | 2 +- src/json-size/__tests__/testJsonSize.ts | 2 +- .../binary/__tests__/CborEncoderCodegenContext.spec.ts | 2 +- .../binary/__tests__/JsonEncoderCodegenContext.spec.ts | 2 +- .../__tests__/MessagePackEncoderCodegenContext.spec.ts | 2 +- .../__tests__/e2e/uws/http/FetchRpcClient.spec.ts | 2 +- .../__tests__/e2e/uws/http/StreamingRpcClient.spec.ts | 2 +- .../__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts | 2 +- .../codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts | 2 +- .../common/codec/binary/__tests__/automated.spec.ts | 2 +- .../common/codec/binary/__tests__/decode.spec.ts | 6 +++--- .../common/codec/binary/__tests__/encode.spec.ts | 2 +- .../common/codec/binary/__tests__/smoke-tests.spec.ts | 2 +- .../codec/compact/__tests__/CompactRpcMessageCodec.spec.ts | 2 +- .../common/codec/compact/__tests__/automated.spec.ts | 2 +- .../common/codec/compact/__tests__/smoke-tests.spec.ts | 2 +- .../common/codec/json-rpc-2/__tests__/automated.spec.ts | 2 +- .../common/rpc/__tests__/RpcPersistentClient.spec.ts | 2 +- .../common/rpc/caller/error/__tests__/RpcErrorType.spec.ts | 2 +- .../server/ws/server/__tests__/WsServerConnection.spec.ts | 4 ++-- src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts | 2 +- 34 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts b/src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts index 6cec6b055d..9bfe94c1bc 100644 --- a/src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts +++ b/src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts @@ -1,5 +1,5 @@ import {RandomJson} from '../../../json-random'; -import {Fuzzer} from '../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {interval, ITimestampStruct, Timespan, ClockVector, ServerClockVector, ts} from '../../clock'; import {SESSION} from '../../constants'; import {Patch} from '../../Patch'; diff --git a/src/json-crdt/__bench__/util/fuzzer-traces.ts b/src/json-crdt/__bench__/util/fuzzer-traces.ts index a359a1fd47..8a7251e9c1 100644 --- a/src/json-crdt/__bench__/util/fuzzer-traces.ts +++ b/src/json-crdt/__bench__/util/fuzzer-traces.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import {Patch} from '../../../json-crdt-patch'; import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Model} from '../../model'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; export const loadFuzzerTrace = (traceName: string): [batch: Patch[], model: Model] => { const root = path.resolve(__dirname, '..', '..', '..', '..'); diff --git a/src/json-crdt/__tests__/fuzzer/Picker.ts b/src/json-crdt/__tests__/fuzzer/Picker.ts index 37a9370b5d..e8415d7fb7 100644 --- a/src/json-crdt/__tests__/fuzzer/Picker.ts +++ b/src/json-crdt/__tests__/fuzzer/Picker.ts @@ -2,7 +2,7 @@ import {DelOp, InsObjOp, InsStrOp, InsBinOp, InsArrOp} from '../../../json-crdt- import {RandomJson} from '../../../json-random'; import {JsonNode, ObjNode, ArrNode, BinNode, StrNode} from '../../nodes'; import {Model} from '../../model/Model'; -import {Fuzzer} from '../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {FuzzerOptions} from './types'; type StringOp = typeof InsStrOp | typeof DelOp; diff --git a/src/json-crdt/__tests__/fuzzer/SessionLogical.ts b/src/json-crdt/__tests__/fuzzer/SessionLogical.ts index eb2d749807..b1c219305c 100644 --- a/src/json-crdt/__tests__/fuzzer/SessionLogical.ts +++ b/src/json-crdt/__tests__/fuzzer/SessionLogical.ts @@ -21,7 +21,7 @@ import {randomU32} from 'hyperdyperid/lib/randomU32'; import {StrNode, ValNode, ObjNode, ArrNode, BinNode} from '../../nodes'; import {interval} from '../../../json-crdt-patch/clock'; import type {JsonCrdtFuzzer} from './JsonCrdtFuzzer'; -import {Fuzzer} from '../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; const jsonEncoder = new JsonEncoder(); const jsonDecoder = new JsonDecoder(); diff --git a/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts b/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts index f030804436..69344286a0 100644 --- a/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts +++ b/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts @@ -1,7 +1,7 @@ /* tslint:disable no-console */ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {BinNode} from '../BinNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import {RandomJson} from '../../../../json-random'; diff --git a/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts index ff7b7222d8..ff90e4ae19 100644 --- a/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts @@ -1,7 +1,7 @@ /* tslint:disable no-console */ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {StrNode} from '../StrNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import * as path from 'path'; diff --git a/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts index 9ab6cbe899..7432aa6ce4 100644 --- a/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts @@ -1,7 +1,7 @@ /* tslint:disable no-console */ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {StrNode} from '../StrNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import {RandomJson} from '../../../../json-random'; diff --git a/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts b/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts index 954995fe01..a3929bb29f 100644 --- a/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts @@ -1,6 +1,6 @@ import {equal} from 'assert'; import {ITimespanStruct, ITimestampStruct, ClockVector, toDisplayString, ts} from '../../../../json-crdt-patch/clock'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {randomSessionId} from '../../../model/util'; import {StrNode} from '../StrNode'; import {printTree} from '../../../../util/print/printTree'; diff --git a/src/json-crdt/schema/__tests__/toSchema.spec.ts b/src/json-crdt/schema/__tests__/toSchema.spec.ts index a0ae310343..9a08bc874b 100644 --- a/src/json-crdt/schema/__tests__/toSchema.spec.ts +++ b/src/json-crdt/schema/__tests__/toSchema.spec.ts @@ -1,6 +1,6 @@ import {NodeBuilder, s, nodes} from '../../../json-crdt-patch'; import {deepEqual} from '../../../json-equal/deepEqual'; -import {cmpUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/cmpUint8Array'; +import {cmpUint8Array} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array'; import {Model} from '../../model'; import {toSchema} from '../toSchema'; diff --git a/src/json-ot/types/ot-binary-irreversible/__tests__/BinaryOtFuzzer.ts b/src/json-ot/types/ot-binary-irreversible/__tests__/BinaryOtFuzzer.ts index 4cbe3195d2..aec4767320 100644 --- a/src/json-ot/types/ot-binary-irreversible/__tests__/BinaryOtFuzzer.ts +++ b/src/json-ot/types/ot-binary-irreversible/__tests__/BinaryOtFuzzer.ts @@ -1,5 +1,5 @@ import {RandomJson} from '../../../../json-random'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {append, normalize} from '../util'; import {BinaryOp} from '../types'; diff --git a/src/json-ot/types/ot-json/__tests__/fuzzer/JsonOtFuzzer.ts b/src/json-ot/types/ot-json/__tests__/fuzzer/JsonOtFuzzer.ts index 4710bfab23..75f1c1ecc9 100644 --- a/src/json-ot/types/ot-json/__tests__/fuzzer/JsonOtFuzzer.ts +++ b/src/json-ot/types/ot-json/__tests__/fuzzer/JsonOtFuzzer.ts @@ -1,7 +1,7 @@ import {clone} from '../../../../../json-clone'; import {find, isArrayReference, isObjectReference, Path} from '../../../../../json-pointer'; import {RandomJson} from '../../../../../json-random'; -import {Fuzzer} from '../../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import type {JsonOp, JsonOpDataComponent, JsonOpDropComponent, JsonOpPickComponent} from '../../types'; import {applyPatch} from '../../../../../json-patch/applyPatch'; diff --git a/src/json-ot/types/ot-string-irreversible/__tests__/StringOtFuzzer.ts b/src/json-ot/types/ot-string-irreversible/__tests__/StringOtFuzzer.ts index 46321c2ef7..c7da7b47e6 100644 --- a/src/json-ot/types/ot-string-irreversible/__tests__/StringOtFuzzer.ts +++ b/src/json-ot/types/ot-string-irreversible/__tests__/StringOtFuzzer.ts @@ -1,5 +1,5 @@ import {RandomJson} from '../../../../json-random'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {append, normalize} from '../util'; import {StringOp} from '../types'; diff --git a/src/json-ot/types/ot-string/__tests__/StringOtFuzzer.ts b/src/json-ot/types/ot-string/__tests__/StringOtFuzzer.ts index a76eba3dd4..1d87f12af1 100644 --- a/src/json-ot/types/ot-string/__tests__/StringOtFuzzer.ts +++ b/src/json-ot/types/ot-string/__tests__/StringOtFuzzer.ts @@ -1,5 +1,5 @@ import {RandomJson} from '../../../../json-random'; -import {Fuzzer} from '../../../../util/Fuzzer'; +import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; import {append, normalize} from '../util'; import {StringOp} from '../types'; diff --git a/src/json-size/__tests__/fuzz.spec.ts b/src/json-size/__tests__/fuzz.spec.ts index 5a067411e2..41d4dd8560 100644 --- a/src/json-size/__tests__/fuzz.spec.ts +++ b/src/json-size/__tests__/fuzz.spec.ts @@ -1,6 +1,6 @@ import {jsonSize} from '..'; import {RandomJson} from '../../json-random/RandomJson'; -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; const random = new RandomJson(); const iterations = 100; diff --git a/src/json-size/__tests__/testJsonSize.ts b/src/json-size/__tests__/testJsonSize.ts index eeae004be2..ae04fdfeb8 100644 --- a/src/json-size/__tests__/testJsonSize.ts +++ b/src/json-size/__tests__/testJsonSize.ts @@ -1,4 +1,4 @@ -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; export const testJsonSize = ( jsonSize: (val: unknown) => number, diff --git a/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts index 2c56ad4c02..cfb2fc23d6 100644 --- a/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/CborEncoderCodegenContext.spec.ts @@ -4,7 +4,7 @@ import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; const writer = new Writer(1); const encoder = new CborEncoder(writer); diff --git a/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts index 5ef8d2067d..a5747828a2 100644 --- a/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/JsonEncoderCodegenContext.spec.ts @@ -3,7 +3,7 @@ import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {parse} from '../../../../json-binary'; const encoder = new JsonEncoder(new Writer(16)); diff --git a/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts b/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts index e7f4cba553..64cf21bf65 100644 --- a/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts +++ b/src/json-type/codegen/binary/__tests__/MessagePackEncoderCodegenContext.spec.ts @@ -4,7 +4,7 @@ import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder' import {Type} from '../../../type'; import {testBinaryCodegen} from './testBinaryCodegen'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; const writer = new Writer(64); const encoder = new MsgPackEncoder(writer); diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts index 037105b016..9de300faf7 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts @@ -5,7 +5,7 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApiTests'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts index ec55936d1d..547953c523 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts @@ -6,7 +6,7 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApi import {StreamingRpcClient} from '../../../../common'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; diff --git a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts index 381b56665f..3972bf42f9 100644 --- a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts +++ b/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts @@ -6,7 +6,7 @@ import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApi import WebSocket from 'ws'; import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import {RpcMessageCodec} from '../../../../common/codec/types'; import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts index e67bc5121e..7cc051bb00 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts @@ -1,5 +1,5 @@ import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RequestCompleteMessage} from '../../../messages'; import {BinaryRpcMessageCodec} from '../BinaryRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts index 49424177ad..ff6a27bc26 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts @@ -1,6 +1,6 @@ import {BinaryRpcMessageCodec} from '..'; import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {messages} from '../../../messages/__tests__/fixtures'; const codec = new BinaryRpcMessageCodec(); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts index e406657c7f..72f56e48e8 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts @@ -11,10 +11,10 @@ import { } from '../../../messages'; import {RpcValue} from '../../../messages/Value'; import {decode} from '../decode'; -import {Reader} from '@jsonjoy.com/json-pack/lib/util/buffers/Reader'; -import {Uint8ArrayCut} from '@jsonjoy.com/json-pack/lib/util/buffers/Uint8ArrayCut'; +import {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; +import {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; const codec = new CborJsonValueCodec(new Writer(64)); const encoder = codec.encoder; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts index 789373112e..c43984911a 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts @@ -7,7 +7,7 @@ import { } from '../../../messages'; import {RpcValue} from '../../../messages/Value'; import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; const cborCodec = new CborJsonValueCodec(new Writer(64)); const encoder = cborCodec.encoder; diff --git a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts index 8a2f839f23..a30ac8eb7c 100644 --- a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts +++ b/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts @@ -1,5 +1,5 @@ import {BinaryRpcMessageCodec} from '..'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import { NotificationMessage, diff --git a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts index c1a2450534..9a35f725a5 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts @@ -1,5 +1,5 @@ import {MsgPackJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/msgpack'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RequestCompleteMessage} from '../../../messages'; import {CompactRpcMessageCodec} from '../CompactRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts index 4be20127eb..0c3119e21e 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts @@ -1,7 +1,7 @@ import {compactMessages} from './compact-messages'; import {CompactRpcMessageCodec} from '..'; import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {messages} from '../../../messages/__tests__/fixtures'; const codec = new CompactRpcMessageCodec(); diff --git a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts index bcb531186e..90950cb400 100644 --- a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts +++ b/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts @@ -1,5 +1,5 @@ import {CompactRpcMessageCodec} from '..'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; import { NotificationMessage, diff --git a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts index fd548252c5..f0a9cdc95a 100644 --- a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts +++ b/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts @@ -1,6 +1,6 @@ import {JsonRpc2RpcMessageCodec} from '..'; import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {ReactiveRpcMessage} from '../../../messages'; import {messages} from '../../../messages/__tests__/fixtures'; diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts index 9eefc5dd2c..61b97ffd95 100644 --- a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts +++ b/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts @@ -6,7 +6,7 @@ import {until} from '../../../../__tests__/util'; import {RpcValue} from '../../messages/Value'; import {RpcCodec} from '../../codec/RpcCodec'; import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RpcMessageCodecs} from '../../codec/RpcMessageCodecs'; test('on remote method execution, sends message over WebSocket only once', async () => { diff --git a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts b/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts index db3f7cc264..c9edeebbb2 100644 --- a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts +++ b/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts @@ -1,5 +1,5 @@ import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {RpcError} from '../RpcError'; import {RpcErrorType} from '../RpcErrorType'; diff --git a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts b/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts index e6aeee348e..c434c44be0 100644 --- a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts +++ b/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts @@ -3,8 +3,8 @@ import {WsServerConnection} from '../WsServerConnection'; import {WsFrameEncoder} from '../../codec/WsFrameEncoder'; import {until} from 'thingies'; import {WsFrameOpcode} from '../../codec'; -import {bufferToUint8Array} from '@jsonjoy.com/json-pack/lib/util/buffers/bufferToUint8Array'; -import {listToUint8} from '@jsonjoy.com/json-pack/lib/util/buffers/concat'; +import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; +import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; const setup = () => { const socket = new stream.PassThrough(); diff --git a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts index b52311c47b..ceece93f88 100644 --- a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts +++ b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts @@ -1,4 +1,4 @@ -import {b} from '@jsonjoy.com/json-pack/lib/util/buffers/b'; +import {b} from '@jsonjoy.com/util/lib/buffers/b'; import {HlcFactory} from '../../../hlc'; import {CidCasMemory} from '../../../store/cas/CidCasMemory'; import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; From 919e91ddf7dc8ec7a71877c5867bafcc517b83b9 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 00:59:14 +0200 Subject: [PATCH 18/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20benchmar?= =?UTF-8?q?k=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/__tests__/fuzzer/generate-trace.ts | 2 +- src/json-size/__bench__/json-size.ts | 2 +- src/json-type/__bench__/encode.ts | 2 +- src/server/__bench__/ping.bench.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/json-crdt/__tests__/fuzzer/generate-trace.ts b/src/json-crdt/__tests__/fuzzer/generate-trace.ts index 2d07cedee8..369e0c7fca 100644 --- a/src/json-crdt/__tests__/fuzzer/generate-trace.ts +++ b/src/json-crdt/__tests__/fuzzer/generate-trace.ts @@ -5,7 +5,7 @@ import {Patch} from '../../../json-crdt-patch'; import {Model} from '../../model'; import {JsonCrdtFuzzer} from './JsonCrdtFuzzer'; import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import * as fs from 'fs'; const sessionNum = 100; diff --git a/src/json-size/__bench__/json-size.ts b/src/json-size/__bench__/json-size.ts index c806dd5b77..845a2d0fdf 100644 --- a/src/json-size/__bench__/json-size.ts +++ b/src/json-size/__bench__/json-size.ts @@ -3,7 +3,7 @@ // npx ts-node src/json-size/__bench__/json-size.ts import * as Benchmark from 'benchmark'; -import {utf8Size} from '@jsonjoy.com/json-pack/lib/util/strings/utf8'; +import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; import {jsonSize, jsonSizeApprox} from '../json'; import {jsonSizeFast} from '../jsonSizeFast'; import {msgpackSizeFast} from '../msgpackSizeFast'; diff --git a/src/json-type/__bench__/encode.ts b/src/json-type/__bench__/encode.ts index 1b6c2b4945..f165be6d44 100644 --- a/src/json-type/__bench__/encode.ts +++ b/src/json-type/__bench__/encode.ts @@ -5,7 +5,7 @@ import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; import {CompiledBinaryEncoder} from '../codegen/types'; import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; const system = new TypeSystem(); const {t} = system; diff --git a/src/server/__bench__/ping.bench.ts b/src/server/__bench__/ping.bench.ts index 50ea60a779..cf44cd261e 100644 --- a/src/server/__bench__/ping.bench.ts +++ b/src/server/__bench__/ping.bench.ts @@ -4,7 +4,7 @@ import {Suite} from 'benchmark'; import {RpcPersistentClient, WebSocketChannel} from '../../reactive-rpc/common'; -import {Writer} from '@jsonjoy.com/json-pack/lib/util/buffers/Writer'; +import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; import {BinaryRpcMessageCodec} from '../../reactive-rpc/common/codec/binary'; import {CompactRpcMessageCodec} from '../../reactive-rpc/common/codec/compact'; import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; From 0d3c8c9148f2db4bb25408bc054e16ad8acc8d44 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Wed, 10 Apr 2024 11:34:04 +0200 Subject: [PATCH 19/42] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20improve=20create?= =?UTF-8?q?=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/routes/blocks/schema.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/server/routes/blocks/schema.ts b/src/server/routes/blocks/schema.ts index 3f9e09f8b4..7fb9483b5a 100644 --- a/src/server/routes/blocks/schema.ts +++ b/src/server/routes/blocks/schema.ts @@ -16,6 +16,8 @@ export const BlockSeq = t.num.options({ }); export type TBlock = ResolveType; + +// prettier-ignore export const Block = t.Object( t.prop('id', t.Ref('BlockId')), t.prop('seq', t.Ref('BlockSeq')), @@ -25,4 +27,23 @@ export const Block = t.Object( ); export type TBlockPatch = ResolveType; -export const BlockPatch = t.Object(t.prop('seq', t.num), t.prop('created', t.num), t.prop('blob', t.bin)); + +// prettier-ignore +export const BlockPatch = t.Object( + t.prop('seq', t.num).options({ + title: 'Patch Sequence Number', + description: 'The sequence number of the patch in the block. A monotonically increasing integer, starting from 0.', + }), + t.prop('created', t.num).options({ + title: 'Patch Creation Time', + description: 'The time when the patch was created, in milliseconds since the Unix epoch.' + + '\n\n' + + 'This time is set by the server when the patch received and stored on the server. If you ' + + 'want to also store the time when the patch was created by the user, you can include this ' + + 'information in the patch blob itself.', + }), + t.prop('blob', t.bin).options({ + title: 'Patch Blob', + description: 'The binary data of the patch. The format of the data is defined by the patch type.', + }), +); From 166f20999e92de2dee2dc12e54c9a1ca4f50c36c Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Wed, 10 Apr 2024 11:34:20 +0200 Subject: [PATCH 20/42] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20initialize=20servi?= =?UTF-8?q?ce=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/routes/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index 8d51c1f401..812c108169 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -4,7 +4,7 @@ import {RpcValue} from '../../reactive-rpc/common/messages/Value'; import {ObjectValueCaller} from '../../reactive-rpc/common/rpc/caller/ObjectValueCaller'; import {system} from './system'; import {ObjectValue} from '../../json-type-value/ObjectValue'; -import type {Services} from '../services/Services'; +import {Services} from '../services/Services'; import type {RouteDeps} from './types'; export const createRouter = (services: Services) => { @@ -18,7 +18,7 @@ export const createRouter = (services: Services) => { return routes(deps)(router); }; -export const createCaller = (services: Services) => { +export const createCaller = (services: Services = new Services()) => { const router = createRouter(services); const caller = new ObjectValueCaller({ router, From d7aa2733be684516f427a8b8ad985c79bd8d9f25 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Wed, 10 Apr 2024 11:34:58 +0200 Subject: [PATCH 21/42] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20improve=20RPC=20ty?= =?UTF-8?q?pes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/reactive-rpc/common/index.ts | 1 + .../common/testing/buildE2eClient.ts | 25 +++---------------- src/reactive-rpc/common/types.ts | 19 ++++++++++++++ src/server/index.ts | 2 ++ 4 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 src/reactive-rpc/common/types.ts diff --git a/src/reactive-rpc/common/index.ts b/src/reactive-rpc/common/index.ts index d988e9bd86..0859fd73d9 100644 --- a/src/reactive-rpc/common/index.ts +++ b/src/reactive-rpc/common/index.ts @@ -1,3 +1,4 @@ +export * from './types'; export * from './messages'; export * from './channel'; export * from './rpc'; diff --git a/src/reactive-rpc/common/testing/buildE2eClient.ts b/src/reactive-rpc/common/testing/buildE2eClient.ts index 937a200fb4..0958b5d727 100644 --- a/src/reactive-rpc/common/testing/buildE2eClient.ts +++ b/src/reactive-rpc/common/testing/buildE2eClient.ts @@ -6,14 +6,8 @@ import {RpcCodecs} from '../codec/RpcCodecs'; import {RpcMessageCodecs} from '../codec/RpcMessageCodecs'; import {ReactiveRpcClientMessage, ReactiveRpcMessage, ReactiveRpcServerMessage} from '../messages'; import {RpcMessageStreamProcessor, StreamingRpcClient, TypedRpcClient} from '../rpc'; -import type {FunctionStreamingType, FunctionType} from '../../../json-type/type/classes'; -import type {Observable} from 'rxjs'; -import type {ResolveType} from '../../../json-type'; -import type {TypeRouter} from '../../../json-type/system/TypeRouter'; -import type {TypeRouterCaller} from '../rpc/caller/TypeRouterCaller'; import type {RpcCaller} from '../rpc/caller/RpcCaller'; -import type {ObjectValueCaller} from '../rpc/caller/ObjectValueCaller'; -import type {ObjectValue, ObjectValueToTypeMap, UnObjectType} from '../../../json-type-value/ObjectValue'; +import type {CallerToMethods} from '../types'; export interface BuildE2eClientOptions { /** @@ -67,7 +61,7 @@ export interface BuildE2eClientOptions { token?: string; } -export const buildE2eClient = >(caller: Caller, opt: BuildE2eClientOptions) => { +export const buildE2eClient = >(caller: Caller, opt: BuildE2eClientOptions = {}) => { const writer = opt.writer ?? new Writer(Fuzzer.randomInt2(opt.writerDefaultBufferKb ?? [4, 4]) * 1024); const codecs = new RpcCodecs(new Codecs(writer), new RpcMessageCodecs()); const ctx = new ConnectionContext( @@ -103,21 +97,8 @@ export const buildE2eClient = >(caller: Caller, op bufferSize: Fuzzer.randomInt2(opt.clientBufferSize ?? [1, 1]), bufferTime: Fuzzer.randomInt2(opt.clientBufferTime ?? [0, 0]), }); - type Router = UnTypeRouterCaller; - type Routes = UnTypeRouter; - type Methods = {[K in keyof Routes]: UnwrapFunction}; - const typedClient = client as TypedRpcClient; + const typedClient = client as TypedRpcClient>; return { client: typedClient, }; }; - -type UnTypeRouterCaller = T extends TypeRouterCaller ? R : T extends ObjectValueCaller ? R : never; -type UnTypeRouter = - T extends TypeRouter ? R : T extends ObjectValue ? ObjectValueToTypeMap> : never; -type UnwrapFunction = - F extends FunctionType - ? (req: ResolveType) => Promise> - : F extends FunctionStreamingType - ? (req$: Observable>) => Observable> - : never; diff --git a/src/reactive-rpc/common/types.ts b/src/reactive-rpc/common/types.ts new file mode 100644 index 0000000000..a1b2f96f0e --- /dev/null +++ b/src/reactive-rpc/common/types.ts @@ -0,0 +1,19 @@ +import type {Observable} from "rxjs"; +import type {FunctionStreamingType, FunctionType, ResolveType} from "../../json-type"; +import type {ObjectValue, ObjectValueToTypeMap, UnObjectType} from "../../json-type-value/ObjectValue"; +import type {TypeRouter} from "../../json-type/system/TypeRouter"; +import type {ObjectValueCaller} from "./rpc/caller/ObjectValueCaller"; +import type {RpcCaller} from "./rpc/caller/RpcCaller"; +import type {TypeRouterCaller} from "./rpc/caller/TypeRouterCaller"; + +export type CallerToMethods> = {[K in keyof UnTypeRouter>]: UnwrapFunction>[K]>}; + +type UnTypeRouterCaller = T extends TypeRouterCaller ? R : T extends ObjectValueCaller ? R : never; +type UnTypeRouter = + T extends TypeRouter ? R : T extends ObjectValue ? ObjectValueToTypeMap> : never; +type UnwrapFunction = + F extends FunctionType + ? (req: ResolveType) => Promise> + : F extends FunctionStreamingType + ? (req$: Observable>) => Observable> + : never; diff --git a/src/server/index.ts b/src/server/index.ts index 0aef51667e..dfe587f508 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,6 +7,8 @@ import {Services} from './services/Services'; import type {MyCtx} from './services/types'; import {RpcServer} from '../reactive-rpc/server/http1/RpcServer'; +export type JsonJoyDemoRpcCaller = ReturnType['caller']; + const app = new RpcApp({ uws: App({}), caller: createCaller(new Services()).caller, From 05af834cafae1fdd4f0df6b2a3300431aaa6b676 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Wed, 10 Apr 2024 11:35:19 +0200 Subject: [PATCH 22/42] =?UTF-8?q?chore(json-crdt):=20=F0=9F=A4=96=20start?= =?UTF-8?q?=20RemoteHistoryServer=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/RemoteHistoryServer.ts | 68 +++++++++++++++++++ .../__tests__/RemoteHistoryServer.spec.ts | 26 +++++++ src/json-crdt-repo/remote/types.ts | 23 +++---- src/json-crdt-repo/types.ts | 34 ---------- 4 files changed, 103 insertions(+), 48 deletions(-) create mode 100644 src/json-crdt-repo/remote/RemoteHistoryServer.ts create mode 100644 src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts diff --git a/src/json-crdt-repo/remote/RemoteHistoryServer.ts b/src/json-crdt-repo/remote/RemoteHistoryServer.ts new file mode 100644 index 0000000000..5f89a5a009 --- /dev/null +++ b/src/json-crdt-repo/remote/RemoteHistoryServer.ts @@ -0,0 +1,68 @@ +import {Patch} from "../../json-crdt-patch"; +import {Log} from "../../json-crdt/log/Log"; +import {JsonJoyDemoRpcCaller} from '../../server'; +import {CallerToMethods, TypedRpcClient} from '../../reactive-rpc/common'; +import type {RemoteHistory} from "./types"; +import {Model} from "../../json-crdt/model"; + +type Methods = CallerToMethods; + +export type Cursor = number; + +export class RemoteHistoryServer implements RemoteHistory { + constructor (protected readonly client: TypedRpcClient) {} + + public async create(id: string, patches: Patch[]): Promise { + await this.client.call('blocks.create', { + id, + patches: patches.map((patch, seq) => ({ + // TODO: seq and created can be skipped in create call. + seq, + created: Date.now(), + blob: patch.toBinary(), + })), + }); + } + + /** + * Load latest state of the model, and any unmerged "tip" of patches + * it might have. + * + * @todo Maybe `state` and `tip` should be serialized to JSON? + */ + public async read(id: string): Promise<{cursor: Cursor, log: Log}> { + const {block, patches} = await this.client.call('blocks.get', {id}); + const log = new Log(() => Model.fromBinary(block.blob)); + for (const patch of patches) log.end.applyPatch(Patch.fromBinary(patch.blob)); + // TODO: Preserver block metadata: block.created, block.updated, block.seq. + // TODO: Preserver patch metadata: patch.created, patch.seq. + return { + cursor: block.seq, + log, + }; + } + + public async scanAhead(id: string, cursor: Cursor): Promise<{cursor: Cursor, tip: Patch[]}> { + throw new Error('Method not implemented.'); + } + + public async scanBehind(id: string, cursor: Cursor): Promise<{cursor: Cursor, log: Log}> { + throw new Error('Method not implemented.'); + } + + public async update(id: string, cursor: Cursor, patches: Patch[]): Promise { + throw new Error('Method not implemented.'); + } + + public async delete?(id: string): Promise { + throw new Error('Method not implemented.'); + } + + /** + * Subscribe to the latest changes to the model. + * @param callback + */ + public listen(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts new file mode 100644 index 0000000000..8351776f76 --- /dev/null +++ b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts @@ -0,0 +1,26 @@ +import {Model} from '../../../json-crdt/model'; +import {buildE2eClient} from '../../../reactive-rpc/common/testing/buildE2eClient'; +import {createCaller} from '../../../server/routes/index'; +import {RemoteHistoryServer} from '../RemoteHistoryServer'; + +const setup = () => { + const {caller, router} = createCaller(); + const {client} = buildE2eClient(caller); + const remote = new RemoteHistoryServer(client); + + return { + caller, + router, + client, + remote, + }; +}; + +test('...', async () => { + const {remote} = await setup(); + const model = Model.withLogicalClock(); + model.api.root({foo: 'bar'}); + const patch = model.api.flush(); + + await remote.create('1234567890', [patch]); +}); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index 53c8404e10..ffef8b67ad 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -1,20 +1,13 @@ +import {Log} from '../../json-crdt/log/Log'; import type {Patch} from '../../json-crdt-patch'; -import type {Model} from '../../json-crdt/model'; /** * A history of patches that have been applied to a model, stored on the * "remote": (1) server; (2) content addressable storage; or (3) somewhere in a * peer-to-peer network. - * - * Cases: - * - * - `RemoteHistoryServer` - * - `RemoteHistoryServerIdempotent` - * - `RemoteHistoryCAS` - * - `RemoteHistoryP2P` */ export interface RemoteHistory { - create(id: string, patches: Patch[], start?: Model): Promise; + create(id: string, patches: Patch[]): Promise; /** * Load latest state of the model, and any unmerged "tip" of patches @@ -22,17 +15,19 @@ export interface RemoteHistory { * * @todo Maybe `state` and `tip` should be serialized to JSON? */ - loadLatest(id: string): Promise<[cursor: Cursor, state: Model]>; + read(id: string): Promise<{cursor: string, log: Log}>; - loadAfter(id: string, cursor: Cursor): Promise<[cursor: Cursor, tip: Patch[]]>; + scanAhead(id: string, cursor: Cursor): Promise<{cursor: Cursor, tip: Patch[]}>; - loadBefore(id: string, cursor: Cursor): Promise<[cursor: Cursor, state: Model, tip: Patch[]]>; + scanBehind(id: string, cursor: Cursor): Promise<{cursor: Cursor, log: Log}>; - apply(id: string, patches: Patch[]): Promise; + update(id: string, cursor: Cursor, patches: Patch[]): Promise; + + delete?(id: string): Promise; /** * Subscribe to the latest changes to the model. * @param callback */ - subscribe(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void; + listen(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void; } diff --git a/src/json-crdt-repo/types.ts b/src/json-crdt-repo/types.ts index 556859e1e0..fa8a7a576f 100644 --- a/src/json-crdt-repo/types.ts +++ b/src/json-crdt-repo/types.ts @@ -2,40 +2,6 @@ import type {Patch} from '../json-crdt-patch'; import type {Log} from '../json-crdt/log/Log'; import type {Model} from '../json-crdt/model'; -/** - * A history of patches that have been applied to a model, stored on the - * "remote": (1) server; (2) content addressable storage; or (3) peer-to-peer - * network. - * - * Cases: - * - * - `RemoteHistoryServer` - * - `RemoteHistoryServerIdempotent` - * - `RemoteHistoryCAS` - * - `RemoteHistoryP2P` - */ -export interface RemoteHistory { - /** - * Load latest state of the model, and any unmerged "tip" of patches - * it might have. - * - * @todo Maybe `state` and `tip` should be serialized to JSON? - */ - loadLatest(id: string): Promise<[cursor: Cursor, state: Model]>; - - loadAfter(id: string, cursor: Cursor): Promise<[cursor: Cursor, tip: Patch[]]>; - - loadBefore(id: string, cursor: Cursor): Promise<[cursor: Cursor, state: Model, tip: Patch[]]>; - - apply(id: string, patches: Patch[]): Promise; - - /** - * Subscribe to the latest changes to the model. - * @param callback - */ - subscribe(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void; -} - export interface LocalHistory { create(collection: string[], log: Log): Promise<{id: string}>; read(collection: string[], id: string): Promise<{log: Log; cursor: string}>; From 1b17c684c124ab42e008f7eef71a0300d900001a Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 14:26:01 +0200 Subject: [PATCH 23/42] =?UTF-8?q?feat(json-crdt):=20=F0=9F=8E=B8=20progres?= =?UTF-8?q?s=20on=20remote=20history=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/RemoteHistoryDemoServer.ts | 97 +++++++++++++++++++ .../remote/RemoteHistoryServer.ts | 68 ------------- .../__tests__/RemoteHistoryServer.spec.ts | 4 +- src/json-crdt-repo/remote/types.ts | 25 +++-- 4 files changed, 114 insertions(+), 80 deletions(-) create mode 100644 src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts delete mode 100644 src/json-crdt-repo/remote/RemoteHistoryServer.ts diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts new file mode 100644 index 0000000000..94e6586c27 --- /dev/null +++ b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts @@ -0,0 +1,97 @@ +import type {CallerToMethods, TypedRpcClient} from '../../reactive-rpc/common'; +import type {JsonJoyDemoRpcCaller} from '../../server'; +import type {RemoteHistory, RemoteModel, RemotePatch} from "./types"; + +type Methods = CallerToMethods; + +export type Cursor = number; + +export interface RemoteServerModel extends RemoteModel { + seq: number; + created: number; + updated: number; +} + +export interface RemoteServerPatch extends RemotePatch { + seq: number; +} + +export class RemoteHistoryDemoServer implements RemoteHistory { + constructor (protected readonly client: TypedRpcClient) {} + + public async create(id: string, patches: RemotePatch[]): Promise { + await this.client.call('blocks.create', { + id, + patches: patches.map((patch, seq) => ({ + // TODO: seq and created should be set on server. (And returned back?) + seq, + created: Date.now(), + blob: patch.blob, + })), + }); + } + + /** + * Load latest state of the model, and any unmerged "tip" of patches + * it might have. + */ + public async read(id: string): Promise<{cursor: Cursor, model: RemoteServerModel, patches: RemoteServerPatch[]}> { + const {block, patches} = await this.client.call('blocks.get', {id}); + return { + cursor: block.seq, + model: block, + patches, + }; + } + + public async scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, patches: RemoteServerPatch[]}> { + const limit = 100; + const res = await this.client.call('blocks.history', { + id, + min: cursor, + max: cursor + limit, + }); + if (res.patches.length === 0) { + return { + cursor, + patches: [], + }; + } + return { + cursor: res.patches[res.patches.length - 1].seq, + patches: res.patches, + }; + } + + public async scanBwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, model: RemoteServerModel, patches: RemoteServerPatch[]}> { + throw new Error('The "blocks.history" should be able to return starting model.'); + } + + public async update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor, patches: RemoteServerPatch[]}> { + + const res = await this.client.call('blocks.edit', { + id, + patches: patches.map((patch, seq) => ({ + seq, + created: Date.now(), + blob: patch.blob, + })), + }); + return { + cursor: res.patches.length ? res.patches[res.patches.length - 1].seq : cursor, + patches: res.patches, + }; + } + + public async delete?(id: string): Promise { + await this.client.call('blocks.remove', {id}); + } + + /** + * Subscribe to the latest changes to the model. + * @param callback + */ + public listen(id: string, cursor: Cursor, callback: (changes: RemoteServerPatch[]) => void): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/json-crdt-repo/remote/RemoteHistoryServer.ts b/src/json-crdt-repo/remote/RemoteHistoryServer.ts deleted file mode 100644 index 5f89a5a009..0000000000 --- a/src/json-crdt-repo/remote/RemoteHistoryServer.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {Patch} from "../../json-crdt-patch"; -import {Log} from "../../json-crdt/log/Log"; -import {JsonJoyDemoRpcCaller} from '../../server'; -import {CallerToMethods, TypedRpcClient} from '../../reactive-rpc/common'; -import type {RemoteHistory} from "./types"; -import {Model} from "../../json-crdt/model"; - -type Methods = CallerToMethods; - -export type Cursor = number; - -export class RemoteHistoryServer implements RemoteHistory { - constructor (protected readonly client: TypedRpcClient) {} - - public async create(id: string, patches: Patch[]): Promise { - await this.client.call('blocks.create', { - id, - patches: patches.map((patch, seq) => ({ - // TODO: seq and created can be skipped in create call. - seq, - created: Date.now(), - blob: patch.toBinary(), - })), - }); - } - - /** - * Load latest state of the model, and any unmerged "tip" of patches - * it might have. - * - * @todo Maybe `state` and `tip` should be serialized to JSON? - */ - public async read(id: string): Promise<{cursor: Cursor, log: Log}> { - const {block, patches} = await this.client.call('blocks.get', {id}); - const log = new Log(() => Model.fromBinary(block.blob)); - for (const patch of patches) log.end.applyPatch(Patch.fromBinary(patch.blob)); - // TODO: Preserver block metadata: block.created, block.updated, block.seq. - // TODO: Preserver patch metadata: patch.created, patch.seq. - return { - cursor: block.seq, - log, - }; - } - - public async scanAhead(id: string, cursor: Cursor): Promise<{cursor: Cursor, tip: Patch[]}> { - throw new Error('Method not implemented.'); - } - - public async scanBehind(id: string, cursor: Cursor): Promise<{cursor: Cursor, log: Log}> { - throw new Error('Method not implemented.'); - } - - public async update(id: string, cursor: Cursor, patches: Patch[]): Promise { - throw new Error('Method not implemented.'); - } - - public async delete?(id: string): Promise { - throw new Error('Method not implemented.'); - } - - /** - * Subscribe to the latest changes to the model. - * @param callback - */ - public listen(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts index 8351776f76..cf0623d132 100644 --- a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts @@ -1,12 +1,12 @@ import {Model} from '../../../json-crdt/model'; import {buildE2eClient} from '../../../reactive-rpc/common/testing/buildE2eClient'; import {createCaller} from '../../../server/routes/index'; -import {RemoteHistoryServer} from '../RemoteHistoryServer'; +import {RemoteHistoryDemoServer} from '../RemoteHistoryServer'; const setup = () => { const {caller, router} = createCaller(); const {client} = buildE2eClient(caller); - const remote = new RemoteHistoryServer(client); + const remote = new RemoteHistoryDemoServer(client); return { caller, diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index ffef8b67ad..2e8ac571a4 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -1,13 +1,10 @@ -import {Log} from '../../json-crdt/log/Log'; -import type {Patch} from '../../json-crdt-patch'; - /** * A history of patches that have been applied to a model, stored on the * "remote": (1) server; (2) content addressable storage; or (3) somewhere in a * peer-to-peer network. */ -export interface RemoteHistory { - create(id: string, patches: Patch[]): Promise; +export interface RemoteHistory { + create(id: string, patches: RemotePatch[]): Promise; /** * Load latest state of the model, and any unmerged "tip" of patches @@ -15,13 +12,13 @@ export interface RemoteHistory { * * @todo Maybe `state` and `tip` should be serialized to JSON? */ - read(id: string): Promise<{cursor: string, log: Log}>; + read(id: string): Promise<{cursor: Cursor, model: M, patches: P[]}>; - scanAhead(id: string, cursor: Cursor): Promise<{cursor: Cursor, tip: Patch[]}>; + scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, patches: P[]}>; - scanBehind(id: string, cursor: Cursor): Promise<{cursor: Cursor, log: Log}>; + scanBwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, model: M, patches: P[]}>; - update(id: string, cursor: Cursor, patches: Patch[]): Promise; + update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor, patches: P[]}>; delete?(id: string): Promise; @@ -29,5 +26,13 @@ export interface RemoteHistory { * Subscribe to the latest changes to the model. * @param callback */ - listen(id: string, cursor: Cursor, callback: (changes: Patch[]) => void): void; + listen(id: string, cursor: Cursor, callback: (patches: P[]) => void): void; +} + +export interface RemoteModel { + blob: Uint8Array; +} + +export interface RemotePatch { + blob: Uint8Array; } From ac22950e6ab3cc90700eb0f7cbe6a4d2f5beeb6e Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 10 Apr 2024 14:26:31 +0200 Subject: [PATCH 24/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20todos=20for?= =?UTF-8?q?=20demo=20server=20APIS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/routes/blocks/methods/create.ts | 1 + src/server/routes/blocks/methods/edit.ts | 1 + src/server/routes/blocks/methods/get.ts | 3 ++- src/server/routes/blocks/methods/history.ts | 1 + src/server/routes/blocks/methods/remove.ts | 1 + src/server/routes/routes.ts | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/server/routes/blocks/methods/create.ts b/src/server/routes/blocks/methods/create.ts index 2f7dec6121..303dbf153a 100644 --- a/src/server/routes/blocks/methods/create.ts +++ b/src/server/routes/blocks/methods/create.ts @@ -1,6 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId, BlockPatch} from '../schema'; +// TODO: Rename to "new", like "block.new"? export const create = ({t, services}: RouteDeps) => (r: Router) => { diff --git a/src/server/routes/blocks/methods/edit.ts b/src/server/routes/blocks/methods/edit.ts index cea15b0ebd..c4c938a325 100644 --- a/src/server/routes/blocks/methods/edit.ts +++ b/src/server/routes/blocks/methods/edit.ts @@ -1,6 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId, BlockPatch} from '../schema'; +// TODO: Rename to "set"? export const edit = ({t, services}: RouteDeps) => (r: Router) => { diff --git a/src/server/routes/blocks/methods/get.ts b/src/server/routes/blocks/methods/get.ts index 7ff48d2d8a..ccd496774f 100644 --- a/src/server/routes/blocks/methods/get.ts +++ b/src/server/routes/blocks/methods/get.ts @@ -12,7 +12,8 @@ export const get = ); const Response = t.Object( - t.prop('block', t.Ref('Block').options({})), + // TODO: Rename this field to `model` or `state`. + t.prop('block', t.Ref('Block')), t.prop('patches', t.Array(t.Ref('BlockPatch'))).options({ title: 'Patches', description: 'The list of all patches.', diff --git a/src/server/routes/blocks/methods/history.ts b/src/server/routes/blocks/methods/history.ts index ea71ef4f2e..cdab447288 100644 --- a/src/server/routes/blocks/methods/history.ts +++ b/src/server/routes/blocks/methods/history.ts @@ -1,6 +1,7 @@ import type {BlockPatch, BlockId} from '../schema'; import type {RouteDeps, Router, RouterBase} from '../../types'; +// TODO: Rename to "scan". export const history = ({t, services}: RouteDeps) => (r: Router) => { diff --git a/src/server/routes/blocks/methods/remove.ts b/src/server/routes/blocks/methods/remove.ts index ae3400d397..e6a7cda856 100644 --- a/src/server/routes/blocks/methods/remove.ts +++ b/src/server/routes/blocks/methods/remove.ts @@ -1,6 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId} from '../schema'; +// TODO: rename to "del". export const remove = ({t, services}: RouteDeps) => (r: Router) => { diff --git a/src/server/routes/routes.ts b/src/server/routes/routes.ts index 3459273faf..5f2fc47fe8 100644 --- a/src/server/routes/routes.ts +++ b/src/server/routes/routes.ts @@ -11,5 +11,6 @@ export const routes = (d: RouteDeps) => >(r: ObjectVal ( util(d) ( pubsub(d) ( presence(d) + // TODO: rename "blocks" to "block", in all methods. ( blocks(d) ( r ))))); From 89d4851621ad0f5105fb65d115bebd8de3780f19 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 11 Apr 2024 01:34:01 +0200 Subject: [PATCH 25/42] =?UTF-8?q?test(json-crdt):=20=F0=9F=92=8D=20add=20r?= =?UTF-8?q?emote=20history=20smoke=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/RemoteHistoryServer.spec.ts | 28 +++++++++++++------ src/json-type-value/ObjectValue.ts | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts index cf0623d132..9cb3e3460c 100644 --- a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts @@ -1,7 +1,7 @@ import {Model} from '../../../json-crdt/model'; import {buildE2eClient} from '../../../reactive-rpc/common/testing/buildE2eClient'; import {createCaller} from '../../../server/routes/index'; -import {RemoteHistoryDemoServer} from '../RemoteHistoryServer'; +import {RemoteHistoryDemoServer} from '../RemoteHistoryDemoServer'; const setup = () => { const {caller, router} = createCaller(); @@ -9,18 +9,28 @@ const setup = () => { const remote = new RemoteHistoryDemoServer(client); return { - caller, router, + caller, client, remote, }; }; -test('...', async () => { - const {remote} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({foo: 'bar'}); - const patch = model.api.flush(); - - await remote.create('1234567890', [patch]); +let cnt = 0; +const genId = () => Math.random().toString(36).slice(2) + '-' + Date.now().toString(36) + '-' + cnt++; + +describe('.create()', () => { + test('can create a block with a simple patch', async () => { + const {remote, caller} = await setup(); + const model = Model.withLogicalClock(); + model.api.root({foo: 'bar'}); + const patch = model.api.flush(); + const blob = patch.toBinary(); + const id = genId(); + await remote.create(id, [{blob}]); + const {data} = await caller.call('blocks.get', {id}, {}); + // console.log(data.patches); + const model2 = Model.fromBinary(data.block.blob); + expect(model2.view()).toEqual({foo: 'bar'}); + }); }); diff --git a/src/json-type-value/ObjectValue.ts b/src/json-type-value/ObjectValue.ts index 85127538b5..795b74e936 100644 --- a/src/json-type-value/ObjectValue.ts +++ b/src/json-type-value/ObjectValue.ts @@ -4,7 +4,7 @@ import {TypeSystem} from '../json-type/system/TypeSystem'; import type {ResolveType} from '../json-type'; import type * as classes from '../json-type/type'; import type * as ts from '../json-type/typescript/types'; -import {TypeBuilder} from '../json-type/type/TypeBuilder'; +import type {TypeBuilder} from '../json-type/type/TypeBuilder'; export type UnObjectType = T extends classes.ObjectType ? U : never; export type UnObjectValue = T extends ObjectValue ? U : never; From 5be2b24cb624ca9a20d12f9318097cf7d29de908 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 11 Apr 2024 01:52:30 +0200 Subject: [PATCH 26/42] =?UTF-8?q?refactor(reactive-rpc):=20=F0=9F=92=A1=20?= =?UTF-8?q?update=20block=20RPC=20command=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/RemoteHistoryDemoServer.ts | 10 +-- .../__tests__/RemoteHistoryServer.spec.ts | 2 +- .../{blocks.spec.ts => block.spec.ts} | 90 +++++++++---------- src/server/routes/{blocks => block}/index.ts | 18 ++-- .../remove.ts => block/methods/del.ts} | 5 +- .../routes/{blocks => block}/methods/get.ts | 2 +- .../{blocks => block}/methods/listen.ts | 2 +- .../create.ts => block/methods/new.ts} | 5 +- .../history.ts => block/methods/scan.ts} | 5 +- .../methods/edit.ts => block/methods/upd.ts} | 5 +- src/server/routes/{blocks => block}/schema.ts | 0 src/server/routes/routes.ts | 4 +- 12 files changed, 72 insertions(+), 76 deletions(-) rename src/server/__tests__/{blocks.spec.ts => block.spec.ts} (83%) rename src/server/routes/{blocks => block}/index.ts (68%) rename src/server/routes/{blocks/methods/remove.ts => block/methods/del.ts} (85%) rename src/server/routes/{blocks => block}/methods/get.ts (94%) rename src/server/routes/{blocks => block}/methods/listen.ts (96%) rename src/server/routes/{blocks/methods/create.ts => block/methods/new.ts} (87%) rename src/server/routes/{blocks/methods/history.ts => block/methods/scan.ts} (90%) rename src/server/routes/{blocks/methods/edit.ts => block/methods/upd.ts} (93%) rename src/server/routes/{blocks => block}/schema.ts (100%) diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts index 94e6586c27..57314b18c6 100644 --- a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts +++ b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts @@ -20,7 +20,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory) {} public async create(id: string, patches: RemotePatch[]): Promise { - await this.client.call('blocks.create', { + await this.client.call('block.new', { id, patches: patches.map((patch, seq) => ({ // TODO: seq and created should be set on server. (And returned back?) @@ -36,7 +36,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { - const {block, patches} = await this.client.call('blocks.get', {id}); + const {block, patches} = await this.client.call('block.get', {id}); return { cursor: block.seq, model: block, @@ -46,7 +46,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { const limit = 100; - const res = await this.client.call('blocks.history', { + const res = await this.client.call('block.scan', { id, min: cursor, max: cursor + limit, @@ -69,7 +69,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { - const res = await this.client.call('blocks.edit', { + const res = await this.client.call('block.upd', { id, patches: patches.map((patch, seq) => ({ seq, @@ -84,7 +84,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { - await this.client.call('blocks.remove', {id}); + await this.client.call('block.del', {id}); } /** diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts index 9cb3e3460c..07c065d4aa 100644 --- a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts @@ -28,7 +28,7 @@ describe('.create()', () => { const blob = patch.toBinary(); const id = genId(); await remote.create(id, [{blob}]); - const {data} = await caller.call('blocks.get', {id}, {}); + const {data} = await caller.call('block.get', {id}, {}); // console.log(data.patches); const model2 = Model.fromBinary(data.block.blob); expect(model2.view()).toEqual({foo: 'bar'}); diff --git a/src/server/__tests__/blocks.spec.ts b/src/server/__tests__/block.spec.ts similarity index 83% rename from src/server/__tests__/blocks.spec.ts rename to src/server/__tests__/block.spec.ts index a000e4639e..08f3eb992c 100644 --- a/src/server/__tests__/blocks.spec.ts +++ b/src/server/__tests__/block.spec.ts @@ -3,12 +3,12 @@ import {RpcErrorCodes} from '../../reactive-rpc/common/rpc/caller'; import {setup} from './setup'; import {tick, until} from '../../__tests__/util'; -describe('blocks.*', () => { - describe('blocks.create', () => { +describe('block.*', () => { + describe('block.new', () => { test('can create an empty block', async () => { const {call} = setup(); - await call('blocks.create', {id: 'my-block', patches: []}); - const {block} = await call('blocks.get', {id: 'my-block'}); + await call('block.new', {id: 'my-block', patches: []}); + const {block} = await call('block.get', {id: 'my-block'}); expect(block).toMatchObject({ id: 'my-block', seq: -1, @@ -32,7 +32,7 @@ describe('blocks.*', () => { age: 26, }); const patch2 = model.api.flush(); - await call('blocks.create', { + await call('block.new', { id: '123412341234', patches: [ { @@ -47,7 +47,7 @@ describe('blocks.*', () => { }, ], }); - const {block} = await call('blocks.get', {id: '123412341234'}); + const {block} = await call('block.get', {id: '123412341234'}); expect(block).toMatchObject({ id: '123412341234', seq: 1, @@ -63,15 +63,15 @@ describe('blocks.*', () => { }); }); - describe('blocks.remove', () => { + describe('block.remove', () => { test('can remove an existing block', async () => { const {call} = setup(); - await call('blocks.create', {id: 'my-block', patches: []}); - const {block} = await call('blocks.get', {id: 'my-block'}); + await call('block.new', {id: 'my-block', patches: []}); + const {block} = await call('block.get', {id: 'my-block'}); expect(block.id).toBe('my-block'); - await call('blocks.remove', {id: 'my-block'}); + await call('block.del', {id: 'my-block'}); try { - await call('blocks.get', {id: 'my-block'}); + await call('block.get', {id: 'my-block'}); throw new Error('not this error'); } catch (err: any) { expect(err.errno).toBe(RpcErrorCodes.NOT_FOUND); @@ -79,7 +79,7 @@ describe('blocks.*', () => { }); }); - describe('blocks.edit', () => { + describe('block.upd', () => { test('can edit a document sequentially', async () => { const {call} = setup(); const id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; @@ -88,7 +88,7 @@ describe('blocks.*', () => { text: 'Hell', }); const patch1 = model.api.flush(); - await call('blocks.create', { + await call('block.new', { id, patches: [ { @@ -102,7 +102,7 @@ describe('blocks.*', () => { const patch2 = model.api.flush(); model.api.str(['text']).ins(5, ' World'); const patch3 = model.api.flush(); - await call('blocks.edit', { + await call('block.upd', { id, patches: [ { @@ -117,7 +117,7 @@ describe('blocks.*', () => { }, ], }); - const block2 = await call('blocks.get', {id}); + const block2 = await call('block.get', {id}); expect(Model.fromBinary(block2.block.blob).view()).toStrictEqual({ text: 'Hello World', }); @@ -125,7 +125,7 @@ describe('blocks.*', () => { const patch4 = model.api.flush(); model.api.str(['text']).ins(12, '!'); const patch5 = model.api.flush(); - await call('blocks.edit', { + await call('block.upd', { id, patches: [ { @@ -140,7 +140,7 @@ describe('blocks.*', () => { }, ], }); - const block3 = await call('blocks.get', {id}); + const block3 = await call('block.get', {id}); expect(Model.fromBinary(block3.block.blob).view()).toStrictEqual({ text: 'Hello, World!', }); @@ -156,7 +156,7 @@ describe('blocks.*', () => { text: 'Hell', }); const patch1 = model.api.flush(); - await call('blocks.create', { + await call('block.new', { id, patches: [ { @@ -168,11 +168,11 @@ describe('blocks.*', () => { }); // User 2 - const block2 = await call('blocks.get', {id}); + const block2 = await call('block.get', {id}); const model2 = Model.fromBinary(block2.block.blob).fork(); model2.api.str(['text']).ins(4, ' yeah!'); const patch2User2 = model2.api.flush(); - await call('blocks.edit', { + await call('block.upd', { id, patches: [ { @@ -184,7 +184,7 @@ describe('blocks.*', () => { }); expect(model2.view()).toStrictEqual({text: 'Hell yeah!'}); - const block3 = await call('blocks.get', {id}); + const block3 = await call('block.get', {id}); const model3 = Model.fromBinary(block3.block.blob).fork(); expect(model3.view()).toStrictEqual({text: 'Hell yeah!'}); @@ -193,7 +193,7 @@ describe('blocks.*', () => { const patch2 = model.api.flush(); model.api.str(['text']).ins(5, ' World'); const patch3 = model.api.flush(); - const {patches} = await call('blocks.edit', { + const {patches} = await call('block.upd', { id, patches: [ { @@ -209,7 +209,7 @@ describe('blocks.*', () => { ], }); - const block4 = await call('blocks.get', {id}); + const block4 = await call('block.get', {id}); const model4 = Model.fromBinary(block4.block.blob).fork(); expect(model4.view()).not.toStrictEqual({text: 'Hell yeah!'}); }); @@ -224,7 +224,7 @@ describe('blocks.*', () => { text: 'Hell', }); const patch1 = model.api.flush(); - await call('blocks.create', { + await call('block.new', { id, patches: [ { @@ -236,11 +236,11 @@ describe('blocks.*', () => { }); // User 2 - const block2 = await call('blocks.get', {id}); + const block2 = await call('block.get', {id}); const model2 = Model.fromBinary(block2.block.blob).fork(); model2.api.str(['text']).ins(4, ' yeah!'); const patch2User2 = model2.api.flush(); - await call('blocks.edit', { + await call('block.upd', { id, patches: [ { @@ -256,7 +256,7 @@ describe('blocks.*', () => { const patch2 = model.api.flush(); model.api.str(['text']).ins(5, ' World'); const patch3 = model.api.flush(); - const {patches} = await call('blocks.edit', { + const {patches} = await call('block.upd', { id, patches: [ { @@ -280,13 +280,13 @@ describe('blocks.*', () => { }); }); - describe('blocks.listen', () => { + describe('block.listen', () => { test('can listen for block changes', async () => { const {client} = setup(); - await client.call('blocks.create', {id: 'my-block', patches: []}); + await client.call('block.new', {id: 'my-block', patches: []}); await tick(11); const emits: any[] = []; - client.call$('blocks.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); + client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); const model = Model.withLogicalClock(); model.api.root({ text: 'Hell', @@ -294,7 +294,7 @@ describe('blocks.*', () => { const patch1 = model.api.flush(); await tick(12); expect(emits.length).toBe(0); - await client.call('blocks.edit', { + await client.call('block.upd', { id: 'my-block', patches: [{seq: 0, created: Date.now(), blob: patch1.toBinary()}], }); @@ -308,7 +308,7 @@ describe('blocks.*', () => { const patch2 = model.api.flush(); await tick(12); expect(emits.length).toBe(1); - await client.call('blocks.edit', { + await client.call('block.upd', { id: 'my-block', patches: [{seq: 1, created: Date.now(), blob: patch2.toBinary()}], }); @@ -321,7 +321,7 @@ describe('blocks.*', () => { test('can subscribe before block is created', async () => { const {client} = setup(); const emits: any[] = []; - client.call$('blocks.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); + client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); const model = Model.withLogicalClock(); model.api.root({ text: 'Hell', @@ -329,7 +329,7 @@ describe('blocks.*', () => { const patch1 = model.api.flush(); await tick(12); expect(emits.length).toBe(0); - await client.call('blocks.create', { + await client.call('block.new', { id: 'my-block', patches: [ { @@ -349,18 +349,18 @@ describe('blocks.*', () => { test('can receive deletion events', async () => { const {client} = setup(); const emits: any[] = []; - client.call$('blocks.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); - await client.call('blocks.create', {id: 'my-block', patches: []}); + client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); + await client.call('block.new', {id: 'my-block', patches: []}); await until(() => emits.length === 1); expect(emits[0].block.seq).toBe(-1); await tick(3); - await client.call('blocks.remove', {id: 'my-block'}); + await client.call('block.del', {id: 'my-block'}); await until(() => emits.length === 2); expect(emits[1].deleted).toBe(true); }); }); - describe('blocks.history', () => { + describe('block.history', () => { test('can retrieve change history', async () => { const {client} = setup(); const model = Model.withLogicalClock(); @@ -368,7 +368,7 @@ describe('blocks.*', () => { text: 'Hell', }); const patch1 = model.api.flush(); - await client.call('blocks.create', { + await client.call('block.new', { id: 'my-block', patches: [ { @@ -385,7 +385,7 @@ describe('blocks.*', () => { age: 26, }); const patch3 = model.api.flush(); - await client.call('blocks.edit', { + await client.call('block.upd', { id: 'my-block', patches: [ { @@ -400,7 +400,7 @@ describe('blocks.*', () => { }, ], }); - const history = await client.call('blocks.history', {id: 'my-block', min: 0, max: 2}); + const history = await client.call('block.scan', {id: 'my-block', min: 0, max: 2}); expect(history).toMatchObject({ patches: [ { @@ -423,7 +423,7 @@ describe('blocks.*', () => { }); }); - describe('blocks.get', () => { + describe('block.get', () => { test('returns whole history when block is loaded', async () => { const {client} = setup(); const model = Model.withLogicalClock(); @@ -431,7 +431,7 @@ describe('blocks.*', () => { text: 'Hell', }); const patch1 = model.api.flush(); - await client.call('blocks.create', { + await client.call('block.new', { id: 'my-block', patches: [ { @@ -447,7 +447,7 @@ describe('blocks.*', () => { age: 26, }); const patch3 = model.api.flush(); - await client.call('blocks.edit', { + await client.call('block.upd', { id: 'my-block', patches: [ { @@ -462,7 +462,7 @@ describe('blocks.*', () => { }, ], }); - const result = await client.call('blocks.get', {id: 'my-block'}); + const result = await client.call('block.get', {id: 'my-block'}); expect(result).toMatchObject({ block: expect.any(Object), patches: [ diff --git a/src/server/routes/blocks/index.ts b/src/server/routes/block/index.ts similarity index 68% rename from src/server/routes/blocks/index.ts rename to src/server/routes/block/index.ts index b219a7ce78..9306885880 100644 --- a/src/server/routes/blocks/index.ts +++ b/src/server/routes/block/index.ts @@ -1,13 +1,13 @@ -import {create} from './methods/create'; +import {new_} from './methods/new'; import {get} from './methods/get'; -import {remove} from './methods/remove'; -import {edit} from './methods/edit'; +import {upd} from './methods/upd'; +import {del} from './methods/del'; +import {scan} from './methods/scan'; import {listen} from './methods/listen'; import {Block, BlockId, BlockPatch, BlockSeq} from './schema'; -import {history} from './methods/history'; import type {RouteDeps, Router, RouterBase} from '../types'; -export const blocks = +export const block = (d: RouteDeps) => (r: Router) => { const {system} = d; @@ -19,11 +19,11 @@ export const blocks = // prettier-ignore return ( - ( create(d) + ( new_(d) ( get(d) - ( remove(d) - ( edit(d) + ( upd(d) + ( del(d) ( listen(d) - ( history(d) + ( scan(d) ( r )))))))); }; diff --git a/src/server/routes/blocks/methods/remove.ts b/src/server/routes/block/methods/del.ts similarity index 85% rename from src/server/routes/blocks/methods/remove.ts rename to src/server/routes/block/methods/del.ts index e6a7cda856..f3e28617c8 100644 --- a/src/server/routes/blocks/methods/remove.ts +++ b/src/server/routes/block/methods/del.ts @@ -1,8 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId} from '../schema'; -// TODO: rename to "del". -export const remove = +export const del = ({t, services}: RouteDeps) => (r: Router) => { const Request = t.Object( @@ -20,7 +19,7 @@ export const remove = description: 'Fetches a block by ID.', }); - return r.prop('blocks.remove', Func, async ({id}) => { + return r.prop('block.del', Func, async ({id}) => { await services.blocks.remove(id); return {}; }); diff --git a/src/server/routes/blocks/methods/get.ts b/src/server/routes/block/methods/get.ts similarity index 94% rename from src/server/routes/blocks/methods/get.ts rename to src/server/routes/block/methods/get.ts index ccd496774f..2da94b63a0 100644 --- a/src/server/routes/blocks/methods/get.ts +++ b/src/server/routes/block/methods/get.ts @@ -26,7 +26,7 @@ export const get = description: 'Fetches a block by ID.', }); - return r.prop('blocks.get', Func, async ({id}) => { + return r.prop('block.get', Func, async ({id}) => { const {block, patches} = await services.blocks.get(id); return { block, diff --git a/src/server/routes/blocks/methods/listen.ts b/src/server/routes/block/methods/listen.ts similarity index 96% rename from src/server/routes/blocks/methods/listen.ts rename to src/server/routes/block/methods/listen.ts index f9272228af..7d6f9b1ce7 100644 --- a/src/server/routes/blocks/methods/listen.ts +++ b/src/server/routes/block/methods/listen.ts @@ -34,7 +34,7 @@ export const listen = description: 'Subscribe to a block to receive updates when it changes.', }); - return r.prop('blocks.listen', Func, (req$) => { + return r.prop('block.listen', Func, (req$) => { return req$.pipe(switchMap(({id}) => services.pubsub.listen$(`__block:${id}`))) as any; }); }; diff --git a/src/server/routes/blocks/methods/create.ts b/src/server/routes/block/methods/new.ts similarity index 87% rename from src/server/routes/blocks/methods/create.ts rename to src/server/routes/block/methods/new.ts index 303dbf153a..e0ac20641b 100644 --- a/src/server/routes/blocks/methods/create.ts +++ b/src/server/routes/block/methods/new.ts @@ -1,8 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId, BlockPatch} from '../schema'; -// TODO: Rename to "new", like "block.new"? -export const create = +export const new_ = ({t, services}: RouteDeps) => (r: Router) => { const Request = t.Object( @@ -24,7 +23,7 @@ export const create = description: 'Creates a new block or applies patches to it.', }); - return r.prop('blocks.create', Func, async ({id, patches}) => { + return r.prop('block.new', Func, async ({id, patches}) => { const {block} = await services.blocks.create(id, patches); return {}; }); diff --git a/src/server/routes/blocks/methods/history.ts b/src/server/routes/block/methods/scan.ts similarity index 90% rename from src/server/routes/blocks/methods/history.ts rename to src/server/routes/block/methods/scan.ts index cdab447288..5af677180b 100644 --- a/src/server/routes/blocks/methods/history.ts +++ b/src/server/routes/block/methods/scan.ts @@ -1,8 +1,7 @@ import type {BlockPatch, BlockId} from '../schema'; import type {RouteDeps, Router, RouterBase} from '../../types'; -// TODO: Rename to "scan". -export const history = +export const scan = ({t, services}: RouteDeps) => (r: Router) => { const Request = t.Object( @@ -33,7 +32,7 @@ export const history = description: 'Returns a list of specified change patches for a block.', }); - return r.prop('blocks.history', Func, async ({id, min, max}) => { + return r.prop('block.scan', Func, async ({id, min, max}) => { const {patches} = await services.blocks.history(id, min, max); return {patches}; }); diff --git a/src/server/routes/blocks/methods/edit.ts b/src/server/routes/block/methods/upd.ts similarity index 93% rename from src/server/routes/blocks/methods/edit.ts rename to src/server/routes/block/methods/upd.ts index c4c938a325..987854f5f5 100644 --- a/src/server/routes/blocks/methods/edit.ts +++ b/src/server/routes/block/methods/upd.ts @@ -1,8 +1,7 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId, BlockPatch} from '../schema'; -// TODO: Rename to "set"? -export const edit = +export const upd = ({t, services}: RouteDeps) => (r: Router) => { const PatchType = t.Ref('BlockPatch'); @@ -39,7 +38,7 @@ export const edit = description: 'Applies patches to an existing document and returns the latest concurrent changes.', }); - return r.prop('blocks.edit', Func, async ({id, patches}) => { + return r.prop('block.upd', Func, async ({id, patches}) => { const res = await services.blocks.edit(id, patches); return { patches: res.patches, diff --git a/src/server/routes/blocks/schema.ts b/src/server/routes/block/schema.ts similarity index 100% rename from src/server/routes/blocks/schema.ts rename to src/server/routes/block/schema.ts diff --git a/src/server/routes/routes.ts b/src/server/routes/routes.ts index 5f2fc47fe8..2d5733147c 100644 --- a/src/server/routes/routes.ts +++ b/src/server/routes/routes.ts @@ -1,7 +1,7 @@ import {util} from './util'; import {pubsub} from './pubsub'; import {presence} from './presence'; -import {blocks} from './blocks'; +import {block} from './block'; import type {RouteDeps} from './types'; import type {ObjectValue} from '../../json-type-value/ObjectValue'; import type {ObjectType} from '../../json-type'; @@ -12,5 +12,5 @@ export const routes = (d: RouteDeps) => >(r: ObjectVal ( pubsub(d) ( presence(d) // TODO: rename "blocks" to "block", in all methods. - ( blocks(d) + ( block(d) ( r ))))); From be282c41219be77fd48105080236f5f3edd37b5b Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 11 Apr 2024 10:42:35 +0200 Subject: [PATCH 27/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20impr?= =?UTF-8?q?ove=20block=20store=20interface,=20return=20infor=20about=20rem?= =?UTF-8?q?oval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/services/blocks/MemoryStore.ts | 8 ++-- src/server/services/blocks/types.ts | 48 ++++++++++++++++++++++- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts index 85c060a7d4..f8acb6685d 100644 --- a/src/server/services/blocks/MemoryStore.ts +++ b/src/server/services/blocks/MemoryStore.ts @@ -70,14 +70,14 @@ export class MemoryStore implements types.Store { return patches.slice(min, max + 1); } - public async remove(id: string): Promise { + public async remove(id: string): Promise { await new Promise((resolve) => setImmediate(resolve)); - this.removeSync(id); + return this.removeSync(id); } - private removeSync(id: string): void { + private removeSync(id: string): boolean { this.blocks.delete(id); - this.patches.delete(id); + return this.patches.delete(id); } public stats(): {blocks: number; patches: number} { diff --git a/src/server/services/blocks/types.ts b/src/server/services/blocks/types.ts index d6932bed34..a7a0fa693a 100644 --- a/src/server/services/blocks/types.ts +++ b/src/server/services/blocks/types.ts @@ -13,11 +13,57 @@ export interface StorePatch { } export interface Store { + /** + * Create a new block. + * + * @param id Block ID. + * @param patches Initial patches to apply to a new block. + * @returns Newly created block data. + */ create(id: string, patches: StorePatch[]): Promise; + + /** + * Retrieve an existing block. + * + * @param id Block ID. + * @returns Block data, or `undefined` if the block does not exist. + */ get(id: string): Promise; + + /** + * Retrieve the sequence number of a block. + * + * @param id Block ID. + * @returns Block sequence number, or `undefined` if the block does not exist. + */ + seq(id: string): Promise; + + /** + * Edit an existing block by applying new patches. + * + * @param id Block ID. + * @param patches Patches to apply to the block. + * @returns Updated block data. + */ edit(id: string, patches: StorePatch[]): Promise; + + /** + * Retrieve the history of patches for a block. + * + * @param id Block ID. + * @param min Minimum sequence number. + * @param max Maximum sequence number. + * @returns List of patches. + */ history(id: string, min: number, max: number): Promise; - remove(id: string): Promise; + + /** + * Remove a block. + * + * @param id Block ID. + * @returns `true` if the block was removed, `false` if the block did not exist. + */ + remove(id: string): Promise; } export interface StoreGetResult { From e62b5efa38e8fb44be2ce8a297f0c45d8ee82e30 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 11 Apr 2024 11:02:10 +0200 Subject: [PATCH 28/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20impr?= =?UTF-8?q?ove=20scan=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/rpc/caller/error/RpcError.ts | 4 +-- src/server/routes/block/methods/scan.ts | 27 ++++++++++++------- src/server/services/blocks/BlocksServices.ts | 21 +++++++++++++-- src/server/services/blocks/MemoryStore.ts | 19 ++++++++----- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/reactive-rpc/common/rpc/caller/error/RpcError.ts b/src/reactive-rpc/common/rpc/caller/error/RpcError.ts index b272370634..3941d4e3ad 100644 --- a/src/reactive-rpc/common/rpc/caller/error/RpcError.ts +++ b/src/reactive-rpc/common/rpc/caller/error/RpcError.ts @@ -53,8 +53,8 @@ export class RpcError extends Error implements IRpcError { return RpcError.fromCode(RpcErrorCodes.INTERNAL_ERROR, message, undefined, originalError); } - public static badRequest(): RpcError { - return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, 'Bad Request'); + public static badRequest(message = 'Bad Request'): RpcError { + return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, message); } public static validation(message: string, meta?: unknown): RpcError { diff --git a/src/server/routes/block/methods/scan.ts b/src/server/routes/block/methods/scan.ts index 5af677180b..c051401c88 100644 --- a/src/server/routes/block/methods/scan.ts +++ b/src/server/routes/block/methods/scan.ts @@ -9,14 +9,22 @@ export const scan = title: 'Block ID', description: 'The ID of the block.', }), - t.prop('max', t.num.options({format: 'u32'})).options({ - title: 'Max', - description: 'The maximum sequence number to return.', + t.prop('seq', t.num.options({format: 'u32'})).options({ + title: 'Starting Sequence Number', + description: 'The sequence number to start from. Defaults to the latest sequence number.', }), - t.prop('min', t.num.options({format: 'u32'})).options({ - title: 'Min', - description: 'The minimum sequence number to return.', + t.prop('len', t.num.options({format: 'u32'})).options({ + title: 'Number of Patches', + description: 'The minimum number of patches to return. Defaults to 10. ' + + 'When positive, returns the patches ahead of the starting sequence number. ' + + 'When negative, returns the patches behind the starting sequence number.', }), + t.prop('model', t.bool) + .options({ + title: 'With Model', + description: 'Whether to include the model in the response. ' + + 'Defaults to `false`, when `len` is positive; and, defaults to `true`, when `len` is negative.', + }), ); const Response = t.Object( @@ -32,8 +40,9 @@ export const scan = description: 'Returns a list of specified change patches for a block.', }); - return r.prop('block.scan', Func, async ({id, min, max}) => { - const {patches} = await services.blocks.history(id, min, max); - return {patches}; + return r.prop('block.scan', Func, async ({id, seq, len, model}) => { + // const {patches} = await services.blocks.history(id, min, max); + // return {patches}; + throw new Error('Not implemented'); }); }; diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 22f139dc03..7419fb003c 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -39,9 +39,12 @@ export class BlocksServices { if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); const patches = await store.history(id, 0, result.block.seq); const {block} = result; + // TODO: should not return `patches`, only the "tip". return {block, patches}; } + public async getSeq(id: string, seq: number) {} + public async remove(id: string) { await this.store.remove(id); this.services.pubsub.publish(`__block:${id}`, {deleted: true}).catch((error) => { @@ -50,10 +53,24 @@ export class BlocksServices { }); } - public async history(id: string, min: number, max: number) { + public async scan(id: string, offset: number | undefined, limit: number | undefined = 10) { const {store} = this; + if (typeof offset !== 'number') offset = await store.seq(id); + let min: number = 0, max: number = 0; + if (!limit || (Math.round(limit) !== limit)) throw RpcError.badRequest('INVALID_LIMIT'); + if (limit > 0) { + min = Number(offset) || 0; + max = min + limit; + } else { + max = Number(offset) || 0; + min = max - limit; + } + if (min < 0) { + min = 0; + max = Math.abs(limit); + } const patches = await store.history(id, min, max); - return {patches}; + // return {patches}; } public async edit(id: string, patches: StorePatch[]) { diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts index f8acb6685d..af2d67fba9 100644 --- a/src/server/services/blocks/MemoryStore.ts +++ b/src/server/services/blocks/MemoryStore.ts @@ -2,19 +2,26 @@ import {Model} from '../../../json-crdt'; import {Patch} from '../../../json-crdt-patch'; import type * as types from './types'; +const tick = new Promise((resolve) => setImmediate(resolve)); + export class MemoryStore implements types.Store { protected readonly blocks = new Map(); protected readonly patches = new Map(); public async get(id: string): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; const block = this.blocks.get(id); if (!block) return; return {block}; } + public async seq(id: string): Promise { + await tick; + return this.blocks.get(id)?.seq; + } + public async create(id: string, patches: types.StorePatch[]): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; if (!Array.isArray(patches)) throw new Error('NO_PATCHES'); if (this.blocks.has(id)) throw new Error('BLOCK_EXISTS'); const model = Model.withLogicalClock(); @@ -41,7 +48,7 @@ export class MemoryStore implements types.Store { } public async edit(id: string, patches: types.StorePatch[]): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; if (!Array.isArray(patches) || !patches.length) throw new Error('NO_PATCHES'); const block = this.blocks.get(id); const existingPatches = this.patches.get(id); @@ -64,14 +71,14 @@ export class MemoryStore implements types.Store { } public async history(id: string, min: number, max: number): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; const patches = this.patches.get(id); if (!patches) return []; return patches.slice(min, max + 1); } public async remove(id: string): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; return this.removeSync(id); } @@ -88,7 +95,7 @@ export class MemoryStore implements types.Store { } public async removeOlderThan(ts: number): Promise { - await new Promise((resolve) => setImmediate(resolve)); + await tick; for (const [id, block] of this.blocks) if (block.created < ts) this.removeSync(id); } } From a43f396dc1d05e3efc4bb8b18507c7c48ce4e8d2 Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 13 Apr 2024 12:23:43 +0200 Subject: [PATCH 29/42] =?UTF-8?q?feat(json-crdt):=20=F0=9F=8E=B8=20allow?= =?UTF-8?q?=20to=20construct=20a=20model=20from=20collection=20of=20patche?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/model/Model.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/json-crdt/model/Model.ts b/src/json-crdt/model/Model.ts index 6dbcd1872d..49409d3099 100644 --- a/src/json-crdt/model/Model.ts +++ b/src/json-crdt/model/Model.ts @@ -65,6 +65,25 @@ export class Model> implements Printable { return decoder.decode(data); } + /** + * Instantiates a model from a collection of patches. The patches are applied + * to the model in the order they are provided. The session ID of the model is + * set to the session ID of the first patch. + * + * @param patches A collection of initial patches to apply to the model. + * @returns A model with the patches applied. + */ + public static fromPatches(patches: Patch[]): Model { + const length = patches.length; + if (!length) throw new Error('NO_PATCHES'); + const first = patches[0]; + const sid = first.getId()!.sid; + if (!sid) throw new Error('NO_SID'); + const model = Model.withLogicalClock(sid); + model.applyBatch(patches); + return model; + } + /** * Root of the JSON document is implemented as Last Write Wins Register, * so that the JSON document does not necessarily need to be an object. The From ecd3a68609eec93db6a7794e78419c1345f4241d Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 13 Apr 2024 12:24:45 +0200 Subject: [PATCH 30/42] =?UTF-8?q?refactor(reactive-rpc):=20=F0=9F=92=A1=20?= =?UTF-8?q?update=20blocks=20scan=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/__tests__/block.spec.ts | 4 ++-- src/server/routes/block/methods/scan.ts | 15 ++++++++------- src/server/services/blocks/BlocksServices.ts | 19 ++++++++++++++++--- src/server/services/blocks/MemoryStore.ts | 2 ++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/server/__tests__/block.spec.ts b/src/server/__tests__/block.spec.ts index 08f3eb992c..7e9bbc1d91 100644 --- a/src/server/__tests__/block.spec.ts +++ b/src/server/__tests__/block.spec.ts @@ -360,7 +360,7 @@ describe('block.*', () => { }); }); - describe('block.history', () => { + describe('block.scan', () => { test('can retrieve change history', async () => { const {client} = setup(); const model = Model.withLogicalClock(); @@ -400,7 +400,7 @@ describe('block.*', () => { }, ], }); - const history = await client.call('block.scan', {id: 'my-block', min: 0, max: 2}); + const history = await client.call('block.scan', {id: 'my-block', seq: 0, limit: 3}); expect(history).toMatchObject({ patches: [ { diff --git a/src/server/routes/block/methods/scan.ts b/src/server/routes/block/methods/scan.ts index c051401c88..d437cc6952 100644 --- a/src/server/routes/block/methods/scan.ts +++ b/src/server/routes/block/methods/scan.ts @@ -9,17 +9,17 @@ export const scan = title: 'Block ID', description: 'The ID of the block.', }), - t.prop('seq', t.num.options({format: 'u32'})).options({ + t.propOpt('seq', t.num.options({format: 'u32'})).options({ title: 'Starting Sequence Number', description: 'The sequence number to start from. Defaults to the latest sequence number.', }), - t.prop('len', t.num.options({format: 'u32'})).options({ + t.propOpt('limit', t.num.options({format: 'u32'})).options({ title: 'Number of Patches', description: 'The minimum number of patches to return. Defaults to 10. ' + 'When positive, returns the patches ahead of the starting sequence number. ' + 'When negative, returns the patches behind the starting sequence number.', }), - t.prop('model', t.bool) + t.propOpt('model', t.bool) .options({ title: 'With Model', description: 'Whether to include the model in the response. ' + @@ -32,6 +32,7 @@ export const scan = title: 'Patches', description: 'The list of patches.', }), + t.propOpt('modelBlob', t.bin), ); const Func = t.Function(Request, Response).options({ @@ -40,9 +41,9 @@ export const scan = description: 'Returns a list of specified change patches for a block.', }); - return r.prop('block.scan', Func, async ({id, seq, len, model}) => { - // const {patches} = await services.blocks.history(id, min, max); - // return {patches}; - throw new Error('Not implemented'); + return r.prop('block.scan', Func, async ({id, seq, limit = 10, model: returnModel = limit > 0}) => { + const {patches, model} = await services.blocks.scan(id, seq, limit, returnModel); + const modelBlob: Uint8Array | undefined = model?.toBinary(); + return {patches, modelBlob}; }); }; diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 7419fb003c..18884f5796 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -2,6 +2,7 @@ import {MemoryStore} from './MemoryStore'; import {StorePatch} from './types'; import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller'; import type {Services} from '../Services'; +import {Model, Patch} from '../../../json-crdt'; const BLOCK_TTL = 1000 * 60 * 60; // 1 hour @@ -43,7 +44,12 @@ export class BlocksServices { return {block, patches}; } - public async getSeq(id: string, seq: number) {} + public async getAtSeq(id: string, seq: number) { + const {store} = this; + const patches = await store.history(id, 0, seq); + const model = Model.fromPatches(patches.map(p => Patch.fromBinary(p.blob))); + return model; + } public async remove(id: string) { await this.store.remove(id); @@ -53,7 +59,7 @@ export class BlocksServices { }); } - public async scan(id: string, offset: number | undefined, limit: number | undefined = 10) { + public async scan(id: string, offset: number | undefined, limit: number | undefined = 10, returnStartModel: boolean = limit < 0) { const {store} = this; if (typeof offset !== 'number') offset = await store.seq(id); let min: number = 0, max: number = 0; @@ -70,7 +76,14 @@ export class BlocksServices { max = Math.abs(limit); } const patches = await store.history(id, min, max); - // return {patches}; + let model: Model | undefined; + if (returnStartModel) { + const startPatches = await store.history(id, 0, min); + if (startPatches.length) { + model = Model.fromPatches(startPatches.map(p => Patch.fromBinary(p.blob))); + } + } + return {patches, model}; } public async edit(id: string, patches: StorePatch[]) { diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts index af2d67fba9..69d10362cd 100644 --- a/src/server/services/blocks/MemoryStore.ts +++ b/src/server/services/blocks/MemoryStore.ts @@ -98,4 +98,6 @@ export class MemoryStore implements types.Store { await tick; for (const [id, block] of this.blocks) if (block.created < ts) this.removeSync(id); } + + // TODO: GC by update timestamp instead } From 11d958caa2e9fe0cc78189fe540619c22a8ec2a4 Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 13 Apr 2024 12:37:09 +0200 Subject: [PATCH 31/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20GC?= =?UTF-8?q?=20old=20blocks=20based=20on=20update=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/services/blocks/BlocksServices.ts | 4 ++-- src/server/services/blocks/MemoryStore.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 18884f5796..46ae8be878 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -1,8 +1,8 @@ import {MemoryStore} from './MemoryStore'; import {StorePatch} from './types'; import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller'; -import type {Services} from '../Services'; import {Model, Patch} from '../../../json-crdt'; +import type {Services} from '../Services'; const BLOCK_TTL = 1000 * 60 * 60; // 1 hour @@ -125,6 +125,6 @@ export class BlocksServices { private async gc(): Promise { const ts = Date.now() - BLOCK_TTL; const {store} = this; - await store.removeOlderThan(ts); + await store.removeAccessedBefore(ts); } } diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts index 69d10362cd..48e87f42b5 100644 --- a/src/server/services/blocks/MemoryStore.ts +++ b/src/server/services/blocks/MemoryStore.ts @@ -99,5 +99,8 @@ export class MemoryStore implements types.Store { for (const [id, block] of this.blocks) if (block.created < ts) this.removeSync(id); } - // TODO: GC by update timestamp instead + public async removeAccessedBefore(ts: number): Promise { + await tick; + for (const [id, block] of this.blocks) if (block.updated < ts) this.removeSync(id); + } } From ca4d16b8bb1533985b8f66bdf1e33a8d456be4b7 Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 13 Apr 2024 14:27:13 +0200 Subject: [PATCH 32/42] =?UTF-8?q?feat(json-type):=20=F0=9F=8E=B8=20add=20a?= =?UTF-8?q?bility=20to=20extend=20ObjectType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-type/type/classes/ObjectType.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/json-type/type/classes/ObjectType.ts b/src/json-type/type/classes/ObjectType.ts index 38e22ece42..9029cc229b 100644 --- a/src/json-type/type/classes/ObjectType.ts +++ b/src/json-type/type/classes/ObjectType.ts @@ -136,6 +136,10 @@ export class ObjectType[] = ObjectFieldType< return this.fields.find((f) => f.key === key); } + public extend[]>(o: ObjectType): ObjectType<[...F, ...F2]> { + return new ObjectType([...this.fields, ...o.fields]); + } + public validateSchema(): void { const schema = this.getSchema(); validateTType(schema, 'obj'); From 1a561460a309e10539f91690737fb9d6dab0d36b Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 11:00:54 +0200 Subject: [PATCH 33/42] =?UTF-8?q?refactor(reactive-rpc):=20=F0=9F=92=A1=20?= =?UTF-8?q?cleanup=20demo=20server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/__tests__/block.spec.ts | 93 +++++++++----------- src/server/routes/block/index.ts | 15 +++- src/server/routes/block/methods/get.ts | 7 +- src/server/routes/block/methods/listen.ts | 9 +- src/server/routes/block/methods/new.ts | 16 ++-- src/server/routes/block/schema.ts | 25 ++++-- src/server/services/blocks/BlocksServices.ts | 80 +++++++++++------ src/server/services/blocks/MemoryStore.ts | 47 +++------- src/server/services/blocks/types.ts | 8 +- 9 files changed, 162 insertions(+), 138 deletions(-) diff --git a/src/server/__tests__/block.spec.ts b/src/server/__tests__/block.spec.ts index 7e9bbc1d91..7a6724c8fe 100644 --- a/src/server/__tests__/block.spec.ts +++ b/src/server/__tests__/block.spec.ts @@ -8,16 +8,16 @@ describe('block.*', () => { test('can create an empty block', async () => { const {call} = setup(); await call('block.new', {id: 'my-block', patches: []}); - const {block} = await call('block.get', {id: 'my-block'}); - expect(block).toMatchObject({ + const {model} = await call('block.get', {id: 'my-block'}); + expect(model).toMatchObject({ id: 'my-block', seq: -1, blob: expect.any(Uint8Array), created: expect.any(Number), updated: expect.any(Number), }); - const model = Model.fromBinary(block.blob); - expect(model.view()).toBe(undefined); + const model2 = Model.fromBinary(model.blob); + expect(model2.view()).toBe(undefined); }); test('can create a block with value', async () => { @@ -36,26 +36,22 @@ describe('block.*', () => { id: '123412341234', patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, { - seq: 1, - created: Date.now(), blob: patch2.toBinary(), }, ], }); - const {block} = await call('block.get', {id: '123412341234'}); - expect(block).toMatchObject({ + const res = await call('block.get', {id: '123412341234'}); + expect(res.model).toMatchObject({ id: '123412341234', seq: 1, blob: expect.any(Uint8Array), created: expect.any(Number), updated: expect.any(Number), }); - const model2 = Model.fromBinary(block.blob); + const model2 = Model.fromBinary(res.model.blob); expect(model2.view()).toStrictEqual({ name: 'Super Woman', age: 26, @@ -67,8 +63,8 @@ describe('block.*', () => { test('can remove an existing block', async () => { const {call} = setup(); await call('block.new', {id: 'my-block', patches: []}); - const {block} = await call('block.get', {id: 'my-block'}); - expect(block.id).toBe('my-block'); + const {model} = await call('block.get', {id: 'my-block'}); + expect(model.id).toBe('my-block'); await call('block.del', {id: 'my-block'}); try { await call('block.get', {id: 'my-block'}); @@ -92,8 +88,6 @@ describe('block.*', () => { id, patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -118,7 +112,7 @@ describe('block.*', () => { ], }); const block2 = await call('block.get', {id}); - expect(Model.fromBinary(block2.block.blob).view()).toStrictEqual({ + expect(Model.fromBinary(block2.model.blob).view()).toStrictEqual({ text: 'Hello World', }); model.api.str(['text']).del(5, 1).ins(5, ', '); @@ -141,7 +135,7 @@ describe('block.*', () => { ], }); const block3 = await call('block.get', {id}); - expect(Model.fromBinary(block3.block.blob).view()).toStrictEqual({ + expect(Model.fromBinary(block3.model.blob).view()).toStrictEqual({ text: 'Hello, World!', }); }); @@ -160,8 +154,6 @@ describe('block.*', () => { id, patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -169,7 +161,7 @@ describe('block.*', () => { // User 2 const block2 = await call('block.get', {id}); - const model2 = Model.fromBinary(block2.block.blob).fork(); + const model2 = Model.fromBinary(block2.model.blob).fork(); model2.api.str(['text']).ins(4, ' yeah!'); const patch2User2 = model2.api.flush(); await call('block.upd', { @@ -185,7 +177,7 @@ describe('block.*', () => { expect(model2.view()).toStrictEqual({text: 'Hell yeah!'}); const block3 = await call('block.get', {id}); - const model3 = Model.fromBinary(block3.block.blob).fork(); + const model3 = Model.fromBinary(block3.model.blob).fork(); expect(model3.view()).toStrictEqual({text: 'Hell yeah!'}); // User 1 @@ -210,7 +202,7 @@ describe('block.*', () => { }); const block4 = await call('block.get', {id}); - const model4 = Model.fromBinary(block4.block.blob).fork(); + const model4 = Model.fromBinary(block4.model.blob).fork(); expect(model4.view()).not.toStrictEqual({text: 'Hell yeah!'}); }); @@ -228,8 +220,6 @@ describe('block.*', () => { id, patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -237,7 +227,7 @@ describe('block.*', () => { // User 2 const block2 = await call('block.get', {id}); - const model2 = Model.fromBinary(block2.block.blob).fork(); + const model2 = Model.fromBinary(block2.model.blob).fork(); model2.api.str(['text']).ins(4, ' yeah!'); const patch2User2 = model2.api.flush(); await call('block.upd', { @@ -333,8 +323,6 @@ describe('block.*', () => { id: 'my-block', patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -349,10 +337,13 @@ describe('block.*', () => { test('can receive deletion events', async () => { const {client} = setup(); const emits: any[] = []; - client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); + client.call$('block.listen', {id: 'my-block'}).subscribe((data) => { + console.log('data', data); + emits.push(data); + }); await client.call('block.new', {id: 'my-block', patches: []}); await until(() => emits.length === 1); - expect(emits[0].block.seq).toBe(-1); + expect(emits[0].model.seq).toBe(-1); await tick(3); await client.call('block.del', {id: 'my-block'}); await until(() => emits.length === 2); @@ -372,8 +363,6 @@ describe('block.*', () => { id: 'my-block', patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -435,8 +424,6 @@ describe('block.*', () => { id: 'my-block', patches: [ { - seq: 0, - created: Date.now(), blob: patch1.toBinary(), }, ], @@ -463,26 +450,26 @@ describe('block.*', () => { ], }); const result = await client.call('block.get', {id: 'my-block'}); - expect(result).toMatchObject({ - block: expect.any(Object), - patches: [ - { - seq: 0, - created: expect.any(Number), - blob: patch1.toBinary(), - }, - { - seq: 1, - created: expect.any(Number), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: expect.any(Number), - blob: patch3.toBinary(), - }, - ], - }); + // expect(result).toMatchObject({ + // block: expect.any(Object), + // // patches: [ + // // { + // // seq: 0, + // // created: expect.any(Number), + // // blob: patch1.toBinary(), + // // }, + // // { + // // seq: 1, + // // created: expect.any(Number), + // // blob: patch2.toBinary(), + // // }, + // // { + // // seq: 2, + // // created: expect.any(Number), + // // blob: patch3.toBinary(), + // // }, + // // ], + // }); }); }); }); diff --git a/src/server/routes/block/index.ts b/src/server/routes/block/index.ts index 9306885880..cda905e8fa 100644 --- a/src/server/routes/block/index.ts +++ b/src/server/routes/block/index.ts @@ -4,7 +4,16 @@ import {upd} from './methods/upd'; import {del} from './methods/del'; import {scan} from './methods/scan'; import {listen} from './methods/listen'; -import {Block, BlockId, BlockPatch, BlockSeq} from './schema'; +import { + Block, + BlockPartial, + BlockPartialReturn, + BlockId, + BlockPatch, + BlockPatchPartial, + BlockPatchPartialReturn, + BlockSeq, +} from './schema'; import type {RouteDeps, Router, RouterBase} from '../types'; export const block = @@ -15,7 +24,11 @@ export const block = system.alias('BlockId', BlockId); system.alias('BlockSeq', BlockSeq); system.alias('Block', Block); + system.alias('BlockPartial', BlockPartial); + system.alias('BlockPartialReturn', BlockPartialReturn); system.alias('BlockPatch', BlockPatch); + system.alias('BlockPatchPartial', BlockPatchPartial); + system.alias('BlockPatchPartialReturn', BlockPatchPartialReturn); // prettier-ignore return ( diff --git a/src/server/routes/block/methods/get.ts b/src/server/routes/block/methods/get.ts index 2da94b63a0..081466b2b9 100644 --- a/src/server/routes/block/methods/get.ts +++ b/src/server/routes/block/methods/get.ts @@ -12,8 +12,7 @@ export const get = ); const Response = t.Object( - // TODO: Rename this field to `model` or `state`. - t.prop('block', t.Ref('Block')), + t.prop('model', t.Ref('Block')), t.prop('patches', t.Array(t.Ref('BlockPatch'))).options({ title: 'Patches', description: 'The list of all patches.', @@ -27,9 +26,9 @@ export const get = }); return r.prop('block.get', Func, async ({id}) => { - const {block, patches} = await services.blocks.get(id); + const {model, patches} = await services.blocks.get(id); return { - block, + model, patches, }; }); diff --git a/src/server/routes/block/methods/listen.ts b/src/server/routes/block/methods/listen.ts index 7d6f9b1ce7..1235cadd96 100644 --- a/src/server/routes/block/methods/listen.ts +++ b/src/server/routes/block/methods/listen.ts @@ -1,4 +1,4 @@ -import {switchMap} from 'rxjs'; +import {switchMap, tap} from 'rxjs'; import type {RouteDeps, Router, RouterBase} from '../../types'; import type {BlockId, BlockPatch, Block} from '../schema'; @@ -14,17 +14,18 @@ export const listen = }), ); + // TODO: Use TLV encoding. const Response = t.Object( t.propOpt('deleted', t.Boolean()).options({ title: 'Deleted', description: 'Emitted only when the block is deleted.', }), - t.propOpt('block', t.Ref('Block')).options({ + t.propOpt('model', t.Ref('Block')).options({ title: 'Block', description: 'The whole block object, emitted only when the block is created.', }), - t.propOpt('patches', t.Array(PatchType)).options({ - title: 'Latest patches', + t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ + title: 'Latest Patches', description: 'Patches that have been applied to the block.', }), ); diff --git a/src/server/routes/block/methods/new.ts b/src/server/routes/block/methods/new.ts index e0ac20641b..b270460417 100644 --- a/src/server/routes/block/methods/new.ts +++ b/src/server/routes/block/methods/new.ts @@ -1,5 +1,5 @@ import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {BlockId, BlockPatch} from '../schema'; +import type {Block, BlockId, BlockPatchPartial, BlockPatchPartialReturn} from '../schema'; export const new_ = ({t, services}: RouteDeps) => @@ -9,13 +9,19 @@ export const new_ = title: 'New block ID', description: 'The ID of the new block.', }), - t.prop('patches', t.Array(t.Ref('BlockPatch'))).options({ + t.prop('patches', t.Array(t.Ref('BlockPatchPartial'))).options({ title: 'Patches', description: 'The patches to apply to the document.', }), ); - const Response = t.obj; + const Response = t.Object( + t.prop('model', t.Ref('Block')), + t.prop('patches', t.Array(t.Ref('BlockPatchPartialReturn'))).options({ + title: 'Patches', + description: 'The list of all patches.', + }), + ); const Func = t.Function(Request, Response).options({ title: 'Create Block', @@ -24,7 +30,7 @@ export const new_ = }); return r.prop('block.new', Func, async ({id, patches}) => { - const {block} = await services.blocks.create(id, patches); - return {}; + const res = await services.blocks.create(id, patches); + return res; }); }; diff --git a/src/server/routes/block/schema.ts b/src/server/routes/block/schema.ts index 7fb9483b5a..6d12c3eaf1 100644 --- a/src/server/routes/block/schema.ts +++ b/src/server/routes/block/schema.ts @@ -18,18 +18,31 @@ export const BlockSeq = t.num.options({ export type TBlock = ResolveType; // prettier-ignore -export const Block = t.Object( +export const BlockPartial = t.Object( + t.prop('blob', t.bin), +); + +export const BlockPartialReturn = t.Object( t.prop('id', t.Ref('BlockId')), t.prop('seq', t.Ref('BlockSeq')), t.prop('created', t.num), t.prop('updated', t.num), - t.prop('blob', t.bin), ); +export const Block = BlockPartial.extend(BlockPartialReturn); + export type TBlockPatch = ResolveType; // prettier-ignore -export const BlockPatch = t.Object( +export const BlockPatchPartial = t.Object( + t.prop('blob', t.bin).options({ + title: 'Patch Blob', + description: 'The binary data of the patch. The format of the data is defined by the patch type.', + }), +); + +// prettier-ignore +export const BlockPatchPartialReturn = t.Object( t.prop('seq', t.num).options({ title: 'Patch Sequence Number', description: 'The sequence number of the patch in the block. A monotonically increasing integer, starting from 0.', @@ -42,8 +55,6 @@ export const BlockPatch = t.Object( 'want to also store the time when the patch was created by the user, you can include this ' + 'information in the patch blob itself.', }), - t.prop('blob', t.bin).options({ - title: 'Patch Blob', - description: 'The binary data of the patch. The format of the data is defined by the patch type.', - }), ); + +export const BlockPatch = BlockPatchPartial.extend(BlockPatchPartialReturn); diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 46ae8be878..3b7d062f71 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -1,15 +1,16 @@ import {MemoryStore} from './MemoryStore'; -import {StorePatch} from './types'; import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller'; import {Model, Patch} from '../../../json-crdt'; +import type {StoreModel, StorePatch} from './types'; import type {Services} from '../Services'; +import {SESSION} from '../../../json-crdt-patch/constants'; -const BLOCK_TTL = 1000 * 60 * 60; // 1 hour +const BLOCK_TTL = 1000 * 60 * 30; // 30 minutes -const validatePatches = (patches: StorePatch[]) => { +const validatePatches = (patches: Pick[]) => { for (const patch of patches) { if (patch.blob.length > 2000) throw RpcError.validation('patch blob too large'); - if (patch.seq > 500_000) throw RpcError.validation('patch seq too large'); + // if (patch.seq > 500_000) throw RpcError.validation('patch seq too large'); } }; @@ -18,37 +19,62 @@ export class BlocksServices { constructor(protected readonly services: Services) {} - public async create(id: string, patches: StorePatch[]) { + public async create(id: string, partialPatches: Pick[]) { this.maybeGc(); - const {store} = this; - validatePatches(patches); - const {block} = await store.create(id, patches); - const data = { - block, - patches, + validatePatches(partialPatches); + if (!Array.isArray(partialPatches)) throw new Error('INVALID_PATCHES'); + const length = partialPatches.length; + const now = Date.now(); + if (!length) { + const rawModel = Model.withLogicalClock(SESSION.GLOBAL); + const model: StoreModel = { + id, + seq: -1, + blob: rawModel.toBinary(), + created: now, + updated: now, + }; + return await this.__create(id, model, []); + } + const rawPatches: Patch[] = []; + const patches: StorePatch[] = []; + let seq = 0; + for (; seq < length; seq++) { + const blob = partialPatches[seq].blob; + rawPatches.push(Patch.fromBinary(blob)); + patches.push({seq, created: now, blob}); + } + const rawModel = Model.fromPatches(rawPatches); + const model: StoreModel = { + id, + seq: seq - 1, + blob: rawModel.toBinary(), + created: now, + updated: now, }; - this.services.pubsub.publish(`__block:${id}`, data).catch((error) => { + return await this.__create(id, model, patches);; + } + + private async __create(id: string, model: StoreModel, patches: StorePatch[]) { + await this.store.create(id, model, patches); + this.services.pubsub.publish(`__block:${id}`, {model, patches}).catch((error) => { // tslint:disable-next-line:no-console console.error('Error publishing block patches', error); }); - return {block}; + return { + model, + patches, + }; } public async get(id: string) { const {store} = this; const result = await store.get(id); if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); - const patches = await store.history(id, 0, result.block.seq); - const {block} = result; + // const patches = await store.history(id, 0, result.block.seq); + const {model} = result; // TODO: should not return `patches`, only the "tip". - return {block, patches}; - } - - public async getAtSeq(id: string, seq: number) { - const {store} = this; - const patches = await store.history(id, 0, seq); - const model = Model.fromPatches(patches.map(p => Patch.fromBinary(p.blob))); - return model; + return {model, patches: []}; } public async remove(id: string) { @@ -93,19 +119,19 @@ export class BlocksServices { const seq = patches[0].seq; const {store} = this; validatePatches(patches); - const {block} = await store.edit(id, patches); + const {model} = await store.edit(id, patches); this.services.pubsub.publish(`__block:${id}`, {patches}).catch((error) => { // tslint:disable-next-line:no-console console.error('Error publishing block patches', error); }); const expectedBlockSeq = seq + patches.length - 1; - const hadConcurrentEdits = block.seq !== expectedBlockSeq; + const hadConcurrentEdits = model.seq !== expectedBlockSeq; let patchesBack: StorePatch[] = []; if (hadConcurrentEdits) { - patchesBack = await store.history(id, seq, block.seq); + patchesBack = await store.history(id, seq, model.seq); } return { - block, + model, patches: patchesBack, }; } diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts index 48e87f42b5..491c7fec13 100644 --- a/src/server/services/blocks/MemoryStore.ts +++ b/src/server/services/blocks/MemoryStore.ts @@ -5,52 +5,33 @@ import type * as types from './types'; const tick = new Promise((resolve) => setImmediate(resolve)); export class MemoryStore implements types.Store { - protected readonly blocks = new Map(); + protected readonly models = new Map(); protected readonly patches = new Map(); public async get(id: string): Promise { await tick; - const block = this.blocks.get(id); - if (!block) return; - return {block}; + const model = this.models.get(id); + if (!model) return; + return {model}; } public async seq(id: string): Promise { await tick; - return this.blocks.get(id)?.seq; + return this.models.get(id)?.seq; } - public async create(id: string, patches: types.StorePatch[]): Promise { + public async create(id: string, model: types.StoreModel, patches: types.StorePatch[]): Promise { await tick; if (!Array.isArray(patches)) throw new Error('NO_PATCHES'); - if (this.blocks.has(id)) throw new Error('BLOCK_EXISTS'); - const model = Model.withLogicalClock(); - let seq = -1; - const now = Date.now(); - if (patches.length) { - for (const patch of patches) { - seq++; - if (seq !== patch.seq) throw new Error('PATCHES_OUT_OF_ORDER'); - model.applyPatch(Patch.fromBinary(patch.blob)); - if (patch.created > now) patch.created = now; - } - } - const block: types.StoreBlock = { - id, - seq: seq, - blob: model.toBinary(), - created: now, - updated: now, - }; - this.blocks.set(id, block); + if (this.models.has(id)) throw new Error('BLOCK_EXISTS'); + this.models.set(id, model); this.patches.set(id, patches); - return {block}; } public async edit(id: string, patches: types.StorePatch[]): Promise { await tick; if (!Array.isArray(patches) || !patches.length) throw new Error('NO_PATCHES'); - const block = this.blocks.get(id); + const block = this.models.get(id); const existingPatches = this.patches.get(id); if (!block || !existingPatches) throw new Error('BLOCK_NOT_FOUND'); let seq = patches[0].seq; @@ -67,7 +48,7 @@ export class MemoryStore implements types.Store { block.blob = model.toBinary(); block.updated = Date.now(); for (const patch of patches) existingPatches.push(patch); - return {block}; + return {model: block}; } public async history(id: string, min: number, max: number): Promise { @@ -83,24 +64,24 @@ export class MemoryStore implements types.Store { } private removeSync(id: string): boolean { - this.blocks.delete(id); + this.models.delete(id); return this.patches.delete(id); } public stats(): {blocks: number; patches: number} { return { - blocks: this.blocks.size, + blocks: this.models.size, patches: [...this.patches.values()].reduce((acc, v) => acc + v.length, 0), }; } public async removeOlderThan(ts: number): Promise { await tick; - for (const [id, block] of this.blocks) if (block.created < ts) this.removeSync(id); + for (const [id, block] of this.models) if (block.created < ts) this.removeSync(id); } public async removeAccessedBefore(ts: number): Promise { await tick; - for (const [id, block] of this.blocks) if (block.updated < ts) this.removeSync(id); + for (const [id, block] of this.models) if (block.updated < ts) this.removeSync(id); } } diff --git a/src/server/services/blocks/types.ts b/src/server/services/blocks/types.ts index a7a0fa693a..89a22c4404 100644 --- a/src/server/services/blocks/types.ts +++ b/src/server/services/blocks/types.ts @@ -1,4 +1,4 @@ -export interface StoreBlock { +export interface StoreModel { id: string; seq: number; created: number; @@ -20,7 +20,7 @@ export interface Store { * @param patches Initial patches to apply to a new block. * @returns Newly created block data. */ - create(id: string, patches: StorePatch[]): Promise; + create(id: string, model: StoreModel, patches: StorePatch[]): Promise; /** * Retrieve an existing block. @@ -67,9 +67,9 @@ export interface Store { } export interface StoreGetResult { - block: StoreBlock; + model: StoreModel; } export interface StoreApplyResult { - block: StoreBlock; + model: StoreModel; } From ae14b6cd176dc873fb3e9c97c620bbdf4aa005df Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 11:07:57 +0200 Subject: [PATCH 34/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20allo?= =?UTF-8?q?w=20to=20optionally=20load=20full=20history=20in=20"block.get"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/__tests__/block.spec.ts | 49 +++++++++++--------- src/server/routes/block/methods/get.ts | 21 ++++++--- src/server/services/blocks/BlocksServices.ts | 4 +- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/server/__tests__/block.spec.ts b/src/server/__tests__/block.spec.ts index 7a6724c8fe..d3c2177455 100644 --- a/src/server/__tests__/block.spec.ts +++ b/src/server/__tests__/block.spec.ts @@ -338,7 +338,6 @@ describe('block.*', () => { const {client} = setup(); const emits: any[] = []; client.call$('block.listen', {id: 'my-block'}).subscribe((data) => { - console.log('data', data); emits.push(data); }); await client.call('block.new', {id: 'my-block', patches: []}); @@ -449,27 +448,33 @@ describe('block.*', () => { }, ], }); - const result = await client.call('block.get', {id: 'my-block'}); - // expect(result).toMatchObject({ - // block: expect.any(Object), - // // patches: [ - // // { - // // seq: 0, - // // created: expect.any(Number), - // // blob: patch1.toBinary(), - // // }, - // // { - // // seq: 1, - // // created: expect.any(Number), - // // blob: patch2.toBinary(), - // // }, - // // { - // // seq: 2, - // // created: expect.any(Number), - // // blob: patch3.toBinary(), - // // }, - // // ], - // }); + const result = await client.call('block.get', {id: 'my-block', history: true}); + expect(result).toMatchObject({ + model: { + id: 'my-block', + seq: 2, + blob: expect.any(Uint8Array), + created: expect.any(Number), + updated: expect.any(Number), + }, + patches: [ + { + seq: 0, + created: expect.any(Number), + blob: patch1.toBinary(), + }, + { + seq: 1, + created: expect.any(Number), + blob: patch2.toBinary(), + }, + { + seq: 2, + created: expect.any(Number), + blob: patch3.toBinary(), + }, + ], + }); }); }); }); diff --git a/src/server/routes/block/methods/get.ts b/src/server/routes/block/methods/get.ts index 081466b2b9..2135db2748 100644 --- a/src/server/routes/block/methods/get.ts +++ b/src/server/routes/block/methods/get.ts @@ -1,3 +1,4 @@ +import {ResolveType} from '../../../../json-type'; import type {RouteDeps, Router, RouterBase} from '../../types'; import type {Block, BlockId, BlockPatch} from '../schema'; @@ -9,11 +10,15 @@ export const get = title: 'Block ID', description: 'The ID of the block to retrieve.', }), + t.propOpt('history', t.bool).options({ + title: 'With History', + description: 'Whether to include the full history of patches in the response. Defaults to `false`.', + }), ); const Response = t.Object( t.prop('model', t.Ref('Block')), - t.prop('patches', t.Array(t.Ref('BlockPatch'))).options({ + t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ title: 'Patches', description: 'The list of all patches.', }), @@ -25,11 +30,13 @@ export const get = description: 'Fetches a block by ID.', }); - return r.prop('block.get', Func, async ({id}) => { - const {model, patches} = await services.blocks.get(id); - return { - model, - patches, - }; + return r.prop('block.get', Func, async ({id, history}) => { + const {model} = await services.blocks.get(id); + const response: ResolveType = {model}; + if (history) { + const {patches} = await services.blocks.scan(id, 0, model.seq); + response.patches = patches; + } + return response; }); }; diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 3b7d062f71..47a3a67195 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -71,10 +71,8 @@ export class BlocksServices { const {store} = this; const result = await store.get(id); if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); - // const patches = await store.history(id, 0, result.block.seq); const {model} = result; - // TODO: should not return `patches`, only the "tip". - return {model, patches: []}; + return {model}; } public async remove(id: string) { From 2dcdad5fb0211d649d899da85ce20c36ca903025 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 11:33:59 +0200 Subject: [PATCH 35/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20impr?= =?UTF-8?q?ove=20emitted=20event=20shape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/__tests__/block.spec.ts | 21 +++++++------ src/server/routes/block/methods/listen.ts | 31 ++++++++++---------- src/server/services/blocks/BlocksServices.ts | 27 +++++++++-------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/server/__tests__/block.spec.ts b/src/server/__tests__/block.spec.ts index d3c2177455..4a52c1760c 100644 --- a/src/server/__tests__/block.spec.ts +++ b/src/server/__tests__/block.spec.ts @@ -290,8 +290,9 @@ describe('block.*', () => { }); await until(() => emits.length === 1); expect(emits.length).toBe(1); - expect(emits[0].patches.length).toBe(1); - expect(emits[0].patches[0].seq).toBe(0); + expect(emits[0][0]).toBe('upd'); + expect(emits[0][1].patches.length).toBe(1); + expect(emits[0][1].patches[0].seq).toBe(0); model.api.root({ text: 'Hello', }); @@ -304,8 +305,9 @@ describe('block.*', () => { }); await until(() => emits.length === 2); expect(emits.length).toBe(2); - expect(emits[1].patches.length).toBe(1); - expect(emits[1].patches[0].seq).toBe(1); + expect(emits[1][0]).toBe('upd'); + expect(emits[1][1].patches.length).toBe(1); + expect(emits[1][1].patches[0].seq).toBe(1); }); test('can subscribe before block is created', async () => { @@ -329,9 +331,10 @@ describe('block.*', () => { }); await until(() => emits.length === 1); expect(emits.length).toBe(1); - expect(emits[0].patches.length).toBe(1); - expect(emits[0].patches[0].seq).toBe(0); - expect(emits[0].patches[0].blob).toStrictEqual(patch1.toBinary()); + expect(emits[0][0]).toBe('upd'); + expect(emits[0][1].patches.length).toBe(1); + expect(emits[0][1].patches[0].seq).toBe(0); + expect(emits[0][1].patches[0].blob).toStrictEqual(patch1.toBinary()); }); test('can receive deletion events', async () => { @@ -342,11 +345,11 @@ describe('block.*', () => { }); await client.call('block.new', {id: 'my-block', patches: []}); await until(() => emits.length === 1); - expect(emits[0].model.seq).toBe(-1); + expect(emits[0][1].model.seq).toBe(-1); await tick(3); await client.call('block.del', {id: 'my-block'}); await until(() => emits.length === 2); - expect(emits[1].deleted).toBe(true); + expect(emits[1][0]).toBe('del'); }); }); diff --git a/src/server/routes/block/methods/listen.ts b/src/server/routes/block/methods/listen.ts index 1235cadd96..e522e0e8a0 100644 --- a/src/server/routes/block/methods/listen.ts +++ b/src/server/routes/block/methods/listen.ts @@ -5,8 +5,6 @@ import type {BlockId, BlockPatch, Block} from '../schema'; export const listen = ({t, services}: RouteDeps) => (r: Router) => { - const PatchType = t.Ref('BlockPatch'); - const Request = t.Object( t.prop('id', t.Ref('BlockId')).options({ title: 'Block ID', @@ -14,20 +12,21 @@ export const listen = }), ); - // TODO: Use TLV encoding. - const Response = t.Object( - t.propOpt('deleted', t.Boolean()).options({ - title: 'Deleted', - description: 'Emitted only when the block is deleted.', - }), - t.propOpt('model', t.Ref('Block')).options({ - title: 'Block', - description: 'The whole block object, emitted only when the block is created.', - }), - t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ - title: 'Latest Patches', - description: 'Patches that have been applied to the block.', - }), + const Response = t.Or( + t.Tuple(t.Const('del')), + t.Tuple( + t.Const('upd'), + t.Object( + t.propOpt('model', t.Ref('Block')).options({ + title: 'Block', + description: 'The whole block object, emitted only when the block is created.', + }), + t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ + title: 'Latest Patches', + description: 'Patches that have been applied to the block.', + }), + ) + ), ); const Func = t.Function$(Request, Response).options({ diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index 47a3a67195..aeb2770c0f 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -1,9 +1,9 @@ import {MemoryStore} from './MemoryStore'; import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller'; import {Model, Patch} from '../../../json-crdt'; +import {SESSION} from '../../../json-crdt-patch/constants'; import type {StoreModel, StorePatch} from './types'; import type {Services} from '../Services'; -import {SESSION} from '../../../json-crdt-patch/constants'; const BLOCK_TTL = 1000 * 60 * 30; // 30 minutes @@ -57,16 +57,21 @@ export class BlocksServices { private async __create(id: string, model: StoreModel, patches: StorePatch[]) { await this.store.create(id, model, patches); - this.services.pubsub.publish(`__block:${id}`, {model, patches}).catch((error) => { - // tslint:disable-next-line:no-console - console.error('Error publishing block patches', error); - }); + this.__emitUpd(id, model, patches); return { model, patches, }; } + private __emitUpd(id: string, model: StoreModel, patches: StorePatch[]) { + const msg = ['upd', {model, patches}]; + this.services.pubsub.publish(`__block:${id}`, msg).catch((error) => { + // tslint:disable-next-line:no-console + console.error('Error publishing block patches', error); + }); + } + public async get(id: string) { const {store} = this; const result = await store.get(id); @@ -77,7 +82,8 @@ export class BlocksServices { public async remove(id: string) { await this.store.remove(id); - this.services.pubsub.publish(`__block:${id}`, {deleted: true}).catch((error) => { + const msg = ['del']; + this.services.pubsub.publish(`__block:${id}`, msg).catch((error) => { // tslint:disable-next-line:no-console console.error('Error publishing block deletion', error); }); @@ -118,16 +124,11 @@ export class BlocksServices { const {store} = this; validatePatches(patches); const {model} = await store.edit(id, patches); - this.services.pubsub.publish(`__block:${id}`, {patches}).catch((error) => { - // tslint:disable-next-line:no-console - console.error('Error publishing block patches', error); - }); + this.__emitUpd(id, model, patches); const expectedBlockSeq = seq + patches.length - 1; const hadConcurrentEdits = model.seq !== expectedBlockSeq; let patchesBack: StorePatch[] = []; - if (hadConcurrentEdits) { - patchesBack = await store.history(id, seq, model.seq); - } + if (hadConcurrentEdits) patchesBack = await store.history(id, seq, model.seq); return { model, patches: patchesBack, From e1343ac92d112146d6ec6af56721f87a69379273 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 11:34:53 +0200 Subject: [PATCH 36/42] =?UTF-8?q?style:=20=F0=9F=92=84=20run=20Prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/RemoteHistoryDemoServer.ts | 20 ++++++++++++------- src/json-crdt-repo/remote/types.ts | 8 ++++---- src/reactive-rpc/common/types.ts | 18 +++++++++-------- src/server/routes/block/methods/listen.ts | 2 +- src/server/routes/block/methods/scan.ts | 19 +++++++++--------- src/server/services/blocks/BlocksServices.ts | 16 ++++++++++----- 6 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts index 57314b18c6..1bc8656ab1 100644 --- a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts +++ b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts @@ -1,6 +1,6 @@ import type {CallerToMethods, TypedRpcClient} from '../../reactive-rpc/common'; import type {JsonJoyDemoRpcCaller} from '../../server'; -import type {RemoteHistory, RemoteModel, RemotePatch} from "./types"; +import type {RemoteHistory, RemoteModel, RemotePatch} from './types'; type Methods = CallerToMethods; @@ -17,7 +17,7 @@ export interface RemoteServerPatch extends RemotePatch { } export class RemoteHistoryDemoServer implements RemoteHistory { - constructor (protected readonly client: TypedRpcClient) {} + constructor(protected readonly client: TypedRpcClient) {} public async create(id: string, patches: RemotePatch[]): Promise { await this.client.call('block.new', { @@ -35,7 +35,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { + public async read(id: string): Promise<{cursor: Cursor; model: RemoteServerModel; patches: RemoteServerPatch[]}> { const {block, patches} = await this.client.call('block.get', {id}); return { cursor: block.seq, @@ -44,7 +44,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { + public async scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; patches: RemoteServerPatch[]}> { const limit = 100; const res = await this.client.call('block.scan', { id, @@ -63,12 +63,18 @@ export class RemoteHistoryDemoServer implements RemoteHistory { + public async scanBwd( + id: string, + cursor: Cursor, + ): Promise<{cursor: Cursor; model: RemoteServerModel; patches: RemoteServerPatch[]}> { throw new Error('The "blocks.history" should be able to return starting model.'); } - public async update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor, patches: RemoteServerPatch[]}> { - + public async update( + id: string, + cursor: Cursor, + patches: RemotePatch[], + ): Promise<{cursor: Cursor; patches: RemoteServerPatch[]}> { const res = await this.client.call('block.upd', { id, patches: patches.map((patch, seq) => ({ diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index 2e8ac571a4..2f76a1037a 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -12,13 +12,13 @@ export interface RemoteHistory; + read(id: string): Promise<{cursor: Cursor; model: M; patches: P[]}>; - scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, patches: P[]}>; + scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; patches: P[]}>; - scanBwd(id: string, cursor: Cursor): Promise<{cursor: Cursor, model: M, patches: P[]}>; + scanBwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; model: M; patches: P[]}>; - update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor, patches: P[]}>; + update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor; patches: P[]}>; delete?(id: string): Promise; diff --git a/src/reactive-rpc/common/types.ts b/src/reactive-rpc/common/types.ts index a1b2f96f0e..b9ee65d320 100644 --- a/src/reactive-rpc/common/types.ts +++ b/src/reactive-rpc/common/types.ts @@ -1,12 +1,14 @@ -import type {Observable} from "rxjs"; -import type {FunctionStreamingType, FunctionType, ResolveType} from "../../json-type"; -import type {ObjectValue, ObjectValueToTypeMap, UnObjectType} from "../../json-type-value/ObjectValue"; -import type {TypeRouter} from "../../json-type/system/TypeRouter"; -import type {ObjectValueCaller} from "./rpc/caller/ObjectValueCaller"; -import type {RpcCaller} from "./rpc/caller/RpcCaller"; -import type {TypeRouterCaller} from "./rpc/caller/TypeRouterCaller"; +import type {Observable} from 'rxjs'; +import type {FunctionStreamingType, FunctionType, ResolveType} from '../../json-type'; +import type {ObjectValue, ObjectValueToTypeMap, UnObjectType} from '../../json-type-value/ObjectValue'; +import type {TypeRouter} from '../../json-type/system/TypeRouter'; +import type {ObjectValueCaller} from './rpc/caller/ObjectValueCaller'; +import type {RpcCaller} from './rpc/caller/RpcCaller'; +import type {TypeRouterCaller} from './rpc/caller/TypeRouterCaller'; -export type CallerToMethods> = {[K in keyof UnTypeRouter>]: UnwrapFunction>[K]>}; +export type CallerToMethods> = { + [K in keyof UnTypeRouter>]: UnwrapFunction>[K]>; +}; type UnTypeRouterCaller = T extends TypeRouterCaller ? R : T extends ObjectValueCaller ? R : never; type UnTypeRouter = diff --git a/src/server/routes/block/methods/listen.ts b/src/server/routes/block/methods/listen.ts index e522e0e8a0..bd3f9c8405 100644 --- a/src/server/routes/block/methods/listen.ts +++ b/src/server/routes/block/methods/listen.ts @@ -25,7 +25,7 @@ export const listen = title: 'Latest Patches', description: 'Patches that have been applied to the block.', }), - ) + ), ), ); diff --git a/src/server/routes/block/methods/scan.ts b/src/server/routes/block/methods/scan.ts index d437cc6952..ce5080199f 100644 --- a/src/server/routes/block/methods/scan.ts +++ b/src/server/routes/block/methods/scan.ts @@ -15,16 +15,17 @@ export const scan = }), t.propOpt('limit', t.num.options({format: 'u32'})).options({ title: 'Number of Patches', - description: 'The minimum number of patches to return. Defaults to 10. ' + - 'When positive, returns the patches ahead of the starting sequence number. ' + - 'When negative, returns the patches behind the starting sequence number.', + description: + 'The minimum number of patches to return. Defaults to 10. ' + + 'When positive, returns the patches ahead of the starting sequence number. ' + + 'When negative, returns the patches behind the starting sequence number.', + }), + t.propOpt('model', t.bool).options({ + title: 'With Model', + description: + 'Whether to include the model in the response. ' + + 'Defaults to `false`, when `len` is positive; and, defaults to `true`, when `len` is negative.', }), - t.propOpt('model', t.bool) - .options({ - title: 'With Model', - description: 'Whether to include the model in the response. ' + - 'Defaults to `false`, when `len` is positive; and, defaults to `true`, when `len` is negative.', - }), ); const Response = t.Object( diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts index aeb2770c0f..dad9f11715 100644 --- a/src/server/services/blocks/BlocksServices.ts +++ b/src/server/services/blocks/BlocksServices.ts @@ -52,7 +52,7 @@ export class BlocksServices { created: now, updated: now, }; - return await this.__create(id, model, patches);; + return await this.__create(id, model, patches); } private async __create(id: string, model: StoreModel, patches: StorePatch[]) { @@ -89,11 +89,17 @@ export class BlocksServices { }); } - public async scan(id: string, offset: number | undefined, limit: number | undefined = 10, returnStartModel: boolean = limit < 0) { + public async scan( + id: string, + offset: number | undefined, + limit: number | undefined = 10, + returnStartModel: boolean = limit < 0, + ) { const {store} = this; if (typeof offset !== 'number') offset = await store.seq(id); - let min: number = 0, max: number = 0; - if (!limit || (Math.round(limit) !== limit)) throw RpcError.badRequest('INVALID_LIMIT'); + let min: number = 0, + max: number = 0; + if (!limit || Math.round(limit) !== limit) throw RpcError.badRequest('INVALID_LIMIT'); if (limit > 0) { min = Number(offset) || 0; max = min + limit; @@ -110,7 +116,7 @@ export class BlocksServices { if (returnStartModel) { const startPatches = await store.history(id, 0, min); if (startPatches.length) { - model = Model.fromPatches(startPatches.map(p => Patch.fromBinary(p.blob))); + model = Model.fromPatches(startPatches.map((p) => Patch.fromBinary(p.blob))); } } return {patches, model}; From f86ed4df3af32087e32e4540b13ec0937ea5f485 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 11:56:23 +0200 Subject: [PATCH 37/42] =?UTF-8?q?feat(reactive-rpc):=20=F0=9F=8E=B8=20clea?= =?UTF-8?q?nup=20RemoteHistoryDemoServer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/RemoteHistoryDemoServer.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts index 1bc8656ab1..27accc7c8b 100644 --- a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts +++ b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts @@ -36,11 +36,11 @@ export class RemoteHistoryDemoServer implements RemoteHistory { - const {block, patches} = await this.client.call('block.get', {id}); + const {model, patches} = await this.client.call('block.get', {id}); return { - cursor: block.seq, - model: block, - patches, + cursor: model.seq, + model, + patches: [], }; } @@ -48,8 +48,8 @@ export class RemoteHistoryDemoServer implements RemoteHistory { + public async delete(id: string): Promise { await this.client.call('block.del', {id}); } From 7d6a033ed929348cf13568123d850e73b59ce390 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 12:47:14 +0200 Subject: [PATCH 38/42] =?UTF-8?q?fix(json-crdt):=20=F0=9F=90=9B=20use=20ri?= =?UTF-8?q?ght=20payload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts | 5 +---- .../remote/__tests__/RemoteHistoryServer.spec.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts index 27accc7c8b..f3ed21b557 100644 --- a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts +++ b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts @@ -22,10 +22,7 @@ export class RemoteHistoryDemoServer implements RemoteHistory { await this.client.call('block.new', { id, - patches: patches.map((patch, seq) => ({ - // TODO: seq and created should be set on server. (And returned back?) - seq, - created: Date.now(), + patches: patches.map((patch) => ({ blob: patch.blob, })), }); diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts index 07c065d4aa..31b85a7b14 100644 --- a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts @@ -30,7 +30,7 @@ describe('.create()', () => { await remote.create(id, [{blob}]); const {data} = await caller.call('block.get', {id}, {}); // console.log(data.patches); - const model2 = Model.fromBinary(data.block.blob); + const model2 = Model.fromBinary(data.model.blob); expect(model2.view()).toEqual({foo: 'bar'}); }); }); From 57d3bbe081757ce3c473302e94e9f529ca41b5e4 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 12:54:33 +0200 Subject: [PATCH 39/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20remove=20web3=20p?= =?UTF-8?q?roject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/web3/adl/feed-crdt/Feed.ts | 321 -------------- src/web3/adl/feed-crdt/FeedFactory.ts | 19 - src/web3/adl/feed-crdt/FeedFrame.ts | 36 -- src/web3/adl/feed-crdt/README.md | 10 - .../feed-crdt/__tests__/Feed-merge.spec.ts | 391 ------------------ src/web3/adl/feed-crdt/__tests__/Feed.spec.ts | 210 ---------- src/web3/adl/feed-crdt/constants.ts | 13 - src/web3/adl/feed-crdt/types.ts | 37 -- src/web3/adl/hamt-crdt/Hamt.ts | 106 ----- src/web3/adl/hamt-crdt/HamtFactory.ts | 12 - src/web3/adl/hamt-crdt/HamtFrame.ts | 217 ---------- src/web3/adl/hamt-crdt/README.md | 10 - src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts | 265 ------------ src/web3/adl/hamt-crdt/constants.ts | 3 - src/web3/adl/hamt-crdt/types.ts | 64 --- src/web3/codec/Codecs.ts | 20 - src/web3/codec/codecs/__tests__/cbor.spec.ts | 22 - src/web3/codec/codecs/cbor.ts | 26 -- src/web3/codec/codecs/raw.ts | 19 - src/web3/codec/codecs/writer.ts | 3 - src/web3/codec/index.ts | 13 - src/web3/codec/types.ts | 7 - src/web3/constants.ts | 4 - src/web3/crypto/index.ts | 2 - src/web3/crypto/sha256.ts | 6 - src/web3/crypto/webcrypto.ts | 8 - src/web3/hlc/Hlc.ts | 16 - src/web3/hlc/HlcFactory.ts | 37 -- src/web3/hlc/README.md | 7 - src/web3/hlc/__tests__/HlcFactory.spec.ts | 20 - src/web3/hlc/__tests__/util.spec.ts | 151 ------- src/web3/hlc/index.ts | 4 - src/web3/hlc/types.ts | 14 - src/web3/hlc/util.ts | 34 -- src/web3/multiformats/Cid.ts | 121 ------ src/web3/multiformats/Multihash.ts | 68 --- src/web3/multiformats/__tests__/Cid.spec.ts | 73 ---- .../multiformats/__tests__/Multihash.spec.ts | 56 --- src/web3/multiformats/constants.ts | 36 -- src/web3/multiformats/index.ts | 2 - src/web3/multiformats/multibase.ts | 9 - src/web3/store/cas/CidCas.ts | 9 - src/web3/store/cas/CidCasMemory.ts | 39 -- src/web3/store/cas/CidCasStruct.ts | 29 -- src/web3/store/cas/CidCasStructCbor.ts | 14 - src/web3/store/cas/README.md | 4 - .../store/cas/__tests__/CidCasMemory.spec.ts | 58 --- .../cas/__tests__/CidCasStructCbor.spec.ts | 64 --- src/web3/util/__tests__/uvint.spec.ts | 58 --- src/web3/util/uvint.ts | 46 --- 50 files changed, 2813 deletions(-) delete mode 100644 src/web3/adl/feed-crdt/Feed.ts delete mode 100644 src/web3/adl/feed-crdt/FeedFactory.ts delete mode 100644 src/web3/adl/feed-crdt/FeedFrame.ts delete mode 100644 src/web3/adl/feed-crdt/README.md delete mode 100644 src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts delete mode 100644 src/web3/adl/feed-crdt/__tests__/Feed.spec.ts delete mode 100644 src/web3/adl/feed-crdt/constants.ts delete mode 100644 src/web3/adl/feed-crdt/types.ts delete mode 100644 src/web3/adl/hamt-crdt/Hamt.ts delete mode 100644 src/web3/adl/hamt-crdt/HamtFactory.ts delete mode 100644 src/web3/adl/hamt-crdt/HamtFrame.ts delete mode 100644 src/web3/adl/hamt-crdt/README.md delete mode 100644 src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts delete mode 100644 src/web3/adl/hamt-crdt/constants.ts delete mode 100644 src/web3/adl/hamt-crdt/types.ts delete mode 100644 src/web3/codec/Codecs.ts delete mode 100644 src/web3/codec/codecs/__tests__/cbor.spec.ts delete mode 100644 src/web3/codec/codecs/cbor.ts delete mode 100644 src/web3/codec/codecs/raw.ts delete mode 100644 src/web3/codec/codecs/writer.ts delete mode 100644 src/web3/codec/index.ts delete mode 100644 src/web3/codec/types.ts delete mode 100644 src/web3/constants.ts delete mode 100644 src/web3/crypto/index.ts delete mode 100644 src/web3/crypto/sha256.ts delete mode 100644 src/web3/crypto/webcrypto.ts delete mode 100644 src/web3/hlc/Hlc.ts delete mode 100644 src/web3/hlc/HlcFactory.ts delete mode 100644 src/web3/hlc/README.md delete mode 100644 src/web3/hlc/__tests__/HlcFactory.spec.ts delete mode 100644 src/web3/hlc/__tests__/util.spec.ts delete mode 100644 src/web3/hlc/index.ts delete mode 100644 src/web3/hlc/types.ts delete mode 100644 src/web3/hlc/util.ts delete mode 100644 src/web3/multiformats/Cid.ts delete mode 100644 src/web3/multiformats/Multihash.ts delete mode 100644 src/web3/multiformats/__tests__/Cid.spec.ts delete mode 100644 src/web3/multiformats/__tests__/Multihash.spec.ts delete mode 100644 src/web3/multiformats/constants.ts delete mode 100644 src/web3/multiformats/index.ts delete mode 100644 src/web3/multiformats/multibase.ts delete mode 100644 src/web3/store/cas/CidCas.ts delete mode 100644 src/web3/store/cas/CidCasMemory.ts delete mode 100644 src/web3/store/cas/CidCasStruct.ts delete mode 100644 src/web3/store/cas/CidCasStructCbor.ts delete mode 100644 src/web3/store/cas/README.md delete mode 100644 src/web3/store/cas/__tests__/CidCasMemory.spec.ts delete mode 100644 src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts delete mode 100644 src/web3/util/__tests__/uvint.spec.ts delete mode 100644 src/web3/util/uvint.ts diff --git a/src/web3/adl/feed-crdt/Feed.ts b/src/web3/adl/feed-crdt/Feed.ts deleted file mode 100644 index 2eb3a8bbf4..0000000000 --- a/src/web3/adl/feed-crdt/Feed.ts +++ /dev/null @@ -1,321 +0,0 @@ -import {FeedFrame} from './FeedFrame'; -import {AvlSet} from '../../../util/trees/avl/AvlSet'; -import {AvlMap} from '../../../util/trees/avl/AvlMap'; -import {Cid} from '../../multiformats'; -import {mutex} from 'thingies/es2020/mutex'; -import {FanOut} from 'thingies/es2020/fanout'; -import {FeedConstraints, FeedOpType} from './constants'; -import * as hlc from '../../hlc'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type * as types from './types'; -import type {SyncStore} from '../../../util/events/sync-store'; - -export interface FeedDependencies { - cas: CidCasStruct; - hlcs: hlc.HlcFactory; - - /** - * Number of operations after which a new frame is created, otherwise the - * operations are appended to the current frame. Defaults to 25. - */ - opsPerFrame?: number; -} - -export class Feed implements types.FeedApi, SyncStore { - public static async merge( - cas: CidCasStruct, - baseCid: Cid, - forkCid: Cid, - opsPerFrame: number = FeedConstraints.DefaultOpsPerFrameThreshold, - ): Promise { - const [commonParent, baseFrames, forkFrames] = await Feed.findForkTriangle(cas, baseCid, forkCid); - const ops = Feed.zipOps(baseFrames, forkFrames); - let lastFrame: FeedFrame | null = commonParent; - const frames: FeedFrame[] = []; - while (ops.length) { - const frameOps = ops.splice(0, opsPerFrame); - const prev = lastFrame ? lastFrame.cid.toBinaryV1() : null; - const seq = lastFrame ? lastFrame.seq() + 1 : FeedConstraints.FirstFrameSeq; - const dto: types.FeedFrameDto = [prev, seq, frameOps]; - const frame = await FeedFrame.create(dto, cas); - frame.prev = lastFrame; - lastFrame = frame; - frames.push(frame); - } - return frames; - } - - protected static zipOps(baseFrames: FeedFrame[], forkFrames: FeedFrame[]): types.FeedOp[] { - const baseOps: types.FeedOp[] = []; - const forkOps: types.FeedOp[] = []; - for (const frame of baseFrames) baseOps.push(...frame.ops()); - for (const frame of forkFrames) forkOps.push(...frame.ops()); - const ops: types.FeedOp[] = []; - while (baseOps.length || forkOps.length) { - if (!baseOps.length) { - ops.push(...forkOps); - break; - } - if (!forkOps.length) { - ops.push(...baseOps); - break; - } - const baseOp = baseOps[0]; - if (baseOp[0] === FeedOpType.Delete) { - ops.push(baseOp); - baseOps.shift(); - continue; - } - const forkOp = forkOps[0]; - if (forkOp[0] === FeedOpType.Delete) { - ops.push(forkOp); - forkOps.shift(); - continue; - } - const baseId = baseOp[1]; - const forkId = forkOp[1]; - const cmp = hlc.cmpDto(baseId, forkId); - if (cmp === 0) { - ops.push(baseOp); - baseOps.shift(); - forkOps.shift(); - continue; - } else if (cmp < 0) { - ops.push(baseOp); - baseOps.shift(); - continue; - } else { - ops.push(forkOp); - forkOps.shift(); - continue; - } - } - return ops; - } - - protected static async findForkTriangle( - cas: CidCasStruct, - leftCid: Cid, - rightCid: Cid, - ): Promise<[commonParent: FeedFrame | null, leftFrames: FeedFrame[], rightFrames: FeedFrame[]]> { - const leftHeadFrame = await FeedFrame.read(leftCid, cas); - const rightHeadFrame = await FeedFrame.read(rightCid, cas); - const leftFrames: FeedFrame[] = [leftHeadFrame]; - const rightFrames: FeedFrame[] = [rightHeadFrame]; - if (leftHeadFrame.seq() > rightHeadFrame.seq()) { - while (true) { - const prevCid = leftFrames[leftFrames.length - 1].prevCid(); - if (!prevCid) throw new Error('INVALID_STATE'); - const cid = Cid.fromBinaryV1(prevCid); - const frame = await FeedFrame.read(cid, cas); - leftFrames.push(frame); - if (frame.seq() <= rightHeadFrame.seq()) break; - } - } - if (leftHeadFrame.seq() < rightHeadFrame.seq()) { - while (true) { - const prevCid = rightFrames[rightFrames.length - 1].prevCid(); - if (!prevCid) throw new Error('INVALID_STATE'); - const cid = Cid.fromBinaryV1(prevCid); - const frame = await FeedFrame.read(cid, cas); - rightFrames.push(frame); - if (frame.seq() <= leftHeadFrame.seq()) break; - } - } - while (true) { - const leftFrame = leftFrames[leftFrames.length - 1]; - const rightFrame = rightFrames[rightFrames.length - 1]; - if (leftFrame.seq() !== rightFrame.seq()) throw new Error('INVALID_STATE'); - if (leftFrame.seq() === 0) return [null, leftFrames, rightFrames]; - if (leftFrame.cid.is(rightFrame.cid)) { - leftFrames.pop(); - rightFrames.pop(); - return [leftFrame, leftFrames, rightFrames]; - } - const prevLeft = leftFrame.prevCid(); - const prevRight = rightFrame.prevCid(); - if (!prevLeft || !prevRight) throw new Error('INVALID_STATE'); - leftFrames.push(await FeedFrame.read(Cid.fromBinaryV1(prevLeft), cas)); - rightFrames.push(await FeedFrame.read(Cid.fromBinaryV1(prevRight), cas)); - } - } - - /** - * Number of operations after which a new frame is created, otherwise the - * operations are appended to the current frame. - */ - public opsPerFrame: number; - - /** - * Emitted when the feed view changes (new entries are added or deleted). - */ - public onChange: FanOut = new FanOut(); - - protected head: FeedFrame | null = null; - protected tail: FeedFrame | null = null; - protected unsaved: types.FeedOp[] = []; - protected readonly deletes = new AvlSet(hlc.cmpDto); - protected readonly inserts = new AvlMap(hlc.cmpDto); - - constructor(protected readonly deps: FeedDependencies) { - this.opsPerFrame = deps.opsPerFrame ?? FeedConstraints.DefaultOpsPerFrameThreshold; - } - - public cid(): Cid | undefined { - return this.head?.cid; - } - - public async loadAll(): Promise { - while (this.hasMore()) await this.loadMore(); - } - - public clear(): void { - this.head = null; - this.tail = null; - this.unsaved = []; - this.deletes.clear(); - if (!this.inserts.isEmpty()) { - this.inserts.clear(); - this.onChange.emit(); - } - } - - public async merge(forkCid: Cid): Promise { - if (this.unsaved.length) await this.save(); - if (!this.head) throw new Error('INVALID_STATE'); - const frames = await Feed.merge(this.deps.cas, this.head.cid, forkCid, this.opsPerFrame); - for (const frame of frames) this.ingestFrameData(frame, true); - const head = frames[frames.length - 1]; - let curr = head; - for (let i = frames.length - 2; i >= 0; i--) { - curr.prev = frames[i]; - curr = frames[i]; - } - let existingCurr: FeedFrame | null = this.head; - while (existingCurr && existingCurr.seq() > curr.seq()) existingCurr = existingCurr.prev; - if (existingCurr) curr.prev = existingCurr.prev; - else this.tail = curr; - this.head = head; - this.onChange.emit(); - } - - // ------------------------------------------------------------------ FeedApi - - public add(data: unknown): hlc.HlcDto { - const id = this.deps.hlcs.inc(); - const idDto = hlc.toDto(id); - const op: types.FeedOpInsert = [FeedOpType.Insert, idDto, data]; - this.unsaved.push(op); - this.inserts.set(op[1], op); - this.onChange.emit(); - return idDto; - } - - public del(opId: hlc.HlcDto): void { - const op: types.FeedOpDelete = [FeedOpType.Delete, opId]; - this.unsaved.push(op); - this.deletes.add(opId); - const unsavedOpIndex = this.unsaved.findIndex( - ([type, id]) => type === FeedOpType.Insert && hlc.cmpDto(opId, id) === 0, - ); - if (unsavedOpIndex !== -1) this.unsaved.splice(unsavedOpIndex, 1); - const deleted = this.inserts.del(opId); - if (deleted) this.onChange.emit(); - } - - @mutex - public async loadHead(cid: Cid): Promise { - this.clear(); - const frame = await FeedFrame.read(cid, this.deps.cas); - this.head = this.tail = frame; - this.ingestFrameData(frame); - } - - @mutex - public async loadMore(): Promise { - const tail = this.tail; - if (!tail) return; - const prevCidDto = tail.data[0]; - if (!prevCidDto) return; - const cid = Cid.fromBinaryV1(prevCidDto); - const frame = this.tail?.prev ?? (await FeedFrame.read(cid, this.deps.cas)); - tail.prev = frame; - this.tail = frame; - this.ingestFrameData(frame); - } - - public hasMore(): boolean { - return !!this.tail?.data[0]; - } - - protected ingestFrameData(frame: FeedFrame, silent?: boolean): void { - const [, , ops] = frame.data; - for (const op of ops) { - switch (op[0]) { - case FeedOpType.Insert: { - const id = op[1]; - if (this.deletes.has(id)) continue; - this.inserts.set(id, op); - break; - } - case FeedOpType.Delete: { - const id = op[1]; - this.deletes.add(id); - this.inserts.del(id); - break; - } - } - } - if (!silent) this.onChange.emit(); - } - - @mutex - public async save(): Promise { - const hasUnsavedChanges = !!this.unsaved.length; - const head = this.head; - if (!hasUnsavedChanges) { - if (head) return head.cid; - const dto: types.FeedFrameDto = [null, 0, []]; - const frame = await FeedFrame.create(dto, this.deps.cas); - this.head = this.tail = frame; - this.unsaved = []; - return frame.cid; - } - if (!head) { - const dto: types.FeedFrameDto = [null, 0, this.unsaved]; - const frame = await FeedFrame.create(dto, this.deps.cas); - this.head = this.tail = frame; - this.unsaved = []; - return frame.cid; - } - const headOps = head.ops(); - const addToHead = headOps.length < this.opsPerFrame; - if (addToHead) { - const dto: types.FeedFrameDto = [head.prevCid(), head.seq(), [...headOps, ...this.unsaved]]; - const frame = await FeedFrame.create(dto, this.deps.cas); - frame.prev = head.prev; - this.head = frame; - this.unsaved = []; - return frame.cid; - } - const dto: types.FeedFrameDto = [head.cid.toBinaryV1(), head.seq() + 1, this.unsaved]; - const frame = await FeedFrame.create(dto, this.deps.cas); - frame.prev = head; - this.head = frame; - this.unsaved = []; - return frame.cid; - } - - // ---------------------------------------------------------------- SyncStore - - public readonly subscribe = (callback: () => void) => { - const unsubscribe = this.onChange.listen(() => callback()); - return () => unsubscribe(); - }; - - public readonly getSnapshot = (): types.FeedOpInsert[] => { - const ops: types.FeedOpInsert[] = []; - this.inserts.forEach((node) => ops.push(node.v)); - return ops; - }; -} diff --git a/src/web3/adl/feed-crdt/FeedFactory.ts b/src/web3/adl/feed-crdt/FeedFactory.ts deleted file mode 100644 index 5e077d55b6..0000000000 --- a/src/web3/adl/feed-crdt/FeedFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Feed, type FeedDependencies} from './Feed'; -import type {Cid} from '../../multiformats'; - -export interface FeedFactoryDependencies extends FeedDependencies {} - -export class FeedFactory { - constructor(protected readonly deps: FeedFactoryDependencies) {} - - public make(): Feed { - const feed = new Feed(this.deps); - return feed; - } - - public async load(cid: Cid): Promise { - const feed = new Feed(this.deps); - await feed.loadHead(cid); - return feed; - } -} diff --git a/src/web3/adl/feed-crdt/FeedFrame.ts b/src/web3/adl/feed-crdt/FeedFrame.ts deleted file mode 100644 index 4c6841a07f..0000000000 --- a/src/web3/adl/feed-crdt/FeedFrame.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {Cid} from '../../multiformats'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type {FeedFrameDto, FeedOp} from './types'; - -export class FeedFrame { - public static async read(cid: Cid, cas: CidCasStruct): Promise { - const dto = (await cas.get(cid)) as FeedFrameDto; - const frame = new FeedFrame(cid, dto); - return frame; - } - - public static async create(data: FeedFrameDto, cas: CidCasStruct): Promise { - const cid = await cas.put(data); - const frame = new FeedFrame(cid, data); - return frame; - } - - public prev: FeedFrame | null = null; - - constructor( - public cid: Cid, - public data: FeedFrameDto, - ) {} - - public prevCid(): Uint8Array | null { - return this.data[0]; - } - - public seq(): number { - return this.data[1]; - } - - public ops(): FeedOp[] { - return this.data[2]; - } -} diff --git a/src/web3/adl/feed-crdt/README.md b/src/web3/adl/feed-crdt/README.md deleted file mode 100644 index 0451cbafcc..0000000000 --- a/src/web3/adl/feed-crdt/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Feed CRDT - -Feed CRDT is an infinite length sorted list of entries. It supports only -*insert* and *delete* operations. A user can append a new entry to the end of -the list (insert), or delete an existing entry from the list. - -Entries are stored in *frames*. The feed is represented as a linked list of -frames. Each frame stores one or more operations. There are insert and delete -operations. Each operation is identified by a hybrid logical clock (HLC) -timestamp. diff --git a/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts b/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts deleted file mode 100644 index dab1eae8de..0000000000 --- a/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts +++ /dev/null @@ -1,391 +0,0 @@ -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {Feed} from '../Feed'; -import {FeedFactory, FeedFactoryDependencies} from '../FeedFactory'; -import {FeedFrame} from '../FeedFrame'; - -interface SetupOpts { - factory?: Pick; -} - -const setup = ({factory}: SetupOpts = {}) => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const feeds = new FeedFactory({...factory, hlcs, cas}); - return { - hlcs, - cas0, - cas, - feeds, - }; -}; - -describe('can merge', () => { - test('when common parent is null', async () => { - const {feeds, cas} = setup(); - const feed = feeds.make(); - feed.add({foo: 'bar'}); - const cid = await feed.save(); - const left = await feeds.load(cid); - const right = await feeds.load(cid); - expect(left.getSnapshot().length).toBe(1); - expect(left.getSnapshot()).toStrictEqual(right.getSnapshot()); - left.add('left1'); - left.add('left2'); - right.add('right1'); - const leftCid = await left.save(); - const rightCid = await right.save(); - const res = await Feed.merge(cas, leftCid, rightCid); - const mergedFeed = await feeds.load(res[0].cid); - expect(mergedFeed.getSnapshot().length).toBe(4); - expect(mergedFeed.getSnapshot()[0][2]).toStrictEqual({foo: 'bar'}); - expect(mergedFeed.getSnapshot()[1][2]).toStrictEqual('left1'); - expect(mergedFeed.getSnapshot()[2][2]).toStrictEqual('left2'); - expect(mergedFeed.getSnapshot()[3][2]).toStrictEqual('right1'); - }); - - const generateCommonParent = async () => { - const deps = setup({factory: {opsPerFrame: 3}}); - const common = deps.feeds.make(); - common.add('c1'); - common.add('c2'); - common.add('c3'); - await common.save(); - common.add('c4'); - common.add('c5'); - common.add('c6'); - await common.save(); - common.add('c7'); - common.add('c8'); - common.add('c9'); - await common.save(); - return {...deps, common}; - }; - - test('when both sides one step from common parent', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'r1', - ]); - }); - - test('when left side is further ahead', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - left.add('l4'); - left.add('l5'); - left.add('l6'); - await left.save(); - left.add('l7'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'l4', - 'l5', - 'l6', - 'l7', - 'r1', - 'r2', - ]); - }); - - test('when right side has not advanced', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - left.add('l4'); - left.add('l5'); - left.add('l6'); - await left.save(); - left.add('l7'); - await left.save(); - const frames = await Feed.merge(cas, left.cid()!, common.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'l4', - 'l5', - 'l6', - 'l7', - ]); - }); - - test('when right side is further ahead', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'r1', - 'r2', - 'r3', - 'r4', - ]); - }); - - test('when right side is further ahead and applied first', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - await right.save(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'r1', - 'r2', - 'r3', - 'r4', - 'l1', - 'l2', - 'l3', - ]); - }); - - test('when left side has not advanced', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - right.add('r5'); - right.add('r6'); - await right.save(); - right.add('r7'); - await right.save(); - const frames = await Feed.merge(cas, common.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'r1', - 'r2', - 'r3', - 'r4', - 'r5', - 'r6', - 'r7', - ]); - }); -}); - -describe('can merge inline', () => { - const generateCommonParent = async () => { - const deps = setup({factory: {opsPerFrame: 3}}); - const common = deps.feeds.make(); - common.add('c1'); - common.add('c2'); - common.add('c3'); - await common.save(); - common.add('c4'); - common.add('c5'); - common.add('c6'); - await common.save(); - common.add('c7'); - common.add('c8'); - common.add('c9'); - await common.save(); - return {...deps, common}; - }; - - test('when both sides one step from common parent', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const fork = await feeds.load(common.cid()!); - fork.add('f1'); - await fork.save(); - common.add('c10'); - await common.save(); - await common.merge(fork.cid()!); - expect(common.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - ]); - let frame: null | FeedFrame = (common as any).head; - let seq = frame?.seq() ?? 0; - while (frame) { - expect(frame.seq()).toBe(seq); - seq--; - frame = frame.prev; - } - }); - - test('can merge forks twice', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const fork = await feeds.load(common.cid()!); - fork.add('f1'); - await fork.save(); - common.add('c10'); - await common.save(); - await common.merge(fork.cid()!); - const fork2 = await feeds.load(common.cid()!); - common.add('c11'); - common.add('c12'); - fork2.add('f2'); - fork2.add('f3'); - fork2.add('f4'); - await fork2.save(); - await common.merge(fork2.cid()!); - expect(common.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - 'c11', - 'c12', - 'f2', - 'f3', - 'f4', - ]); - const common2 = await feeds.load(common.cid()!); - await common2.loadAll(); - expect(common2.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - 'c11', - 'c12', - 'f2', - 'f3', - 'f4', - ]); - }); -}); diff --git a/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts b/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts deleted file mode 100644 index e842510515..0000000000 --- a/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {FeedFactory} from '../FeedFactory'; -import {FeedFrameDto} from '../types'; - -const setup = () => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const feeds = new FeedFactory({hlcs, cas}); - return { - hlcs, - cas0, - cas, - feeds, - }; -}; - -test('can instantiate factory', async () => { - setup(); -}); - -test('can create a new empty feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - expect(feed.getSnapshot()).toStrictEqual([]); -}); - -test('can persist empty feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - await feed.save(); - expect(feed.getSnapshot()).toStrictEqual([]); -}); - -test('can add entries to a new feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - expect(feed.getSnapshot().length).toStrictEqual(0); - feed.add('asdf'); - expect(feed.getSnapshot().length).toStrictEqual(1); - expect(feed.getSnapshot()[0][2]).toStrictEqual('asdf'); - feed.add(['abc']); - expect(feed.getSnapshot().length).toStrictEqual(2); - expect(feed.getSnapshot()[1][2]).toStrictEqual(['abc']); -}); - -test('can save and restore a feed', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.add('asdf'); - feed1.add('abc'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res).toBeInstanceOf(Array); - expect(res.length).toBe(3); - expect(res[0]).toBe(null); - expect(res[1]).toBe(0); - expect(res[2]).toBeInstanceOf(Array); - const feed2 = await feeds.load(cid); - expect(feed2.getSnapshot().length).toStrictEqual(2); - expect(feed2.getSnapshot()[0][2]).toStrictEqual('asdf'); - expect(feed2.getSnapshot()[1][2]).toStrictEqual('abc'); -}); - -test('can delete an entry', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - const id1 = feed1.add('a1'); - const id2 = feed1.add('a2'); - const id3 = feed1.add('a3'); - expect(feed1.getSnapshot().length).toStrictEqual(3); - const cid1 = await feed1.save(); - expect(feed1.getSnapshot().length).toStrictEqual(3); - feed1.del(id1); - expect(feed1.getSnapshot().length).toStrictEqual(2); - expect(feed1.getSnapshot()[0][2]).toStrictEqual('a2'); - expect(feed1.getSnapshot()[1][2]).toStrictEqual('a3'); - const cid2 = await feed1.save(); - const res = (await cas.get(cid2)) as FeedFrameDto; - expect(res[2].length).toStrictEqual(4); - expect(cid2.is(cid1)).toBe(false); - const feed2 = await feeds.load(cid2); - expect(feed2.getSnapshot().length).toStrictEqual(2); - feed2.del(id3); - expect(feed2.getSnapshot().length).toStrictEqual(1); - const cid3 = await feed2.save(); - const feed3 = await feeds.load(cid3); - expect(feed3.getSnapshot().length).toStrictEqual(1); - expect(feed1.getSnapshot()[0][2]).toStrictEqual('a2'); - feed3.del(id2); - expect(feed3.getSnapshot().length).toStrictEqual(0); - const cid4 = await feed3.save(); - const feed4 = feeds.make(); - const cidEmpty = await feed4.save(); - expect(cid4.is(cidEmpty)).toBe(false); -}); - -test('delete items from another frame', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.opsPerFrame = 5; - const id1 = feed1.add('a1'); - const id2 = feed1.add('a2'); - const id3 = feed1.add('a3'); - const id4 = feed1.add('a4'); - const id5 = feed1.add('a5'); - const id6 = feed1.add('a6'); - const id7 = feed1.add('a7'); - const id8 = feed1.add('a8'); - const cid1 = await feed1.save(); - const id9 = feed1.add('a9'); - const id10 = feed1.add('a10'); - const id11 = feed1.add('a11'); - const id12 = feed1.add('a12'); - const id13 = feed1.add('a13'); - const id14 = feed1.add('a14'); - const cid2 = await feed1.save(); - const frame = (await cas.get(cid2)) as FeedFrameDto; - expect(frame[1]).toBe(1); - expect(feed1.getSnapshot()[1][2]).toBe('a2'); - await feed1.del(id2); - expect(feed1.getSnapshot()[1][2]).toBe('a3'); - const cid3 = await feed1.save(); - const feed2 = await feeds.load(cid3); - await feed2.loadAll(); - expect(feed2.getSnapshot()[1][2]).toBe('a3'); -}); - -test('combines entries in one block, if operation count is withing a threshold', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.add('entry-1'); - feed1.add('entry-2'); - await feed1.save(); - feed1.add('entry-3'); - feed1.add('entry-4'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res[2].length).toBe(4); -}); - -test('creates a new block if last one has 25 or more entries', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - for (let i = 0; i < feed1.opsPerFrame; i++) { - feed1.add('entry-' + i); - } - await feed1.save(); - feed1.add('entry-x'); - feed1.add('entry-y'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res[2].length).toBe(2); -}); - -test('can create and read out 200 entries', async () => { - const {feeds} = setup(); - const feed1 = feeds.make(); - for (let i = 0; i < 200; i++) { - feed1.add('entry-' + i); - await feed1.save(); - } - const cid = feed1.cid()!; - const feed2 = await feeds.load(cid); - await feed2.loadAll(); - const entries = feed2.getSnapshot(); - expect(entries.length).toBe(200); - for (let i = 0; i < 200; i++) { - expect(entries[i][2]).toBe('entry-' + i); - } -}); - -test('view shows only entries, which are not deleted', async () => { - const {hlcs, cas, feeds} = setup(); - const feed1 = feeds.make(); - feed1.add('a'); - const bId = feed1.add('b'); - feed1.add('c1'); - feed1.add('c2'); - feed1.add('c3'); - feed1.add('c4'); - feed1.add('c5'); - feed1.add('c6'); - feed1.add('c7'); - feed1.add('c8'); - feed1.add('c9'); - feed1.add('c10'); - feed1.add('c11'); - await feed1.save(); - feed1.del(bId); - const cid = await feed1.save(); - const feeds2 = new FeedFactory({hlcs, cas}); - const feed2 = await feeds2.load(cid); - await feed2.loadAll(); - const list = feed2.getSnapshot(); - expect(list[0][2]).toBe('a'); - expect(list[1][2]).toBe('c1'); - expect(list[2][2]).toBe('c2'); - expect(list[3][2]).toBe('c3'); - expect(list[4][2]).toBe('c4'); - expect(list[5][2]).toBe('c5'); - expect(list[6][2]).toBe('c6'); - expect(list[7][2]).toBe('c7'); - expect(list[8][2]).toBe('c8'); - expect(list[9][2]).toBe('c9'); - expect(list[10][2]).toBe('c10'); - expect(list[11][2]).toBe('c11'); -}); diff --git a/src/web3/adl/feed-crdt/constants.ts b/src/web3/adl/feed-crdt/constants.ts deleted file mode 100644 index a648e09ae8..0000000000 --- a/src/web3/adl/feed-crdt/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const enum FeedOpType { - Insert = 0, - Delete = 1, -} - -export const enum FeedConstraints { - /** - * Number of ops per frame that triggers a new frame creation. - */ - DefaultOpsPerFrameThreshold = 25, - - FirstFrameSeq = 0, -} diff --git a/src/web3/adl/feed-crdt/types.ts b/src/web3/adl/feed-crdt/types.ts deleted file mode 100644 index 0d0c7fd8ae..0000000000 --- a/src/web3/adl/feed-crdt/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type {FeedOpType} from './constants'; -import type {HlcDto} from '../../hlc'; -import type {Cid} from '../../multiformats'; - -/** - * Public API of a feed CRDT. - */ -export interface FeedApi { - /** Append a new item to the end of the feed. */ - add(data: unknown): HlcDto; - /** Delete some item from the feed. */ - del(operationId: HlcDto): void; - /** Load the latest entries of the feed. */ - loadHead(cid: Cid): Promise; - /** Load more entries of the feed. */ - loadMore(): Promise; - /** Whether feed has more frames to load. */ - hasMore(): boolean; - /** Persist any unsaved changes to the storage. */ - save(): Promise; -} - -/** - * Serialized feed frame. - */ -export type FeedFrameDto = [ - /** CID of the previous block. */ - prev: Uint8Array | null, - /** Monotonically incrementing sequence number for each new block. */ - seq: number, - /** A list of operations that current block contains. */ - ops: FeedOp[], -]; - -export type FeedOpInsert = [type: FeedOpType.Insert, id: HlcDto, value: unknown]; -export type FeedOpDelete = [type: FeedOpType.Delete, id: HlcDto]; -export type FeedOp = FeedOpInsert | FeedOpDelete; diff --git a/src/web3/adl/hamt-crdt/Hamt.ts b/src/web3/adl/hamt-crdt/Hamt.ts deleted file mode 100644 index 8d5edc957d..0000000000 --- a/src/web3/adl/hamt-crdt/Hamt.ts +++ /dev/null @@ -1,106 +0,0 @@ -import {Defer} from 'thingies/es2020/Defer'; -import {concurrency} from 'thingies/es2020/concurrencyDecorator'; -import {HamtFrame} from './HamtFrame'; -import * as hlc from '../../hlc'; -import {Cid} from '../../multiformats'; -import {sha256} from '../../crypto'; -import {toBuf} from '@jsonjoy.com/util/lib/buffers/toBuf'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type * as types from './types'; - -export interface HamtDependencies { - cas: CidCasStruct; - hlcs: hlc.HlcFactory; -} - -export class Hamt implements types.HamtApi { - protected _root: HamtFrame; - protected _dirty: boolean = false; - protected _loading: Promise | null = null; - - public cid: Cid | null = null; - public prev: Cid | null = null; - public seq: number = 0; - public ops: types.HamtOp[] = []; - - constructor(protected readonly deps: HamtDependencies) { - this._root = new HamtFrame(deps.cas, null); - } - - public hasChanges(): boolean { - return this._dirty; - } - - // ------------------------------------------------------------------ HamtApi - - public async load(cid: Cid): Promise { - this.cid = cid; - const future = new Defer(); - this._loading = future.promise; - try { - const [prev, seq, ops, data] = (await this.deps.cas.get(cid)) as types.HamtRootFrameDto; - this.prev = prev; - this.seq = seq; - this._root.loadDto(data, null); - future.resolve(); - } catch (err) { - future.reject(err); - } finally { - this._loading = null; - } - return future.promise; - } - - @concurrency(1) - public async put(key: Uint8Array | string, val: unknown): Promise { - if (this._loading) await this._loading; - const hashedKey = await this._key(key); - const id = this.deps.hlcs.inc(); - const idDto = hlc.toDto(id); - const op: types.HamtOp = [hashedKey, val, idDto]; - const success = await this._root.put(op); - if (success) this.ops.push(op); - return success; - } - - public async get(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - const hashedKey = await this._key(key); - return await this._root.get(hashedKey); - } - - /** Convert any key to buffer and prefix with 4-byte hash. */ - protected async _key(key: Uint8Array | string): Promise { - const keyBuf = typeof key === 'string' ? toBuf(key) : key; - const hash = await sha256(keyBuf); - const buf = new Uint8Array(4 + keyBuf.length); - buf.set(hash.subarray(0, 4), 0); - buf.set(keyBuf, 4); - return buf; - } - - public async has(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - return (await this.get(key)) !== undefined; - } - - public async del(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - return await this.put(key, undefined); - } - - public async save(): Promise<[head: Cid, affected: Cid[]]> { - const [, affected] = await this._root.saveChildren(); - const prev = this.cid; - const seq = this.seq + 1; - const frameDto = this._root.toDto(); - const dto: types.HamtRootFrameDto = [prev, seq, this.ops, frameDto]; - const cid = await this.deps.cas.put(dto); - this.cid = cid; - this.prev = prev; - this.seq = seq; - this.ops = []; - affected.push(cid); - return [cid, affected]; - } -} diff --git a/src/web3/adl/hamt-crdt/HamtFactory.ts b/src/web3/adl/hamt-crdt/HamtFactory.ts deleted file mode 100644 index 15397c35f6..0000000000 --- a/src/web3/adl/hamt-crdt/HamtFactory.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {Hamt, type HamtDependencies} from './Hamt'; - -export interface HamtFactoryDependencies extends HamtDependencies {} - -export class HamtFactory { - constructor(protected readonly deps: HamtFactoryDependencies) {} - - public make(): Hamt { - const hamt = new Hamt(this.deps); - return hamt; - } -} diff --git a/src/web3/adl/hamt-crdt/HamtFrame.ts b/src/web3/adl/hamt-crdt/HamtFrame.ts deleted file mode 100644 index 14b62e32c0..0000000000 --- a/src/web3/adl/hamt-crdt/HamtFrame.ts +++ /dev/null @@ -1,217 +0,0 @@ -import {Defer} from 'thingies/es2020/Defer'; -import {cmpUint8Array2} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array2'; -import {cmpDto} from '../../hlc'; -import {CidCasStruct} from '../../store/cas/CidCasStruct'; -import {Cid} from '../../multiformats'; -import {HamtConstraints} from './constants'; -import {mutex} from 'thingies/es2020/mutex'; -import type * as types from './types'; - -export class HamtFrame { - protected _entries: types.HamtFrameEntry[] = []; - protected _children: (HamtFrame | null)[] = [null]; - protected _loaded: boolean = false; - protected _loading: Defer | null = null; - /** Maybe instead of `_dirty`, just consider `id` === `null` to mean there are unsaved changes. */ - protected _dirty: boolean = false; - - constructor( - protected readonly cas: CidCasStruct, - public cid: Cid | null, - ) {} - - protected async ensureLoaded(): Promise { - if (this._loading) return this._loading.promise; - if (this._loaded) return; - if (!this.cid) return; - if (this.cid) await this.load(); - } - - /** - * Load the current node by CID from CAS. - * - * @param id CID of the node to load. - */ - @mutex - protected async load(): Promise { - const id = this.cid; - if (!id) throw new Error('ID_NOT_SET'); - this._loading = new Defer(); - const data = (await this.cas.get(id)) as types.HamtFrameDto; - this.loadDto(data, id); - this._loading.resolve(); - this._loading = null; - } - - /** - * Load the current node from known data. Provided data will be mutated - * internally, so it MUST not be used after this method is called. - * - * @param data Serialized data of the node to load. - * @param cid CID of the node to load, or null if CID is not known. - */ - public loadDto(data: types.HamtFrameDto, cid: Cid | null) { - this.cid = cid; - const [entries, children] = data; - this._entries = entries; - this._children = []; - const length = children.length; - for (let i = 0; i < length; i++) { - const childCid = children[i]; - const child = childCid ? new HamtFrame(this.cas, childCid) : null; - this._children.push(child); - } - this._loaded = true; - } - - public async getEntry(key: Uint8Array): Promise { - if (!this._loaded) await this.ensureLoaded(); - const entries = this._entries; - const length = entries.length; - for (let i = 0; i < length; i++) { - const entry = entries[i]; - const currentKey = entry[0]; - const comparison = cmpUint8Array2(currentKey, key); - if (comparison === 0) return entry; - const isKeySmallerThanCurrentKey = comparison > 0; - if (isKeySmallerThanCurrentKey) { - const child = this._children[i]; - if (!child) return undefined; - return await child.getEntry(key); - } - } - const lastChild = this._children[length]; - if (!lastChild) return undefined; - return await lastChild.getEntry(key); - } - - /** - * Recursively find a key value from current node or any of its children. - * - * @param key The key to fetch. - * @returns Returns the value if found, otherwise undefined. - */ - public async get(key: Uint8Array): Promise { - const entry = await this.getEntry(key); - if (!entry) return undefined; - return entry[1]; - } - - public async has(key: Uint8Array): Promise { - return (await this.get(key)) !== undefined; - } - - /** - * Insert or overwrite a key value pair in current node or any of its children. - * - * @param id HLC ID of the key. - * @param key Key to put. - * @param val Key value to put. - * @returns Returns true if the key was inserted. Insertion can fail if the - * ID of the insert operation is lower than the ID of the last write. - */ - public async put(op: types.HamtOp): Promise { - if (!this._loaded) await this.ensureLoaded(); - const [key, , id] = op; - const entries = this._entries; - const length = entries.length; - const insertInChild = length >= HamtConstraints.MaxEntriesPerFrame; - for (let i = 0; i < length; i++) { - const entry = entries[i]; - const currentKey = entry[0]; - const comparison = cmpUint8Array2(currentKey, key); - // Replace existing entry if keys are equal. - if (comparison === 0) { - const oldId = entry[2]; - if (cmpDto(oldId, id) >= 0) return false; - this._entries[i] = op; - this._markDirty(); - return true; - } - const isKeySmallerThanCurrentKey = comparison > 0; - if (isKeySmallerThanCurrentKey) { - if (insertInChild) { - // Insert at child node. - const wasInserted = await this._putAtChild(i, op); - if (wasInserted) this._markDirty(); - return wasInserted; - } else { - // Insert at current node, but shifting entries to the right. - this._entries.splice(i, 0, op); - this._children.splice(i, 0, null); - this._markDirty(); - return true; - } - } - } - // Insert into the last child. - if (insertInChild) { - const wasInserted = await this._putAtChild(length, op); - if (wasInserted) this._markDirty(); - return wasInserted; - } - // Append entry at the end of current block. - this._entries.push(op); - this._children.push(null); - this._markDirty(); - return true; - } - - protected _markDirty() { - this._dirty = true; - this.cid = null; - } - - private async _putAtChild(i: number, op: types.HamtOp): Promise { - let child = this._children[i]; - if (!child) child = this._children[i] = new HamtFrame(this.cas, null); - return await child.put(op); - } - - /** - * Save current node and all of its children. - * - * @returns Returns CID of current node, and a list of all affected CIDs, - * including the current CID. - */ - public async save(): Promise<[id: Cid, affected: Cid[]]> { - if (!this._loaded) await this.ensureLoaded(); - // TODO: Maybe throw if there are no changes. - if (this.cid && !this._dirty) return [this.cid, []]; - const [children, affected] = await this.saveChildren(); - const data: types.HamtFrameDto = [this._entries, children]; - const cid = await this.cas.put(data); - this.cid = cid; - affected.push(cid); - return [cid, affected]; - } - - /** - * Saves all "dirty" children and returns a list of all children. - * - * @returns Returns a list of stored CIDs and a all children of the current node, - * even the children which were not saved. - */ - public async saveChildren(): Promise<[children: (Cid | null)[], affected: Cid[]]> { - const ids: Cid[] = []; - const children: (Cid | null)[] = []; - const length = this._children.length; - for (let i = 0; i < length; i++) { - const child = this._children[i]; - if (!child) { - children.push(null); - continue; - } - const [childCid, affected] = await child.save(); - ids.push(...affected); - children.push(childCid); - } - return [children, ids]; - } - - public toDto(): types.HamtFrameDto { - const children = this._children.map((child) => (child ? child.cid : null)); - const dto: types.HamtFrameDto = [this._entries, children]; - return dto; - } -} diff --git a/src/web3/adl/hamt-crdt/README.md b/src/web3/adl/hamt-crdt/README.md deleted file mode 100644 index eb4d07b59b..0000000000 --- a/src/web3/adl/hamt-crdt/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# HAMT CRDT - -HAMT CRDT is an infinitely scalable key-value store that is implemented using as -a HAMT data structure as a CRDT. Each HAMT node stores up to 16 *key-value-time* -3-tuple entries and up to 17 links to child nodes. - -It supports only a single `.put()` operation. To delete a key, one puts an -`undefined` value. Each operation is accompanied by an ID (timestamp), expressed -as hybrid logical clock (HLC) value. Each key is a single value LWW register, -where the HLC clock is used for determining the winner. diff --git a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts deleted file mode 100644 index ceece93f88..0000000000 --- a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts +++ /dev/null @@ -1,265 +0,0 @@ -import {b} from '@jsonjoy.com/util/lib/buffers/b'; -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {HamtFactory} from '../HamtFactory'; -import {HamtRootFrameDto} from '../types'; - -const setup = () => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const hamts = new HamtFactory({hlcs, cas}); - const hamt = hamts.make(); - return { - hlcs, - cas0, - cas, - hamts, - hamt, - }; -}; - -const toArr = (buf: Uint8Array): number[] => { - const arr: number[] = []; - for (let i = 0; i < buf.length; i++) arr.push(buf[i]); - return arr; -}; - -describe('HamtCrdt', () => { - test('new database has no changes', async () => { - const {hamt} = setup(); - const res = hamt.hasChanges(); - expect(res).toBe(false); - }); - - describe('.get()', () => { - test('returns undefined in empty database', async () => { - const {hamt} = setup(); - const res1 = await hamt.get(b(1, 2, 3)); - const res2 = await hamt.get('test'); - expect(res1).toBe(undefined); - expect(res2).toBe(undefined); - }); - - test('returns undefined in empty database', async () => { - const {hamt} = setup(); - const res1 = await hamt.get(b(1, 2, 3)); - const res2 = await hamt.get('test'); - expect(res1).toBe(undefined); - expect(res2).toBe(undefined); - }); - }); - - describe('.put()', () => { - test('can store a string key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put('test', b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get('test'); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can store a multiple keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.put('/@user1', b(1, 2, 3)); - const res2 = await hamt.put('/@user2', b(4, 5, 6)); - const res3 = await hamt.put('/@user3', b(7, 7, 7)); - expect(res1).toBe(true); - expect(res2).toBe(true); - expect(res3).toBe(true); - const res4 = await hamt.get('/@user1'); - const res5 = await hamt.get('/@user2'); - const res6 = await hamt.get('/@user3'); - expect(res4).toStrictEqual(b(1, 2, 3)); - expect(res5).toStrictEqual(b(4, 5, 6)); - expect(res6).toStrictEqual(b(7, 7, 7)); - }); - - test('can store into a binary key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(69), b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get(b(69)); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can store into an empty key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(), b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get(b()); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can overwrite a key', async () => { - const {hamt} = setup(); - await hamt.put('foo', b(1, 2, 3)); - await hamt.put('foo', b(4, 5, 6)); - const res2 = await hamt.get('foo'); - expect(res2).toStrictEqual(b(4, 5, 6)); - }); - - test('can add more than 16 keys', async () => { - const {hamt} = setup(); - for (let i = 0; i < 30; i++) { - await hamt.put('foo-' + i, b(i)); - } - for (let i = 0; i < 30; i++) { - const res = await hamt.get('foo-' + i); - expect(res).toStrictEqual(b(i)); - } - }); - - test('can store any serializable value', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(), {foo: 123, bar: [true, false]}); - expect(res1).toBe(true); - const res2 = await hamt.get(b()); - expect(res2).toStrictEqual({foo: 123, bar: [true, false]}); - }); - }); - - describe('.has()', () => { - test('returns false for missing keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.has('a'); - const res2 = await hamt.has('b'); - expect(res1).toBe(false); - expect(res2).toBe(false); - }); - - test('returns true for existing keys', async () => { - const {hamt} = setup(); - await hamt.put('a', b()); - await hamt.put('b', b(1, 2, 3)); - const res1 = await hamt.has('a'); - const res2 = await hamt.has('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - }); - }); - - describe('.del()', () => { - test('can delete non-existing keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.del('a'); - const res2 = await hamt.del('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - const res3 = await hamt.get('a'); - const res4 = await hamt.get('b'); - expect(res3).toBe(undefined); - expect(res4).toBe(undefined); - }); - - test('can delete existing key', async () => { - const {hamt} = setup(); - await hamt.put('a', b()); - await hamt.put('b', b(1, 2, 3)); - const res1 = await hamt.del('a'); - const res2 = await hamt.del('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - const res3 = await hamt.get('a'); - const res4 = await hamt.get('b'); - expect(res3).toBe(undefined); - expect(res4).toBe(undefined); - }); - }); - - describe('.save()', () => { - test('can persist empty HAMT', async () => { - const {hamt, hamts} = setup(); - const [cid] = await hamt.save(); - expect(cid).toBeDefined(); - const hamt2 = hamts.make(); - await hamt2.load(cid); - }); - - test('can save a single key', async () => { - const {hamt, cas0} = setup(); - const data = 111; - await hamt.put('a', b(data)); - const size = await (cas0 as CidCasMemory).size(); - expect(size).toBe(0); - const [cid] = await hamt.save(); - expect(await (cas0 as CidCasMemory).size()).toBe(size + 1); - const blob = await cas0.get(cid); - const found = toArr(blob).findIndex((octet) => octet === data); - expect(found > -1).toBe(true); - }); - - test('can load saved data', async () => { - const {hamt, hamts} = setup(); - await hamt.put('a', b(123)); - const [cid] = await hamt.save(); - const hamt2 = hamts.make(); - const res1 = await hamt2.get('a'); - expect(res1).toBe(undefined); - await hamt2.load(cid); - const res2 = await hamt2.get('a'); - expect(res2).toStrictEqual(b(123)); - }); - - test('can save and load more than 16 keys of data', async () => { - const {hamt, hamts} = setup(); - const keys = 1111; - for (let i = 0; i < keys; i++) { - await hamt.put('a:' + i, b(i, i + 1, i + 2)); - } - const [cid, all] = await hamt.save(); - const hamt2 = hamts.make(); - await hamt2.load(cid); - for (let i = 0; i < keys; i++) { - const res = await hamt2.get('a:' + i); - expect(res).toStrictEqual(b(i, i + 1, i + 2)); - } - }); - - test('can save and load more than 16 keys .save()"ed at periodic intervals', async () => { - const {hamt, hamts} = setup(); - const keysPerSave = 10; - const saves = 20; - for (let j = 0; j < saves; j++) { - for (let i = 0; i < keysPerSave; i++) { - const key = j * keysPerSave + i; - await hamt.put('abc:' + key, b(key, key + 1, key + 2)); - } - await hamt.save(); - } - const hamt2 = hamts.make(); - await hamt2.load(hamt.cid!); - for (let j = 0; j < saves; j++) { - for (let i = 0; i < keysPerSave; i++) { - const key = j * keysPerSave + i; - const res = await hamt2.get('abc:' + key); - expect(res).toStrictEqual(b(key, key + 1, key + 2)); - } - } - }); - }); - - describe('operations', () => { - test('stores operations as keys are edited', async () => { - const {hamt} = setup(); - expect(hamt.ops.length).toBe(0); - await hamt.put(b(0), 0); - expect(hamt.ops.length).toBe(1); - expect(hamt.ops).toStrictEqual([[expect.any(Uint8Array), 0, expect.any(Array)]]); - await hamt.put(b(1), 1); - expect(hamt.ops.length).toBe(2); - expect(hamt.ops).toStrictEqual([ - [expect.any(Uint8Array), 0, expect.any(Array)], - [expect.any(Uint8Array), 1, expect.any(Array)], - ]); - await hamt.del(b(0)); - expect(hamt.ops.length).toBe(3); - expect(hamt.ops).toStrictEqual([ - [expect.any(Uint8Array), 0, expect.any(Array)], - [expect.any(Uint8Array), 1, expect.any(Array)], - [hamt.ops[0][0], undefined, expect.any(Array)], - ]); - }); - }); -}); diff --git a/src/web3/adl/hamt-crdt/constants.ts b/src/web3/adl/hamt-crdt/constants.ts deleted file mode 100644 index c12b921b13..0000000000 --- a/src/web3/adl/hamt-crdt/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const enum HamtConstraints { - MaxEntriesPerFrame = 16, -} diff --git a/src/web3/adl/hamt-crdt/types.ts b/src/web3/adl/hamt-crdt/types.ts deleted file mode 100644 index ee9e54d8d8..0000000000 --- a/src/web3/adl/hamt-crdt/types.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type {HlcDto} from '../../hlc'; -import type {Cid} from '../../multiformats'; - -export interface HamtApi { - load(id: Cid): Promise; - put(key: Uint8Array | string, val: unknown): Promise; - get(key: Uint8Array | string): Promise; - has(key: Uint8Array | string): Promise; - del(key: Uint8Array | string): Promise; - save(): Promise<[head: Cid, affected: Cid[]]>; -} - -/** Data of the root node of the HAMT. */ -export type HamtRootFrameDto = [ - /** - * CID of the previous state, previous root node. Zero, if there is no - * previous state. - */ - prev: Cid | null, - - /** - * Monotonically incrementing sequence number of the current state - * (increments with each new state). - */ - seq: number, - - /** - * An ordered list of operations which were performed on previous state to - * create the current state. Sorted, where the first operation is the oldest. - */ - ops: HamtOp[], - - /** - * Root level data of the HAMT. - */ - data: HamtFrameDto, -]; - -export type HamtFrameDto = [ - /** - * List of key value pairs stored in this node. - */ - entries: HamtFrameEntry[], - - /** - * Links to child nodes. This array must always be exactly one element larger - * than the `entries` array. Gaps are filled with nulls. - */ - children: (Cid | null)[], -]; - -export type HamtFrameEntry = [key: Uint8Array, val: unknown, id: HlcDto]; - -/** - * Key update operation. - */ -export type HamtOp = [ - /** Key that was updated. */ - key: Uint8Array, - /** New value of the key. */ - val: unknown, - /** ID of the operation as hybrid logical clock. */ - id: HlcDto, -]; diff --git a/src/web3/codec/Codecs.ts b/src/web3/codec/Codecs.ts deleted file mode 100644 index 7229348474..0000000000 --- a/src/web3/codec/Codecs.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {MulticodecIpld} from '../multiformats'; -import type {IpldCodec} from './types'; - -export class Codecs { - protected readonly map = new Map(); - - public set(codec: MulticodecIpld, jsonCodec: IpldCodec): void { - this.map.set(codec, jsonCodec); - } - - public get(codec: MulticodecIpld): IpldCodec | undefined { - return this.map.get(codec); - } - - public getOrThrow(codec: MulticodecIpld): IpldCodec { - const jsonCodec = this.get(codec); - if (!jsonCodec) throw new Error(`Codec ${codec} (0x${codec.toString(16)}) not found`); - return jsonCodec; - } -} diff --git a/src/web3/codec/codecs/__tests__/cbor.spec.ts b/src/web3/codec/codecs/__tests__/cbor.spec.ts deleted file mode 100644 index bcfc278429..0000000000 --- a/src/web3/codec/codecs/__tests__/cbor.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {Cid} from '../../../multiformats'; -import {cbor} from '../cbor'; - -test('can encode and decode CID', async () => { - const cid = await Cid.fromData(new Uint8Array([1, 2, 3, 4])); - const data = {foo: cid}; - const encoded = cbor.encoder.encode(data); - const decoded = cbor.decoder.decode(encoded); - expect(decoded).toStrictEqual(data); -}); - -test('can encode simplest fixture', async () => { - const data = [2]; - const encoded = cbor.encoder.encode(data); - const decoded = cbor.decoder.decode(encoded); - expect(decoded).toStrictEqual(data); - expect(encoded.length).toBe(2); - expect(encoded[0]).toBe(0x81); - expect(encoded[1]).toBe(0x02); - const cid = await Cid.fromDagCbor(encoded); - expect(cid.toText('base32')).toBe('bafyreihdb57fdysx5h35urvxz64ros7zvywshber7id6t6c6fek37jgyfe'); -}); diff --git a/src/web3/codec/codecs/cbor.ts b/src/web3/codec/codecs/cbor.ts deleted file mode 100644 index 96f18fc171..0000000000 --- a/src/web3/codec/codecs/cbor.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {CborEncoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoderDag'; -import {CborDecoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderDag'; -import {Cid} from '../../multiformats'; -import {writer} from './writer'; -import type {IpldCodec} from '../types'; - -const encoder = new (class extends CborEncoderDag { - public writeUnknown(val: unknown): void { - if (val instanceof Cid) this.writeTag(42, val.toBinary()); - else throw new Error('UNKNOWN_VALUE'); - } -})(writer); - -const decoder = new (class extends CborDecoderDag { - public readTagRaw(tag: number): Cid | unknown { - const value = this.val(); - if (tag === 42) return Cid.fromBinary(value as Uint8Array); - throw new Error('UNKNOWN_TAG'); - } -})(); - -export const cbor: IpldCodec = { - name: 'DAG-CBOR', - encoder, - decoder, -}; diff --git a/src/web3/codec/codecs/raw.ts b/src/web3/codec/codecs/raw.ts deleted file mode 100644 index 18101aa0bc..0000000000 --- a/src/web3/codec/codecs/raw.ts +++ /dev/null @@ -1,19 +0,0 @@ -// import {bufferToUint8Array} from '../../../util/buffers/bufferToUint8Array'; -import type {IpldCodec} from '../types'; - -export const raw: IpldCodec = { - name: 'Raw', - encoder: { - encode: (value: unknown): Uint8Array => { - if (value instanceof Uint8Array) return value; - // if (typeof Buffer !== 'undefined') { - // if(Buffer.isBuffer(value)) return bufferToUint8Array(value as Buffer); - // return bufferToUint8Array(Buffer.from(String(value))); - // } - throw new Error('VALUE_NOT_SUPPORTED'); - }, - }, - decoder: { - decode: (data: Uint8Array): unknown => data, - }, -}; diff --git a/src/web3/codec/codecs/writer.ts b/src/web3/codec/codecs/writer.ts deleted file mode 100644 index 8636e0f104..0000000000 --- a/src/web3/codec/codecs/writer.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; - -export const writer = new Writer(); diff --git a/src/web3/codec/index.ts b/src/web3/codec/index.ts deleted file mode 100644 index 77f5672358..0000000000 --- a/src/web3/codec/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {MulticodecIpld} from '../multiformats'; -import {Codecs} from './Codecs'; -import {raw} from './codecs/raw'; -import {cbor} from './codecs/cbor'; - -export * from './types'; -export * from './Codecs'; - -export const codecs = new Codecs(); - -codecs.set(MulticodecIpld.Raw, raw); -codecs.set(MulticodecIpld.Cbor, cbor); -codecs.set(MulticodecIpld.DagCbor, cbor); diff --git a/src/web3/codec/types.ts b/src/web3/codec/types.ts deleted file mode 100644 index 8c72eaf954..0000000000 --- a/src/web3/codec/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type {BinaryJsonDecoder, BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; - -export interface IpldCodec { - name: string; - encoder: Pick; - decoder: Pick; -} diff --git a/src/web3/constants.ts b/src/web3/constants.ts deleted file mode 100644 index 4b4dd07c1f..0000000000 --- a/src/web3/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Whether this code runs in a Node.js process. - */ -export const isNode: boolean = !!process?.versions?.node; diff --git a/src/web3/crypto/index.ts b/src/web3/crypto/index.ts deleted file mode 100644 index c7c41c22e3..0000000000 --- a/src/web3/crypto/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './webcrypto'; -export * from './sha256'; diff --git a/src/web3/crypto/sha256.ts b/src/web3/crypto/sha256.ts deleted file mode 100644 index 630abd2cb4..0000000000 --- a/src/web3/crypto/sha256.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {crypto} from './webcrypto'; - -export const sha256 = async (buf: Uint8Array): Promise => { - const ab = await crypto.subtle.digest('SHA-256', buf); - return new Uint8Array(ab); -}; diff --git a/src/web3/crypto/webcrypto.ts b/src/web3/crypto/webcrypto.ts deleted file mode 100644 index 51f41f6428..0000000000 --- a/src/web3/crypto/webcrypto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {isNode} from '../constants'; - -/** - * Universal Node.js/browser Web Crypto API reference. - * - * @todo Maybe create an isomorphic package for this? - */ -export const crypto: Crypto = isNode ? require('node:crypto').webcrypto : window.crypto; diff --git a/src/web3/hlc/Hlc.ts b/src/web3/hlc/Hlc.ts deleted file mode 100644 index 1b767a4ada..0000000000 --- a/src/web3/hlc/Hlc.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {HybridLogicalClock} from './types'; - -/** - * @todo Rename this such that it includes "timestamp" in the name. - */ -export class Hlc implements HybridLogicalClock { - public readonly ts: number; - public readonly seq: number; - public readonly node: number; - - constructor(ts: number, seq: number, node: number) { - this.ts = ts; - this.seq = seq; - this.node = node; - } -} diff --git a/src/web3/hlc/HlcFactory.ts b/src/web3/hlc/HlcFactory.ts deleted file mode 100644 index ccc508dbf9..0000000000 --- a/src/web3/hlc/HlcFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {create, inc} from './util'; -import {Hlc} from './Hlc'; - -export interface HlcFactoryDependencies { - /** Wall clock generator. */ - now?: () => number; - - /** ID of the current node/process. */ - node?: number; -} - -const defaultNow = () => Math.round(Date.now() / 1000) - 2272147200; - -export class HlcFactory { - public id: Hlc; - public readonly now: () => number; - public readonly node: number; - - constructor({now = defaultNow, node = Math.round(Math.random() * 0xff)}: HlcFactoryDependencies) { - this.now = now; - this.node = node; - this.id = new Hlc(now(), 0, node); - } - - public create(): Hlc { - return create(this.now(), this.node); - } - - /** - * @todo Is this method necessary? - */ - public inc(): Hlc { - const id = inc(this.id, this.now()); - this.id = id; - return id; - } -} diff --git a/src/web3/hlc/README.md b/src/web3/hlc/README.md deleted file mode 100644 index be09558e51..0000000000 --- a/src/web3/hlc/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Hybrid Logic Clock - -Hybrid logical clock is a 3-tuple of: (1) wall clock; (2) logical sequence -number; and (3) a node session ID. The wall clock is the current time in -seconds or milliseconds. The logical sequence number is a monotonically -increasing counter that is incremented whenever wall clocks are equal. The node -session ID is a unique identifier for the node generating the HLC. diff --git a/src/web3/hlc/__tests__/HlcFactory.spec.ts b/src/web3/hlc/__tests__/HlcFactory.spec.ts deleted file mode 100644 index 87b3f5885c..0000000000 --- a/src/web3/hlc/__tests__/HlcFactory.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {HlcFactory, HlcFactoryDependencies} from '../HlcFactory'; - -test('can construct HCL instances', () => { - const options: HlcFactoryDependencies = { - now: () => 5, - node: 0, - }; - const factory = new HlcFactory(options); - - const hlc1 = factory.create(); - const hlc2 = factory.create(); - - expect(hlc1.ts).toBe(5); - expect(hlc1.seq).toBe(0); - expect(hlc1.node).toBe(0); - - expect(hlc2.ts).toBe(5); - expect(hlc2.seq).toBe(0); - expect(hlc2.node).toBe(0); -}); diff --git a/src/web3/hlc/__tests__/util.spec.ts b/src/web3/hlc/__tests__/util.spec.ts deleted file mode 100644 index d403a6e494..0000000000 --- a/src/web3/hlc/__tests__/util.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import {create, toDto, inc, Hlc, cmp, cmpDto, merge, HlcDto} from '..'; - -describe('create()', () => { - test('can create an HLC instance', () => { - const hlc = create(5, 0); - expect(hlc.ts).toBe(5); - expect(hlc.seq).toBe(0); - expect(hlc.node).toBe(0); - }); -}); - -describe('toDto()', () => { - test('can serialize an HLC', () => { - const hlc = new Hlc(1, 2, 3); - const dto = toDto(hlc); - expect(dto).toStrictEqual([1, 2, 3]); - }); -}); - -describe('inc()', () => { - test('increments time, if wall clock incremented', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = inc(hlc1, 2); - expect(hlc2.ts).toBe(2); - expect(hlc2.seq).toBe(0); - expect(hlc2.node).toBe(3); - }); - - test('increments sequence number, if wall clock the same', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = inc(hlc1, 1); - expect(hlc2.ts).toBe(1); - expect(hlc2.seq).toBe(3); - expect(hlc2.node).toBe(3); - }); - - test('increments sequence number, if wall clock decremented (for whatever reason)', () => { - const hlc1 = new Hlc(11, 2, 3); - const hlc2 = inc(hlc1, 10); - expect(hlc2.ts).toBe(11); - expect(hlc2.seq).toBe(3); - expect(hlc2.node).toBe(3); - }); -}); - -describe('cmp()', () => { - test('compares by wall clocks, if they are different', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(4, 5, 6); - const result = cmp(hlc1, hlc2); - expect(result).toBe(1 - 4); - }); - - test('compares by wall clocks, if they are different - 2', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(4, 5, 6); - const result = cmp(hlc2, hlc1); - expect(result).toBe(4 - 1); - }); - - test('compares by sequence numbers, if wall clocks equal and sequence numbers different', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(1, 4, 5); - expect(cmp(hlc1, hlc2)).toBe(2 - 4); - expect(cmp(hlc2, hlc1)).toBe(4 - 2); - }); - - test('compares by node ID, if wall clocks equal and sequence numbers equal', () => { - const hlc1 = new Hlc(1, 2, 55); - const hlc2 = new Hlc(1, 2, 66); - expect(cmp(hlc1, hlc2)).toBe(55 - 66); - expect(cmp(hlc2, hlc1)).toBe(66 - 55); - }); -}); - -describe('cmpDto()', () => { - test('compares by wall clocks, if they are different', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [4, 5, 6]; - const result = cmpDto(hlc1, hlc2); - expect(result).toBe(1 - 4); - }); - - test('compares by wall clocks, if they are different - 2', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [4, 5, 6]; - const result = cmpDto(hlc2, hlc1); - expect(result).toBe(4 - 1); - }); - - test('compares by sequence numbers, if wall clocks equal and sequence numbers different', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [1, 4, 5]; - expect(cmpDto(hlc1, hlc2)).toBe(2 - 4); - expect(cmpDto(hlc2, hlc1)).toBe(4 - 2); - }); - - test('compares by node ID, if wall clocks equal and sequence numbers equal', () => { - const hlc1: HlcDto = [1, 2, 55]; - const hlc2: HlcDto = [1, 2, 66]; - expect(cmpDto(hlc1, hlc2)).toBe(55 - 66); - expect(cmpDto(hlc2, hlc1)).toBe(66 - 55); - }); -}); - -describe('merge()', () => { - test('uses wall clock, if wall clock greater than local and remote timestamp', () => { - const local = new Hlc(1, 2, 3); - const remote = new Hlc(4, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(10); - expect(merged.seq).toBe(0); - expect(merged.node).toBe(3); - }); - - test('uses greatest sequence number, when wall clocks equal', () => { - const local = new Hlc(100, 2, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(100); - expect(merged.seq).toBe(5); - expect(merged.node).toBe(3); - }); - - test('uses greatest sequence number, when wall clocks equal - 2', () => { - const local = new Hlc(100, 22, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(100); - expect(merged.seq).toBe(22); - expect(merged.node).toBe(3); - }); - - test('increments local sequence number, if local wall clock is greatest', () => { - const local = new Hlc(200, 22, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(200); - expect(merged.seq).toBe(23); - expect(merged.node).toBe(3); - }); - - test('increments remote sequence number, if remote wall clock is greatest', () => { - const local = new Hlc(200, 22, 3); - const remote = new Hlc(300, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(300); - expect(merged.seq).toBe(6); - expect(merged.node).toBe(3); - }); -}); diff --git a/src/web3/hlc/index.ts b/src/web3/hlc/index.ts deleted file mode 100644 index 37ea6e2f06..0000000000 --- a/src/web3/hlc/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type * from './types'; -export * from './util'; -export * from './Hlc'; -export * from './HlcFactory'; diff --git a/src/web3/hlc/types.ts b/src/web3/hlc/types.ts deleted file mode 100644 index 7b03b53d4e..0000000000 --- a/src/web3/hlc/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface HybridLogicalClock { - /** Timestamp, wall clock, number in seconds or milliseconds. */ - readonly ts: number; - /** Monotonically incrementing sequence counter. */ - readonly seq: number; - /** Process ID (aka site ID, session ID). */ - readonly node: number; -} - -/** - * Hybrid Logical Clock (HLC) as a tuple, used when serializing for storage in - * CBOR or JSON formats. - */ -export type HlcDto = [ts: number, seq: number, node: number]; diff --git a/src/web3/hlc/util.ts b/src/web3/hlc/util.ts deleted file mode 100644 index c77ece1f06..0000000000 --- a/src/web3/hlc/util.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Hlc} from './Hlc'; -import type {HlcDto, HybridLogicalClock} from './types'; - -export const create = (now: number, node: number): HybridLogicalClock => { - return new Hlc(now, 0, node); -}; - -export const toDto = (hlc: HybridLogicalClock): HlcDto => [hlc.ts, hlc.seq, hlc.node]; - -export const fromDto = ([ts, seq, node]: HlcDto): HybridLogicalClock => new Hlc(ts, seq, node); - -export const inc = (local: HybridLogicalClock, now: number): HybridLogicalClock => { - if (now > local.ts) return new Hlc(now, 0, local.node); - return new Hlc(local.ts, local.seq + 1, local.node); -}; - -export const cmp = (a: HybridLogicalClock, b: HybridLogicalClock): number => { - if (a.ts !== b.ts) return a.ts - b.ts; - if (a.seq !== b.seq) return a.seq - b.seq; - return a.node - b.node; -}; - -export const cmpDto = ([ts1, seq1, node1]: HlcDto, [ts2, seq2, node2]: HlcDto): number => { - if (ts1 !== ts2) return ts1 - ts2; - if (seq1 !== seq2) return seq1 - seq2; - return node1 - node2; -}; - -export const merge = (local: HybridLogicalClock, remote: HybridLogicalClock, now: number): HybridLogicalClock => { - if (now > local.ts && now > remote.ts) return new Hlc(now, 0, local.node); - if (local.ts === remote.ts) return new Hlc(local.ts, Math.max(local.seq, remote.seq), local.node); - if (local.ts > remote.ts) return new Hlc(local.ts, local.seq + 1, local.node); - return new Hlc(remote.ts, remote.seq + 1, local.node); -}; diff --git a/src/web3/multiformats/Cid.ts b/src/web3/multiformats/Cid.ts deleted file mode 100644 index 7391a6bc1b..0000000000 --- a/src/web3/multiformats/Cid.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as vuint from '../util/uvint'; -import * as multibase from './multibase'; -import {Multihash} from './Multihash'; -import {Multicodec, MulticodecIpld} from './constants'; - -export type CidVersion = 0 | 1; - -export class Cid { - public static fromBinary(buf: Uint8Array): Cid { - const isV0 = buf[0] === 0x12 && buf[1] === 0x20; - return isV0 ? Cid.fromBinaryV0(buf) : Cid.fromBinaryV1(buf); - } - - public static fromBinaryV0(buf: Uint8Array): Cid { - if (buf[0] !== 0x12 || buf[1] !== 0x20) throw new Error('EXPECTED_CIDV0'); - const hash = new Multihash(buf); - return new Cid(0, Multicodec.DagPb, hash); - } - - public static fromBinaryV1(buf: Uint8Array): Cid { - const [v1Tag, offset1] = vuint.read(buf, 0); - if (v1Tag !== Multicodec.CidV1) throw new Error('EXPECTED_CIDV1'); - const [contentType, offset2] = vuint.read(buf, offset1); - const hash = new Multihash(buf.slice(offset2)); - return new Cid(1, contentType, hash); - } - - public static fromText(text: string): Cid { - if (text.charCodeAt(0) === 81 && text.charCodeAt(1) === 109) { - const buf = multibase.decode('z' + text); - return Cid.fromBinaryV0(buf); - } - const buf = multibase.decode(text); - if (buf[0] === 0x12) throw new Error('UNSUPPORTED_CIDV0'); - return Cid.fromBinaryV1(buf); - } - - public static async fromData(data: Uint8Array, ipldType: MulticodecIpld = MulticodecIpld.Raw): Promise { - const hash = await Multihash.fromData(data); - return new Cid(1, ipldType, hash); - } - - public static async fromCbor(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.Cbor); - } - - public static async fromDagCbor(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.DagCbor); - } - - public static async fromJson(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.Json); - } - - public static async fromDagJson(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.DagJson); - } - - constructor( - public readonly v: CidVersion, - public readonly ipldType: number, - public readonly hash: Multihash, - ) {} - - public is(cid: Cid): boolean { - return this.v === cid.v && this.ipldType === cid.ipldType && this.hash.is(cid.hash); - } - - public toV0(): Cid { - if (this.v === 0) return this; - return new Cid(0, Multicodec.DagPb, this.hash); - } - - public toV1(): Cid { - if (this.v === 1) return this; - return new Cid(1, this.ipldType, this.hash); - } - - public toBinary(version: CidVersion = this.v): Uint8Array { - if (version === 0) return this.toBinaryV0(); - return this.toBinaryV1(); - } - - public toBinaryV0(): Uint8Array { - return this.hash.buf; - } - - public toBinaryV1(): Uint8Array { - let size = 2; - const contentType = this.ipldType; - if (contentType >= 0b10000000) size += 1; - if (contentType >= 0b10000000_0000000) size += 1; - if (contentType >= 0b10000000_0000000_0000000) throw new Error('UNSUPPORTED_IPLD_TYPE'); - const hash = this.hash; - const hashBuf = hash.buf; - size += hashBuf.length; - const buf = new Uint8Array(size); - buf[0] = Multicodec.CidV1; - const offset = vuint.write(buf, 1, contentType); - buf.set(hashBuf, offset); - return buf; - } - - public toText(format: multibase.BaseNameOrCode = 'base64url'): string { - if (this.v === 0) return this.toTextV0(); - const buf = multibase.encode(format, this.toBinaryV1()); - const str = String.fromCharCode(...buf); - return str; - } - - public toTextV0(): string { - const blob = this.toBinaryV0(); - const buf = multibase.encode('base58btc', blob); - const str = String.fromCharCode(...buf); - return str.slice(1); - } - - public toString(): string { - return this.toText(); - } -} diff --git a/src/web3/multiformats/Multihash.ts b/src/web3/multiformats/Multihash.ts deleted file mode 100644 index 367af4a8e7..0000000000 --- a/src/web3/multiformats/Multihash.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {cmpUint8Array} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array'; -import {crypto} from '../crypto'; -import * as uvint from '../util/uvint'; -import {Multicodec} from './constants'; - -export class Multihash { - public static async fromData(buf: Uint8Array) { - const hash = await crypto.subtle.digest('SHA-256', buf); - const byteLength = hash.byteLength; - const uint8 = new Uint8Array(2 + byteLength); - uint8[0] = Multicodec.Sha2_256; - uint8[1] = byteLength; - uint8.set(new Uint8Array(hash), 2); - return new Multihash(uint8); - } - - public static validate = (buf: Uint8Array) => { - const length = buf.length; - if (length < 2) throw new Error('INVALID_MULTIHASH'); - const [code, offset1] = uvint.read(buf, 0); - switch (code) { - case Multicodec.Sha2_256: { - break; - } - default: { - throw new Error('UNKNOWN_MULTICODEC'); - } - } - const [lengthHash, offset2] = uvint.read(buf, offset1); - if (offset2 + lengthHash !== length) throw new Error('INVALID_MULTIHASH'); - }; - - constructor(public readonly buf: Uint8Array) { - Multihash.validate(buf); - } - - public type(): number { - const [type] = uvint.read(this.buf, 0); - return type; - } - - public length(): number { - const buf = this.buf; - const [, offset] = uvint.read(buf, 0); - const [length] = uvint.read(buf, offset); - return length; - } - - public value(): Uint8Array { - const buf = this.buf; - const [, offset1] = uvint.read(buf, 0); - const [, offset2] = uvint.read(buf, offset1); - return buf.slice(offset2); - } - - public is(hash: Multihash): boolean { - return cmpUint8Array(this.buf, hash.buf); - } - - public toString16(): string { - let res = ''; - for (const byte of this.buf) { - const hex = byte.toString(16); - res += hex.length === 1 ? '0' + hex : hex; - } - return res; - } -} diff --git a/src/web3/multiformats/__tests__/Cid.spec.ts b/src/web3/multiformats/__tests__/Cid.spec.ts deleted file mode 100644 index 046f20c007..0000000000 --- a/src/web3/multiformats/__tests__/Cid.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {Cid} from '../Cid'; -import {Multicodec, MulticodecIpld} from '../constants'; - -describe('CID v0', () => { - test('can decode a sample CID', () => { - const txt = 'QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB'; - const cid = Cid.fromText(txt); - expect(cid.v).toBe(0); - expect(cid.ipldType).toBe(Multicodec.DagPb); - expect(cid.hash.type()).toBe(Multicodec.Sha2_256); - expect(cid.hash.length()).toBe(32); - expect(cid.hash.value()).toEqual( - new Uint8Array([ - 14, 112, 113, 197, 157, 243, 185, 69, 77, 29, 24, 161, 82, 112, 170, 54, 213, 79, 137, 96, 106, 87, 109, 198, - 33, 117, 122, 253, 68, 173, 29, 46, - ]), - ); - }); - - test('can encode a sample CID back', () => { - const txt = 'QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB'; - const cid = Cid.fromText(txt); - const txt2 = cid.toTextV0(); - expect(txt2).toEqual(txt); - }); -}); - -describe('CID v1', () => { - test('can convert CID v0 to v1', () => { - const txt = 'QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR'; - const cid = Cid.fromText(txt); - const cid2 = cid.toV1(); - expect(cid2.toText('base32')).toEqual('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'); - }); - - test('can convert CID v1 to v2', () => { - const txt = 'bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'; - const cid = Cid.fromText(txt); - const cid2 = cid.toV0(); - expect(cid2.toText()).toEqual('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'); - }); - - test('can decode a sample CID', () => { - const txt = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'; - const cid = Cid.fromText(txt); - expect(cid.v).toBe(1); - expect(cid.ipldType).toBe(Multicodec.DagPb); - expect(cid.hash.type()).toBe(Multicodec.Sha2_256); - expect(cid.hash.length()).toBe(32); - expect(cid.hash.value()).toEqual( - new Uint8Array([ - 195, 196, 115, 62, 200, 175, 253, 6, 207, 158, 159, 245, 15, 252, 107, 205, 46, 200, 90, 97, 112, 0, 75, 183, 9, - 102, 156, 49, 222, 148, 57, 26, - ]), - ); - }); - - test('can encode a sample CID back', () => { - const txt = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'; - const cid = Cid.fromText(txt); - const txt2 = cid.toText('base32'); - expect(txt2).toEqual(txt); - }); - - test('can create a CID from data', async () => { - const text = 'Merkleโ€“Damgรฅrd'; - const data = new TextEncoder().encode(text); - const cid = await Cid.fromData(data); - expect(cid.v).toBe(1); - expect(cid.ipldType).toBe(MulticodecIpld.Raw); - expect(cid.toText('base16')).toBe('f01551220' + '41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); - }); -}); diff --git a/src/web3/multiformats/__tests__/Multihash.spec.ts b/src/web3/multiformats/__tests__/Multihash.spec.ts deleted file mode 100644 index 0d08a23e67..0000000000 --- a/src/web3/multiformats/__tests__/Multihash.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {Multihash} from '../Multihash'; - -const text = 'Merkleโ€“Damgรฅrd'; -const data = new TextEncoder().encode(text); - -test('computes correctly SHA2-256 hash for sample data', async () => { - const hash = await Multihash.fromData(data); - expect(hash.toString16()).toBe('122041dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('by default uses SHA2-256 codec', async () => { - const hash = await Multihash.fromData(data); - expect(hash.type()).toBe(0x12); -}); - -test('returns 32 length for SHA2-256 codec', async () => { - const hash = await Multihash.fromData(data); - expect(hash.length()).toBe(32); -}); - -test('returns correct hash value', async () => { - const hash = await Multihash.fromData(data); - const value = hash.value(); - const hex = [...value].map((byte) => byte.toString(16).padStart(2, '0')).join(''); - expect(hex).toBe('41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('can create a Multihash instance from a hash', async () => { - const hash = new Multihash((await Multihash.fromData(data)).buf); - expect(hash.type()).toBe(0x12); - expect(hash.length()).toBe(32); - const value = hash.value(); - const hex = [...value].map((byte) => byte.toString(16).padStart(2, '0')).join(''); - expect(hex).toBe('41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('throws on hash being too short', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf.slice(0, -1); - const hash2 = new Multihash(hash.buf); - expect(() => new Multihash(buf)).toThrow('INVALID_MULTIHASH'); -}); - -test('throws on unknown hash codec', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf; - buf[0] = 0x00; - expect(() => new Multihash(buf)).toThrow('UNKNOWN_MULTICODEC'); -}); - -test('throws on invalid hash length', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf; - buf[1] = 31; - expect(() => new Multihash(buf)).toThrow('INVALID_MULTIHASH'); -}); diff --git a/src/web3/multiformats/constants.ts b/src/web3/multiformats/constants.ts deleted file mode 100644 index 45a919c34e..0000000000 --- a/src/web3/multiformats/constants.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @see https://github.com/multiformats/multicodec/blob/master/table.csv - */ -export const enum Multicodec { - CidV1 = 0x01, - CidV2 = 0x02, - CidV3 = 0x03, - Sha2_256 = 0x12, - Cbor = 0x51, - Raw = 0x55, - DagPb = 0x70, - DagCbor = 0x71, - Libp2pKey = 0x72, - GitRaw = 0x78, - TorrentInfo = 0x7b, - TorrentFile = 0x7c, - DagJose = 0x85, - DagCose = 0x86, - DagJson = 0x0129, - Json = 0x0200, -} - -export const enum MulticodecIpld { - Cbor = Multicodec.Cbor, - Raw = Multicodec.Raw, - DagPb = Multicodec.DagPb, - DagCbor = Multicodec.DagCbor, - Libp2pKey = Multicodec.Libp2pKey, - GitRaw = Multicodec.GitRaw, - TorrentInfo = Multicodec.TorrentInfo, - TorrentFile = Multicodec.TorrentFile, - DagJose = Multicodec.DagJose, - DagCose = Multicodec.DagCose, - DagJson = Multicodec.DagJson, - Json = Multicodec.Json, -} diff --git a/src/web3/multiformats/index.ts b/src/web3/multiformats/index.ts deleted file mode 100644 index d62e00661f..0000000000 --- a/src/web3/multiformats/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './constants'; -export * from './Cid'; diff --git a/src/web3/multiformats/multibase.ts b/src/web3/multiformats/multibase.ts deleted file mode 100644 index 6aaa8655eb..0000000000 --- a/src/web3/multiformats/multibase.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @todo The `multibase` is old, and does not have the best performance. - * Especially because it concatenates Uint8Arrays and returns back another - * Uint8Array. But the output of this text encoding is a string, it could - * encode to string directly. Same as what json-joy Base64 does. - */ -import {encode, decode, BaseNameOrCode} from 'multibase'; - -export {encode, decode, BaseNameOrCode}; diff --git a/src/web3/store/cas/CidCas.ts b/src/web3/store/cas/CidCas.ts deleted file mode 100644 index 08ab0b488e..0000000000 --- a/src/web3/store/cas/CidCas.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type {Cid} from '../../multiformats'; -import type {MulticodecIpld} from '../../multiformats/constants'; - -export interface CidCas { - get(cid: Cid): Promise; - has(cid: Cid): Promise; - del(cid: Cid): Promise; - put(value: Uint8Array, ipld?: MulticodecIpld): Promise; -} diff --git a/src/web3/store/cas/CidCasMemory.ts b/src/web3/store/cas/CidCasMemory.ts deleted file mode 100644 index dfec3d53ed..0000000000 --- a/src/web3/store/cas/CidCasMemory.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {Cid} from '../../multiformats'; -import {MulticodecIpld} from '../../multiformats/constants'; -import type {CidCas} from './CidCas'; - -export class CidCasMemory implements CidCas { - protected store: Map = new Map(); - - public async get(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - const value = this.store.get(key); - if (!value) throw new Error(`No value for CID: ${key}`); - return value; - } - - public async has(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - return this.store.has(key); - } - - public async del(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - this.store.delete(key); - } - - public async put(value: Uint8Array, ipldType: MulticodecIpld = MulticodecIpld.Raw): Promise { - const cid = await Cid.fromData(value, ipldType); - await new Promise((resolve) => setImmediate(resolve)); - const key = cid.toText(); - this.store.set(key, value); - return cid; - } - - public async size(): Promise { - return this.store.size; - } -} diff --git a/src/web3/store/cas/CidCasStruct.ts b/src/web3/store/cas/CidCasStruct.ts deleted file mode 100644 index a8fdcbb918..0000000000 --- a/src/web3/store/cas/CidCasStruct.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Cid, MulticodecIpld} from '../../multiformats'; -import type {CidCas} from './CidCas'; -import type {IpldCodec} from '../../codec'; - -export class CidCasStruct { - constructor( - protected readonly cas: CidCas, - protected readonly ipldType: MulticodecIpld, - protected readonly codec: IpldCodec, - ) {} - - public async get(cid: Cid): Promise { - const blob = await this.cas.get(cid); - return this.codec.decoder.decode(blob); - } - - public has(cid: Cid): Promise { - return this.cas.has(cid); - } - - public async del(cid: Cid): Promise { - await this.cas.del(cid); - } - - public async put(value: unknown): Promise { - const blob = this.codec.encoder.encode(value); - return this.cas.put(blob, this.ipldType); - } -} diff --git a/src/web3/store/cas/CidCasStructCbor.ts b/src/web3/store/cas/CidCasStructCbor.ts deleted file mode 100644 index 08f7cd4f8e..0000000000 --- a/src/web3/store/cas/CidCasStructCbor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {MulticodecIpld} from '../../multiformats'; -import {CidCasStruct} from './CidCasStruct'; -import {cbor} from '../../codec/codecs/cbor'; -import type {CidCas} from './CidCas'; -import type {IpldCodec} from '../../codec'; - -export class CidCasStructCbor extends CidCasStruct { - constructor( - protected readonly cas: CidCas, - protected readonly codec: IpldCodec = cbor, - ) { - super(cas, MulticodecIpld.Cbor, codec); - } -} diff --git a/src/web3/store/cas/README.md b/src/web3/store/cas/README.md deleted file mode 100644 index aea0cb6ac5..0000000000 --- a/src/web3/store/cas/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Content Addressable Storage (CAS) - -Content Addressable Storage (CAS) is a blob key-value database, where the key -is always the hash of the value (content), usually SHA256. diff --git a/src/web3/store/cas/__tests__/CidCasMemory.spec.ts b/src/web3/store/cas/__tests__/CidCasMemory.spec.ts deleted file mode 100644 index 8619f5ac53..0000000000 --- a/src/web3/store/cas/__tests__/CidCasMemory.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {CidCasMemory} from '../CidCasMemory'; - -describe('put()', () => { - test('can store and read a value', async () => { - const cas = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas.put(value); - const stored = await cas.get(cid); - expect(stored).toEqual(value); - }); -}); - -describe('del()', () => { - test('can remove an item', async () => { - const cas = new CidCasMemory(); - const value = new Uint8Array([25]); - const cid = await cas.put(value); - expect(await cas.has(cid)).toBe(true); - expect(await cas.has(cid)).toBe(true); - await cas.del(cid); - expect(await cas.has(cid)).toBe(false); - expect(await cas.has(cid)).toBe(false); - }); - - test('does not throw when deleting non-existing item', async () => { - const cas1 = new CidCasMemory(); - const cas2 = new CidCasMemory(); - const value = new Uint8Array([25]); - const cid = await cas1.put(value); - await cas2.del(cid); - await cas2.del(cid); - await cas2.del(cid); - }); -}); - -describe('has()', () => { - test('can check if item exists', async () => { - const cas1 = new CidCasMemory(); - const cas2 = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - const has2 = await cas2.has(cid); - expect(has1).toBe(true); - expect(has2).toBe(false); - }); - - test('returns false for deleted item', async () => { - const cas1 = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - expect(has1).toBe(true); - await cas1.del(cid); - const has2 = await cas1.has(cid); - expect(has2).toBe(false); - }); -}); diff --git a/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts b/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts deleted file mode 100644 index e7f400fd42..0000000000 --- a/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {CidCasMemory} from '../CidCasMemory'; -import {CidCasStructCbor} from '../CidCasStructCbor'; - -const setup = () => { - const cas = new CidCasStructCbor(new CidCasMemory()); - return {cas}; -}; - -describe('put()', () => { - test('can store and read a value', async () => { - const {cas} = setup(); - const value = {foo: 'bar', baz: 123}; - const cid = await cas.put(value); - const stored = await cas.get(cid); - expect(stored).toEqual(value); - }); -}); - -describe('del()', () => { - test('can remove an item', async () => { - const {cas} = setup(); - const value = {foo: 'bar'}; - const cid = await cas.put(value); - expect(await cas.has(cid)).toBe(true); - expect(await cas.has(cid)).toBe(true); - await cas.del(cid); - expect(await cas.has(cid)).toBe(false); - expect(await cas.has(cid)).toBe(false); - }); - - test('does not throw when deleting non-existing item', async () => { - const {cas: cas1} = setup(); - const {cas: cas2} = setup(); - const value = {foo: 'bar', baz: 123}; - const cid = await cas1.put(value); - await cas2.del(cid); - await cas2.del(cid); - await cas2.del(cid); - }); -}); - -describe('has()', () => { - test('can check if item exists', async () => { - const {cas: cas1} = setup(); - const {cas: cas2} = setup(); - const value = {foo: 'bar'}; - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - const has2 = await cas2.has(cid); - expect(has1).toBe(true); - expect(has2).toBe(false); - }); - - test('returns false for deleted item', async () => { - const {cas: cas1} = setup(); - const value = {foo: 'bar'}; - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - expect(has1).toBe(true); - await cas1.del(cid); - const has2 = await cas1.has(cid); - expect(has2).toBe(false); - }); -}); diff --git a/src/web3/util/__tests__/uvint.spec.ts b/src/web3/util/__tests__/uvint.spec.ts deleted file mode 100644 index de99c3cfc1..0000000000 --- a/src/web3/util/__tests__/uvint.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {read, write} from '../uvint'; - -describe('read', () => { - test('can read single byte uvint', () => { - expect(read(new Uint8Array([0]), 0)).toEqual([0, 1]); - expect(read(new Uint8Array([1]), 0)).toEqual([1, 1]); - expect(read(new Uint8Array([65]), 0)).toEqual([65, 1]); - expect(read(new Uint8Array([0b1111111]), 0)).toEqual([0b1111111, 1]); - }); - - test('can read two byte uvint', () => { - expect(read(new Uint8Array([0b10000000, 0b00000001]), 0)).toEqual([0x80, 2]); - expect(read(new Uint8Array([0b11111111, 0b00000001]), 0)).toEqual([0xff, 2]); - expect(read(new Uint8Array([0b10101100, 0b00000010]), 0)).toEqual([0x012c, 2]); - }); - - test('can read three byte uvint', () => { - expect(read(new Uint8Array([0b10000000, 0b10000000, 0b00000001]), 0)).toEqual([0x4000, 3]); - }); -}); - -describe('write', () => { - test('can write single byte uvint', () => { - const buf = new Uint8Array(4); - const offset = write(buf, 1, 5); - expect(offset).toEqual(2); - expect(buf[1]).toEqual(5); - }); - - test('can write two byte uvint', () => { - const buf = new Uint8Array(4); - const offset = write(buf, 2, 222); - expect(offset).toEqual(4); - expect(buf[2]).toEqual(222); - expect(buf[3]).toEqual(1); - }); - - describe('can write different positive integers', () => { - const integers = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0b0_0000000_0011100_1111101, - 0b0_0000100_0011100_1111101, 0b0_0000101_1011100_1111101, 0b0_0010101_1011100_1111101, - 0b0_0110101_1011100_1111101, 0b0_0111101_1011100_1111101, 0b0_1000000_0000000_0100000, - 0b0_1011000_0000000_0100000, 0b0_1011000_0000000_0100001, 0b0_1011000_0000000_0101111, - 0b0_1011000_0000000_0111111, 0b0_1011000_1110000_0111111, 0b0_1011111_1110000_0111111, - 0b0_1111111_1110000_0111111, 0b0_1111111_1110110_0111111, 0b0_1111111_1111111_1111111, - ]; - for (const int of integers) { - test(String(int), () => { - const buf = new Uint8Array(5); - const offset = write(buf, 1, int); - expect(offset).toBeLessThanOrEqual(4); - const [value, newOffset] = read(buf, 1); - expect(value).toEqual(int); - expect(newOffset).toEqual(offset); - }); - } - }); -}); diff --git a/src/web3/util/uvint.ts b/src/web3/util/uvint.ts deleted file mode 100644 index f92b8aea70..0000000000 --- a/src/web3/util/uvint.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Reads an unsigned variable length integer in MSB Multicodec format up to - * 3 bytes. - * - * @see https://github.com/multiformats/unsigned-varint - * - * @param buf Blob from where to read the unsigned variable integer. - * @param offset Offset from where to start reading. - * @returns - */ -export const read = (buf: Uint8Array, offset: number): [value: number, offset: number] => { - const octet1 = buf[offset++]; - if (!(octet1 & 0b10000000)) return [octet1, offset]; - const octet2 = buf[offset++]; - if (!(octet2 & 0b10000000)) return [(octet2 << 7) | (octet1 & 0b01111111), offset]; - const octet3 = buf[offset++]; - if (!(octet3 & 0b10000000)) return [(octet3 << 14) | ((octet2 & 0b01111111) << 7) | (octet1 & 0b01111111), offset]; - throw new Error('UNSUPPORTED_UVINT'); -}; - -/** - * Write up to 3 bytes of an unsigned variable length integer in MSB Multicodec - * format. - * - * @param buf Blob where to write the unsigned variable integer. - * @param offset Position where to start writing. - * @param value Value to write. - */ -export const write = (buf: Uint8Array, offset: number, value: number): number => { - if (value < 0b10000000) { - buf[offset] = value; - return offset + 1; - } - if (value < 0b10000000_00000000) { - buf[offset] = (value & 0b01111111) | 0b10000000; - buf[offset + 1] = value >> 7; - return offset + 2; - } - if (value < 0b10000000_00000000_00000000) { - buf[offset] = (value & 0b01111111) | 0b10000000; - buf[offset + 1] = ((value >> 7) & 0b01111111) | 0b10000000; - buf[offset + 2] = value >> 14; - return offset + 3; - } - throw new Error('UNSUPPORTED_UVINT'); -}; From 21527202f5571659e483f5f86496260c620955bc Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 15 Apr 2024 11:01:35 +0200 Subject: [PATCH 40/42] =?UTF-8?q?chore(reactive-rpc):=20=F0=9F=A4=96=20rem?= =?UTF-8?q?ove=20Reactive=20RPC=20and=20related=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/LocalHistoryCrud.ts | 96 -- src/json-crdt-repo/SessionHistory.ts | 78 -- src/json-crdt-repo/UndoRedoStack.ts | 41 - .../__tests__/LocalHistoryCrud.spec.ts | 96 -- .../remote/RemoteHistoryDemoServer.ts | 100 -- .../__tests__/RemoteHistoryServer.spec.ts | 36 - src/json-crdt-repo/remote/types.ts | 38 - src/json-crdt-repo/types.ts | 18 - src/reactive-rpc/README.md | 4 - src/reactive-rpc/__demos__/server.ts | 12 - src/reactive-rpc/__demos__/ws.ts | 13 - src/reactive-rpc/__tests__/e2e/README.md | 26 - src/reactive-rpc/__tests__/e2e/run.ts | 71 -- .../e2e/uws/http/FetchRpcClient.spec.ts | 68 -- .../e2e/uws/http/StreamingRpcClient.spec.ts | 86 -- .../e2e/uws/ws/RpcPersistentClient.spec.ts | 75 -- .../browser/createBinaryWsRpcClient.ts | 29 - .../browser/createJsonWsRpcClient.ts | 29 - src/reactive-rpc/browser/index.ts | 1 - src/reactive-rpc/common/channel/README.md | 1 - .../__tests__/PersistentChannel.spec.ts | 170 --- .../__tests__/WebSocketChannel.spec.ts | 282 ----- src/reactive-rpc/common/channel/channel.ts | 342 ------ src/reactive-rpc/common/channel/constants.ts | 6 - src/reactive-rpc/common/channel/index.ts | 3 - src/reactive-rpc/common/channel/mock.ts | 132 --- src/reactive-rpc/common/channel/types.ts | 10 - src/reactive-rpc/common/codec/RpcCodec.ts | 32 - src/reactive-rpc/common/codec/RpcCodecs.ts | 9 - .../common/codec/RpcMessageCodecs.ts | 16 - .../codec/binary/BinaryRpcMessageCodec.ts | 53 - .../common/codec/binary/README.md | 220 ---- .../__tests__/BinaryRpcMessageCodec.spec.ts | 28 - .../codec/binary/__tests__/automated.spec.ts | 18 - .../codec/binary/__tests__/decode.spec.ts | 91 -- .../codec/binary/__tests__/encode.spec.ts | 165 --- .../binary/__tests__/smoke-tests.spec.ts | 149 --- .../common/codec/binary/constants.ts | 10 - .../common/codec/binary/decode.ts | 106 -- src/reactive-rpc/common/codec/binary/index.ts | 2 - .../codec/compact/CompactRpcMessageCodec.ts | 112 -- .../__tests__/CompactRpcMessageCodec.spec.ts | 18 - .../codec/compact/__tests__/automated.spec.ts | 32 - .../compact/__tests__/compact-messages.ts | 267 ----- .../compact/__tests__/smoke-tests.spec.ts | 119 -- .../common/codec/compact/constants.ts | 13 - .../common/codec/compact/index.ts | 3 - .../common/codec/compact/types.ts | 40 - src/reactive-rpc/common/codec/constants.ts | 5 - .../json-rpc-2/JsonRpc2RpcMessageCodec.ts | 151 --- .../json-rpc-2/__tests__/automated.spec.ts | 43 - .../common/codec/json-rpc-2/index.ts | 2 - .../common/codec/json-rpc-2/schema.ts | 102 -- .../common/codec/nominal/README.md | 16 - src/reactive-rpc/common/codec/types.ts | 12 - src/reactive-rpc/common/index.ts | 4 - src/reactive-rpc/common/messages/Value.ts | 14 - .../common/messages/__tests__/fixtures.ts | 241 ---- src/reactive-rpc/common/messages/index.ts | 2 - src/reactive-rpc/common/messages/messages.ts | 422 ------- src/reactive-rpc/common/messages/types.ts | 34 - src/reactive-rpc/common/rpc/RpcDuplex.ts | 60 - .../common/rpc/RpcMessageBatchProcessor.ts | 90 -- .../common/rpc/RpcMessageStreamProcessor.ts | 248 ---- .../RpcMessageStreamProcessorLocalClient.ts | 33 - .../common/rpc/RpcPersistentClient.ts | 95 -- .../common/rpc/__tests__/RpcDuplex.spec.ts | 67 -- .../RpcMessageStreamProcessor-api.spec.ts | 37 - .../RpcMessageStreamProcessor.spec.ts | 1027 ----------------- ...sageStreamProcessorLocalClient-api.spec.ts | 27 - ...cMessageStreamProcessorLocalClient.spec.ts | 47 - .../rpc/__tests__/RpcPersistentClient.spec.ts | 44 - .../common/rpc/__tests__/api.spec.ts | 33 - .../common/rpc/__tests__/runApiTests.ts | 137 --- .../common/rpc/__tests__/sample-api.ts | 189 --- .../common/rpc/caller/ApiRpcCaller.ts | 39 - .../common/rpc/caller/ObjectValueCaller.ts | 128 -- .../common/rpc/caller/RpcCaller.ts | 230 ---- .../common/rpc/caller/TypeRouterCaller.ts | 120 -- .../common/rpc/caller/TypedApiCaller.ts | 93 -- .../__tests__/ObjectValueCaller.spec.ts | 16 - .../RpcApiCaller.polymorphic-results.spec.ts | 55 - .../rpc/caller/__tests__/RpcApiCaller.spec.ts | 121 -- .../common/rpc/caller/error/RpcError.ts | 107 -- .../common/rpc/caller/error/RpcErrorType.ts | 32 - .../error/__tests__/RpcErrorType.spec.ts | 18 - .../common/rpc/caller/error/index.ts | 2 - src/reactive-rpc/common/rpc/caller/index.ts | 2 - src/reactive-rpc/common/rpc/caller/types.ts | 16 - .../rpc/client/EncodedStaticRpcClient.ts | 40 - .../common/rpc/client/FetchRpcClient.ts | 64 - .../common/rpc/client/StaticRpcClient.ts | 102 -- .../common/rpc/client/StreamingRpcClient.ts | 253 ---- .../client/__tests__/StaticRpcClient.spec.ts | 22 - .../__tests__/StreamingRpcClient.spec.ts | 557 --------- src/reactive-rpc/common/rpc/client/index.ts | 2 - src/reactive-rpc/common/rpc/client/types.ts | 49 - src/reactive-rpc/common/rpc/index.ts | 6 - .../common/rpc/methods/StaticRpcMethod.ts | 32 - .../common/rpc/methods/StreamingRpcMethod.ts | 31 - src/reactive-rpc/common/rpc/methods/types.ts | 65 -- src/reactive-rpc/common/rpc/types.ts | 8 - src/reactive-rpc/common/rpc/validation.ts | 13 - .../common/testing/buildE2eClient.ts | 104 -- src/reactive-rpc/common/types.ts | 21 - src/reactive-rpc/common/util/TimedQueue.ts | 45 - .../common/util/__tests__/TimedQueue.spec.ts | 138 --- .../subscribeCompleteObserver.spec.ts | 181 --- .../__tests__/subscribeSyncObserver.spec.ts | 84 -- src/reactive-rpc/common/util/microtask.ts | 8 - src/reactive-rpc/common/util/of.ts | 16 - .../common/util/subscribeCompleteObserver.ts | 55 - .../common/util/subscribeSyncObserver.ts | 41 - src/reactive-rpc/server/context.ts | 213 ---- src/reactive-rpc/server/http1/Http1Server.ts | 281 ----- src/reactive-rpc/server/http1/RpcServer.ts | 200 ---- src/reactive-rpc/server/http1/context.ts | 55 - src/reactive-rpc/server/http1/errors.ts | 5 - src/reactive-rpc/server/http1/types.ts | 4 - src/reactive-rpc/server/http1/util.ts | 96 -- src/reactive-rpc/server/index.ts | 1 - src/reactive-rpc/server/uws/RpcApp.ts | 283 ----- src/reactive-rpc/server/uws/index.ts | 2 - src/reactive-rpc/server/uws/types-uws.ts | 204 ---- src/reactive-rpc/server/uws/types.ts | 31 - src/reactive-rpc/server/uws/util.ts | 16 - .../server/ws/codec/WsFrameDecoder.ts | 119 -- .../server/ws/codec/WsFrameEncoder.ts | 126 -- .../server/ws/codec/__tests__/decoder.spec.ts | 362 ------ .../server/ws/codec/__tests__/encoder.spec.ts | 208 ---- src/reactive-rpc/server/ws/codec/constants.ts | 14 - src/reactive-rpc/server/ws/codec/errors.ts | 11 - src/reactive-rpc/server/ws/codec/frames.ts | 45 - src/reactive-rpc/server/ws/codec/index.ts | 4 - .../server/ws/server/WsServerConnection.ts | 208 ---- .../__tests__/WsServerConnection.spec.ts | 287 ----- src/server/__bench__/ping.bench.ts | 59 - src/server/__tests__/block.spec.ts | 483 -------- src/server/__tests__/presence.spec.ts | 114 -- src/server/__tests__/pubsub.spec.ts | 110 -- src/server/__tests__/setup.ts | 18 - src/server/__tests__/util.spec.ts | 50 - src/server/index.ts | 22 - src/server/routes/block/index.ts | 42 - src/server/routes/block/methods/del.ts | 26 - src/server/routes/block/methods/get.ts | 42 - src/server/routes/block/methods/listen.ts | 40 - src/server/routes/block/methods/new.ts | 36 - src/server/routes/block/methods/scan.ts | 50 - src/server/routes/block/methods/upd.ts | 47 - src/server/routes/block/schema.ts | 60 - src/server/routes/index.ts | 34 - src/server/routes/presence/index.ts | 18 - src/server/routes/presence/methods/listen.ts | 41 - src/server/routes/presence/methods/remove.ts | 29 - src/server/routes/presence/methods/update.ts | 74 -- src/server/routes/presence/schema.ts | 16 - src/server/routes/pubsub/index.ts | 9 - src/server/routes/pubsub/listen.ts | 30 - src/server/routes/pubsub/publish.ts | 35 - src/server/routes/routes.ts | 16 - src/server/routes/system.ts | 4 - src/server/routes/types.ts | 14 - src/server/routes/util/index.ts | 70 -- src/server/services/PresenceService.ts | 101 -- src/server/services/PubSubService.ts | 36 - src/server/services/Services.ts | 15 - src/server/services/blocks/BlocksServices.ts | 161 --- src/server/services/blocks/MemoryStore.ts | 87 -- src/server/services/blocks/types.ts | 75 -- src/server/services/types.ts | 4 - 171 files changed, 14307 deletions(-) delete mode 100644 src/json-crdt-repo/LocalHistoryCrud.ts delete mode 100644 src/json-crdt-repo/SessionHistory.ts delete mode 100644 src/json-crdt-repo/UndoRedoStack.ts delete mode 100644 src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts delete mode 100644 src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts delete mode 100644 src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts delete mode 100644 src/json-crdt-repo/remote/types.ts delete mode 100644 src/json-crdt-repo/types.ts delete mode 100644 src/reactive-rpc/README.md delete mode 100644 src/reactive-rpc/__demos__/server.ts delete mode 100644 src/reactive-rpc/__demos__/ws.ts delete mode 100644 src/reactive-rpc/__tests__/e2e/README.md delete mode 100644 src/reactive-rpc/__tests__/e2e/run.ts delete mode 100644 src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts delete mode 100644 src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts delete mode 100644 src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts delete mode 100644 src/reactive-rpc/browser/createBinaryWsRpcClient.ts delete mode 100644 src/reactive-rpc/browser/createJsonWsRpcClient.ts delete mode 100644 src/reactive-rpc/browser/index.ts delete mode 100644 src/reactive-rpc/common/channel/README.md delete mode 100644 src/reactive-rpc/common/channel/__tests__/PersistentChannel.spec.ts delete mode 100644 src/reactive-rpc/common/channel/__tests__/WebSocketChannel.spec.ts delete mode 100644 src/reactive-rpc/common/channel/channel.ts delete mode 100644 src/reactive-rpc/common/channel/constants.ts delete mode 100644 src/reactive-rpc/common/channel/index.ts delete mode 100644 src/reactive-rpc/common/channel/mock.ts delete mode 100644 src/reactive-rpc/common/channel/types.ts delete mode 100644 src/reactive-rpc/common/codec/RpcCodec.ts delete mode 100644 src/reactive-rpc/common/codec/RpcCodecs.ts delete mode 100644 src/reactive-rpc/common/codec/RpcMessageCodecs.ts delete mode 100644 src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/README.md delete mode 100644 src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts delete mode 100644 src/reactive-rpc/common/codec/binary/constants.ts delete mode 100644 src/reactive-rpc/common/codec/binary/decode.ts delete mode 100644 src/reactive-rpc/common/codec/binary/index.ts delete mode 100644 src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts delete mode 100644 src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts delete mode 100644 src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts delete mode 100644 src/reactive-rpc/common/codec/compact/__tests__/compact-messages.ts delete mode 100644 src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts delete mode 100644 src/reactive-rpc/common/codec/compact/constants.ts delete mode 100644 src/reactive-rpc/common/codec/compact/index.ts delete mode 100644 src/reactive-rpc/common/codec/compact/types.ts delete mode 100644 src/reactive-rpc/common/codec/constants.ts delete mode 100644 src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts delete mode 100644 src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts delete mode 100644 src/reactive-rpc/common/codec/json-rpc-2/index.ts delete mode 100644 src/reactive-rpc/common/codec/json-rpc-2/schema.ts delete mode 100644 src/reactive-rpc/common/codec/nominal/README.md delete mode 100644 src/reactive-rpc/common/codec/types.ts delete mode 100644 src/reactive-rpc/common/index.ts delete mode 100644 src/reactive-rpc/common/messages/Value.ts delete mode 100644 src/reactive-rpc/common/messages/__tests__/fixtures.ts delete mode 100644 src/reactive-rpc/common/messages/index.ts delete mode 100644 src/reactive-rpc/common/messages/messages.ts delete mode 100644 src/reactive-rpc/common/messages/types.ts delete mode 100644 src/reactive-rpc/common/rpc/RpcDuplex.ts delete mode 100644 src/reactive-rpc/common/rpc/RpcMessageBatchProcessor.ts delete mode 100644 src/reactive-rpc/common/rpc/RpcMessageStreamProcessor.ts delete mode 100644 src/reactive-rpc/common/rpc/RpcMessageStreamProcessorLocalClient.ts delete mode 100644 src/reactive-rpc/common/rpc/RpcPersistentClient.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcDuplex.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor-api.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient-api.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/api.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/runApiTests.ts delete mode 100644 src/reactive-rpc/common/rpc/__tests__/sample-api.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/ApiRpcCaller.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/ObjectValueCaller.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/RpcCaller.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/TypeRouterCaller.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/TypedApiCaller.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/__tests__/ObjectValueCaller.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.polymorphic-results.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/error/RpcError.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/error/RpcErrorType.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/error/index.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/index.ts delete mode 100644 src/reactive-rpc/common/rpc/caller/types.ts delete mode 100644 src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts delete mode 100644 src/reactive-rpc/common/rpc/client/FetchRpcClient.ts delete mode 100644 src/reactive-rpc/common/rpc/client/StaticRpcClient.ts delete mode 100644 src/reactive-rpc/common/rpc/client/StreamingRpcClient.ts delete mode 100644 src/reactive-rpc/common/rpc/client/__tests__/StaticRpcClient.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/client/__tests__/StreamingRpcClient.spec.ts delete mode 100644 src/reactive-rpc/common/rpc/client/index.ts delete mode 100644 src/reactive-rpc/common/rpc/client/types.ts delete mode 100644 src/reactive-rpc/common/rpc/index.ts delete mode 100644 src/reactive-rpc/common/rpc/methods/StaticRpcMethod.ts delete mode 100644 src/reactive-rpc/common/rpc/methods/StreamingRpcMethod.ts delete mode 100644 src/reactive-rpc/common/rpc/methods/types.ts delete mode 100644 src/reactive-rpc/common/rpc/types.ts delete mode 100644 src/reactive-rpc/common/rpc/validation.ts delete mode 100644 src/reactive-rpc/common/testing/buildE2eClient.ts delete mode 100644 src/reactive-rpc/common/types.ts delete mode 100644 src/reactive-rpc/common/util/TimedQueue.ts delete mode 100644 src/reactive-rpc/common/util/__tests__/TimedQueue.spec.ts delete mode 100644 src/reactive-rpc/common/util/__tests__/subscribeCompleteObserver.spec.ts delete mode 100644 src/reactive-rpc/common/util/__tests__/subscribeSyncObserver.spec.ts delete mode 100644 src/reactive-rpc/common/util/microtask.ts delete mode 100644 src/reactive-rpc/common/util/of.ts delete mode 100644 src/reactive-rpc/common/util/subscribeCompleteObserver.ts delete mode 100644 src/reactive-rpc/common/util/subscribeSyncObserver.ts delete mode 100644 src/reactive-rpc/server/context.ts delete mode 100644 src/reactive-rpc/server/http1/Http1Server.ts delete mode 100644 src/reactive-rpc/server/http1/RpcServer.ts delete mode 100644 src/reactive-rpc/server/http1/context.ts delete mode 100644 src/reactive-rpc/server/http1/errors.ts delete mode 100644 src/reactive-rpc/server/http1/types.ts delete mode 100644 src/reactive-rpc/server/http1/util.ts delete mode 100644 src/reactive-rpc/server/index.ts delete mode 100644 src/reactive-rpc/server/uws/RpcApp.ts delete mode 100644 src/reactive-rpc/server/uws/index.ts delete mode 100644 src/reactive-rpc/server/uws/types-uws.ts delete mode 100644 src/reactive-rpc/server/uws/types.ts delete mode 100644 src/reactive-rpc/server/uws/util.ts delete mode 100644 src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts delete mode 100644 src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts delete mode 100644 src/reactive-rpc/server/ws/codec/__tests__/decoder.spec.ts delete mode 100644 src/reactive-rpc/server/ws/codec/__tests__/encoder.spec.ts delete mode 100644 src/reactive-rpc/server/ws/codec/constants.ts delete mode 100644 src/reactive-rpc/server/ws/codec/errors.ts delete mode 100644 src/reactive-rpc/server/ws/codec/frames.ts delete mode 100644 src/reactive-rpc/server/ws/codec/index.ts delete mode 100644 src/reactive-rpc/server/ws/server/WsServerConnection.ts delete mode 100644 src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts delete mode 100644 src/server/__bench__/ping.bench.ts delete mode 100644 src/server/__tests__/block.spec.ts delete mode 100644 src/server/__tests__/presence.spec.ts delete mode 100644 src/server/__tests__/pubsub.spec.ts delete mode 100644 src/server/__tests__/setup.ts delete mode 100644 src/server/__tests__/util.spec.ts delete mode 100644 src/server/index.ts delete mode 100644 src/server/routes/block/index.ts delete mode 100644 src/server/routes/block/methods/del.ts delete mode 100644 src/server/routes/block/methods/get.ts delete mode 100644 src/server/routes/block/methods/listen.ts delete mode 100644 src/server/routes/block/methods/new.ts delete mode 100644 src/server/routes/block/methods/scan.ts delete mode 100644 src/server/routes/block/methods/upd.ts delete mode 100644 src/server/routes/block/schema.ts delete mode 100644 src/server/routes/index.ts delete mode 100644 src/server/routes/presence/index.ts delete mode 100644 src/server/routes/presence/methods/listen.ts delete mode 100644 src/server/routes/presence/methods/remove.ts delete mode 100644 src/server/routes/presence/methods/update.ts delete mode 100644 src/server/routes/presence/schema.ts delete mode 100644 src/server/routes/pubsub/index.ts delete mode 100644 src/server/routes/pubsub/listen.ts delete mode 100644 src/server/routes/pubsub/publish.ts delete mode 100644 src/server/routes/routes.ts delete mode 100644 src/server/routes/system.ts delete mode 100644 src/server/routes/types.ts delete mode 100644 src/server/routes/util/index.ts delete mode 100644 src/server/services/PresenceService.ts delete mode 100644 src/server/services/PubSubService.ts delete mode 100644 src/server/services/Services.ts delete mode 100644 src/server/services/blocks/BlocksServices.ts delete mode 100644 src/server/services/blocks/MemoryStore.ts delete mode 100644 src/server/services/blocks/types.ts delete mode 100644 src/server/services/types.ts diff --git a/src/json-crdt-repo/LocalHistoryCrud.ts b/src/json-crdt-repo/LocalHistoryCrud.ts deleted file mode 100644 index a886f3ba21..0000000000 --- a/src/json-crdt-repo/LocalHistoryCrud.ts +++ /dev/null @@ -1,96 +0,0 @@ -import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; -import {LogEncoder} from '../json-crdt/log/codec/LogEncoder'; -import {LogDecoder} from '../json-crdt/log/codec/LogDecoder'; -import type {CrudApi} from 'memfs/lib/crud/types'; -import type {Locks} from 'thingies/es2020/Locks'; -import type {Patch} from '../json-crdt-patch'; -import type {Log} from '../json-crdt/log/Log'; -import type {LocalHistory} from './types'; - -export const genId = (octets: number = 8): string => { - const uint8 = crypto.getRandomValues(new Uint8Array(octets)); - let hex = ''; - for (let i = 0; i < octets; i++) hex += uint8[i].toString(16).padStart(2, '0'); - return hex; -}; - -const STATE_FILE_NAME = 'state.seq.cbor'; - -export class LocalHistoryCrud implements LocalHistory { - protected encoder: LogEncoder = new LogEncoder({ - cborEncoder: new CborEncoder(), - }); - protected decoder: LogDecoder = new LogDecoder({ - cborDecoder: new CborDecoder(), - }); - - constructor( - protected readonly crud: CrudApi, - protected readonly locks: Locks, - ) {} - - public async create(collection: string[], log: Log, id: string = genId()): Promise<{id: string}> { - const blob = this.encode(log); - await this.lock(collection, id, async () => { - await this.crud.put([...collection, id], STATE_FILE_NAME, blob, {throwIf: 'exists'}); - }); - return {id}; - } - - protected encode(log: Log): Uint8Array { - // TODO: Add browser-native compression. Wrap the blob into `[]` TLV tuple. - return this.encoder.encode(log, { - format: 'seq.cbor', - model: 'binary', - history: 'binary', - noView: true, - }); - } - - public async read(collection: string[], id: string): Promise<{log: Log; cursor: string}> { - const blob = await this.crud.get([...collection, id], STATE_FILE_NAME); - const {frontier} = this.decoder.decode(blob, {format: 'seq.cbor', frontier: true}); - return { - log: frontier!, - cursor: '1', - }; - } - - public async readHistory(collection: string[], id: string, cursor: string): Promise<{log: Log; cursor: string}> { - const blob = await this.crud.get([...collection, id], STATE_FILE_NAME); - const {history} = this.decoder.decode(blob, {format: 'seq.cbor', history: true}); - return { - log: history!, - cursor: '', - }; - } - - public async update(collection: string[], id: string, patches: Patch[]): Promise { - await this.lock(collection, id, async () => { - const blob = await this.crud.get([...collection, id], STATE_FILE_NAME); - const decoded = this.decoder.decode(blob, {format: 'seq.cbor', history: true}); - const log = decoded.history!; - log.end.applyBatch(patches); - const blob2 = this.encode(log); - await this.crud.put([...collection, id], STATE_FILE_NAME, blob2, {throwIf: 'missing'}); - }); - } - - public async delete(collection: string[], id: string): Promise { - await this.lock(collection, id, async () => { - await this.crud.drop(collection, true); - }); - } - - protected async lock(collection: string[], id: string, fn: () => Promise): Promise { - const key = collection.join('/') + '/' + id; - await this.locks.lock( - key, - 250, - 500, - )(async () => { - await fn(); - }); - } -} diff --git a/src/json-crdt-repo/SessionHistory.ts b/src/json-crdt-repo/SessionHistory.ts deleted file mode 100644 index 6bbc7ef660..0000000000 --- a/src/json-crdt-repo/SessionHistory.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {createRace} from 'thingies/es2020/createRace'; -import {FanOutUnsubscribe} from 'thingies/es2020/fanout'; -import {InsValOp, Patch} from '../json-crdt-patch'; -import {ValNode} from '../json-crdt/nodes'; -import {toSchema} from '../json-crdt/schema/toSchema'; -import {Log} from '../json-crdt/log/Log'; -import {RedoItem, UndoItem, UndoRedoStack} from './UndoRedoStack'; -import type {LocalHistory} from './types'; - -class Undo implements UndoItem { - constructor(public readonly undo: () => Redo) {} -} - -class Redo implements RedoItem { - constructor(public readonly redo: () => Undo) {} -} - -export class SessionHistory { - constructor( - public readonly collection: string[], - public readonly id: string, - protected readonly local: LocalHistory, - ) {} - - private readonly __onPatchRace = createRace(); - - public attachUndoRedo(stack: UndoRedoStack): FanOutUnsubscribe { - // const onBeforePatch = (patch: Patch) => { - // this.__onPatchRace(() => { - // const undo = this.createUndo(patch); - // stack.push(undo); - // }); - // }; - // const unsubscribe = this.log.end.api.onBeforePatch.listen(onBeforePatch); - // return unsubscribe; - throw new Error('Method not implemented.'); - } - - public createUndo(patch: Patch): Undo { - const undoTasks: Array<() => void> = []; - const ops = patch.ops; - const length = ops.length; - for (let i = length - 1; i >= 0; i--) { - const op = ops[i]; - switch (op.name()) { - case 'ins_val': { - // const insOp = op as InsValOp; - // const valNode = this.log.end.index.get(insOp.obj); - // if (!(valNode instanceof ValNode)) throw new Error('INVALID_NODE'); - // const copy = toSchema(valNode.node()); - // const valNodeId = valNode.id; - // const task = () => { - // const end = this.log.end; - // const valNode = end.index.get(valNodeId); - // if (!valNode) return; - // end.api.wrap(valNode).asVal().set(copy); - // }; - // undoTasks.push(task); - } - } - } - const undo = new Undo(() => { - this.__onPatchRace(() => { - for (const task of undoTasks) task(); - }); - return new Redo(() => { - const undo = this.__onPatchRace(() => { - // // TODO: This line needs to be changed: - // const redoPatch = Patch.fromBinary(patch.toBinary()); - // this.log.end.api.builder.patch = redoPatch; - // return this.createUndo(redoPatch); - }); - return undo!; - }); - }); - return undo; - } -} diff --git a/src/json-crdt-repo/UndoRedoStack.ts b/src/json-crdt-repo/UndoRedoStack.ts deleted file mode 100644 index 526e8f1234..0000000000 --- a/src/json-crdt-repo/UndoRedoStack.ts +++ /dev/null @@ -1,41 +0,0 @@ -export interface UndoItem { - undo(): RedoItem; -} - -export interface RedoItem { - redo(): UndoItem; -} - -export class UndoRedoStack { - private undoStack: UndoItem[] = []; - private redoStack: RedoItem[] = []; - - public undoLength(): number { - return this.undoStack.length; - } - - public redoLength(): number { - return this.redoStack.length; - } - - public push(undo: UndoItem): RedoItem[] { - const redoStack = this.redoStack; - this.redoStack = []; - this.undoStack.push(undo); - return redoStack; - } - - public undo(): void { - const undo = this.undoStack.pop(); - if (!undo) return; - const redo = undo.undo(); - this.redoStack.push(redo); - } - - public redo(): void { - const redo = this.redoStack.pop(); - if (!redo) return; - const undo = redo.redo(); - this.undoStack.push(undo); - } -} diff --git a/src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts b/src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts deleted file mode 100644 index 234b206db3..0000000000 --- a/src/json-crdt-repo/__tests__/LocalHistoryCrud.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import {memfs} from 'memfs'; -import {NodeCrud} from 'memfs/lib/node-to-crud'; -import {Locks} from 'thingies/es2020/Locks'; -import {LocalHistoryCrud} from '../LocalHistoryCrud'; -import {Log} from '../../json-crdt/log/Log'; -import {Model} from '../../json-crdt/model'; - -const setup = async () => { - const {fs, vol} = memfs(); - const crud = new NodeCrud({fs: fs.promises, dir: '/'}); - const locks = new Locks(); - const local = new LocalHistoryCrud(crud, locks); - return { - fs, - vol, - crud, - locks, - local, - }; -}; - -test('can create a new document', async () => { - const {local} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({ - foo: 'spam', - }); - const log = Log.fromNewModel(model); - const {id} = await local.create(['test'], log); - expect(typeof id).toBe('string'); - expect(id.length > 6).toBe(true); - const {log: log2} = await local.read(['test'], id); - expect(log2.end.view()).toStrictEqual({foo: 'spam'}); -}); - -test('throws on non-existing document', async () => { - const {local} = await setup(); - try { - await local.read(['test'], 'asdfasdf'); - throw new Error('FAIL'); - } catch (err) { - expect((err as Error).message).toBe('Collection /test/asdfasdf does not exist'); - } -}); - -test('can delete a document', async () => { - const {local} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({ - foo: 'spam', - }); - const log = Log.fromNewModel(model); - const {id} = await local.create(['test'], log); - await local.read(['test'], id); - await local.delete(['test'], id); - try { - await local.read(['test'], id); - throw new Error('FAIL'); - } catch (err) { - expect((err as Error).message).toBe(`Collection /test/${id} does not exist`); - } -}); - -test('can update document', async () => { - const {local} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({ - foo: 'spam', - }); - const log = Log.fromNewModel(model); - const {id} = await local.create(['test'], log); - const {log: log2} = await local.read(['test'], id); - log2.end.api.obj([]).set({ - bar: 'eggs', - }); - const patch = log2.end.api.flush(); - await local.update(['test'], id, [patch]); - const {log: log3} = await local.read(['test'], id); - expect(log3.end.view()).toStrictEqual({ - foo: 'spam', - bar: 'eggs', - }); -}); - -test('can delete document', async () => { - const {local} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({ - foo: 'spam', - }); - const log = Log.fromNewModel(model); - const {id} = await local.create(['test'], log); - await local.read(['test'], id); - await local.delete(['test'], id); - expect(() => local.read(['test'], id)).rejects.toThrow(`Collection /test/${id} does not exist`); -}); diff --git a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts b/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts deleted file mode 100644 index f3ed21b557..0000000000 --- a/src/json-crdt-repo/remote/RemoteHistoryDemoServer.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type {CallerToMethods, TypedRpcClient} from '../../reactive-rpc/common'; -import type {JsonJoyDemoRpcCaller} from '../../server'; -import type {RemoteHistory, RemoteModel, RemotePatch} from './types'; - -type Methods = CallerToMethods; - -export type Cursor = number; - -export interface RemoteServerModel extends RemoteModel { - seq: number; - created: number; - updated: number; -} - -export interface RemoteServerPatch extends RemotePatch { - seq: number; -} - -export class RemoteHistoryDemoServer implements RemoteHistory { - constructor(protected readonly client: TypedRpcClient) {} - - public async create(id: string, patches: RemotePatch[]): Promise { - await this.client.call('block.new', { - id, - patches: patches.map((patch) => ({ - blob: patch.blob, - })), - }); - } - - /** - * Load latest state of the model, and any unmerged "tip" of patches - * it might have. - */ - public async read(id: string): Promise<{cursor: Cursor; model: RemoteServerModel; patches: RemoteServerPatch[]}> { - const {model, patches} = await this.client.call('block.get', {id}); - return { - cursor: model.seq, - model, - patches: [], - }; - } - - public async scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; patches: RemoteServerPatch[]}> { - const limit = 100; - const res = await this.client.call('block.scan', { - id, - seq: cursor, - limit: cursor + limit, - }); - if (res.patches.length === 0) { - return { - cursor, - patches: [], - }; - } - return { - cursor: res.patches[res.patches.length - 1].seq, - patches: res.patches, - }; - } - - public async scanBwd( - id: string, - cursor: Cursor, - ): Promise<{cursor: Cursor; model: RemoteServerModel; patches: RemoteServerPatch[]}> { - throw new Error('The "blocks.history" should be able to return starting model.'); - } - - public async update( - id: string, - cursor: Cursor, - patches: RemotePatch[], - ): Promise<{cursor: Cursor; patches: RemoteServerPatch[]}> { - const res = await this.client.call('block.upd', { - id, - patches: patches.map((patch, seq) => ({ - seq, - created: Date.now(), - blob: patch.blob, - })), - }); - return { - cursor: res.patches.length ? res.patches[res.patches.length - 1].seq : cursor, - patches: res.patches, - }; - } - - public async delete(id: string): Promise { - await this.client.call('block.del', {id}); - } - - /** - * Subscribe to the latest changes to the model. - * @param callback - */ - public listen(id: string, cursor: Cursor, callback: (changes: RemoteServerPatch[]) => void): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts b/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts deleted file mode 100644 index 31b85a7b14..0000000000 --- a/src/json-crdt-repo/remote/__tests__/RemoteHistoryServer.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Model} from '../../../json-crdt/model'; -import {buildE2eClient} from '../../../reactive-rpc/common/testing/buildE2eClient'; -import {createCaller} from '../../../server/routes/index'; -import {RemoteHistoryDemoServer} from '../RemoteHistoryDemoServer'; - -const setup = () => { - const {caller, router} = createCaller(); - const {client} = buildE2eClient(caller); - const remote = new RemoteHistoryDemoServer(client); - - return { - router, - caller, - client, - remote, - }; -}; - -let cnt = 0; -const genId = () => Math.random().toString(36).slice(2) + '-' + Date.now().toString(36) + '-' + cnt++; - -describe('.create()', () => { - test('can create a block with a simple patch', async () => { - const {remote, caller} = await setup(); - const model = Model.withLogicalClock(); - model.api.root({foo: 'bar'}); - const patch = model.api.flush(); - const blob = patch.toBinary(); - const id = genId(); - await remote.create(id, [{blob}]); - const {data} = await caller.call('block.get', {id}, {}); - // console.log(data.patches); - const model2 = Model.fromBinary(data.model.blob); - expect(model2.view()).toEqual({foo: 'bar'}); - }); -}); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts deleted file mode 100644 index 2f76a1037a..0000000000 --- a/src/json-crdt-repo/remote/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * A history of patches that have been applied to a model, stored on the - * "remote": (1) server; (2) content addressable storage; or (3) somewhere in a - * peer-to-peer network. - */ -export interface RemoteHistory { - create(id: string, patches: RemotePatch[]): Promise; - - /** - * Load latest state of the model, and any unmerged "tip" of patches - * it might have. - * - * @todo Maybe `state` and `tip` should be serialized to JSON? - */ - read(id: string): Promise<{cursor: Cursor; model: M; patches: P[]}>; - - scanFwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; patches: P[]}>; - - scanBwd(id: string, cursor: Cursor): Promise<{cursor: Cursor; model: M; patches: P[]}>; - - update(id: string, cursor: Cursor, patches: RemotePatch[]): Promise<{cursor: Cursor; patches: P[]}>; - - delete?(id: string): Promise; - - /** - * Subscribe to the latest changes to the model. - * @param callback - */ - listen(id: string, cursor: Cursor, callback: (patches: P[]) => void): void; -} - -export interface RemoteModel { - blob: Uint8Array; -} - -export interface RemotePatch { - blob: Uint8Array; -} diff --git a/src/json-crdt-repo/types.ts b/src/json-crdt-repo/types.ts deleted file mode 100644 index fa8a7a576f..0000000000 --- a/src/json-crdt-repo/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type {Patch} from '../json-crdt-patch'; -import type {Log} from '../json-crdt/log/Log'; -import type {Model} from '../json-crdt/model'; - -export interface LocalHistory { - create(collection: string[], log: Log): Promise<{id: string}>; - read(collection: string[], id: string): Promise<{log: Log; cursor: string}>; - readHistory(collection: string[], id: string, cursor: string): Promise<{log: Log; cursor: string}>; - update(collection: string[], id: string, patches: Patch[]): Promise; - delete(collection: string[], id: string): Promise; -} - -export interface EditingSessionHistory { - load(id: string): Promise; - loadHistory(id: string): Promise; - undo(id: string): Promise; - redo(id: string): Promise; -} diff --git a/src/reactive-rpc/README.md b/src/reactive-rpc/README.md deleted file mode 100644 index 8b98e4d124..0000000000 --- a/src/reactive-rpc/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Reactive-RPC - -Implements [Reactive-RPC](https://onp4.com/@vadim/p/qgzwgi42cz) protocol. - diff --git a/src/reactive-rpc/__demos__/server.ts b/src/reactive-rpc/__demos__/server.ts deleted file mode 100644 index 7f71178f21..0000000000 --- a/src/reactive-rpc/__demos__/server.ts +++ /dev/null @@ -1,12 +0,0 @@ -// npx ts-node src/reactive-rpc/__demos__/server.ts - -import {App} from 'uWebSockets.js'; -import {createCaller} from '../common/rpc/__tests__/sample-api'; -import {RpcApp} from '../server/uws/RpcApp'; - -const app = new RpcApp({ - uws: App({}), - caller: createCaller(), -}); - -app.startWithDefaults(); diff --git a/src/reactive-rpc/__demos__/ws.ts b/src/reactive-rpc/__demos__/ws.ts deleted file mode 100644 index 4c0a205a8d..0000000000 --- a/src/reactive-rpc/__demos__/ws.ts +++ /dev/null @@ -1,13 +0,0 @@ -// npx ts-node src/reactive-rpc/__demos__/ws.ts - -import {createCaller} from '../common/rpc/__tests__/sample-api'; -import {RpcServer} from '../server/http1/RpcServer'; - -const server = RpcServer.startWithDefaults({ - port: 3000, - caller: createCaller(), - logger: console, -}); - -// tslint:disable-next-line no-console -console.log(server + ''); diff --git a/src/reactive-rpc/__tests__/e2e/README.md b/src/reactive-rpc/__tests__/e2e/README.md deleted file mode 100644 index 6f4e23fcd4..0000000000 --- a/src/reactive-rpc/__tests__/e2e/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Reactive-RPC E2E tests - -This folder contains E2E tests for Reactive-RPC. You can run the tests with a -single command: - -``` -yarn test:reactive-rpc -``` - -Or you can start the server in one terminal window: - -``` -yarn demo:reactive-rpc:server -``` - -And run the test cases in another terminal window: - -``` -yarn test:reactive-rpc:jest -``` - -To run a specific test suite use this command: - -``` -TEST_E2E=1 npx jest --no-cache src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts -``` diff --git a/src/reactive-rpc/__tests__/e2e/run.ts b/src/reactive-rpc/__tests__/e2e/run.ts deleted file mode 100644 index 9f50727240..0000000000 --- a/src/reactive-rpc/__tests__/e2e/run.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {spawn} from 'child_process'; -import {Defer} from '../../../util/Defer'; - -const startServer = async () => { - const started = new Defer(); - const exitCode = new Defer(); - const cp = spawn('yarn', ['demo:reactive-rpc:server'], { - shell: true, - }); - process.on('exit', (code) => { - cp.kill(); - }); - cp.stdout.on('data', (data) => { - const line = String(data); - if (line.indexOf('SERVER_STARTED') > -1) started.resolve(); - process.stderr.write('[server] ' + line); - }); - cp.stderr.on('data', (data) => { - // tslint:disable-next-line no-console - console.error('Could not start server'); - started.reject(data); - process.stderr.write('ERROR: [server] ' + String(data)); - }); - cp.on('close', (code) => { - exitCode.resolve(code || 0); - process.stdout.write('[server] ' + `process exited with code ${code}\n`); - }); - return { - cp, - started: started.promise, - exitCode: exitCode.promise, - }; -}; - -const runTests = async () => { - const exitCode = new Defer(); - const cp = spawn('yarn', ['test:reactive-rpc:jest'], { - env: { - ...process.env, - TEST_E2E: '1', - }, - stdio: 'inherit', - }); - process.on('exit', (code) => { - cp.kill(); - }); - cp.on('close', (code) => { - exitCode.resolve(code || 0); - process.stdout.write('[jest] ' + `process exited with code ${code}\n`); - }); - return { - cp, - exitCode: exitCode.promise, - }; -}; - -(async () => { - try { - const server = await startServer(); - await server.started; - let exitCode = 0; - const jest = await runTests(); - exitCode = await jest.exitCode; - if (exitCode !== 0) throw exitCode; - process.exit(exitCode); - } catch (error) { - // tslint:disable-next-line no-console - console.error(error); - process.exit(1); - } -})(); diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts deleted file mode 100644 index 9de300faf7..0000000000 --- a/src/reactive-rpc/__tests__/e2e/uws/http/FetchRpcClient.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @jest-environment node - */ - -import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApiTests'; -import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; -import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import {FetchRpcClient} from '../../../../common/rpc/client/FetchRpcClient'; - -if (process.env.TEST_E2E) { - const codecs = new RpcCodecs(new Codecs(new Writer()), new RpcMessageCodecs()); - const {binary, compact, jsonRpc2} = codecs.messages; - const {json, cbor, msgpack} = codecs.value; - const cases: [specifier: string, protocol: RpcMessageCodec, req: JsonValueCodec, res: JsonValueCodec][] = [ - ['rpc.rx.compact.json', compact, json, json], - ['rpc.rx.compact.cbor', compact, cbor, cbor], - ['rpc.rx.compact.msgpack', compact, msgpack, msgpack], - ['rpc.rx.compact.json-cbor', compact, json, cbor], - ['rpc.rx.compact.json-msgpack', compact, json, msgpack], - ['rpc.rx.compact.cbor-json', compact, cbor, json], - ['rpc.rx.compact.cbor-msgpack', compact, cbor, msgpack], - ['rpc.rx.compact.msgpack-json', compact, msgpack, json], - ['rpc.rx.compact.msgpack-cbor', compact, msgpack, cbor], - - ['rpc.rx.binary.cbor', binary, cbor, cbor], - ['rpc.rx.binary.msgpack', binary, msgpack, msgpack], - ['rpc.rx.binary.json', binary, json, json], - ['rpc.rx.binary.json-cbor', binary, json, cbor], - ['rpc.rx.binary.json-msgpack', binary, json, msgpack], - ['rpc.rx.binary.cbor-json', binary, cbor, json], - ['rpc.rx.binary.cbor-msgpack', binary, cbor, msgpack], - ['rpc.rx.binary.msgpack-json', binary, msgpack, json], - ['rpc.rx.binary.msgpack-cbor', binary, msgpack, cbor], - - ['rpc.json2.verbose.json', jsonRpc2, json, json], - ['rpc.json2.verbose.cbor', jsonRpc2, cbor, cbor], - ['rpc.json2.verbose.msgpack', jsonRpc2, msgpack, msgpack], - ['rpc.json2.verbose.json-cbor', jsonRpc2, json, cbor], - ['rpc.json2.verbose.json-msgpack', jsonRpc2, json, msgpack], - ['rpc.json2.verbose.cbor-json', jsonRpc2, cbor, json], - ['rpc.json2.verbose.cbor-msgpack', jsonRpc2, cbor, msgpack], - ['rpc.json2.verbose.msgpack-json', jsonRpc2, msgpack, json], - ['rpc.json2.verbose.msgpack-cbor', jsonRpc2, msgpack, cbor], - ]; - - for (const [protocolSpecifier, msgCodec, reqCodec, resCodec] of cases) { - const setup: ApiTestSetup = async () => { - const port = +(process.env.PORT || 9999); - const url = `http://localhost:${port}/rpc`; - const client = new FetchRpcClient({ - url, - msgCodec, - reqCodec, - resCodec, - }); - return {client}; - }; - describe(`Content-Type: application/x.${protocolSpecifier}`, () => { - runApiTests(setup, {staticOnly: true}); - }); - } -} else { - test.skip('set TEST_E2E=1 env var to run this test suite', () => {}); -} diff --git a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts deleted file mode 100644 index 547953c523..0000000000 --- a/src/reactive-rpc/__tests__/e2e/uws/http/StreamingRpcClient.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @jest-environment node - */ - -import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApiTests'; -import {StreamingRpcClient} from '../../../../common'; -import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; -import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; - -if (process.env.TEST_E2E) { - const codecs = new RpcCodecs(new Codecs(new Writer()), new RpcMessageCodecs()); - const {binary, compact, jsonRpc2} = codecs.messages; - const {json, cbor, msgpack} = codecs.value; - const cases: [specifier: string, protocol: RpcMessageCodec, req: JsonValueCodec, res: JsonValueCodec][] = [ - ['rpc.rx.compact.json', compact, json, json], - ['rpc.rx.compact.cbor', compact, cbor, cbor], - ['rpc.rx.compact.msgpack', compact, msgpack, msgpack], - ['rpc.rx.compact.json-cbor', compact, json, cbor], - ['rpc.rx.compact.json-msgpack', compact, json, msgpack], - ['rpc.rx.compact.cbor-json', compact, cbor, json], - ['rpc.rx.compact.cbor-msgpack', compact, cbor, msgpack], - ['rpc.rx.compact.msgpack-json', compact, msgpack, json], - ['rpc.rx.compact.msgpack-cbor', compact, msgpack, cbor], - - ['rpc.rx.binary.cbor', binary, cbor, cbor], - ['rpc.rx.binary.msgpack', binary, msgpack, msgpack], - ['rpc.rx.binary.json', binary, json, json], - ['rpc.rx.binary.json-cbor', binary, json, cbor], - ['rpc.rx.binary.json-msgpack', binary, json, msgpack], - ['rpc.rx.binary.cbor-json', binary, cbor, json], - ['rpc.rx.binary.cbor-msgpack', binary, cbor, msgpack], - ['rpc.rx.binary.msgpack-json', binary, msgpack, json], - ['rpc.rx.binary.msgpack-cbor', binary, msgpack, cbor], - - ['rpc.json2.verbose.json', jsonRpc2, json, json], - ['rpc.json2.verbose.cbor', jsonRpc2, cbor, cbor], - ['rpc.json2.verbose.msgpack', jsonRpc2, msgpack, msgpack], - ['rpc.json2.verbose.json-cbor', jsonRpc2, json, cbor], - ['rpc.json2.verbose.json-msgpack', jsonRpc2, json, msgpack], - ['rpc.json2.verbose.cbor-json', jsonRpc2, cbor, json], - ['rpc.json2.verbose.cbor-msgpack', jsonRpc2, cbor, msgpack], - ['rpc.json2.verbose.msgpack-json', jsonRpc2, msgpack, json], - ['rpc.json2.verbose.msgpack-cbor', jsonRpc2, msgpack, cbor], - ]; - - for (const [protocolSpecifier, msgCodec, reqCodec, resCodec] of cases) { - const contentType = 'application/x.' + protocolSpecifier; - const setup: ApiTestSetup = async () => { - const client = new StreamingRpcClient({ - send: async (messages) => { - const port = +(process.env.PORT || 9999); - const url = `http://localhost:${port}/rpc`; - reqCodec.encoder.writer.reset(); - msgCodec.encodeBatch(reqCodec, messages); - const body = reqCodec.encoder.writer.flush(); - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': contentType, - }, - body, - }); - const buffer = await response.arrayBuffer(); - const data = new Uint8Array(buffer); - const responseMessages = msgCodec.decodeBatch(resCodec, data); - client.onMessages(responseMessages as any); - } catch (err) { - // tslint:disable-next-line:no-console - console.error(err); - } - }, - }); - return {client}; - }; - describe(`Content-Type: ${contentType}`, () => { - runApiTests(setup, {staticOnly: true}); - }); - } -} else { - test.skip('set TEST_E2E=1 env var to run this test suite', () => {}); -} diff --git a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts b/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts deleted file mode 100644 index 3972bf42f9..0000000000 --- a/src/reactive-rpc/__tests__/e2e/uws/ws/RpcPersistentClient.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @jest-environment node - */ - -import {ApiTestSetup, runApiTests} from '../../../../common/rpc/__tests__/runApiTests'; -import WebSocket from 'ws'; -import {RpcCodecs} from '../../../../common/codec/RpcCodecs'; -import {RpcMessageCodecs} from '../../../../common/codec/RpcMessageCodecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {RpcMessageCodec} from '../../../../common/codec/types'; -import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import {RpcCodec} from '../../../../common/codec/RpcCodec'; -import {RpcPersistentClient, WebSocketChannel} from '../../../../common'; - -if (process.env.TEST_E2E) { - const codecs = new RpcCodecs(new Codecs(new Writer()), new RpcMessageCodecs()); - const {binary, compact, jsonRpc2} = codecs.messages; - const {json, cbor, msgpack} = codecs.value; - const cases: [specifier: string, protocol: RpcMessageCodec, req: JsonValueCodec, res: JsonValueCodec][] = [ - ['rpc.rx.compact.json', compact, json, json], - ['rpc.rx.compact.cbor', compact, cbor, cbor], - ['rpc.rx.compact.msgpack', compact, msgpack, msgpack], - ['rpc.rx.compact.json-cbor', compact, json, cbor], - ['rpc.rx.compact.json-msgpack', compact, json, msgpack], - ['rpc.rx.compact.cbor-json', compact, cbor, json], - ['rpc.rx.compact.cbor-msgpack', compact, cbor, msgpack], - ['rpc.rx.compact.msgpack-json', compact, msgpack, json], - ['rpc.rx.compact.msgpack-cbor', compact, msgpack, cbor], - - ['rpc.rx.binary.cbor', binary, cbor, cbor], - ['rpc.rx.binary.msgpack', binary, msgpack, msgpack], - ['rpc.rx.binary.json', binary, json, json], - ['rpc.rx.binary.json-cbor', binary, json, cbor], - ['rpc.rx.binary.json-msgpack', binary, json, msgpack], - ['rpc.rx.binary.cbor-json', binary, cbor, json], - ['rpc.rx.binary.cbor-msgpack', binary, cbor, msgpack], - ['rpc.rx.binary.msgpack-json', binary, msgpack, json], - ['rpc.rx.binary.msgpack-cbor', binary, msgpack, cbor], - - ['rpc.json2.verbose.json', jsonRpc2, json, json], - ['rpc.json2.verbose.cbor', jsonRpc2, cbor, cbor], - ['rpc.json2.verbose.msgpack', jsonRpc2, msgpack, msgpack], - ['rpc.json2.verbose.json-cbor', jsonRpc2, json, cbor], - ['rpc.json2.verbose.json-msgpack', jsonRpc2, json, msgpack], - ['rpc.json2.verbose.cbor-json', jsonRpc2, cbor, json], - ['rpc.json2.verbose.cbor-msgpack', jsonRpc2, cbor, msgpack], - ['rpc.json2.verbose.msgpack-json', jsonRpc2, msgpack, json], - ['rpc.json2.verbose.msgpack-cbor', jsonRpc2, msgpack, cbor], - ]; - - for (const [protocolSpecifier, msgCodec, reqCodec, resCodec] of cases) { - const setup: ApiTestSetup = async () => { - const port = +(process.env.PORT || 9999); - const url = `ws://localhost:${port}/rpc`; - const codec = new RpcCodec(msgCodec, reqCodec, resCodec); - const client = new RpcPersistentClient({ - codec, - channel: { - newChannel: () => - new WebSocketChannel({ - newSocket: () => new WebSocket(url, [codec.specifier()]) as any, - }), - }, - }); - client.start(); - return {client}; - }; - describe(`protocol: application/x.${protocolSpecifier}`, () => { - runApiTests(setup); - }); - } -} else { - test.skip('set TEST_E2E=1 env var to run this test suite', () => {}); -} diff --git a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts b/src/reactive-rpc/browser/createBinaryWsRpcClient.ts deleted file mode 100644 index 78cfc585c7..0000000000 --- a/src/reactive-rpc/browser/createBinaryWsRpcClient.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RpcPersistentClient, WebSocketChannel} from '../common'; -import {RpcCodec} from '../common/codec/RpcCodec'; -import {BinaryRpcMessageCodec} from '../common/codec/binary'; - -/** - * Constructs a JSON Reactive RPC client. - * @param url RPC endpoint. - * @param token Authentication token. - * @returns An RPC client. - */ -export const createBinaryWsRpcClient = (url: string, token: string) => { - const writer = new Writer(1024 * 4); - const msg = new BinaryRpcMessageCodec(); - const req = new CborJsonValueCodec(writer); - const codec = new RpcCodec(msg, req, req); - const client = new RpcPersistentClient({ - codec, - channel: { - newChannel: () => - new WebSocketChannel({ - newSocket: () => new WebSocket(url, [codec.specifier(), token]), - }), - }, - }); - client.start(); - return client; -}; diff --git a/src/reactive-rpc/browser/createJsonWsRpcClient.ts b/src/reactive-rpc/browser/createJsonWsRpcClient.ts deleted file mode 100644 index 0478532008..0000000000 --- a/src/reactive-rpc/browser/createJsonWsRpcClient.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RpcPersistentClient, WebSocketChannel} from '../common'; -import {RpcCodec} from '../common/codec/RpcCodec'; -import {CompactRpcMessageCodec} from '../common/codec/compact'; - -/** - * Constructs a JSON Reactive RPC client. - * @param url RPC endpoint. - * @param token Authentication token. - * @returns An RPC client. - */ -export const createJsonWsRpcClient = (url: string, token: string) => { - const writer = new Writer(1024 * 4); - const msg = new CompactRpcMessageCodec(); - const req = new JsonJsonValueCodec(writer); - const codec = new RpcCodec(msg, req, req); - const client = new RpcPersistentClient({ - codec, - channel: { - newChannel: () => - new WebSocketChannel({ - newSocket: () => new WebSocket(url, [codec.specifier(), token]), - }), - }, - }); - client.start(); - return client; -}; diff --git a/src/reactive-rpc/browser/index.ts b/src/reactive-rpc/browser/index.ts deleted file mode 100644 index c15f26f4e5..0000000000 --- a/src/reactive-rpc/browser/index.ts +++ /dev/null @@ -1 +0,0 @@ -// ... diff --git a/src/reactive-rpc/common/channel/README.md b/src/reactive-rpc/common/channel/README.md deleted file mode 100644 index 2e6d048e36..0000000000 --- a/src/reactive-rpc/common/channel/README.md +++ /dev/null @@ -1 +0,0 @@ -A *channel* is essentially a WebSocket. It is a two-way communication medium. diff --git a/src/reactive-rpc/common/channel/__tests__/PersistentChannel.spec.ts b/src/reactive-rpc/common/channel/__tests__/PersistentChannel.spec.ts deleted file mode 100644 index 25eea541d3..0000000000 --- a/src/reactive-rpc/common/channel/__tests__/PersistentChannel.spec.ts +++ /dev/null @@ -1,170 +0,0 @@ -import {createWebSocketMock, MockWebSocket} from '../mock'; -import {WebSocketChannel, PersistentChannel, Channel, PersistentChannelParams} from '../channel'; -import {firstValueFrom} from 'rxjs'; -import {take} from 'rxjs/operators'; -import {of} from '../../util/of'; - -const setup = ( - params: Partial> = {}, -) => { - let ws: MockWebSocket; - const onClose = jest.fn(); - const onSend = jest.fn(); - const persistent = new PersistentChannel({ - ...params, - newChannel: () => - new WebSocketChannel({ - newSocket: jest.fn(() => { - const Socket = createWebSocketMock({ - onClose, - onSend, - }); - ws = new Socket('http://example.com'); - return ws; - }), - }), - }); - return { - ws: () => ws!, - persistent, - onClose, - onSend, - }; -}; - -test('when WebSocket connects open$ state is set to "true"', async () => { - const {ws, persistent} = setup(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - expect(channel).toBe(undefined); - expect(persistent.open$.getValue()).toBe(false); - persistent.start(); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - expect(channel).toBeInstanceOf(WebSocketChannel); - expect(persistent.open$.getValue()).toBe(true); -}); - -describe('.start()', () => { - test('initially persistent channel is not open, then automatically connects and sets the channel', async () => { - const {persistent} = setup(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - expect(channel).toBe(undefined); - expect(persistent.open$.getValue()).toBe(false); - persistent.start(); - await new Promise((r) => setTimeout(r, 1)); - expect(channel).toBeInstanceOf(WebSocketChannel); - expect(persistent.open$.getValue()).toBe(false); - }); - - test('start life-cycle can be started using .start$ observable', async () => { - const {ws, persistent} = setup(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - expect(channel).toBe(undefined); - expect(persistent.open$.getValue()).toBe(false); - await new Promise((r) => setTimeout(r, 1)); - expect(channel).toBe(undefined); - expect(persistent.open$.getValue()).toBe(false); - persistent.start(); - expect(channel).toBeInstanceOf(WebSocketChannel); - expect(persistent.open$.getValue()).toBe(false); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - expect(channel).toBeInstanceOf(WebSocketChannel); - expect(persistent.open$.getValue()).toBe(true); - }); -}); - -describe('.stop()', () => { - test('closes channel when .stop() is executed', async () => { - const {ws, onSend, onClose, persistent} = setup(); - persistent.start(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - await new Promise((r) => setTimeout(r, 1)); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - await firstValueFrom(persistent.send$('asdf').pipe(take(1))); - expect(onSend).toHaveBeenCalledTimes(1); - expect(persistent.open$.getValue()).toBe(true); - await firstValueFrom(persistent.send$('foo').pipe(take(1))); - expect(onSend).toHaveBeenCalledTimes(2); - expect(onClose).toHaveBeenCalledTimes(0); - persistent.stop(); - await new Promise((r) => setTimeout(r, 1)); - expect(onSend).toHaveBeenCalledTimes(2); - expect(onClose).toHaveBeenCalledTimes(1); - }); -}); - -describe('.send$() method', () => { - test('sends out message to the channel when channel is connected', async () => { - const {ws, onSend, persistent} = setup(); - persistent.start(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - expect(onSend).toHaveBeenCalledTimes(0); - expect(persistent.open$.getValue()).toBe(false); - await new Promise((r) => setTimeout(r, 1)); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - expect(onSend).toHaveBeenCalledTimes(0); - expect(persistent.open$.getValue()).toBe(true); - await firstValueFrom(persistent.send$('asdf').pipe(take(1))); - expect(onSend).toHaveBeenCalledTimes(1); - expect(onSend).toHaveBeenCalledWith('asdf'); - expect(persistent.open$.getValue()).toBe(true); - }); - - test('buffers and sends message out once channel is connected', async () => { - const {ws, onSend, persistent} = setup(); - persistent.start(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - expect(onSend).toHaveBeenCalledTimes(0); - const future = of(firstValueFrom(persistent.send$(new Uint8Array([1, 2, 3])).pipe(take(1)))); - expect(onSend).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 1)); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - await future; - expect(onSend).toHaveBeenCalledTimes(1); - expect(onSend).toHaveBeenCalledWith(new Uint8Array([1, 2, 3])); - }); - - test('does not send messages once .stop() is executed', async () => { - const {ws, onSend, persistent} = setup(); - persistent.start(); - let channel: Channel | undefined; - persistent.channel$.subscribe((ch) => { - channel = ch; - }); - await new Promise((r) => setTimeout(r, 1)); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - await firstValueFrom(persistent.send$('asdf').pipe(take(1))); - expect(onSend).toHaveBeenCalledTimes(1); - await firstValueFrom(persistent.send$('foo').pipe(take(1))); - expect(onSend).toHaveBeenCalledTimes(2); - persistent.stop(); - const a = of(firstValueFrom(persistent.send$('bar').pipe(take(1)))); - await new Promise((r) => setTimeout(r, 1)); - ws()._open(); - await new Promise((r) => setTimeout(r, 1)); - expect(onSend).toHaveBeenCalledTimes(2); - }); -}); diff --git a/src/reactive-rpc/common/channel/__tests__/WebSocketChannel.spec.ts b/src/reactive-rpc/common/channel/__tests__/WebSocketChannel.spec.ts deleted file mode 100644 index 754723a25b..0000000000 --- a/src/reactive-rpc/common/channel/__tests__/WebSocketChannel.spec.ts +++ /dev/null @@ -1,282 +0,0 @@ -import {createWebSocketMock, MockWebSocket} from '../mock'; -import {ChannelState, WebSocketChannel} from '../channel'; -import {WebSocketState} from '../constants'; - -test('creates raw socket and initializes it with listeners', () => { - let ws: MockWebSocket; - const newSocket = jest.fn(() => { - const Socket = createWebSocketMock({}); - ws = new Socket('http://example.com'); - return ws; - }); - - expect(newSocket).toHaveBeenCalledTimes(0); - - const rx = new WebSocketChannel({ - newSocket, - }); - - expect(newSocket).toHaveBeenCalledTimes(1); - expect(ws!.onclose).not.toBe(null); - expect(ws!.onerror).not.toBe(null); - expect(ws!.onmessage).not.toBe(null); - expect(ws!.onopen).not.toBe(null); -}); - -const setup = () => { - let ws: MockWebSocket; - const onClose = jest.fn(); - const onSend = jest.fn(); - const newSocket = jest.fn(() => { - const Socket = createWebSocketMock({ - onClose, - onSend, - }); - ws = new Socket('http://example.com'); - return ws; - }); - const rx = new WebSocketChannel({ - newSocket, - }); - - return { - ws: ws!, - rx, - onClose, - onSend, - }; -}; - -test('passes through websocket ready state', () => { - const {ws, rx} = setup(); - - expect(ws.readyState).toBe(WebSocketState.CONNECTING); - expect(rx.state$.getValue()).toBe(ChannelState.CONNECTING); - expect(rx.isOpen()).toBe(false); - - ws!._open(); - - expect(ws.readyState).toBe(WebSocketState.OPEN); - expect(rx.state$.getValue()).toBe(ChannelState.OPEN); - expect(rx.isOpen()).toBe(true); - - ws!._close(0, '', true); - - expect(ws.readyState).toBe(WebSocketState.CLOSED); - expect(rx.state$.getValue()).toBe(ChannelState.CLOSED); - expect(rx.isOpen()).toBe(false); -}); - -test('passes through websocket buffered amount', () => { - const {ws, rx} = setup(); - expect(rx.buffer()).toBe(0); - ws._bufferedAmount = 123; - expect(rx.buffer()).toBe(123); - ws._open(); - ws.send('test'); - expect(rx.buffer()).toBe(127); -}); - -test('passes through .close() method', () => { - const t1 = setup(); - expect(t1.onClose).toHaveBeenCalledTimes(0); - t1.rx.close(); - expect(t1.onClose).toHaveBeenCalledTimes(1); - expect(t1.onClose).toHaveBeenCalledWith(undefined, undefined); - - const t2 = setup(); - expect(t2.onClose).toHaveBeenCalledTimes(0); - t2.rx.close(1, 'reason'); - expect(t2.onClose).toHaveBeenCalledTimes(1); - expect(t2.onClose).toHaveBeenCalledWith(1, 'reason'); -}); - -test('passes through .send() method', () => { - const t1 = setup(); - expect(t1.onSend).toHaveBeenCalledTimes(0); - t1.rx.send('asdf'); - expect(t1.onSend).toHaveBeenCalledTimes(1); - expect(t1.onSend).toHaveBeenCalledWith('asdf'); -}); - -test('.send() returns buffered amount diff', () => { - const t1 = setup(); - const buffered = t1.rx.send('asdf'); - expect(buffered).toBe(4); -}); - -test('.send() returns buffered amount diff', () => { - const t1 = setup(); - const buffered = t1.rx.send('asdf'); - expect(buffered).toBe(4); -}); - -describe('.open$', () => { - test('does not emit at the beginning', async () => { - const {rx, ws} = setup(); - const open = jest.fn(); - rx.open$.subscribe(open); - expect(open).toHaveBeenCalledTimes(0); - }); - - test('emits when websocket opens', async () => { - const {rx, ws} = setup(); - const open = jest.fn(); - rx.open$.subscribe(open); - ws._open(); - expect(open).toHaveBeenCalledTimes(1); - }); - - test('immediately completes observable when open emits', async () => { - const {rx, ws} = setup(); - const complete = jest.fn(); - rx.open$.subscribe({complete}); - ws._open(); - expect(complete).toHaveBeenCalledTimes(1); - }); - - test('emits open event when subscription was late', async () => { - const {rx, ws} = setup(); - const open = jest.fn(); - ws._open(); - rx.open$.subscribe(open); - expect(open).toHaveBeenCalledTimes(1); - }); - - test('notifies multiple subscribers on open event', async () => { - const {rx, ws} = setup(); - ws._open(); - const open1 = jest.fn(); - const open2 = jest.fn(); - rx.open$.subscribe(open1); - rx.open$.subscribe(open2); - expect(open1).toHaveBeenCalledTimes(1); - expect(open2).toHaveBeenCalledTimes(1); - }); -}); - -describe('.close$', () => { - test('does not emit at the beginning', async () => { - const {rx} = setup(); - const close = jest.fn(); - rx.close$.subscribe(close); - expect(close).toHaveBeenCalledTimes(0); - }); - - test('emits when websocket closes', async () => { - const {rx, ws} = setup(); - const close = jest.fn(); - rx.close$.subscribe(close); - ws._close(0, 'test', true); - expect(close).toHaveBeenCalledTimes(1); - }); - - test('immediately completes the observable', async () => { - const {rx, ws} = setup(); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - rx.close$.subscribe({next, error, complete}); - ws._close(0, 'test', true); - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - }); - - test('passes through closing information', async () => { - const {rx, ws} = setup(); - const close = jest.fn(); - rx.close$.subscribe(close); - ws._close(123, 'test', true); - expect(close).toHaveBeenCalledTimes(1); - expect(close).toHaveBeenCalledWith([ - rx, - { - code: 123, - reason: 'test', - wasClean: true, - }, - ]); - }); - - test('when constructor fails closes with "CONSTRUCTOR" reason', async () => { - const newSocket = jest.fn(() => { - throw new Error('lala'); - }); - const rx = new WebSocketChannel({ - newSocket, - }); - const close = jest.fn(); - rx.close$.subscribe(close); - expect(close).toHaveBeenCalledTimes(1); - expect(close).toHaveBeenCalledWith([ - rx, - { - code: 0, - reason: 'CONSTRUCTOR', - wasClean: true, - }, - ]); - }); - - test('emits close event when subscription was late', async () => { - const {rx, ws} = setup(); - const close = jest.fn(); - ws._close(0, 'test', true); - rx.close$.subscribe(close); - expect(close).toHaveBeenCalledTimes(1); - }); - - test('notifies multiple subscribers on close event', async () => { - const {rx, ws} = setup(); - ws._close(0, 'test', true); - const close1 = jest.fn(); - const close2 = jest.fn(); - rx.close$.subscribe(close1); - rx.close$.subscribe(close2); - expect(close1).toHaveBeenCalledTimes(1); - expect(close2).toHaveBeenCalledTimes(1); - }); -}); - -describe('.error$', () => { - test('does not emit at the beginning', async () => { - const {rx, ws} = setup(); - const error = jest.fn(); - rx.error$.subscribe(error); - expect(error).toHaveBeenCalledTimes(0); - }); - - test('emits when error happens', async () => { - const {rx, ws} = setup(); - const error = jest.fn(); - rx.error$.subscribe(error); - ws._error(); - expect(error).toHaveBeenCalledTimes(1); - }); -}); - -describe('.message$', () => { - test('does not emit at the beginning', async () => { - const {rx, ws} = setup(); - const message = jest.fn(); - rx.message$.subscribe(message); - expect(message).toHaveBeenCalledTimes(0); - }); - - test('emits when websocket receives a message', async () => { - const {rx, ws} = setup(); - const message = jest.fn(); - rx.message$.subscribe(message); - ws._message('test'); - expect(message).toHaveBeenCalledTimes(1); - expect(message).toHaveBeenCalledWith('test'); - ws._message('test2'); - expect(message).toHaveBeenCalledTimes(2); - expect(message).toHaveBeenCalledWith('test2'); - const uint8 = new Uint8Array([1, 2, 3]); - ws._message(uint8); - expect(message).toHaveBeenCalledTimes(3); - expect(message).toHaveBeenCalledWith(uint8); - }); -}); diff --git a/src/reactive-rpc/common/channel/channel.ts b/src/reactive-rpc/common/channel/channel.ts deleted file mode 100644 index f31ecb37be..0000000000 --- a/src/reactive-rpc/common/channel/channel.ts +++ /dev/null @@ -1,342 +0,0 @@ -import type {WebSocketBase, CloseEventBase} from './types'; -import {Subject, ReplaySubject, BehaviorSubject, Observable, from} from 'rxjs'; -import {toUint8Array} from '@jsonjoy.com/util/lib/buffers/toUint8Array'; -import {delay, filter, map, skip, switchMap, take, takeUntil, tap} from 'rxjs/operators'; - -export const enum ChannelState { - CONNECTING = 0, - OPEN = 1, - CLOSED = 2, -} - -export interface Channel { - /** - * Emits on every new incoming message. - */ - message$: Observable; - - /** - * Emits an error in channel is raised. - */ - error$: Observable; - - /** - * Contains the current state of the channel and emits on state every state - * transition. - */ - state$: BehaviorSubject; - - /** - * Emits once when channel transitions into "open" state. - */ - open$: Observable; - - /** - * Emits once when channel transitions into "close" state. - */ - close$: Observable<[self: Channel, event: CloseEventBase]>; - - /** - * Whether the channel currently is open. - */ - isOpen(): boolean; - - /** - * Sends an outgoing message to the channel immediately. - * - * @param data A message payload. - * @returns Number of bytes buffered or -1 if channel is not ready. - */ - send(data: T): number; - - /** - * Waits for the channel to connect and only then sends out the message. If - * channel is closed, emits an error. - * - * @param data A message payload. - * @returns Number of bytes buffered. - */ - send$(data: T): Observable; - - /** - * Closes the channel. - * - * @param code Closure code. - * @param reason Closure reason. - */ - close(code?: number, reason?: string): void; - - /** - * Amount of buffered outgoing messages in bytes. - */ - buffer(): number; -} - -export interface WebSocketChannelParams { - /** - * Should return a new WebSocket instance. The binary type of the WebSocket - * will be automatically changed to "arraybuffer". - */ - newSocket: () => WebSocketBase; -} - -/** - * A `Channel` interface using WebSocket implementation. - */ -export class WebSocketChannel implements Channel { - /** - * Native WebSocket reference, or `undefined` if construction of WebSocket - * failed. - */ - public readonly ws: WebSocketBase | undefined; - - public readonly state$ = new BehaviorSubject(ChannelState.CONNECTING); - public readonly open$ = new ReplaySubject(1); - public readonly close$ = new ReplaySubject<[self: Channel, event: CloseEventBase]>(1); - public readonly error$ = new Subject(); - public readonly message$ = new Subject(); - - constructor({newSocket}: WebSocketChannelParams) { - try { - const ws = (this.ws = newSocket()); - ws.binaryType = 'arraybuffer'; - ws.onopen = () => { - this.state$.next(ChannelState.OPEN); - this.open$.next(this); - this.open$.complete(); - }; - ws.onclose = (event) => { - this.state$.next(ChannelState.CLOSED); - this.close$.next([this, event]); - this.close$.complete(); - this.message$.complete(); - }; - ws.onerror = (event: Event) => { - const errorEvent: Partial = event as unknown as Partial; - const error: Error = - errorEvent.error instanceof Error ? errorEvent.error : new Error(String(errorEvent.message) || 'ERROR'); - this.error$.next(error); - }; - ws.onmessage = (event) => { - const data = event.data; - const message: T = (typeof data === 'string' ? data : toUint8Array(data)) as unknown as T; - this.message$.next(message); - }; - } catch (error) { - this.state$.next(ChannelState.CLOSED); - this.error$.next(error as Error); - this.close$.next([this, {code: 0, wasClean: true, reason: 'CONSTRUCTOR'}]); - this.close$.complete(); - } - } - - public buffer(): number { - if (!this.ws) return 0; - return this.ws.bufferedAmount; - } - - public close(code?: number, reason?: string): void { - if (!this.ws) return; - this.ws.close(code, reason); - } - - public isOpen(): boolean { - return this.state$.getValue() === ChannelState.OPEN; - } - - public send(data: T): number { - if (!this.ws) return -1; - const buffered = this.ws.bufferedAmount; - this.ws.send(data); - return this.ws.bufferedAmount - buffered; - } - - public send$(data: T): Observable { - return this.open$.pipe( - map(() => { - if (!this.isOpen()) throw new Error('CLOSED'); - return this.send(data); - }), - ); - } -} - -export interface PersistentChannelParams { - /** - * New `Channel` factory. - */ - newChannel: () => Channel; - - /** - * Minimum amount of time in ms to wait before attempting to reconnect. - * Defaults to 1,500 +/- 500 milliseconds. - */ - minReconnectionDelay?: number; - - /** - * Maximum amount of time in ms to wait before attempting to reconnect. - * Defaults to 10,000 milliseconds. - */ - maxReconnectionDelay?: number; - - /** - * Factor which is raised to the power of number of retry attempts and - * subsequently multiplied the `minReconnectionDelay` to get to current - * reconnection delay. - */ - reconnectionDelayGrowFactor?: number; - - /** - * Minimum time the WebSocket should be open to reset retry counter. - * Defaults to 5,000 milliseconds. - */ - minUptime?: number; -} - -/** - * Channel which automatically reconnects if disconnected. - */ -export class PersistentChannel { - /** - * Whether the service is "active". The service becomes active when it is - * started using the ".start()" method. When service is "active" it will - * attempt to always keep an open channel connected. - */ - public readonly active$ = new BehaviorSubject(false); - - /** - * Currently used channel, if any. When service is "active" it attempts to - * create and open a channel. - */ - public readonly channel$ = new BehaviorSubject>(undefined); - - /** - * Whether the currently active channel (if any) is "open". An open channel - * is one where communication can happen, where a message can be sent to the - * other side. - */ - public readonly open$ = new BehaviorSubject(false); - - /** Emits incoming messages. */ - public readonly message$ = this.channel$.pipe( - filter((channel) => !!channel), - switchMap((channel) => channel!.message$), - ); - - /** Number of times we have attempted to reconnect. */ - protected retries = 0; - - constructor(public readonly params: PersistentChannelParams) { - const start$ = new Subject(); - const stop$ = new Subject(); - - this.active$ - .pipe( - skip(1), - filter((active) => active), - ) - .subscribe(() => { - start$.next(undefined); - }); - - this.active$ - .pipe( - skip(1), - filter((active) => !active), - ) - .subscribe(() => { - stop$.next(undefined); - }); - - // Create new channel when service starts. - start$.subscribe(() => this.channel$.next(params.newChannel())); - - // Re-connect, when channel closes. - start$ - .pipe( - switchMap(() => this.channel$), - filter((channel) => !!channel), - takeUntil(stop$), - switchMap((channel) => channel!.close$), - takeUntil(stop$), - switchMap(() => - from( - (async () => { - const timeout = this.reconnectDelay(); - this.retries++; - await new Promise((resolve) => setTimeout(resolve, timeout)); - })(), - ), - ), - takeUntil(stop$), - tap(() => this.channel$.next(params.newChannel())), - delay(params.minUptime || 5_000), - takeUntil(stop$), - tap(() => { - const isOpen = this.channel$.getValue()?.isOpen(); - if (isOpen) { - this.retries = 0; - } - }), - ) - .subscribe(); - - // Track if channel is connected. - start$ - .pipe( - switchMap(() => this.channel$), - filter((channel) => !!channel), - switchMap((channel) => channel!.state$), - map((state) => state === ChannelState.OPEN), - ) - .subscribe((open) => { - if (open !== this.open$.getValue()) this.open$.next(open); - }); - - // Reset re-try counter when service stops. - stop$.subscribe(() => { - this.retries = 0; - }); - } - - public start(): void { - if (this.active$.getValue()) return; - this.active$.next(true); - } - - public stop(): void { - if (!this.active$.getValue()) return; - this.active$.next(false); - const channel = this.channel$.getValue(); - if (channel) { - channel.close(); - this.channel$.next(undefined); - } - this.open$.next(false); - } - - public reconnectDelay(): number { - if (this.retries <= 0) return 0; - const minReconnectionDelay = this.params.minReconnectionDelay || Math.round(1_000 + Math.random() * 1_000); - const maxReconnectionDelay = this.params.maxReconnectionDelay || 10_000; - const reconnectionDelayGrowFactor = this.params.reconnectionDelayGrowFactor || 1.3; - const delay = Math.min( - maxReconnectionDelay, - minReconnectionDelay * reconnectionDelayGrowFactor ** (this.retries - 1), - ); - return delay; - } - - public send$(data: T): Observable { - return this.channel$.pipe( - filter((channel) => !!channel), - switchMap((channel) => channel!.open$), - filter((channel) => channel.isOpen()), - take(1), - map((channel) => { - const canSend = this.active$.getValue() && this.open$.getValue(); - return canSend ? channel.send(data) : -1; - }), - ); - } -} diff --git a/src/reactive-rpc/common/channel/constants.ts b/src/reactive-rpc/common/channel/constants.ts deleted file mode 100644 index f4d889f959..0000000000 --- a/src/reactive-rpc/common/channel/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const enum WebSocketState { - CONNECTING = 0, - OPEN = 1, - CLOSING = 2, - CLOSED = 3, -} diff --git a/src/reactive-rpc/common/channel/index.ts b/src/reactive-rpc/common/channel/index.ts deleted file mode 100644 index 2f8cb96f8a..0000000000 --- a/src/reactive-rpc/common/channel/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './types'; -export * from './constants'; -export * from './channel'; diff --git a/src/reactive-rpc/common/channel/mock.ts b/src/reactive-rpc/common/channel/mock.ts deleted file mode 100644 index e6ef9c15bc..0000000000 --- a/src/reactive-rpc/common/channel/mock.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {WebSocketState} from './constants'; -import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; - -export interface CreateWebSocketMockParams { - onClose: (code?: number, reason?: string) => void; - onSend: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void; -} - -export interface MockWebSocket extends WebSocket { - readonly _protocol: string | string[]; - _readyState: WebSocketState; - _bufferedAmount: number; - _extendParams(newParams: Partial): void; - _open(): void; - _close(code: number, reason: string, wasClean: boolean): void; - _error(): void; - _message(message: string | ArrayBuffer | ArrayBufferView): void; -} - -export const createWebSocketMock = (params: Partial) => { - const WebSocketMock = class WebSocketMock implements MockWebSocket { - public static readonly CONNECTING = 0; - public static readonly OPEN = 1; - public static readonly CLOSING = 2; - public static readonly CLOSED = 3; - - public readonly CONNECTING = 0; - public readonly OPEN = 1; - public readonly CLOSING = 2; - public readonly CLOSED = 3; - - public onclose = null; - public onerror = null; - public onmessage = null; - public onopen = null; - - public binaryType: 'arraybuffer' | 'blob' = 'blob'; - - public _readyState: WebSocketState = WebSocketState.CONNECTING; - public _bufferedAmount: number = 0; - - public get bufferedAmount(): number { - return this._bufferedAmount; - } - - public get extensions(): string { - return ''; - } - - public get protocol(): string { - return this._protocol instanceof Array ? this._protocol.join(',') : this._protocol; - } - - public get readyState(): number { - return this._readyState; - } - - constructor( - public readonly url: string, - public readonly _protocol: string | string[] = '', - ) {} - - public close(code?: number, reason?: string): void { - if (!params.onClose) return; - return params.onClose(code, reason); - } - - public send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void { - if (typeof data === 'string') { - this._bufferedAmount += utf8Size(data); - } else if (ArrayBuffer.isView(data)) { - this._bufferedAmount += data.byteLength; - } else if (data && typeof data === 'object') { - if ((data as any).byteLength !== undefined) { - this._bufferedAmount += Number((data as any).byteLength); - } else if ((data as unknown as Blob).size !== undefined) { - this._bufferedAmount += Number((data as unknown as Blob).size); - } - } - if (!params.onSend) return; - return params.onSend(data); - } - - public addEventListener() { - throw new Error('not implemented'); - } - - public removeEventListener() { - throw new Error('not implemented'); - } - - public dispatchEvent(): boolean { - throw new Error('not implemented'); - } - - public _extendParams(newParams: Partial): void { - Object.assign(params, newParams); - } - - public _open() { - this._readyState = WebSocketState.OPEN; - if (typeof this.onopen === 'function') { - (this.onopen as any).call(this, new Event('open')); - } - } - - public _close(code: number, reason: string, wasClean: boolean): void { - if (this._readyState === WebSocketState.CLOSED) throw new Error('Mock WebSocket already closed.'); - this._readyState = WebSocketState.CLOSED; - if (!this.onclose) return; - const event: Pick = { - code, - reason, - wasClean, - }; - (this.onclose as any).call(this, event); - } - - public _error() { - if (!this.onerror) return; - (this.onerror as any).call(this, new Event('error')); - } - - public _message(message: string | ArrayBuffer | ArrayBufferView): void { - if (!this.onmessage) return; - const event = {data: message}; - (this.onmessage as any).call(this, event); - } - }; - - return WebSocketMock; -}; diff --git a/src/reactive-rpc/common/channel/types.ts b/src/reactive-rpc/common/channel/types.ts deleted file mode 100644 index b76796552f..0000000000 --- a/src/reactive-rpc/common/channel/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type WebSocketBase = Pick< - WebSocket, - 'binaryType' | 'readyState' | 'bufferedAmount' | 'onopen' | 'onclose' | 'onerror' | 'onmessage' | 'close' | 'send' ->; - -export interface CloseEventBase { - readonly code: number; - readonly reason: string; - readonly wasClean: boolean; -} diff --git a/src/reactive-rpc/common/codec/RpcCodec.ts b/src/reactive-rpc/common/codec/RpcCodec.ts deleted file mode 100644 index cf93a6e3a4..0000000000 --- a/src/reactive-rpc/common/codec/RpcCodec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type {RpcSpecifier} from '../rpc'; -import type {ReactiveRpcMessage} from '../messages'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {RpcMessageCodec} from './types'; - -export class RpcCodec { - constructor( - public readonly msg: RpcMessageCodec, - public readonly req: JsonValueCodec, - public readonly res: JsonValueCodec, - ) {} - - public specifier(): RpcSpecifier { - const specifier = `rpc.${this.msg.id}.${this.req.id}` + (this.req.id !== this.res.id ? `-${this.res.id}` : ''); - return specifier as RpcSpecifier; - } - - public encode(messages: ReactiveRpcMessage[], valueCodec: JsonValueCodec): Uint8Array { - const encoder = valueCodec.encoder; - const writer = encoder.writer; - writer.reset(); - this.msg.encodeBatch(valueCodec, messages); - return writer.flush(); - } - - public decode(data: Uint8Array, valueCodec: JsonValueCodec): ReactiveRpcMessage[] { - const decoder = valueCodec.decoder; - const reader = decoder.reader; - reader.reset(data); - return this.msg.decodeBatch(valueCodec, data); - } -} diff --git a/src/reactive-rpc/common/codec/RpcCodecs.ts b/src/reactive-rpc/common/codec/RpcCodecs.ts deleted file mode 100644 index 580ebc261b..0000000000 --- a/src/reactive-rpc/common/codec/RpcCodecs.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {RpcMessageCodecs} from './RpcMessageCodecs'; - -export class RpcCodecs { - constructor( - public readonly value: Codecs, - public readonly messages: RpcMessageCodecs, - ) {} -} diff --git a/src/reactive-rpc/common/codec/RpcMessageCodecs.ts b/src/reactive-rpc/common/codec/RpcMessageCodecs.ts deleted file mode 100644 index b906209f87..0000000000 --- a/src/reactive-rpc/common/codec/RpcMessageCodecs.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {BinaryRpcMessageCodec} from './binary'; -import {CompactRpcMessageCodec} from './compact'; -import {JsonRpc2RpcMessageCodec} from './json-rpc-2/JsonRpc2RpcMessageCodec'; -import type {RpcMessageCodec} from './types'; - -export class RpcMessageCodecs { - binary: RpcMessageCodec; - compact: RpcMessageCodec; - jsonRpc2: RpcMessageCodec; - - constructor() { - this.binary = new BinaryRpcMessageCodec(); - this.compact = new CompactRpcMessageCodec(); - this.jsonRpc2 = new JsonRpc2RpcMessageCodec(); - } -} diff --git a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts b/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts deleted file mode 100644 index d8b3d2a236..0000000000 --- a/src/reactive-rpc/common/codec/binary/BinaryRpcMessageCodec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {RpcMessageFormat} from '../constants'; -import {decode} from './decode'; -import * as msg from '../../messages'; -import type {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; -import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; - -export class BinaryRpcMessageCodec implements RpcMessageCodec { - id = 'rx.binary'; - format = RpcMessageFormat.Compact; - - public encodeMessage(jsonCodec: JsonValueCodec, message: msg.ReactiveRpcMessage): void { - message.encodeBinary(jsonCodec); - } - - public encodeBatch(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): void { - const length = batch.length; - for (let i = 0; i < length; i++) batch[i].encodeBinary(jsonCodec); - } - - public decodeBatch(jsonCodec: JsonValueCodec, uint8: Uint8Array): msg.ReactiveRpcMessage[] { - const decoder = jsonCodec.decoder; - const reader = decoder.reader; - reader.reset(uint8); - const size = uint8.length; - const messages: msg.ReactiveRpcMessage[] = []; - while (reader.x < size) { - const message = decode(reader); - messages.push(message); - } - const length = messages.length; - for (let i = 0; i < length; i++) { - const message = messages[i]; - const value = (message as any).value; - if (value) { - const cut = value.data as Uint8ArrayCut; - const arr = cut.uint8.subarray(cut.start, cut.start + cut.size); - const data = arr.length ? decoder.read(arr) : undefined; - if (data === undefined) (message as any).value = undefined; - else value.data = data; - } - } - return messages; - } - - public encode(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): Uint8Array { - const encoder = jsonCodec.encoder; - const writer = encoder.writer; - writer.reset(); - this.encodeBatch(jsonCodec, batch); - return writer.flush(); - } -} diff --git a/src/reactive-rpc/common/codec/binary/README.md b/src/reactive-rpc/common/codec/binary/README.md deleted file mode 100644 index 1fd849e486..0000000000 --- a/src/reactive-rpc/common/codec/binary/README.md +++ /dev/null @@ -1,220 +0,0 @@ -# `binary` codec for Reactive-RPC messages - -`binary` codec specifies a binary encoding format for Reactive-RPC protocol -messages. - - -## Message encoding - -- All messages are at least 4 bytes long. - -Notation in diagrams: - -``` -One byte: -+--------+ -| | -+--------+ - -Zero or more repeating bytes: -+........+ -| | -+........+ - -Variable number of bytes: -+========+ -| | -+========+ -``` - - -### Notification Message - -The Notification message consists of: - -1. Leading `000` represents the message type. -1. `x` encodes the size of the payload `data`. -1. `z` encodes method `name` as unsigned 8-bit integer. -1. Remote method `name` string, encoded as ASCII text. -1. Binary payload `data`. - -The Notification message has only this form: - -``` -+--------+--------+--------+--------+========+========+ -|000xxxxx|xxxxxxxx|xxxxxxxx|zzzzzzzz| name | data | -+--------+--------+--------+--------+========+========+ -``` - -- The maximum size of the payload `data` is 2 ** 23 - 1 bytes (~2MB). - - -### Message with payload and name encoding - -A message with payload and name consists of: - -1. Leading bits `iii` represent the message type. -1. `x` encodes the size of the payload `data`. -1. `y` encodes the channel/subscription sequence number. -1. `z` encodes method `name` as unsigned 8-bit integer. -1. Remote method `name` string, encoded as ASCII text. -1. Binary payload `data`. -1. `?` is a bit flag which determines if the following byte should be used for - decoding a variable length integer. - -General message structure: - -``` -+--------+--------+--------+--------+--------+========+========+........+........+ -|iii?xxxx|xxxxxxxx|?xyxyxyx|xyxyxyxy|zzzzzzzz| name | data |yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+--------+========+========+........+........+ -``` - -Encoding depends on the size of `data`: - -``` -if x <= 0b1111_11111111 (4095 ~ 4KB): -+--------+--------+--------+--------+--------+========+========+ -|iii0xxxx|xxxxxxxx|yyyyyyyy|yyyyyyyy|zzzzzzzz| name | data | -+--------+--------+--------+--------+--------+========+========+ - -if x <= 0b1111_11111111_1111111 (524287 ~ 512KB): -+--------+--------+--------+--------+--------+========+========+--------+ -|iii1xxxx|xxxxxxxx|0xxxxxxx|yyyyyyyy|zzzzzzzz| name | data |yyyyyyyy| -+--------+--------+--------+--------+--------+========+========+--------+ - -if x > 0b1111_11111111_1111111 (524287) - and x <= 0b1111_11111111_1111111_11111111 (134217727 ~ 128MB): -+--------+--------+--------+--------+--------+========+========+--------+--------+ -|iii1xxxx|xxxxxxxx|1xxxxxxx|xxxxxxxx|zzzzzzzz| name | data |yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+--------+========+========+--------+--------+ -``` - - -### Message with payload-only encoding - -A message with payload-only consists of: - -1. Leading bits `iii` represent the message type. -1. `x` encodes the size of the payload `data`. -1. `y` encodes the channel/subscription sequence number. -1. Binary payload `data`. -1. `?` is a bit flag which determines if the following byte should be used for - decoding a variable length integer. - -General message structure: - -``` -+--------+--------+--------+--------+========+........+........+ -|iii?xxxx|xxxxxxxx|?xyxyxyx|xyxyxyxy| data |yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+========+........+........+ -``` - -Encoding depends on the size of `data`: - -``` -if x <= 0b1111_11111111 (4095 ~ 4KB): -+--------+--------+--------+--------+========+ -|iii0xxxx|xxxxxxxx|yyyyyyyy|yyyyyyyy| data | -+--------+--------+--------+--------+========+ - -if x <= 0b1111_11111111_1111111 (524287 ~ 512KB): -+--------+--------+--------+--------+========+--------+ -|iii1xxxx|xxxxxxxx|0xxxxxxx|yyyyyyyy| data |yyyyyyyy| -+--------+--------+--------+--------+========+--------+ - -if x > 0b1111_11111111_1111111 (524287) - and x <= 0b1111_11111111_1111111_11111111 (134217727 ~ 128MB): -+--------+--------+--------+--------+========+--------+--------+ -|iii1xxxx|xxxxxxxx|1xxxxxxx|xxxxxxxx| data |yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+========+--------+--------+ -``` - - -### Request Data Message - -The *Request Data Message* is encoded as a message with payload and name with -`001` set as the leading bits. - - -### Request Complete Message - -The *Request Data Message* is encoded as a message with payload and name with -`010` set as the leading bits. - - -### Request Error Message - -The *Request Error Message* is encoded as a message with payload-only with -`011` set as the leading bits. - - -### Request Un-subscribe Message - -The *Request Un-subscribe Message* message consists of: - -1. Leading `111` represents the control message type. -1. Second byte `00000000` indicates that it is a *Request Un-subscribe Message*. -1. `y` encodes the channel/subscription sequence number. - -``` -+--------+--------+--------+--------+ -|11100000|00000000|yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+ -``` - - -### Response Data Message - -The *Response Data Message* is encoded as a message with payload-only with -`100` set as the leading bits. - - -### Response Complete Message - -The *Response Complete Message* is encoded as a message with payload-only with -`101` set as the leading bits. - - -### Response Error Message - -The *Response Complete Message* is encoded as a message with payload-only with -`110` set as the leading bits. - - -### Response Un-subscribe Message - -The *Response Un-subscribe Message* message consists of: - -1. Leading `111` represents the control message type. -1. Second byte `00000001` indicates that it is a *Response Un-subscribe Message*. -1. `y` encodes the channel/subscription sequence number. - -``` -+--------+--------+--------+--------+ -|11100000|00000001|yyyyyyyy|yyyyyyyy| -+--------+--------+--------+--------+ -``` - - -### The channel/subscription sequence number `y` - -The `y` sequence number uniquely identifies an *active ID* (a request/response in-flight -or an active subscription). It is chosen to be encoded as 2 bytes as a compromise -between: (1) it should consume as little bytes on the wire as possible; (2) it -should be big enough to be able to support a reasonable number of active IDs for -a typical server-client application. - -The `id` field typically starts from 0, and for each new subscribe message the -client increments it by one. Once the `id` reaches 65535, it is reset back to -zero. - -Both, client and server, know the set of active IDs. - -The client must check for all currently active IDs when generating a new ID, if -the generated ID collides with an already active ID, the client must skip that -ID and try the next one. - -If the server receives a new subscribe message with ID which collides with an -active ID, it should stop processing and purge the existing subscription with -that ID and then process the new subscribe message with that ID as usual. diff --git a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts deleted file mode 100644 index 7cc051bb00..0000000000 --- a/src/reactive-rpc/common/codec/binary/__tests__/BinaryRpcMessageCodec.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RequestCompleteMessage} from '../../../messages'; -import {BinaryRpcMessageCodec} from '../BinaryRpcMessageCodec'; - -describe('sample messages', () => { - test('can decode a batch of two messages', () => { - const cbor = new CborJsonValueCodec(new Writer(128)); - const codec = new BinaryRpcMessageCodec(); - const batch = new Uint8Array([ - // Message 1 - 64, 6, 0, 1, 6, 100, 111, 117, 98, 108, 101, 161, 99, 110, 117, 109, 1, - - // Message 2 - 64, 6, 0, 2, 6, 100, 111, 117, 98, 108, 101, 161, 99, 110, 117, 109, 2, - ]); - const messages = codec.decodeBatch(cbor, batch); - expect(messages.length).toBe(2); - expect(messages[0]).toBeInstanceOf(RequestCompleteMessage); - expect(messages[1]).toBeInstanceOf(RequestCompleteMessage); - expect((messages[0] as any).id).toBe(1); - expect((messages[1] as any).id).toBe(2); - expect((messages[0] as any).method).toBe('double'); - expect((messages[1] as any).method).toBe('double'); - expect((messages[0] as any).value.data.num).toBe(1); - expect((messages[1] as any).value.data.num).toBe(2); - }); -}); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts deleted file mode 100644 index ff6a27bc26..0000000000 --- a/src/reactive-rpc/common/codec/binary/__tests__/automated.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {BinaryRpcMessageCodec} from '..'; -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {messages} from '../../../messages/__tests__/fixtures'; - -const codec = new BinaryRpcMessageCodec(); -const cborCodec = new CborJsonValueCodec(new Writer()); - -describe('encode, decode', () => { - for (const [name, message] of Object.entries(messages)) { - test(name, () => { - codec.encodeBatch(cborCodec, [message]); - const encoded = cborCodec.encoder.writer.flush(); - const [decoded] = codec.decodeBatch(cborCodec, encoded); - expect(decoded).toStrictEqual(message); - }); - } -}); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts deleted file mode 100644 index 72f56e48e8..0000000000 --- a/src/reactive-rpc/common/codec/binary/__tests__/decode.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - NotificationMessage, - ReactiveRpcMessage, - RequestCompleteMessage, - RequestDataMessage, - RequestUnsubscribeMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, - ResponseUnsubscribeMessage, -} from '../../../messages'; -import {RpcValue} from '../../../messages/Value'; -import {decode} from '../decode'; -import {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; -import {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; - -const codec = new CborJsonValueCodec(new Writer(64)); -const encoder = codec.encoder; -const decoder = codec.decoder; -const val = (v: T) => new RpcValue(v, undefined); -const assertMessage = (msg: ReactiveRpcMessage) => { - encoder.writer.reset(); - msg.encodeBinary(codec); - const encoded = encoder.writer.flush(); - const reader = new Reader(); - reader.reset(encoded); - const decoded = decode(reader); - // console.log(decoded); - if ((decoded as any).value) { - const cut = (decoded as any).value.data as Uint8ArrayCut; - const arr = cut.uint8.subarray(cut.start, cut.start + cut.size); - (decoded as any).value.data = arr.length ? decoder.decode(arr) : undefined; - } - expect(decoded).toEqual(msg); -}; - -describe('decodes back various messages', () => { - test('empty notification', () => { - assertMessage(new NotificationMessage('', val(undefined))); - }); - - test('notification with empty payload', () => { - assertMessage(new NotificationMessage('hello.world', val(undefined))); - }); - - test('notification with payload', () => { - assertMessage(new NotificationMessage('foo', val({foo: 'bar'}))); - }); - - test('empty Request Data message', () => { - assertMessage(new RequestDataMessage(0, '', val(undefined))); - }); - - test('Request Data message', () => { - assertMessage(new RequestDataMessage(123, 'abc', val({foo: 'bar'}))); - }); - - test('Request Complete message', () => { - assertMessage(new RequestCompleteMessage(23324, 'adfasdf', val({foo: 'bar'}))); - }); - - test('Request Error message', () => { - assertMessage(new RequestCompleteMessage(4321, '', val('asdf'))); - }); - - test('Request Un-subscribe message', () => { - assertMessage(new RequestUnsubscribeMessage(4321)); - }); - - test('empty Response Data message', () => { - assertMessage(new ResponseDataMessage(0, val(undefined))); - }); - - test('Response Data message', () => { - assertMessage(new ResponseDataMessage(123, val({foo: 'bar'}))); - }); - - test('Response Complete message', () => { - assertMessage(new ResponseCompleteMessage(123, val({foo: 'bar'}))); - }); - - test('Response Error message', () => { - assertMessage(new ResponseErrorMessage(123, val({foo: 'bar'}))); - }); - - test('Response Un-subscribe message', () => { - assertMessage(new ResponseUnsubscribeMessage(4321)); - }); -}); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts deleted file mode 100644 index c43984911a..0000000000 --- a/src/reactive-rpc/common/codec/binary/__tests__/encode.spec.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { - NotificationMessage, - ReactiveRpcMessage, - RequestDataMessage, - RequestUnsubscribeMessage, - ResponseDataMessage, -} from '../../../messages'; -import {RpcValue} from '../../../messages/Value'; -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; - -const cborCodec = new CborJsonValueCodec(new Writer(64)); -const encoder = cborCodec.encoder; -const val = (v: T) => new RpcValue(v, undefined); -const encode = (msg: ReactiveRpcMessage) => { - msg.encodeBinary(cborCodec); - return encoder.writer.flush(); -}; - -describe('notification message', () => { - test('encodes notification message with no method and no payload', () => { - cborCodec.encoder.writer.x0 = 61; - cborCodec.encoder.writer.x = 61; - const msg = new NotificationMessage('', val(undefined)); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 0, - 0, - 0, - 0, - ] - `); - }); - - test('encodes notification message with no payload', () => { - const msg = new NotificationMessage('abc', val(undefined)); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 0, - 0, - 0, - 3, - 97, - 98, - 99, - ] - `); - }); - - test('encodes notification message with payload', () => { - const msg = new NotificationMessage('abc', val(123)); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 0, - 0, - 2, - 3, - 97, - 98, - 99, - 24, - 123, - ] - `); - }); -}); - -describe('request', () => { - describe('data message', () => { - test('empty method name', () => { - const msg = new RequestDataMessage(0x0abc, '', val(undefined)); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 32, - 0, - 10, - 188, - 0, - ] - `); - }); - - test('no payload', () => { - const msg = new RequestDataMessage(0x1, 'foo', val(undefined)); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 32, - 0, - 0, - 1, - 3, - 102, - 111, - 111, - ] - `); - }); - - test('with payload', () => { - cborCodec.encoder.writer.x0 = 49; - cborCodec.encoder.writer.x = 49; - const msg = new RequestDataMessage(0x1, 'aaa', val('bbb')); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 32, - 4, - 0, - 1, - 3, - 97, - 97, - 97, - 99, - 98, - 98, - 98, - ] - `); - }); - }); - - describe('unsubscribe message', () => { - test('encodes a simple un-subscribe message', () => { - const msg = new RequestUnsubscribeMessage(0x7); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 224, - 0, - 0, - 7, - ] - `); - }); - }); -}); - -describe('response', () => { - describe('data message', () => { - test('encodes a data message', () => { - cborCodec.encoder.writer.x0 = 49; - cborCodec.encoder.writer.x = 49; - const msg = new ResponseDataMessage(0x3, val('bbb')); - const buf = encode(msg); - expect(buf).toMatchInlineSnapshot(` - Uint8Array [ - 128, - 4, - 0, - 3, - 99, - 98, - 98, - 98, - ] - `); - }); - }); -}); diff --git a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts deleted file mode 100644 index a30ac8eb7c..0000000000 --- a/src/reactive-rpc/common/codec/binary/__tests__/smoke-tests.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {BinaryRpcMessageCodec} from '..'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import { - NotificationMessage, - ReactiveRpcMessage, - RequestCompleteMessage, - RequestDataMessage, - RequestErrorMessage, - RequestUnsubscribeMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, - ResponseUnsubscribeMessage, -} from '../../../messages'; -import {RpcValue} from '../../../messages/Value'; -import {messages} from '../../../messages/__tests__/fixtures'; - -const writer = new Writer(8 * Math.round(Math.random() * 100)); -const codecs = new Codecs(writer); -const codec = new BinaryRpcMessageCodec(); - -const codecList = [codecs.cbor, codecs.msgpack, codecs.json]; - -for (const jsonCodec of codecList) { - const assertMessage = (message: ReactiveRpcMessage) => { - const encoded = codec.encode(jsonCodec, [message]); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual([message]); - }; - - describe(jsonCodec.id, () => { - test('Notification message', () => { - const value = new RpcValue({foo: 'bar'}, undefined); - const message = new NotificationMessage('abc', value); - assertMessage(message); - }); - - test('Request Data message', () => { - const value = new RpcValue([1, 2, 3], undefined); - const message = new RequestDataMessage(9999, 'a', value); - assertMessage(message); - }); - - test('Request Complete message', () => { - const value = new RpcValue(true, undefined); - const message = new RequestCompleteMessage(3, 'abc', value); - assertMessage(message); - }); - - test('Request Error message', () => { - const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined); - const message = new RequestErrorMessage(0, 'wtf', value); - assertMessage(message); - }); - - test('Request Unsubscribe message', () => { - const message = new RequestUnsubscribeMessage(8383); - assertMessage(message); - }); - - test('Response Data message', () => { - const value = new RpcValue([1, 2, 3], undefined); - const message = new ResponseDataMessage(30000, value); - assertMessage(message); - }); - - test('Response Complete message', () => { - const value = new RpcValue(true, undefined); - const message = new ResponseCompleteMessage(3, value); - assertMessage(message); - }); - - test('Response Error message', () => { - const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined); - const message = new ResponseErrorMessage(0, value); - assertMessage(message); - }); - - test('Response Unsubscribe message', () => { - const message = new ResponseUnsubscribeMessage(16000); - assertMessage(message); - }); - }); -} - -describe('batch of messages', () => { - const value = new RpcValue({foo: 'bar'}, undefined); - const message1 = new NotificationMessage('abc', value); - const message2 = new RequestDataMessage(888, 'a', value); - const message3 = new ResponseCompleteMessage(3, value); - - for (const jsonCodec of codecList) { - describe(jsonCodec.id, () => { - test('two messages', () => { - const encoded = codec.encode(jsonCodec, [message1, message2]); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual([message1, message2]); - }); - - test('three messages', () => { - const encoded = codec.encode(jsonCodec, [message3, message1, message2]); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual([message3, message1, message2]); - }); - - test('three messages - 2', () => { - const encoded = codec.encode(jsonCodec, [message1, message2, message3]); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual([message1, message2, message3]); - }); - - test('four messages', () => { - const encoded = codec.encode(jsonCodec, [message1, message2, message1, message3]); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual([message1, message2, message1, message3]); - }); - - test('many messages', () => { - const list = Object.values(messages); - const encoded = codec.encode(jsonCodec, list); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual(list); - }); - - test('notification messages', () => { - const list = [ - messages.notification1, - messages.notification3, - messages.notification3, - messages.notification3, - messages.notification3, - messages.notification2, - messages.notification1, - messages.notification2, - messages.notification2, - messages.notification2, - messages.notification3, - messages.notification4, - messages.notification3, - messages.notification4, - ]; - const encoded = codec.encode(jsonCodec, list); - const decoded = codec.decodeBatch(jsonCodec, encoded); - expect(decoded).toStrictEqual(list); - }); - }); - } -}); diff --git a/src/reactive-rpc/common/codec/binary/constants.ts b/src/reactive-rpc/common/codec/binary/constants.ts deleted file mode 100644 index 37b267f428..0000000000 --- a/src/reactive-rpc/common/codec/binary/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const enum BinaryMessageType { - Notification = 0b000, - RequestData = 0b001, - RequestComplete = 0b010, - RequestError = 0b011, - ResponseData = 0b100, - ResponseComplete = 0b101, - ResponseError = 0b110, - Control = 0b111, -} diff --git a/src/reactive-rpc/common/codec/binary/decode.ts b/src/reactive-rpc/common/codec/binary/decode.ts deleted file mode 100644 index db38628a90..0000000000 --- a/src/reactive-rpc/common/codec/binary/decode.ts +++ /dev/null @@ -1,106 +0,0 @@ -import {Uint8ArrayCut} from '@jsonjoy.com/util/lib/buffers/Uint8ArrayCut'; -import { - NotificationMessage, - ReactiveRpcMessage, - RequestCompleteMessage, - RequestDataMessage, - RequestErrorMessage, - RequestUnsubscribeMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, - ResponseUnsubscribeMessage, -} from '../../messages'; -import {RpcValue} from '../../messages/Value'; -import {BinaryMessageType} from './constants'; -import type {Reader} from '@jsonjoy.com/util/lib/buffers/Reader'; - -export const decode = (reader: Reader): ReactiveRpcMessage => { - const word = reader.u32(); - const type = word >>> 29; - switch (type) { - case BinaryMessageType.Notification: { - const z = word & 0xff; - const x = word >>> 8; - const name = reader.ascii(z); - const cut = new Uint8ArrayCut(reader.uint8, reader.x, x); - const value = new RpcValue(cut, undefined); - reader.skip(x); - return new NotificationMessage(name, value); - } - case BinaryMessageType.RequestData: - case BinaryMessageType.RequestComplete: - case BinaryMessageType.RequestError: { - const z = reader.u8(); - const name = reader.ascii(z); - const cutStart = reader.x; - let x = 0, - y = 0; - if (word & 0b1_0000_00000000_00000000_00000000) { - if (word & 0b10000000_00000000) { - x = ((0b1111_11111111 & (word >>> 16)) << 15) | (word & 0b1111111_11111111); - reader.skip(x); - y = reader.u16(); - } else { - x = ((0b1111_11111111 & (word >>> 16)) << 7) | ((word >>> 8) & 0x7f); - reader.skip(x); - y = ((word & 0xff) << 8) | reader.u8(); - } - } else { - x = (word >>> 16) & 0b1111_11111111; - y = word & 0xffff; - reader.skip(x); - } - const cut = new Uint8ArrayCut(reader.uint8, cutStart, x); - const value = new RpcValue(cut, undefined); - switch (type) { - case BinaryMessageType.RequestData: - return new RequestDataMessage(y, name, value); - case BinaryMessageType.RequestComplete: - return new RequestCompleteMessage(y, name, value); - case BinaryMessageType.RequestError: - return new RequestErrorMessage(y, name, value); - } - break; - } - case BinaryMessageType.ResponseData: - case BinaryMessageType.ResponseComplete: - case BinaryMessageType.ResponseError: { - const cutStart = reader.x; - let x = 0, - y = 0; - if (word & 0b1_0000_00000000_00000000_00000000) { - if (word & 0b10000000_00000000) { - x = ((0b1111_11111111 & (word >>> 16)) << 15) | (word & 0b1111111_11111111); - reader.skip(x); - y = reader.u16(); - } else { - x = ((0b1111_11111111 & (word >>> 16)) << 7) | ((word >>> 8) & 0x7f); - reader.skip(x); - y = ((word & 0xff) << 8) | reader.u8(); - } - } else { - x = (word >>> 16) & 0b1111_11111111; - y = word & 0xffff; - reader.skip(x); - } - const cut = new Uint8ArrayCut(reader.uint8, cutStart, x); - const value = new RpcValue(cut, undefined); - switch (type) { - case BinaryMessageType.ResponseData: - return new ResponseDataMessage(y, value); - case BinaryMessageType.ResponseComplete: - return new ResponseCompleteMessage(y, value); - case BinaryMessageType.ResponseError: - return new ResponseErrorMessage(y, value); - } - break; - } - case BinaryMessageType.Control: { - const isResponse = word & 0b1_00000000_00000000; - const id = word & 0xffff; - return isResponse ? new ResponseUnsubscribeMessage(id) : new RequestUnsubscribeMessage(id); - } - } - throw new Error('UNKNOWN_MSG'); -}; diff --git a/src/reactive-rpc/common/codec/binary/index.ts b/src/reactive-rpc/common/codec/binary/index.ts deleted file mode 100644 index dc2212168c..0000000000 --- a/src/reactive-rpc/common/codec/binary/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './constants'; -export * from './BinaryRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts b/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts deleted file mode 100644 index 0a0c790e60..0000000000 --- a/src/reactive-rpc/common/codec/compact/CompactRpcMessageCodec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import {RpcMessageFormat} from '../constants'; -import {RpcError, RpcErrorCodes} from '../../rpc/caller/error'; -import * as msg from '../../messages'; -import {CompactMessageType} from './constants'; -import {RpcValue} from '../../messages/Value'; -import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; -import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type * as types from './types'; - -const fromJson = (arr: unknown | unknown[] | types.CompactMessage): msg.ReactiveRpcMessage => { - if (!(arr instanceof Array)) throw RpcError.fromCode(RpcErrorCodes.BAD_REQUEST); - const type = arr[0]; - switch (type) { - case CompactMessageType.RequestComplete: { - const data = arr[3]; - const value = data === undefined ? data : new RpcValue(data, undefined); - return new msg.RequestCompleteMessage(arr[1], arr[2], value); - } - case CompactMessageType.RequestData: { - const data = arr[3]; - const value = data === undefined ? data : new RpcValue(data, undefined); - return new msg.RequestDataMessage(arr[1], arr[2], value); - } - case CompactMessageType.RequestError: { - return new msg.RequestErrorMessage(arr[1], arr[2], new RpcValue(arr[3], undefined)); - } - case CompactMessageType.RequestUnsubscribe: { - return new msg.RequestUnsubscribeMessage(arr[1]); - } - case CompactMessageType.ResponseComplete: { - const data = arr[2]; - const value = data === undefined ? data : new RpcValue(data, undefined); - return new msg.ResponseCompleteMessage(arr[1], value); - } - case CompactMessageType.ResponseData: { - return new msg.ResponseDataMessage(arr[1], new RpcValue(arr[2], undefined)); - } - case CompactMessageType.ResponseError: { - return new msg.ResponseErrorMessage(arr[1], new RpcValue(arr[2], undefined)); - } - case CompactMessageType.ResponseUnsubscribe: { - return new msg.ResponseUnsubscribeMessage(arr[1]); - } - case CompactMessageType.Notification: { - return new msg.NotificationMessage(arr[1], new RpcValue(arr[2], undefined)); - } - } - throw RpcError.value(RpcError.validation('Unknown message type')); -}; - -export class CompactRpcMessageCodec implements RpcMessageCodec { - id = 'rx.compact'; - format = RpcMessageFormat.Compact; - - public encodeMessage(jsonCodec: JsonValueCodec, message: msg.ReactiveRpcMessage): void { - message.encodeCompact(jsonCodec); - } - - public encodeBatch(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): void { - const encoder = jsonCodec.encoder; - if (encoder instanceof CborEncoder || encoder instanceof MsgPackEncoder) { - const length = batch.length; - encoder.writeArrHdr(length); - for (let i = 0; i < length; i++) batch[i].encodeCompact(jsonCodec); - } else if (encoder instanceof JsonEncoder) { - const length = batch.length; - const last = length - 1; - encoder.writeStartArr(); - for (let i = 0; i < last; i++) { - batch[i].encodeCompact(jsonCodec); - encoder.writeArrSeparator(); - } - if (length > 0) batch[last].encodeCompact(jsonCodec); - encoder.writeEndArr(); - } else { - const jsonMessages: types.CompactMessage[] = []; - const length = batch.length; - for (let i = 0; i < length; i++) jsonMessages.push(batch[i].toCompact()); - const encoder = jsonCodec.encoder; - encoder.writeArr(jsonMessages); - } - } - - public encode(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): Uint8Array { - const encoder = jsonCodec.encoder; - const writer = encoder.writer; - writer.reset(); - this.encodeBatch(jsonCodec, batch); - return writer.flush(); - } - - public decodeBatch(jsonCodec: JsonValueCodec, uint8: Uint8Array): msg.ReactiveRpcMessage[] { - const decoder = jsonCodec.decoder; - const value = decoder.read(uint8); - if (!(value instanceof Array)) throw RpcError.badRequest(); - if (typeof value[0] === 'number') return [fromJson(value as unknown[])]; - const result: msg.ReactiveRpcMessage[] = []; - const length = value.length; - for (let i = 0; i < length; i++) { - const item = value[i]; - result.push(fromJson(item as unknown)); - } - return result; - } - - public fromJson(compact: types.CompactMessage): msg.ReactiveRpcMessage { - return fromJson(compact); - } -} diff --git a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts deleted file mode 100644 index 9a35f725a5..0000000000 --- a/src/reactive-rpc/common/codec/compact/__tests__/CompactRpcMessageCodec.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {MsgPackJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/msgpack'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RequestCompleteMessage} from '../../../messages'; -import {CompactRpcMessageCodec} from '../CompactRpcMessageCodec'; - -describe('sample messages', () => { - test('can decode a message', () => { - const msgpack = new MsgPackJsonValueCodec(new Writer(128)); - const codec = new CompactRpcMessageCodec(); - const batch = new Uint8Array([145, 148, 1, 1, 166, 100, 111, 117, 98, 108, 101, 129, 163, 110, 117, 109, 128]); - const messages = codec.decodeBatch(msgpack, batch); - expect(messages.length).toBe(1); - expect(messages[0]).toBeInstanceOf(RequestCompleteMessage); - expect((messages[0] as any).id).toBe(1); - expect((messages[0] as any).method).toBe('double'); - expect((messages[0] as any).value.data.num).toEqual({}); - }); -}); diff --git a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts deleted file mode 100644 index 0c3119e21e..0000000000 --- a/src/reactive-rpc/common/codec/compact/__tests__/automated.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {compactMessages} from './compact-messages'; -import {CompactRpcMessageCodec} from '..'; -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {messages} from '../../../messages/__tests__/fixtures'; - -const codec = new CompactRpcMessageCodec(); -const writer = new Writer(8 * Math.round(Math.random() * 100)); -const cborCodec = new CborJsonValueCodec(writer); - -describe('hydrate, encode, decode', () => { - for (const [name, compact] of Object.entries(compactMessages)) { - test(name, () => { - const message = codec.fromJson(compact); - codec.encodeBatch(cborCodec, [message]); - const encoded = cborCodec.encoder.writer.flush(); - const [decoded] = codec.decodeBatch(cborCodec, encoded); - expect(decoded).toStrictEqual(message); - }); - } -}); - -describe('encode, decode', () => { - for (const [name, message] of Object.entries(messages)) { - test(name, () => { - codec.encodeBatch(cborCodec, [message]); - const encoded = cborCodec.encoder.writer.flush(); - const [decoded] = codec.decodeBatch(cborCodec, encoded); - expect(decoded).toStrictEqual(message); - }); - } -}); diff --git a/src/reactive-rpc/common/codec/compact/__tests__/compact-messages.ts b/src/reactive-rpc/common/codec/compact/__tests__/compact-messages.ts deleted file mode 100644 index ee12612804..0000000000 --- a/src/reactive-rpc/common/codec/compact/__tests__/compact-messages.ts +++ /dev/null @@ -1,267 +0,0 @@ -import {CompactMessageType} from '../constants'; -import { - CompactNotificationMessage, - CompactRequestDataMessage, - CompactRequestCompleteMessage, - CompactRequestErrorMessage, - CompactRequestUnsubscribeMessage, - CompactResponseDataMessage, - CompactResponseCompleteMessage, - CompactResponseErrorMessage, - CompactResponseUnsubscribeMessage, -} from '../types'; - -const notification1: CompactNotificationMessage = [CompactMessageType.Notification, 'a']; -const notification2: CompactNotificationMessage = [CompactMessageType.Notification, 'test']; -const notification3: CompactNotificationMessage = [CompactMessageType.Notification, 'test', 123]; -const notification4: CompactNotificationMessage = [CompactMessageType.Notification, 'foo', {hello: 'world'}]; - -const reqData1: CompactRequestDataMessage = [CompactMessageType.RequestData, 1, '']; -const reqData2: CompactRequestDataMessage = [CompactMessageType.RequestData, 2, 'test']; -const reqData3: CompactRequestDataMessage = [CompactMessageType.RequestData, 3, 'hello.world']; -const reqData4: CompactRequestDataMessage = [CompactMessageType.RequestData, 3, 'ga.hello.world', {}]; -const reqData5: CompactRequestDataMessage = [ - CompactMessageType.RequestData, - 3, - 'ga.hello.world', - {id: '123lasdjflaksjdf'}, -]; -const reqData6: CompactRequestDataMessage = [ - CompactMessageType.RequestData, - 0, - 'ga.hello.world', - { - texts: [ - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - ], - }, -]; - -const reqComplete1: CompactRequestCompleteMessage = [CompactMessageType.RequestComplete, 1, '']; -const reqComplete2: CompactRequestCompleteMessage = [CompactMessageType.RequestComplete, 1, 'asdf']; -const reqComplete3: CompactRequestCompleteMessage = [CompactMessageType.RequestComplete, 1234, 'a.b.c.c.d.a']; -const reqComplete4: CompactRequestCompleteMessage = [CompactMessageType.RequestComplete, 1234, 'a.b.c.c.d.a', {}]; -const reqComplete5: CompactRequestCompleteMessage = [CompactMessageType.RequestComplete, 1234, 'a.b.c.c.d.a', []]; -const reqComplete6: CompactRequestCompleteMessage = [ - CompactMessageType.RequestComplete, - 12345, - 'a.b.c.c.d.a', - {foo: {bar: 'test'}}, -]; - -const reqError1: CompactRequestErrorMessage = [CompactMessageType.RequestError, 1, '', {}]; -const reqError2: CompactRequestErrorMessage = [CompactMessageType.RequestError, 5, '', {a: 'b'}]; -const reqError3: CompactRequestErrorMessage = [CompactMessageType.RequestError, 15, 'hmm', {a: [1, 2, 3]}]; -const reqError4: CompactRequestErrorMessage = [ - CompactMessageType.RequestError, - 55555, - '', - { - message: 'Some error happened', - code: 'SOME_ERROR', - errno: 94849, - }, -]; - -const reqUnsubscribe1: CompactRequestUnsubscribeMessage = [CompactMessageType.RequestUnsubscribe, 1]; -const reqUnsubscribe2: CompactRequestUnsubscribeMessage = [CompactMessageType.RequestUnsubscribe, 23423]; - -const resData1: CompactResponseDataMessage = [CompactMessageType.ResponseData, 1, 'response']; -const resData2: CompactResponseDataMessage = [CompactMessageType.ResponseData, 123, {}]; - -const resComplete1: CompactResponseCompleteMessage = [CompactMessageType.ResponseComplete, 1]; -const resComplete2: CompactResponseCompleteMessage = [CompactMessageType.ResponseComplete, 123]; -const resComplete3: CompactResponseCompleteMessage = [CompactMessageType.ResponseComplete, 4444, {}]; -const resComplete4: CompactResponseCompleteMessage = [ - CompactMessageType.ResponseComplete, - 4444, - [1, 2, 3, {foo: 'bar'}], -]; -const resComplete5: CompactResponseCompleteMessage = [ - CompactMessageType.ResponseComplete, - 4444, - [ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ], -]; -const resComplete6: CompactResponseCompleteMessage = [ - CompactMessageType.ResponseComplete, - 4444, - [ - 1, - 2, - 3, - { - texts: [ - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - ], - }, - ], -]; - -const resError1: CompactResponseErrorMessage = [CompactMessageType.ResponseError, 1, {}]; -const resError2: CompactResponseErrorMessage = [CompactMessageType.ResponseError, 123, {block: {id: 123}}]; - -const resUnsubscribe1: CompactResponseUnsubscribeMessage = [CompactMessageType.ResponseUnsubscribe, 1]; -const resUnsubscribe2: CompactResponseUnsubscribeMessage = [CompactMessageType.ResponseUnsubscribe, 999]; - -export const compactMessages = { - notification1, - notification2, - notification3, - notification4, - reqData1, - reqData2, - reqData3, - reqData4, - reqData5, - reqData6, - reqComplete1, - reqComplete2, - reqComplete3, - reqComplete4, - reqComplete5, - reqComplete6, - reqError1, - reqError2, - reqError3, - reqError4, - reqUnsubscribe1, - reqUnsubscribe2, - resData1, - resData2, - resComplete1, - resComplete2, - resComplete3, - resComplete4, - resComplete5, - resComplete6, - resError1, - resError2, - resUnsubscribe1, - resUnsubscribe2, -}; diff --git a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts b/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts deleted file mode 100644 index 90950cb400..0000000000 --- a/src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import {CompactRpcMessageCodec} from '..'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import { - NotificationMessage, - ReactiveRpcMessage, - RequestCompleteMessage, - RequestDataMessage, - RequestErrorMessage, - RequestUnsubscribeMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, - ResponseUnsubscribeMessage, -} from '../../../messages'; -import {RpcValue} from '../../../messages/Value'; -import {RpcError} from '../../../rpc/caller/error'; - -const writer = new Writer(8 * Math.round(Math.random() * 100)); -const codecs = new Codecs(writer); -const codec = new CompactRpcMessageCodec(); - -// const codecList = [codecs.msgpack]; -const codecList = [codecs.cbor, codecs.msgpack, codecs.json]; - -for (const jsonCodec of codecList) { - const assertMessage = (message: ReactiveRpcMessage) => { - const encoded = codec.encode(jsonCodec, [message]); - const decoded = jsonCodec.decoder.read(encoded); - expect(decoded).toStrictEqual([message.toCompact()]); - }; - - describe(jsonCodec.id, () => { - test('Notification message', () => { - const value = new RpcValue({foo: 'bar'}, undefined); - const message = new NotificationMessage('abc', value); - assertMessage(message); - }); - - test('Request Data message', () => { - const value = new RpcValue([1, 2, 3], undefined); - const message = new RequestDataMessage(123456, 'a', value); - assertMessage(message); - }); - - test('Request Complete message', () => { - const value = new RpcValue(true, undefined); - const message = new RequestCompleteMessage(3, 'abc', value); - assertMessage(message); - }); - - test('Request Error message', () => { - const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined); - const message = new RequestErrorMessage(0, 'wtf', value); - assertMessage(message); - }); - - test('Request Unsubscribe message', () => { - const message = new RequestUnsubscribeMessage(98765); - assertMessage(message); - }); - - test('Response Data message', () => { - const value = new RpcValue([1, 2, 3], undefined); - const message = new ResponseDataMessage(123456, value); - assertMessage(message); - }); - - test('Response Complete message', () => { - const value = new RpcValue(true, undefined); - const message = new ResponseCompleteMessage(3, value); - assertMessage(message); - }); - - test('Response Error message', () => { - const value = new RpcValue({message: 'Error!', errno: 123, code: 'ERROR'}, undefined); - const message = new ResponseErrorMessage(0, value); - assertMessage(message); - }); - - test('Response Unsubscribe message', () => { - const message = new ResponseUnsubscribeMessage(98765); - assertMessage(message); - }); - - test('Response Error typed', () => { - const value = RpcError.internalErrorValue(null); - const message = new ResponseErrorMessage(123, value); - const encoded = codec.encode(jsonCodec, [message]); - const decoded1 = jsonCodec.decoder.read(encoded); - const decoded2 = codec.decodeBatch(jsonCodec, encoded); - expect((decoded1 as any)[0]).toEqual(decoded2[0].toCompact()); - expect((decoded2 as any)[0].value.data.message).toEqual(message.value.data.message); - }); - }); -} - -describe('batch of messages', () => { - const value = new RpcValue({foo: 'bar'}, undefined); - const message1 = new NotificationMessage('abc', value); - const message2 = new RequestDataMessage(123456, 'a', value); - const message3 = new ResponseCompleteMessage(3, value); - - for (const jsonCodec of codecList) { - describe(jsonCodec.id, () => { - test('two messages', () => { - const encoded = codec.encode(jsonCodec, [message1, message2]); - const decoded = jsonCodec.decoder.read(encoded); - expect(decoded).toStrictEqual([message1.toCompact(), message2.toCompact()]); - }); - - test('three messages', () => { - const encoded = codec.encode(jsonCodec, [message3, message1, message2]); - const decoded = jsonCodec.decoder.read(encoded); - expect(decoded).toStrictEqual([message3.toCompact(), message1.toCompact(), message2.toCompact()]); - }); - }); - } -}); diff --git a/src/reactive-rpc/common/codec/compact/constants.ts b/src/reactive-rpc/common/codec/compact/constants.ts deleted file mode 100644 index f5e00a4bca..0000000000 --- a/src/reactive-rpc/common/codec/compact/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const enum CompactMessageType { - RequestData = 0, - RequestComplete = 1, - RequestError = 2, - RequestUnsubscribe = 3, - - ResponseData = 4, - ResponseComplete = 5, - ResponseError = 6, - ResponseUnsubscribe = 7, - - Notification = 8, -} diff --git a/src/reactive-rpc/common/codec/compact/index.ts b/src/reactive-rpc/common/codec/compact/index.ts deleted file mode 100644 index 5cdd08cbba..0000000000 --- a/src/reactive-rpc/common/codec/compact/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './constants'; -export * from './types'; -export * from './CompactRpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/compact/types.ts b/src/reactive-rpc/common/codec/compact/types.ts deleted file mode 100644 index 5235cde801..0000000000 --- a/src/reactive-rpc/common/codec/compact/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type {CompactMessageType} from './constants'; - -/** Must be positive integer. */ -export type Id = number; - -/** Must be non-empty string, no longer than 128 characters. */ -export type Name = string; - -export type CompactNotificationMessage = - | [CompactMessageType.Notification, Name] - | [CompactMessageType.Notification, Name, Data]; - -export type CompactRequestDataMessage = - | [CompactMessageType.RequestData, Id, Name] - | [CompactMessageType.RequestData, Id, Name, Data]; -export type CompactRequestCompleteMessage = - | [CompactMessageType.RequestComplete, Id, Name] - | [CompactMessageType.RequestComplete, Id, Name, Data]; -export type CompactRequestErrorMessage = [CompactMessageType.RequestError, Id, Name, Data]; -export type CompactRequestUnsubscribeMessage = [CompactMessageType.RequestUnsubscribe, Id]; - -export type CompactResponseDataMessage = [CompactMessageType.ResponseData, Id, Data]; -export type CompactResponseCompleteMessage = - | [CompactMessageType.ResponseComplete, Id] - | [CompactMessageType.ResponseComplete, Id, Data]; -export type CompactResponseErrorMessage = [CompactMessageType.ResponseError, Id, Data]; -export type CompactResponseUnsubscribeMessage = [CompactMessageType.ResponseUnsubscribe, Id]; - -export type CompactMessage = - | CompactNotificationMessage - | CompactRequestDataMessage - | CompactRequestCompleteMessage - | CompactRequestErrorMessage - | CompactRequestUnsubscribeMessage - | CompactResponseDataMessage - | CompactResponseCompleteMessage - | CompactResponseErrorMessage - | CompactResponseUnsubscribeMessage; - -export type CompactMessageBatch = (CompactMessage | CompactMessageBatch)[]; diff --git a/src/reactive-rpc/common/codec/constants.ts b/src/reactive-rpc/common/codec/constants.ts deleted file mode 100644 index b9a9b81217..0000000000 --- a/src/reactive-rpc/common/codec/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const enum RpcMessageFormat { - Binary, - Compact, - JsonRpc2, -} diff --git a/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts b/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts deleted file mode 100644 index d5d2ab7237..0000000000 --- a/src/reactive-rpc/common/codec/json-rpc-2/JsonRpc2RpcMessageCodec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import {RpcMessageFormat} from '../constants'; -import {RpcError} from '../../rpc/caller/error'; -import {RpcValue} from '../../messages/Value'; -import {EncodingFormat} from '@jsonjoy.com/json-pack/lib/constants'; -import {TlvBinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; -import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import * as msg from '../../messages'; -import * as schema from './schema'; -import type {RpcMessageCodec} from '../types'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; - -export class JsonRpc2RpcMessageCodec implements RpcMessageCodec { - id = 'json2.verbose'; - format = RpcMessageFormat.JsonRpc2; - - public encodeMessage(jsonCodec: JsonValueCodec, message: msg.ReactiveRpcMessage): void { - if (message instanceof msg.ResponseCompleteMessage || message instanceof msg.ResponseDataMessage) { - const pojo: schema.JsonRpc2ResponseMessage = { - id: message.id, - result: message.value, - } as schema.JsonRpc2ResponseMessage; - schema.JsonRpc2Response.encoder(jsonCodec.format)(pojo, jsonCodec.encoder); - } else if (message instanceof msg.ResponseErrorMessage) { - const error = message.value.data; - if (error instanceof RpcError) { - const pojo: schema.JsonRpc2ErrorMessage = { - id: message.id, - error: { - message: error.message, - code: error.errno, - data: error.toJson(), - }, - } as schema.JsonRpc2ErrorMessage; - schema.JsonRpc2Error.encoder(jsonCodec.format)(pojo, jsonCodec.encoder); - } else { - const pojo: schema.JsonRpc2ErrorMessage = { - id: message.id, - error: { - message: 'Unknown error', - code: 0, - data: error, - }, - } as schema.JsonRpc2ErrorMessage; - schema.JsonRpc2Error.encoder(jsonCodec.format)(pojo, jsonCodec.encoder); - } - } else if (message instanceof msg.NotificationMessage) { - const pojo: schema.JsonRpc2NotificationMessage = { - method: message.method, - params: message.value, - } as schema.JsonRpc2NotificationMessage; - // console.log(schema.JsonRpc2Notification.compileJsonEncoder({}) + ''); - schema.JsonRpc2Notification.encoder(jsonCodec.format)(pojo, jsonCodec.encoder); - } else if ( - message instanceof msg.RequestCompleteMessage || - message instanceof msg.RequestDataMessage || - message instanceof msg.RequestErrorMessage - ) { - const pojo: schema.JsonRpc2RequestMessage = { - jsonrpc: '2.0', - id: message.id, - method: message.method, - params: message.value, - }; - schema.JsonRpc2Request.encoder(jsonCodec.format)(pojo, jsonCodec.encoder); - } - } - - public encodeBatch(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): void { - const length = batch.length; - if (length === 1) { - this.encodeMessage(jsonCodec, batch[0]); - } else { - switch (jsonCodec.format) { - case EncodingFormat.Cbor: - case EncodingFormat.MsgPack: { - const encoder = jsonCodec.encoder as unknown as TlvBinaryJsonEncoder; - encoder.writeArrHdr(length); - for (let i = 0; i < length; i++) { - this.encodeMessage(jsonCodec, batch[i]); - } - break; - } - case EncodingFormat.Json: { - const encoder = (jsonCodec as JsonJsonValueCodec).encoder; - encoder.writeStartArr(); - const last = length - 1; - for (let i = 0; i < last; i++) { - this.encodeMessage(jsonCodec, batch[i]); - encoder.writeArrSeparator(); - } - if (length > 0) this.encodeMessage(jsonCodec, batch[last]); - encoder.writeEndArr(); - break; - } - } - } - } - - public encode(jsonCodec: JsonValueCodec, batch: msg.ReactiveRpcMessage[]): Uint8Array { - const encoder = jsonCodec.encoder; - const writer = encoder.writer; - writer.reset(); - this.encodeBatch(jsonCodec, batch); - return writer.flush(); - } - - public decodeBatch(jsonCodec: JsonValueCodec, uint8: Uint8Array): msg.ReactiveRpcMessage[] { - try { - let jsonRpcMessages = jsonCodec.decoder.read(uint8) as unknown as schema.JsonRpc2Message[]; - if (!Array.isArray(jsonRpcMessages)) jsonRpcMessages = [jsonRpcMessages]; - const messages: msg.ReactiveRpcMessage[] = []; - const length = jsonRpcMessages.length; - for (let i = 0; i < length; i++) messages.push(this.fromJson(jsonRpcMessages[i])); - return messages; - } catch (error) { - if (error instanceof RpcError) throw error; - throw RpcError.badRequest(); - } - } - - public fromJson(message: schema.JsonRpc2Message): msg.ReactiveRpcMessage { - if (!message || typeof message !== 'object') throw RpcError.badRequest(); - if ((message as any).id === undefined) { - const notification = message as schema.JsonRpc2NotificationMessage; - const data = notification.params; - const value = new RpcValue(data, undefined); - return new msg.NotificationMessage(notification.method, value); - } - if (typeof (message as schema.JsonRpc2RequestMessage).method === 'string') { - const request = message as schema.JsonRpc2RequestMessage; - const data = request.params; - const value = data === undefined ? undefined : new RpcValue(request.params, undefined); - if (typeof request.id !== 'number') throw RpcError.badRequest(); - return new msg.RequestCompleteMessage(request.id, request.method, value); - } - if ((message as schema.JsonRpc2ResponseMessage).result !== undefined) { - const response = message as schema.JsonRpc2ResponseMessage; - if (typeof response.id !== 'number') throw RpcError.badRequest(); - const data = response.result; - const value = data === undefined ? undefined : new RpcValue(response.result, undefined); - return new msg.ResponseCompleteMessage(response.id, value); - } - if ((message as schema.JsonRpc2ErrorMessage).error !== undefined) { - const response = message as schema.JsonRpc2ErrorMessage; - const value = new RpcValue(response.error.data, undefined); - if (typeof response.id !== 'number') throw RpcError.badRequest(); - return new msg.ResponseErrorMessage(response.id, value); - } - throw RpcError.badRequest(); - } -} diff --git a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts b/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts deleted file mode 100644 index f0a9cdc95a..0000000000 --- a/src/reactive-rpc/common/codec/json-rpc-2/__tests__/automated.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {JsonRpc2RpcMessageCodec} from '..'; -import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {ReactiveRpcMessage} from '../../../messages'; -import {messages} from '../../../messages/__tests__/fixtures'; - -const filteredMessages: [string, ReactiveRpcMessage][] = [ - ['notification1', messages.notification1], - ['notification2', messages.notification2], - ['notification3', messages.notification3], - ['notification4', messages.notification4], - - ['reqComplete1', messages.reqComplete1], - ['reqComplete1', messages.reqComplete2], - ['reqComplete2', messages.reqComplete3], - ['reqComplete3', messages.reqComplete4], - ['reqComplete4', messages.reqComplete5], - ['reqComplete5', messages.reqComplete6], - - ['resComplete3', messages.resComplete3], - ['resComplete4', messages.resComplete4], - ['resComplete5', messages.resComplete5], - ['resComplete6', messages.resComplete6], - - ['resError1', messages.resError1], - ['resError2', messages.resError2], -]; - -const codec = new JsonRpc2RpcMessageCodec(); -const valueCodec = new JsonJsonValueCodec(new Writer(24)); - -describe('encode, decode', () => { - for (const [name, message] of filteredMessages) { - test(name, () => { - // console.log(message); - codec.encodeBatch(valueCodec, [message]); - const encoded = valueCodec.encoder.writer.flush(); - // console.log(Buffer.from(encoded).toString('utf8')); - const [decoded] = codec.decodeBatch(valueCodec, encoded); - expect(decoded).toStrictEqual(message); - }); - } -}); diff --git a/src/reactive-rpc/common/codec/json-rpc-2/index.ts b/src/reactive-rpc/common/codec/json-rpc-2/index.ts deleted file mode 100644 index 3bfa8a4fac..0000000000 --- a/src/reactive-rpc/common/codec/json-rpc-2/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './schema'; -export * from './JsonRpc2RpcMessageCodec'; diff --git a/src/reactive-rpc/common/codec/json-rpc-2/schema.ts b/src/reactive-rpc/common/codec/json-rpc-2/schema.ts deleted file mode 100644 index c958ab812a..0000000000 --- a/src/reactive-rpc/common/codec/json-rpc-2/schema.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {ResolveType, TypeSystem} from '../../../../json-type'; - -export const system = new TypeSystem(); -const t = system.t; - -export const JsonRpc2Id = t.num.options({format: 'i32'}); -system.alias('JsonRpc2Id', JsonRpc2Id); - -export const JsonRpc2Notification = t.Object( - t.prop('jsonrpc', t.Const('2.0')), - t.prop('method', t.str), - t.propOpt('params', t.any), -); -system.alias('JsonRpc2Notification', JsonRpc2Notification); - -export const JsonRpc2Request = t.Object( - t.prop('jsonrpc', t.Const('2.0')), - t.prop('id', t.Ref('JsonRpc2Id')), - t.prop( - 'method', - t.str.options({ - title: 'RPC method name', - description: 'JSON RPC 2.0 method name.', - }), - ), - t.propOpt('params', t.any), -); -system.alias('JsonRpc2Request', JsonRpc2Request); - -export const JsonRpc2Response = t.Object( - t.prop('jsonrpc', t.Const('2.0')), - t.prop('id', t.Ref('JsonRpc2Id')), - t.prop('result', t.any), -); -system.alias('JsonRpc2Response', JsonRpc2Response); - -export const JsonRpc2Error = t.Object( - t.prop('jsonrpc', t.Const('2.0')), - t.prop('id', t.Ref('JsonRpc2Id')), - t.prop( - 'error', - t.Object( - t.prop( - 'message', - t.str.options({ - title: 'Error message', - description: 'A string providing a short description of the error.', - }), - ), - t.prop( - 'code', - t.num.options({ - title: 'Error code', - description: 'A number that indicates the error type that occurred.', - format: 'i32', - }), - ), - t.propOpt( - 'data', - t.any.options({ - title: 'Error data', - description: 'A Primitive or Structured value that contains additional information about the error.', - }), - ), - ), - ), -); -system.alias('JsonRpc2Error', JsonRpc2Error); - -// export const JsonRpc2BatchRequest = t.Array( -// t.Or( -// t.Ref('JsonRpc2Request'), -// t.Ref('JsonRpc2Notification'), -// ).options({ -// discriminator: ['if', ['$?', ['=', '/method']], 0, 1], -// }), -// ); - -// export const JsonRpc2BatchResponse = t.Array( -// t.Or( -// t.Ref('JsonRpc2Response'), -// t.Ref('JsonRpc2Error'), -// ).options({ -// discriminator: ['if', ['$?', ['=', '/result']], 0, 1], -// }), -// ); - -// export const JsonRpc2PolymorphicRequest = t.Or( -// t.Ref('JsonRpc2Request'), -// t.Ref('JsonRpc2Notification'), -// t.Ref('JsonRpc2BatchRequest'), -// ); - -export type JsonRpc2NotificationMessage = ResolveType; -export type JsonRpc2RequestMessage = ResolveType; -export type JsonRpc2ResponseMessage = ResolveType; -export type JsonRpc2ErrorMessage = ResolveType; -export type JsonRpc2Message = - | JsonRpc2NotificationMessage - | JsonRpc2RequestMessage - | JsonRpc2ResponseMessage - | JsonRpc2ErrorMessage; diff --git a/src/reactive-rpc/common/codec/nominal/README.md b/src/reactive-rpc/common/codec/nominal/README.md deleted file mode 100644 index 7961c9fbd4..0000000000 --- a/src/reactive-rpc/common/codec/nominal/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# `nominal` codec for Reactive-RPC messages - -`nominal` codec specifies, in principle, what messages and what fields exist in -Reactive-RPC protocol. - -| No. | Message | Message ID | Method name | Payload | -|-----------|------------------------|--------------|-------------|------------| -| 1 | Notification | No | Yes | Maybe | -| 2 | Request Data | Yes | Yes | Yes | -| 3 | Request Complete | Yes | Maybe | Maybe | -| 4 | Request Error | Yes | Maybe | Yes | -| 5 | Request Un-subscribe | Yes | No | No | -| 6 | Response Data | Yes | No | Yes | -| 7 | Response Complete | Yes | No | Maybe | -| 8 | Response Error | Yes | No | Yes | -| 9 | Response Un-subscribe | Yes | No | No | diff --git a/src/reactive-rpc/common/codec/types.ts b/src/reactive-rpc/common/codec/types.ts deleted file mode 100644 index 543f62638b..0000000000 --- a/src/reactive-rpc/common/codec/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {ReactiveRpcMessage} from '../messages'; -import type {RpcMessageFormat} from './constants'; - -export interface RpcMessageCodec { - id: string; - format: RpcMessageFormat; - encodeMessage(jsonCodec: JsonValueCodec, message: ReactiveRpcMessage): void; - encodeBatch(jsonCodec: JsonValueCodec, batch: ReactiveRpcMessage[]): void; - encode(jsonCodec: JsonValueCodec, batch: ReactiveRpcMessage[]): Uint8Array; - decodeBatch(jsonCodec: JsonValueCodec, uint8: Uint8Array): ReactiveRpcMessage[]; -} diff --git a/src/reactive-rpc/common/index.ts b/src/reactive-rpc/common/index.ts deleted file mode 100644 index 0859fd73d9..0000000000 --- a/src/reactive-rpc/common/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './types'; -export * from './messages'; -export * from './channel'; -export * from './rpc'; diff --git a/src/reactive-rpc/common/messages/Value.ts b/src/reactive-rpc/common/messages/Value.ts deleted file mode 100644 index f111ae81c5..0000000000 --- a/src/reactive-rpc/common/messages/Value.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Value as V} from '../../../json-type-value/Value'; -import type {Type} from '../../../json-type'; - -/** - * @deprecated Use `Value` directly. - */ -export class RpcValue extends V { - constructor( - public data: V, - public type: Type | undefined, - ) { - super(type, data); - } -} diff --git a/src/reactive-rpc/common/messages/__tests__/fixtures.ts b/src/reactive-rpc/common/messages/__tests__/fixtures.ts deleted file mode 100644 index e0408a7ba7..0000000000 --- a/src/reactive-rpc/common/messages/__tests__/fixtures.ts +++ /dev/null @@ -1,241 +0,0 @@ -import {RpcValue} from '../Value'; -import * as msg from '../messages'; - -const val = (data: unknown = undefined) => new RpcValue(data, undefined); - -const notification1 = new msg.NotificationMessage('a', val('a')); -const notification2 = new msg.NotificationMessage('test', val(null)); -const notification3 = new msg.NotificationMessage('test', val(123)); -const notification4 = new msg.NotificationMessage('foo', val({hello: 'world'})); - -const reqData1 = new msg.RequestDataMessage(2, 'test', undefined); -const reqData2 = new msg.RequestDataMessage(2, 'hello.world', undefined); -const reqData3 = new msg.RequestDataMessage(3, 'hello.world', undefined); -const reqData4 = new msg.RequestDataMessage(3, 'ga.hello.world', val({})); -const reqData5 = new msg.RequestDataMessage(3, 'ga.hello.world', val({id: '1234asdf'})); -const reqData6 = new msg.RequestDataMessage( - 0, - 'ga.hello.world', - val({ - texts: [ - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - ], - }), -); - -const reqComplete1 = new msg.RequestCompleteMessage(1, '', undefined); -const reqComplete2 = new msg.RequestCompleteMessage(1, 'asdf', undefined); -const reqComplete3 = new msg.RequestCompleteMessage(1234, 'a.b.c.d.e.f', undefined); -const reqComplete4 = new msg.RequestCompleteMessage(1234, 'a.b.c.d.e.f', val({})); -const reqComplete5 = new msg.RequestCompleteMessage(1234, 'a.b.c.d.e.f', val([])); -const reqComplete6 = new msg.RequestCompleteMessage(1234, 'a.b.c.d.e.f', val({foo: {bar: 'test'}})); - -const reqError1 = new msg.RequestErrorMessage(1, '', val(null)); -const reqError2 = new msg.RequestErrorMessage(5, '', val({a: 'b'})); -const reqError3 = new msg.RequestErrorMessage(15, 'hmm', val({a: [1, 2, 3]})); -const reqError4 = new msg.RequestErrorMessage( - 55555, - '', - val({ - message: 'Some error happened', - code: 'SOME_ERROR', - errno: 94849, - }), -); - -const reqUnsubscribe1 = new msg.RequestUnsubscribeMessage(1); -const reqUnsubscribe2 = new msg.RequestUnsubscribeMessage(12345); - -const resData1 = new msg.ResponseDataMessage(1, val('response')); -const resData2 = new msg.ResponseDataMessage(123, val({})); - -const resComplete1 = new msg.ResponseCompleteMessage(1, undefined); -const resComplete2 = new msg.ResponseCompleteMessage(123, undefined); -const resComplete3 = new msg.ResponseCompleteMessage(4444, val({})); -const resComplete4 = new msg.ResponseCompleteMessage(4444, val([1, 2, 3, {foo: 'bar'}])); -const resComplete5 = new msg.ResponseCompleteMessage( - 4444, - val([ - 1, - 2, - 3, - { - looongLoooonnnngggg: 'bar', - looongLoooonnnngggg2: 'bar', - looongLoooonnnngggg3: 'bar', - looongLoooonnnngggg4: 'bar', - looongLoooonnnngggg5: 'bar', - looongLoooonnnngggg6: 'bar', - looongLoooonnnngggg7: 'bar', - someVeryVeryLongKeyNameSuperDuperLongKeyName: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName1: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName2: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName3: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName4: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName5: 'very very long value, I said, very very long value', - someVeryVeryLongKeyNameSuperDuperLongKeyName6: 'very very long value, I said, very very long value', - }, - ]), -); -const resComplete6 = new msg.ResponseCompleteMessage( - 4444, - val([ - 1, - 2, - 3, - { - texts: [ - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - 'asldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfaasldfkjaslkdfjlajksdf923oaplsjdf09q23lf;asdjf09q23fasdfj0a93flasdjf092q3rljfkasdfaksjdflasjdfl;asdfja9j203jasdlfkj9832jflaisjdf902j3rfa', - ], - }, - ]), -); - -const resError1 = new msg.ResponseErrorMessage(1, val({})); -const resError2 = new msg.ResponseErrorMessage(123, val({block: {id: 123}})); - -const resUnsubscribe1 = new msg.ResponseUnsubscribeMessage(1); -const resUnsubscribe2 = new msg.ResponseUnsubscribeMessage(999); - -export const messages = { - notification1, - notification2, - notification3, - notification4, - reqData1, - reqData2, - reqData3, - reqData4, - reqData5, - reqData6, - reqComplete1, - reqComplete2, - reqComplete3, - reqComplete4, - reqComplete5, - reqComplete6, - reqError1, - reqError2, - reqError3, - reqError4, - reqUnsubscribe1, - reqUnsubscribe2, - resData1, - resData2, - resComplete1, - resComplete2, - resComplete3, - resComplete4, - resComplete5, - resComplete6, - resError1, - resError2, - resUnsubscribe1, - resUnsubscribe2, -}; diff --git a/src/reactive-rpc/common/messages/index.ts b/src/reactive-rpc/common/messages/index.ts deleted file mode 100644 index 32fcb87ff0..0000000000 --- a/src/reactive-rpc/common/messages/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './messages'; diff --git a/src/reactive-rpc/common/messages/messages.ts b/src/reactive-rpc/common/messages/messages.ts deleted file mode 100644 index 309e232b32..0000000000 --- a/src/reactive-rpc/common/messages/messages.ts +++ /dev/null @@ -1,422 +0,0 @@ -import {BinaryMessageType} from '../codec/binary/constants'; -import {CompactMessageType} from '../codec/compact/constants'; -import {validateId, validateMethod} from '../rpc/validation'; -import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; -import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {RpcValue} from './Value'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; -import type * as cmsg from '../codec/compact/types'; -import type {Message} from './types'; - -const encodeHeader = ( - writer: BinaryJsonEncoder['writer'], - typeU16: number, - id: number, - payloadSize: number, - start: number, -) => { - if (payloadSize <= 0b1111_11111111) { - const w1 = typeU16 | payloadSize; - const w2 = id; - writer.view.setUint32(start, (w1 << 16) | w2); - } else if (payloadSize <= 0b1111_11111111_1111111) { - writer.u8(id & 0xff); - const w1 = typeU16 | 0b000_1_0000_00000000 | (payloadSize >> 7); - const w2 = ((payloadSize & 0b0111_1111) << 8) | (id >> 8); - writer.view.setUint32(start, (w1 << 16) | w2); - } else { - writer.u16(id); - const w1 = typeU16 | 0b000_1_0000_00000000 | (payloadSize >> 15); - const w2 = 0b1000_0000_00000000 | (payloadSize & 0b0111_1111_11111111); - writer.view.setUint32(start, (w1 << 16) | w2); - } -}; - -const encodeBinaryWithNameAndPayload = ( - codec: JsonValueCodec, - typeU16: number, - id: number, - name: string, - value: RpcValue | undefined, -) => { - const writer = codec.encoder.writer; - const nameLength = name.length; - writer.ensureCapacity(5 + nameLength); - writer.uint8[writer.x + 4] = nameLength; - writer.x += 5; - writer.ascii(name); - const x0 = writer.x0; - const x = writer.x; - if (value) value.encode(codec); - const shift = writer.x0 - x0; - const payloadStart = x + shift; - const start = payloadStart - 5 - nameLength; - const payloadSize = writer.x - payloadStart; - encodeHeader(writer, typeU16, id, payloadSize, start); -}; - -const encodeBinaryWithPayload = ( - codec: JsonValueCodec, - typeU16: number, - id: number, - value: RpcValue | undefined, -) => { - const writer = codec.encoder.writer; - writer.move(4); - const x0 = writer.x0; - const x = writer.x; - if (value) value.encode(codec); - const shift = writer.x0 - x0; - const payloadStart = x + shift; - const start = payloadStart - 4; - const payloadSize = writer.x - payloadStart; - encodeHeader(writer, typeU16, id, payloadSize, start); -}; - -const encodeCompactWithNameAndPayload = ( - codec: JsonValueCodec, - type: CompactMessageType, - msg: RequestDataMessage | RequestCompleteMessage | RequestErrorMessage, -) => { - const encoder = codec.encoder; - if (encoder instanceof CborEncoder || encoder instanceof MsgPackEncoder) { - const value = msg.value; - const hasValue = value !== undefined; - encoder.writeArrHdr(hasValue ? 4 : 3); - encoder.writeUInteger(type); - encoder.writeUInteger(msg.id); - encoder.writeAsciiStr(msg.method); - if (hasValue) { - if (value.type) value.type.encoder(codec.format)(value.data, encoder); - else encoder.writeAny(value.data); - } - } else if (encoder instanceof JsonEncoder) { - const value = msg.value; - encoder.writeStartArr(); - encoder.writeNumber(type); - encoder.writeArrSeparator(); - encoder.writeNumber(msg.id); - encoder.writeArrSeparator(); - encoder.writeAsciiStr(msg.method); - const hasValue = value !== undefined; - if (hasValue) { - encoder.writeArrSeparator(); - if (value.type) value.type.encoder(codec.format)(value.data, encoder); - else encoder.writeAny(value.data); - } - encoder.writeEndArr(); - } else encoder.writeArr(msg.toCompact()); -}; - -const encodeCompactWithPayload = ( - codec: JsonValueCodec, - type: CompactMessageType, - msg: ResponseCompleteMessage | ResponseDataMessage | ResponseErrorMessage, -) => { - const encoder = codec.encoder; - if (encoder instanceof CborEncoder || encoder instanceof MsgPackEncoder) { - const value = msg.value; - const hasValue = value !== undefined; - encoder.writeArrHdr(hasValue ? 3 : 2); - encoder.writeUInteger(type); - encoder.writeUInteger(msg.id); - if (hasValue) { - if (value.type) { - value.type.encoder(codec.format)(value.data, encoder); - } else encoder.writeAny(value.data); - } - } else if (encoder instanceof JsonEncoder) { - const value = msg.value; - encoder.writeStartArr(); - encoder.writeNumber(type); - encoder.writeArrSeparator(); - encoder.writeNumber(msg.id); - const hasValue = value !== undefined; - if (hasValue) { - encoder.writeArrSeparator(); - if (value.type) value.type.encoder(codec.format)(value.data, encoder); - else encoder.writeAny(value.data); - } - encoder.writeEndArr(); - } else encoder.writeArr(msg.toCompact()); -}; - -/** - * @category Message - */ -export class NotificationMessage = RpcValue> implements Message { - constructor( - public readonly method: string, - public readonly value: V, - ) {} - - public validate(): void { - validateMethod(this.method); - } - - public toCompact(): cmsg.CompactNotificationMessage { - return this.value === undefined - ? [CompactMessageType.Notification, this.method] - : [CompactMessageType.Notification, this.method, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - const encoder = codec.encoder; - if (encoder instanceof CborEncoder || encoder instanceof MsgPackEncoder) { - const value = this.value; - const hasValue = value !== undefined; - encoder.writeArrHdr(hasValue ? 3 : 2); - encoder.writeUInteger(CompactMessageType.Notification); - encoder.writeAsciiStr(this.method); - if (hasValue) { - if (value.type) value.type.encoder(codec.format)(value.data, encoder); - else encoder.writeAny(value.data); - } - } else if (encoder instanceof JsonEncoder) { - const value = this.value; - encoder.writeStartArr(); - encoder.writeNumber(CompactMessageType.Notification); - encoder.writeArrSeparator(); - encoder.writeAsciiStr(this.method); - const hasValue = value !== undefined; - if (hasValue) { - encoder.writeArrSeparator(); - if (value.type) value.type.encoder(codec.format)(value.data, encoder); - else encoder.writeAny(value.data); - } - encoder.writeEndArr(); - } else encoder.writeArr(this.toCompact()); - } - - public encodeBinary(codec: JsonValueCodec): void { - const writer = codec.encoder.writer; - const name = this.method; - const nameLength = name.length; - writer.move(4); - writer.ascii(name); - const x0 = writer.x0; - const x = writer.x; - this.value.encode(codec); - const shift = writer.x0 - x0; - const payloadStart = x + shift; - const start = payloadStart - 4 - nameLength; - const payloadSize = writer.x - payloadStart; - writer.view.setUint32(start, (payloadSize << 8) + nameLength); - } -} - -/** - * @category Message - */ -export class RequestDataMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public readonly method: string, - public readonly value: undefined | V, - ) {} - - public validate(): void { - validateId(this.id); - if (this.method) validateMethod(this.method); - } - - public toCompact(): cmsg.CompactRequestDataMessage { - return this.value === undefined - ? [CompactMessageType.RequestData, this.id, this.method] - : [CompactMessageType.RequestData, this.id, this.method, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithNameAndPayload(codec, CompactMessageType.RequestData, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithNameAndPayload(codec, BinaryMessageType.RequestData << 13, this.id, this.method, this.value); - } -} - -/** - * @category Message - */ -export class RequestCompleteMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public readonly method: string, - public readonly value: undefined | V, - ) {} - - public validate(): void { - validateId(this.id); - if (this.method) validateMethod(this.method); - } - - public toCompact(): cmsg.CompactRequestCompleteMessage { - return this.value === undefined - ? [CompactMessageType.RequestComplete, this.id, this.method] - : [CompactMessageType.RequestComplete, this.id, this.method, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithNameAndPayload(codec, CompactMessageType.RequestComplete, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithNameAndPayload(codec, BinaryMessageType.RequestComplete << 13, this.id, this.method, this.value); - } -} - -/** - * @category Message - */ -export class RequestErrorMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public method: string, - public readonly value: V, - ) {} - - public validate(): void { - validateId(this.id); - if (this.method) validateMethod(this.method); - } - - public toCompact(): cmsg.CompactRequestErrorMessage { - return [CompactMessageType.RequestError, this.id, this.method, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithNameAndPayload(codec, CompactMessageType.RequestError, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithNameAndPayload(codec, BinaryMessageType.RequestError << 13, this.id, this.method, this.value); - } -} - -/** - * @category Message - */ -export class RequestUnsubscribeMessage implements Message { - constructor(public readonly id: number) {} - - public validate(): void { - validateId(this.id); - } - - public toCompact(): cmsg.CompactRequestUnsubscribeMessage { - return [CompactMessageType.RequestUnsubscribe, this.id]; - } - - public encodeCompact(codec: JsonValueCodec): void { - codec.encoder.writeArr(this.toCompact()); - } - - public encodeBinary(codec: JsonValueCodec): void { - codec.encoder.writer.u32(0b11100000_00000000_00000000_00000000 | this.id); - } -} - -/** - * @category Message - */ -export class ResponseCompleteMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public readonly value: undefined | V, - ) {} - - public validate(): void { - validateId(this.id); - } - - public toCompact(): cmsg.CompactResponseCompleteMessage { - return this.value === undefined - ? [CompactMessageType.ResponseComplete, this.id] - : [CompactMessageType.ResponseComplete, this.id, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithPayload(codec, CompactMessageType.ResponseComplete, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithPayload(codec, BinaryMessageType.ResponseComplete << 13, this.id, this.value); - } -} - -/** - * @category Message - */ -export class ResponseDataMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public readonly value: V, - ) {} - - public validate(): void { - validateId(this.id); - } - - public toCompact(): cmsg.CompactResponseDataMessage { - return [CompactMessageType.ResponseData, this.id, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithPayload(codec, CompactMessageType.ResponseData, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithPayload(codec, BinaryMessageType.ResponseData << 13, this.id, this.value); - } -} - -/** - * @category Message - */ -export class ResponseErrorMessage = RpcValue> implements Message { - constructor( - public readonly id: number, - public readonly value: V, - ) {} - - public validate(): void { - validateId(this.id); - } - - public toCompact(): cmsg.CompactResponseErrorMessage { - return [CompactMessageType.ResponseError, this.id, this.value.data]; - } - - public encodeCompact(codec: JsonValueCodec): void { - encodeCompactWithPayload(codec, CompactMessageType.ResponseError, this); - } - - public encodeBinary(codec: JsonValueCodec): void { - encodeBinaryWithPayload(codec, BinaryMessageType.ResponseError << 13, this.id, this.value); - } -} - -/** - * @category Message - */ -export class ResponseUnsubscribeMessage implements Message { - constructor(public readonly id: number) {} - - public validate(): void { - validateId(this.id); - } - - public toCompact(): cmsg.CompactResponseUnsubscribeMessage { - return [CompactMessageType.ResponseUnsubscribe, this.id]; - } - - public encodeCompact(codec: JsonValueCodec): void { - codec.encoder.writeArr(this.toCompact()); - } - - public encodeBinary(codec: JsonValueCodec): void { - codec.encoder.writer.u32(0b11100000_00000001_00000000_00000000 | this.id); - } -} diff --git a/src/reactive-rpc/common/messages/types.ts b/src/reactive-rpc/common/messages/types.ts deleted file mode 100644 index 2c36fd2741..0000000000 --- a/src/reactive-rpc/common/messages/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type * as msg from './messages'; - -/** - * Messages that client can send. - */ -export type ReactiveRpcClientMessage = - | msg.NotificationMessage - | msg.RequestDataMessage - | msg.RequestCompleteMessage - | msg.RequestErrorMessage - | msg.ResponseUnsubscribeMessage; - -/** - * Messages with which server can respond. - */ -export type ReactiveRpcServerMessage = - | msg.ResponseDataMessage - | msg.ResponseCompleteMessage - | msg.ResponseErrorMessage - | msg.RequestUnsubscribeMessage; - -/** - * All Reactive RPC messages. - */ -export type ReactiveRpcMessage = ReactiveRpcClientMessage | ReactiveRpcServerMessage; - -export interface Message

{ - value?: undefined | unknown; - validate(): void; - toCompact(): P; - encodeCompact(codec: JsonValueCodec): void; - encodeBinary(codec: JsonValueCodec): void; -} diff --git a/src/reactive-rpc/common/rpc/RpcDuplex.ts b/src/reactive-rpc/common/rpc/RpcDuplex.ts deleted file mode 100644 index f5219110a3..0000000000 --- a/src/reactive-rpc/common/rpc/RpcDuplex.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {Observable} from 'rxjs'; -import * as msg from '../messages'; -import {StreamingRpcClient} from './client/StreamingRpcClient'; -import {RpcMessageStreamProcessor} from './RpcMessageStreamProcessor'; - -export interface RpcDuplexParams { - client: StreamingRpcClient; - server: RpcMessageStreamProcessor; -} - -export class RpcDuplex { - public readonly client: StreamingRpcClient; - public readonly server: RpcMessageStreamProcessor; - - public constructor(params: RpcDuplexParams) { - this.client = params.client; - this.server = params.server; - } - - public onMessages(messages: msg.ReactiveRpcMessage[], ctx: Ctx): void { - const length = messages.length; - for (let i = 0; i < length; i++) this.onMessage(messages[i], ctx); - } - - public onMessage(message: msg.ReactiveRpcMessage, ctx: Ctx): void { - if (message instanceof msg.RequestDataMessage) this.server.onRequestDataMessage(message, ctx); - else if (message instanceof msg.RequestCompleteMessage) this.server.onRequestCompleteMessage(message, ctx); - else if (message instanceof msg.RequestErrorMessage) this.server.onRequestErrorMessage(message, ctx); - else if (message instanceof msg.ResponseUnsubscribeMessage) this.server.onUnsubscribeMessage(message); - else if (message instanceof msg.NotificationMessage) this.server.onNotificationMessage(message, ctx); - else if (message instanceof msg.ResponseCompleteMessage) return this.client.onResponseComplete(message); - else if (message instanceof msg.ResponseDataMessage) return this.client.onResponseData(message); - else if (message instanceof msg.ResponseErrorMessage) return this.client.onResponseError(message); - else if (message instanceof msg.RequestUnsubscribeMessage) return this.client.onRequestUnsubscribe(message); - } - - public call$(method: string, data: unknown): Observable; - public call$(method: string, data: Observable): Observable; - public call$(method: string, data: unknown | Observable): Observable { - return this.client.call$(method, data as any); - } - - public call(method: string, data: unknown): Promise { - return this.client.call(method, data); - } - - public notify(method: string, data: undefined | unknown): void { - this.client.notify(method, data); - } - - public stop() { - this.client.stop(); - this.server.stop(); - } - - public disconnect() { - this.client.disconnect(); - this.server.disconnect(); - } -} diff --git a/src/reactive-rpc/common/rpc/RpcMessageBatchProcessor.ts b/src/reactive-rpc/common/rpc/RpcMessageBatchProcessor.ts deleted file mode 100644 index e8f7410e32..0000000000 --- a/src/reactive-rpc/common/rpc/RpcMessageBatchProcessor.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as msg from '../messages'; -import {RpcError, RpcErrorValue} from './caller'; -import {validateId, validateMethod} from './validation'; -import type {RpcCaller} from './caller/RpcCaller'; - -export type IncomingBatchMessage = - | msg.RequestDataMessage - | msg.RequestCompleteMessage - | msg.RequestErrorMessage - | msg.NotificationMessage; -export type OutgoingBatchMessage = msg.ResponseCompleteMessage | msg.ResponseErrorMessage; - -export interface RpcMessageBatchProcessorOptions { - caller: RpcCaller; -} - -/** - * A global instance for processing RPC messages. Used for request/response like - * scenarios. For streaming scenarios, use `RpcMessageStreamProcessor`. - * - * This processor can be shared across different connection/requests as "ctx" - * is passed on each call and not state is held. - */ -export class RpcMessageBatchProcessor { - protected readonly caller: RpcCaller; - - constructor({caller}: RpcMessageBatchProcessorOptions) { - this.caller = caller; - } - - public async onBatch(list: IncomingBatchMessage[], ctx: Ctx): Promise { - try { - const promises: Promise[] = []; - const length = list.length; - for (let i = 0; i < length; i++) { - const message = list[i]; - switch (message.constructor) { - case msg.NotificationMessage: - this.onNotification(message as msg.NotificationMessage, ctx); - break; - case msg.RequestDataMessage: - case msg.RequestCompleteMessage: - case msg.RequestErrorMessage: - promises.push( - this.onRequest( - message as msg.RequestDataMessage | msg.RequestCompleteMessage | msg.RequestErrorMessage, - ctx, - ), - ); - break; - } - } - const settled = await Promise.allSettled(promises); - const result: OutgoingBatchMessage[] = []; - const settledLength = settled.length; - for (let i = 0; i < settledLength; i++) { - const item = settled[i]; - result.push(item.status === 'fulfilled' ? item.value : item.reason); - } - return result; - } catch (error) { - const value = RpcError.internalErrorValue(error); - return [new msg.ResponseErrorMessage(-1, value)]; - } - } - - public onNotification(message: msg.NotificationMessage, ctx: Ctx): void { - const method = message.method; - validateMethod(method); - this.caller.notification(method, message.value.data, ctx).catch((error: unknown) => {}); - } - - public async onRequest( - message: msg.RequestDataMessage | msg.RequestCompleteMessage | msg.RequestErrorMessage, - ctx: Ctx, - ): Promise { - const id = message.id; - validateId(id); - const method = message.method; - validateMethod(method); - try { - const value = message.value; - const data = value ? value.data : undefined; - const result = await this.caller.call(method, data, ctx); - return new msg.ResponseCompleteMessage(id, result); - } catch (error) { - throw new msg.ResponseErrorMessage(id, error as RpcErrorValue); - } - } -} diff --git a/src/reactive-rpc/common/rpc/RpcMessageStreamProcessor.ts b/src/reactive-rpc/common/rpc/RpcMessageStreamProcessor.ts deleted file mode 100644 index 5c797a96ea..0000000000 --- a/src/reactive-rpc/common/rpc/RpcMessageStreamProcessor.ts +++ /dev/null @@ -1,248 +0,0 @@ -import * as msg from '../messages'; -import {subscribeCompleteObserver} from '../util/subscribeCompleteObserver'; -import {TimedQueue} from '../util/TimedQueue'; -import {RpcErrorCodes, RpcError} from './caller/error'; -import {RpcValue} from '../messages/Value'; -import type {RpcCaller} from './caller/RpcCaller'; -import type {Call, RpcApiMap} from './caller/types'; - -type Send = (messages: (msg.ReactiveRpcServerMessage | msg.NotificationMessage)[]) => void; - -export interface RpcMessageStreamProcessorOptions { - caller: RpcCaller; - - /** - * Method to be called by server when it wants to send messages to the client. - * This is usually your WebSocket "send" method. - */ - send: Send; - - /** - * Number of messages to keep in buffer before sending them out. - * The buffer is flushed when the message reaches this limit or when the - * buffering time has reached the time specified in `bufferTime` parameter. - * Defaults to 10 messages. - */ - bufferSize?: number; - - /** - * Time in milliseconds for how long to buffer messages before sending them - * out. Defaults to 1 milliseconds. Set it to zero to disable buffering. - */ - bufferTime?: number; -} - -export interface RpcMessageStreamProcessorFromApiOptions - extends Omit, 'onCall'> { - api: RpcApiMap; -} - -export class RpcMessageStreamProcessor { - protected readonly caller: RpcCaller; - private readonly activeStreamCalls: Map> = new Map(); - protected send: (message: msg.ReactiveRpcServerMessage | msg.NotificationMessage) => void; - - /** Callback which sends message out of the server. */ - public onSend: Send; - - constructor({caller, send, bufferSize = 10, bufferTime = 1}: RpcMessageStreamProcessorOptions) { - this.caller = caller; - this.onSend = send; - - if (bufferTime) { - const buffer = new TimedQueue(); - buffer.itemLimit = bufferSize; - buffer.timeLimit = bufferTime; - buffer.onFlush = (messages) => this.onSend(messages as any); - this.send = (message) => { - buffer.push(message as any); - }; - } else { - this.send = (message) => { - this.onSend([message as any]); - }; - } - } - - /** - * Processes a single incoming Reactive-RPC message. - * - * @param message A single Reactive-RPC message. - * @param ctx Server context. - */ - public onMessage(message: msg.ReactiveRpcClientMessage, ctx: Ctx): void { - if (message instanceof msg.RequestDataMessage) this.onRequestDataMessage(message, ctx); - else if (message instanceof msg.RequestCompleteMessage) this.onRequestCompleteMessage(message, ctx); - else if (message instanceof msg.RequestErrorMessage) this.onRequestErrorMessage(message, ctx); - else if (message instanceof msg.NotificationMessage) this.onNotificationMessage(message, ctx); - else if (message instanceof msg.ResponseUnsubscribeMessage) this.onUnsubscribeMessage(message); - } - - /** - * Receives a list of all incoming messages from the client to process. - * - * @param messages A list of received messages. - * @param ctx Server context. - */ - public onMessages(messages: msg.ReactiveRpcClientMessage[], ctx: Ctx): void { - const length = messages.length; - for (let i = 0; i < length; i++) this.onMessage(messages[i], ctx); - } - - public sendNotification(method: string, value: RpcValue): void { - const message = new msg.NotificationMessage(method, value); - this.send(message); - } - - protected sendCompleteMessage(id: number, value: RpcValue | undefined): void { - const message = new msg.ResponseCompleteMessage(id, value); - this.send(message); - } - - protected sendDataMessage(id: number, value: RpcValue): void { - const message = new msg.ResponseDataMessage(id, value); - this.send(message); - } - - protected sendErrorMessage(id: number, value: RpcValue): void { - const message = new msg.ResponseErrorMessage(id, value); - this.send(message); - } - - protected sendUnsubscribeMessage(id: number): void { - const message = new msg.RequestUnsubscribeMessage(id); - this.send(message); - } - - protected execStaticCall(id: number, name: string, request: unknown, ctx: Ctx): void { - this.caller - .call(name, request, ctx) - .then((value: RpcValue) => this.sendCompleteMessage(id, value)) - .catch((value: RpcValue) => this.sendErrorMessage(id, value)); - } - - protected onStreamError = (id: number, error: RpcValue): void => { - this.sendErrorMessage(id, error); - this.activeStreamCalls.delete(id); - }; - - public stop(reason: RpcErrorCodes = RpcErrorCodes.STOP) { - this.send = (() => {}) as any; - for (const call of this.activeStreamCalls.values()) call.req$.error(RpcError.valueFromCode(reason)); - this.activeStreamCalls.clear(); - } - - public disconnect() { - this.stop(RpcErrorCodes.DISCONNECT); - } - - private sendError(id: number, code: RpcErrorCodes): void { - const data = RpcError.valueFromCode(code); - this.sendErrorMessage(id, data); - } - - private createStreamCall(id: number, name: string, ctx: Ctx): Call { - const call = this.caller.createCall(name, ctx); - this.activeStreamCalls.set(id, call); - subscribeCompleteObserver(call.res$, { - next: (value: RpcValue) => this.sendDataMessage(id, value), - error: (error: unknown) => this.onStreamError(id, error as RpcValue), - complete: (value: RpcValue | undefined) => { - this.activeStreamCalls.delete(id); - this.sendCompleteMessage(id, value); - }, - }); - call.reqUnsubscribe$.subscribe(() => { - if (this.activeStreamCalls.has(id)) this.sendUnsubscribeMessage(id); - }); - return call; - } - - public onRequestDataMessage(message: msg.RequestDataMessage, ctx: Ctx): void { - const {id, method, value} = message; - let call = this.activeStreamCalls.get(id); - if (!call) { - if (!method) { - this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); - return; - } - const info = this.caller.info(method); - if (!info) { - this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); - return; - } - if (info.isStreaming) { - call = this.createStreamCall(id, method, ctx); - } else { - this.execStaticCall(id, method, value ? value.data : undefined, ctx); - return; - } - } - if (call) { - const data = value ? value.data : undefined; - if (data !== undefined) { - call.req$.next(data); - } - } - } - - public onRequestCompleteMessage(message: msg.RequestCompleteMessage, ctx: Ctx): void { - const {id, method, value} = message; - const call = this.activeStreamCalls.get(id); - if (call) { - const {req$} = call; - const data = value ? value.data : undefined; - if (data !== undefined) req$.next(data); - req$.complete(); - return; - } - if (!method) { - this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); - return; - } - const caller = this.caller; - if (!caller.exists(method)) { - this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); - return; - } - const {isStreaming} = caller.info(method); - const data = value ? value.data : undefined; - if (isStreaming) { - const newCall = this.createStreamCall(id, method, ctx); - if (newCall) { - if (data !== undefined) { - newCall.req$.next(data); - newCall.req$.complete(); - } - } - } else this.execStaticCall(id, method, data, ctx); - } - - public onRequestErrorMessage(message: msg.RequestErrorMessage, ctx: Ctx): void { - const {id, method, value} = message; - const call = this.activeStreamCalls.get(id); - if (call) return call.req$.error(value.data); - if (!method) return this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); - if (!this.caller.exists(method)) return this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); - const {isStreaming} = this.caller.info(method); - if (!isStreaming) return this.sendError(id, RpcErrorCodes.INVALID_METHOD); - const streamCall = this.createStreamCall(id, method, ctx); - if (!streamCall) return; - streamCall.req$.error(value.data); - } - - public onUnsubscribeMessage(message: msg.ResponseUnsubscribeMessage): void { - const {id} = message; - const call = this.activeStreamCalls.get(id); - if (!call) return; - this.activeStreamCalls.delete(id); - call.req$.complete(); - } - - public onNotificationMessage(message: msg.NotificationMessage, ctx: Ctx): void { - const {method, value} = message; - if (!method || method.length > 128) throw RpcError.fromCode(RpcErrorCodes.INVALID_METHOD); - const request = value && typeof value === 'object' ? value?.data : undefined; - this.caller.notification(method, request, ctx).catch((error: unknown) => {}); - } -} diff --git a/src/reactive-rpc/common/rpc/RpcMessageStreamProcessorLocalClient.ts b/src/reactive-rpc/common/rpc/RpcMessageStreamProcessorLocalClient.ts deleted file mode 100644 index cd620a10fe..0000000000 --- a/src/reactive-rpc/common/rpc/RpcMessageStreamProcessorLocalClient.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as msg from '../messages'; -import {StreamingRpcClient} from './client/StreamingRpcClient'; -import {RpcMessageStreamProcessor} from './RpcMessageStreamProcessor'; - -export interface RpcMessageStreamProcessorLocalClientParams { - ctx: Ctx; - server: RpcMessageStreamProcessor; -} - -/** - * Implementation or Reactive-RPC client that locally wraps around the server - * and provides imperative `.call()` and `.call$()` methods, which can be used - * for testing. - */ -export class RpcMessageStreamProcessorLocalClient extends StreamingRpcClient { - constructor(protected readonly params: RpcMessageStreamProcessorLocalClientParams) { - super({ - send: (messages: msg.ReactiveRpcClientMessage[]) => { - Promise.resolve().then(() => { - this.params.server.onMessages(messages, this.params.ctx); - }); - }, - bufferSize: 1, - bufferTime: 0, - }); - - this.params.server.onSend = (messages: unknown) => { - Promise.resolve().then(() => { - this.onMessages(messages as msg.ReactiveRpcServerMessage[]); - }); - }; - } -} diff --git a/src/reactive-rpc/common/rpc/RpcPersistentClient.ts b/src/reactive-rpc/common/rpc/RpcPersistentClient.ts deleted file mode 100644 index 1ad960ae5e..0000000000 --- a/src/reactive-rpc/common/rpc/RpcPersistentClient.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as msg from '../messages'; -import {firstValueFrom, Observable, ReplaySubject, timer} from 'rxjs'; -import {filter, first, share, switchMap, takeUntil} from 'rxjs/operators'; -import {StreamingRpcClient, StreamingRpcClientOptions} from './client/StreamingRpcClient'; -import {PersistentChannel, PersistentChannelParams} from '../channel'; -import type {RpcCodec} from '../codec/RpcCodec'; - -export interface RpcPersistentClientParams { - channel: PersistentChannelParams; - codec: RpcCodec; - client?: Omit; - - /** - * Number of milliseconds to periodically send keep-alive ".ping" notification - * messages. If not specified, will default to 15,000 (15 seconds). If 0, will - * not send ping messages. - */ - ping?: number; - - /** - * The notification method name that is used for ping keep-alive messages, if - * not specified, defaults to ".ping". - */ - pingMethod?: string; -} - -/** - * RPC client which automatically reconnects if disconnected. - */ -export class RpcPersistentClient { - public channel: PersistentChannel; - public rpc?: StreamingRpcClient; - public readonly rpc$ = new ReplaySubject(1); - - constructor(params: RpcPersistentClientParams) { - const ping = params.ping ?? 15000; - const codec = params.codec; - const textEncoder = new TextEncoder(); - this.channel = new PersistentChannel(params.channel); - this.channel.open$.pipe(filter((open) => open)).subscribe(() => { - const close$ = this.channel.open$.pipe(filter((open) => !open)); - const client = new StreamingRpcClient({ - ...(params.client || {}), - send: (messages: msg.ReactiveRpcClientMessage[]): void => { - const encoded = codec.encode(messages, codec.req); - this.channel.send$(encoded).subscribe(); - }, - }); - - this.channel.message$.pipe(takeUntil(close$)).subscribe((data) => { - const encoded = typeof data === 'string' ? textEncoder.encode(data) : new Uint8Array(data); - const messages = codec.decode(encoded, codec.res); - client.onMessages((messages instanceof Array ? messages : [messages]) as msg.ReactiveRpcServerMessage[]); - }); - - // Send ping notifications to keep the connection alive. - if (ping) { - timer(ping, ping) - .pipe(takeUntil(close$)) - .subscribe(() => { - client.notify(params.pingMethod || '.ping', undefined); - }); - } - - if (this.rpc) this.rpc.disconnect(); - this.rpc = client; - this.rpc$.next(client); - }); - } - - public call$(method: string, data: unknown | Observable): Observable { - return this.rpc$.pipe( - first(), - switchMap((rpc) => rpc.call$(method, data as any)), - share(), - ); - } - - public call(method: string, data: unknown): Promise { - return firstValueFrom(this.call$(method, data)); - } - - public notify(method: string, data: unknown): void { - this.rpc$.subscribe((rpc) => rpc.notify(method, data)); - } - - public start() { - this.channel.start(); - } - - public stop() { - this.channel.stop(); - if (this.rpc) this.rpc.stop(); - } -} diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcDuplex.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcDuplex.spec.ts deleted file mode 100644 index 31dd27b382..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcDuplex.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {RpcMessageStreamProcessor} from '../RpcMessageStreamProcessor'; -import {StreamingRpcClient} from '../client/StreamingRpcClient'; -import {RpcDuplex} from '../RpcDuplex'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; -import {sampleApi} from './sample-api'; -import {ApiTestSetup, runApiTests} from './runApiTests'; - -const setup = () => { - const server = new RpcDuplex({ - client: new StreamingRpcClient({ - send: (messages) => { - setTimeout(() => { - client.onMessages(messages, {ip: '127.0.0.1'}); - }, 1); - }, - bufferSize: 2, - bufferTime: 1, - }), - server: new RpcMessageStreamProcessor({ - send: (messages: any) => { - setTimeout(() => { - client.onMessages(messages, {ip: '127.0.0.1'}); - }, 1); - }, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - bufferSize: 2, - bufferTime: 1, - }), - }); - const client = new RpcDuplex({ - client: new StreamingRpcClient({ - send: (messages) => { - setTimeout(() => { - server.onMessages(messages, {ip: '127.0.0.1'}); - }, 1); - }, - bufferSize: 2, - bufferTime: 1, - }), - server: new RpcMessageStreamProcessor({ - send: (messages: any) => { - setTimeout(() => { - server.onMessages(messages, {ip: '127.0.0.1'}); - }, 1); - }, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - bufferSize: 2, - bufferTime: 1, - }), - }); - return {server, client}; -}; - -const setup1: ApiTestSetup = () => ({client: setup().client}); -const setup2: ApiTestSetup = () => ({client: setup().server}); - -describe('duplex 1', () => { - runApiTests(setup1); -}); - -describe('duplex 2', () => { - runApiTests(setup2); -}); diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor-api.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor-api.spec.ts deleted file mode 100644 index 03726c36a1..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor-api.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {Subject} from 'rxjs'; -import {RequestCompleteMessage, ResponseCompleteMessage} from '../../messages'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; -import {RpcValue} from '../../messages/Value'; -import {RpcMessageStreamProcessor, RpcMessageStreamProcessorFromApiOptions} from '../RpcMessageStreamProcessor'; -import {sampleApi} from './sample-api'; - -const setup = (params: Partial = {}) => { - const send = jest.fn(); - const subject = new Subject(); - const ctx = {ip: '127.0.0.1'}; - const server = new RpcMessageStreamProcessor({ - send, - bufferTime: 0, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - ...params, - }); - return {server, send, ctx, subject}; -}; - -test('can create server', async () => { - setup(); -}); - -test('can execute static RPC method', async () => { - const {server, send} = setup(); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(4, 'ping', new RpcValue({}, undefined)), {}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - const value: RpcValue = send.mock.calls[0][0][0].value; - expect(value.data).toBe('pong'); -}); diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor.spec.ts deleted file mode 100644 index b06de3c4b8..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor.spec.ts +++ /dev/null @@ -1,1027 +0,0 @@ -import {RpcMessageStreamProcessor, RpcMessageStreamProcessorOptions} from '../RpcMessageStreamProcessor'; -import {RpcError, RpcErrorCodes} from '../caller'; -import {of, from, Subject, Observable, Subscriber} from 'rxjs'; -import {map, switchMap, take} from 'rxjs/operators'; -import {ApiRpcCaller, ApiRpcCallerOptions} from '../caller/ApiRpcCaller'; -import { - NotificationMessage, - RequestCompleteMessage, - RequestDataMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, - ResponseUnsubscribeMessage, -} from '../../messages'; -import {until} from '../../../../__tests__/util'; -import {RpcValue} from '../../messages/Value'; -import {t} from '../../../../json-type'; -import {Defer} from '../../../../util/Defer'; - -const setup = ( - params: Partial = {}, - callerParams: Partial> = {}, -) => { - const send = jest.fn(); - const subject = new Subject(); - let token: string = ''; - const ctx = {ip: '127.0.0.1'}; - const caller = new ApiRpcCaller({ - api: { - setToken: { - isStreaming: false, - call: (tkn: string) => { - token = tkn; - }, - res: t.Const(undefined), - }, - // test: { - // isStreaming: false, - // call: () => {}, - // res: t.Const(undefined), - // }, - ping: { - isStreaming: false, - call: async () => 'pong', - }, - promise: { - isStreaming: false, - call: async () => 'this is promise result', - }, - promiseError: { - isStreaming: false, - call: async () => { - // tslint:disable-next-line:no-string-throw - throw RpcError.internal(null, 'this promise can throw'); - }, - }, - promiseDelay: { - isStreaming: false, - call: async () => { - await new Promise((r) => setTimeout(r, 5)); - return {}; - }, - }, - error: { - isStreaming: false, - call: async () => { - throw RpcError.internal(null, 'this promise can throw'); - }, - }, - emitOnceSync: { - isStreaming: true, - call$: (request$: any, ctx: any) => { - const obs = request$.pipe( - take(1), - switchMap((request) => { - return of(JSON.stringify({request, ctx})); - }), - ); - return obs; - }, - }, - emitThreeSync: { - isStreaming: true, - call$: (request$: any) => { - const obs = request$.pipe( - take(1), - switchMap((request) => from([new Uint8Array([1]), new Uint8Array([2]), new Uint8Array([3])])), - ); - return obs; - }, - }, - subject: { - isStreaming: true, - call$: (request$: any) => { - return subject; - }, - }, - echo: { - isStreaming: false, - call: (payload: any) => { - return Promise.resolve(payload); - }, - }, - double: { - isStreaming: true, - call$: (request$: any) => - request$.pipe( - take(1), - switchMap((value: any) => of(2 * value)), - ), - }, - getToken: { - isStreaming: false, - call: async () => token, - }, - streamDelay: { - isStreaming: true, - call$: () => - from( - (async () => { - await new Promise((r) => setTimeout(r, 5)); - return {}; - })(), - ), - }, - }, - ...callerParams, - }); - const server = new RpcMessageStreamProcessor({ - send, - caller, - bufferTime: 0, - ...params, - }); - return {server, send, caller, ctx, subject}; -}; - -const val = (value: T) => new RpcValue(value, undefined); - -test('can create server', async () => { - setup(); -}); - -test('does not execute any methods on initialization', async () => { - const {send} = setup(); - expect(send).toHaveBeenCalledTimes(0); -}); - -test('if RPC method throws, sends back error message', async () => { - const {server, send, caller, ctx} = setup(); - jest.spyOn(caller, 'call'); - expect(send).toHaveBeenCalledTimes(0); - const message = new RequestCompleteMessage(1, 'error', val(new Uint8Array([2]))); - server.onMessages([message], undefined); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('this promise can throw'); -}); - -test('sends complete message if observable immediately completes after emitting one value', async () => { - const {server, send, caller, ctx} = setup(); - jest.spyOn(caller, 'createCall'); - server.onMessages([new RequestCompleteMessage(25, 'emitOnceSync', val({foo: 'bar'}))], ctx); - await until(() => (caller.createCall as any).mock.calls.length === 1); - expect(caller.createCall).toHaveBeenCalledTimes(1); - await until(() => send.mock.calls.length === 1); - expect(send).toHaveBeenCalledTimes(1); - const msg = send.mock.calls[0][0][0]; - expect(msg).toBeInstanceOf(ResponseCompleteMessage); - expect(msg.id).toBe(25); - expect(msg.value.data).toEqual( - JSON.stringify({ - request: {foo: 'bar'}, - ctx: {ip: '127.0.0.1'}, - }), - ); -}); - -test('observable emits three values synchronously', async () => { - const {server, send, caller} = setup(); - jest.spyOn(caller, 'createCall'); - server.onMessages([new RequestCompleteMessage(123, 'emitThreeSync', val(new Uint8Array([0])))], undefined); - await new Promise((r) => setTimeout(r, 1)); - expect(caller.createCall).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledTimes(3); - expect(send.mock.calls[0][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([1])))]); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[1][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([2])))]); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[2][0]).toEqual([new ResponseCompleteMessage(123, val(new Uint8Array([3])))]); - expect(send.mock.calls[2][0][0]).toBeInstanceOf(ResponseCompleteMessage); -}); - -test('when observable completes asynchronously, sends empty complete message', async () => { - const {server, send, caller, subject} = setup(); - jest.spyOn(caller, 'createCall'); - server.onMessages([new RequestCompleteMessage(123, 'subject', val(new Uint8Array([0])))], undefined); - subject.next(new Uint8Array([1])); - subject.next(new Uint8Array([2])); - subject.next(new Uint8Array([3])); - await new Promise((r) => setTimeout(r, 1)); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(caller.createCall).toHaveBeenCalledTimes(1); - await until(() => send.mock.calls.length === 4); - expect(send).toHaveBeenCalledTimes(4); - expect(send.mock.calls[0][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([1])))]); - expect(send.mock.calls[1][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([2])))]); - expect(send.mock.calls[2][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([3])))]); - expect(send.mock.calls[3][0]).toEqual([new ResponseCompleteMessage(123, undefined)]); -}); - -test('when observable completes asynchronously and emits asynchronously, sends empty complete message', async () => { - const {server, send, caller, subject} = setup(); - jest.spyOn(caller, 'createCall'); - server.onMessages([new RequestCompleteMessage(123, 'subject', val(new Uint8Array([0])))], undefined); - subject.next(new Uint8Array([1])); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - subject.next(new Uint8Array([2])); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); - subject.next(new Uint8Array([3])); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(3); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(caller.createCall).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledTimes(4); - expect(send.mock.calls[0][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([1])))]); - expect(send.mock.calls[1][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([2])))]); - expect(send.mock.calls[2][0]).toEqual([new ResponseDataMessage(123, val(new Uint8Array([3])))]); - expect(send.mock.calls[3][0]).toEqual([new ResponseCompleteMessage(123, undefined)]); -}); - -test('call can return a promise', async () => { - const {server, send, caller} = setup(); - jest.spyOn(caller, 'call'); - expect(caller.call).toHaveBeenCalledTimes(0); - server.onMessages([new RequestCompleteMessage(3, 'promise', val(new Uint8Array([1, 2, 3])))], {}); - expect(caller.call).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0]).toEqual([new ResponseCompleteMessage(3, val('this is promise result'))]); -}); - -test('sends error message if promise throws', async () => { - const {server, send, caller} = setup(); - jest.spyOn(caller, 'call'); - expect(caller.call).toHaveBeenCalledTimes(0); - server.onMessages([new RequestCompleteMessage(3, 'promiseError', val(new Uint8Array([1, 2, 3])))], {}); - expect(caller.call).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0].value.data.message).toBe('this promise can throw'); -}); - -test('can add authentication on as higher level API', async () => { - const {server, send} = setup(); - server.onMessage(new NotificationMessage('setToken', val('1234')), {}); - server.onMessage(new RequestCompleteMessage(1, 'getToken', val({})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0]).toEqual([new ResponseCompleteMessage(1, val('1234'))]); -}); - -test('stops sending messages after server stop()', async () => { - let sub: Subscriber; - const {server, send} = setup( - {}, - { - api: { - foo: { - isStreaming: true, - call$: () => { - return new Observable((subscriber) => { - sub = subscriber; - }); - }, - }, - }, - }, - ); - expect(!!sub!).toBe(false); - server.onMessage(new RequestCompleteMessage(1, 'foo', val({})), undefined); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - expect(!!sub!).toBe(true); - sub!.next(1); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - server.stop(); - sub!.next(2); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - server.onMessage(new RequestCompleteMessage(1, 'foo', val(undefined)), undefined); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); -}); - -test('does not send subscription complete message from server when client cancels subscription', async () => { - const subject = new Subject(); - const {server, send} = setup( - {}, - { - api: { - foo: { - isStreaming: true, - call$: () => subject, - }, - }, - }, - ); - server.onMessage(new RequestDataMessage(1, 'foo', val({})), {}); - expect(send).toHaveBeenCalledTimes(0); - subject.next(1); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - subject.next(2); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); - server.onMessage(new ResponseUnsubscribeMessage(1), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); -}); - -test.todo('can subscribe to streaming request twice'); - -describe('validation', () => { - test('successfully validates a static call', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: false, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call: async (req: any) => (req as {num: number}).num * 2, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({num: 3})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - }); - - test('returns error on invalid input', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: false, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call: async (req: any) => (req as {num: number}).num * 2, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({gg: 3})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - }); - - describe('for streaming method', () => { - test('successfully validates a streaming call', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => req$.pipe(map((req) => (req as {num: number}).num * 2)), - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({num: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[0][0][0]).toEqual(new ResponseDataMessage(1, val(10))); - server.onMessage(new RequestDataMessage(1, 'test', val({num: 25})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[1][0][0]).toEqual(new ResponseDataMessage(1, val(50))); - }); - - test('errors stream on first data message invalid', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (ctx: any, req$: any) => req$.pipe(map((req) => (req as {num: number}).num * 2)), - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - - server.onMessage(new RequestDataMessage(1, 'test', val({GG: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('Bad Request'); - }); - - test('errors stream on first data message invalid and is a complete message', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => req$.pipe(map((req) => (req as {num: number}).num * 2)), - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({GG: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('Bad Request'); - }); - - test('errors stream on second data message invalid', async () => { - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => req$.pipe(map((req) => (req as {num: number}).num * 2)), - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({num: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[0][0][0]).toEqual(new ResponseDataMessage(1, val(10))); - - server.onMessage(new RequestDataMessage(1, 'test', val({GG: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[1][0][0].value.data.message).toBe('Bad Request'); - }); - - test('when second data message validation fails errors request$ observable', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => { - req$.subscribe({next, error, complete}); - const subject = new Subject(); - return subject; - }, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({num: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({INVALID: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('Bad Request'); - }); - - test('when first data message validation fails errors response observable and does not start request observable', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => { - req$.subscribe({next, error, complete}); - const subject = new Subject(); - return subject; - }, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({INVALID: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('Bad Request'); - }); - - test('when first RequestCompleteMessage validation fails errors response observable and does not start request observable', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - validate: (req: unknown) => { - if (typeof req !== 'object') throw new Error('Invalid request.'); - if (!req) throw new Error('Invalid request.'); - if (typeof (req as {num: number}).num !== 'number') throw new Error('Invalid request.'); - }, - call$: (req$: any) => { - req$.subscribe({next, error, complete}); - const subject = new Subject(); - return subject; - }, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({INVALID: 5})), {}); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('Bad Request'); - }); - }); -}); - -describe('pre-call checks', () => { - describe('static method', () => { - test('proceeds with call when pre-call checks pass', async () => { - const onPreCall = jest.fn(async (request) => {}); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: false, - onPreCall, - call: async (req: any) => (req as {num: number}).num * 2, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - expect(onPreCall).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({num: 6})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[0][0][0]).toEqual(new ResponseCompleteMessage(1, val(12))); - expect(onPreCall).toHaveBeenCalledTimes(1); - expect(onPreCall).toHaveBeenCalledWith({foo: 'bar'}, {num: 6}); - }); - - test('fails call when pre-call checks fail', async () => { - const onPreCall = jest.fn(async (request) => { - throw RpcError.internal(null, 'fail...'); - }); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: false, - onPreCall, - call: async (req: any) => (req as {num: number}).num * 2, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - expect(onPreCall).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'test', val({num: 6})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toEqual('fail...'); - expect(onPreCall).toHaveBeenCalledTimes(1); - expect(onPreCall).toHaveBeenCalledWith({foo: 'bar'}, {num: 6}); - }); - }); - - describe('streaming method', () => { - test('proceeds with call when pre-call checks pass', async () => { - const onPreCall = jest.fn(async (request) => {}); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - onPreCall, - call$: (req$: any) => from([1, 2]), - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - expect(onPreCall).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: 'b'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseDataMessage); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[0][0][0]).toEqual(new ResponseDataMessage(1, val(1))); - expect(send.mock.calls[1][0][0]).toEqual(new ResponseCompleteMessage(1, val(2))); - expect(onPreCall).toHaveBeenCalledTimes(1); - expect(onPreCall).toHaveBeenCalledWith({foo: 'bar'}, {a: 'b'}); - }); - - describe('request buffer', () => { - test('buffer size is 10 by default', async () => { - const preCallFuture = new Defer(); - const onPreCall = jest.fn(async (request) => { - await preCallFuture.promise; - }); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - onPreCall, - call$: (req$: any) => from([1, 2]), - validate: () => {}, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '1'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '2'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '3'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '4'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '5'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '6'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '7'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '8'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '9'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '10'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '11'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('BUFFER_OVERFLOW'); - }); - - test('buffer size can be set to 5 for the whole server', async () => { - const preCallFuture = new Defer(); - const onPreCall = jest.fn(async (request) => { - await preCallFuture.promise; - }); - const {server, send} = setup( - {}, - { - preCallBufferSize: 5, - api: { - test: { - onPreCall, - isStreaming: true, - call$: (req$: any) => from([1, 2]), - validate: () => {}, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '1'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '2'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '3'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '4'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '5'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '6'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('BUFFER_OVERFLOW'); - }); - - test('buffer size can be set to 5 per method', async () => { - const preCallFuture = new Defer(); - const onPreCall = jest.fn(async (request) => { - await preCallFuture.promise; - }); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - onPreCall, - call$: (req$: any) => from([1, 2]), - validate: () => {}, - preCallBufferSize: 5, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '1'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '2'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '3'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '4'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '5'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '6'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('BUFFER_OVERFLOW'); - }); - - test('when pre-call checks finish just before buffer is full, can receive more request data', async () => { - const preCallFuture = new Defer(); - const response$ = new Subject(); - const onPreCall = jest.fn(async (request) => { - await preCallFuture.promise; - }); - const next = jest.fn(); - const {server, send} = setup( - {}, - { - api: { - test: { - isStreaming: true, - onPreCall, - call$: (req$: any) => { - req$.subscribe({next, error: () => {}}); - return response$; - }, - validate: () => {}, - preCallBufferSize: 5, - }, - }, - }, - ); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '1'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '2'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '3'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '4'})), {foo: 'bar'}); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '5'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - expect(next).toHaveBeenCalledTimes(0); - preCallFuture.resolve(undefined); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(5); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '6'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '7'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - server.onMessage(new RequestDataMessage(1, 'test', val({a: '8'})), {foo: 'bar'}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - response$.next(1); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledTimes(8); - }); - }); - }); -}); - -describe('when server stops', () => { - test('does not emit messages from static calls', async () => { - const {server, send} = setup(); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'promiseDelay', val({})), {}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - server.onMessage(new RequestCompleteMessage(2, 'promiseDelay', val({})), {}); - expect(send).toHaveBeenCalledTimes(1); - server.stop(); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - }); - - test('does not emit messages from streaming calls', async () => { - const {server, send} = setup(); - expect(send).toHaveBeenCalledTimes(0); - server.onMessage(new RequestCompleteMessage(1, 'streamDelay', val({})), {}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - server.onMessage(new RequestCompleteMessage(2, 'streamDelay', val({})), {}); - expect(send).toHaveBeenCalledTimes(1); - server.stop(); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - }); -}); - -describe('buffering', () => { - test('batches messages received within buffering window', async () => { - const {server, send} = setup( - { - bufferTime: 1, - }, - { - api: { - method1: { - isStreaming: false, - call: async () => 123, - }, - method2: { - isStreaming: false, - call: async () => 123, - }, - }, - }, - ); - server.onMessage(new RequestCompleteMessage(1, 'method1', val(Buffer.from('a'))), {ctx: 1}); - server.onMessage(new RequestCompleteMessage(2, 'method2', val(Buffer.from('b'))), {ctx: 2}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0]).toEqual([ - new ResponseCompleteMessage(1, val(123)), - new ResponseCompleteMessage(2, val(123)), - ]); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[0][0][1]).toBeInstanceOf(ResponseCompleteMessage); - }); - - test('batches errors received within buffering window', async () => { - const {server, send} = setup({bufferTime: 1}); - server.onMessage(new RequestCompleteMessage(1, 'not_exist_1', val(Buffer.from('a'))), {ctx: 1}); - server.onMessage(new RequestCompleteMessage(2, 'not_exist_2', val(Buffer.from('b'))), {ctx: 2}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][0].value.data.message).toBe('METHOD_NOT_FOUND'); - expect(send.mock.calls[0][0][1]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][1].value.data.message).toBe('METHOD_NOT_FOUND'); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseErrorMessage); - expect(send.mock.calls[0][0][1]).toBeInstanceOf(ResponseErrorMessage); - }); - - test('does not batch consecutive messages when buffering is disabled', async () => { - const {server, send} = setup( - { - bufferTime: 0, - }, - { - api: { - method1: { - isStreaming: false, - call: async () => 123, - }, - method2: { - isStreaming: false, - call: async () => 123, - }, - }, - }, - ); - server.onMessage(new RequestCompleteMessage(1, 'method1', val(Buffer.from('a'))), {ctx: 1}); - server.onMessage(new RequestCompleteMessage(2, 'method2', val(Buffer.from('b'))), {ctx: 2}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[0][0]).toEqual([new ResponseCompleteMessage(1, val(123))]); - expect(send.mock.calls[1][0]).toEqual([new ResponseCompleteMessage(2, val(123))]); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseCompleteMessage); - }); - - test('does not batch messages when they are far apart', async () => { - const {server, send} = setup( - { - bufferTime: 1, - }, - { - api: { - method1: { - isStreaming: false, - call: async () => 123, - }, - method2: { - isStreaming: false, - call: async () => 123, - }, - }, - }, - ); - server.onMessage(new RequestCompleteMessage(1, 'method1', val(Buffer.from('a'))), {ctx: 1}); - await new Promise((r) => setTimeout(r, 10)); - server.onMessage(new RequestCompleteMessage(2, 'method2', val(Buffer.from('b'))), {ctx: 2}); - await until(() => send.mock.calls.length === 2); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[0][0]).toEqual([new ResponseCompleteMessage(1, val(123))]); - expect(send.mock.calls[1][0]).toEqual([new ResponseCompleteMessage(2, val(123))]); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(ResponseCompleteMessage); - }); - - test('batches and sends out messages when buffer is filled up', async () => { - const {server, send} = setup( - { - bufferTime: 100, - bufferSize: 2, - }, - { - api: { - method1: { - isStreaming: false, - call: async () => 123, - }, - method2: { - isStreaming: false, - call: async () => 123, - }, - }, - }, - ); - server.onMessage(new RequestCompleteMessage(1, 'method1', val(Buffer.from('a'))), {ctx: 1}); - server.onMessage(new RequestCompleteMessage(2, 'method2', val(Buffer.from('b'))), {ctx: 2}); - expect(send).toHaveBeenCalledTimes(0); - await new Promise((r) => setTimeout(r, 10)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0]).toEqual([ - new ResponseCompleteMessage(1, val(123)), - new ResponseCompleteMessage(2, val(123)), - ]); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(ResponseCompleteMessage); - expect(send.mock.calls[0][0][1]).toBeInstanceOf(ResponseCompleteMessage); - }); -}); diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient-api.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient-api.spec.ts deleted file mode 100644 index e36038669a..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient-api.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {RpcMessageStreamProcessor} from '../RpcMessageStreamProcessor'; -import {RpcMessageStreamProcessorLocalClient} from '../RpcMessageStreamProcessorLocalClient'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; -import {runApiTests} from './runApiTests'; -import {sampleApi} from './sample-api'; - -const setup = () => { - const server = new RpcMessageStreamProcessor({ - send: (messages: unknown) => {}, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - bufferSize: 2, - bufferTime: 1, - }); - const client = new RpcMessageStreamProcessorLocalClient({ - ctx: {}, - server, - }); - - return { - server, - client, - }; -}; - -runApiTests(() => ({client: setup().client})); diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient.spec.ts deleted file mode 100644 index a9222925cb..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessorLocalClient.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {RpcMessageStreamProcessor} from '../RpcMessageStreamProcessor'; -import {RpcMessageStreamProcessorLocalClient} from '../RpcMessageStreamProcessorLocalClient'; -import {of} from '../../util/of'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; -import {sampleApi} from './sample-api'; - -const setup = () => { - const server = new RpcMessageStreamProcessor({ - send: (messages: any) => {}, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - bufferSize: 2, - bufferTime: 1, - }); - const client = new RpcMessageStreamProcessorLocalClient({ - ctx: {}, - server, - }); - - return { - server, - client, - }; -}; - -describe('.call() method', () => { - test('can execute a simple "ping" call', async () => { - const {client} = setup(); - const res = await client.call('ping', {}); - expect(res).toBe('pong'); - }); - - test('ฬ€can pass arguments', async () => { - const {client} = setup(); - const res = await client.call('double', {num: 1.2}); - expect(res).toEqual({num: 2.4}); - }); - - test('can return back an error', async () => { - const {client} = setup(); - const [, error1] = await of(client.call('error', {})); - expect(error1.message).toBe('this promise can throw'); - const [, error2] = await of(client.call('streamError', {})); - expect(error2.message).toEqual('Stream always errors'); - }); -}); diff --git a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts b/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts deleted file mode 100644 index 61b97ffd95..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/RpcPersistentClient.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {WebSocketChannel} from '../../channel'; -import {RpcPersistentClient} from '../RpcPersistentClient'; -import {createWebSocketMock} from '../../channel/mock'; -import {RequestCompleteMessage} from '../..'; -import {until} from '../../../../__tests__/util'; -import {RpcValue} from '../../messages/Value'; -import {RpcCodec} from '../../codec/RpcCodec'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RpcMessageCodecs} from '../../codec/RpcMessageCodecs'; - -test('on remote method execution, sends message over WebSocket only once', async () => { - const onSend = jest.fn(); - const Ws = createWebSocketMock({onSend}); - const ws = new Ws(''); - const valueCodecs = new Codecs(new Writer(128)); - const messageCodecs = new RpcMessageCodecs(); - const codec = new RpcCodec(messageCodecs.compact, valueCodecs.cbor, valueCodecs.cbor); - const client = new RpcPersistentClient({ - channel: { - newChannel: () => - new WebSocketChannel({ - newSocket: () => ws, - }), - }, - codec, - }); - client.start(); - setTimeout(() => { - ws._open(); - }, 1); - const observable = client.call$('foo.bar', {foo: 'bar'}); - observable.subscribe(() => {}); - observable.subscribe(() => {}); - observable.subscribe(() => {}); - await until(() => onSend.mock.calls.length === 1); - expect(onSend).toHaveBeenCalledTimes(1); - const message = onSend.mock.calls[0][0]; - const decoded = codec.decode(message, codec.req); - const messageDecoded = decoded[0]; - expect(messageDecoded).toBeInstanceOf(RequestCompleteMessage); - expect(messageDecoded).toMatchObject(new RequestCompleteMessage(1, 'foo.bar', new RpcValue({foo: 'bar'}, undefined))); - client.stop(); -}); diff --git a/src/reactive-rpc/common/rpc/__tests__/api.spec.ts b/src/reactive-rpc/common/rpc/__tests__/api.spec.ts deleted file mode 100644 index da54a1095e..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/api.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {RpcMessageStreamProcessor} from '../RpcMessageStreamProcessor'; -import {StreamingRpcClient} from '../client/StreamingRpcClient'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; -import {sampleApi} from './sample-api'; -import {runApiTests} from './runApiTests'; - -const setup = () => { - const ctx = {ip: '127.0.0.1'}; - const server = new RpcMessageStreamProcessor({ - send: (messages: any) => { - setTimeout(() => { - client.onMessages(messages); - }, 1); - }, - caller: new ApiRpcCaller({ - api: sampleApi, - }), - bufferSize: 2, - bufferTime: 1, - }); - const client = new StreamingRpcClient({ - send: (messages) => { - setTimeout(() => { - server.onMessages(messages, ctx); - }, 1); - }, - bufferSize: 2, - bufferTime: 1, - }); - return {server, client}; -}; - -runApiTests(setup); diff --git a/src/reactive-rpc/common/rpc/__tests__/runApiTests.ts b/src/reactive-rpc/common/rpc/__tests__/runApiTests.ts deleted file mode 100644 index a4e43ac47e..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/runApiTests.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {firstValueFrom, lastValueFrom} from 'rxjs'; -import {StreamingRpcClient} from '../client/StreamingRpcClient'; -import {of} from '../../util/of'; -import {RpcError} from '../caller'; - -export interface ApiTestSetupResult { - client: Pick; -} - -export type ApiTestSetup = () => ApiTestSetupResult | Promise; - -export const runApiTests = (setup: ApiTestSetup, params: {staticOnly?: boolean} = {}) => { - describe('ping', () => { - test('can execute static RPC method', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('ping', {})); - expect(result).toBe('pong'); - await client.stop(); - }, 15_000); - - test('can execute without payload', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('ping', undefined)); - expect(result).toBe('pong'); - await client.stop(); - }); - - test('can execute with unexpected payload', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('ping', 'VERY_UNEXPECTED')); - expect(result).toBe('pong'); - await client.stop(); - }); - }); - - describe('double', () => { - test('can execute simple "double" method', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('double', {num: 1.2})); - expect(result).toEqual({num: 2.4}); - await client.stop(); - }); - - test('can execute two request in parallel', async () => { - const {client} = await setup(); - const promise1 = of(firstValueFrom(client.call$('double', {num: 1}))); - const promise2 = of(firstValueFrom(client.call$('double', {num: 2}))); - const [res1, res2] = await Promise.all([promise1, promise2]); - expect(res1[0]).toEqual({num: 2}); - expect(res2[0]).toEqual({num: 4}); - await client.stop(); - }); - - test('throws error when validation fails', async () => { - const {client} = await setup(); - const [, error] = await of(firstValueFrom(client.call$('double', {num: {}}))); - expect((error as RpcError).code).toBe('BAD_REQUEST'); - expect((error as RpcError).message).toBe('Payload .num field missing.'); - await client.stop(); - }); - }); - - describe('error', () => { - test('throws error on static RPC error', async () => { - const {client} = await setup(); - const [, error] = await of(firstValueFrom(client.call$('error', {}))); - expect(error).toMatchObject({message: 'this promise can throw'}); - await client.stop(); - }); - }); - - describe('streamError', () => { - test('throws error on streaming RPC error', async () => { - const {client} = await setup(); - const [, error] = await of(lastValueFrom(client.call$('streamError', {}))); - expect(error).toMatchObject({message: 'Stream always errors'}); - await client.stop(); - }); - }); - - describe('util.info', () => { - test('can receive one value of stream that ends after emitting one value', async () => { - const {client} = await setup(); - const observable = client.call$('util.info', {}); - const result = await firstValueFrom(observable); - expect(result).toEqual({ - commit: 'AAAAAAAAAAAAAAAAAAA', - sha1: 'BBBBBBBBBBBBBBBBBBB', - }); - await client.stop(); - }); - }); - - describe('doubleStringWithValidation', () => { - test('can execute successfully', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('doubleStringWithValidation', {foo: 'a'})); - expect(result).toEqual({ - bar: 'aa', - }); - await client.stop(); - }); - - test('throws on invalid data', async () => { - const {client} = await setup(); - const [, error] = await of(firstValueFrom(client.call$('doubleStringWithValidation', {foo: 123}))); - expect(error).toMatchObject({ - message: '"foo" property missing.', - }); - await client.stop(); - }); - }); - - // We loop here to check for memory leaks. - for (let i = 0; i < 5; i++) { - describe(`doubleStringWithValidation2, iteration ${i + 1}`, () => { - test('can execute successfully', async () => { - const {client} = await setup(); - const result = await firstValueFrom(client.call$('doubleStringWithValidation2', {foo: 'a'})); - await new Promise((r) => setTimeout(r, 15)); - expect(result).toEqual({ - bar: 'aa', - }); - await client.stop(); - }); - - test('throws on invalid data', async () => { - const {client} = await setup(); - const [, error] = await of(firstValueFrom(client.call$('doubleStringWithValidation2', {foo: 123}))); - expect(error).toMatchObject({ - message: '"foo" property missing.', - }); - await client.stop(); - }); - }); - } -}; diff --git a/src/reactive-rpc/common/rpc/__tests__/sample-api.ts b/src/reactive-rpc/common/rpc/__tests__/sample-api.ts deleted file mode 100644 index 5ed0e797e8..0000000000 --- a/src/reactive-rpc/common/rpc/__tests__/sample-api.ts +++ /dev/null @@ -1,189 +0,0 @@ -import {timer, from, Observable} from 'rxjs'; -import {map, switchMap, take} from 'rxjs/operators'; -import {IStaticRpcMethod, IStreamingRpcMethod} from '../types'; -import {t} from '../../../../json-type'; -import {RpcError} from '../caller'; -import {ApiRpcCaller} from '../caller/ApiRpcCaller'; - -const ping: IStaticRpcMethod = { - isStreaming: false, - call: async () => { - return 'pong'; - }, - res: t.Const('pong'), -}; - -const delay: IStaticRpcMethod = { - isStreaming: false, - call: async ({timeout = 10}: {timeout?: number} = {}) => { - await new Promise((r) => setTimeout(r, timeout)); - return { - done: true, - timeout, - }; - }, -}; - -let value = 0; - -const notificationSetValue: IStaticRpcMethod = { - isStreaming: false, - call: async (params) => { - value = params.value; - }, - res: t.Const(undefined), -}; - -const getValue: IStaticRpcMethod = { - isStreaming: false, - call: async () => { - return {value}; - }, -}; - -const delayStreaming: IStreamingRpcMethod = { - isStreaming: true, - call$: (req$) => { - return req$.pipe( - take(1), - switchMap(({timeout = 10}: {timeout?: number} = {}) => { - return from( - new Promise((r) => { - setTimeout(() => { - r(timeout); - }, timeout); - }), - ); - }), - map((timeout: number) => ({ - done: true, - timeout, - })), - ); - }, -}; - -const double: IStaticRpcMethod = { - isStreaming: false, - validate: (data: {num: number}) => { - if (typeof data !== 'object') throw RpcError.validation('Payload must be object.'); - if (data === null) throw RpcError.validation('Payload cannot be null.'); - if (typeof data.num !== 'number') throw RpcError.validation('Payload .num field missing.'); - }, - call: async ({num}) => ({num: num * 2}), -}; - -const error: IStaticRpcMethod = { - isStreaming: false, - call: async () => { - throw new RpcError('this promise can throw', '', 0, '', undefined, undefined); - }, -}; - -const getUser: IStaticRpcMethod = { - isStreaming: false, - call: async (request) => { - return { - id: request.id, - name: 'Mario Dragi', - tags: ['news', 'cola', 'bcaa'], - }; - }, -}; - -const streamError: IStreamingRpcMethod = { - isStreaming: true, - call$: () => - from( - (async () => { - throw RpcError.internal(null, 'Stream always errors'); - })(), - ), -}; - -const utilTimer: IStreamingRpcMethod = { - isStreaming: true, - call$: () => timer(10, 10), -}; - -const buildinfo: IStreamingRpcMethod = { - isStreaming: true, - call$: () => - from([ - { - commit: 'AAAAAAAAAAAAAAAAAAA', - sha1: 'BBBBBBBBBBBBBBBBBBB', - }, - ]), -}; - -const count: IStreamingRpcMethod = { - isStreaming: true, - call$: (request$) => { - return request$.pipe( - switchMap( - ({count}) => - new Observable((observer) => { - let cnt = 0; - const timer = setInterval(() => { - observer.next(cnt++); - if (cnt >= count) { - observer.complete(); - clearInterval(timer); - } - }, 10); - return () => { - clearInterval(timer); - }; - }), - ), - ); - }, -}; - -const doubleStringWithValidation: IStaticRpcMethod = { - isStreaming: false, - validate: (request: any) => { - if (!request || typeof request !== 'object') throw RpcError.validation('Request must be object.'); - if (typeof request.foo !== 'string') throw RpcError.validation('"foo" property missing.'); - }, - call: async ({foo}) => { - return {bar: foo + foo}; - }, -}; - -const doubleStringWithValidation2: IStreamingRpcMethod = { - isStreaming: true, - validate: (request) => { - if (!request || typeof request !== 'object') throw RpcError.validation('Request must be object.'); - if (typeof request.foo !== 'string') throw RpcError.validation('"foo" property missing.'); - }, - call$: (req$) => { - return req$.pipe(map(({foo}) => ({bar: foo + foo}))); - }, -}; - -const passthroughStream: IStreamingRpcMethod = { - isStreaming: true, - call$: (req$) => req$, -}; - -export const sampleApi = { - ping, - delay, - notificationSetValue, - getValue, - delayStreaming, - double, - count, - error, - streamError, - 'auth.users.get': getUser, - 'util.info': buildinfo, - 'util.timer': utilTimer, - doubleStringWithValidation, - doubleStringWithValidation2, - passthroughStream, -}; - -export const createCaller = () => new ApiRpcCaller({api: sampleApi}); diff --git a/src/reactive-rpc/common/rpc/caller/ApiRpcCaller.ts b/src/reactive-rpc/common/rpc/caller/ApiRpcCaller.ts deleted file mode 100644 index 4fe350ea93..0000000000 --- a/src/reactive-rpc/common/rpc/caller/ApiRpcCaller.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {StaticRpcMethod} from '../methods/StaticRpcMethod'; -import {StreamingRpcMethod} from '../methods/StreamingRpcMethod'; -import {RpcCaller, RpcApiCallerOptions} from './RpcCaller'; -import type {IStaticRpcMethod, IStreamingRpcMethod} from '../types'; -import type {RpcApiMap} from './types'; - -export interface ApiRpcCallerOptions, Ctx = unknown> - extends Omit, 'getMethod'> { - api: Api; -} - -export class ApiRpcCaller< - Api extends RpcApiMap, - Ctx = unknown, - Methods = { - [K in keyof Api]: Api[K] extends IStaticRpcMethod - ? StaticRpcMethod - : Api[K] extends IStreamingRpcMethod - ? StreamingRpcMethod - : never; - }, -> extends RpcCaller { - protected readonly methods = new Map(); - - constructor({api, ...rest}: ApiRpcCallerOptions) { - super({ - ...rest, - getMethod: (name: string) => this.get(name as any) as StaticRpcMethod | StreamingRpcMethod, - }); - for (const key in api) { - const method = api[key]; - this.methods.set(key, (method.isStreaming ? new StreamingRpcMethod(method) : new StaticRpcMethod(method))); - } - } - - protected get(name: K): Methods[K] | undefined { - return this.methods.get(name); - } -} diff --git a/src/reactive-rpc/common/rpc/caller/ObjectValueCaller.ts b/src/reactive-rpc/common/rpc/caller/ObjectValueCaller.ts deleted file mode 100644 index 71ee5674d9..0000000000 --- a/src/reactive-rpc/common/rpc/caller/ObjectValueCaller.ts +++ /dev/null @@ -1,128 +0,0 @@ -import {RpcError} from './error'; -import {RpcCaller, type RpcApiCallerOptions} from './RpcCaller'; -import {type AbstractType, FunctionStreamingType, FunctionType} from '../../../../json-type/type/classes'; -import {StaticRpcMethod, type StaticRpcMethodOptions} from '../methods/StaticRpcMethod'; -import {StreamingRpcMethod, type StreamingRpcMethodOptions} from '../methods/StreamingRpcMethod'; -import { - type ObjectType, - type Schema, - type TypeSystem, - ObjectFieldType, - TypeOf, - SchemaOf, - Type, -} from '../../../../json-type'; -import type {ObjectValue, UnObjectType, UnObjectValue} from '../../../../json-type-value/ObjectValue'; -import type {Value} from '../../../../json-type-value/Value'; -import type {Observable} from 'rxjs'; -import type {RpcValue} from '../../messages/Value'; - -type ObjectFieldToTuple = F extends ObjectFieldType ? [K, V] : never; -type ToObject = T extends [string, unknown][] ? {[K in T[number] as K[0]]: K[1]} : never; -type ObjectFieldsToMap = ToObject<{[K in keyof F]: ObjectFieldToTuple}>; -type ObjectValueToTypeMap = ObjectFieldsToMap>>; - -type MethodReq = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodRes = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodDefinition = - F extends FunctionType - ? StaticRpcMethodOptions, MethodRes> - : F extends FunctionStreamingType - ? StreamingRpcMethodOptions, MethodRes> - : never; - -export interface ObjectValueCallerOptions>, Ctx = unknown> - extends Omit, 'getMethod'> { - router: V; -} - -export class ObjectValueCaller>, Ctx = unknown> extends RpcCaller { - protected readonly router: V; - protected readonly system: TypeSystem; - protected readonly methods = new Map | StreamingRpcMethod>(); - - constructor({router: value, ...rest}: ObjectValueCallerOptions) { - super({ - ...rest, - getMethod: (name) => this.get(name) as any as StaticRpcMethod | StreamingRpcMethod, - }); - this.router = value; - const system = value.type.system; - if (!system) throw new Error('NO_SYSTEM'); - this.system = system; - } - - public get>( - id: K, - ): MethodDefinition[K]> | undefined { - let method = this.methods.get(id as string) as any; - if (method) return method; - const fn = this.router.get(id) as Value; - if (!fn || !(fn.type instanceof FunctionType || fn.type instanceof FunctionStreamingType)) { - return undefined; - } - const fnType = fn.type as FunctionType | FunctionStreamingType; - const {req, res} = fnType; - const call = fn.data; - const validator = fnType.req.validator('object'); - const requestSchema = (fnType.req as AbstractType).getSchema(); - const isRequestVoid = requestSchema.__t === 'const' && requestSchema.value === undefined; - const validate = isRequestVoid - ? () => {} - : (req: unknown) => { - const error: any = validator(req); - if (error) { - const message = error.message + (Array.isArray(error?.path) ? ' Path: /' + error.path.join('/') : ''); - throw RpcError.value(RpcError.validation(message, error)); - } - }; - method = - fnType instanceof FunctionType - ? new StaticRpcMethod({req, res, validate, call}) - : new StreamingRpcMethod({req, res, validate, call$: call}); - this.methods.set(id as string, method as any); - return method; - } - - public async call>( - id: K, - request: MethodReq[K]>, - ctx: Ctx, - ): Promise[K]>>> { - return super.call(id as string, request, ctx) as any; - } - - public async callSimple>( - id: K, - request: MethodReq[K]>, - ctx: Ctx = {} as any, - ): Promise[K]>> { - try { - const res = await this.call(id as string, request, ctx); - return res.data; - } catch (err) { - const error = err as RpcValue; - throw error.data; - } - } - - public call$>( - id: K, - request: Observable[K]>>, - ctx: Ctx, - ): Observable[K]>>> { - return super.call$(id as string, request, ctx) as any; - } -} diff --git a/src/reactive-rpc/common/rpc/caller/RpcCaller.ts b/src/reactive-rpc/common/rpc/caller/RpcCaller.ts deleted file mode 100644 index 22320d4665..0000000000 --- a/src/reactive-rpc/common/rpc/caller/RpcCaller.ts +++ /dev/null @@ -1,230 +0,0 @@ -import {firstValueFrom, from, Observable, Subject} from 'rxjs'; -import {catchError, finalize, first, map, mergeWith, share, switchMap, take, tap} from 'rxjs/operators'; -import {BufferSubject} from '../../../../util/rx/BufferSubject'; -import {RpcError, RpcErrorCodes, RpcErrorValue} from './error'; -import {RpcValue} from '../../messages/Value'; -import {StaticRpcMethod} from '../methods/StaticRpcMethod'; -import type {Call} from './types'; -import type {RpcMethod} from '../types'; -import type {StreamingRpcMethod} from '../methods/StreamingRpcMethod'; - -export interface RpcApiCallerOptions { - getMethod: (name: string) => undefined | StaticRpcMethod | StreamingRpcMethod; - - /** - * When call `request$` is a multi-value observable and request data is coming - * in while pre-call check is still being executed, this property determines - * how many `request$` values to buffer in memory before raising an error - * and stopping the streaming call. Defaults to 10. - */ - preCallBufferSize?: number; - - wrapInternalError?: (error: unknown) => unknown; -} - -const INVALID_REQUEST_ERROR_VALUE = RpcError.value(RpcError.badRequest()); - -const defaultWrapInternalError = (error: unknown) => RpcError.valueFrom(error); - -/** - * Implements methods to call Reactive-RPC methods on the server. - */ -export class RpcCaller { - protected readonly getMethod: RpcApiCallerOptions['getMethod']; - protected readonly preCallBufferSize: number; - protected readonly wrapInternalError: (error: unknown) => unknown; - - constructor({ - getMethod, - preCallBufferSize = 10, - wrapInternalError = defaultWrapInternalError, - }: RpcApiCallerOptions) { - this.getMethod = getMethod; - this.preCallBufferSize = preCallBufferSize; - this.wrapInternalError = wrapInternalError; - } - - public exists(name: string): boolean { - return !!this.getMethod(name); - } - - public getMethodStrict(name: string): StaticRpcMethod | StreamingRpcMethod { - const method = this.getMethod(name); - if (!method) throw RpcError.valueFromCode(RpcErrorCodes.METHOD_NOT_FOUND); - return method; - } - - public info(name: string): Pick { - return this.getMethodStrict(name); - } - - protected validate(method: StaticRpcMethod | StreamingRpcMethod, request: unknown): void { - const validate = method.validate; - if (!validate) return; - try { - const errors = validate(request); - if (errors as any) throw errors; - } catch (error) { - throw this.wrapValidationError(error); - } - } - - protected wrapValidationError(error: unknown): RpcErrorValue { - return RpcError.valueFrom(error, INVALID_REQUEST_ERROR_VALUE); - } - - /** - * "call" executes degenerate version of RPC where both request and response data - * are simple single value. - * - * It is a separate implementation from "call$" for performance and complexity - * reasons. - * - * @param name Method name. - * @param request Request data. - * @param ctx Server context object. - * @returns Response data. - */ - public async call(name: string, request: unknown, ctx: Ctx): Promise> { - const method = this.getMethodStrict(name); - this.validate(method, request); - try { - const preCall = method.onPreCall; - if (preCall) await preCall(ctx, request); - const data = await method.call(request, ctx); - return new RpcValue(data, method.res); - } catch (error) { - throw this.wrapInternalError(error); - } - } - - public async notification(name: string, request: unknown, ctx: Ctx): Promise { - const method = this.getMethodStrict(name); - if (!(method instanceof StaticRpcMethod)) return; - if (!method.acceptsNotifications) return; - this.validate(method, request); - try { - if (method.onPreCall) await method.onPreCall(ctx, request); - await method.call(request, ctx); - } catch (error) { - throw this.wrapInternalError(error); - } - } - - /** - * A call may error in a number of ways: - * - * - [x] Request stream itself emits an error. - * - [x] Any of the request stream values fails validation. - * - [x] Pre-call checks fail. - * - [x] Pre-call request buffer is overflown. - * - [x] Due to inactivity timeout. - */ - public createCall(name: string, ctx: Ctx): Call { - const req$ = new Subject(); - const reqUnsubscribe$ = new Subject(); - try { - // This throws when Reactive-RPC method does not exist. - const method = this.getMethodStrict(name); - - // When Reactive-RPC method is "static". - if (!method.isStreaming) { - const response$: Observable = from( - (async () => { - const request = await firstValueFrom(req$.pipe(first())); - return await this.call(name, request, ctx); - })(), - ); - const res$ = new Subject(); - response$.subscribe(res$); - - // Format errors using custom error formatter. - const $resWithErrorsFormatted = res$.pipe( - catchError((error) => { - throw this.wrapInternalError(error); - }), - ); - - return {req$, reqUnsubscribe$, res$: $resWithErrorsFormatted}; - } - - // Here we are sure the call will be streaming. - const methodStreaming = method; - - // Validate all incoming stream requests. - const requestValidated$ = req$.pipe( - tap((request) => { - this.validate(methodStreaming, request); - }), - ); - - // Buffer incoming requests while pre-call checks are executed. - const bufferSize = methodStreaming.preCallBufferSize || this.preCallBufferSize; - const requestBuffered$ = new BufferSubject(bufferSize); - // requestBuffered$.subscribe({error: () => {}}); - - // Error signal (only emits errors), merged with response stream. - // Used for pre-call buffer overflow and timeout errors. - const error$ = new Subject(); - - // Keep track of buffering errors, such as buffer overflow. - requestBuffered$.subscribe({ - error: (error) => { - error$.error(error); - }, - // complete: () => { error$.complete(); }, - }); - requestValidated$.subscribe(requestBuffered$); - - // Main call execution. - const methodResponseType = method.res; - const result$ = requestBuffered$.pipe( - // First, execute pre-call checks with only the first request. - take(1), - switchMap((request) => { - return methodStreaming.onPreCall ? from(methodStreaming.onPreCall(ctx, request)) : from([0]); - }), - // Execute the actual RPC call and flush request buffer. - switchMap(() => { - Promise.resolve().then(() => { - requestBuffered$.flush(); - }); - return method.call$(requestBuffered$, ctx).pipe( - map((response) => new RpcValue(response, methodResponseType)), - finalize(() => { - error$.complete(); - }), - ); - }), - // Make sure we don't call method implementation more than once. - share(), - // Make sure external errors are captured. - mergeWith(error$), - ); - - // Format errors using custom error formatter. - const $resWithErrorsFormatted = result$.pipe( - finalize(() => { - error$.complete(); - }), - catchError((error) => { - throw RpcError.valueFrom(error); - }), - ); - - return {req$, reqUnsubscribe$, res$: $resWithErrorsFormatted}; - } catch (error) { - const errorFormatted = RpcError.valueFrom(error); - req$.error(errorFormatted); - const res$ = new Subject(); - res$.error(errorFormatted); - return {req$, reqUnsubscribe$, res$}; - } - } - - public call$(name: string, request$: Observable, ctx: Ctx): Observable { - const call = this.createCall(name, ctx); - request$.subscribe(call.req$); - return call.res$; - } -} diff --git a/src/reactive-rpc/common/rpc/caller/TypeRouterCaller.ts b/src/reactive-rpc/common/rpc/caller/TypeRouterCaller.ts deleted file mode 100644 index 796131474d..0000000000 --- a/src/reactive-rpc/common/rpc/caller/TypeRouterCaller.ts +++ /dev/null @@ -1,120 +0,0 @@ -import {RpcError, RpcErrorCodes} from './error'; -import {RpcCaller, type RpcApiCallerOptions} from './RpcCaller'; -import {type AbstractType, FunctionStreamingType, FunctionType} from '../../../../json-type/type/classes'; -import {StaticRpcMethod, type StaticRpcMethodOptions} from '../methods/StaticRpcMethod'; -import {StreamingRpcMethod, type StreamingRpcMethodOptions} from '../methods/StreamingRpcMethod'; -import type {Schema, SchemaOf, TypeOf, TypeSystem} from '../../../../json-type'; -import type {TypeRouter} from '../../../../json-type/system/TypeRouter'; -import type {RpcValue} from '../../messages/Value'; -import type {Observable} from 'rxjs'; - -export interface TypedApiCallerOptions, Ctx = unknown> - extends Omit, 'getMethod'> { - router: Router; -} - -export class TypeRouterCaller, Ctx = unknown> extends RpcCaller { - protected readonly router: Router; - protected readonly system: TypeSystem; - protected readonly methods = new Map | StreamingRpcMethod>(); - - constructor({router, ...rest}: TypedApiCallerOptions) { - super({ - ...rest, - getMethod: (name) => this.get(name) as any as StaticRpcMethod | StreamingRpcMethod, - }); - this.router = router; - this.system = router.system; - } - - public readonly req: {[K in keyof Routes]: MethodReq[K]>} = null as any; - public readonly res: {[K in keyof Routes]: MethodRes[K]>} = null as any; - - public get>(id: K): MethodDefinition[K]> | undefined { - let method = this.methods.get(id as string) as any; - if (method) return method; - const fn = this.router.routes[id as string]; - if (!fn || !(fn instanceof FunctionType || fn instanceof FunctionStreamingType)) return undefined; - const validator = fn.req.validator('object'); - const requestSchema = (fn.req as AbstractType).getSchema(); - const isRequestVoid = requestSchema.__t === 'const' && requestSchema.value === undefined; - const validate = isRequestVoid - ? () => {} - : (req: unknown) => { - const error = validator(req); - if (error) { - const message = error.message + (Array.isArray(error?.path) ? ' Path: /' + error.path.join('/') : ''); - throw RpcError.value(RpcError.validation(message, error)); - } - }; - method = - fn instanceof FunctionType - ? new StaticRpcMethod({ - req: fn.req, - res: fn.res, - validate, - call: fn.singleton as any, - }) - : new StreamingRpcMethod({ - req: fn.req, - res: fn.res, - validate, - call$: fn.singleton as any, - }); - this.methods.set(id as string, method as any); - return method; - } - - public async call>( - id: K, - request: MethodReq[K]>, - ctx: Ctx, - ): Promise[K]>>> { - return super.call(id as string, request, ctx) as any; - } - - public async callSimple>( - id: K, - request: MethodReq[K]>, - ctx: Ctx = {} as any, - ): Promise[K]>> { - try { - const res = await this.call(id as string, request, ctx); - return res.data; - } catch (err) { - const error = err as RpcValue; - throw error.data; - } - } - - public call$>( - id: K, - request: Observable[K]>>, - ctx: Ctx, - ): Observable[K]>>> { - return super.call$(id as string, request, ctx) as any; - } -} - -type Routes = Router extends TypeRouter ? R : never; - -type MethodReq = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodRes = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodDefinition = - F extends FunctionType - ? StaticRpcMethodOptions, MethodRes> - : F extends FunctionStreamingType - ? StreamingRpcMethodOptions, MethodRes> - : never; diff --git a/src/reactive-rpc/common/rpc/caller/TypedApiCaller.ts b/src/reactive-rpc/common/rpc/caller/TypedApiCaller.ts deleted file mode 100644 index ae33cef99e..0000000000 --- a/src/reactive-rpc/common/rpc/caller/TypedApiCaller.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {RpcError, RpcErrorCodes} from './error'; -import {RpcCaller, RpcApiCallerOptions} from './RpcCaller'; -import {FunctionStreamingType, FunctionType} from '../../../../json-type/type/classes'; -import {StaticRpcMethod, type StaticRpcMethodOptions} from '../methods/StaticRpcMethod'; -import {StreamingRpcMethod, type StreamingRpcMethodOptions} from '../methods/StreamingRpcMethod'; -import type {SchemaOf, TypeMap, TypeOf, TypeSystem} from '../../../../json-type'; - -export interface TypedApiCallerOptions extends Omit, 'getMethod'> { - system: TypeSystem; -} - -type MethodReq = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodRes = - F extends FunctionType - ? TypeOf> - : F extends FunctionStreamingType - ? TypeOf> - : never; - -type MethodDefinition = - F extends FunctionType - ? StaticRpcMethodOptions, MethodRes> - : F extends FunctionStreamingType - ? StreamingRpcMethodOptions, MethodRes> - : never; - -export class TypedApiCaller extends RpcCaller { - protected readonly system: TypeSystem; - protected readonly methods = new Map | StreamingRpcMethod>(); - - constructor({system, ...rest}: TypedApiCallerOptions) { - super({ - ...rest, - getMethod: (name) => this.get(name) as any as StaticRpcMethod | StreamingRpcMethod, - }); - this.system = system; - } - - public readonly req: {[K in keyof Types]: MethodReq} = null as any; - public readonly res: {[K in keyof Types]: MethodRes} = null as any; - - public implement(id: K, definition_: MethodDefinition): void { - const definition = definition_ as any; - if (this.methods.has(id as string)) throw new Error(`Method [id = ${id as string}] is already implemented.`); - const alias = this.system.resolve(id as string); - const type = alias.type as FunctionType | FunctionStreamingType; - if (!(type instanceof FunctionType || type instanceof FunctionStreamingType)) - throw new Error(`Type [alias = ${alias.id}] is not a function.`); - const validator = type.validator('boolean'); - const customValidator = definition.validate; - const validate = customValidator - ? (req: unknown) => { - const error = validator(req); - if (error) throw RpcError.valueFromCode(RpcErrorCodes.BAD_REQUEST); - customValidator(req); - } - : (req: unknown) => { - const error = validator(req); - if (error) throw RpcError.valueFromCode(RpcErrorCodes.BAD_REQUEST); - }; - const isStaticMethodAlias = alias.type instanceof FunctionType; - const isStreamingMethodAlias = alias.type instanceof FunctionStreamingType; - const method = isStaticMethodAlias - ? new StaticRpcMethod({ - ...(definition as StaticRpcMethodOptions), - req: type.req, - res: type.res, - validate, - }) - : isStreamingMethodAlias - ? new StreamingRpcMethod({ - ...(definition as StreamingRpcMethodOptions), - req: type.req, - res: type.res, - validate, - }) - : null; - if (!method) throw new Error(`Type [alias = ${alias.id}] is not a function.`); - this.methods.set(id as string, method as any); - } - - public get(id: K): MethodDefinition { - const method = this.methods.get(id as string) as any; - if (!method) throw RpcError.valueFromCode(RpcErrorCodes.METHOD_NOT_FOUND); - return method; - } -} diff --git a/src/reactive-rpc/common/rpc/caller/__tests__/ObjectValueCaller.spec.ts b/src/reactive-rpc/common/rpc/caller/__tests__/ObjectValueCaller.spec.ts deleted file mode 100644 index 65b95cb1e4..0000000000 --- a/src/reactive-rpc/common/rpc/caller/__tests__/ObjectValueCaller.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {TypeSystem} from '../../../../../json-type'; -import {ObjectValue} from '../../../../../json-type-value/ObjectValue'; -import {ObjectValueCaller} from '../ObjectValueCaller'; - -test('can execute simple calls', async () => { - const system = new TypeSystem(); - const {t} = system; - const router = ObjectValue.create(system) - .prop('ping', t.Function(t.any, t.Const('pong')), async () => 'pong') - .prop('echo', t.Function(t.any, t.any), async (req) => req); - const caller = new ObjectValueCaller({router}); - const res1 = await caller.call('ping', null, {}); - expect(res1.data).toBe('pong'); - const res2 = await caller.callSimple('echo', {foo: 'bar'}, {}); - expect(res2).toEqual({foo: 'bar'}); -}); diff --git a/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.polymorphic-results.spec.ts b/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.polymorphic-results.spec.ts deleted file mode 100644 index 42fe9b361e..0000000000 --- a/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.polymorphic-results.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {ApiRpcCaller} from '../ApiRpcCaller'; -import {firstValueFrom, Observable, of} from 'rxjs'; -import {map} from 'rxjs/operators'; -import {StreamingRpcMethod} from '../../methods/StreamingRpcMethod'; -import {StaticRpcMethod} from '../../methods/StaticRpcMethod'; - -describe('static calls', () => { - describe('can execute call using different serializations', () => { - test('when implementation is not serialized', async () => { - let cnt = 0; - const caller = new ApiRpcCaller({ - api: { - add: new StaticRpcMethod({ - call: async ([a, b]: any) => { - cnt++; - return `${a} + ${b} = ${a + b}`; - }, - }), - }, - }); - const res1 = await caller.call('add', [1, 2], {}); - const res4 = await firstValueFrom(caller.call$('add', of([1, 2]), {})); - expect(cnt).toBe(2); - expect(res1.data).toBe('1 + 2 = 3'); - expect(res4.data).toBe('1 + 2 = 3'); - }); - }); -}); - -describe('streaming calls', () => { - describe('can execute call using different serializations', () => { - test('when implementation is not serialized', async () => { - let cnt = 0; - const caller = new ApiRpcCaller({ - api: { - add: new StreamingRpcMethod({ - call$: (req$: Observable<[number, number]>) => { - cnt++; - return req$.pipe( - map(([a, b]) => { - return `${a} + ${b} = ${a + b}`; - }), - ); - }, - }), - }, - }); - const res1 = await caller.call('add', [1, 2], {}); - const res4 = await firstValueFrom(caller.call$('add', of([1, 2]), {})); - expect(cnt).toBe(2); - expect(res1.data).toBe('1 + 2 = 3'); - expect(res4.data).toBe('1 + 2 = 3'); - }); - }); -}); diff --git a/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.spec.ts b/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.spec.ts deleted file mode 100644 index 35dc828ff7..0000000000 --- a/src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as Rx from 'rxjs'; -import {catchError, skip} from 'rxjs/operators'; -import {ApiRpcCaller} from '../ApiRpcCaller'; -import {runApiTests} from '../../__tests__/runApiTests'; -import {sampleApi} from '../../__tests__/sample-api'; -import {of} from '../../../util/of'; -import {StreamingRpcMethod} from '../../methods/StreamingRpcMethod'; -import {RpcError} from '../error'; - -const setup = () => { - const caller = new ApiRpcCaller({ - api: sampleApi, - }); - return {caller}; -}; - -test('can instantiate', () => { - const caller = new ApiRpcCaller({ - api: {}, - }); -}); - -describe('static calls', () => { - test('can execute "ping"', async () => { - const {caller} = setup(); - const res = await caller.call('ping', undefined, {}); - expect(res.data).toBe('pong'); - }); - - test('can execute "double"', async () => { - const {caller} = setup(); - const res = (await caller.call('double', {num: 5}, {})) as any; - expect(res.data.num).toBe(10); - }); - - test('wraps error into RpcError', async () => { - const caller = new ApiRpcCaller({ - api: { - test: { - isStreaming: false, - call: async () => { - // tslint:disable-next-line:no-string-throw - throw 'lol'; - }, - }, - }, - }); - const [, error] = await of(caller.call('test', {}, {})); - expect(error).toEqual(RpcError.internalErrorValue(null)); - }); -}); - -describe('notifications', () => { - test('can execute a notification to set a value', async () => { - const {caller} = setup(); - await caller.notification('notificationSetValue', {value: 123}, {}); - const val1 = await caller.call('getValue', undefined, {}); - expect((val1.data as any).value).toBe(123); - await caller.notification('notificationSetValue', {value: 456}, {}); - const val2 = await caller.call('getValue', undefined, {}); - expect((val2.data as any).value).toBe(456); - }); -}); - -describe('streaming calls', () => { - test('can execute "ping"', async () => { - const {caller} = setup(); - const res = await Rx.firstValueFrom(caller.call$('ping', Rx.of(undefined), {})); - expect(res.data).toBe('pong'); - }); - - test('can execute "double"', async () => { - const {caller} = setup(); - const res = (await Rx.firstValueFrom(caller.call$('double', Rx.of({num: 5}), {}))) as any; - expect(res.data.num).toBe(10); - }); - - test('can execute "timer"', async () => { - const {caller} = setup(); - const res = await Rx.firstValueFrom(caller.call$('util.timer', Rx.of(undefined), {}).pipe(skip(2))); - expect(res.data).toBe(2); - }); - - test('wraps errors into internal RpcError values', async () => { - const caller = new ApiRpcCaller({ - api: { - test: new StreamingRpcMethod({ - call$: () => { - const subject = new Rx.Subject(); - subject.error('lol'); - return subject; - }, - }), - }, - }); - - const [, error1] = await of(caller.call('test', {}, {})); - expect(error1).toEqual(RpcError.internalErrorValue(null)); - - const [, error2] = await of(Rx.firstValueFrom(caller.call$('test', Rx.of(undefined), {}))); - expect(error2).toEqual(RpcError.internalErrorValue(null)); - }); -}); - -describe('smoke tests', () => { - runApiTests(() => { - const {caller} = setup(); - return { - client: { - call$: (name: any, request: any) => - caller.call$(name, Rx.isObservable(request) ? request : Rx.of(request), {}).pipe( - Rx.map((value) => value.data), - catchError((error) => { - throw error.data; - }), - ), - stop: () => {}, - }, - }; - }); -}); diff --git a/src/reactive-rpc/common/rpc/caller/error/RpcError.ts b/src/reactive-rpc/common/rpc/caller/error/RpcError.ts deleted file mode 100644 index 3941d4e3ad..0000000000 --- a/src/reactive-rpc/common/rpc/caller/error/RpcError.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {RpcValue} from '../../../messages/Value'; -import {IRpcError, RpcErrorType} from './RpcErrorType'; - -export enum RpcErrorCodes { - /** Any unknown sever error is wrapped into INTERNAL_ERROR, error 500. */ - INTERNAL_ERROR, - - /** When request is not valid, e.g. when request validation fails, error 400. */ - BAD_REQUEST, - - /** - * Error thrown when there was no activity on a - * stream for a long time, and timeout was reached. - */ - TIMEOUT, - - /** Resource not found, error 404. */ - NOT_FOUND, - - /** When operation cannot be performed due to a conflict, error 409. */ - CONFLICT, - - ID_TAKEN, - INVALID_METHOD, - INVALID_METHOD_NAME, - NO_METHOD_SPECIFIED, - METHOD_NOT_FOUND, - - STOP, - DISCONNECT, - BUFFER_OVERFLOW, -} - -export type RpcErrorValue = RpcValue; - -export class RpcError extends Error implements IRpcError { - public static from(error: unknown): RpcError { - if (error instanceof RpcError) return error; - return RpcError.internal(error); - } - - public static fromCode( - errno: RpcErrorCodes, - message: string = '', - meta: unknown = undefined, - originalError: unknown = undefined, - ): RpcError { - const code = RpcErrorCodes[errno]; - return new RpcError(message || code, code, errno, undefined, meta || undefined, originalError); - } - - public static internal(originalError: unknown, message: string = 'Internal Server Error'): RpcError { - return RpcError.fromCode(RpcErrorCodes.INTERNAL_ERROR, message, undefined, originalError); - } - - public static badRequest(message = 'Bad Request'): RpcError { - return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, message); - } - - public static validation(message: string, meta?: unknown): RpcError { - return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, message, meta); - } - - public static value(error: RpcError): RpcErrorValue { - return new RpcValue(error, RpcErrorType); - } - - public static valueFrom(error: unknown, def = RpcError.internalErrorValue(error)): RpcErrorValue { - if (error instanceof RpcValue && error.data instanceof RpcError && error.type === RpcErrorType) return error; - if (error instanceof RpcError) return RpcError.value(error); - return def; - } - - public static valueFromCode(errno: RpcErrorCodes, message?: string): RpcErrorValue { - return RpcError.value(RpcError.fromCode(errno, message)); - } - - public static internalErrorValue(originalError: unknown): RpcErrorValue { - return RpcError.value(RpcError.internal(originalError)); - } - - public static isRpcError(error: unknown): error is RpcError { - return error instanceof RpcError; - } - - constructor( - public readonly message: string, - public readonly code: string | undefined, - public readonly errno: number, - public readonly errorId: string | undefined, - public readonly meta: unknown | undefined, - public readonly originalError: unknown | undefined, - ) { - super(message); - if (message === code) this.code = undefined; - Object.setPrototypeOf(this, RpcError.prototype); - } - - public toJson(): IRpcError { - const err: IRpcError = {message: this.message}; - if (this.code) err.code = this.code; - if (this.errno) err.errno = this.errno; - if (this.errorId) err.errorId = this.errorId; - if (this.meta) err.meta = this.meta; - return err; - } -} diff --git a/src/reactive-rpc/common/rpc/caller/error/RpcErrorType.ts b/src/reactive-rpc/common/rpc/caller/error/RpcErrorType.ts deleted file mode 100644 index c1d1911142..0000000000 --- a/src/reactive-rpc/common/rpc/caller/error/RpcErrorType.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {ResolveType, t} from '../../../../../json-type'; - -export const RpcErrorType = t - .Object( - t.prop('message', t.str).options({ - title: 'Error message', - description: 'A human-readable error message.', - }), - t.propOpt('code', t.str.options({ascii: true})).options({ - title: 'Error code', - description: 'A machine-readable fixed error code tag.', - }), - t.propOpt('errno', t.num).options({ - title: 'Error number', - description: 'A machine-readable fixed error code number. Same as "code" but in numeric form.', - }), - t.propOpt('errorId', t.str.options({ascii: true})).options({ - title: 'Error ID', - description: - 'Unique ID of the error as it is stored the system. Can be referenced to later to retrieve from storage.', - }), - t.propOpt('meta', t.any).options({ - title: 'Error metadata', - description: 'Additional extra metadata.', - }), - ) - .options({ - title: 'RPC Error', - encodeUnknownFields: false, - }); - -export type IRpcError = ResolveType; diff --git a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts b/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts deleted file mode 100644 index c9edeebbb2..0000000000 --- a/src/reactive-rpc/common/rpc/caller/error/__tests__/RpcErrorType.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {RpcError} from '../RpcError'; -import {RpcErrorType} from '../RpcErrorType'; - -const codecs = new Codecs(new Writer(16)); - -test('can encode an internal error', () => { - const error = RpcError.internal(null); - const encoded = RpcErrorType.encode(codecs.json, error); - // console.log(RpcErrorType.encoder(EncodingFormat.Json).toString()); - const json = JSON.parse(Buffer.from(encoded).toString()); - expect(json).toEqual({ - message: 'Internal Server Error', - code: 'INTERNAL_ERROR', - errno: 0, - }); -}); diff --git a/src/reactive-rpc/common/rpc/caller/error/index.ts b/src/reactive-rpc/common/rpc/caller/error/index.ts deleted file mode 100644 index 44dadcbd69..0000000000 --- a/src/reactive-rpc/common/rpc/caller/error/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './RpcErrorType'; -export * from './RpcError'; diff --git a/src/reactive-rpc/common/rpc/caller/index.ts b/src/reactive-rpc/common/rpc/caller/index.ts deleted file mode 100644 index 89f8b05688..0000000000 --- a/src/reactive-rpc/common/rpc/caller/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './error'; diff --git a/src/reactive-rpc/common/rpc/caller/types.ts b/src/reactive-rpc/common/rpc/caller/types.ts deleted file mode 100644 index 714be3bc97..0000000000 --- a/src/reactive-rpc/common/rpc/caller/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {Observable, Observer} from 'rxjs'; -import type {IStaticRpcMethod, IStreamingRpcMethod, RpcMethod} from '../methods/types'; -import type {RpcValue} from '../../messages/Value'; - -export type RpcApiMap = { - [name: string]: IStaticRpcMethod | IStreamingRpcMethod; -}; - -/** - * Represents an in-flight call. - */ -export interface Call { - req$: Observer; - reqUnsubscribe$: Observable; - res$: Observable>; -} diff --git a/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts b/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts deleted file mode 100644 index 3f36acae67..0000000000 --- a/src/reactive-rpc/common/rpc/client/EncodedStaticRpcClient.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as msg from '../../messages'; -import {StaticRpcClient} from './StaticRpcClient'; -import {RpcMessageCodec} from '../../codec/types'; -import {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {Observable} from 'rxjs'; -import type {RpcClient} from './types'; - -export interface StaticRpcClientOptions { - send: (buf: Uint8Array) => Promise; - msgCodec: RpcMessageCodec; - reqCodec: JsonValueCodec; - resCodec?: JsonValueCodec; - client: StaticRpcClient; -} - -export class EncodedStaticRpcClient implements RpcClient { - public readonly client: StaticRpcClient; - - constructor({send, msgCodec, reqCodec, resCodec = reqCodec, client}: StaticRpcClientOptions) { - this.client = client; - client.onsend = async (messages) => { - const buf = msgCodec.encode(reqCodec, messages); - const res = await send(buf); - const resultMessages = msgCodec.decodeBatch(resCodec, res); - return resultMessages as msg.ReactiveRpcServerMessage[]; - }; - } - - public call$(method: string, data: unknown | Observable): Observable { - return this.client.call$(method, data); - } - - public async call(method: string, request: unknown): Promise { - return this.call(method, request); - } - - public notify(method: string, data: undefined | unknown): void { - this.notify(method, data); - } -} diff --git a/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts b/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts deleted file mode 100644 index eed94b78fb..0000000000 --- a/src/reactive-rpc/common/rpc/client/FetchRpcClient.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {StaticRpcClient, StaticRpcClientOptions} from './StaticRpcClient'; -import {EncodedStaticRpcClient} from './EncodedStaticRpcClient'; -import type {RpcMessageCodec} from '../../codec/types'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {Observable} from 'rxjs'; -import type {RpcClient} from './types'; - -type IFetch = typeof fetch; - -export interface FetchRpcClientOptions extends StaticRpcClientOptions { - url: string; - msgCodec: RpcMessageCodec; - reqCodec: JsonValueCodec; - resCodec?: JsonValueCodec; - fetch?: IFetch; -} - -/** - * Static method RPC client, which uses `fetch` to send requests. - */ -export class FetchRpcClient implements RpcClient { - public readonly client: EncodedStaticRpcClient; - - constructor(options: FetchRpcClientOptions) { - const {msgCodec, reqCodec, resCodec = reqCodec, url} = options; - let contentType = `application/x.rpc.${msgCodec.id}.${reqCodec.id}`; - if (reqCodec.id !== resCodec.id) contentType += `-${resCodec.id}`; - const myFetch = options.fetch || fetch; - this.client = new EncodedStaticRpcClient({ - client: new StaticRpcClient({ - bufferSize: options.bufferSize, - bufferTime: options.bufferTime, - }), - msgCodec, - reqCodec, - resCodec, - send: async (body) => { - const response = await myFetch(url, { - method: 'POST', - headers: { - 'Content-Type': contentType, - }, - body, - }); - const buffer = await response.arrayBuffer(); - return new Uint8Array(buffer); - }, - }); - } - - public call$(method: string, data: unknown | Observable): Observable { - return this.client.call$(method, data); - } - - public async call(method: string, request: unknown): Promise { - return this.call(method, request); - } - - public notify(method: string, data: undefined | unknown): void { - this.notify(method, data); - } - - public stop() {} -} diff --git a/src/reactive-rpc/common/rpc/client/StaticRpcClient.ts b/src/reactive-rpc/common/rpc/client/StaticRpcClient.ts deleted file mode 100644 index c94b75a9c8..0000000000 --- a/src/reactive-rpc/common/rpc/client/StaticRpcClient.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as msg from '../../messages'; -import {TimedQueue} from '../../util/TimedQueue'; -import {RpcValue} from '../../messages/Value'; -import {Defer} from '../../../../util/Defer'; -import {Observable, of, switchMap} from 'rxjs'; -import type {RpcClient} from './types'; - -/** - * Configuration parameters for {@link StaticRpcClient}. - */ -export interface StaticRpcClientOptions { - /** - * Method to be called by client when it wants to send messages to the server. - * This is usually connected to your WebSocket "send" method. - */ - send?: (messages: msg.ReactiveRpcClientMessage[]) => Promise; - - /** - * Number of messages to keep in buffer before sending them to the server. - * The buffer is flushed when the message reaches this limit or when the - * buffering time has reached the time specified in `bufferTime` parameter. - * Defaults to 100 messages. - */ - bufferSize?: number; - - /** - * Time in milliseconds for how long to buffer messages before sending them - * to the server. Defaults to 10 milliseconds. - */ - bufferTime?: number; -} - -export class StaticRpcClient implements RpcClient { - private id: number = 1; - public readonly buffer: TimedQueue; - public onsend: (messages: msg.ReactiveRpcClientMessage[]) => Promise = async () => { - throw new Error('onsend not implemented'); - }; - - /** - * In-flight RPC calls. - */ - private readonly calls = new Map>(); - - constructor({send, bufferSize = 100, bufferTime = 10}: StaticRpcClientOptions) { - if (send) this.onsend = send; - this.buffer = new TimedQueue(); - this.buffer.itemLimit = bufferSize; - this.buffer.timeLimit = bufferTime; - this.buffer.onFlush = (messages: msg.ReactiveRpcClientMessage[]) => { - this.onsend(messages) - .then((responses: msg.ReactiveRpcServerMessage[]) => { - for (const response of responses) { - const id = response.id; - const calls = this.calls; - const future = calls.get(id); - calls.delete(id); - if (!future) continue; - if (response instanceof msg.ResponseCompleteMessage) future.resolve(response.value?.data); - else if (response instanceof msg.ResponseErrorMessage) future.reject(response.value?.data); - } - }) - .catch((error) => { - for (const message of messages) - if (message instanceof msg.RequestCompleteMessage) { - const id = message.id; - const calls = this.calls; - const future = calls.get(id); - calls.delete(id); - if (!future) continue; - future.reject(error); - } - }) - .finally(() => { - for (const message of messages) - if (message instanceof msg.RequestCompleteMessage) this.calls.delete(message.id); - }); - }; - } - - public call$(method: string, data: unknown | Observable): Observable { - return (data instanceof Observable ? data : of(data)).pipe(switchMap((data) => this.call(method, data))); - } - - public async call(method: string, request: unknown): Promise { - const id = this.id; - this.id = (id + 1) % 0xffff; - const value = new RpcValue(request, undefined); - const message = new msg.RequestCompleteMessage(id, method, value); - const future = new Defer(); - this.calls.set(id, future); - this.buffer.push(message); - return await future.promise; - } - - public notify(method: string, data: undefined | unknown): void { - const value = new RpcValue(data, undefined); - this.buffer.push(new msg.NotificationMessage(method, value)); - } - - public stop() {} -} diff --git a/src/reactive-rpc/common/rpc/client/StreamingRpcClient.ts b/src/reactive-rpc/common/rpc/client/StreamingRpcClient.ts deleted file mode 100644 index 4da9bef955..0000000000 --- a/src/reactive-rpc/common/rpc/client/StreamingRpcClient.ts +++ /dev/null @@ -1,253 +0,0 @@ -import {firstValueFrom, isObservable, Observable, Observer, Subject} from 'rxjs'; -import * as msg from '../../messages'; -import {subscribeCompleteObserver} from '../../util/subscribeCompleteObserver'; -import {TimedQueue} from '../../util/TimedQueue'; -import {RpcValue} from '../../messages/Value'; -import type {RpcClient} from './types'; - -/** - * Configuration parameters for {@link StreamingRpcClient}. - */ -export interface StreamingRpcClientOptions { - /** - * Method to be called by client when it wants to send messages to the server. - * This is usually connected to your WebSocket "send" method. - */ - send: (messages: msg.ReactiveRpcClientMessage[]) => void; - - /** - * Number of messages to keep in buffer before sending them to the server. - * The buffer is flushed when the message reaches this limit or when the - * buffering time has reached the time specified in `bufferTime` parameter. - * Defaults to 100 messages. - */ - bufferSize?: number; - - /** - * Time in milliseconds for how long to buffer messages before sending them - * to the server. Defaults to 10 milliseconds. - */ - bufferTime?: number; -} - -interface ObserverEntry { - /* In-between observable for request stream. */ - req$: Subject; - /* In-between observable for response stream. */ - res$: Subject; - /** Whether response stream was finalized by server. */ - resFinalized?: boolean; -} - -/** - * Implements client-side part of Reactive-RPC protocol. - * - * ## Usage - * - * Connect RPC client to WebSocket: - * - * ```ts - * const client = new RpcClient({ - * send: (messages) => ws.send(serialize(messages)), - * }); - * ws.on('message', (event) => { - * client.onMessages(deserialize(event.data)); - * }); - * ``` - * - * Send notifications to the server: - * - * ```ts - * client.notify(method, payload); - * ``` - * - * Execute RPC methods with streaming support: - * - * ```ts - * client.call(method, data$).subscribe((value) => { - * // ... - * }); - * ``` - */ -export class StreamingRpcClient implements RpcClient { - private id: number = 1; - public readonly buffer: TimedQueue; - - /** - * In-flight RPC calls. - */ - private readonly calls = new Map(); - - constructor({send, bufferSize = 100, bufferTime = 10}: StreamingRpcClientOptions) { - this.buffer = new TimedQueue(); - this.buffer.itemLimit = bufferSize; - this.buffer.timeLimit = bufferTime; - this.buffer.onFlush = send; - } - - /** - * Returns the number of active in-flight calls. Useful for reporting and - * testing for memory leaks in unit tests. - * - * @returns Number of in-flight RPC calls. - */ - public getInflightCallCount(): number { - return this.calls.size; - } - - /** - * Processes a batch of messages received from the server. - * - * @param messages List of messages from server. - */ - public onMessages(messages: msg.ReactiveRpcServerMessage[]): void { - const length = messages.length; - for (let i = 0; i < length; i++) this.onMessage(messages[i]); - } - - /** - * Processes a message received from the server. - * - * @param messages A message from the server. - */ - public onMessage(message: msg.ReactiveRpcServerMessage): void { - if (message instanceof msg.ResponseCompleteMessage) return this.onResponseComplete(message); - else if (message instanceof msg.ResponseDataMessage) return this.onResponseData(message); - else if (message instanceof msg.ResponseErrorMessage) return this.onResponseError(message); - // else if (message instanceof RequestUnsubscribeMessage) return this.onRequestUnsubscribe(message); - return this.onRequestUnsubscribe(message); - } - - public onResponseComplete(message: msg.ResponseCompleteMessage): void { - const {id, value} = message; - const call = this.calls.get(id); - if (!call) return; - call.resFinalized = true; - const data = value ? value.data : undefined; - if (data !== undefined) call.res$.next(data); - call.res$.complete(); - } - - public onResponseData(message: msg.ResponseDataMessage): void { - const {id, value} = message; - const call = this.calls.get(id); - if (!call) return; - call.res$.next(value.data); - } - - public onResponseError(message: msg.ResponseErrorMessage): void { - const {id, value} = message; - const call = this.calls.get(id); - if (!call) return; - call.resFinalized = true; - call.res$.error(value.data); - } - - public onRequestUnsubscribe(message: msg.RequestUnsubscribeMessage): void { - const {id} = message; - const call = this.calls.get(id); - if (!call) return; - call.req$.complete(); - } - - /** - * Execute remote RPC method. We use in-between `req$` and `res$` observables. - * - * ``` - * +--------+ +--------+ - * | data | -> | req$ | -> Server messages - * +--------+ +--------+ - * - * +--------+ +-------------------+ - * Server messages -> | res$ | -> | user observable | - * +--------+ +-------------------+ - * ``` - * - * @param method RPC method name. - * @param data RPC method static payload or stream of data. - */ - public call$(method: string, data: unknown): Observable; - public call$(method: string, data: Observable): Observable; - public call$(method: string, data: unknown | Observable): Observable { - const id = this.id++; - if (this.id >= 0xffff) this.id = 1; - if (this.calls.has(id)) return this.call$(method, data as any); - const req$ = new Subject(); - const res$ = new Subject(); - let finalizedStreams = 0; - const cleanup = () => { - finalizedStreams++; - if (finalizedStreams === 2) this.calls.delete(id); - }; - res$.subscribe({error: cleanup, complete: cleanup}); - const entry: ObserverEntry = {req$, res$}; - this.calls.set(id, entry); - if (isObservable(data)) { - let firstMessageSent = false; - subscribeCompleteObserver(req$, { - next: (value) => { - const messageMethod = firstMessageSent ? '' : method; - firstMessageSent = true; - const message = new msg.RequestDataMessage(id, messageMethod, new RpcValue(value, undefined)); - this.buffer.push(message); - }, - error: (error) => { - cleanup(); - const messageMethod = firstMessageSent ? '' : method; - const message = new msg.RequestErrorMessage(id, messageMethod, new RpcValue(error, undefined)); - this.buffer.push(message); - }, - complete: (value) => { - cleanup(); - const messageMethod = firstMessageSent ? '' : method; - const message = new msg.RequestCompleteMessage(id, messageMethod, new RpcValue(value, undefined)); - this.buffer.push(message); - }, - }); - data.subscribe(req$); - } else { - this.buffer.push(new msg.RequestCompleteMessage(id, method, new RpcValue(data, undefined))); - req$.complete(); - cleanup(); - } - return new Observable((observer: Observer) => { - res$.subscribe(observer); - return () => { - if (!entry.resFinalized) this.buffer.push(new msg.ResponseUnsubscribeMessage(id)); - res$.complete(); - }; - }); - } - - public async call(method: string, request: unknown): Promise { - return await firstValueFrom(this.call$(method, request)); - } - - /** - * Send a one-way notification message without expecting any response. - * - * @param method Remote method name. - * @param data Static payload data. - */ - public notify(method: string, data: undefined | unknown): void { - const value = new RpcValue(data, undefined); - this.buffer.push(new msg.NotificationMessage(method, value)); - } - - /** - * Stop all in-flight RPC calls and disable buffer. This operation is not - * reversible, you cannot use the RPC client after this call. - */ - public stop(reason: string = 'STOP'): void { - this.buffer.onFlush = (message) => {}; - for (const call of this.calls.values()) { - call.req$.error(new Error(reason)); - call.req$.error(new Error(reason)); - } - this.calls.clear(); - } - - public disconnect() { - this.stop('DISCONNECT'); - } -} diff --git a/src/reactive-rpc/common/rpc/client/__tests__/StaticRpcClient.spec.ts b/src/reactive-rpc/common/rpc/client/__tests__/StaticRpcClient.spec.ts deleted file mode 100644 index 709590e8d3..0000000000 --- a/src/reactive-rpc/common/rpc/client/__tests__/StaticRpcClient.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {RpcMessageBatchProcessor} from '../../RpcMessageBatchProcessor'; -import {runApiTests} from '../../__tests__/runApiTests'; -import {sampleApi} from '../../__tests__/sample-api'; -import {ApiRpcCaller} from '../../caller/ApiRpcCaller'; -import {StaticRpcClient} from '../StaticRpcClient'; - -const setup = () => { - const ctx = {ip: '127.0.0.1'}; - const server = new RpcMessageBatchProcessor({ - caller: new ApiRpcCaller({ - api: sampleApi, - }), - }); - const client = new StaticRpcClient({ - send: async (messages) => await server.onBatch(messages as any, ctx), - bufferSize: 2, - bufferTime: 1, - }); - return {server, client}; -}; - -runApiTests(setup); diff --git a/src/reactive-rpc/common/rpc/client/__tests__/StreamingRpcClient.spec.ts b/src/reactive-rpc/common/rpc/client/__tests__/StreamingRpcClient.spec.ts deleted file mode 100644 index a5fd1e3440..0000000000 --- a/src/reactive-rpc/common/rpc/client/__tests__/StreamingRpcClient.spec.ts +++ /dev/null @@ -1,557 +0,0 @@ -import {StreamingRpcClient} from '../StreamingRpcClient'; -import { - NotificationMessage, - RequestCompleteMessage, - RequestDataMessage, - RequestErrorMessage, - RequestUnsubscribeMessage, - ResponseCompleteMessage, - ResponseDataMessage, - ResponseErrorMessage, -} from '../../../messages'; -import {firstValueFrom, Subject} from 'rxjs'; -import {until} from '../../../../../__tests__/util'; -import {RpcValue} from '../../../messages/Value'; - -test('can create client', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); -}); - -test('does not send any messages on initialization', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); -}); - -test('sends notification message on .notify() call', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - client.notify('foo', Buffer.from('bar')); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const value = new RpcValue(Buffer.from('bar'), undefined); - expect(send).toHaveBeenCalledWith([new NotificationMessage('foo', value)]); -}); - -test('sends notification with no payload', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - client.notify('foo', undefined); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const value = new RpcValue(undefined, undefined); - expect(send).toHaveBeenCalledWith([new NotificationMessage('foo', value)]); -}); - -test('returns Observable on new execution', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - expect(typeof result.subscribe).toBe('function'); -}); - -test('observable does not emit before it receives messages from server', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const sub = jest.fn(); - result.subscribe(sub); - await new Promise((r) => setTimeout(r, 2)); - expect(sub).toHaveBeenCalledTimes(0); -}); - -test('sends Request Complete Message to the server', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const value = new RpcValue(Buffer.from("{foo: 'bar'}"), undefined); - expect(send).toHaveBeenCalledWith([new RequestCompleteMessage(1, 'test', value)]); -}); - -test('sends Request Un-subscribe Message to the server on unsubscribe', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const sub = jest.fn(); - const subscription = result.subscribe(sub); - await new Promise((r) => setTimeout(r, 2)); - subscription.unsubscribe(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(2); - expect(send).toHaveBeenCalledWith([new RequestUnsubscribeMessage(1)]); -}); - -test('server can immediately complete the subscription', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 2)); - const completeMsg = new ResponseCompleteMessage(1, undefined); - client.onMessages([completeMsg]); - await new Promise((r) => setTimeout(r, 2)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('server can immediately complete the subscription with payload', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 20)); - const value = new RpcValue(Buffer.from("{x: 'y'}"), undefined); - const completeMsg = new ResponseCompleteMessage(1, value); - client.onMessages([completeMsg]); - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith(Buffer.from("{x: 'y'}")); -}); - -test('server can send multiple values before completing', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 20)); - const value1 = new RpcValue(Buffer.from("{x: 'y'}"), undefined); - client.onMessages([new ResponseDataMessage(1, value1)]); - await new Promise((r) => setTimeout(r, 20)); - const value2 = new RpcValue(Buffer.from("{z: 'a'}"), undefined); - client.onMessages([new ResponseDataMessage(1, value2)]); - await new Promise((r) => setTimeout(r, 20)); - const value3 = new RpcValue(Buffer.from("{b: 'c'}"), undefined); - client.onMessages([new ResponseCompleteMessage(1, value3)]); - await new Promise((r) => setTimeout(r, 20)); - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith(Buffer.from("{x: 'y'}")); - expect(next).toHaveBeenCalledWith(Buffer.from("{z: 'a'}")); - expect(next).toHaveBeenCalledWith(Buffer.from("{b: 'c'}")); -}); - -test('values are not emitted after observable is unsubscribed', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subscription = result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 20)); - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from([1]), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from([2]), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - subscription.unsubscribe(); - client.onMessages([new ResponseCompleteMessage(1, new RpcValue(Buffer.from([3]), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - expect(next).toHaveBeenCalledWith(Buffer.from([1])); - expect(next).toHaveBeenCalledWith(Buffer.from([2])); -}); - -test('can subscribe to multiple methods', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - - const result1 = client.call$('foo', Buffer.from([1])); - const next1 = jest.fn(); - const error1 = jest.fn(); - const complete1 = jest.fn(); - const subscription1 = result1.subscribe({next: next1, error: error1, complete: complete1}); - - await new Promise((r) => setTimeout(r, 2)); - - const result2 = client.call$('bar', Buffer.from([2])); - const next2 = jest.fn(); - const error2 = jest.fn(); - const complete2 = jest.fn(); - const subscription2 = result2.subscribe({next: next2, error: error2, complete: complete2}); - - await new Promise((r) => setTimeout(r, 2)); - - expect(send).toHaveBeenCalledTimes(2); - expect(send).toHaveBeenCalledWith([new RequestCompleteMessage(1, 'foo', new RpcValue(Buffer.from([1]), undefined))]); - expect(send).toHaveBeenCalledWith([new RequestCompleteMessage(2, 'bar', new RpcValue(Buffer.from([2]), undefined))]); - - client.onMessages([new ResponseDataMessage(2, new RpcValue(Buffer.from('gg'), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - - expect(next1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledWith(Buffer.from('gg')); - - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from('lala'), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - - expect(next1).toHaveBeenCalledWith(Buffer.from('lala')); - expect(next1).toHaveBeenCalledTimes(1); - expect(next2).toHaveBeenCalledTimes(1); - - client.onMessages([new ResponseCompleteMessage(1, new RpcValue(Buffer.from('1'), undefined))]); - client.onMessages([new ResponseCompleteMessage(2, undefined)]); - - expect(next1).toHaveBeenCalledWith(Buffer.from('1')); - expect(next1).toHaveBeenCalledTimes(2); - expect(next2).toHaveBeenCalledTimes(1); - - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(1); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(1); -}); - -test('can respond with error', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subscription = result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 20)); - client.onMessages([new ResponseErrorMessage(1, new RpcValue(Buffer.from([1]), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledWith(Buffer.from([1])); -}); - -test('response can complete without sending any data', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subscription = result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 4)); - client.onMessages([new ResponseCompleteMessage(1, undefined)]); - await new Promise((r) => setTimeout(r, 3)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('does not send unsubscribe when complete has been received', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const result = client.call$('test', Buffer.from("{foo: 'bar'}")); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subscription = result.subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 20)); - expect(send).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledTimes(0); - client.onMessages([new ResponseCompleteMessage(1, new RpcValue(Buffer.from([1]), undefined))]); - await new Promise((r) => setTimeout(r, 20)); - expect(next).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledTimes(1); -}); - -test('does not send unsubscribe when complete has been received - 2', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const observable = client.call$('test', Buffer.from("{foo: 'bar'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const promise = firstValueFrom(observable); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const value = new RpcValue(Buffer.from([25]), undefined); - client.onMessages([new ResponseCompleteMessage(1, value)]); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const result = await promise; - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - expect(result).toEqual(Buffer.from([25])); -}); - -test('does not send unsubscribe when error has been received', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const observable = client.call$('test', Buffer.from("{foo: 'bar'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const promise = firstValueFrom(observable); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const value = new RpcValue(Buffer.from([25]), undefined); - client.onMessages([new ResponseErrorMessage(1, value)]); - expect(send).toHaveBeenCalledTimes(1); - let error; - try { - await promise; - } catch (err) { - error = err; - } - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - expect(error).toEqual(Buffer.from([25])); -}); - -test('after .stop() completes subscriptions', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const observable = client.call$('test', Buffer.from('{}')); - const data = jest.fn(); - observable.subscribe(data); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - expect(data).toHaveBeenCalledTimes(0); - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from([1]), undefined))]); - expect(data).toHaveBeenCalledTimes(1); - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from([2]), undefined))]); - expect(data).toHaveBeenCalledTimes(2); - client.stop(); - client.onMessages([new ResponseDataMessage(1, new RpcValue(Buffer.from([3]), undefined))]); - expect(data).toHaveBeenCalledTimes(2); -}); - -test('combines multiple messages in a batch', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const observable1 = client.call$('test', Buffer.from('{}')); - const observable2 = client.call$('test2', Buffer.from("{foo: 'bar'}")); - client.notify('test3', Buffer.from("{gg: 'bet'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - const messages = send.mock.calls[0][0]; - expect(messages[0]).toBeInstanceOf(RequestCompleteMessage); - expect((messages[0] as any).id).toBe(1); - expect((messages[0] as any).method).toBe('test'); - expect(messages[1]).toBeInstanceOf(RequestCompleteMessage); - expect((messages[1] as any).id).toBe(2); - expect((messages[1] as any).method).toBe('test2'); - expect(messages[2]).toBeInstanceOf(NotificationMessage); - expect((messages[2] as any).method).toBe('test3'); -}); - -test('can receive and process a batch from server', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const observable1 = client.call$('test', Buffer.from('{}')); - const observable2 = client.call$('test2', Buffer.from("{foo: 'bar'}")); - const data1 = jest.fn(); - const data2 = jest.fn(); - const error1 = jest.fn(); - const error2 = jest.fn(); - const complete1 = jest.fn(); - const complete2 = jest.fn(); - observable1.subscribe({next: data1, error: error1, complete: complete1}); - observable2.subscribe({next: data2, error: error2, complete: complete2}); - client.notify('test3', Buffer.from("{gg: 'bet'}")); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(1); - expect(data1).toHaveBeenCalledTimes(0); - expect(data2).toHaveBeenCalledTimes(0); - const value1 = new RpcValue(Buffer.from("{foo: 'bar'}"), undefined); - const value2 = new RpcValue(Buffer.from("{foo: 'baz'}"), undefined); - client.onMessages([new ResponseCompleteMessage(1, value1), new ResponseCompleteMessage(2, value2)]); - await new Promise((r) => setTimeout(r, 2)); - expect(data1).toHaveBeenCalledTimes(1); - expect(data2).toHaveBeenCalledTimes(1); - expect(Buffer.from(data1.mock.calls[0][0]).toString()).toBe("{foo: 'bar'}"); - expect(Buffer.from(data2.mock.calls[0][0]).toString()).toBe("{foo: 'baz'}"); - expect(error1).toHaveBeenCalledTimes(0); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(1); - expect(complete2).toHaveBeenCalledTimes(1); -}); - -test('subscribing twice to call$ does not execute request twice', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const observable = client.call$('test', {}); - observable.subscribe(() => {}); - observable.subscribe(() => {}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(1); -}); - -describe('streaming request', () => { - test('request payload can be streamed', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - const value1 = new RpcValue('1', undefined); - data$.next('1'); - await until(() => send.mock.calls.length === 1); - expect(send).toHaveBeenCalledTimes(1); - expect(send).toHaveBeenCalledWith([new RequestDataMessage(1, 'a.b', value1)]); - data$.next('1.1'); - await until(() => send.mock.calls.length === 2); - expect(send).toHaveBeenCalledTimes(2); - expect(send).toHaveBeenCalledWith([new RequestDataMessage(1, '', new RpcValue('1.1', undefined))]); - data$.next('1.1.1'); - data$.complete(); - await until(() => send.mock.calls.length === 3); - expect(send).toHaveBeenCalledTimes(3); - expect(send).toHaveBeenCalledWith([new RequestDataMessage(1, '', new RpcValue('1.1.1', undefined))]); - }); - - test('request payload error is sent to server', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - data$.next('1'); - await until(() => send.mock.calls.length === 1); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(RequestDataMessage); - expect(send).toHaveBeenCalledWith([new RequestDataMessage(1, 'a.b', new RpcValue('1', undefined))]); - data$.error('1.1'); - await until(() => send.mock.calls.length === 2); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(RequestErrorMessage); - expect(send).toHaveBeenCalledWith([new RequestErrorMessage(1, '', new RpcValue('1.1', undefined))]); - data$.next('1.1.1'); - expect(send).toHaveBeenCalledTimes(2); - }); - - test('request payload complete is sent to server', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - data$.next('1'); - await until(() => send.mock.calls.length === 1); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(RequestDataMessage); - expect(send).toHaveBeenCalledWith([new RequestDataMessage(1, 'a.b', new RpcValue('1', undefined))]); - data$.complete(); - await until(() => send.mock.calls.length === 2); - expect(send).toHaveBeenCalledTimes(2); - expect(send.mock.calls[1][0][0]).toBeInstanceOf(RequestCompleteMessage); - expect(send).toHaveBeenCalledWith([new RequestErrorMessage(1, '', new RpcValue(undefined, undefined))]); - data$.next('1.1.1'); - expect(send).toHaveBeenCalledTimes(2); - }); - - test('can send error as the first request stream message', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - data$.error({foo: 'bar'}); - await until(() => send.mock.calls.length === 1); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(RequestErrorMessage); - expect(send).toHaveBeenCalledWith([new RequestErrorMessage(1, 'a.b', new RpcValue({foo: 'bar'}, undefined))]); - data$.complete(); - data$.next('1.1.1'); - await new Promise((r) => setTimeout(r, 4)); - expect(send).toHaveBeenCalledTimes(1); - }); - - test('can send complete as the first request stream message', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - await new Promise((r) => setTimeout(r, 1)); - expect(send).toHaveBeenCalledTimes(0); - data$.complete(); - await new Promise((r) => setTimeout(r, 4)); - expect(send).toHaveBeenCalledTimes(1); - expect(send.mock.calls[0][0][0]).toBeInstanceOf(RequestCompleteMessage); - expect(send).toHaveBeenCalledWith([new RequestCompleteMessage(1, 'a.b', new RpcValue(undefined, undefined))]); - data$.complete(); - data$.error(123); - data$.next('1.1.1'); - await new Promise((r) => setTimeout(r, 4)); - expect(send).toHaveBeenCalledTimes(1); - }); -}); - -describe('memory leaks', () => { - test('removes calls when request and response complete', async () => { - const send = jest.fn(); - const client = new StreamingRpcClient({send, bufferTime: 1}); - expect(client.getInflightCallCount()).toBe(0); - const data$ = new Subject(); - await new Promise((r) => setTimeout(r, 2)); - expect(send).toHaveBeenCalledTimes(0); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - client.call$('a.b', data$).subscribe({next, error, complete}); - expect(client.getInflightCallCount()).toBe(1); - data$.complete(); - expect(next).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - expect(client.getInflightCallCount()).toBe(1); - client.onMessages([new ResponseCompleteMessage(1, new RpcValue('gaga', undefined))]); - await new Promise((r) => setTimeout(r, 4)); - expect(next).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(1); - expect(client.getInflightCallCount()).toBe(0); - }); -}); diff --git a/src/reactive-rpc/common/rpc/client/index.ts b/src/reactive-rpc/common/rpc/client/index.ts deleted file mode 100644 index 691555b569..0000000000 --- a/src/reactive-rpc/common/rpc/client/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './StreamingRpcClient'; diff --git a/src/reactive-rpc/common/rpc/client/types.ts b/src/reactive-rpc/common/rpc/client/types.ts deleted file mode 100644 index 1ca8edfa37..0000000000 --- a/src/reactive-rpc/common/rpc/client/types.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type {Observable} from 'rxjs'; - -export interface RpcClient { - /** - * Execute a streaming RPC method. - * - * @param method RPC method name. - * @param data RPC method static payload or stream of data. - */ - call$(method: string, data: unknown | Observable): Observable; - - /** - * Execute a one-way RPC method. - * - * @param method RPC method name. - * @param request RPC method static payload. - */ - call(method: string, request: unknown): Promise; - - /** - * Send a one-way notification message without expecting any response. - * - * @param method Remote method name. - * @param data Static payload data. - */ - notify(method: string, data: undefined | unknown): void; - - // start(): void; - // stop(): void; -} - -type TypedRpcClientFn = (req: Request) => Promise; -type TypedRpcClientFn$ = (req: Observable) => Observable; -type UnPromise = T extends Promise ? U : T; -type UnObservable = T extends Observable ? U : T; -type UnwrapResponse = UnPromise>; - -export interface TypedRpcClient | TypedRpcClientFn$>> - extends RpcClient { - call$( - method: K, - data: Parameters[0] | UnObservable[0]>, - ): Observable>>; - call( - method: K, - data: Parameters[0], - ): Promise>>; - notify(method: K, data: UnObservable[0]>): void; -} diff --git a/src/reactive-rpc/common/rpc/index.ts b/src/reactive-rpc/common/rpc/index.ts deleted file mode 100644 index bdbdd0d8e3..0000000000 --- a/src/reactive-rpc/common/rpc/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './types'; -export * from './client'; -export * from './RpcMessageBatchProcessor'; -export * from './RpcMessageStreamProcessor'; -export * from './RpcDuplex'; -export {RpcPersistentClient, type RpcPersistentClientParams} from './RpcPersistentClient'; diff --git a/src/reactive-rpc/common/rpc/methods/StaticRpcMethod.ts b/src/reactive-rpc/common/rpc/methods/StaticRpcMethod.ts deleted file mode 100644 index 0da0fa5c6e..0000000000 --- a/src/reactive-rpc/common/rpc/methods/StaticRpcMethod.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {firstValueFrom, from} from 'rxjs'; -import {IRpcMethodBase, IStaticRpcMethod} from './types'; -import {ConstSchema} from '../../../../json-type'; - -export interface StaticRpcMethodOptions - extends Omit, 'isStreaming' | 'call$'> {} - -export class StaticRpcMethod implements IStaticRpcMethod { - public readonly isStreaming = false; - public readonly pretty: boolean; - public readonly validate?: IStaticRpcMethod['validate']; - public readonly onPreCall?: (ctx: Ctx, request: Req) => Promise; - public readonly req?: IStaticRpcMethod['req']; - public readonly res?: IStaticRpcMethod['res']; - public readonly call: IRpcMethodBase['call']; - public readonly call$: IRpcMethodBase['call$']; - public readonly acceptsNotifications: boolean; - - constructor(options: StaticRpcMethodOptions) { - const {call, validate, onPreCall, pretty, req, res} = options; - this.pretty = !!pretty; - this.validate = validate; - this.onPreCall = onPreCall; - this.req = req; - this.res = res; - const responseIsVoid = - !!res && res.getTypeName() === 'const' && (res.getSchema() as ConstSchema).value === undefined; - this.acceptsNotifications = responseIsVoid; - this.call = call; - this.call$ = (request$, ctx) => from((async () => this.call(await firstValueFrom(request$), ctx))()); - } -} diff --git a/src/reactive-rpc/common/rpc/methods/StreamingRpcMethod.ts b/src/reactive-rpc/common/rpc/methods/StreamingRpcMethod.ts deleted file mode 100644 index 4ac6c43bf0..0000000000 --- a/src/reactive-rpc/common/rpc/methods/StreamingRpcMethod.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {firstValueFrom, of} from 'rxjs'; -import type {IRpcMethodBase, IStreamingRpcMethod} from './types'; - -export interface StreamingRpcMethodOptions - extends Omit, 'isStreaming' | 'call'> {} - -export class StreamingRpcMethod - implements IStreamingRpcMethod -{ - public readonly isStreaming = true; - public readonly pretty: boolean; - public readonly validate?: IStreamingRpcMethod['validate']; - public readonly onPreCall?: (ctx: Ctx, request: Req) => Promise; - public readonly req?: IStreamingRpcMethod['req']; - public readonly res?: IStreamingRpcMethod['res']; - public readonly call: IRpcMethodBase['call']; - public readonly call$: IRpcMethodBase['call$']; - public readonly preCallBufferSize?: number; - - constructor(options: StreamingRpcMethodOptions) { - const {call$, onPreCall, pretty, validate, req, res, preCallBufferSize} = options; - this.pretty = !!pretty; - this.validate = validate; - this.onPreCall = onPreCall; - this.req = req; - this.res = res; - this.preCallBufferSize = preCallBufferSize; - this.call = (request, ctx) => firstValueFrom(call$(of(request), ctx)); - this.call$ = call$; - } -} diff --git a/src/reactive-rpc/common/rpc/methods/types.ts b/src/reactive-rpc/common/rpc/methods/types.ts deleted file mode 100644 index ed0badc67b..0000000000 --- a/src/reactive-rpc/common/rpc/methods/types.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type {Observable} from 'rxjs'; -import type {Type} from '../../../../json-type'; - -export interface IRpcMethodBase { - /** - * Specifies if request or response of the method could be a stream. - */ - isStreaming: boolean; - - /** - * Validation logic. Should throw if request is invalid, not throw otherwise. - * In case request is a stream, validation method is executed for every - * emitted value. - */ - validate?: (request: Req) => void; - - /** - * Method which is executed before an actual call to an RPC method. Pre-call - * checks should execute all necessary checks (such as authentication, - * authorization, throttling, etc.) before allowing the real method to - * proceed. Pre-call checks should throw, if for any reason the call should - * not proceed. Return void to allow execution of the actual call. - * - * @param ctx Request context object. - * @param request Request payload, the first emitted value in case of - * streaming request. - */ - onPreCall?: (ctx: Ctx, request: Req) => Promise; - - /** - * Whether to pretty print the response. - */ - pretty?: boolean; - - req?: Type; - res?: Type; - - call: (request: Req, ctx: Ctx) => Promise; - call$: (request$: Observable, ctx: Ctx) => Observable; -} - -export interface IStaticRpcMethod - extends Omit, 'call$'> { - isStreaming: false; -} - -export interface IStreamingRpcMethod - extends Omit, 'call'> { - isStreaming: true; - - /** - * When call `request$` is a multi-value observable and request data is coming - * in while pre-call check is still being executed, this property determines - * how many `request$` values to buffer in memory before raising an error - * and stopping the streaming call. Defaults to the `preCallBufferSize` param - * set on the `RpcServer`. - */ - preCallBufferSize?: number; -} - -export type RpcMethod = - | IStaticRpcMethod - | IStreamingRpcMethod; - -export type RpcMethodMap = {[name: string]: RpcMethod}; diff --git a/src/reactive-rpc/common/rpc/types.ts b/src/reactive-rpc/common/rpc/types.ts deleted file mode 100644 index 96987a643a..0000000000 --- a/src/reactive-rpc/common/rpc/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './client/types'; -export * from './methods/types'; -export * from './caller/types'; - -export type RpcSpecifier = RpcSpecifierRx | RpcSpecifierJson2; -type RpcSpecifierRx = `rpc.rx.${'binary' | 'compact'}.${RpcSpecifierEncoding}`; -type RpcSpecifierJson2 = `rpc.json2.verbose.${RpcSpecifierEncoding}`; -type RpcSpecifierEncoding = 'cbor' | 'json' | 'msgpack'; diff --git a/src/reactive-rpc/common/rpc/validation.ts b/src/reactive-rpc/common/rpc/validation.ts deleted file mode 100644 index 75de59de07..0000000000 --- a/src/reactive-rpc/common/rpc/validation.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {RpcError} from './caller'; - -export const validateId = (id: unknown) => { - if (typeof id !== 'number' || !Number.isInteger(id) || id < 0) { - throw RpcError.value(RpcError.validation('Invalid id')); - } -}; - -export const validateMethod = (method: unknown) => { - if (!method || typeof method !== 'string' || method.length > 64) { - throw RpcError.value(RpcError.validation('Invalid method')); - } -}; diff --git a/src/reactive-rpc/common/testing/buildE2eClient.ts b/src/reactive-rpc/common/testing/buildE2eClient.ts deleted file mode 100644 index 0958b5d727..0000000000 --- a/src/reactive-rpc/common/testing/buildE2eClient.ts +++ /dev/null @@ -1,104 +0,0 @@ -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {ConnectionContext} from '../../server/context'; -import {RpcCodecs} from '../codec/RpcCodecs'; -import {RpcMessageCodecs} from '../codec/RpcMessageCodecs'; -import {ReactiveRpcClientMessage, ReactiveRpcMessage, ReactiveRpcServerMessage} from '../messages'; -import {RpcMessageStreamProcessor, StreamingRpcClient, TypedRpcClient} from '../rpc'; -import type {RpcCaller} from '../rpc/caller/RpcCaller'; -import type {CallerToMethods} from '../types'; - -export interface BuildE2eClientOptions { - /** - * Writer to use for encoding messages. Defaults to `new Writer(4 * 1024)`. - */ - writer?: Writer; - - /** - * Minimum and maximum size of the default buffer in kilobytes. An actual - * size will be picked randomly between these two values. Defaults to - * `[4, 4]`. Used when `writer` is not specified. - */ - writerDefaultBufferKb?: [min: number, max: number]; - - /** - * Number of messages to keep in buffer before sending them to the client. - * The actual number of messages will be picked randomly between these two - * values. Defaults to `[1, 1]`. - */ - serverBufferSize?: [min: number, max: number]; - - /** - * Time in milliseconds for how long to buffer messages before sending them - * to the client. The actual time will be picked randomly between these two - * values. Defaults to `[0, 0]`. - */ - serverBufferTime?: [min: number, max: number]; - - /** - * Number of messages to keep in buffer before sending them to the server. - * The actual number of messages will be picked randomly between these two - * values. Defaults to `[1, 1]`. - */ - clientBufferSize?: [min: number, max: number]; - - /** - * Time in milliseconds for how long to buffer messages before sending them - * to the server. The actual time will be picked randomly between these two - * values. Defaults to `[0, 0]`. - */ - clientBufferTime?: [min: number, max: number]; - - /** - * IP address to use for the connection. Defaults to `0.0.0.0`. - */ - ip?: string; - - /** - * Authentication token to use for the connection. Defaults to empty string. - */ - token?: string; -} - -export const buildE2eClient = >(caller: Caller, opt: BuildE2eClientOptions = {}) => { - const writer = opt.writer ?? new Writer(Fuzzer.randomInt2(opt.writerDefaultBufferKb ?? [4, 4]) * 1024); - const codecs = new RpcCodecs(new Codecs(writer), new RpcMessageCodecs()); - const ctx = new ConnectionContext( - opt.ip ?? '0.0.0.0', - opt.ip ?? '', - null, - {}, - codecs.value.cbor, - codecs.value.cbor, - codecs.messages.binary, - ); - let client: StreamingRpcClient; - const streamProcessor = new RpcMessageStreamProcessor({ - caller, - send: (messages: ReactiveRpcMessage[]) => { - const encoded = ctx.msgCodec.encode(ctx.resCodec, messages); - setTimeout(() => { - const decoded = ctx.msgCodec.decodeBatch(ctx.resCodec, encoded); - client.onMessages(decoded as ReactiveRpcServerMessage[]); - }, 1); - }, - bufferSize: Fuzzer.randomInt2(opt.serverBufferSize ?? [1, 1]), - bufferTime: Fuzzer.randomInt2(opt.serverBufferTime ?? [0, 0]), - }); - client = new StreamingRpcClient({ - send: (messages: ReactiveRpcClientMessage[]) => { - const encoded = ctx.msgCodec.encode(ctx.reqCodec, messages); - setTimeout(() => { - const decoded = ctx.msgCodec.decodeBatch(ctx.reqCodec, encoded); - streamProcessor.onMessages(decoded as ReactiveRpcClientMessage[], {}); - }, 1); - }, - bufferSize: Fuzzer.randomInt2(opt.clientBufferSize ?? [1, 1]), - bufferTime: Fuzzer.randomInt2(opt.clientBufferTime ?? [0, 0]), - }); - const typedClient = client as TypedRpcClient>; - return { - client: typedClient, - }; -}; diff --git a/src/reactive-rpc/common/types.ts b/src/reactive-rpc/common/types.ts deleted file mode 100644 index b9ee65d320..0000000000 --- a/src/reactive-rpc/common/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type {Observable} from 'rxjs'; -import type {FunctionStreamingType, FunctionType, ResolveType} from '../../json-type'; -import type {ObjectValue, ObjectValueToTypeMap, UnObjectType} from '../../json-type-value/ObjectValue'; -import type {TypeRouter} from '../../json-type/system/TypeRouter'; -import type {ObjectValueCaller} from './rpc/caller/ObjectValueCaller'; -import type {RpcCaller} from './rpc/caller/RpcCaller'; -import type {TypeRouterCaller} from './rpc/caller/TypeRouterCaller'; - -export type CallerToMethods> = { - [K in keyof UnTypeRouter>]: UnwrapFunction>[K]>; -}; - -type UnTypeRouterCaller = T extends TypeRouterCaller ? R : T extends ObjectValueCaller ? R : never; -type UnTypeRouter = - T extends TypeRouter ? R : T extends ObjectValue ? ObjectValueToTypeMap> : never; -type UnwrapFunction = - F extends FunctionType - ? (req: ResolveType) => Promise> - : F extends FunctionStreamingType - ? (req$: Observable>) => Observable> - : never; diff --git a/src/reactive-rpc/common/util/TimedQueue.ts b/src/reactive-rpc/common/util/TimedQueue.ts deleted file mode 100644 index ac56cbb358..0000000000 --- a/src/reactive-rpc/common/util/TimedQueue.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Queue that is flushed automatically when it reaches some item limit - * or when timeout is reached. - */ -export class TimedQueue { - /** - * Queue will be flushed when it reaches this number of items. - */ - public itemLimit: number = 100; - - /** - * Queue will be flushed after this many milliseconds. - */ - public timeLimit: number = 5; - - /** - * Method that will be called when queue is flushed. - */ - public onFlush: (list: T[]) => void = (list: T[]) => {}; - - private list: T[] = []; - private timer: null | number | NodeJS.Timeout = null; - - public push(item: T) { - this.list.push(item); - if (this.list.length >= this.itemLimit) { - this.flush(); - return; - } - if (!this.timer) { - this.timer = setTimeout(() => { - this.flush(); - }, this.timeLimit); - } - } - - public flush(): T[] { - const list = this.list; - this.list = []; - if (this.timer) clearTimeout(this.timer as any); - this.timer = null; - if (list.length) this.onFlush(list); - return list; - } -} diff --git a/src/reactive-rpc/common/util/__tests__/TimedQueue.spec.ts b/src/reactive-rpc/common/util/__tests__/TimedQueue.spec.ts deleted file mode 100644 index 2b96dd4c75..0000000000 --- a/src/reactive-rpc/common/util/__tests__/TimedQueue.spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import {TimedQueue} from '../TimedQueue'; - -test('can create queue', () => { - const queue = new TimedQueue<123>(); -}); - -test('can add items', () => { - const queue = new TimedQueue(); - queue.onFlush = jest.fn(); - queue.push(123); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledWith([123]); -}); - -test('can flush items', () => { - const queue = new TimedQueue(); - queue.onFlush = jest.fn(); - queue.push(123); - queue.push(3); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledWith([123, 3]); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - queue.push(1); - queue.push(2); - queue.push(3); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledWith([1, 2, 3]); - expect(queue.onFlush).toHaveBeenCalledTimes(2); -}); - -test('flushes queue when item limit is reached, subsequent flush does not execute', () => { - const queue = new TimedQueue(); - queue.itemLimit = 5; - queue.onFlush = jest.fn(); - queue.push(0); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - queue.push(1); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - queue.push(2); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - queue.push(3); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - queue.push(4); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - expect(queue.onFlush).toHaveBeenCalledWith([0, 1, 2, 3, 4]); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledTimes(1); -}); - -test('flushes queue multiple times', () => { - const queue = new TimedQueue(); - queue.itemLimit = 2; - queue.onFlush = jest.fn(); - queue.push(0); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - queue.push(1); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - queue.push(2); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - queue.push(3); - expect(queue.onFlush).toHaveBeenCalledTimes(2); - expect(queue.onFlush).toHaveBeenCalledWith([0, 1]); - expect(queue.onFlush).toHaveBeenCalledWith([2, 3]); - queue.push(4); - expect(queue.onFlush).toHaveBeenCalledTimes(2); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledWith([4]); -}); - -test('flushes when timeout is reached', (done) => { - const queue = new TimedQueue(); - queue.timeLimit = 100; - queue.onFlush = jest.fn(); - queue.push(1); - queue.push(3); - queue.push(2); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledWith([1, 3, 2]); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - done(); - }, 101); -}); - -test('flushes on timeout twice', (done) => { - const queue = new TimedQueue(); - queue.timeLimit = 20; - queue.onFlush = jest.fn(); - queue.push(1); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledWith([1]); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - queue.push(2); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledWith([2]); - expect(queue.onFlush).toHaveBeenCalledTimes(2); - done(); - }, 21); - }, 21); -}); - -test('does not flush after timeout if queue is empty', (done) => { - const queue = new TimedQueue(); - queue.timeLimit = 20; - queue.onFlush = jest.fn(); - queue.push(1); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledWith([1]); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledTimes(1); - done(); - }, 21); - }, 21); -}); - -test('when flushed manually, does not flush after timeout', (done) => { - const queue = new TimedQueue(); - queue.timeLimit = 20; - queue.onFlush = jest.fn(); - queue.push(1); - expect(queue.onFlush).toHaveBeenCalledTimes(0); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledWith([1]); - expect(queue.onFlush).toHaveBeenCalledTimes(1); - queue.push(3); - queue.flush(); - expect(queue.onFlush).toHaveBeenCalledTimes(2); - expect(queue.onFlush).toHaveBeenCalledWith([3]); - setTimeout(() => { - expect(queue.onFlush).toHaveBeenCalledTimes(2); - done(); - }, 21); - }, 21); -}); diff --git a/src/reactive-rpc/common/util/__tests__/subscribeCompleteObserver.spec.ts b/src/reactive-rpc/common/util/__tests__/subscribeCompleteObserver.spec.ts deleted file mode 100644 index 91c8109b0d..0000000000 --- a/src/reactive-rpc/common/util/__tests__/subscribeCompleteObserver.spec.ts +++ /dev/null @@ -1,181 +0,0 @@ -import {Subject} from 'rxjs'; -import {subscribeCompleteObserver} from '../subscribeCompleteObserver'; - -test('does not execute callback on initial subscription', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); -}); - -test('receives one emitted value', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); -}); - -test('receives one emitted value and complete message', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.next(1); - subject.complete(); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledWith(1, true); -}); - -test('receives one emitted value and complete message, when complete is async', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.next(1); - await new Promise((r) => process.nextTick(r)); - subject.complete(); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledWith(undefined, false); -}); - -test('can complete immediately', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.complete(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('can complete after async await', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.complete(); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('can emit async multiple times, multiple sync emission per time', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.next(1); - subject.next(2); - await new Promise((r) => setTimeout(r, 1)); - subject.next(3); - subject.next(4); - subject.next(5); - await new Promise((r) => setTimeout(r, 1)); - subject.next(6); - subject.next(7); - subject.next(8); - subject.next(9); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(8); - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); - expect(next.mock.calls[3][0]).toBe(4); - expect(next.mock.calls[4][0]).toBe(5); - expect(next.mock.calls[5][0]).toBe(6); - expect(next.mock.calls[6][0]).toBe(7); - expect(next.mock.calls[7][0]).toBe(8); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledWith(9, true); -}); - -test('can emit async multiple times, multiple sync emission per time and complete with no value', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.next(1); - subject.next(2); - await new Promise((r) => setTimeout(r, 1)); - subject.next(3); - subject.next(4); - subject.next(5); - await new Promise((r) => setTimeout(r, 1)); - subject.next(6); - subject.next(7); - subject.next(8); - subject.next(9); - await new Promise((r) => setTimeout(r, 1)); - subject.complete(); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(9); - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); - expect(next.mock.calls[3][0]).toBe(4); - expect(next.mock.calls[4][0]).toBe(5); - expect(next.mock.calls[5][0]).toBe(6); - expect(next.mock.calls[6][0]).toBe(7); - expect(next.mock.calls[7][0]).toBe(8); - expect(next.mock.calls[8][0]).toBe(9); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledWith(undefined, false); -}); - -test('can error in the middle of execution', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeCompleteObserver(subject, {next, error, complete}); - subject.next(1); - subject.next(2); - await new Promise((r) => setTimeout(r, 1)); - subject.next(3); - subject.next(4); - subject.error('test'); - subject.error('test2'); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(4); - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); - expect(next.mock.calls[3][0]).toBe(4); - expect(error).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledWith('test'); - expect(complete).toHaveBeenCalledTimes(0); -}); diff --git a/src/reactive-rpc/common/util/__tests__/subscribeSyncObserver.spec.ts b/src/reactive-rpc/common/util/__tests__/subscribeSyncObserver.spec.ts deleted file mode 100644 index 3567ccf222..0000000000 --- a/src/reactive-rpc/common/util/__tests__/subscribeSyncObserver.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {Subject} from 'rxjs'; -import {subscribeSyncObserver} from '../subscribeSyncObserver'; - -test('does not execute callback on initial subscription', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeSyncObserver(subject, {next, error, complete}); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); -}); - -test('receives one emitted value', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeSyncObserver(subject, {next, error, complete}); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith([1], false); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); -}); - -test('receives one emitted value and complete message', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeSyncObserver(subject, {next, error, complete}); - subject.next(1); - subject.complete(); - await new Promise((r) => process.nextTick(r)); - expect(next).toHaveBeenCalledTimes(1); - expect(next).toHaveBeenCalledWith([1], true); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('can complete immediately', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeSyncObserver(subject, {next, error, complete}); - subject.complete(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); - -test('can emit async multiple times, multiple sync emission per time', async () => { - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const subject = new Subject(); - subscribeSyncObserver(subject, {next, error, complete}); - subject.next(1); - subject.next(2); - await new Promise((r) => setTimeout(r, 1)); - subject.next(3); - subject.next(4); - subject.next(5); - await new Promise((r) => setTimeout(r, 1)); - subject.next(6); - subject.next(7); - subject.next(8); - subject.next(9); - subject.complete(); - await new Promise((r) => setTimeout(r, 1)); - expect(next).toHaveBeenCalledTimes(3); - expect(next).toHaveBeenCalledWith([1, 2], false); - expect(next).toHaveBeenCalledWith([3, 4, 5], false); - expect(next).toHaveBeenCalledWith([6, 7, 8, 9], true); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); -}); diff --git a/src/reactive-rpc/common/util/microtask.ts b/src/reactive-rpc/common/util/microtask.ts deleted file mode 100644 index 0d7330078b..0000000000 --- a/src/reactive-rpc/common/util/microtask.ts +++ /dev/null @@ -1,8 +0,0 @@ -const nextTickMicrotask = - typeof process === 'object' && typeof process.nextTick === 'function' ? process.nextTick : null; - -const promiseMicrotask = (callback: () => void) => { - Promise.resolve().then(callback); -}; - -export const microtask = nextTickMicrotask || promiseMicrotask; diff --git a/src/reactive-rpc/common/util/of.ts b/src/reactive-rpc/common/util/of.ts deleted file mode 100644 index 8b963000b3..0000000000 --- a/src/reactive-rpc/common/util/of.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Given a promise awaits it and returns a 3-tuple, with the following members: - * - * - First entry is either the resolved value of the promise or `undefined`. - * - Second entry is either the error thrown by promise or `undefined`. - * - Third entry is a boolean, truthy if promise was resolved and falsy if rejected. - * - * @param promise Promise to convert to 3-tuple. - */ -export const of = async (promise: Promise): Promise<[T | undefined, E | undefined, boolean]> => { - try { - return [await promise, undefined, true]; - } catch (error) { - return [undefined, error as E, false]; - } -}; diff --git a/src/reactive-rpc/common/util/subscribeCompleteObserver.ts b/src/reactive-rpc/common/util/subscribeCompleteObserver.ts deleted file mode 100644 index 86b91f969c..0000000000 --- a/src/reactive-rpc/common/util/subscribeCompleteObserver.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {Observable} from 'rxjs'; -import {microtask} from './microtask'; - -export interface CompleteObserver { - next: (value: T) => void; - error: (error: unknown) => void; - complete: (value: undefined | T, hasValue: boolean) => void; -} - -/** - * Subscribes `CompleteObserver` to observable. `CompleteObserver` attempts to - * receive the last emitted value in `.complete(value)` callback, instead of - * calling `.next(value)` followed by `.complete()`. - * - * @param observable Observable to which to subscribe. - * @param observer Observer which to subscribe to observable. - * @returns Subscription - */ -export function subscribeCompleteObserver(observable: Observable, observer: CompleteObserver) { - let completed = false; - let completeCalled = false; - let tasks = 0; - return observable.subscribe({ - next: (value: T) => { - tasks++; - microtask(() => { - tasks--; - if (completed && !tasks) { - completeCalled = true; - observer.complete(value, true); - } else { - observer.next(value); - } - }); - }, - error: (error: unknown) => { - if (!tasks) observer.error(error); - else - microtask(() => { - observer.error(error); - }); - }, - complete: () => { - completed = true; - if (completeCalled) return; - if (!tasks) observer.complete(undefined, false); - else { - microtask(() => { - if (completeCalled) return; - observer.complete(undefined, false); - }); - } - }, - }); -} diff --git a/src/reactive-rpc/common/util/subscribeSyncObserver.ts b/src/reactive-rpc/common/util/subscribeSyncObserver.ts deleted file mode 100644 index 8b51d2fa4a..0000000000 --- a/src/reactive-rpc/common/util/subscribeSyncObserver.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Observable} from 'rxjs'; -import {microtask} from './microtask'; - -export interface SyncObserver { - next: (values: T[], completed: boolean) => void; - error: (error: unknown) => void; - complete: () => void; -} - -/** - * Subscribes `SyncObserver` to observable. `SyncObserver` a list of all values - * that were synchronously emitted within one micro-tick cycle. - * - * @param observable Observable to which to subscribe. - * @param observer Observer which to subscribe to observable. - * @returns Subscription - */ -export function subscribeSyncObserver(observable: Observable, observer: SyncObserver) { - let completed = false; - let buffer: T[] = []; - const flush = () => { - if (!buffer.length) return; - observer.next(buffer, completed); - buffer = []; - }; - return observable.subscribe({ - next: (data: T) => { - buffer.push(data); - if (buffer.length === 1) microtask(flush); - }, - error: (error: unknown) => { - flush(); - observer.error(error); - }, - complete: () => { - completed = true; - flush(); - observer.complete(); - }, - }); -} diff --git a/src/reactive-rpc/server/context.ts b/src/reactive-rpc/server/context.ts deleted file mode 100644 index 7b48210ae6..0000000000 --- a/src/reactive-rpc/server/context.ts +++ /dev/null @@ -1,213 +0,0 @@ -import {NullObject} from '@jsonjoy.com/util/lib/NullObject'; -import {copy} from '@jsonjoy.com/util/lib/buffers/copy'; -import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {RpcMessageCodec} from '../common/codec/types'; -import type {RpcApp} from './uws/RpcApp'; -import type {HttpRequest, HttpResponse} from './uws/types'; -import type {RpcCodecs} from '../common/codec/RpcCodecs'; - -const REGEX_AUTH_TOKEN_SPECIFIER = /tkn\.([a-zA-Z0-9\-_]+)(?:[^a-zA-Z0-9\-_]|$)/; -const REGEX_CODECS_SPECIFIER = /rpc\.(\w{0,32})\.(\w{0,32})\.(\w{0,32})(?:\-(\w{0,32}))?/; - -export class ConnectionContext> { - private static findIp(req: HttpRequest, res: HttpResponse): string { - return ( - req.getHeader('x-forwarded-for') || - req.getHeader('x-real-ip') || - Buffer.from(res.getRemoteAddressAsText()).toString() - ); - } - - private static findTokenInText(text: string): string { - const match = REGEX_AUTH_TOKEN_SPECIFIER.exec(text); - if (!match) return ''; - return match[1] || ''; - } - - /** - * Looks for an authentication token in the following places: - * - * 1. The `Authorization` header. - * 2. The URI query parameters. - * 3. The `Cookie` header. - * 4. The `Sec-Websocket-Protocol` header. - * - * @param req HTTP request - * @returns Authentication token, if any. - */ - private static findToken(req: HttpRequest): string { - let token: string = ''; - let text: string = ''; - text = req.getHeader('authorization'); - if (text) token = ConnectionContext.findTokenInText(text); - if (token) return token; - text = req.getQuery(); - if (text) token = ConnectionContext.findTokenInText(text); - if (token) return token; - text = req.getHeader('cookie'); - if (text) token = ConnectionContext.findTokenInText(text); - if (token) return token; - text = req.getHeader('sec-websocket-protocol'); - if (text) token = ConnectionContext.findTokenInText(text); - return token; - } - - public static fromReqRes( - req: HttpRequest, - res: HttpResponse, - params: string[] | null, - app: RpcApp, - ): ConnectionContext { - const ip = ConnectionContext.findIp(req, res); - const token: string = ConnectionContext.findToken(req); - const codecs = app.codecs; - const valueCodecs = codecs.value; - const ctx = new ConnectionContext( - ip, - token, - params, - new NullObject(), - valueCodecs.json, - valueCodecs.json, - codecs.messages.compact, - res, - ); - const contentType = req.getHeader('content-type'); - if (contentType) ctx.setCodecs(contentType, codecs); - return ctx; - } - - public static fromWs( - req: HttpRequest, - res: HttpResponse, - secWebSocketProtocol: string, - params: string[] | null, - app: RpcApp, - ): ConnectionContext { - const ip = ConnectionContext.findIp(req, res); - const token: string = ConnectionContext.findToken(req); - const codecs = app.codecs; - const valueCodecs = codecs.value; - const ctx = new ConnectionContext( - ip, - token, - params, - new NullObject(), - valueCodecs.json, - valueCodecs.json, - codecs.messages.compact, - res, - ); - const contentType = req.getHeader('content-type'); - if (contentType) ctx.setCodecs(contentType, codecs); - else if (secWebSocketProtocol) ctx.setCodecs(secWebSocketProtocol, codecs); - return ctx; - } - - constructor( - public readonly ip: string, - public readonly token: string, - public readonly params: string[] | null, - public readonly meta: Meta, - public reqCodec: JsonValueCodec, - public resCodec: JsonValueCodec, - public msgCodec: RpcMessageCodec, - public res: HttpResponse | undefined = undefined, - ) {} - - /** - * @param specifier A string which may contain a codec specifier. For example: - * - `rpc.rx.compact.cbor` for Rx-RPC with compact messages and CBOR values. - * - `rpc.json2.verbose.json` for JSON-RPC 2.0 with verbose messages encoded as JSON. - */ - public setCodecs(specifier: string, codecs: RpcCodecs): void { - const match = REGEX_CODECS_SPECIFIER.exec(specifier); - if (!match) return; - const [, protocol, messageFormat, request, response] = match; - switch (protocol) { - case 'rx': { - switch (messageFormat) { - case 'compact': { - this.msgCodec = codecs.messages.compact; - break; - } - case 'binary': { - this.msgCodec = codecs.messages.binary; - break; - } - } - break; - } - case 'json2': { - this.msgCodec = codecs.messages.jsonRpc2; - break; - } - } - switch (request) { - case 'cbor': { - this.resCodec = this.reqCodec = codecs.value.cbor; - break; - } - case 'json': { - this.resCodec = this.reqCodec = codecs.value.json; - break; - } - case 'msgpack': { - this.resCodec = this.reqCodec = codecs.value.msgpack; - break; - } - } - switch (response) { - case 'cbor': { - this.resCodec = codecs.value.cbor; - break; - } - case 'json': { - this.resCodec = codecs.value.json; - break; - } - case 'msgpack': { - this.resCodec = codecs.value.msgpack; - break; - } - } - } - - public requestBodyParts(max: number): Promise { - const res = this.res; - return new Promise((resolve) => { - const list: Uint8Array[] = []; - if (!res) return resolve(list); - let running = 0; - res.onData((ab, isLast) => { - running += ab.byteLength; - if (running > max) { - res.aborted = true; - res.end('too large'); - } - // Last `ab` does not need to be copied, as per docs. - if (isLast) list.push(new Uint8Array(ab)), resolve(list); - else list.push(copy(new Uint8Array(ab))); - }); - }); - } - - public async requestBody(max: number): Promise { - const parts = await this.requestBodyParts(max); - return listToUint8(parts); - } - - public async requestBodyJson(max: number): Promise { - const parts = await this.requestBodyParts(max); - const bodyUint8 = listToUint8(parts); - return this.reqCodec.decoder.read(bodyUint8); - } - - public sendResponse(response: Uint8Array): void { - const res = this.res; - if (!res) return; - if (res.aborted) return; - res.end(response); - } -} diff --git a/src/reactive-rpc/server/http1/Http1Server.ts b/src/reactive-rpc/server/http1/Http1Server.ts deleted file mode 100644 index b00ef387d8..0000000000 --- a/src/reactive-rpc/server/http1/Http1Server.ts +++ /dev/null @@ -1,281 +0,0 @@ -import * as http from 'http'; -import * as net from 'net'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {WsServerConnection} from '../ws/server/WsServerConnection'; -import {WsFrameEncoder} from '../ws/codec/WsFrameEncoder'; -import {RouteMatcher} from '../../../util/router/codegen'; -import {Router} from '../../../util/router'; -import {Printable} from '../../../util/print/types'; -import {printTree} from '../../../util/print/printTree'; -import {PayloadTooLarge} from './errors'; -import {findTokenInText, setCodecs} from './util'; -import {Http1ConnectionContext, WsConnectionContext} from './context'; -import {RpcCodecs} from '../../common/codec/RpcCodecs'; -import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; -import {NullObject} from '@jsonjoy.com/util/lib/NullObject'; - -export type Http1Handler = (ctx: Http1ConnectionContext) => void | Promise; -export type Http1NotFoundHandler = (res: http.ServerResponse, req: http.IncomingMessage) => void; -export type Http1InternalErrorHandler = (error: unknown, res: http.ServerResponse, req: http.IncomingMessage) => void; - -export class Http1EndpointMatch { - constructor(public readonly handler: Http1Handler) {} -} - -export interface Http1EndpointDefinition { - /** - * The HTTP method to match. If not specified, then the handler will be - * invoked for any method. - */ - method?: string | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT'; - - /** - * The path to match. Should start with a slash. - */ - path: string; - - /** - * The handler function. - */ - handler: Http1Handler; -} - -export interface WsEndpointDefinition { - path: string; - maxIncomingMessage?: number; - maxOutgoingBackpressure?: number; - onUpgrade?(req: http.IncomingMessage, connection: WsServerConnection): void; - handler(ctx: WsConnectionContext, req: http.IncomingMessage): void; -} - -export interface Http1ServerOpts { - server: http.Server; - codecs?: RpcCodecs; - writer?: Writer; -} - -export class Http1Server implements Printable { - public static start(opts: http.ServerOptions = {}, port = 8000): Http1Server { - const rawServer = http.createServer(opts); - rawServer.listen(port); - const server = new Http1Server({server: rawServer}); - return server; - } - - public readonly codecs: RpcCodecs; - public readonly server: http.Server; - - constructor(protected readonly opts: Http1ServerOpts) { - this.server = opts.server; - const writer = opts.writer ?? new Writer(); - this.codecs = opts.codecs ?? new RpcCodecs(opts.codecs ?? new Codecs(writer), new RpcMessageCodecs()); - this.wsEncoder = new WsFrameEncoder(writer); - } - - public start(): void { - const server = this.server; - this.httpMatcher = this.httpRouter.compile(); - this.wsMatcher = this.wsRouter.compile(); - server.on('request', this.onRequest); - server.on('upgrade', this.onWsUpgrade); - server.on('clientError', (err, socket) => { - socket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); - }); - } - - // ------------------------------------------------------------- HTTP routing - - public onnotfound: Http1NotFoundHandler = (res) => { - res.writeHead(404, 'Not Found'); - res.end(); - }; - - public oninternalerror: Http1InternalErrorHandler = (error: unknown, res) => { - if (error instanceof PayloadTooLarge) { - res.statusCode = 413; - res.statusMessage = 'Payload Too Large'; - res.end(); - return; - } - res.statusCode = 500; - res.statusMessage = 'Internal Server Error'; - res.end(); - }; - - protected readonly httpRouter = new Router(); - protected httpMatcher: RouteMatcher = () => undefined; - - public route(def: Http1EndpointDefinition): void { - let path = def.path; - if (path[0] !== '/') path = '/' + path; - const method = def.method ? def.method.toUpperCase() : 'GET'; - const route = method + path; - Number(route); - const match = new Http1EndpointMatch(def.handler); - this.httpRouter.add(route, match); - } - - private readonly onRequest = async (req: http.IncomingMessage, res: http.ServerResponse) => { - try { - res.sendDate = false; - const url = req.url ?? ''; - const queryStartIndex = url.indexOf('?'); - let path = url; - let query = ''; - if (queryStartIndex >= 0) { - path = url.slice(0, queryStartIndex); - query = url.slice(queryStartIndex + 1); - } - const route = (req.method || '') + path; - const match = this.httpMatcher(route); - if (!match) { - this.onnotfound(res, req); - return; - } - const codecs = this.codecs; - const ip = this.findIp(req); - const token = this.findToken(req); - const ctx = new Http1ConnectionContext( - req, - res, - path, - query, - ip, - token, - match.params, - new NullObject(), - codecs.value.json, - codecs.value.json, - codecs.messages.compact, - ); - const headers = req.headers; - const contentType = headers['content-type']; - if (typeof contentType === 'string') setCodecs(ctx, contentType, codecs); - const handler = match.data.handler; - await handler(ctx); - } catch (error) { - this.oninternalerror(error, res, req); - } - }; - - // --------------------------------------------------------------- WebSockets - - protected readonly wsEncoder: WsFrameEncoder; - protected readonly wsRouter = new Router(); - protected wsMatcher: RouteMatcher = () => undefined; - - private readonly onWsUpgrade = (req: http.IncomingMessage, socket: net.Socket) => { - const url = req.url ?? ''; - const queryStartIndex = url.indexOf('?'); - let path = url; - let query = ''; - if (queryStartIndex >= 0) { - path = url.slice(0, queryStartIndex); - query = url.slice(queryStartIndex + 1); - } - const match = this.wsMatcher(path); - if (!match) { - socket.end(); - return; - } - const def = match.data; - const headers = req.headers; - const connection = new WsServerConnection(this.wsEncoder, socket as net.Socket); - connection.maxIncomingMessage = def.maxIncomingMessage ?? 2 * 1024 * 1024; - connection.maxBackpressure = def.maxOutgoingBackpressure ?? 2 * 1024 * 1024; - if (def.onUpgrade) def.onUpgrade(req, connection); - else { - const secWebSocketKey = headers['sec-websocket-key'] ?? ''; - const secWebSocketProtocol = headers['sec-websocket-protocol'] ?? ''; - const secWebSocketExtensions = headers['sec-websocket-extensions'] ?? ''; - connection.upgrade(secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions); - } - const codecs = this.codecs; - const ip = this.findIp(req); - const token = this.findToken(req); - const ctx = new WsConnectionContext( - connection, - path, - query, - ip, - token, - match.params, - new NullObject(), - codecs.value.json, - codecs.value.json, - codecs.messages.compact, - ); - const contentType = headers['content-type']; - if (typeof contentType === 'string') setCodecs(ctx, contentType, codecs); - else { - const secWebSocketProtocol = headers['sec-websocket-protocol'] ?? ''; - if (typeof secWebSocketProtocol === 'string') setCodecs(ctx, secWebSocketProtocol, codecs); - } - def.handler(ctx, req); - }; - - public ws(def: WsEndpointDefinition): void { - this.wsRouter.add(def.path, def); - } - - // ------------------------------------------------------- Context management - - public findIp(req: http.IncomingMessage): string { - const headers = req.headers; - const ip = headers['x-forwarded-for'] || headers['x-real-ip'] || req.socket.remoteAddress || ''; - return ip instanceof Array ? ip[0] : ip; - } - - /** - * Looks for an authentication token in the following places: - * - * 1. The `Authorization` header. - * 2. The URI query parameters. - * 3. The `Cookie` header. - * 4. The `Sec-Websocket-Protocol` header. - * - * @param req HTTP request - * @returns Authentication token, if any. - */ - public findToken(req: http.IncomingMessage): string { - let token: string = ''; - const headers = req.headers; - let header: string | string[] | undefined; - header = headers.authorization; - if (typeof header === 'string') token = findTokenInText(header); - if (token) return token; - const url = req.url; - if (typeof url === 'string') token = findTokenInText(url); - if (token) return token; - header = headers.cookie; - if (typeof header === 'string') token = findTokenInText(header); - if (token) return token; - header = headers['sec-websocket-protocol']; - if (typeof header === 'string') token = findTokenInText(header); - return token; - } - - // ------------------------------------------------------- High-level routing - - public enableHttpPing(path: string = '/ping') { - this.route({ - path, - handler: (ctx) => { - ctx.res.end('"pong"'); - }, - }); - } - - // ---------------------------------------------------------------- Printable - - public toString(tab: string = ''): string { - return ( - `${this.constructor.name}` + - printTree(tab, [ - (tab) => `HTTP ${this.httpRouter.toString(tab)}`, - (tab) => `WebSocket ${this.wsRouter.toString(tab)}`, - ]) - ); - } -} diff --git a/src/reactive-rpc/server/http1/RpcServer.ts b/src/reactive-rpc/server/http1/RpcServer.ts deleted file mode 100644 index 890d30bfe4..0000000000 --- a/src/reactive-rpc/server/http1/RpcServer.ts +++ /dev/null @@ -1,200 +0,0 @@ -import * as http from 'http'; -import {Printable} from '../../../util/print/types'; -import {printTree} from '../../../util/print/printTree'; -import {Http1Server} from './Http1Server'; -import {RpcError} from '../../common/rpc/caller'; -import { - IncomingBatchMessage, - ReactiveRpcClientMessage, - ReactiveRpcMessage, - RpcMessageBatchProcessor, - RpcMessageStreamProcessor, -} from '../../common'; -import {ConnectionContext, WsConnectionContext} from './context'; -import type {RpcCaller} from '../../common/rpc/caller/RpcCaller'; -import type {ServerLogger} from './types'; - -const DEFAULT_MAX_PAYLOAD = 4 * 1024 * 1024; - -export interface RpcServerOpts { - http1: Http1Server; - caller: RpcCaller; - logger?: ServerLogger; -} - -export interface RpcServerStartOpts extends Omit { - port?: number; - server?: http.Server; -} - -export class RpcServer implements Printable { - public static readonly create = (opts: RpcServerOpts) => { - const server = new RpcServer(opts); - opts.http1.enableHttpPing(); - return server; - }; - - public static readonly startWithDefaults = (opts: RpcServerStartOpts): RpcServer => { - const port = opts.port ?? 8080; - const logger = opts.logger ?? console; - const server = http.createServer(); - const http1Server = new Http1Server({ - server, - }); - const rpcServer = new RpcServer({ - caller: opts.caller, - http1: http1Server, - logger, - }); - rpcServer.enableDefaults(); - http1Server.start(); - server.listen(port, () => { - let host = server.address() || 'localhost'; - if (typeof host === 'object') host = (host as any).address; - logger.log({msg: 'SERVER_STARTED', host, port}); - }); - return rpcServer; - }; - - public readonly http1: Http1Server; - protected readonly batchProcessor: RpcMessageBatchProcessor; - - constructor(protected readonly opts: RpcServerOpts) { - const http1 = (this.http1 = opts.http1); - const onInternalError = http1.oninternalerror; - http1.oninternalerror = (error, res, req) => { - if (error instanceof RpcError) { - res.statusCode = 400; - const data = JSON.stringify(error.toJson()); - res.end(data); - return; - } - onInternalError(error, res, req); - }; - this.batchProcessor = new RpcMessageBatchProcessor({caller: opts.caller}); - } - - public enableHttpPing(): void { - this.http1.enableHttpPing(); - } - - public enableCors(): void { - this.http1.route({ - method: 'OPTIONS', - path: '/{::\n}', - handler: (ctx) => { - const res = ctx.res; - res.writeHead(200, 'OK', { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Credentials': 'true', - // 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - // 'Access-Control-Allow-Headers': 'Content-Type', - // 'Access-Control-Max-Age': '86400', - }); - res.end(); - }, - }); - } - - public enableHttpRpc(path: string = '/rpc'): void { - const batchProcessor = this.batchProcessor; - const logger = this.opts.logger ?? console; - this.http1.route({ - method: 'POST', - path, - handler: async (ctx) => { - const res = ctx.res; - const body = await ctx.body(DEFAULT_MAX_PAYLOAD); - if (!res.socket) return; - try { - const messageCodec = ctx.msgCodec; - const incomingMessages = messageCodec.decodeBatch(ctx.reqCodec, body); - try { - const outgoingMessages = await batchProcessor.onBatch(incomingMessages as IncomingBatchMessage[], ctx); - if (!res.socket) return; - const resCodec = ctx.resCodec; - messageCodec.encodeBatch(resCodec, outgoingMessages); - const buf = resCodec.encoder.writer.flush(); - if (!res.socket) return; - res.end(buf); - } catch (error) { - logger.error('HTTP_RPC_PROCESSING', error, {messages: incomingMessages}); - throw RpcError.from(error); - } - } catch (error) { - if (typeof error === 'object' && error) - if ((error as any).message === 'Invalid JSON') throw RpcError.badRequest(); - throw RpcError.from(error); - } - }, - }); - } - - public enableWsRpc(path: string = '/rpc'): void { - const opts = this.opts; - const logger = opts.logger ?? console; - const caller = opts.caller; - this.http1.ws({ - path, - maxIncomingMessage: 2 * 1024 * 1024, - maxOutgoingBackpressure: 2 * 1024 * 1024, - handler: (ctx: WsConnectionContext, req: http.IncomingMessage) => { - const connection = ctx.connection; - const reqCodec = ctx.reqCodec; - const resCodec = ctx.resCodec; - const msgCodec = ctx.msgCodec; - const encoder = resCodec.encoder; - const rpc = new RpcMessageStreamProcessor({ - caller, - send: (messages: ReactiveRpcMessage[]) => { - try { - const writer = encoder.writer; - writer.reset(); - msgCodec.encodeBatch(resCodec, messages); - const encoded = writer.flush(); - connection.sendBinMsg(encoded); - } catch (error) { - logger.error('WS_SEND', error, {messages}); - connection.close(); - } - }, - bufferSize: 1, - bufferTime: 0, - }); - connection.onmessage = (uint8: Uint8Array, isUtf8: boolean) => { - let messages: ReactiveRpcClientMessage[]; - try { - messages = msgCodec.decodeBatch(reqCodec, uint8) as ReactiveRpcClientMessage[]; - } catch (error) { - logger.error('RX_RPC_DECODING', error, {codec: reqCodec.id, buf: Buffer.from(uint8).toString('base64')}); - connection.close(); - return; - } - try { - rpc.onMessages(messages, ctx); - } catch (error) { - logger.error('RX_RPC_PROCESSING', error, messages!); - connection.close(); - return; - } - }; - connection.onclose = (code: number, reason: string) => { - rpc.stop(); - }; - }, - }); - } - - public enableDefaults(): void { - this.enableCors(); - this.enableHttpPing(); - this.enableHttpRpc(); - this.enableWsRpc(); - } - - // ---------------------------------------------------------------- Printable - - public toString(tab: string = ''): string { - return `${this.constructor.name}` + printTree(tab, [(tab) => this.http1.toString(tab)]); - } -} diff --git a/src/reactive-rpc/server/http1/context.ts b/src/reactive-rpc/server/http1/context.ts deleted file mode 100644 index d1f3513db9..0000000000 --- a/src/reactive-rpc/server/http1/context.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {getBody} from './util'; -import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; -import type * as http from 'http'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; -import type {RpcMessageCodec} from '../../common/codec/types'; -import type {WsServerConnection} from '../ws/server/WsServerConnection'; - -export interface ConnectionContext> { - path: string; - query: string; - ip: string; - token: string; - params: string[] | null; - meta: Meta; - reqCodec: JsonValueCodec; - resCodec: JsonValueCodec; - msgCodec: RpcMessageCodec; -} - -export class Http1ConnectionContext> implements ConnectionContext { - constructor( - public readonly req: http.IncomingMessage, - public readonly res: http.ServerResponse, - public path: string, - public query: string, - public readonly ip: string, - public token: string, - public readonly params: string[] | null, - public readonly meta: Meta, - public reqCodec: JsonValueCodec, - public resCodec: JsonValueCodec, - public msgCodec: RpcMessageCodec, - ) {} - - public async body(maxPayload: number): Promise { - const list = await getBody(this.req, maxPayload); - const bodyUint8 = listToUint8(list); - return bodyUint8; - } -} - -export class WsConnectionContext> implements ConnectionContext { - constructor( - public readonly connection: WsServerConnection, - public path: string, - public query: string, - public readonly ip: string, - public token: string, - public readonly params: string[] | null, - public readonly meta: Meta, - public reqCodec: JsonValueCodec, - public resCodec: JsonValueCodec, - public msgCodec: RpcMessageCodec, - ) {} -} diff --git a/src/reactive-rpc/server/http1/errors.ts b/src/reactive-rpc/server/http1/errors.ts deleted file mode 100644 index 01fff751a0..0000000000 --- a/src/reactive-rpc/server/http1/errors.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class PayloadTooLarge extends Error { - constructor() { - super('TOO_LARGE'); - } -} diff --git a/src/reactive-rpc/server/http1/types.ts b/src/reactive-rpc/server/http1/types.ts deleted file mode 100644 index dcb64f5180..0000000000 --- a/src/reactive-rpc/server/http1/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ServerLogger { - log(msg: unknown): void; - error(kind: string, error?: Error | unknown | null, meta?: unknown): void; -} diff --git a/src/reactive-rpc/server/http1/util.ts b/src/reactive-rpc/server/http1/util.ts deleted file mode 100644 index 7c45acea16..0000000000 --- a/src/reactive-rpc/server/http1/util.ts +++ /dev/null @@ -1,96 +0,0 @@ -import {PayloadTooLarge} from './errors'; -import type {ConnectionContext} from './context'; -import type * as http from 'http'; -import type {RpcCodecs} from '../../common/codec/RpcCodecs'; - -export const getBody = (request: http.IncomingMessage, max: number): Promise => { - return new Promise((resolve, reject) => { - let size: number = 0; - const chunks: Buffer[] = []; - request.on('error', (error) => { - request.removeAllListeners(); - reject(error); - }); - request.on('data', (chunk) => { - size += chunk.length; - if (size > max) { - request.removeAllListeners(); - reject(new PayloadTooLarge()); - return; - } - chunks.push(chunk); - }); - request.on('end', () => { - // request.removeAllListeners(); - resolve(chunks); - }); - }); -}; - -const REGEX_AUTH_TOKEN_SPECIFIER = /tkn\.([a-zA-Z0-9\-_]+)(?:[^a-zA-Z0-9\-_]|$)/; - -export const findTokenInText = (text: string): string => { - const match = REGEX_AUTH_TOKEN_SPECIFIER.exec(text); - if (!match) return ''; - return match[1] || ''; -}; - -const REGEX_CODECS_SPECIFIER = /rpc\.(\w{0,32})\.(\w{0,32})\.(\w{0,32})(?:\-(\w{0,32}))?/; - -/** - * @param specifier A string which may contain a codec specifier. For example: - * - `rpc.rx.compact.cbor` for Rx-RPC with compact messages and CBOR values. - * - `rpc.json2.verbose.json` for JSON-RPC 2.0 with verbose messages encoded as JSON. - */ -export const setCodecs = (ctx: ConnectionContext, specifier: string, codecs: RpcCodecs): void => { - const match = REGEX_CODECS_SPECIFIER.exec(specifier); - if (!match) return; - const [, protocol, messageFormat, request, response] = match; - switch (protocol) { - case 'rx': { - switch (messageFormat) { - case 'compact': { - ctx.msgCodec = codecs.messages.compact; - break; - } - case 'binary': { - ctx.msgCodec = codecs.messages.binary; - break; - } - } - break; - } - case 'json2': { - ctx.msgCodec = codecs.messages.jsonRpc2; - break; - } - } - switch (request) { - case 'cbor': { - ctx.resCodec = ctx.reqCodec = codecs.value.cbor; - break; - } - case 'json': { - ctx.resCodec = ctx.reqCodec = codecs.value.json; - break; - } - case 'msgpack': { - ctx.resCodec = ctx.reqCodec = codecs.value.msgpack; - break; - } - } - switch (response) { - case 'cbor': { - ctx.resCodec = codecs.value.cbor; - break; - } - case 'json': { - ctx.resCodec = codecs.value.json; - break; - } - case 'msgpack': { - ctx.resCodec = codecs.value.msgpack; - break; - } - } -}; diff --git a/src/reactive-rpc/server/index.ts b/src/reactive-rpc/server/index.ts deleted file mode 100644 index f43635100e..0000000000 --- a/src/reactive-rpc/server/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './uws'; diff --git a/src/reactive-rpc/server/uws/RpcApp.ts b/src/reactive-rpc/server/uws/RpcApp.ts deleted file mode 100644 index f633bb0708..0000000000 --- a/src/reactive-rpc/server/uws/RpcApp.ts +++ /dev/null @@ -1,283 +0,0 @@ -import {enableCors} from './util'; -import {Match, Router} from '../../../util/router'; -import {IncomingBatchMessage, RpcMessageBatchProcessor} from '../../common/rpc/RpcMessageBatchProcessor'; -import {RpcError, RpcErrorCodes, RpcErrorType} from '../../common/rpc/caller/error'; -import {ConnectionContext} from '../context'; -import {RpcMessageCodecs} from '../../common/codec/RpcMessageCodecs'; -import {RpcValue} from '../../common/messages/Value'; -import {RpcCodecs} from '../../common/codec/RpcCodecs'; -import {Codecs} from '@jsonjoy.com/json-pack/lib/codecs/Codecs'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {copy} from '@jsonjoy.com/util/lib/buffers/copy'; -import {type ReactiveRpcMessage, RpcMessageStreamProcessor, ReactiveRpcClientMessage} from '../../common'; -import type * as types from './types'; -import type {RouteHandler} from './types'; -import type {RpcCaller} from '../../common/rpc/caller/RpcCaller'; -import type {JsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/types'; - -const HDR_BAD_REQUEST = Buffer.from('400 Bad Request', 'utf8'); -const HDR_NOT_FOUND = Buffer.from('404 Not Found', 'utf8'); -const ERR_NOT_FOUND = RpcError.fromCode(RpcErrorCodes.NOT_FOUND, 'Not Found'); - -const noop = (x: any) => {}; - -export interface RpcAppOptions { - uws: types.TemplatedApp; - caller: RpcCaller; - - /** - * Maximum request body size in bytes. Default is 1MB. - */ - maxRequestBodySize?: number; - - /** - * Serializers and de-serializers for request and response bodies. - */ - codecs?: Codecs; - - /** - * HTTP port to listen on. If not specified, the PORT environment variable - * will be used, or 9999 if not set. - */ - port?: number; - - /** - * Host to listen to. If not specified, the HOST environment variable will be - * used, or '0.0.0.0' if not set. - */ - host?: string; - - /** - * This method allows to augment connection context with additional data. - * - * @param ctx Connection context. - */ - augmentContext?: (ctx: ConnectionContext) => void; - - /** - * Logger to use for logging. If not specified, console will be used. - */ - logger?: types.ServerLogger; -} - -export class RpcApp { - public readonly codecs: RpcCodecs; - protected readonly app: types.TemplatedApp; - protected readonly maxRequestBodySize: number; - protected readonly router = new Router(); - protected readonly batchProcessor: RpcMessageBatchProcessor; - - constructor(protected readonly options: RpcAppOptions) { - this.app = options.uws; - (this.maxRequestBodySize = options.maxRequestBodySize ?? 1024 * 1024), - (this.codecs = new RpcCodecs(options.codecs ?? new Codecs(new Writer()), new RpcMessageCodecs())); - this.batchProcessor = new RpcMessageBatchProcessor({caller: options.caller}); - } - - public enableCors() { - enableCors(this.options.uws); - } - - public routeRaw(method: types.HttpMethodPermissive, path: string, handler: RouteHandler): void { - method = method.toLowerCase() as types.HttpMethodPermissive; - this.router.add(method + path, handler); - } - - public route(method: types.HttpMethodPermissive, path: string, handler: types.JsonRouteHandler): void { - this.routeRaw(method, path, async (ctx: Ctx) => { - const result = await handler(ctx); - const res = ctx.res!; - if (res.aborted) return; - const codec = ctx.resCodec; - const encoder = codec.encoder; - const writer = encoder.writer; - writer.reset(); - if (res instanceof RpcValue) { - if (res.type) res.type.encoder(codec.format)(res.data, encoder); - else encoder.writeAny(res.data); - } else { - encoder.writeAny(result); - } - if (res.aborted) return; - ctx.sendResponse(writer.flush()); - }); - } - - public enableHttpPing(path: string = '/ping'): this { - this.route('GET', path, async () => { - return 'pong'; - }); - return this; - } - - public enableHttpRpc(path: string = '/rpc'): this { - this.routeRaw('POST', path, async (ctx: Ctx) => { - try { - const res = ctx.res!; - const bodyUint8 = await ctx.requestBody(this.maxRequestBodySize); - if (res.aborted) return; - const messageCodec = ctx.msgCodec; - const incomingMessages = messageCodec.decodeBatch(ctx.reqCodec, bodyUint8); - try { - const outgoingMessages = await this.batchProcessor.onBatch(incomingMessages as IncomingBatchMessage[], ctx); - if (res.aborted) return; - const resCodec = ctx.resCodec; - messageCodec.encodeBatch(resCodec, outgoingMessages); - const buf = resCodec.encoder.writer.flush(); - if (res.aborted) return; - res.end(buf); - } catch (error) { - const logger = this.options.logger ?? console; - logger.error('HTTP_RPC_PROCESSING', error, {messages: incomingMessages}); - throw RpcError.internal(error); - } - } catch (error) { - if (typeof error === 'object' && error) - if ((error as any).message === 'Invalid JSON') throw RpcError.badRequest(); - throw RpcError.from(error); - } - }); - return this; - } - - public enableWsRpc(path: string = '/rpc'): this { - const maxBackpressure = 4 * 1024 * 1024; - const augmentContext = this.options.augmentContext ?? noop; - const options = this.options; - const logger = options.logger ?? console; - const caller = options.caller; - this.app.ws(path, { - idleTimeout: 0, - maxPayloadLength: 4 * 1024 * 1024, - upgrade: (res, req, context) => { - const secWebSocketKey = req.getHeader('sec-websocket-key'); - const secWebSocketProtocol = req.getHeader('sec-websocket-protocol'); - const secWebSocketExtensions = req.getHeader('sec-websocket-extensions'); - const ctx = ConnectionContext.fromWs(req, res, secWebSocketProtocol, null, this); - augmentContext(ctx); - /* This immediately calls open handler, you must not use res after this call */ - res.upgrade({ctx}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, context); - }, - open: (ws_: types.WebSocket) => { - try { - const ws = ws_ as types.RpcWebSocket; - const ctx = ws.ctx; - const resCodec = ctx.resCodec; - const msgCodec = ctx.msgCodec; - const encoder = resCodec.encoder; - ws.rpc = new RpcMessageStreamProcessor({ - caller, - send: (messages: ReactiveRpcMessage[]) => { - try { - if (ws.getBufferedAmount() > maxBackpressure) return; - const writer = encoder.writer; - writer.reset(); - msgCodec.encodeBatch(resCodec, messages); - const encoded = writer.flush(); - ws.send(encoded, true, false); - } catch (error) { - logger.error('WS_SEND', error, {messages}); - } - }, - bufferSize: 1, - bufferTime: 0, - }); - } catch (error) { - logger.error('RX_WS_OPEN', error); - } - }, - message: (ws_: types.WebSocket, buf: ArrayBuffer, isBinary: boolean) => { - try { - const ws = ws_ as types.RpcWebSocket; - const ctx = ws.ctx; - const reqCodec = ctx.reqCodec; - const msgCodec = ctx.msgCodec; - const uint8 = copy(new Uint8Array(buf)); - const rpc = ws.rpc!; - try { - const messages = msgCodec.decodeBatch(reqCodec, uint8) as ReactiveRpcClientMessage[]; - try { - rpc.onMessages(messages, ctx); - } catch (error) { - logger.error('RX_RPC_PROCESSING', error, messages); - return; - } - } catch (error) { - logger.error('RX_RPC_DECODING', error, {codec: reqCodec.id, buf: Buffer.from(uint8).toString()}); - } - } catch (error) { - logger.error('RX_WS_MESSAGE', error); - } - }, - close: (ws_: types.WebSocket, code: number, message: ArrayBuffer) => { - const ws = ws_ as types.RpcWebSocket; - ws.rpc!.stop(); - }, - }); - return this; - } - - public startRouting(): void { - const matcher = this.router.compile(); - const codecs = this.codecs; - let responseCodec: JsonValueCodec = codecs.value.json; - const options = this.options; - const augmentContext = options.augmentContext ?? noop; - const logger = options.logger ?? console; - this.app.any('/*', async (res: types.HttpResponse, req: types.HttpRequest) => { - try { - res.onAborted(() => { - res.aborted = true; - }); - const method = req.getMethod(); - const url = req.getUrl(); - try { - const match = matcher(method + url) as undefined | Match; - if (!match) { - res.cork(() => { - res.writeStatus(HDR_NOT_FOUND); - res.end(RpcErrorType.encode(responseCodec, ERR_NOT_FOUND)); - }); - return; - } - const handler = match.data as RouteHandler; - const params = match.params; - const ctx = ConnectionContext.fromReqRes(req, res, params, this) as Ctx; - responseCodec = ctx.resCodec; - augmentContext(ctx); - await handler(ctx); - } catch (err) { - if (err instanceof RpcValue) err = err.data; - if (!(err instanceof RpcError)) err = RpcError.from(err); - const error = err; - if (error.errno === RpcErrorCodes.INTERNAL_ERROR) { - logger.error('UWS_ROUTER_INTERNAL_ERROR', error, {originalError: error.originalError ?? null}); - } - res.cork(() => { - res.writeStatus(HDR_BAD_REQUEST); - res.end(RpcErrorType.encode(responseCodec, error)); - }); - } - } catch {} - }); - } - - public startWithDefaults(): void { - this.enableCors(); - this.enableHttpPing(); - this.enableHttpRpc(); - this.enableWsRpc(); - this.startRouting(); - const options = this.options; - const port = options.port ?? +(process.env.PORT || 9999); - const host = options.host ?? process.env.HOST ?? '0.0.0.0'; - const logger = options.logger ?? console; - this.options.uws.listen(host, port, (token) => { - if (token) { - logger.log({msg: 'SERVER_STARTED', url: `http://localhost:${port}`}); - } else { - logger.error('SERVER_START', new Error(`Failed to listen on ${port} port.`)); - } - }); - } -} diff --git a/src/reactive-rpc/server/uws/index.ts b/src/reactive-rpc/server/uws/index.ts deleted file mode 100644 index b9b0fc4c2a..0000000000 --- a/src/reactive-rpc/server/uws/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './RpcApp'; diff --git a/src/reactive-rpc/server/uws/types-uws.ts b/src/reactive-rpc/server/uws/types-uws.ts deleted file mode 100644 index 9a108d166f..0000000000 --- a/src/reactive-rpc/server/uws/types-uws.ts +++ /dev/null @@ -1,204 +0,0 @@ -export type RecognizedString = - | string - | ArrayBuffer - | Uint8Array - | Int8Array - | Uint16Array - | Int16Array - | Uint32Array - | Int32Array - | Float32Array - | Float64Array; - -export interface HttpRequest { - /** Returns the lowercased header value or empty string. */ - getHeader(lowerCaseKey: RecognizedString): string; - /** Returns the URL including initial /slash */ - getUrl(): string; - /** Returns the HTTP method, useful for "any" routes. */ - getMethod(): string; - /** Returns the raw querystring (the part of URL after ? sign) or empty string. */ - getQuery(): string; - /** Returns a decoded query parameter value or empty string. */ - getQuery(key: string): string; -} - -export interface HttpResponse { - /** - * Writes the HTTP status message such as "200 OK". - * This has to be called first in any response, otherwise - * it will be called automatically with "200 OK". - * - * If you want to send custom headers in a WebSocket - * upgrade response, you have to call writeStatus with - * "101 Switching Protocols" before you call writeHeader, - * otherwise your first call to writeHeader will call - * writeStatus with "200 OK" and the upgrade will fail. - * - * As you can imagine, we format outgoing responses in a linear - * buffer, not in a hash table. You can read about this in - * the user manual under "corking". - */ - writeStatus(status: RecognizedString): HttpResponse; - /** - * Writes key and value to HTTP response. - * See writeStatus and corking. - */ - writeHeader(key: RecognizedString, value: RecognizedString): HttpResponse; - /** Enters or continues chunked encoding mode. Writes part of the response. End with zero length write. Returns true if no backpressure was added. */ - write(chunk: RecognizedString): boolean; - /** Ends this response by copying the contents of body. */ - end(body?: RecognizedString, closeConnection?: boolean): HttpResponse; - /** Immediately force closes the connection. Any onAborted callback will run. */ - close(): HttpResponse; - /** - * Every HttpResponse MUST have an attached abort handler IF you do not respond - * to it immediately inside of the callback. Returning from an Http request handler - * without attaching (by calling onAborted) an abort handler is ill-use and will terminate. - * When this event emits, the response has been aborted and may not be used. - */ - onAborted(handler: () => void): HttpResponse; - /** - * Handler for reading data from POST and such requests. You MUST copy the - * data of chunk if isLast is not true. We Neuter ArrayBuffers on return, - * making it zero length. - */ - onData(handler: (chunk: ArrayBuffer, isLast: boolean) => void): HttpResponse; - /** Returns the remote IP address as text. */ - getRemoteAddressAsText(): ArrayBuffer; - /** Corking a response is a performance improvement in both CPU and network, as you ready the IO system for writing multiple chunks at once. - * By default, you're corked in the immediately executing top portion of the route handler. In all other cases, such as when returning from - * await, or when being called back from an async database request or anything that isn't directly executing in the route handler, you'll want - * to cork before calling writeStatus, writeHeader or just write. Corking takes a callback in which you execute the writeHeader, writeStatus and - * such calls, in one atomic IO operation. This is important, not only for TCP but definitely for TLS where each write would otherwise result - * in one TLS block being sent off, each with one send syscall. - * - * Example usage: - * - * res.cork(() => { - * res.writeStatus("200 OK").writeHeader("Some", "Value").write("Hello world!"); - * }); - */ - cork(cb: () => void): HttpResponse; - /** Upgrades a HttpResponse to a WebSocket. See UpgradeAsync, UpgradeSync example files. */ - upgrade( - userData: T, - secWebSocketKey: RecognizedString, - secWebSocketProtocol: RecognizedString, - secWebSocketExtensions: RecognizedString, - context: unknown, - ): void; - /** Arbitrary user data may be attached to this object */ - [key: string]: any; -} - -/** TemplatedApp is either an SSL or non-SSL app. See App for more info, read user manual. */ -export interface TemplatedApp { - /** Listens to hostname & port. Callback hands either false or a listen socket. */ - listen(host: RecognizedString, port: number, cb: (listenSocket: unknown) => void): TemplatedApp; - /** Registers an HTTP GET handler matching specified URL pattern. */ - get(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP POST handler matching specified URL pattern. */ - post(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP OPTIONS handler matching specified URL pattern. */ - options(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP DELETE handler matching specified URL pattern. */ - del(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP PATCH handler matching specified URL pattern. */ - patch(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP PUT handler matching specified URL pattern. */ - put(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP HEAD handler matching specified URL pattern. */ - head(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP CONNECT handler matching specified URL pattern. */ - connect(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP TRACE handler matching specified URL pattern. */ - trace(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers an HTTP handler matching specified URL pattern on any HTTP method. */ - any(pattern: RecognizedString, handler: (res: HttpResponse, req: HttpRequest) => void): TemplatedApp; - /** Registers a handler matching specified URL pattern where WebSocket upgrade requests are caught. */ - ws(pattern: RecognizedString, behavior: WebSocketBehavior): TemplatedApp; -} - -/** A structure holding settings and handlers for a WebSocket URL route handler. */ -export interface WebSocketBehavior { - /** Maximum length of received message. If a client tries to send you a message larger than this, the connection is immediately closed. Defaults to 16 * 1024. */ - maxPayloadLength?: number; - /** Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest. - * Disable by using 0. Defaults to 120. - */ - idleTimeout?: number; - /** What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB. Defaults to uWS.DISABLED. */ - compression?: number; - /** Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout. Defaults to 1024 * 1024. */ - maxBackpressure?: number; - /** Whether or not we should automatically send pings to uphold a stable connection given whatever idleTimeout. */ - sendPingsAutomatically?: boolean; - /** Upgrade handler used to intercept HTTP upgrade requests and potentially upgrade to WebSocket. - * See UpgradeAsync and UpgradeSync example files. - */ - upgrade?: (res: HttpResponse, req: HttpRequest, context: unknown) => void; - /** Handler for new WebSocket connection. WebSocket is valid from open to close, no errors. */ - open?: (ws: WebSocket) => void; - /** Handler for a WebSocket message. Messages are given as ArrayBuffer no matter if they are binary or not. Given ArrayBuffer is valid during the lifetime of this callback (until first await or return) and will be neutered. */ - message?: (ws: WebSocket, message: ArrayBuffer, isBinary: boolean) => void; - /** Handler for when WebSocket backpressure drains. Check ws.getBufferedAmount(). Use this to guide / drive your backpressure throttling. */ - drain?: (ws: WebSocket) => void; - /** Handler for close event, no matter if error, timeout or graceful close. You may not use WebSocket after this event. Do not send on this WebSocket from within here, it is closed. */ - close?: (ws: WebSocket, code: number, message: ArrayBuffer) => void; - /** Handler for received ping control message. You do not need to handle this, pong messages are automatically sent as per the standard. */ - ping?: (ws: WebSocket, message: ArrayBuffer) => void; - /** Handler for received pong control message. */ - pong?: (ws: WebSocket, message: ArrayBuffer) => void; -} - -/** A WebSocket connection that is valid from open to close event. - * Read more about this in the user manual. - */ -export interface WebSocket { - /** Sends a message. Returns 1 for success, 2 for dropped due to backpressure limit, and 0 for built up backpressure that will drain over time. You can check backpressure before or after sending by calling getBufferedAmount(). - * - * Make sure you properly understand the concept of backpressure. Check the backpressure example file. - */ - send(message: RecognizedString, isBinary?: boolean, compress?: boolean): number; - /** Returns the bytes buffered in backpressure. This is similar to the bufferedAmount property in the browser counterpart. - * Check backpressure example. - */ - getBufferedAmount(): number; - /** Gracefully closes this WebSocket. Immediately calls the close handler. - * A WebSocket close message is sent with code and shortMessage. - */ - end(code?: number, shortMessage?: RecognizedString): void; - /** Forcefully closes this WebSocket. Immediately calls the close handler. - * No WebSocket close message is sent. - */ - close(): void; - /** Sends a ping control message. Returns sendStatus similar to WebSocket.send (regarding backpressure). This helper function correlates to WebSocket::send(message, uWS::OpCode::PING, ...) in C++. */ - ping(message?: RecognizedString): number; - /** Subscribe to a topic. */ - subscribe(topic: RecognizedString): boolean; - /** Unsubscribe from a topic. Returns true on success, if the WebSocket was subscribed. */ - unsubscribe(topic: RecognizedString): boolean; - /** Returns whether this websocket is subscribed to topic. */ - isSubscribed(topic: RecognizedString): boolean; - /** Returns a list of topics this websocket is subscribed to. */ - getTopics(): string[]; - /** Publish a message under topic. Backpressure is managed according to maxBackpressure, closeOnBackpressureLimit settings. - * Order is guaranteed since v20. - */ - publish(topic: RecognizedString, message: RecognizedString, isBinary?: boolean, compress?: boolean): boolean; - /** See HttpResponse.cork. Takes a function in which the socket is corked (packing many sends into one single syscall/SSL block) */ - cork(cb: () => void): WebSocket; - /** Returns the remote IP address. Note that the returned IP is binary, not text. - * - * IPv4 is 4 byte long and can be converted to text by printing every byte as a digit between 0 and 255. - * IPv6 is 16 byte long and can be converted to text in similar ways, but you typically print digits in HEX. - * - * See getRemoteAddressAsText() for a text version. - */ - getRemoteAddress(): ArrayBuffer; - /** Returns the remote IP address as text. See RecognizedString. */ - getRemoteAddressAsText(): ArrayBuffer; - /** Arbitrary user data may be attached to this object. In C++ this is done by using getUserData(). */ - [key: string]: any; -} diff --git a/src/reactive-rpc/server/uws/types.ts b/src/reactive-rpc/server/uws/types.ts deleted file mode 100644 index dce0c2121b..0000000000 --- a/src/reactive-rpc/server/uws/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type {RpcMessageStreamProcessor} from '../../common'; -import type {ConnectionContext} from '../context'; -import * as uws from './types-uws'; - -export * from './types-uws'; - -export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options' | 'trace' | 'connect'; -export type HttpMethodPermissive = - | HttpMethod - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH' - | 'HEAD' - | 'OPTIONS' - | 'TRACE' - | 'CONNECT'; - -export type RouteHandler = (ctx: Ctx) => void; -export type JsonRouteHandler = (ctx: Ctx) => void; - -export interface RpcWebSocket extends uws.WebSocket { - ctx: Ctx; - rpc?: RpcMessageStreamProcessor; -} - -export interface ServerLogger { - log(msg: unknown): void; - error(kind: string, error?: Error | unknown | null, meta?: unknown): void; -} diff --git a/src/reactive-rpc/server/uws/util.ts b/src/reactive-rpc/server/uws/util.ts deleted file mode 100644 index 560af0a0d9..0000000000 --- a/src/reactive-rpc/server/uws/util.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {TemplatedApp} from './types'; - -export const enableCors = (uws: TemplatedApp) => { - const AccessControlAllowOrigin = Buffer.from('Access-Control-Allow-Origin'); - const AccessControlAllowOriginAllowAll = Buffer.from('*'); - const AccessControlAllowCredentials = Buffer.from('Access-Control-Allow-Credentials'); - const AccessControlAllowCredentialsTrue = Buffer.from('true'); - - uws.options('/*', (res, req) => { - res.cork(() => { - res.writeHeader(AccessControlAllowOrigin, AccessControlAllowOriginAllowAll); - res.writeHeader(AccessControlAllowCredentials, AccessControlAllowCredentialsTrue); - res.end(); - }); - }); -}; diff --git a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts deleted file mode 100644 index 8c97f3448d..0000000000 --- a/src/reactive-rpc/server/ws/codec/WsFrameDecoder.ts +++ /dev/null @@ -1,119 +0,0 @@ -import {StreamingOctetReader} from '@jsonjoy.com/util/lib/buffers/StreamingOctetReader'; -import {WsFrameOpcode} from './constants'; -import {WsFrameDecodingError} from './errors'; -import {WsCloseFrame, WsFrameHeader, WsPingFrame, WsPongFrame} from './frames'; - -export class WsFrameDecoder { - public readonly reader = new StreamingOctetReader(); - - public push(uint8: Uint8Array): void { - this.reader.push(uint8); - } - - public readFrameHeader(): WsFrameHeader | undefined { - try { - const reader = this.reader; - if (reader.size() < 2) return undefined; - const b0 = reader.u8(); - const b1 = reader.u8(); - const fin = <0 | 1>(b0 >>> 7); - const opcode = b0 & 0b1111; - const maskBit = b1 >>> 7; - let length = b1 & 0b01111111; - if (length === 126) { - if (reader.size() < 2) return undefined; - length = (reader.u8() << 8) | reader.u8(); - } else if (length === 127) { - if (reader.size() < 8) return undefined; - reader.skip(4); - length = reader.u32(); - } - let mask: undefined | [number, number, number, number]; - if (maskBit) { - if (reader.size() < 4) return undefined; - mask = [reader.u8(), reader.u8(), reader.u8(), reader.u8()]; - } - if (opcode >= WsFrameOpcode.MIN_CONTROL_OPCODE) { - switch (opcode) { - case WsFrameOpcode.CLOSE: { - return new WsCloseFrame(fin, opcode, length, mask, 0, ''); - } - case WsFrameOpcode.PING: { - if (length > 125) throw new WsFrameDecodingError(); - const data = mask ? reader.bufXor(length, mask, 0) : reader.buf(length); - return new WsPingFrame(fin, opcode, length, mask, data); - } - case WsFrameOpcode.PONG: { - if (length > 125) throw new WsFrameDecodingError(); - const data = mask ? reader.bufXor(length, mask, 0) : reader.buf(length); - return new WsPongFrame(fin, opcode, length, mask, data); - } - default: { - throw new WsFrameDecodingError(); - } - } - } - return new WsFrameHeader(fin, opcode, length, mask); - } catch (err) { - if (err instanceof RangeError) return undefined; - throw err; - } - } - - /** - * Read application data of a frame and copy it to the destination buffer. - * Receives the frame header and the number of bytes that still need to be - * copied, returns back the number of bytes that still need to be copied in - * subsequent calls. - * - * @param frame Frame header. - * @param remaining How many bytes are remaining to be copied. - * @param dst The destination buffer to write to. - * @param pos Position in the destination buffer to start writing to. - * @returns The number of bytes that still need to be copied in the next call. - */ - public readFrameData(frame: WsFrameHeader, remaining: number, dst: Uint8Array, pos: number): number { - const reader = this.reader; - const mask = frame.mask; - const readSize = Math.min(reader.size(), remaining); - if (!mask) reader.copy(readSize, dst, pos); - else { - const alreadyRead = frame.length - remaining; - reader.copyXor(readSize, dst, pos, mask, alreadyRead); - } - return remaining - readSize; - } - - public copyFrameData(frame: WsFrameHeader, dst: Uint8Array, pos: number): void { - const reader = this.reader; - const mask = frame.mask; - const readSize = frame.length; - if (!mask) reader.copy(readSize, dst, pos); - else reader.copyXor(readSize, dst, pos, mask, 0); - } - - /** - * Reads application data of the CLOSE frame and sets the code and reason - * properties of the frame. - * - * @param frame Close frame. - */ - public readCloseFrameData(frame: WsCloseFrame): void { - let length = frame.length; - if (length > 125) throw new WsFrameDecodingError(); - let code: number = 0; - let reason: string = ''; - if (length > 0) { - if (length < 2) throw new WsFrameDecodingError(); - const reader = this.reader; - const mask = frame.mask; - const octet1 = reader.u8() ^ (mask ? mask[0] : 0); - const octet2 = reader.u8() ^ (mask ? mask[1] : 0); - code = (octet1 << 8) | octet2; - length -= 2; - if (length) reason = reader.utf8(length, mask ?? [0, 0, 0, 0], 2); - } - frame.code = code; - frame.reason = reason; - } -} diff --git a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts b/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts deleted file mode 100644 index 1c3628b5eb..0000000000 --- a/src/reactive-rpc/server/ws/codec/WsFrameEncoder.ts +++ /dev/null @@ -1,126 +0,0 @@ -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {WsFrameOpcode} from './constants'; -import {WsFrameEncodingError} from './errors'; -import type {IWriter, IWriterGrowable} from '@jsonjoy.com/util/lib/buffers'; - -const maskBuf = new Uint8Array(4); -const maskBufView = new DataView(maskBuf.buffer, maskBuf.byteOffset, maskBuf.byteLength); - -export class WsFrameEncoder { - constructor(public readonly writer: W = new Writer() as any) {} - - public encodePing(data: Uint8Array | null): Uint8Array { - this.writePing(data); - return this.writer.flush(); - } - - public encodePong(data: Uint8Array | null): Uint8Array { - this.writePong(data); - return this.writer.flush(); - } - - public encodeClose(reason: string, code: number = 0): Uint8Array { - this.writeClose(reason, code); - return this.writer.flush(); - } - - public encodeHdr(fin: 0 | 1, opcode: WsFrameOpcode, length: number, mask: number): Uint8Array { - this.writeHdr(fin, opcode, length, mask); - return this.writer.flush(); - } - - public encodeDataMsgHdrFast(length: number): Uint8Array { - this.writeDataMsgHdrFast(length); - return this.writer.flush(); - } - - public writePing(data: Uint8Array | null): void { - let length = 0; - if (data && (length = data.length)) { - this.writeHdr(1, WsFrameOpcode.PING, length, 0); - this.writer.buf(data, length); - } else { - this.writeHdr(1, WsFrameOpcode.PING, 0, 0); - } - } - - public writePong(data: Uint8Array | null): void { - let length = 0; - if (data && (length = data.length)) { - this.writeHdr(1, WsFrameOpcode.PONG, length, 0); - this.writer.buf(data, length); - } else { - this.writeHdr(1, WsFrameOpcode.PONG, 0, 0); - } - } - - public writeClose(reason: string, code: number = 0): void { - if (reason || code) { - const reasonLength = reason.length; - const length = 2 + reasonLength; - const writer = this.writer; - writer.ensureCapacity( - 2 + // Frame header - 2 + // Close code 2 bytes - reasonLength * 4, // Close reason, max 4 bytes per UTF-8 char - ); - const lengthX = writer.x + 1; - this.writeHdr(1, WsFrameOpcode.CLOSE, length, 0); - writer.u16(code); - if (reasonLength) { - const utf8Length = writer.utf8(reason); - if (utf8Length !== reasonLength) { - if (utf8Length > 126 - 2) throw new WsFrameEncodingError(); - writer.uint8[lengthX] = (writer.uint8[lengthX] & 0b10000000) | (utf8Length + 2); - } - } - } else { - this.writeHdr(1, WsFrameOpcode.CLOSE, 0, 0); - } - } - - public writeHdr(fin: 0 | 1, opcode: WsFrameOpcode, length: number, mask: number): void { - const octet1 = (fin << 7) | opcode; - const maskBit = mask ? 0b10000000 : 0b00000000; - const writer = this.writer; - if (length < 126) { - const octet2 = maskBit | length; - writer.u16((octet1 << 8) | octet2); - } else if (length < 0x10000) { - const octet2 = maskBit | 126; - writer.u32(((octet1 << 8) | octet2) * 0x10000 + length); - } else { - const octet2 = maskBit | 127; - writer.u16((octet1 << 8) | octet2); - writer.u32(0); - writer.u32(length); - } - if (mask) writer.u32(mask); - } - - public writeDataMsgHdrFast(length: number): void { - const writer = this.writer; - if (length < 126) { - writer.u16(0b10000010_00000000 + length); - return; - } - if (length < 0x10000) { - writer.u32(0b10000010_01111110_00000000_00000000 + length); - return; - } - writer.u16(0b10000010_01111111); - writer.u32(0); - writer.u32(length); - } - - public writeBufXor(buf: Uint8Array, mask: number): void { - maskBufView.setUint32(0, mask, false); - const writer = this.writer; - const length = buf.length; - writer.ensureCapacity(length); - let x = writer.x; - const uint8 = writer.uint8; - for (let i = 0; i < length; i++) uint8[x++] = buf[i] ^ maskBuf[i & 3]; - writer.x = x; - } -} diff --git a/src/reactive-rpc/server/ws/codec/__tests__/decoder.spec.ts b/src/reactive-rpc/server/ws/codec/__tests__/decoder.spec.ts deleted file mode 100644 index d96ab60739..0000000000 --- a/src/reactive-rpc/server/ws/codec/__tests__/decoder.spec.ts +++ /dev/null @@ -1,362 +0,0 @@ -import {WsFrameDecoder} from '../WsFrameDecoder'; -import {WsFrameOpcode} from '../constants'; -import {WsCloseFrame, WsFrameHeader, WsPingFrame, WsPongFrame} from '../frames'; - -const {frame: WebSocketFrame} = require('websocket'); - -describe('data frames', () => { - test('can read final text packet with mask', () => { - const buf = Buffer.from( - new Uint8Array([ - 129, - 136, // Header - 136, - 35, - 93, - 205, // Mask - 231, - 85, - 56, - 191, - 177, - 19, - 109, - 253, // Payload - ]), - ); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(1); - expect(frame.length).toBe(8); - expect(frame.mask).toEqual([136, 35, 93, 205]); - expect(dst.toString()).toBe('over9000'); - }); - - test('can decode multiple chunks', () => { - const decoder = new WsFrameDecoder(); - const chunks: string[] = [ - 'gpbMadbAlzLn7P1F9LW4ALruvAC4p+5Frb2RNA==', - 'gv4IkyOW2h54zesyEbr4a1f/tjBT/7R5AbqhY366gS8PpfY8VuKzcg3ms3BEtPZlXsv2RRK67jIB4653T7iqd03x+DJY64cyeKf2Kw+0r2pK+vRuSvi9PA/tp0MPzesyFbr4a1f/tjBT/7R5AbqhY366gS8PofY8VuKzcg3ms3BEtPZlXsv2RRK64jIB4653T7iqd03x+DJY64cyeKf2Jw+0r2pK+vRuSvi9PA/tp0MPzesyEqb2PFbis3IN5rNwRLT2ZV7L9kUSuusvD7Svakr69G5K+L08D+2nQw/N6zISpPY8VuKzcg3ms3BEtPZlXsv2RRK66y0PtK9qSvr0bkr4vTwP7adDD83rMhKi9jxW4rNyDeazcES09mVey/ZFErrrKw+0r2pK+vRuSvi9PA/tp0MPzesyEqD2PFbis3IN5rNwRLT2ZV7L9kUSuuspD7Svakr69G5K+L08D+2nQw/N6zISrvY8VuKzcg3ms3BEtPZlXsv2RRK66ycPtK9qSvr0bkr4vTwP7adDD83rMhGm9jxW4rNyDeazcES09mVey/ZFErroLw+0r2pK+vRuSvi9PA/tp0MPzesyEaT2PFbis3IN5rNwRLT2ZV7L9kUSuugtD7Svakr69G5K+L08D+2nQw/N6zIRovY8VuKzcg3ms3BEtPZlXsv2RRK66CsPtK9qSvr0bkr4vTwP7adDD83rMhGg9jxW4rNyDeazcES09mVey/ZFErroKQ+0r2pK+vRuSvi9PA/tp0MPzesyEa72PFbis3IN5rNwRLT2ZV7L9kUSuugnD7Svakr69G5K+L08D+2nQw/N6zIQpvY8VuKzcg3ms3BEtPZlXsv2RRK66S8PtK9qSvr0bkr4vTwP7adDD83rMhCk9jxW4rNyDeazcES09mVey/ZFErrpLQ+0r2pK+vRuSvi9PA/tp0MPzesyEKL2PFbis3IN5rNwRLT2ZV7L9kUSuukrD7Svakr69G5K+L08D+2nQw/N6zIQoPY8VuKzcg3ms3BEtPZlXsv2RRK66SkPtK9qSvr0bkr4vTwP7adDD83rMhCu9jxW4rNyDeazcES09mVey/ZFErrpJw+0r2pK+vRuSvi9PA/tp0MPzesyF6b2PFbis3IN5rNwRLT2ZV7L9kUSuu4vD7Svakr69G5K+L08D+2nQw/N6zIXpPY8VuKzcg3ms3BEtPZlXsv2RRK67i0PtK9qSvr0bkr4vTwP7adDD83rMhei9jxW4rNyDeazcES09mVey/ZFErruKw+0r2pK+vRuSvi9PA/tp0MPzesyF6D2PFbis3IN5rNwRLT2ZV7L9kUSuu4pD7Svakr69G5K+L08D+2nQw/N6zIXrvY8VuKzcg3ms3BEtPZlXsv2RRK67icPtK9qSvr0bkr4vTwP7adDD83rMham9jxW4rNyDeazcES09mVey/ZFErrvLw+0r2pK+vRuSvi9PA/tp0MPzesyFqT2PFbis3IN5rNwRLT2ZV7L9kUSuu8tD7Svakr69G5K+L08D+2nQw/N6zIWovY8VuKzcg3ms3BEtPZlXsv2RRK67ysPtK9qSvr0bkr4vTwP7adDD83rMhag9jxW4rNyDeazcES09mVey/ZFErrvKQ+0r2pK+vRuSvi9PA/tp0MPzesyFq72PFbis3IN5rNwRLT2ZV7L9kUSuu8nD7Svakr69G5K+L08D+2nQw/N6zIVpvY8VuKzcg3ms3BEtPZlXsv2RRK67C8PtK9qSvr0bkr4vTwP7adDD83rMhWk9jxW4rNyDeazcES09mVey/ZFErrsLQ+0r2pK+vRuSvi9PA/tp0MPzesyFaL2PFbis3IN5rNwRLT2ZV7L9kUSuuwrD7Svakr69G5K+L08D+2nQw/N6zIVoPY8VuKzcg3ms3BEtPZlXsv2RRK67CkPtK9qSvr0bkr4vTwP7adDD83rMhWu9jxW4rNyDeazcES09mVey/ZFErrsJw+0r2pK+vRuSvi9PA/tp0MPzesyFKb2PFbis3IN5rNwRLT2ZV7L9kUSuu0vD7Svakr69G5K+L08D+2nQw/N6zIUpPY8VuKzcg3ms3BEtPZlXsv2RRK67S0PtK9qSvr0bkr4vTwP7adDD83rMhSi9jxW4rNyDeazcES09mVey/ZFErrtKw+0r2pK+vRuSvi9PA/tp0MPzesyFKD2PFbis3IN5rNwRLT2ZV7L9kUSuu0pD7Svakr69G5K+L08D+2nQw/N6zIUrvY8VuKzcg3ms3BEtPZlXsv2RRK67ScPtK9qSvr0bkr4vTwP7adDD83rMhum9jxW4rNyDeazcES09mVey/ZFErriLw+0r2pK+vRuSvi9PA/tp0MPzesyG6T2PFbis3IN5rNwRLT2ZV7L9kUSuuItD7Svakr69G5K+L08D+2nQw/N6zIbovY8VuKzcg3ms3BEtPZlXsv2RRK64isPtK9qSvr0bkr4vTwP7adDD83rMhug9jxW4rNyDeazcES09mVey/ZFErriKQ+0r2pK+vRuSvi9PA/tp0MPzesyG672PFbis3IN5rNwRLT2ZV7L9kUSuuInD7Svakr69G5K+L08D+2nQw/N6zIapvY8VuKzcg3ms3BEtPZlXsv2RRK64y8PtK9qSvr0bkr4vTwP7adDD83rMhqk9jxW4rNyDeazcES09mVey/ZFErrjLQ+0r2pK+vRuSvi9PA/tp0MPzesyGqL2PFbis3IN5rNwRLT2ZV7L9kUSuuMrD7Svakr69G5K+L08D+2nQw/N6zIaoPY8VuKzcg3ms3BEtPZlXsv2RRK64ykPtK9qSvr0bkr4vTwP7adDD83rMhqu9jxW4rNyDeazcES09mVey/ZFErrjJw+0r2pK+vRuSvi9PA/tp0MPzesyEqbqMgHjrndPuKp3TfH4MljrhzJ4p/YvE6f2PFbis3IN5rNwRLT2ZV7Lhw==', - 'gv4I/eI8WRu5Z2g30wxrN8BJLXKOEilyjFt7N5lBBDe5DXUq0g91OZdIMHfMTDB1hR51YJ9hdUDTEGgr1hB7bpZVNTWSVTd8wBAiZr8QAirODWkuzh4sb4tQd2uLUj45zkckRs5naDfTDG83wEktco4SKXKMW3s3mUEEN7kNdSrSC3U5l0gwd8xMMHWFHnVgn2F1QNMQaCvaEHtullU1NZJVN3zAECJmvxACKs4NaSLOHixvi1B3a4tSPjnORyRGzmdoN9MNaTfASS1yjhIpcoxbezeZQQQ3uQ11KtMNdTmXSDB3zEwwdYUedWCfYXVA0xBoKtAQe26WVTU1klU3fMAQIma/EAIqzg1oKM4eLG+LUHdri1I+Oc5HJEbOZ2g30w1tN8BJLXKOEilyjFt7N5lBBDe5DXUq0wl1OZdIMHfMTDB1hR51YJ9hdUDTEGgq1BB7bpZVNTWSVTd8wBAiZr8QAirODWgszh4sb4tQd2uLUj45zkckRs5naDfTDWE3wEktco4SKXKMW3s3mUEEN7kNdSrTBXU5l0gwd8xMMHWFHnVgn2F1QNMQaCnSEHtullU1NZJVN3zAECJmvxACKs4NayrOHixvi1B3a4tSPjnORyRGzmdoN9MOazfASS1yjhIpcoxbezeZQQQ3uQ11KtAPdTmXSDB3zEwwdYUedWCfYXVA0xBoKdYQe26WVTU1klU3fMAQIma/EAIqzg1rLs4eLG+LUHdri1I+Oc5HJEbOZ2g30w5vN8BJLXKOEilyjFt7N5lBBDe5DXUq0At1OZdIMHfMTDB1hR51YJ9hdUDTEGgp2hB7bpZVNTWSVTd8wBAiZr8QAirODWsizh4sb4tQd2uLUj45zkckRs5naDfTD2k3wEktco4SKXKMW3s3mUEEN7kNdSrRDXU5l0gwd8xMMHWFHnVgn2F1QNMQaCjQEHtullU1NZJVN3zAECJmvxACKs4NaijOHixvi1B3a4tSPjnORyRGzmdoN9MPbTfASS1yjhIpcoxbezeZQQQ3uQ11KtEJdTmXSDB3zEwwdYUedWCfYXVA0xBoKNQQe26WVTU1klU3fMAQIma/EAIqzg1qLM4eLG+LUHdri1I+Oc5HJEbOZ2g30w9hN8BJLXKOEilyjFt7N5lBBDe5DXUq0QV1OZdIMHfMTDB1hR51YJ9hdUDTEGgv0hB7bpZVNTWSVTd8wBAiZr8QAirODW0qzh4sb4tQd2uLUj45zkckRs5naDfTCGs3wEktco4SKXKMW3s3mUEEN7kNdSrWD3U5l0gwd8xMMHWFHnVgn2F1QNMQaC/WEHtullU1NZJVN3zAECJmvxACKs4NbS7OHixvi1B3a4tSPjnORyRGzmdoN9MIbzfASS1yjhIpcoxbezeZQQQ3uQ11KtYLdTmXSDB3zEwwdYUedWCfYXVA0xBoL9oQe26WVTU1klU3fMAQIma/EAIqzg1tIs4eLG+LUHdri1I+Oc5HJEbOZ2g30wlpN8BJLXKOEilyjFt7N5lBBDe5DXUq1w11OZdIMHfMTDB1hR51YJ9hdUDTEGgu0BB7bpZVNTWSVTd8wBAiZr8QAirODWwozh4sb4tQd2uLUj45zkckRs5naDfTCW03wEktco4SKXKMW3s3mUEEN7kNdSrXCXU5l0gwd8xMMHWFHnVgn2F1QNMQaC7UEHtullU1NZJVN3zAECJmvxACKs4NbCzOHixvi1B3a4tSPjnORyRGzmdoN9MJYTfASS1yjhIpcoxbezeZQQQ3uQ11KtcFdTmXSDB3zEwwdYUedWCfYXVA0xBoLdIQe26WVTU1klU3fMAQIma/EAIqzg1vKs4eLG+LUHdri1I+Oc5HJEbOZ2g30wprN8BJLXKOEilyjFt7N5lBBDe5DXUq1A91OZdIMHfMTDB1hR51YJ9hdUDTEGgt1hB7bpZVNTWSVTd8wBAiZr8QAirODW8uzh4sb4tQd2uLUj45zkckRs5naDfTCm83wEktco4SKXKMW3s3mUEEN7kNdSrUC3U5l0gwd8xMMHWFHnVgn2F1QNMQaC3aEHtullU1NZJVN3zAECJmvxACKs4NbyLOHixvi1B3a4tSPjnORyRGzmdoN9MLaTfASS1yjhIpcoxbezeZQQQ3uQ11KtUNdTmXSDB3zEwwdYUedWCfYXVA0xBoLNAQe26WVTU1klU3fMAQIma/EAIqzg1uKM4eLG+LUHdri1I+Oc5HJEbOZ2g30wttN8BJLXKOEilyjFt7N5lBBDe5DXUq1Ql1OZdIMHfMTDB1hR51YJ9hdUDTEGgs1BB7bpZVNTWSVTd8wBAiZr8QAirODW4szh4sb4tQd2uLUj45zkckRs5naDfTC2E3wEktco4SKXKMW3s3mUEEN7kNdSrVBXU5l0gwd8xMMHWFHnVgn2F1QNMQaCPSEHtullU1NZJVN3zAECJmvxACKs4NYSrOHixvi1B3a4tSPjnORyRGzmdoN9MEazfASS1yjhIpcoxbezeZQQQ3uQ11KtoPdTmXSDB3zEwwdYUedWCfYXVA0xBoI9YQe26WVTU1klU3fMAQIma/EAIqzg1hLs4eLG+LUHdri1I+Oc5HJEbOZ2g30wRvN8BJLXKOEilyjFt7N5lBBDe5DXUq2gt1OZdIMHfMTDB1hR51YJ9hdUDTEGgj2hB7bpZVNTWSVTd8wBAiZr8QAirODWEizh4sb4tQd2uLUj45zkckRs5naDfTBWk3wEktco4SKXKMW3s3mUEEN7kNdSrbDXU5l0gwd8xMMHWFHnVgn2F1QNMQaCLQEHtullU1NZJVN3zAECJmvxACKs4NYCjOHixvi1B3a4tSPjnORyRGzmdoN9MFbTfASS1yjhIpcoxbezeZQQQ3uQ11KtsJdTmXSDB3zEwwdYUedWCfYXVA0xBoItQQe26WVTU1klU3fMAQIma/EAIqzg1gLM4eLG+LUHdri1I+Oc5HJEbOZ2g30wVhN8BJLXKOEilyjFt7N5lBBDe5DXUq2wV1OZdIMHfMTDB1hR51YJ9hdUDTEGsr0hB7bpZVNTWSVTd8wBAiZr8QAirODmkqzh4sb4tQd2uLUj45zkckRr+C/gj9scVY1uqeafqD9Wr6k7Asv93rKL/fonr6yrgF+ur0dOSB9nT0xLExup+1MbjW53StzJh0jYDpauaF6Xqjxaw0+MGsNrGT6SOr7OkD5533aOOd5y2i2Kl2ptirP/SdviWLnZ5p+oP1bvqTsCy/3esov9+ievrKuAX66vR05IHydPTEsTG6n7UxuNbndK3MmHSNgOlq5onpeqPFrDT4waw2sZPpI6vs6QPnnfdo753nLaLYqXam2Ks/9J2+JYudnmn6g/Ro+pOwLL/d6yi/36J6+sq4Bfrq9HTkgPR09MSxMbqftTG41ud0rcyYdI2A6Wrng+l6o8WsNPjBrDaxk+kjq+zpA+ed92nlnectotipdqbYqz/0nb4li52eafqD9Gz6k7Asv93rKL/fonr6yrgF+ur0dOSA8HT0xLExup+1MbjW53StzJh0jYDpaueH6Xqjxaw0+MGsNrGT6SOr7OkD5533aeGd5y2i2Kl2ptirP/SdviWLnZ5p+oP0YPqTsCy/3esov9+ievrKuAX66vR05ID8dPTEsTG6n7UxuNbndK3MmHSNgOlq5IHpeqPFrDT4waw2sZPpI6vs6QPnnfdq553nLaLYqXam2Ks/9J2+JYudnmn6g/dq+pOwLL/d6yi/36J6+sq4Bfrq9HTkg/Z09MSxMbqftTG41ud0rcyYdI2A6Wrkhel6o8WsNPjBrDaxk+kjq+zpA+ed92rjnectotipdqbYqz/0nb4li52eafqD9276k7Asv93rKL/fonr6yrgF+ur0dOSD8nT0xLExup+1MbjW53StzJh0jYDpauSJ6Xqjxaw0+MGsNrGT6SOr7OkD5533au+d5y2i2Kl2ptirP/SdviWLnZ5p+oP2aPqTsCy/3esov9+ievrKuAX66vR05IL0dPTEsTG6n7UxuNbndK3MmHSNgOlq5YPpeqPFrDT4waw2sZPpI6vs6QPnnfdr5Z3nLaLYqXam2Ks/9J2+JYudnmn6g/Zs+pOwLL/d6yi/36J6+sq4Bfrq9HTkgvB09MSxMbqftTG41ud0rcyYdI2A6Wrlh+l6o8WsNPjBrDaxk+kjq+zpA+ed92vhnectotipdqbYqz/0nb4li52eafqD9mD6k7Asv93rKL/fonr6yrgF+ur0dOSC/HT0xLExup+1MbjW53StzJh0jYDpauKB6Xqjxaw0+MGsNrGT6SOr7OkD5533bOed5y2i2Kl2ptirP/SdviWLnZ5p+oPxavqTsCy/3esov9+ievrKuAX66vR05IX2dPTEsTG6n7UxuNbndK3MmHSNgOlq4oXpeqPFrDT4waw2sZPpI6vs6QPnnfds453nLaLYqXam2Ks/9J2+JYudnmn6g/Fu+pOwLL/d6yi/36J6+sq4Bfrq9HTkhfJ09MSxMbqftTG41ud0rcyYdI2A6Wriiel6o8WsNPjBrDaxk+kjq+zpA+ed92zvnectotipdqbYqz/0nb4li52eafqD8Gj6k7Asv93rKL/fonr6yrgF+ur0dOSE9HT0xLExup+1MbjW53StzJh0jYDpauOD6Xqjxaw0+MGsNrGT6SOr7OkD5533beWd5y2i2Kl2ptirP/SdviWLnZ5p+oPwbPqTsCy/3esov9+ievrKuAX66vR05ITwdPTEsTG6n7UxuNbndK3MmHSNgOlq44fpeqPFrDT4waw2sZPpI6vs6QPnnfdt4Z3nLaLYqXam2Ks/9J2+JYudnmn6g/Bg+pOwLL/d6yi/36J6+sq4Bfrq9HTkhPx09MSxMbqftTG41ud0rcyYdI2A6Wrggel6o8WsNPjBrDaxk+kjq+zpA+ed927nnectotipdqbYqz/0nb4li52eafqD82r6k7Asv93rKL/fonr6yrgF+ur0dOSH9nT0xLExup+1MbjW53StzJh0jYDpauCF6Xqjxaw0+MGsNrGT6SOr7OkD5533buOd5y2i2Kl2ptirP/SdviWLnZ5p+oPzbvqTsCy/3esov9+ievrKuAX66vR05IfydPTEsTG6n7UxuNbndK3MmHSNgOlq4InpeqPFrDT4waw2sZPpI6vs6QPnnfdu753nLaLYqXam2Ks/9J2+JYudnmn6g/Jo+pOwLL/d6yi/36J6+sq4Bfrq9HTkhvR09MSxMbqftTG41ud0rcyYdI2A6Wrhg+l6o8WsNPjBrDaxk+kjq+zpA+ed92/lnectotipdqbYqz/0nb4li52eafqD8mz6k7Asv93rKL/fonr6yrgF+ur0dOSG8HT0xLExup+1MbjW53StzJh0jYDpauGH6Xqjxaw0+MGsNrGT6SOr7OkD5533b+Gd5y2i2Kl2ptirP/SdviWLnZ5p+oPyYPqTsCy/3esov9+ievrKuAX66vR05Ib8dPTEsTG6n7UxuNbndK3MmHSNgOlq7oHpeqPFrDT4waw2sZPpI6vs6QPnnfdg553nLaLYqXam2Ks/9J2+JYudnmn6g/1q+pOwLL/d6yi/36J6+sq4Bfrq9HTkifZ09MSxMbqftTG41ud0rcyYdI2A6Wruhel6o8WsNPjBrDaxk+kjq+zpA+ed92DjnectotipdqbYqz/0nb4li52eafqD/W76k7Asv93rKL/fonr6yrgF+ur0dOSJ8nT0xLExup+1MbjW53StzJh0jYDpau6J6Xqjxaw0+MGsNrGT6SOr7OkD5533YO+d5y2i2Kl2ptirP/SdviWLnZ5p+oP8aPqTsCy/3esov9+ievrKuAX66vR05Ij0dPTEsTG6n7UxuNbndK3MmHSNgOlq74PpeqPFrDT4waw2sZPpI6vs6QPnnfdh5Z3nLaLYqXam2Ks/9J2+JYudnmn6g/xs+pOwLL/d6yi/36J6+sq4Bfrq9HTkiPB09MSxMbqftTG41ud0rcyYdI2A6Wrvh+l6o8WsNPjBrDaxk+kjq+zpA+ed92HhnectotipdqbYqz/0nb4li52eafqD/GD6k7Asv93rKL/fonr6yrgF+ur0dOSI/HT0xLExup+1MbjW53StzJh0jYDpa+aB6Xqjxaw0+MGsNrGT6SOr7OkD5532aOed5y2i2Kl2ptirP/SdviWL7A==', - 'gv4HgfpnciGhPEMNy1VCE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VCGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VDGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VAGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VBGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VGGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VHGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VEGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFENZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFE9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFEtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFFdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFFNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFF9ZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFFtZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFGdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VFGNZFB1WTC1xRkwkVA9YcD3zWPEMNy1VKEdZFB1WTC1xRkwkVA9YcD3zWPEMNy1VKENZFB1WTC1xRkwkVA9YcD3yn', - ]; - for (const chunk of chunks) { - const buf = Buffer.from(chunk, 'base64'); - decoder.push(buf); - } - const frames: WsFrameHeader[] = []; - const payloads: Uint8Array[] = []; - let currentFrame: WsFrameHeader | undefined; - while (true) { - if (currentFrame) { - const length = currentFrame.length; - if (length <= decoder.reader.size()) { - const buf = new Uint8Array(length); - decoder.copyFrameData(currentFrame, buf, 0); - payloads.push(buf); - currentFrame = undefined; - } else break; - } - const frame = decoder.readFrameHeader(); - if (!frame) break; - else if (frame instanceof WsFrameHeader) { - frames.push(frame); - if (frame.length) currentFrame = frame; - } - } - expect(frames.length).toBe(5); - expect(frames[0].fin).toBe(1); - expect(frames[1].fin).toBe(1); - expect(frames[2].fin).toBe(1); - expect(frames[3].fin).toBe(1); - expect(frames[4].fin).toBe(1); - expect(frames[0].opcode).toBe(2); - expect(frames[1].opcode).toBe(2); - expect(frames[2].opcode).toBe(2); - expect(frames[3].opcode).toBe(2); - expect(frames[4].opcode).toBe(2); - expect(frames[0].length).toBe(22); - expect(frames[1].length).toBe(2195); - expect(frames[2].length).toBe(2301); - expect(frames[3].length).toBe(2301); - expect(frames[4].length).toBe(1921); - expect(Buffer.from(payloads[0]).toString()).toBe('[[1,1,"util.ping",{}]]'); - expect(Buffer.from(payloads[1]).toString()).toBe( - '[[1,2,"util.ping",{}],[1,3,"util.ping",{}],[1,4,"util.ping",{}],[1,5,"util.ping",{}],[1,6,"util.ping",{}],[1,7,"util.ping",{}],[1,8,"util.ping",{}],[1,9,"util.ping",{}],[1,10,"util.ping",{}],[1,11,"util.ping",{}],[1,12,"util.ping",{}],[1,13,"util.ping",{}],[1,14,"util.ping",{}],[1,15,"util.ping",{}],[1,16,"util.ping",{}],[1,17,"util.ping",{}],[1,18,"util.ping",{}],[1,19,"util.ping",{}],[1,20,"util.ping",{}],[1,21,"util.ping",{}],[1,22,"util.ping",{}],[1,23,"util.ping",{}],[1,24,"util.ping",{}],[1,25,"util.ping",{}],[1,26,"util.ping",{}],[1,27,"util.ping",{}],[1,28,"util.ping",{}],[1,29,"util.ping",{}],[1,30,"util.ping",{}],[1,31,"util.ping",{}],[1,32,"util.ping",{}],[1,33,"util.ping",{}],[1,34,"util.ping",{}],[1,35,"util.ping",{}],[1,36,"util.ping",{}],[1,37,"util.ping",{}],[1,38,"util.ping",{}],[1,39,"util.ping",{}],[1,40,"util.ping",{}],[1,41,"util.ping",{}],[1,42,"util.ping",{}],[1,43,"util.ping",{}],[1,44,"util.ping",{}],[1,45,"util.ping",{}],[1,46,"util.ping",{}],[1,47,"util.ping",{}],[1,48,"util.ping",{}],[1,49,"util.ping",{}],[1,50,"util.ping",{}],[1,51,"util.ping",{}],[1,52,"util.ping",{}],[1,53,"util.ping",{}],[1,54,"util.ping",{}],[1,55,"util.ping",{}],[1,56,"util.ping",{}],[1,57,"util.ping",{}],[1,58,"util.ping",{}],[1,59,"util.ping",{}],[1,60,"util.ping",{}],[1,61,"util.ping",{}],[1,62,"util.ping",{}],[1,63,"util.ping",{}],[1,64,"util.ping",{}],[1,65,"util.ping",{}],[1,66,"util.ping",{}],[1,67,"util.ping",{}],[1,68,"util.ping",{}],[1,69,"util.ping",{}],[1,70,"util.ping",{}],[1,71,"util.ping",{}],[1,72,"util.ping",{}],[1,73,"util.ping",{}],[1,74,"util.ping",{}],[1,75,"util.ping",{}],[1,76,"util.ping",{}],[1,77,"util.ping",{}],[1,78,"util.ping",{}],[1,79,"util.ping",{}],[1,80,"util.ping",{}],[1,81,"util.ping",{}],[1,82,"util.ping",{}],[1,83,"util.ping",{}],[1,84,"util.ping",{}],[1,85,"util.ping",{}],[1,86,"util.ping",{}],[1,87,"util.ping",{}],[1,88,"util.ping",{}],[1,89,"util.ping",{}],[1,90,"util.ping",{}],[1,91,"util.ping",{}],[1,92,"util.ping",{}],[1,93,"util.ping",{}],[1,94,"util.ping",{}],[1,95,"util.ping",{}],[1,96,"util.ping",{}],[1,97,"util.ping",{}],[1,98,"util.ping",{}],[1,99,"util.ping",{}],[1,100,"util.ping",{}],[1,101,"util.ping",{}]]', - ); - expect(Buffer.from(payloads[2]).toString()).toBe( - '[[1,102,"util.ping",{}],[1,103,"util.ping",{}],[1,104,"util.ping",{}],[1,105,"util.ping",{}],[1,106,"util.ping",{}],[1,107,"util.ping",{}],[1,108,"util.ping",{}],[1,109,"util.ping",{}],[1,110,"util.ping",{}],[1,111,"util.ping",{}],[1,112,"util.ping",{}],[1,113,"util.ping",{}],[1,114,"util.ping",{}],[1,115,"util.ping",{}],[1,116,"util.ping",{}],[1,117,"util.ping",{}],[1,118,"util.ping",{}],[1,119,"util.ping",{}],[1,120,"util.ping",{}],[1,121,"util.ping",{}],[1,122,"util.ping",{}],[1,123,"util.ping",{}],[1,124,"util.ping",{}],[1,125,"util.ping",{}],[1,126,"util.ping",{}],[1,127,"util.ping",{}],[1,128,"util.ping",{}],[1,129,"util.ping",{}],[1,130,"util.ping",{}],[1,131,"util.ping",{}],[1,132,"util.ping",{}],[1,133,"util.ping",{}],[1,134,"util.ping",{}],[1,135,"util.ping",{}],[1,136,"util.ping",{}],[1,137,"util.ping",{}],[1,138,"util.ping",{}],[1,139,"util.ping",{}],[1,140,"util.ping",{}],[1,141,"util.ping",{}],[1,142,"util.ping",{}],[1,143,"util.ping",{}],[1,144,"util.ping",{}],[1,145,"util.ping",{}],[1,146,"util.ping",{}],[1,147,"util.ping",{}],[1,148,"util.ping",{}],[1,149,"util.ping",{}],[1,150,"util.ping",{}],[1,151,"util.ping",{}],[1,152,"util.ping",{}],[1,153,"util.ping",{}],[1,154,"util.ping",{}],[1,155,"util.ping",{}],[1,156,"util.ping",{}],[1,157,"util.ping",{}],[1,158,"util.ping",{}],[1,159,"util.ping",{}],[1,160,"util.ping",{}],[1,161,"util.ping",{}],[1,162,"util.ping",{}],[1,163,"util.ping",{}],[1,164,"util.ping",{}],[1,165,"util.ping",{}],[1,166,"util.ping",{}],[1,167,"util.ping",{}],[1,168,"util.ping",{}],[1,169,"util.ping",{}],[1,170,"util.ping",{}],[1,171,"util.ping",{}],[1,172,"util.ping",{}],[1,173,"util.ping",{}],[1,174,"util.ping",{}],[1,175,"util.ping",{}],[1,176,"util.ping",{}],[1,177,"util.ping",{}],[1,178,"util.ping",{}],[1,179,"util.ping",{}],[1,180,"util.ping",{}],[1,181,"util.ping",{}],[1,182,"util.ping",{}],[1,183,"util.ping",{}],[1,184,"util.ping",{}],[1,185,"util.ping",{}],[1,186,"util.ping",{}],[1,187,"util.ping",{}],[1,188,"util.ping",{}],[1,189,"util.ping",{}],[1,190,"util.ping",{}],[1,191,"util.ping",{}],[1,192,"util.ping",{}],[1,193,"util.ping",{}],[1,194,"util.ping",{}],[1,195,"util.ping",{}],[1,196,"util.ping",{}],[1,197,"util.ping",{}],[1,198,"util.ping",{}],[1,199,"util.ping",{}],[1,200,"util.ping",{}],[1,201,"util.ping",{}]]', - ); - expect(Buffer.from(payloads[3]).toString()).toBe( - '[[1,202,"util.ping",{}],[1,203,"util.ping",{}],[1,204,"util.ping",{}],[1,205,"util.ping",{}],[1,206,"util.ping",{}],[1,207,"util.ping",{}],[1,208,"util.ping",{}],[1,209,"util.ping",{}],[1,210,"util.ping",{}],[1,211,"util.ping",{}],[1,212,"util.ping",{}],[1,213,"util.ping",{}],[1,214,"util.ping",{}],[1,215,"util.ping",{}],[1,216,"util.ping",{}],[1,217,"util.ping",{}],[1,218,"util.ping",{}],[1,219,"util.ping",{}],[1,220,"util.ping",{}],[1,221,"util.ping",{}],[1,222,"util.ping",{}],[1,223,"util.ping",{}],[1,224,"util.ping",{}],[1,225,"util.ping",{}],[1,226,"util.ping",{}],[1,227,"util.ping",{}],[1,228,"util.ping",{}],[1,229,"util.ping",{}],[1,230,"util.ping",{}],[1,231,"util.ping",{}],[1,232,"util.ping",{}],[1,233,"util.ping",{}],[1,234,"util.ping",{}],[1,235,"util.ping",{}],[1,236,"util.ping",{}],[1,237,"util.ping",{}],[1,238,"util.ping",{}],[1,239,"util.ping",{}],[1,240,"util.ping",{}],[1,241,"util.ping",{}],[1,242,"util.ping",{}],[1,243,"util.ping",{}],[1,244,"util.ping",{}],[1,245,"util.ping",{}],[1,246,"util.ping",{}],[1,247,"util.ping",{}],[1,248,"util.ping",{}],[1,249,"util.ping",{}],[1,250,"util.ping",{}],[1,251,"util.ping",{}],[1,252,"util.ping",{}],[1,253,"util.ping",{}],[1,254,"util.ping",{}],[1,255,"util.ping",{}],[1,256,"util.ping",{}],[1,257,"util.ping",{}],[1,258,"util.ping",{}],[1,259,"util.ping",{}],[1,260,"util.ping",{}],[1,261,"util.ping",{}],[1,262,"util.ping",{}],[1,263,"util.ping",{}],[1,264,"util.ping",{}],[1,265,"util.ping",{}],[1,266,"util.ping",{}],[1,267,"util.ping",{}],[1,268,"util.ping",{}],[1,269,"util.ping",{}],[1,270,"util.ping",{}],[1,271,"util.ping",{}],[1,272,"util.ping",{}],[1,273,"util.ping",{}],[1,274,"util.ping",{}],[1,275,"util.ping",{}],[1,276,"util.ping",{}],[1,277,"util.ping",{}],[1,278,"util.ping",{}],[1,279,"util.ping",{}],[1,280,"util.ping",{}],[1,281,"util.ping",{}],[1,282,"util.ping",{}],[1,283,"util.ping",{}],[1,284,"util.ping",{}],[1,285,"util.ping",{}],[1,286,"util.ping",{}],[1,287,"util.ping",{}],[1,288,"util.ping",{}],[1,289,"util.ping",{}],[1,290,"util.ping",{}],[1,291,"util.ping",{}],[1,292,"util.ping",{}],[1,293,"util.ping",{}],[1,294,"util.ping",{}],[1,295,"util.ping",{}],[1,296,"util.ping",{}],[1,297,"util.ping",{}],[1,298,"util.ping",{}],[1,299,"util.ping",{}],[1,300,"util.ping",{}],[1,301,"util.ping",{}]]', - ); - }); - - test('can read final text packet without mask', () => { - const buf = Buffer.from(new Uint8Array([129, 8, 111, 118, 101, 114, 57, 48, 48, 48])); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(1); - expect(frame.length).toBe(8); - expect(frame.mask).toEqual(undefined); - expect(dst.toString()).toBe('over9000'); - }); - - test('can read final masked text frame', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = true; - frame0.binaryPayload = Buffer.from('hello world'); - frame0.opcode = 1; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(1); - expect(frame.length).toBe(11); - expect(frame.mask).toBeInstanceOf(Array); - expect(dst.toString()).toBe('hello world'); - }); - - test('can read non-final masked text frame', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = false; - frame0.mask = true; - frame0.binaryPayload = Buffer.from('hello world'); - frame0.opcode = 1; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - const slice1 = buf.slice(0, 2); - const slice2 = buf.slice(2, 6); - const slice3 = buf.slice(6, 10); - const slice4 = buf.slice(10); - decoder.push(slice1); - decoder.push(slice2); - decoder.push(slice3); - decoder.push(slice4); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(0); - expect(frame.opcode).toBe(1); - expect(frame.length).toBe(11); - expect(frame.mask).toBeInstanceOf(Array); - expect(dst.toString()).toBe('hello world'); - }); - - test('can read non-final masked binary frame', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = false; - frame0.mask = true; - frame0.binaryPayload = Buffer.from('hello world'); - frame0.opcode = 2; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(0); - expect(frame.opcode).toBe(2); - expect(frame.length).toBe(11); - expect(frame.mask).toBeInstanceOf(Array); - expect(dst.toString()).toBe('hello world'); - }); - - test('can read non-final non-masked binary frame', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = false; - frame0.mask = false; - frame0.binaryPayload = Buffer.from('hello world'); - frame0.opcode = 2; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - const dst = Buffer.alloc(frame.length); - let remaining = frame.length; - remaining = decoder.readFrameData(frame, remaining, dst, 0); - expect(frame.fin).toBe(0); - expect(frame.opcode).toBe(2); - expect(frame.length).toBe(11); - expect(frame.mask).toBe(undefined); - expect(dst.toString()).toBe('hello world'); - }); - - test('can decode a frame with a continuation frame', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = false; - frame0.mask = true; - frame0.binaryPayload = Buffer.from('hello '); - frame0.opcode = 2; - const frame1 = new WebSocketFrame(Buffer.alloc(4), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame1.fin = true; - frame1.mask = true; - frame1.binaryPayload = Buffer.from('world'); - frame1.opcode = 0; - const buf0 = frame0.toBuffer(); - const buf1 = frame1.toBuffer(); - const dst = Buffer.alloc(11); - const decoder = new WsFrameDecoder(); - decoder.push(buf0); - const header0 = decoder.readFrameHeader()!; - let remaining0 = header0.length; - remaining0 = decoder.readFrameData(header0, remaining0, dst, 0); - expect(header0.fin).toBe(0); - decoder.push(buf1); - const header1 = decoder.readFrameHeader()!; - let remaining1 = header1.length; - remaining1 = decoder.readFrameData(header1, remaining1, dst, 6); - expect(header1.fin).toBe(1); - expect(dst.toString()).toBe('hello world'); - }); -}); - -describe('control frames', () => { - test('can read CLOSE frame with masked UTF-8 payload', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = true; - frame0.binaryPayload = Buffer.from('something ๐Ÿคทโ€โ™‚๏ธ happened'); - frame0.closeStatus = 1000; - frame0.opcode = WsFrameOpcode.CLOSE; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(frame0.binaryPayload.length + 2); - expect(frame.mask).toBeInstanceOf(Array); - expect((frame as WsCloseFrame).code).toBe(0); - expect((frame as WsCloseFrame).reason).toBe(''); - decoder.readCloseFrameData(frame as WsCloseFrame); - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(frame0.binaryPayload.length + 2); - expect(frame.mask).toBeInstanceOf(Array); - expect((frame as WsCloseFrame).code).toBe(1000); - expect((frame as WsCloseFrame).reason).toBe('something ๐Ÿคทโ€โ™‚๏ธ happened'); - }); - - test('can read CLOSE frame with un-masked UTF-8 payload', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = false; - frame0.binaryPayload = Buffer.from('something ๐Ÿคทโ€โ™‚๏ธ happened'); - frame0.closeStatus = 1000; - frame0.opcode = WsFrameOpcode.CLOSE; - const buf = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(frame0.binaryPayload.length + 2); - expect(frame.mask).toBe(undefined); - expect((frame as WsCloseFrame).code).toBe(0); - expect((frame as WsCloseFrame).reason).toBe(''); - decoder.readCloseFrameData(frame as WsCloseFrame); - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(frame0.binaryPayload.length + 2); - expect(frame.mask).toBe(undefined); - expect((frame as WsCloseFrame).code).toBe(1000); - expect((frame as WsCloseFrame).reason).toBe('something ๐Ÿคทโ€โ™‚๏ธ happened'); - }); - - test('can read PING frame with masked bytes', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = true; - frame0.binaryPayload = new Uint8Array([1, 2, 3]); - frame0.opcode = WsFrameOpcode.PING; - const buf0 = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf0); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPingFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PING); - expect(frame.length).toBe(3); - expect(frame.mask).toBeInstanceOf(Array); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array([1, 2, 3])); - }); - - test('can read PING frame with un-masked bytes', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = false; - frame0.binaryPayload = Buffer.from(new Uint8Array([1, 2, 3])); - frame0.opcode = WsFrameOpcode.PING; - const buf0 = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf0); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPingFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PING); - expect(frame.length).toBe(3); - expect(frame.mask).toBe(undefined); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array([1, 2, 3])); - }); - - test('can read PONG frame with masked bytes', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = true; - frame0.binaryPayload = new Uint8Array([1, 2, 3]); - frame0.opcode = WsFrameOpcode.PONG; - const buf0 = frame0.toBuffer(); - const decoder = new WsFrameDecoder(); - decoder.push(buf0); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPongFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PONG); - expect(frame.length).toBe(3); - expect(frame.mask).toBeInstanceOf(Array); - expect((frame as WsPongFrame).data).toEqual(new Uint8Array([1, 2, 3])); - }); - - test('can read PONG frame with un-masked bytes', () => { - const frame0 = new WebSocketFrame(Buffer.alloc(256), Buffer.alloc(128), {maxReceivedFrameSize: 1000000}); - frame0.fin = true; - frame0.mask = false; - frame0.binaryPayload = Buffer.from(new Uint8Array([1, 2, 3])); - frame0.opcode = WsFrameOpcode.PONG; - const buf0 = frame0.toBuffer(); - const slice0 = buf0.slice(0, 2); - const slice1 = buf0.slice(2); - const decoder = new WsFrameDecoder(); - decoder.push(slice0); - decoder.push(slice1); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPongFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PONG); - expect(frame.length).toBe(3); - expect(frame.mask).toBe(undefined); - expect((frame as WsPongFrame).data).toEqual(new Uint8Array([1, 2, 3])); - }); -}); diff --git a/src/reactive-rpc/server/ws/codec/__tests__/encoder.spec.ts b/src/reactive-rpc/server/ws/codec/__tests__/encoder.spec.ts deleted file mode 100644 index dd24f9bf38..0000000000 --- a/src/reactive-rpc/server/ws/codec/__tests__/encoder.spec.ts +++ /dev/null @@ -1,208 +0,0 @@ -import {WsFrameDecoder} from '../WsFrameDecoder'; -import {WsFrameEncoder} from '../WsFrameEncoder'; -import {WsFrameOpcode} from '../constants'; -import {WsCloseFrame, WsFrameHeader, WsPingFrame, WsPongFrame} from '../frames'; - -describe('control frames', () => { - test('can encode an empty PING frame', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodePing(null); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPingFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PING); - expect(frame.length).toBe(0); - expect(frame.mask).toBeUndefined(); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array(0)); - }); - - test('can encode a PING frame with data', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodePing(new Uint8Array([1, 2, 3, 4])); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPingFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PING); - expect(frame.length).toBe(4); - expect(frame.mask).toBeUndefined(); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array([1, 2, 3, 4])); - }); - - test('can encode an empty PONG frame', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodePong(null); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPongFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PONG); - expect(frame.length).toBe(0); - expect(frame.mask).toBeUndefined(); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array(0)); - }); - - test('can encode a PONG frame with data', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodePong(new Uint8Array([1, 2, 3, 4])); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsPongFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.PONG); - expect(frame.length).toBe(4); - expect(frame.mask).toBeUndefined(); - expect((frame as WsPingFrame).data).toEqual(new Uint8Array([1, 2, 3, 4])); - }); - - test('can encode an empty CLOSE frame', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodeClose(''); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(0); - expect(frame.mask).toBeUndefined(); - }); - - test('can encode a CLOSE frame with code and reason', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodeClose('gg wp', 123); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - decoder.readCloseFrameData(frame as WsCloseFrame); - expect(frame).toBeInstanceOf(WsCloseFrame); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.CLOSE); - expect(frame.length).toBe(2 + 5); - expect(frame.mask).toBeUndefined(); - expect((frame as WsCloseFrame).code).toBe(123); - expect((frame as WsCloseFrame).reason).toBe('gg wp'); - }); -}); - -describe('data frames', () => { - test('can encode an empty BINARY data frame', () => { - const encoder = new WsFrameEncoder(); - const encoded = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 0, 0); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsFrameHeader); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame.length).toBe(0); - expect(frame.mask).toBeUndefined(); - }); - - test('can encode a BINARY data frame with data', () => { - const encoder = new WsFrameEncoder(); - encoder.writeHdr(1, WsFrameOpcode.BINARY, 5, 0); - encoder.writer.buf(new Uint8Array([1, 2, 3, 4, 5]), 5); - const encoded = encoder.writer.flush(); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsFrameHeader); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame.length).toBe(5); - expect(frame.mask).toBeUndefined(); - const data = decoder.reader.buf(5); - expect(data).toEqual(new Uint8Array([1, 2, 3, 4, 5])); - }); - - test('can encode a fast BINARY data frame with data', () => { - const encoder = new WsFrameEncoder(); - const data = new Uint8Array(333); - encoder.writeDataMsgHdrFast(data.length); - encoder.writer.buf(data, data.length); - const encoded = encoder.writer.flush(); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsFrameHeader); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame.length).toBe(data.length); - expect(frame.mask).toBeUndefined(); - const data2 = decoder.reader.buf(frame.length); - expect(data2).toEqual(data); - }); - - describe('can encode different message sizes', () => { - const sizes = [0, 1, 2, 125, 126, 127, 128, 129, 255, 1234, 65535, 65536, 65537, 7777777, 2 ** 31 - 1]; - const encoder = new WsFrameEncoder(); - const decoder = new WsFrameDecoder(); - for (const size of sizes) { - test(`size ${size}`, () => { - const encoded = encoder.encodeHdr(1, WsFrameOpcode.BINARY, size, 0); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsFrameHeader); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame.length).toBe(size); - }); - } - }); - - test('can encode a masked frame', () => { - const encoder = new WsFrameEncoder(); - const data = new Uint8Array([1, 2, 3, 4, 5]); - const mask = 123456789; - encoder.writeHdr(1, WsFrameOpcode.BINARY, data.length, mask); - encoder.writeBufXor(data, mask); - const encoded = encoder.writer.flush(); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame = decoder.readFrameHeader()!; - expect(frame).toBeInstanceOf(WsFrameHeader); - expect(frame.fin).toBe(1); - expect(frame.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame.length).toBe(data.length); - expect(frame.mask).toEqual([7, 91, 205, 21]); - const data2 = decoder.reader.bufXor(frame.length, frame.mask!, 0); - expect(data2).toEqual(data); - }); - - test('can encode and decode a fragmented message', () => { - const encoder = new WsFrameEncoder(); - const data1 = new Uint8Array([1, 2, 3]); - const data2 = new Uint8Array([4, 5]); - const mask1 = 333444555; - const mask2 = 123123123; - encoder.writeHdr(0, WsFrameOpcode.BINARY, data1.length, mask1); - encoder.writeBufXor(data1, mask1); - encoder.writeHdr(1, WsFrameOpcode.CONTINUE, data2.length, mask2); - encoder.writeBufXor(data2, mask2); - const encoded = encoder.writer.flush(); - const decoder = new WsFrameDecoder(); - decoder.push(encoded); - const frame0 = decoder.readFrameHeader()!; - expect(frame0).toBeInstanceOf(WsFrameHeader); - expect(frame0.fin).toBe(0); - expect(frame0.opcode).toBe(WsFrameOpcode.BINARY); - expect(frame0.length).toBe(data1.length); - expect(frame0.mask).toEqual([19, 223, 245, 203]); - const data3 = decoder.reader.bufXor(frame0.length, frame0.mask!, 0); - expect(data3).toEqual(data1); - const frame1 = decoder.readFrameHeader()!; - expect(frame1).toBeInstanceOf(WsFrameHeader); - expect(frame1.fin).toBe(1); - expect(frame1.opcode).toBe(WsFrameOpcode.CONTINUE); - expect(frame1.length).toBe(data2.length); - expect(frame1.mask).toEqual([7, 86, 181, 179]); - const data4 = decoder.reader.bufXor(frame1.length, frame1.mask!, 0); - expect(data4).toEqual(data2); - }); -}); diff --git a/src/reactive-rpc/server/ws/codec/constants.ts b/src/reactive-rpc/server/ws/codec/constants.ts deleted file mode 100644 index ccd1296261..0000000000 --- a/src/reactive-rpc/server/ws/codec/constants.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const enum WsFrameOpcode { - // Continuation fragment of a data frame - CONTINUE = 0, - - // Data frames - TEXT = 1, - BINARY = 2, - - // Control frames - MIN_CONTROL_OPCODE = 8, - CLOSE = 8, - PING = 9, - PONG = 10, -} diff --git a/src/reactive-rpc/server/ws/codec/errors.ts b/src/reactive-rpc/server/ws/codec/errors.ts deleted file mode 100644 index dec8534b17..0000000000 --- a/src/reactive-rpc/server/ws/codec/errors.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class WsFrameDecodingError extends Error { - constructor() { - super('WS_FRAME_DECODING'); - } -} - -export class WsFrameEncodingError extends Error { - constructor() { - super('WS_FRAME_ENCODING'); - } -} diff --git a/src/reactive-rpc/server/ws/codec/frames.ts b/src/reactive-rpc/server/ws/codec/frames.ts deleted file mode 100644 index e8c8f4e84f..0000000000 --- a/src/reactive-rpc/server/ws/codec/frames.ts +++ /dev/null @@ -1,45 +0,0 @@ -export class WsFrameHeader { - constructor( - public readonly fin: 0 | 1, - public readonly opcode: number, - public readonly length: number, - public readonly mask: undefined | [number, number, number, number], - ) {} -} - -export class WsPingFrame extends WsFrameHeader { - constructor( - fin: 0 | 1, - opcode: number, - length: number, - mask: undefined | [number, number, number, number], - public readonly data: Uint8Array, - ) { - super(fin, opcode, length, mask); - } -} - -export class WsPongFrame extends WsFrameHeader { - constructor( - fin: 0 | 1, - opcode: number, - length: number, - mask: undefined | [number, number, number, number], - public readonly data: Uint8Array, - ) { - super(fin, opcode, length, mask); - } -} - -export class WsCloseFrame extends WsFrameHeader { - constructor( - fin: 0 | 1, - opcode: number, - length: number, - mask: undefined | [number, number, number, number], - public code: number, - public reason: string, - ) { - super(fin, opcode, length, mask); - } -} diff --git a/src/reactive-rpc/server/ws/codec/index.ts b/src/reactive-rpc/server/ws/codec/index.ts deleted file mode 100644 index ddd48ff815..0000000000 --- a/src/reactive-rpc/server/ws/codec/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './constants'; -export * from './errors'; -export * from './frames'; -export * from './WsFrameDecoder'; diff --git a/src/reactive-rpc/server/ws/server/WsServerConnection.ts b/src/reactive-rpc/server/ws/server/WsServerConnection.ts deleted file mode 100644 index 1112093472..0000000000 --- a/src/reactive-rpc/server/ws/server/WsServerConnection.ts +++ /dev/null @@ -1,208 +0,0 @@ -import * as crypto from 'crypto'; -import * as stream from 'stream'; -import {utf8Size} from '@jsonjoy.com/util/lib/strings/utf8'; -import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; -import {WsCloseFrame, WsFrameDecoder, WsFrameHeader, WsFrameOpcode, WsPingFrame, WsPongFrame} from '../codec'; -import type {WsFrameEncoder} from '../codec/WsFrameEncoder'; - -export type WsServerConnectionSocket = stream.Duplex; - -export class WsServerConnection { - public closed: boolean = false; - public maxIncomingMessage: number = 2 * 1024 * 1024; - public maxBackpressure: number = 2 * 1024 * 1024; - - public readonly defaultOnPing = (data: Uint8Array | null): void => { - this.sendPong(data); - }; - - private _fragments: Uint8Array[] = []; - private _fragmentsSize: number = 0; - public readonly defaultOnFragment = (isLast: boolean, data: Uint8Array, isUtf8: boolean): void => { - const fragments = this._fragments; - this._fragmentsSize += data.length; - if (this._fragmentsSize > this.maxIncomingMessage) { - this.onClose(1009, 'TOO_LARGE'); - return; - } - fragments.push(data); - if (!isLast) return; - this._fragments = []; - this._fragmentsSize = 0; - const message = listToUint8(fragments); - this.onmessage(message, isUtf8); - }; - - public onmessage: (data: Uint8Array, isUtf8: boolean) => void = () => {}; - public onfragment: (isLast: boolean, data: Uint8Array, isUtf8: boolean) => void = this.defaultOnFragment; - public onping: (data: Uint8Array | null) => void = this.defaultOnPing; - public onpong: (data: Uint8Array | null) => void = () => {}; - public onclose: (code: number, reason: string) => void = () => {}; - - constructor( - protected readonly encoder: WsFrameEncoder, - public readonly socket: WsServerConnectionSocket, - ) { - const decoder = new WsFrameDecoder(); - let currentFrameHeader: WsFrameHeader | null = null; - let fragmentStartFrameHeader: WsFrameHeader | null = null; - const handleData = (data: Uint8Array): void => { - try { - decoder.push(data); - main: while (true) { - if (currentFrameHeader instanceof WsFrameHeader) { - const length = currentFrameHeader.length; - if (length > this.maxIncomingMessage) { - this.onClose(1009, 'TOO_LARGE'); - return; - } - if (length <= decoder.reader.size()) { - const buf = new Uint8Array(length); - decoder.copyFrameData(currentFrameHeader, buf, 0); - if (fragmentStartFrameHeader instanceof WsFrameHeader) { - const isText = fragmentStartFrameHeader.opcode === WsFrameOpcode.TEXT; - const isLast = currentFrameHeader.fin === 1; - currentFrameHeader = null; - if (isLast) fragmentStartFrameHeader = null; - this.onfragment(isLast, buf, isText); - } else { - const isText = currentFrameHeader.opcode === WsFrameOpcode.TEXT; - currentFrameHeader = null; - this.onmessage(buf, isText); - } - } else break; - } - const frame = decoder.readFrameHeader(); - if (!frame) break; - if (frame instanceof WsPingFrame) { - this.onping(frame.data); - continue main; - } - if (frame instanceof WsPongFrame) { - this.onpong(frame.data); - continue main; - } - if (frame instanceof WsCloseFrame) { - decoder.readCloseFrameData(frame); - this.onClose(frame.code, frame.reason); - continue main; - } - if (frame instanceof WsFrameHeader) { - if (fragmentStartFrameHeader) { - if (frame.opcode !== WsFrameOpcode.CONTINUE) { - this.onClose(1002, 'DATA'); - return; - } - currentFrameHeader = frame; - } - if (frame.fin === 0) { - fragmentStartFrameHeader = frame; - currentFrameHeader = frame; - continue main; - } - currentFrameHeader = frame; - continue main; - } - } - } catch (error) { - this.onClose(1002, 'DATA'); - } - }; - const handleClose = (hadError: boolean): void => { - if (this.closed) return; - this.onClose(hadError ? 1001 : 1002, 'END'); - }; - socket.on('data', handleData); - socket.on('close', handleClose); - } - - public close(): void { - const code = 1000; - const reason = 'CLOSE'; - const frame = this.encoder.encodeClose(reason, code); - this.socket.write(frame); - this.onClose(code, reason); - } - - private onClose(code: number, reason: string): void { - this.closed = true; - if (this.__writeTimer) { - clearImmediate(this.__writeTimer); - this.__writeTimer = null; - } - const socket = this.socket; - socket.removeAllListeners(); - if (!socket.destroyed) socket.destroy(); - this.onclose(code, reason); - } - - // ----------------------------------------------------------- Handle upgrade - - public upgrade(secWebSocketKey: string, secWebSocketProtocol: string, secWebSocketExtensions: string): void { - const accept = secWebSocketKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; - const acceptSha1 = crypto.createHash('sha1').update(accept).digest('base64'); - // prettier-ignore - this.socket.write( - 'HTTP/1.1 101 Switching Protocols\r\n' + - 'Upgrade: websocket\r\n' + - 'Connection: Upgrade\r\n' + - 'Sec-WebSocket-Accept: ' + acceptSha1 + '\r\n' + - (secWebSocketProtocol ? 'Sec-WebSocket-Protocol: ' + secWebSocketProtocol + '\r\n' : '') + - // 'Sec-WebSocket-Extensions: ""\r\n' + - '\r\n', - ); - } - - // ---------------------------------------------------------- Write to socket - - private __buffer: Uint8Array[] = []; - private __writeTimer: NodeJS.Immediate | null = null; - - public write(buf: Uint8Array): void { - if (this.closed) return; - this.__buffer.push(buf); - if (this.__writeTimer) return; - this.__writeTimer = setImmediate(() => { - this.__writeTimer = null; - const buffer = this.__buffer; - this.__buffer = []; - if (!buffer.length) return; - const socket = this.socket; - if (socket.writableLength > this.maxBackpressure) this.onClose(1009, 'BACKPRESSURE'); - // TODO: benchmark if corking helps - socket.cork(); - for (let i = 0, len = buffer.length; i < len; i++) socket.write(buffer[i]); - socket.uncork(); - }); - } - - // ------------------------------------------------- Write WebSocket messages - - public sendPing(data: Uint8Array | null): void { - const frame = this.encoder.encodePing(data); - this.write(frame); - } - - public sendPong(data: Uint8Array | null): void { - const frame = this.encoder.encodePong(data); - this.write(frame); - } - - public sendBinMsg(data: Uint8Array): void { - const encoder = this.encoder; - const header = encoder.encodeDataMsgHdrFast(data.length); - this.write(header); - this.write(data); - } - - public sendTxtMsg(txt: string): void { - const encoder = this.encoder; - const writer = encoder.writer; - const size = utf8Size(txt); - encoder.writeHdr(1, WsFrameOpcode.TEXT, size, 0); - writer.ensureCapacity(size); - writer.utf8(txt); - const buf = writer.flush(); - this.write(buf); - } -} diff --git a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts b/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts deleted file mode 100644 index c434c44be0..0000000000 --- a/src/reactive-rpc/server/ws/server/__tests__/WsServerConnection.spec.ts +++ /dev/null @@ -1,287 +0,0 @@ -import * as stream from 'stream'; -import {WsServerConnection} from '../WsServerConnection'; -import {WsFrameEncoder} from '../../codec/WsFrameEncoder'; -import {until} from 'thingies'; -import {WsFrameOpcode} from '../../codec'; -import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; -import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; - -const setup = () => { - const socket = new stream.PassThrough(); - const encoder = new WsFrameEncoder(); - const connection = new WsServerConnection(encoder, socket); - return {socket, encoder, connection}; -}; - -describe('.onping', () => { - test('can parse PING frame', async () => { - const {socket, encoder, connection} = setup(); - const pings: Uint8Array[] = []; - connection.onping = (data: Uint8Array | null): void => { - if (data) pings.push(data); - }; - const pingFrame = encoder.encodePing(Buffer.from([0x01, 0x02, 0x03])); - socket.write(pingFrame); - await until(() => pings.length === 1); - expect(pings[0]).toEqual(new Uint8Array([0x01, 0x02, 0x03])); - }); - - test('can parse empty PING frame', async () => { - const {socket, encoder, connection} = setup(); - const pings: Uint8Array[] = []; - connection.onping = (data: Uint8Array | null): void => { - if (data) pings.push(data); - }; - const pingFrame = encoder.encodePing(Buffer.from([0x01, 0x02, 0x03])); - socket.write(pingFrame); - const pingFrame2 = encoder.encodePing(Buffer.from([])); - socket.write(pingFrame2); - await until(() => pings.length === 2); - expect(pings[0]).toEqual(new Uint8Array([0x01, 0x02, 0x03])); - expect(pings[1]).toEqual(new Uint8Array([])); - }); -}); - -describe('.onping', () => { - test('can parse PONG frame', async () => { - const {socket, encoder, connection} = setup(); - const pongs: Uint8Array[] = []; - connection.onpong = (data: Uint8Array | null): void => { - if (data) pongs.push(data); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - socket.write(pingFrame); - await until(() => pongs.length === 1); - expect(pongs[0]).toEqual(new Uint8Array([0x01, 0x02, 0x03])); - }); -}); - -describe('.onclose', () => { - test('can parse CLOSE frame', async () => { - const {socket, encoder, connection} = setup(); - const closes: [code: number, reason: string][] = []; - connection.onclose = (code: number, reason: string): void => { - closes.push([code, reason]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - socket.write(pingFrame); - const closeFrame = encoder.encodeClose('OK', 1000); - socket.write(closeFrame); - await until(() => closes.length === 1); - expect(closes[0]).toEqual([1000, 'OK']); - }); -}); - -describe('.onmessage', () => { - describe('un-masked', () => { - test('binary data frame', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const frame = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const payload = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(frame); - socket.write(payload); - await until(() => messages.length === 1); - expect(messages[0]).toEqual([new Uint8Array([0x01, 0x02, 0x03]), false]); - }); - - test('two binary data frames', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const frame1 = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const payload1 = encoder.writer.flush(); - const frame2 = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x04, 0x05, 0x06]), 3); - const payload2 = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(listToUint8([frame1, payload1, frame2, payload2])); - await until(() => messages.length === 2); - expect(messages[0]).toEqual([new Uint8Array([0x01, 0x02, 0x03]), false]); - expect(messages[1]).toEqual([new Uint8Array([0x04, 0x05, 0x06]), false]); - }); - - test('errors when incoming message is too large', async () => { - const {socket, encoder, connection} = setup(); - connection.maxIncomingMessage = 3; - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const closes: [code: number, reason: string][] = []; - connection.onclose = (code: number, reason: string): void => { - closes.push([code, reason]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const frame1 = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const payload1 = encoder.writer.flush(); - const frame2 = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 4, 0); - encoder.writer.buf(Buffer.from([0x04, 0x05, 0x06, 0x07]), 4); - const payload2 = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(listToUint8([frame1, payload1, frame2, payload2])); - await until(() => messages.length === 1); - await until(() => closes.length === 1); - expect(messages[0]).toEqual([new Uint8Array([0x01, 0x02, 0x03]), false]); - expect(closes[0]).toEqual([1009, 'TOO_LARGE']); - }); - - test('text frame', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame1 = encoder.encodePing(Buffer.from([0x01])); - const pingFrame2 = encoder.encodePing(Buffer.from([0x01, 0x02, 0x03])); - const closeFrame = encoder.encodeHdr(1, WsFrameOpcode.TEXT, 4, 0); - encoder.writer.buf(Buffer.from('asdf'), 4); - const payload = encoder.writer.flush(); - socket.write(pingFrame1); - socket.write(pingFrame2); - socket.write(closeFrame); - socket.write(payload); - await until(() => messages.length === 1); - expect(messages[0]).toEqual([bufferToUint8Array(Buffer.from('asdf')), true]); - }); - }); - - describe('masked', () => { - test('binary data frame', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const closeFrame = encoder.encodeHdr(1, WsFrameOpcode.BINARY, 3, 0x12345678); - encoder.writeBufXor(Buffer.from([0x01, 0x02, 0x03]), 0x12345678); - const payload = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(closeFrame); - socket.write(payload); - await until(() => messages.length === 1); - expect(messages[0]).toEqual([new Uint8Array([0x01, 0x02, 0x03]), false]); - }); - - test('text frame', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame1 = encoder.encodePing(Buffer.from([0x01])); - const pingFrame2 = encoder.encodePing(Buffer.from([0x01, 0x02, 0x03])); - const closeFrame = encoder.encodeHdr(1, WsFrameOpcode.TEXT, 4, 0x12345678); - encoder.writeBufXor(Buffer.from('asdf'), 0x12345678); - const payload = encoder.writer.flush(); - socket.write(pingFrame1); - socket.write(pingFrame2); - socket.write(closeFrame); - socket.write(payload); - await until(() => messages.length === 1); - expect(messages[0]).toEqual([bufferToUint8Array(Buffer.from('asdf')), true]); - }); - }); -}); - -describe('.onfragment', () => { - test('parses out message fragments', async () => { - const {socket, encoder, connection} = setup(); - const fragments: [isLast: boolean, data: Uint8Array, isUtf8: boolean][] = []; - connection.onfragment = (isLast: boolean, data: Uint8Array, isUtf8: boolean): void => { - fragments.push([isLast, data, isUtf8]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const buf1 = encoder.encodeHdr(0, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const buf2 = encoder.writer.flush(); - const buf3 = encoder.encodeHdr(0, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x04, 0x05, 0x06]), 3); - const buf4 = encoder.writer.flush(); - const buf5 = encoder.encodeHdr(1, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x07, 0x08, 0x09]), 3); - const buf6 = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(buf1); - socket.write(buf2); - socket.write(buf3); - socket.write(buf4); - socket.write(buf5); - socket.write(buf6); - await until(() => fragments.length === 3); - expect(fragments).toEqual([ - [false, new Uint8Array([0x01, 0x02, 0x03]), false], - [false, new Uint8Array([0x04, 0x05, 0x06]), false], - [true, new Uint8Array([0x07, 0x08, 0x09]), false], - ]); - }); - - describe('when .onfragment is not defined', () => { - test('emits an .onmessage with fully assembled message', async () => { - const {socket, encoder, connection} = setup(); - const messages: [data: Uint8Array, isUtf8: boolean][] = []; - connection.onmessage = (data: Uint8Array, isUtf8: boolean): void => { - messages.push([data, isUtf8]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const buf1 = encoder.encodeHdr(0, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const buf2 = encoder.writer.flush(); - const buf3 = encoder.encodeHdr(0, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x04, 0x05, 0x06]), 3); - const buf4 = encoder.writer.flush(); - const buf5 = encoder.encodeHdr(1, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x07, 0x08, 0x09]), 3); - const buf6 = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(buf1); - socket.write(buf2); - socket.write(buf3); - socket.write(buf4); - socket.write(buf5); - socket.write(buf6); - await until(() => messages.length === 1); - expect(messages).toEqual([[new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]), false]]); - }); - - test('errors out when incoming message is too large', async () => { - const {socket, encoder, connection} = setup(); - connection.maxIncomingMessage = 8; - const closes: [code: number, reason: string][] = []; - connection.onclose = (code: number, reason: string): void => { - closes.push([code, reason]); - }; - const pingFrame = encoder.encodePong(Buffer.from([0x01, 0x02, 0x03])); - const buf1 = encoder.encodeHdr(0, WsFrameOpcode.BINARY, 3, 0); - encoder.writer.buf(Buffer.from([0x01, 0x02, 0x03]), 3); - const buf2 = encoder.writer.flush(); - const buf3 = encoder.encodeHdr(0, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x04, 0x05, 0x06]), 3); - const buf4 = encoder.writer.flush(); - const buf5 = encoder.encodeHdr(1, WsFrameOpcode.CONTINUE, 3, 0); - encoder.writer.buf(Buffer.from([0x07, 0x08, 0x09]), 3); - const buf6 = encoder.writer.flush(); - socket.write(pingFrame); - socket.write(buf1); - socket.write(buf2); - socket.write(buf3); - socket.write(buf4); - socket.write(buf5); - socket.write(buf6); - await until(() => closes.length === 1); - expect(closes).toEqual([[1009, 'TOO_LARGE']]); - }); - }); -}); diff --git a/src/server/__bench__/ping.bench.ts b/src/server/__bench__/ping.bench.ts deleted file mode 100644 index cf44cd261e..0000000000 --- a/src/server/__bench__/ping.bench.ts +++ /dev/null @@ -1,59 +0,0 @@ -// npx ts-node src/server/__bench__/ping.bench.ts - -/* tslint:disable no-console */ - -import {Suite} from 'benchmark'; -import {RpcPersistentClient, WebSocketChannel} from '../../reactive-rpc/common'; -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {BinaryRpcMessageCodec} from '../../reactive-rpc/common/codec/binary'; -import {CompactRpcMessageCodec} from '../../reactive-rpc/common/codec/compact'; -import {CborJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/cbor'; -import {JsonJsonValueCodec} from '@jsonjoy.com/json-pack/lib/codecs/json'; -import {RpcCodec} from '../../reactive-rpc/common/codec/RpcCodec'; -import {WebSocket} from 'ws'; - -const main = async () => { - const writer = new Writer(1024 * 4); - // const msg = new BinaryRpcMessageCodec(); - // const req = new CborJsonValueCodec(writer); - const msg = new CompactRpcMessageCodec(); - const req = new JsonJsonValueCodec(writer); - const codec = new RpcCodec(msg, req, req); - const client = new RpcPersistentClient({ - codec, - channel: { - newChannel: () => - new WebSocketChannel({ - newSocket: () => - new WebSocket('ws://localhost:9999/rpc', [codec.specifier()], {perMessageDeflate: false}) as any, - }), - }, - }); - client.start(); - - await client.call('util.ping', {}); // Warmup - - const suite = new Suite(); - suite - .add('fetch', async () => { - const res = await fetch('http://localhost:9999/ping', {keepalive: true}); - const pong = await res.text(); - if (pong !== '"pong"') throw new Error('Unexpected response'); - }) - .add('RpcPersistentClient', async () => { - const res = await client.call('util.ping', {}); - if (res !== 'pong') throw new Error('Unexpected response'); - }) - .on('cycle', (event: any) => { - console.log(String(event.target)); - }) - .on('complete', () => { - console.log('Fastest is ' + suite.filter('fastest').map('name')); - }) - .run({async: true}); -}; - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/src/server/__tests__/block.spec.ts b/src/server/__tests__/block.spec.ts deleted file mode 100644 index 4a52c1760c..0000000000 --- a/src/server/__tests__/block.spec.ts +++ /dev/null @@ -1,483 +0,0 @@ -import {Model} from '../../json-crdt'; -import {RpcErrorCodes} from '../../reactive-rpc/common/rpc/caller'; -import {setup} from './setup'; -import {tick, until} from '../../__tests__/util'; - -describe('block.*', () => { - describe('block.new', () => { - test('can create an empty block', async () => { - const {call} = setup(); - await call('block.new', {id: 'my-block', patches: []}); - const {model} = await call('block.get', {id: 'my-block'}); - expect(model).toMatchObject({ - id: 'my-block', - seq: -1, - blob: expect.any(Uint8Array), - created: expect.any(Number), - updated: expect.any(Number), - }); - const model2 = Model.fromBinary(model.blob); - expect(model2.view()).toBe(undefined); - }); - - test('can create a block with value', async () => { - const {call} = setup(); - const model = Model.withLogicalClock(); - model.api.root({ - name: 'Super Woman', - age: 25, - }); - const patch1 = model.api.flush(); - model.api.obj([]).set({ - age: 26, - }); - const patch2 = model.api.flush(); - await call('block.new', { - id: '123412341234', - patches: [ - { - blob: patch1.toBinary(), - }, - { - blob: patch2.toBinary(), - }, - ], - }); - const res = await call('block.get', {id: '123412341234'}); - expect(res.model).toMatchObject({ - id: '123412341234', - seq: 1, - blob: expect.any(Uint8Array), - created: expect.any(Number), - updated: expect.any(Number), - }); - const model2 = Model.fromBinary(res.model.blob); - expect(model2.view()).toStrictEqual({ - name: 'Super Woman', - age: 26, - }); - }); - }); - - describe('block.remove', () => { - test('can remove an existing block', async () => { - const {call} = setup(); - await call('block.new', {id: 'my-block', patches: []}); - const {model} = await call('block.get', {id: 'my-block'}); - expect(model.id).toBe('my-block'); - await call('block.del', {id: 'my-block'}); - try { - await call('block.get', {id: 'my-block'}); - throw new Error('not this error'); - } catch (err: any) { - expect(err.errno).toBe(RpcErrorCodes.NOT_FOUND); - } - }); - }); - - describe('block.upd', () => { - test('can edit a document sequentially', async () => { - const {call} = setup(); - const id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await call('block.new', { - id, - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - model.api.str(['text']).ins(4, 'o'); - const patch2 = model.api.flush(); - model.api.str(['text']).ins(5, ' World'); - const patch3 = model.api.flush(); - await call('block.upd', { - id, - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: Date.now(), - blob: patch3.toBinary(), - }, - ], - }); - const block2 = await call('block.get', {id}); - expect(Model.fromBinary(block2.model.blob).view()).toStrictEqual({ - text: 'Hello World', - }); - model.api.str(['text']).del(5, 1).ins(5, ', '); - const patch4 = model.api.flush(); - model.api.str(['text']).ins(12, '!'); - const patch5 = model.api.flush(); - await call('block.upd', { - id, - patches: [ - { - seq: 3, - created: Date.now(), - blob: patch4.toBinary(), - }, - { - seq: 4, - created: Date.now(), - blob: patch5.toBinary(), - }, - ], - }); - const block3 = await call('block.get', {id}); - expect(Model.fromBinary(block3.model.blob).view()).toStrictEqual({ - text: 'Hello, World!', - }); - }); - - test('can edit a document concurrently', async () => { - const {call} = setup(); - const id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; - - // User 1 - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await call('block.new', { - id, - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - - // User 2 - const block2 = await call('block.get', {id}); - const model2 = Model.fromBinary(block2.model.blob).fork(); - model2.api.str(['text']).ins(4, ' yeah!'); - const patch2User2 = model2.api.flush(); - await call('block.upd', { - id, - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2User2.toBinary(), - }, - ], - }); - expect(model2.view()).toStrictEqual({text: 'Hell yeah!'}); - - const block3 = await call('block.get', {id}); - const model3 = Model.fromBinary(block3.model.blob).fork(); - expect(model3.view()).toStrictEqual({text: 'Hell yeah!'}); - - // User 1 - model.api.str(['text']).ins(4, 'o'); - const patch2 = model.api.flush(); - model.api.str(['text']).ins(5, ' World'); - const patch3 = model.api.flush(); - const {patches} = await call('block.upd', { - id, - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: Date.now(), - blob: patch3.toBinary(), - }, - ], - }); - - const block4 = await call('block.get', {id}); - const model4 = Model.fromBinary(block4.model.blob).fork(); - expect(model4.view()).not.toStrictEqual({text: 'Hell yeah!'}); - }); - - test('returns patches that happened concurrently', async () => { - const {call} = setup(); - const id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; - - // User 1 - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await call('block.new', { - id, - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - - // User 2 - const block2 = await call('block.get', {id}); - const model2 = Model.fromBinary(block2.model.blob).fork(); - model2.api.str(['text']).ins(4, ' yeah!'); - const patch2User2 = model2.api.flush(); - await call('block.upd', { - id, - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2User2.toBinary(), - }, - ], - }); - - // User 1 - model.api.str(['text']).ins(4, 'o'); - const patch2 = model.api.flush(); - model.api.str(['text']).ins(5, ' World'); - const patch3 = model.api.flush(); - const {patches} = await call('block.upd', { - id, - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: Date.now(), - blob: patch3.toBinary(), - }, - ], - }); - expect(patches.length).toBe(3); - expect(patches[0].seq).toBe(1); - expect(patches[1].seq).toBe(2); - expect(patches[2].seq).toBe(3); - expect(patches[1].blob).toStrictEqual(patch2.toBinary()); - expect(patches[2].blob).toStrictEqual(patch3.toBinary()); - }); - }); - - describe('block.listen', () => { - test('can listen for block changes', async () => { - const {client} = setup(); - await client.call('block.new', {id: 'my-block', patches: []}); - await tick(11); - const emits: any[] = []; - client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await tick(12); - expect(emits.length).toBe(0); - await client.call('block.upd', { - id: 'my-block', - patches: [{seq: 0, created: Date.now(), blob: patch1.toBinary()}], - }); - await until(() => emits.length === 1); - expect(emits.length).toBe(1); - expect(emits[0][0]).toBe('upd'); - expect(emits[0][1].patches.length).toBe(1); - expect(emits[0][1].patches[0].seq).toBe(0); - model.api.root({ - text: 'Hello', - }); - const patch2 = model.api.flush(); - await tick(12); - expect(emits.length).toBe(1); - await client.call('block.upd', { - id: 'my-block', - patches: [{seq: 1, created: Date.now(), blob: patch2.toBinary()}], - }); - await until(() => emits.length === 2); - expect(emits.length).toBe(2); - expect(emits[1][0]).toBe('upd'); - expect(emits[1][1].patches.length).toBe(1); - expect(emits[1][1].patches[0].seq).toBe(1); - }); - - test('can subscribe before block is created', async () => { - const {client} = setup(); - const emits: any[] = []; - client.call$('block.listen', {id: 'my-block'}).subscribe((data) => emits.push(data)); - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await tick(12); - expect(emits.length).toBe(0); - await client.call('block.new', { - id: 'my-block', - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - await until(() => emits.length === 1); - expect(emits.length).toBe(1); - expect(emits[0][0]).toBe('upd'); - expect(emits[0][1].patches.length).toBe(1); - expect(emits[0][1].patches[0].seq).toBe(0); - expect(emits[0][1].patches[0].blob).toStrictEqual(patch1.toBinary()); - }); - - test('can receive deletion events', async () => { - const {client} = setup(); - const emits: any[] = []; - client.call$('block.listen', {id: 'my-block'}).subscribe((data) => { - emits.push(data); - }); - await client.call('block.new', {id: 'my-block', patches: []}); - await until(() => emits.length === 1); - expect(emits[0][1].model.seq).toBe(-1); - await tick(3); - await client.call('block.del', {id: 'my-block'}); - await until(() => emits.length === 2); - expect(emits[1][0]).toBe('del'); - }); - }); - - describe('block.scan', () => { - test('can retrieve change history', async () => { - const {client} = setup(); - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await client.call('block.new', { - id: 'my-block', - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - await tick(11); - model.api.str(['text']).ins(4, 'o'); - const patch2 = model.api.flush(); - model.api.obj([]).set({ - age: 26, - }); - const patch3 = model.api.flush(); - await client.call('block.upd', { - id: 'my-block', - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: Date.now(), - blob: patch3.toBinary(), - }, - ], - }); - const history = await client.call('block.scan', {id: 'my-block', seq: 0, limit: 3}); - expect(history).toMatchObject({ - patches: [ - { - seq: 0, - created: expect.any(Number), - blob: patch1.toBinary(), - }, - { - seq: 1, - created: expect.any(Number), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: expect.any(Number), - blob: patch3.toBinary(), - }, - ], - }); - }); - }); - - describe('block.get', () => { - test('returns whole history when block is loaded', async () => { - const {client} = setup(); - const model = Model.withLogicalClock(); - model.api.root({ - text: 'Hell', - }); - const patch1 = model.api.flush(); - await client.call('block.new', { - id: 'my-block', - patches: [ - { - blob: patch1.toBinary(), - }, - ], - }); - model.api.str(['text']).ins(4, 'o'); - const patch2 = model.api.flush(); - model.api.obj([]).set({ - age: 26, - }); - const patch3 = model.api.flush(); - await client.call('block.upd', { - id: 'my-block', - patches: [ - { - seq: 1, - created: Date.now(), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: Date.now(), - blob: patch3.toBinary(), - }, - ], - }); - const result = await client.call('block.get', {id: 'my-block', history: true}); - expect(result).toMatchObject({ - model: { - id: 'my-block', - seq: 2, - blob: expect.any(Uint8Array), - created: expect.any(Number), - updated: expect.any(Number), - }, - patches: [ - { - seq: 0, - created: expect.any(Number), - blob: patch1.toBinary(), - }, - { - seq: 1, - created: expect.any(Number), - blob: patch2.toBinary(), - }, - { - seq: 2, - created: expect.any(Number), - blob: patch3.toBinary(), - }, - ], - }); - }); - }); -}); diff --git a/src/server/__tests__/presence.spec.ts b/src/server/__tests__/presence.spec.ts deleted file mode 100644 index 6bd66a3b0f..0000000000 --- a/src/server/__tests__/presence.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import {setup} from './setup'; -import {tick, until} from '../../__tests__/util'; - -describe('presence', () => { - test('can subscribe and receive published presence entries', async () => { - const {call, call$} = setup(); - const emits: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits.push(res); - }); - await call('presence.update', { - room: 'my-room', - id: 'user-1', - data: { - hello: 'world', - }, - }); - await until(() => emits.length === 1); - expect(emits[0]).toMatchObject({ - time: expect.any(Number), - entries: [ - { - id: 'user-1', - lastSeen: expect.any(Number), - validUntil: expect.any(Number), - data: { - hello: 'world', - }, - }, - ], - }); - }); - - test('can receive an existing record when subscribing after it was created', async () => { - const {call, call$} = setup(); - const emits: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits.push(res); - }); - await call('presence.update', { - room: 'my-room', - id: 'user-1', - data: { - hello: 'world', - }, - }); - await until(() => emits.length === 1); - const emits2: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits2.push(res); - }); - await until(() => emits2.length === 1); - expect(emits2[0]).toMatchObject({ - time: expect.any(Number), - entries: [ - { - id: 'user-1', - lastSeen: expect.any(Number), - validUntil: expect.any(Number), - data: { - hello: 'world', - }, - }, - ], - }); - }); - - test('can remove existing entries', async () => { - const {call, call$} = setup(); - const emits: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits.push(res); - }); - await call('presence.update', { - room: 'my-room', - id: 'user-1', - data: { - hello: 'world', - }, - }); - await until(() => emits.length === 1); - await call('presence.remove', {room: 'my-room', id: 'user-1'}); - await until(() => emits.length === 2); - const emits2: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits2.push(res); - }); - await tick(50); - expect(emits2.length).toBe(0); - }); - - test('emits entry deletion messages', async () => { - const {call, call$} = setup(); - await call('presence.update', { - room: 'my-room', - id: 'user-1', - data: { - hello: 'world', - }, - }); - const emits: any[] = []; - call$('presence.listen', {room: 'my-room'}).subscribe((res) => { - emits.push(res); - }); - await call('presence.remove', {room: 'my-room', id: 'user-1'}); - await until(() => emits.length === 2); - expect(emits[1].entries[0]).toMatchObject({ - id: 'user-1', - lastSeen: expect.any(Number), - validUntil: 0, - data: expect.any(Object), - }); - }); -}); diff --git a/src/server/__tests__/pubsub.spec.ts b/src/server/__tests__/pubsub.spec.ts deleted file mode 100644 index f2c8725440..0000000000 --- a/src/server/__tests__/pubsub.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import {setup} from './setup'; -import {tick, until} from '../../__tests__/util'; - -describe('pubsub', () => { - test('throws error on invalid input', async () => { - const {call} = setup(); - try { - await call('pubsub.publish', { - channel2: 'INVALID KEY', - message: 'hello world', - } as any); - throw new Error('should not reach here'); - } catch (err: any) { - expect(err.meta.path).toStrictEqual(['channel2']); - } - }); - - test('can subscribe and receive published messages', async () => { - const {call, call$} = setup(); - const emits: any[] = []; - call$('pubsub.listen', {channel: 'my-channel'}).subscribe((res) => { - emits.push(res.message); - }); - await call('pubsub.publish', { - channel: 'my-channel', - message: 'hello world', - }); - await until(() => emits.length === 1); - expect(emits).toStrictEqual(['hello world']); - }); - - test('does not receive messages after un-subscription', async () => { - const {call, call$} = setup(); - const emits: any[] = []; - const sub = call$('pubsub.listen', {channel: 'my-channel'}).subscribe((res) => { - emits.push(res.message); - }); - await call('pubsub.publish', { - channel: 'my-channel', - message: 'msg1', - }); - await call('pubsub.publish', { - channel: 'my-channel', - message: 'msg2', - }); - await until(() => emits.length === 2); - sub.unsubscribe(); - await tick(25); - await call('pubsub.publish', { - channel: 'my-channel', - message: 'msg3', - }); - await tick(50); - expect(emits.indexOf('msg1') > -1).toBe(true); - expect(emits.indexOf('msg2') > -1).toBe(true); - }); - - test('multiple multiple subscribers can subscribe to multiple channels', async () => { - const {call, call$} = setup(); - const user1: any[] = []; - const user2: any[] = []; - const user3: any[] = []; - call$('pubsub.listen', {channel: 'channel-1'}).subscribe((res) => { - user1.push(res.message); - }); - const sub2 = call$('pubsub.listen', {channel: 'channel-2'}).subscribe((res) => { - user2.push(res.message); - }); - call$('pubsub.listen', {channel: 'channel-1'}).subscribe((res) => { - user3.push(res.message); - }); - call$('pubsub.listen', {channel: 'channel-2'}).subscribe((res) => { - user3.push(res.message); - }); - await call('pubsub.publish', { - channel: 'my-channel', - message: 'hello world', - }); - await tick(50); - expect(user1).toStrictEqual([]); - expect(user2).toStrictEqual([]); - expect(user3).toStrictEqual([]); - call('pubsub.publish', { - channel: 'channel-1', - message: 'msg1', - }).catch(() => {}); - call('pubsub.publish', { - channel: 'channel-2', - message: 'msg2', - }).catch(() => {}); - await until(() => user1.length === 1); - await until(() => user2.length === 1); - await until(() => user3.length === 2); - expect(user1).toStrictEqual(['msg1']); - expect(user2).toStrictEqual(['msg2']); - expect(user3.indexOf('msg1') > -1).toBe(true); - expect(user3.indexOf('msg2') > -1).toBe(true); - sub2.unsubscribe(); - call('pubsub.publish', { - channel: 'channel-2', - message: 'msg3', - }).catch(() => {}); - await until(() => user3.length === 3); - expect(user1).toStrictEqual(['msg1']); - expect(user2).toStrictEqual(['msg2']); - expect(user3.indexOf('msg1') > -1).toBe(true); - expect(user3.indexOf('msg2') > -1).toBe(true); - expect(user3.indexOf('msg3') > -1).toBe(true); - }); -}); diff --git a/src/server/__tests__/setup.ts b/src/server/__tests__/setup.ts deleted file mode 100644 index 075e7eeca7..0000000000 --- a/src/server/__tests__/setup.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {buildE2eClient} from '../../reactive-rpc/common/testing/buildE2eClient'; -import {createCaller} from '../routes'; -import {Services} from '../services/Services'; - -export const setup = () => { - const services = new Services(); - const {caller} = createCaller(services); - const {client} = buildE2eClient(caller, { - writerDefaultBufferKb: [1, 32], - clientBufferSize: [1, 3], - clientBufferTime: [1, 10], - serverBufferSize: [1, 3], - serverBufferTime: [1, 10], - }); - const call = client.call.bind(client); - const call$ = client.call$.bind(client); - return {services, caller, client, call, call$}; -}; diff --git a/src/server/__tests__/util.spec.ts b/src/server/__tests__/util.spec.ts deleted file mode 100644 index 1c6b59e123..0000000000 --- a/src/server/__tests__/util.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {setup} from './setup'; - -describe('util.*', () => { - describe('util.ping', () => { - test('returns pong', async () => { - const {call} = setup(); - const res = await call('util.ping', {}); - expect(res).toBe('pong'); - }); - }); - - describe('util.echo', () => { - test('returns strings', async () => { - const {call} = setup(); - const res = await call('util.echo', 'hello world'); - expect(res).toBe('hello world'); - }); - - test('returns objects', async () => { - const {call} = setup(); - const res = await call('util.echo', {foo: 'bar'}); - expect(res).toStrictEqual({foo: 'bar'}); - }); - }); - - describe('util.info', () => { - test('returns stats object', async () => { - const {call} = setup(); - const res = await call('util.info', {}); - expect(res).toMatchObject({ - now: expect.any(Number), - stats: { - pubsub: { - channels: expect.any(Number), - observers: expect.any(Number), - }, - presence: { - rooms: expect.any(Number), - entries: expect.any(Number), - observers: expect.any(Number), - }, - blocks: { - blocks: expect.any(Number), - patches: expect.any(Number), - }, - }, - }); - }); - }); -}); diff --git a/src/server/index.ts b/src/server/index.ts deleted file mode 100644 index dfe587f508..0000000000 --- a/src/server/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Run: npx ts-node src/server/index.ts - -import {App} from 'uWebSockets.js'; -import {RpcApp} from '../reactive-rpc/server/uws/RpcApp'; -import {createCaller} from './routes'; -import {Services} from './services/Services'; -import type {MyCtx} from './services/types'; -import {RpcServer} from '../reactive-rpc/server/http1/RpcServer'; - -export type JsonJoyDemoRpcCaller = ReturnType['caller']; - -const app = new RpcApp({ - uws: App({}), - caller: createCaller(new Services()).caller, -}); -app.startWithDefaults(); - -// const server = RpcServer.startWithDefaults({ -// port: 9999, -// caller: createCaller(new Services()).caller, -// logger: console, -// }); diff --git a/src/server/routes/block/index.ts b/src/server/routes/block/index.ts deleted file mode 100644 index cda905e8fa..0000000000 --- a/src/server/routes/block/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {new_} from './methods/new'; -import {get} from './methods/get'; -import {upd} from './methods/upd'; -import {del} from './methods/del'; -import {scan} from './methods/scan'; -import {listen} from './methods/listen'; -import { - Block, - BlockPartial, - BlockPartialReturn, - BlockId, - BlockPatch, - BlockPatchPartial, - BlockPatchPartialReturn, - BlockSeq, -} from './schema'; -import type {RouteDeps, Router, RouterBase} from '../types'; - -export const block = - (d: RouteDeps) => - (r: Router) => { - const {system} = d; - - system.alias('BlockId', BlockId); - system.alias('BlockSeq', BlockSeq); - system.alias('Block', Block); - system.alias('BlockPartial', BlockPartial); - system.alias('BlockPartialReturn', BlockPartialReturn); - system.alias('BlockPatch', BlockPatch); - system.alias('BlockPatchPartial', BlockPatchPartial); - system.alias('BlockPatchPartialReturn', BlockPatchPartialReturn); - - // prettier-ignore - return ( - ( new_(d) - ( get(d) - ( upd(d) - ( del(d) - ( listen(d) - ( scan(d) - ( r )))))))); - }; diff --git a/src/server/routes/block/methods/del.ts b/src/server/routes/block/methods/del.ts deleted file mode 100644 index f3e28617c8..0000000000 --- a/src/server/routes/block/methods/del.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {BlockId} from '../schema'; - -export const del = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'Block ID', - description: 'The ID of the block to delete.', - }), - ); - - const Response = t.obj; - - const Func = t.Function(Request, Response).options({ - title: 'Read Block', - intro: 'Retrieves a block by ID.', - description: 'Fetches a block by ID.', - }); - - return r.prop('block.del', Func, async ({id}) => { - await services.blocks.remove(id); - return {}; - }); - }; diff --git a/src/server/routes/block/methods/get.ts b/src/server/routes/block/methods/get.ts deleted file mode 100644 index 2135db2748..0000000000 --- a/src/server/routes/block/methods/get.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {ResolveType} from '../../../../json-type'; -import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {Block, BlockId, BlockPatch} from '../schema'; - -export const get = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'Block ID', - description: 'The ID of the block to retrieve.', - }), - t.propOpt('history', t.bool).options({ - title: 'With History', - description: 'Whether to include the full history of patches in the response. Defaults to `false`.', - }), - ); - - const Response = t.Object( - t.prop('model', t.Ref('Block')), - t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ - title: 'Patches', - description: 'The list of all patches.', - }), - ); - - const Func = t.Function(Request, Response).options({ - title: 'Read Block', - intro: 'Retrieves a block by ID.', - description: 'Fetches a block by ID.', - }); - - return r.prop('block.get', Func, async ({id, history}) => { - const {model} = await services.blocks.get(id); - const response: ResolveType = {model}; - if (history) { - const {patches} = await services.blocks.scan(id, 0, model.seq); - response.patches = patches; - } - return response; - }); - }; diff --git a/src/server/routes/block/methods/listen.ts b/src/server/routes/block/methods/listen.ts deleted file mode 100644 index bd3f9c8405..0000000000 --- a/src/server/routes/block/methods/listen.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {switchMap, tap} from 'rxjs'; -import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {BlockId, BlockPatch, Block} from '../schema'; - -export const listen = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'Block ID', - description: 'The ID of the block to subscribe to.', - }), - ); - - const Response = t.Or( - t.Tuple(t.Const('del')), - t.Tuple( - t.Const('upd'), - t.Object( - t.propOpt('model', t.Ref('Block')).options({ - title: 'Block', - description: 'The whole block object, emitted only when the block is created.', - }), - t.propOpt('patches', t.Array(t.Ref('BlockPatch'))).options({ - title: 'Latest Patches', - description: 'Patches that have been applied to the block.', - }), - ), - ), - ); - - const Func = t.Function$(Request, Response).options({ - title: 'Listen for block changes', - description: 'Subscribe to a block to receive updates when it changes.', - }); - - return r.prop('block.listen', Func, (req$) => { - return req$.pipe(switchMap(({id}) => services.pubsub.listen$(`__block:${id}`))) as any; - }); - }; diff --git a/src/server/routes/block/methods/new.ts b/src/server/routes/block/methods/new.ts deleted file mode 100644 index b270460417..0000000000 --- a/src/server/routes/block/methods/new.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {Block, BlockId, BlockPatchPartial, BlockPatchPartialReturn} from '../schema'; - -export const new_ = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'New block ID', - description: 'The ID of the new block.', - }), - t.prop('patches', t.Array(t.Ref('BlockPatchPartial'))).options({ - title: 'Patches', - description: 'The patches to apply to the document.', - }), - ); - - const Response = t.Object( - t.prop('model', t.Ref('Block')), - t.prop('patches', t.Array(t.Ref('BlockPatchPartialReturn'))).options({ - title: 'Patches', - description: 'The list of all patches.', - }), - ); - - const Func = t.Function(Request, Response).options({ - title: 'Create Block', - intro: 'Creates a new block or applies patches to it.', - description: 'Creates a new block or applies patches to it.', - }); - - return r.prop('block.new', Func, async ({id, patches}) => { - const res = await services.blocks.create(id, patches); - return res; - }); - }; diff --git a/src/server/routes/block/methods/scan.ts b/src/server/routes/block/methods/scan.ts deleted file mode 100644 index ce5080199f..0000000000 --- a/src/server/routes/block/methods/scan.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type {BlockPatch, BlockId} from '../schema'; -import type {RouteDeps, Router, RouterBase} from '../../types'; - -export const scan = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'Block ID', - description: 'The ID of the block.', - }), - t.propOpt('seq', t.num.options({format: 'u32'})).options({ - title: 'Starting Sequence Number', - description: 'The sequence number to start from. Defaults to the latest sequence number.', - }), - t.propOpt('limit', t.num.options({format: 'u32'})).options({ - title: 'Number of Patches', - description: - 'The minimum number of patches to return. Defaults to 10. ' + - 'When positive, returns the patches ahead of the starting sequence number. ' + - 'When negative, returns the patches behind the starting sequence number.', - }), - t.propOpt('model', t.bool).options({ - title: 'With Model', - description: - 'Whether to include the model in the response. ' + - 'Defaults to `false`, when `len` is positive; and, defaults to `true`, when `len` is negative.', - }), - ); - - const Response = t.Object( - t.prop('patches', t.Array(t.Ref('BlockPatch'))).options({ - title: 'Patches', - description: 'The list of patches.', - }), - t.propOpt('modelBlob', t.bin), - ); - - const Func = t.Function(Request, Response).options({ - title: 'Block History', - intro: 'Fetch block history.', - description: 'Returns a list of specified change patches for a block.', - }); - - return r.prop('block.scan', Func, async ({id, seq, limit = 10, model: returnModel = limit > 0}) => { - const {patches, model} = await services.blocks.scan(id, seq, limit, returnModel); - const modelBlob: Uint8Array | undefined = model?.toBinary(); - return {patches, modelBlob}; - }); - }; diff --git a/src/server/routes/block/methods/upd.ts b/src/server/routes/block/methods/upd.ts deleted file mode 100644 index 987854f5f5..0000000000 --- a/src/server/routes/block/methods/upd.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../../types'; -import type {BlockId, BlockPatch} from '../schema'; - -export const upd = - ({t, services}: RouteDeps) => - (r: Router) => { - const PatchType = t.Ref('BlockPatch'); - - const Request = t.Object( - t.prop('id', t.Ref('BlockId')).options({ - title: 'Document ID', - description: 'The ID of the document to apply the patch to.', - }), - // This can be inferred from the "seq" of the first patch: - // t.prop('seq', t.Ref('BlockSeq')).options({ - // title: 'Last known sequence number', - // description: - // 'The last known sequence number of the document. ' + - // 'If the document has changed since this sequence number, ' + - // 'the response will contain all the necessary patches for the client to catch up.', - // }), - t.prop('patches', t.Array(PatchType)).options({ - title: 'Patches', - description: 'The patches to apply to the document.', - }), - ); - - const Response = t.Object( - t.prop('patches', t.Array(PatchType)).options({ - title: 'Latest patches', - description: 'The list of patches that the client might have missed and should apply to the document.', - }), - ); - - const Func = t.Function(Request, Response).options({ - title: 'Edit Block', - intro: 'Applies patches to an existing block.', - description: 'Applies patches to an existing document and returns the latest concurrent changes.', - }); - - return r.prop('block.upd', Func, async ({id, patches}) => { - const res = await services.blocks.edit(id, patches); - return { - patches: res.patches, - }; - }); - }; diff --git a/src/server/routes/block/schema.ts b/src/server/routes/block/schema.ts deleted file mode 100644 index 6d12c3eaf1..0000000000 --- a/src/server/routes/block/schema.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {type ResolveType} from '../../../json-type'; -import {t} from '../system'; - -export type TBlockId = ResolveType; -export const BlockId = t.str.options({ - title: 'Block ID', - min: 6, - max: 40, -}); - -export type TBlockSeq = ResolveType; -export const BlockSeq = t.num.options({ - title: 'Block Sequence Number', - gte: 0, - format: 'i32', -}); - -export type TBlock = ResolveType; - -// prettier-ignore -export const BlockPartial = t.Object( - t.prop('blob', t.bin), -); - -export const BlockPartialReturn = t.Object( - t.prop('id', t.Ref('BlockId')), - t.prop('seq', t.Ref('BlockSeq')), - t.prop('created', t.num), - t.prop('updated', t.num), -); - -export const Block = BlockPartial.extend(BlockPartialReturn); - -export type TBlockPatch = ResolveType; - -// prettier-ignore -export const BlockPatchPartial = t.Object( - t.prop('blob', t.bin).options({ - title: 'Patch Blob', - description: 'The binary data of the patch. The format of the data is defined by the patch type.', - }), -); - -// prettier-ignore -export const BlockPatchPartialReturn = t.Object( - t.prop('seq', t.num).options({ - title: 'Patch Sequence Number', - description: 'The sequence number of the patch in the block. A monotonically increasing integer, starting from 0.', - }), - t.prop('created', t.num).options({ - title: 'Patch Creation Time', - description: 'The time when the patch was created, in milliseconds since the Unix epoch.' + - '\n\n' + - 'This time is set by the server when the patch received and stored on the server. If you ' + - 'want to also store the time when the patch was created by the user, you can include this ' + - 'information in the patch blob itself.', - }), -); - -export const BlockPatch = BlockPatchPartial.extend(BlockPatchPartialReturn); diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts deleted file mode 100644 index 812c108169..0000000000 --- a/src/server/routes/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {routes} from './routes'; -import {RpcError} from '../../reactive-rpc/common/rpc/caller'; -import {RpcValue} from '../../reactive-rpc/common/messages/Value'; -import {ObjectValueCaller} from '../../reactive-rpc/common/rpc/caller/ObjectValueCaller'; -import {system} from './system'; -import {ObjectValue} from '../../json-type-value/ObjectValue'; -import {Services} from '../services/Services'; -import type {RouteDeps} from './types'; - -export const createRouter = (services: Services) => { - const router = ObjectValue.create(system); - const deps: RouteDeps = { - services, - router, - system, - t: system.t, - }; - return routes(deps)(router); -}; - -export const createCaller = (services: Services = new Services()) => { - const router = createRouter(services); - const caller = new ObjectValueCaller({ - router, - wrapInternalError: (error: unknown) => { - if (error instanceof RpcValue) return error; - if (error instanceof RpcError) return RpcError.value(error); - // tslint:disable-next-line:no-console - console.error(error); - return RpcError.valueFrom(error); - }, - }); - return {router, caller}; -}; diff --git a/src/server/routes/presence/index.ts b/src/server/routes/presence/index.ts deleted file mode 100644 index 7a5e92f5be..0000000000 --- a/src/server/routes/presence/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {update} from './methods/update'; -import {listen} from './methods/listen'; -import {remove} from './methods/remove'; -import {PresenceEntry} from './schema'; -import type {RouteDeps, Router, RouterBase} from '../types'; - -export const presence = - (d: RouteDeps) => - (r: Router) => { - d.system.alias('PresenceEntry', PresenceEntry); - - // prettier-ignore - return ( - ( update(d) - ( remove(d) - ( listen(d) - ( r ))))); - }; diff --git a/src/server/routes/presence/methods/listen.ts b/src/server/routes/presence/methods/listen.ts deleted file mode 100644 index ee5406956a..0000000000 --- a/src/server/routes/presence/methods/listen.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {map, switchMap} from 'rxjs'; -import type {PresenceEntry, TPresenceEntry} from '../schema'; -import type {RouteDeps, Router, RouterBase} from '../../types'; - -export const listen = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('room', t.str).options({ - title: 'Room ID', - description: 'The ID of the room to subscribe to.', - }), - ); - - const Response = t.Object( - t.prop('entries', t.Array(t.Ref('PresenceEntry'))), - t.prop('time', t.num).options({ - title: 'Current time', - description: 'The current server time in milliseconds since the UNIX epoch.', - }), - ); - - const Func = t.Function$(Request, Response).options({ - title: 'Subscribe to a room.', - intro: 'Subscribes to presence updates in a room.', - description: - 'This method subscribes to presence updates in a room. ' + - 'It returns an array of all current presence entries in the room, and then emits an update whenever ' + - 'a presence entry is updated or deleted. ', - }); - - return r.prop('presence.listen', Func, (req$) => { - return req$.pipe( - switchMap((req) => services.presence.listen$(req.room)), - map((entries: TPresenceEntry[]) => ({ - entries, - time: Date.now(), - })), - ); - }); - }; diff --git a/src/server/routes/presence/methods/remove.ts b/src/server/routes/presence/methods/remove.ts deleted file mode 100644 index 80f0085bea..0000000000 --- a/src/server/routes/presence/methods/remove.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../../types'; - -export const remove = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('room', t.str).options({ - title: 'Room ID', - description: 'The ID of the room from which to remove the entry.', - }), - t.prop('id', t.str).options({ - title: 'ID of the entry', - description: 'The ID of the entry to remove.', - }), - ); - - const Response = t.obj; - - const Func = t.Function(Request, Response).options({ - title: 'Remove a presence entry.', - intro: 'Removes a presence entry from a room and notifies all listeners.', - description: 'This method removes a presence entry from a room and notifies all listeners. ', - }); - - return r.prop('presence.remove', Func, async ({room, id}) => { - await services.presence.remove(room, id); - return {}; - }); - }; diff --git a/src/server/routes/presence/methods/update.ts b/src/server/routes/presence/methods/update.ts deleted file mode 100644 index a45d65e4f3..0000000000 --- a/src/server/routes/presence/methods/update.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type {ResolveType} from '../../../../json-type'; -import type {PresenceEntry} from '../schema'; -import type {RouteDeps, Router, RouterBase} from '../../types'; - -/** Entry TLL in seconds. */ -const ttl = 30; - -export const update = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t - .Object( - t.prop('room', t.str).options({ - title: 'Room ID', - description: 'The ID of the room to update.', - }), - t.prop('id', t.str).options({ - title: 'ID of the entry', - description: 'The ID of the entry to update.', - }), - t.prop('data', t.any).options({ - title: 'Entry data', - description: 'A map of key-value pairs to update. The object is merged with the existing entry data, if any.', - }), - ) - .options({ - examples: [ - { - title: 'Update user entry', - description: - 'The data section of the entry is merged with the existing data. ' + - 'It can contain any key-value pairs. For example, the `cursor` property is used to store the ' + - 'current cursor position of the user in the room.', - value: { - room: 'my-room', - id: 'user-1', - data: { - name: 'John Doe', - cursor: [123, 456], - }, - }, - }, - ], - }); - - const Response = t - .Object( - t.prop('entry', t.Ref('PresenceEntry')), - t.prop('time', t.num).options({ - title: 'Current time', - description: 'The current server time in milliseconds since the UNIX epoch.', - }), - ) - .options({ - title: 'Presence update response', - }); - - const Func = t.Function(Request, Response).options({ - title: 'Update presence entry', - intro: 'Update a presence entry in a room.', - description: - 'This method updates a presence entry in a room. ' + - `The entry is automatically removed after ${ttl} seconds. ` + - `Every time the entry is updated, the TTL is reset to ${ttl} seconds.`, - }); - - return r.prop('presence.update', Func, async ({room, id, data}) => { - const entry = (await services.presence.update(room, id, ttl * 1000, data)) as ResolveType; - return { - entry, - time: Date.now(), - }; - }); - }; diff --git a/src/server/routes/presence/schema.ts b/src/server/routes/presence/schema.ts deleted file mode 100644 index e8eca6d8a1..0000000000 --- a/src/server/routes/presence/schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {type ResolveType} from '../../../json-type'; -import {t} from '../system'; - -export const PresenceEntry = t.Object( - t.prop('id', t.str), - t.prop('lastSeen', t.num), - t.prop('validUntil', t.num), - t.prop( - 'data', - t.obj.options({ - encodeUnknownFields: true, - }), - ), -); - -export type TPresenceEntry = ResolveType; diff --git a/src/server/routes/pubsub/index.ts b/src/server/routes/pubsub/index.ts deleted file mode 100644 index dea1b491be..0000000000 --- a/src/server/routes/pubsub/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {publish} from './publish'; -import {listen} from './listen'; -import type {RouteDeps, Router, RouterBase} from '../types'; - -// prettier-ignore -export const pubsub = (d: RouteDeps) => (r: Router) => - ( publish(d) - ( listen(d) - ( r ))); diff --git a/src/server/routes/pubsub/listen.ts b/src/server/routes/pubsub/listen.ts deleted file mode 100644 index c5e833a290..0000000000 --- a/src/server/routes/pubsub/listen.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {map, switchMap} from 'rxjs'; -import type {RouteDeps, Router, RouterBase} from '../types'; - -export const listen = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('channel', t.str).options({ - title: 'Channel name', - description: 'The name of the channel to subscribe to.', - }), - ); - - const Response = t.Object( - t.prop('message', t.any).options({ - title: 'Subscription message', - description: 'A message received from the channel. Emitted every time a message is published to the channel.', - }), - ); - - const Func = t.Function$(Request, Response); - - return r.prop('pubsub.listen', Func, (req) => { - const response = req.pipe( - switchMap((req) => services.pubsub.listen$(req.channel)), - map((message: any) => ({message})), - ); - return response; - }); - }; diff --git a/src/server/routes/pubsub/publish.ts b/src/server/routes/pubsub/publish.ts deleted file mode 100644 index aba6c557e1..0000000000 --- a/src/server/routes/pubsub/publish.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../types'; - -export const publish = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.Object( - t.prop('channel', t.str).options({ - title: 'Channel name', - description: 'The name of the channel to publish to.', - }), - t.prop('message', t.any).options({ - title: 'Message', - description: 'The message to publish to the channel. Can be any JSON/CBOR value.', - }), - ); - - const Response = t.obj.options({ - title: 'Publish response', - description: 'An empty object.', - }); - - const Func = t.Function(Request, Response).options({ - title: 'Publish to channel', - intro: 'Publish a message to a channel.', - description: - 'This method publishes a message to a global channel with the given `channel` name. ' + - 'All subscribers to the channel will receive the message. The `message` can be any value. ' + - 'The most efficient way to publish a message is to send a primitive or a `Uint8Array` buffer.', - }); - - return r.prop('pubsub.publish', Func, async ({channel, message}) => { - services.pubsub.publish(channel, message); - return {}; - }); - }; diff --git a/src/server/routes/routes.ts b/src/server/routes/routes.ts deleted file mode 100644 index 2d5733147c..0000000000 --- a/src/server/routes/routes.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {util} from './util'; -import {pubsub} from './pubsub'; -import {presence} from './presence'; -import {block} from './block'; -import type {RouteDeps} from './types'; -import type {ObjectValue} from '../../json-type-value/ObjectValue'; -import type {ObjectType} from '../../json-type'; - -// prettier-ignore -export const routes = (d: RouteDeps) => >(r: ObjectValue) => - ( util(d) - ( pubsub(d) - ( presence(d) - // TODO: rename "blocks" to "block", in all methods. - ( block(d) - ( r ))))); diff --git a/src/server/routes/system.ts b/src/server/routes/system.ts deleted file mode 100644 index 28d0e17ae2..0000000000 --- a/src/server/routes/system.ts +++ /dev/null @@ -1,4 +0,0 @@ -import {TypeSystem} from '../../json-type'; - -export const system = new TypeSystem(); -export const t = system.t; diff --git a/src/server/routes/types.ts b/src/server/routes/types.ts deleted file mode 100644 index 74b441138b..0000000000 --- a/src/server/routes/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type {ObjectType, TypeSystem} from '../../json-type'; -import type {ObjectValue} from '../../json-type-value/ObjectValue'; -import type {TypeBuilder} from '../../json-type/type/TypeBuilder'; -import type {Services} from '../services/Services'; - -export interface RouteDeps { - services: Services; - system: TypeSystem; - t: TypeBuilder; - router: ObjectValue; -} - -export type RouterBase = ObjectType; -export type Router = ObjectValue; diff --git a/src/server/routes/util/index.ts b/src/server/routes/util/index.ts deleted file mode 100644 index e9c6664b47..0000000000 --- a/src/server/routes/util/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type {RouteDeps, Router, RouterBase} from '../types'; - -export const ping = - ({t}: RouteDeps) => - (r: Router) => { - const Request = t.any; - const Response = t.Const('pong'); - const Func = t.Function(Request, Response); - return r.prop('util.ping', Func, async () => { - return 'pong'; - }); - }; - -export const echo = - ({t}: RouteDeps) => - (r: Router) => { - const Request = t.any; - const Response = t.any; - const Func = t.Function(Request, Response); - return r.prop('util.echo', Func, async (msg) => msg); - }; - -export const info = - ({t, services}: RouteDeps) => - (r: Router) => { - const Request = t.any; - const Response = t.Object( - t.prop('now', t.num), - t.prop( - 'stats', - t.Object( - t.prop('pubsub', t.Object(t.prop('channels', t.num), t.prop('observers', t.num))), - t.prop('presence', t.Object(t.prop('rooms', t.num), t.prop('entries', t.num), t.prop('observers', t.num))), - t.prop('blocks', t.Object(t.prop('blocks', t.num), t.prop('patches', t.num))), - ), - ), - ); - const Func = t.Function(Request, Response); - return r.prop('util.info', Func, async () => { - return { - now: Date.now(), - stats: { - pubsub: services.pubsub.stats(), - presence: services.presence.stats(), - blocks: services.blocks.stats(), - }, - }; - }); - }; - -export const schema = - ({t, router}: RouteDeps) => - (r: Router) => { - const Request = t.any; - const Response = t.Object(t.prop('typescript', t.str)); - const Func = t.Function(Request, Response); - return r.prop('util.schema', Func, async () => { - return { - typescript: router.toTypeScript(), - }; - }); - }; - -// prettier-ignore -export const util = (d: RouteDeps) => (r: Router) => - ( ping(d) - ( echo(d) - ( info(d) - ( schema(d) - ( r ))))); diff --git a/src/server/services/PresenceService.ts b/src/server/services/PresenceService.ts deleted file mode 100644 index fca1c38a44..0000000000 --- a/src/server/services/PresenceService.ts +++ /dev/null @@ -1,101 +0,0 @@ -import {Observable, Observer} from 'rxjs'; -import {TPresenceEntry} from '../routes/presence/schema'; - -export type PresenceRoom = Map; - -export class PresenceService { - private readonly rooms = new Map(); - private readonly observers = new Map[]>(); - - public async update(roomId: string, entryId: string, ttl: number, data: unknown): Promise { - const now = Date.now(); - const room = this.getRoom(roomId); - if (!data || typeof data !== 'object') throw new Error('ROOM_ENTRY_MUST_BE_OBJECT'); - const entry: TPresenceEntry = room.get(entryId) ?? { - id: entryId, - lastSeen: now, - validUntil: now + ttl, - data: {}, - }; - entry.lastSeen = now; - entry.validUntil = now + ttl; - Object.assign(entry.data, data); - room.set(entryId, entry); - this.cleanUpRoom(roomId); - await new Promise((resolve) => setImmediate(resolve)); - const observers = this.observers.get(roomId); - if (observers) for (const observer of observers) observer.next([entry]); - return entry; - } - - public async remove(roomId: string, entryId: string): Promise { - const room = this.getRoom(roomId); - room.delete(entryId); - if (!room.size) this.rooms.delete(roomId); - await new Promise((resolve) => setImmediate(resolve)); - const observers = this.observers.get(roomId); - if (observers) - for (const observer of observers) - observer.next([ - { - id: entryId, - lastSeen: Date.now(), - validUntil: 0, - data: {}, - }, - ]); - } - - public listen$(roomId: string): Observable { - return new Observable((observer) => { - this.cleanUpRoom(roomId); - if (!this.observers.has(roomId)) this.observers.set(roomId, []); - this.observers.get(roomId)!.push(observer); - const room = this.getRoom(roomId); - const entries: TPresenceEntry[] = []; - for (const entry of room.values()) { - entries.push(entry); - if (entries.length === 100) break; - } - if (entries.length) observer.next(entries); - return () => { - const observers: Observer[] = this.observers.get(roomId)!; - if (!observers) { - this.cleanUpRoom(roomId); - return; - } - const index = observers.findIndex((o) => o === observer); - if (index > -1) observers.splice(index, 1); - if (!observers.length) { - this.observers.delete(roomId); - } - }; - }); - } - - private getRoom(roomId: string): PresenceRoom { - const room = this.rooms.get(roomId); - if (room) return room; - const newRoom = new Map(); - this.rooms.set(roomId, newRoom); - return newRoom; - } - - private cleanUpRoom(roomId: string) { - const room = this.rooms.get(roomId); - if (!room) return; - const now = Date.now(); - for (const [entry, presence] of room.entries()) { - if (presence.validUntil < now) room.delete(entry); - } - if (!room.size) this.rooms.delete(roomId); - } - - public stats(): {rooms: number; entries: number; observers: number} { - return { - rooms: this.rooms.size, - entries: [...this.rooms.values()].reduce((acc, v) => acc + v.size, 0), - observers: [...this.observers.values()].reduce((acc, v) => acc + v.length, 0), - }; - } -} diff --git a/src/server/services/PubSubService.ts b/src/server/services/PubSubService.ts deleted file mode 100644 index d5a5b9b83b..0000000000 --- a/src/server/services/PubSubService.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Observable, Observer} from 'rxjs'; - -export class PubsubService { - private readonly observers = new Map[]>(); - - public listen$(channel: string): Observable { - return new Observable((observer) => { - if (!this.observers.has(channel)) this.observers.set(channel, []); - const observers: Observer[] = this.observers.get(channel)!; - observers.push(observer); - return () => { - const observers: Observer[] = this.observers.get(channel)!; - if (!observers) return; - const index = observers.findIndex((o) => o === observer); - if (index > -1) observers.splice(index, 1); - if (!observers.length) { - this.observers.delete(channel); - } - }; - }); - } - - public async publish(channel: string, message: unknown): Promise { - await new Promise((resolve) => setImmediate(resolve)); - const observers = this.observers.get(channel); - if (!observers) return; - for (const observer of observers) observer.next(message); - } - - public stats(): {channels: number; observers: number} { - return { - channels: this.observers.size, - observers: [...this.observers.values()].reduce((acc, v) => acc + v.length, 0), - }; - } -} diff --git a/src/server/services/Services.ts b/src/server/services/Services.ts deleted file mode 100644 index 373700405b..0000000000 --- a/src/server/services/Services.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {PresenceService} from './PresenceService'; -import {PubsubService} from './PubSubService'; -import {BlocksServices} from './blocks/BlocksServices'; - -export class Services { - public readonly pubsub: PubsubService; - public readonly presence: PresenceService; - public readonly blocks: BlocksServices; - - constructor() { - this.pubsub = new PubsubService(); - this.presence = new PresenceService(); - this.blocks = new BlocksServices(this); - } -} diff --git a/src/server/services/blocks/BlocksServices.ts b/src/server/services/blocks/BlocksServices.ts deleted file mode 100644 index dad9f11715..0000000000 --- a/src/server/services/blocks/BlocksServices.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {MemoryStore} from './MemoryStore'; -import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller'; -import {Model, Patch} from '../../../json-crdt'; -import {SESSION} from '../../../json-crdt-patch/constants'; -import type {StoreModel, StorePatch} from './types'; -import type {Services} from '../Services'; - -const BLOCK_TTL = 1000 * 60 * 30; // 30 minutes - -const validatePatches = (patches: Pick[]) => { - for (const patch of patches) { - if (patch.blob.length > 2000) throw RpcError.validation('patch blob too large'); - // if (patch.seq > 500_000) throw RpcError.validation('patch seq too large'); - } -}; - -export class BlocksServices { - protected readonly store = new MemoryStore(); - - constructor(protected readonly services: Services) {} - - public async create(id: string, partialPatches: Pick[]) { - this.maybeGc(); - validatePatches(partialPatches); - if (!Array.isArray(partialPatches)) throw new Error('INVALID_PATCHES'); - const length = partialPatches.length; - const now = Date.now(); - if (!length) { - const rawModel = Model.withLogicalClock(SESSION.GLOBAL); - const model: StoreModel = { - id, - seq: -1, - blob: rawModel.toBinary(), - created: now, - updated: now, - }; - return await this.__create(id, model, []); - } - const rawPatches: Patch[] = []; - const patches: StorePatch[] = []; - let seq = 0; - for (; seq < length; seq++) { - const blob = partialPatches[seq].blob; - rawPatches.push(Patch.fromBinary(blob)); - patches.push({seq, created: now, blob}); - } - const rawModel = Model.fromPatches(rawPatches); - const model: StoreModel = { - id, - seq: seq - 1, - blob: rawModel.toBinary(), - created: now, - updated: now, - }; - return await this.__create(id, model, patches); - } - - private async __create(id: string, model: StoreModel, patches: StorePatch[]) { - await this.store.create(id, model, patches); - this.__emitUpd(id, model, patches); - return { - model, - patches, - }; - } - - private __emitUpd(id: string, model: StoreModel, patches: StorePatch[]) { - const msg = ['upd', {model, patches}]; - this.services.pubsub.publish(`__block:${id}`, msg).catch((error) => { - // tslint:disable-next-line:no-console - console.error('Error publishing block patches', error); - }); - } - - public async get(id: string) { - const {store} = this; - const result = await store.get(id); - if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); - const {model} = result; - return {model}; - } - - public async remove(id: string) { - await this.store.remove(id); - const msg = ['del']; - this.services.pubsub.publish(`__block:${id}`, msg).catch((error) => { - // tslint:disable-next-line:no-console - console.error('Error publishing block deletion', error); - }); - } - - public async scan( - id: string, - offset: number | undefined, - limit: number | undefined = 10, - returnStartModel: boolean = limit < 0, - ) { - const {store} = this; - if (typeof offset !== 'number') offset = await store.seq(id); - let min: number = 0, - max: number = 0; - if (!limit || Math.round(limit) !== limit) throw RpcError.badRequest('INVALID_LIMIT'); - if (limit > 0) { - min = Number(offset) || 0; - max = min + limit; - } else { - max = Number(offset) || 0; - min = max - limit; - } - if (min < 0) { - min = 0; - max = Math.abs(limit); - } - const patches = await store.history(id, min, max); - let model: Model | undefined; - if (returnStartModel) { - const startPatches = await store.history(id, 0, min); - if (startPatches.length) { - model = Model.fromPatches(startPatches.map((p) => Patch.fromBinary(p.blob))); - } - } - return {patches, model}; - } - - public async edit(id: string, patches: StorePatch[]) { - this.maybeGc(); - if (!Array.isArray(patches)) throw RpcError.validation('patches must be an array'); - if (!patches.length) throw RpcError.validation('patches must not be empty'); - const seq = patches[0].seq; - const {store} = this; - validatePatches(patches); - const {model} = await store.edit(id, patches); - this.__emitUpd(id, model, patches); - const expectedBlockSeq = seq + patches.length - 1; - const hadConcurrentEdits = model.seq !== expectedBlockSeq; - let patchesBack: StorePatch[] = []; - if (hadConcurrentEdits) patchesBack = await store.history(id, seq, model.seq); - return { - model, - patches: patchesBack, - }; - } - - public stats() { - return this.store.stats(); - } - - private maybeGc(): void { - if (Math.random() < 0.05) - this.gc().catch((error) => { - // tslint:disable-next-line:no-console - console.error('Error running gc', error); - }); - } - - private async gc(): Promise { - const ts = Date.now() - BLOCK_TTL; - const {store} = this; - await store.removeAccessedBefore(ts); - } -} diff --git a/src/server/services/blocks/MemoryStore.ts b/src/server/services/blocks/MemoryStore.ts deleted file mode 100644 index 491c7fec13..0000000000 --- a/src/server/services/blocks/MemoryStore.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {Model} from '../../../json-crdt'; -import {Patch} from '../../../json-crdt-patch'; -import type * as types from './types'; - -const tick = new Promise((resolve) => setImmediate(resolve)); - -export class MemoryStore implements types.Store { - protected readonly models = new Map(); - protected readonly patches = new Map(); - - public async get(id: string): Promise { - await tick; - const model = this.models.get(id); - if (!model) return; - return {model}; - } - - public async seq(id: string): Promise { - await tick; - return this.models.get(id)?.seq; - } - - public async create(id: string, model: types.StoreModel, patches: types.StorePatch[]): Promise { - await tick; - if (!Array.isArray(patches)) throw new Error('NO_PATCHES'); - if (this.models.has(id)) throw new Error('BLOCK_EXISTS'); - this.models.set(id, model); - this.patches.set(id, patches); - } - - public async edit(id: string, patches: types.StorePatch[]): Promise { - await tick; - if (!Array.isArray(patches) || !patches.length) throw new Error('NO_PATCHES'); - const block = this.models.get(id); - const existingPatches = this.patches.get(id); - if (!block || !existingPatches) throw new Error('BLOCK_NOT_FOUND'); - let seq = patches[0].seq; - const diff = seq - block.seq - 1; - if (block.seq + 1 < seq) throw new Error('PATCH_SEQ_TOO_HIGH'); - const model = Model.fromBinary(block.blob); - for (const patch of patches) { - if (seq !== patch.seq) throw new Error('PATCHES_OUT_OF_ORDER'); - model.applyPatch(Patch.fromBinary(patch.blob)); - patch.seq -= diff; - seq++; - } - block.seq += patches.length; - block.blob = model.toBinary(); - block.updated = Date.now(); - for (const patch of patches) existingPatches.push(patch); - return {model: block}; - } - - public async history(id: string, min: number, max: number): Promise { - await tick; - const patches = this.patches.get(id); - if (!patches) return []; - return patches.slice(min, max + 1); - } - - public async remove(id: string): Promise { - await tick; - return this.removeSync(id); - } - - private removeSync(id: string): boolean { - this.models.delete(id); - return this.patches.delete(id); - } - - public stats(): {blocks: number; patches: number} { - return { - blocks: this.models.size, - patches: [...this.patches.values()].reduce((acc, v) => acc + v.length, 0), - }; - } - - public async removeOlderThan(ts: number): Promise { - await tick; - for (const [id, block] of this.models) if (block.created < ts) this.removeSync(id); - } - - public async removeAccessedBefore(ts: number): Promise { - await tick; - for (const [id, block] of this.models) if (block.updated < ts) this.removeSync(id); - } -} diff --git a/src/server/services/blocks/types.ts b/src/server/services/blocks/types.ts deleted file mode 100644 index 89a22c4404..0000000000 --- a/src/server/services/blocks/types.ts +++ /dev/null @@ -1,75 +0,0 @@ -export interface StoreModel { - id: string; - seq: number; - created: number; - updated: number; - blob: Uint8Array; -} - -export interface StorePatch { - seq: number; - created: number; - blob: Uint8Array; -} - -export interface Store { - /** - * Create a new block. - * - * @param id Block ID. - * @param patches Initial patches to apply to a new block. - * @returns Newly created block data. - */ - create(id: string, model: StoreModel, patches: StorePatch[]): Promise; - - /** - * Retrieve an existing block. - * - * @param id Block ID. - * @returns Block data, or `undefined` if the block does not exist. - */ - get(id: string): Promise; - - /** - * Retrieve the sequence number of a block. - * - * @param id Block ID. - * @returns Block sequence number, or `undefined` if the block does not exist. - */ - seq(id: string): Promise; - - /** - * Edit an existing block by applying new patches. - * - * @param id Block ID. - * @param patches Patches to apply to the block. - * @returns Updated block data. - */ - edit(id: string, patches: StorePatch[]): Promise; - - /** - * Retrieve the history of patches for a block. - * - * @param id Block ID. - * @param min Minimum sequence number. - * @param max Maximum sequence number. - * @returns List of patches. - */ - history(id: string, min: number, max: number): Promise; - - /** - * Remove a block. - * - * @param id Block ID. - * @returns `true` if the block was removed, `false` if the block did not exist. - */ - remove(id: string): Promise; -} - -export interface StoreGetResult { - model: StoreModel; -} - -export interface StoreApplyResult { - model: StoreModel; -} diff --git a/src/server/services/types.ts b/src/server/services/types.ts deleted file mode 100644 index 8da0c546f2..0000000000 --- a/src/server/services/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type {ConnectionContext} from '../../reactive-rpc/server/context'; -import type {Services} from './Services'; - -export type MyCtx = ConnectionContext<{services: Services}>; From f03c2c299e07045b3b374aac17b7ea95c2d07091 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 15 Apr 2024 12:14:47 +0200 Subject: [PATCH 41/42] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20remove=20Reactive-RP?= =?UTF-8?q?C=20from=20testing=20and=20cleanup=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr.yml | 15 +-- .github/workflows/release.yml | 1 - package.json | 21 +--- yarn.lock | 231 +--------------------------------- 4 files changed, 7 insertions(+), 261 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f4c1ef4ef7..afc05a9023 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -45,17 +45,4 @@ jobs: - run: yarn demo:json-patch - run: yarn demo:json-pointer - run: yarn typedoc - e2e-rx-rpc: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: yarn - - run: yarn install --frozen-lockfile - - run: yarn test:reactive-rpc + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e96b382d8..feabb2f0e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,6 @@ jobs: - run: yarn test:cli:pack - run: yarn demo:json-patch - run: yarn demo:json-pointer - - run: yarn test:reactive-rpc - name: Semantic Release uses: cycjimmy/semantic-release-action@v4 env: diff --git a/package.json b/package.json index 3c5421ec5e..cf3e29f951 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,6 @@ "sync", "synchronization", "distributed-state", - "rpc", - "reactive-rpc", "marshaling", "serializations", "json-patch", @@ -89,17 +87,14 @@ "build": "yarn build:es2020", "jest": "node -r ts-node/register ./node_modules/.bin/jest", "test": "jest --maxWorkers 7", - "test:all": "yarn lint && yarn test && yarn build:all && yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack && yarn test:reactive-rpc && yarn demo:json-patch && yarn demo:json-pointer", + "test:all": "yarn lint && yarn test && yarn build:all && yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack && yarn demo:json-patch && yarn demo:json-pointer", "test:ci": "yarn jest --maxWorkers 3 --no-cache", "test:cli": "yarn test:cli:pointer && yarn test:cli:patch && yarn test:cli:pack", "test:cli:pointer": "./bin/json-pointer-test.js ./bin/json-pointer.js", "test:cli:patch": "./bin/json-patch-test.js ./bin/json-patch.js", "test:cli:pack": "./bin/json-pack-test.js ./bin/json-pack.js", - "test:reactive-rpc": "node -r ts-node/register/transpile-only src/reactive-rpc/__tests__/e2e/run.ts", - "test:reactive-rpc:jest": "TEST_E2E=1 jest --maxWorkers 1 --no-cache src/reactive-rpc/__tests__/e2e/", "demo:json-patch": "ts-node src/json-patch/__demos__/json-patch.ts", "demo:json-pointer": "ts-node src/json-pointer/__demos__/json-pointer.ts", - "demo:reactive-rpc:server": "ts-node src/reactive-rpc/__demos__/server.ts", "coverage": "yarn test --collectCoverage", "typedoc": "typedoc", "build:pages": "rimraf gh-pages && mkdir -p gh-pages && cp -r typedocs/* gh-pages && cp -r coverage gh-pages/coverage", @@ -136,8 +131,6 @@ "@types/benchmark": "^2.1.2", "@types/jest": "^29.5.12", "@types/quill": "^2.0.14", - "@types/react": "^18.2.74", - "@types/react-dom": "^18.2.24", "ajv": "^8.11.0", "app-root-path": "^3.1.0", "axios": "^1.3.5", @@ -145,9 +138,7 @@ "concurrently": "^8.0.1", "diamond-types-node": "1.0.2", "editing-traces": "https://github.com/streamich/editing-traces#6494020428530a6e382378b98d1d7e31334e2d7b", - "eventsource": "^2.0.2", "fast-json-patch": "^3.0.0-1", - "find-my-way": "^7.6.0", "fork-ts-checker-webpack-plugin": "^8.0.0", "gh-pages": "^5.0.0", "html-webpack-plugin": "^5.5.1", @@ -158,7 +149,6 @@ "json-crdt-traces": "https://github.com/streamich/json-crdt-traces#ec825401dc05cbb74b9e0b3c4d6527399f54d54d", "json-logic-js": "^2.0.1", "loro-crdt": "^0.4.1", - "memfs": "^4.8.1", "nodemon": "^3.0.0", "ot-text": "^1.0.2", "ot-text-unicode": "^4.0.0", @@ -166,12 +156,8 @@ "prettier": "^3.2.5", "pretty-quick": "^3.1.3", "quill-delta": "^5.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "redis-parser": "^3.0.0", "rimraf": "^5.0.0", "rope.js": "0.1.0", - "rxjs": "^7.5.5", "sorted-btree": "^1.8.1", "tinybench": "^2.4.0", "ts-jest": "^29.1.2", @@ -182,12 +168,9 @@ "tslint-config-common": "^1.6.2", "typedoc": "^0.25.13", "typescript": "^5.4.4", - "uWebSockets.js": "uNetworking/uWebSockets.js#v20.23.0", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", - "websocket": "^1.0.34", - "ws": "^8.16.0", "yjs": "13.6.9", "ywasm": "0.16.10" }, @@ -235,7 +218,6 @@ "json-expression", "json-hash", "json-ot", - "json-patch-multicore", "json-patch-ot", "json-patch", "json-pointer", @@ -246,7 +228,6 @@ "json-type", "json-type-value", "json-walk", - "reactive-rpc", "util" ] } diff --git a/yarn.lock b/yarn.lock index 1901ae0f7a..fbfcb3562e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -978,11 +978,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/prop-types@*": - version "15.7.10" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.10.tgz#892afc9332c4d62a5ea7e897fe48ed2085bbb08a" - integrity sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A== - "@types/qs@*": version "6.9.10" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8" @@ -1001,40 +996,11 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^18.2.24": - version "18.2.24" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.24.tgz#8dda8f449ae436a7a6e91efed8035d4ab03ff759" - integrity sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg== - dependencies: - "@types/react" "*" - -"@types/react@*": - version "18.2.37" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.37.tgz#0f03af69e463c0f19a356c2660dbca5d19c44cae" - integrity sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^18.2.74": - version "18.2.74" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.74.tgz#2d52eb80e4e7c4ea8812c89181d6d590b53f958c" - integrity sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/retry@0.12.2": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== -"@types/scheduler@*": - version "0.16.6" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.6.tgz#eb26db6780c513de59bee0b869ef289ad3068711" - integrity sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA== - "@types/seedrandom@^2.4.28": version "2.4.34" resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.34.tgz#c725cd0fc0442e2d3d0e5913af005686ffb7eb99" @@ -1629,13 +1595,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -bufferutil@^4.0.1: - version "4.0.8" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" - integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== - dependencies: - node-gyp-build "^4.3.0" - builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2012,19 +1971,11 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -csstype@^3.0.2, csstype@^3.0.6: +csstype@^3.0.6: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -2032,7 +1983,7 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@2.6.9, debug@^2.2.0: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2295,32 +2246,6 @@ es-module-lexer@^1.2.1: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -2386,11 +2311,6 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -eventsource@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" - integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== - execa@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -2474,18 +2394,6 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - -fast-decode-uri-component@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" - integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2511,13 +2419,6 @@ fast-loops@^1.1.3: resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== -fast-querystring@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" - integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== - dependencies: - fast-decode-uri-component "^1.0.1" - fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -2585,15 +2486,6 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-my-way@^7.6.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/find-my-way/-/find-my-way-7.7.0.tgz#d7b51ca6046782bcddd5a8435e99ad057e5a8876" - integrity sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ== - dependencies: - fast-deep-equal "^3.1.3" - fast-querystring "^1.0.0" - safe-regex2 "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3141,11 +3033,6 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - is-wsl@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" @@ -3612,7 +3499,7 @@ js-sdsl@^4.4.0: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847" integrity sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w== -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3752,13 +3639,6 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -loose-envify@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - loro-crdt@^0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/loro-crdt/-/loro-crdt-0.4.3.tgz#58ba611a4ef104897d84a4e75e5399cfb7a63cab" @@ -3850,7 +3730,7 @@ memfs@^3.4.1: dependencies: fs-monkey "^1.0.4" -memfs@^4.6.0, memfs@^4.8.1: +memfs@^4.6.0: version "4.8.1" resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.8.1.tgz#1e02c15c4397212a9a1b037fa4324c6f7dd45b47" integrity sha512-7q/AdPzf2WpwPlPL4v1kE2KsJsHl7EF4+hAeVzlyanr2+YnR21NVn9mDqo+7DEaKDRsQy8nvxPlKH4WqMtiO0w== @@ -4013,11 +3893,6 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -4036,11 +3911,6 @@ node-forge@^1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.3.0: - version "4.7.1" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.1.tgz#cd7d2eb48e594874053150a9418ac85af83ca8f7" - integrity sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg== - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4468,26 +4338,11 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.0" - react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" - readable-stream@^2.0.1: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -4524,18 +4379,6 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" -redis-errors@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" - integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== - -redis-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" - integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== - dependencies: - redis-errors "^1.0.0" - regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" @@ -4603,11 +4446,6 @@ resolve@^1.20.0, resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -ret@~0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" - integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== - retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -4637,7 +4475,7 @@ run-applescript@^7.0.0: resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== -rxjs@^7.5.5, rxjs@^7.8.1: +rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -4654,25 +4492,11 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex2@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-2.0.0.tgz#b287524c397c7a2994470367e0185e1916b1f5b9" - integrity sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ== - dependencies: - ret "~0.2.0" - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== - dependencies: - loose-envify "^1.1.0" - schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" @@ -5323,23 +5147,6 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedoc@^0.25.13: version "0.25.13" resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.13.tgz#9a98819e3b2d155a6d78589b46fa4c03768f0922" @@ -5355,10 +5162,6 @@ typescript@^5.4.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.4.tgz#eb2471e7b0a5f1377523700a21669dce30c2d952" integrity sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw== -uWebSockets.js@uNetworking/uWebSockets.js#v20.23.0: - version "20.23.0" - resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/49f8f1eb54435e4c3e3e436571e93d1f06aaabbb" - undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" @@ -5404,13 +5207,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -utf-8-validate@^5.0.2: - version "5.0.10" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" - integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== - dependencies: - node-gyp-build "^4.3.0" - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5612,18 +5408,6 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -websocket@^1.0.34: - version "1.0.34" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" - integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -5677,11 +5461,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" From bb5dcca5c4083d1258cd19c3a989ac4dcd5d9fc1 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 15 Apr 2024 12:15:11 +0200 Subject: [PATCH 42/42] =?UTF-8?q?chore:=20=F0=9F=A4=96=20remove=20Reactive?= =?UTF-8?q?-RPC=20and=20code=20related=20to=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - src/__tests__/README.md | 5 - src/json-cli/jj.ts | 20 - src/json-cli/routes/crdt.ts | 89 -- src/json-cli/routes/patch.ts | 40 - src/json-cli/routes/util.ts | 40 - src/json-type-cli/Cli.ts | 166 ---- src/json-type-cli/CliCodecs.ts | 49 - src/json-type-cli/RequestParam.ts | 1 - src/json-type-cli/codecs/cbor.ts | 24 - src/json-type-cli/codecs/json.ts | 24 - src/json-type-cli/codecs/json2.ts | 31 - src/json-type-cli/codecs/json4.ts | 31 - src/json-type-cli/codecs/msgpack.ts | 24 - src/json-type-cli/codecs/raw.ts | 16 - src/json-type-cli/codecs/text.ts | 16 - src/json-type-cli/codecs/tree.ts | 16 - src/json-type-cli/codecs/ubjson.ts | 24 - src/json-type-cli/defaultCodecs.ts | 24 - src/json-type-cli/defaultParams.ts | 30 - src/json-type-cli/index.ts | 11 - src/json-type-cli/methods.ts | 27 - src/json-type-cli/params/CliParamBool.ts | 19 - src/json-type-cli/params/CliParamCmd.ts | 52 -- src/json-type-cli/params/CliParamFile.ts | 28 - src/json-type-cli/params/CliParamFormat.ts | 22 - src/json-type-cli/params/CliParamHelp.ts | 79 -- src/json-type-cli/params/CliParamJson.ts | 19 - src/json-type-cli/params/CliParamNum.ts | 19 - src/json-type-cli/params/CliParamPlan.ts | 32 - src/json-type-cli/params/CliParamStdin.ts | 25 - src/json-type-cli/params/CliParamStdout.ts | 20 - src/json-type-cli/params/CliParamStr.ts | 19 - src/json-type-cli/params/CliParamUnd.ts | 16 - src/json-type-cli/params/CliParamVersion.ts | 16 - src/json-type-cli/types.ts | 30 - src/json-type-cli/util.ts | 9 - src/json-type/system/__tests__/demo.ts | 39 - src/util/router/README.md | 107 --- src/util/router/__bench__/realistic.bench.ts | 57 -- .../__bench__/router-benchmark.bench.ts | 80 -- src/util/router/__bench__/routers.ts | 68 -- src/util/router/__bench__/routes.ts | 113 --- src/util/router/__tests__/Destination.spec.ts | 63 -- src/util/router/__tests__/Router.spec.ts | 149 --- .../router/__tests__/benchmark-routes.spec.ts | 27 - src/util/router/__tests__/radix-tree.spec.ts | 27 - src/util/router/__tests__/trie-tree.spec.ts | 24 - src/util/router/__tests__/wildcard.spec.ts | 21 - src/util/router/codegen.ts | 30 - src/util/router/index.ts | 2 - src/util/router/router.ts | 141 --- src/util/router/steps.ts | 33 - src/util/router/tree.ts | 240 ----- src/util/router/types.ts | 3 - src/util/rx/BufferSubject.ts | 41 - src/util/rx/__tests__/BufferSubject.spec.ts | 846 ------------------ 57 files changed, 3225 deletions(-) delete mode 100644 src/json-cli/jj.ts delete mode 100644 src/json-cli/routes/crdt.ts delete mode 100644 src/json-cli/routes/patch.ts delete mode 100644 src/json-cli/routes/util.ts delete mode 100644 src/json-type-cli/Cli.ts delete mode 100644 src/json-type-cli/CliCodecs.ts delete mode 100644 src/json-type-cli/RequestParam.ts delete mode 100644 src/json-type-cli/codecs/cbor.ts delete mode 100644 src/json-type-cli/codecs/json.ts delete mode 100644 src/json-type-cli/codecs/json2.ts delete mode 100644 src/json-type-cli/codecs/json4.ts delete mode 100644 src/json-type-cli/codecs/msgpack.ts delete mode 100644 src/json-type-cli/codecs/raw.ts delete mode 100644 src/json-type-cli/codecs/text.ts delete mode 100644 src/json-type-cli/codecs/tree.ts delete mode 100644 src/json-type-cli/codecs/ubjson.ts delete mode 100644 src/json-type-cli/defaultCodecs.ts delete mode 100644 src/json-type-cli/defaultParams.ts delete mode 100644 src/json-type-cli/index.ts delete mode 100644 src/json-type-cli/methods.ts delete mode 100644 src/json-type-cli/params/CliParamBool.ts delete mode 100644 src/json-type-cli/params/CliParamCmd.ts delete mode 100644 src/json-type-cli/params/CliParamFile.ts delete mode 100644 src/json-type-cli/params/CliParamFormat.ts delete mode 100644 src/json-type-cli/params/CliParamHelp.ts delete mode 100644 src/json-type-cli/params/CliParamJson.ts delete mode 100644 src/json-type-cli/params/CliParamNum.ts delete mode 100644 src/json-type-cli/params/CliParamPlan.ts delete mode 100644 src/json-type-cli/params/CliParamStdin.ts delete mode 100644 src/json-type-cli/params/CliParamStdout.ts delete mode 100644 src/json-type-cli/params/CliParamStr.ts delete mode 100644 src/json-type-cli/params/CliParamUnd.ts delete mode 100644 src/json-type-cli/params/CliParamVersion.ts delete mode 100644 src/json-type-cli/types.ts delete mode 100644 src/json-type-cli/util.ts delete mode 100644 src/util/router/README.md delete mode 100644 src/util/router/__bench__/realistic.bench.ts delete mode 100644 src/util/router/__bench__/router-benchmark.bench.ts delete mode 100644 src/util/router/__bench__/routers.ts delete mode 100644 src/util/router/__bench__/routes.ts delete mode 100644 src/util/router/__tests__/Destination.spec.ts delete mode 100644 src/util/router/__tests__/Router.spec.ts delete mode 100644 src/util/router/__tests__/benchmark-routes.spec.ts delete mode 100644 src/util/router/__tests__/radix-tree.spec.ts delete mode 100644 src/util/router/__tests__/trie-tree.spec.ts delete mode 100644 src/util/router/__tests__/wildcard.spec.ts delete mode 100644 src/util/router/codegen.ts delete mode 100644 src/util/router/index.ts delete mode 100644 src/util/router/router.ts delete mode 100644 src/util/router/steps.ts delete mode 100644 src/util/router/tree.ts delete mode 100644 src/util/router/types.ts delete mode 100644 src/util/rx/BufferSubject.ts delete mode 100644 src/util/rx/__tests__/BufferSubject.spec.ts diff --git a/README.md b/README.md index 92f123fb27..ae4f2e2276 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ merging of changes in JSON data models, avoiding conflicts between replicas. - Very fast binary tree (Radix, AVL, Red-black*, Splay) implementations in JavaScript. - Very fast JSON Patch (and JSON Pointer) implementation in JavaScript, including many non-standard operations, and JSON Predicate implementation. - Very fast JSON Expression implementation in JavaScript. -- JSON Reactive RPC protocol (RPC with server push) implementation, for real-time collaborative apps. [json-joy]: https://jsonjoy.com diff --git a/src/__tests__/README.md b/src/__tests__/README.md index 75e4167ec7..a796eb8b98 100644 --- a/src/__tests__/README.md +++ b/src/__tests__/README.md @@ -56,8 +56,3 @@ yarn test:cli:pointer yarn test:cli:patch yarn test:cli:pack ``` - -### Reactive-RPC - -`/src/__tests__/reactive-rcp/` folder contains E2E tests for Reactive-RPC server -and its clients. See [README](./reactive-rpc/README.md) for more info. diff --git a/src/json-cli/jj.ts b/src/json-cli/jj.ts deleted file mode 100644 index 6353a52b1d..0000000000 --- a/src/json-cli/jj.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {createCli} from '../json-type-cli'; -import {ObjectValue} from '../json-type-value/ObjectValue'; -import {defineCrdtRoutes} from './routes/crdt'; -import {definePatchRoutes} from './routes/patch'; -import {defineUtilRoutes} from './routes/util'; - -// prettier-ignore -const router = - ( definePatchRoutes - ( defineCrdtRoutes - ( defineUtilRoutes - ( ObjectValue.create())))); - -const cli = createCli({ - router, - version: 'v' + require('../../package.json').version, - cmd: 'jj', -}); - -cli.run(); diff --git a/src/json-cli/routes/crdt.ts b/src/json-cli/routes/crdt.ts deleted file mode 100644 index a16355ffef..0000000000 --- a/src/json-cli/routes/crdt.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {Model} from '../../json-crdt'; -import {Encoder as VerboseEncoder} from '../../json-crdt/codec/structural/verbose/Encoder'; -import {Encoder as CompactEncoder} from '../../json-crdt/codec/structural/compact/Encoder'; -import {encode as encodePatch} from '../../json-crdt-patch/codec/compact/encode'; -import {ObjectType} from '../../json-type'; -import {ObjectValue} from '../../json-type-value/ObjectValue'; - -export const defineCrdtRoutes = >(r: ObjectValue) => - r.prop( - 'crdt.create', - (t) => - t - .Function( - t.Object( - t.propOpt('value', t.any).options({ - title: 'Initial value', - description: 'Initial value of the JSON CRDT document. Can be any JSON value.', - }), - t.propOpt('sid', t.num).options({ - title: 'Session ID', - description: - 'Session ID to use for the logic clock of the document. If not provided, a random number will be used.', - }), - t.propOpt('serverClock', t.bool).options({ - title: 'Use server clock', - description: - 'Whether to use server logical clock for this document. If set to true, the session ID will be ignored set to 1.', - }), - t - .propOpt( - 'codec', - t.Or(t.Const('binary'), t.Const('compact'), t.Const('verbose')).options({ - discriminator: ['?', ['==', ['$', ''], 'binary'], 0, ['?', ['==', ['$', ''], 'compact'], 1, 2]], - }), - ) - .options({ - title: 'Codec for the document', - description: 'Codec to use for the document. Defaults to binary.', - }), - ), - t.Object( - t.prop('doc', t.any).options({ - title: 'JSON CRDT document', - description: 'JSON CRDT document.', - }), - t.prop('codec', t.str), - t.prop('patch', t.any), - ), - ) - .options({ - title: 'Create a CRDT document', - description: 'Creates a new JSON CRDT document.', - }), - async ({value, sid, serverClock, codec}) => { - const model = serverClock - ? Model.withServerClock() - : sid !== undefined - ? Model.withLogicalClock(sid) - : Model.withLogicalClock(); - if (value !== undefined) model.api.root(value); - const patch = model.api.flush(); - const patchEncoded = patch && patch.ops.length ? encodePatch(patch) : null; - codec ??= 'binary'; - let doc: any = null; - switch (codec) { - case 'binary': { - doc = model.toBinary(); - break; - } - case 'compact': { - const encoder = new CompactEncoder(); - doc = encoder.encode(model); - break; - } - case 'verbose': { - const encoder = new VerboseEncoder(); - doc = encoder.encode(model); - break; - } - default: - throw new Error(`Unknown codec: ${codec}`); - } - return { - doc, - codec, - patch: patchEncoded, - }; - }, - ); diff --git a/src/json-cli/routes/patch.ts b/src/json-cli/routes/patch.ts deleted file mode 100644 index 4f0261168a..0000000000 --- a/src/json-cli/routes/patch.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {applyPatch, validateOperations, type Operation} from '../../json-patch'; -import type {ObjectType} from '../../json-type'; -import type {ObjectValue} from '../../json-type-value/ObjectValue'; - -export const definePatchRoutes = >(r: ObjectValue) => { - const {t, system} = r; - system.alias('JsonPatch', t.Array(t.any)); - - const Func = t - .Function( - t.Object( - t.prop('doc', t.any).options({ - title: 'A document', - description: 'A JSON document to apply the patch to.', - }), - t.prop('patch', t.Ref('JsonPatch')).options({ - title: 'A JSON Patch', - description: 'A JSON Patch to apply to the document.', - }), - ), - t.Object( - t.propOpt('doc', t.any).options({ - title: '', - description: 'JSON CRDT document.', - }), - ), - ) - .options({ - title: 'Apply JSON Patch to a document', - description: 'Applies a JSON Patch to a JSON document and returns the result.', - }); - - return r.prop('patch.apply', Func, async ({doc, patch}) => { - validateOperations(patch as Operation[]); - const result = applyPatch(doc, patch as Operation[], {mutate: true}); - return { - doc: result, - }; - }); -}; diff --git a/src/json-cli/routes/util.ts b/src/json-cli/routes/util.ts deleted file mode 100644 index 926696690d..0000000000 --- a/src/json-cli/routes/util.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {ObjectType} from '../../json-type'; -import {CliContext} from '../../json-type-cli/types'; -import {ObjectValue} from '../../json-type-value/ObjectValue'; - -export const defineUtilRoutes = >(r: ObjectValue) => - r.extend((t, prop) => [ - prop( - 'util.time', - t.Function(t.undef, t.num).options({ - title: 'Get time', - description: 'Returns the current time', - }), - async () => 132, - ), - - prop( - 'util.throw', - t.Function(t.undef, t.undef).options({ - title: 'Throw an error', - description: 'This method always throws an error.', - }), - async (req, ctx) => { - throw new Error(`${(ctx as CliContext).cli.argv![0]} always throws an error.`); - }, - ), - - prop( - 'util.schema', - t.Function(t.Object(t.prop('alias', t.str)), t.Object(t.prop('schema', t.any))).options({ - title: 'Get schema', - description: 'Returns the schema definition of a type', - }), - async ({alias}, ctx) => { - const resolved = (ctx as CliContext).cli.types.resolve(alias); - return { - schema: resolved.getType().getSchema(), - }; - }, - ), - ]); diff --git a/src/json-type-cli/Cli.ts b/src/json-type-cli/Cli.ts deleted file mode 100644 index ce042eb9d0..0000000000 --- a/src/json-type-cli/Cli.ts +++ /dev/null @@ -1,166 +0,0 @@ -import {parseArgs} from 'node:util'; -import {TypeSystem} from '../json-type/system/TypeSystem'; -import {ObjectValueCaller} from '../reactive-rpc/common/rpc/caller/ObjectValueCaller'; -import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; -import {formatError} from './util'; -import {defineBuiltinRoutes} from './methods'; -import {defaultParams} from './defaultParams'; -import {ObjectValue} from '../json-type-value/ObjectValue'; -import type {CliCodecs} from './CliCodecs'; -import type {TypeBuilder} from '../json-type/type/TypeBuilder'; -import type {WriteStream, ReadStream} from 'tty'; -import type {CliCodec, CliContext, CliParam, CliParamInstance} from './types'; -import type {Value} from '../json-type-value/Value'; - -export interface CliOptions> { - codecs: CliCodecs; - params?: CliParam[]; - router?: Router; - version?: string; - cmd?: string; - argv?: string[]; - stdout?: WriteStream; - stderr?: WriteStream; - stdin?: ReadStream; - exit?: (errno: number) => void; -} - -export class Cli = ObjectValue> { - public router: Router; - public readonly params: CliParam[]; - public readonly paramMap: Map; - public readonly types: TypeSystem; - public readonly t: TypeBuilder; - public readonly caller: ObjectValueCaller; - public readonly codecs: CliCodecs; - public request?: unknown; - public response?: unknown; - public argv: string[]; - public stdout: WriteStream; - public stderr: WriteStream; - public stdin: ReadStream; - public exit: (errno: number) => void; - public requestCodec: CliCodec; - public responseCodec: CliCodec; - public rawStdinInput?: Uint8Array; - public stdinInput?: unknown; - protected paramInstances: CliParamInstance[] = []; - - public constructor(public readonly options: CliOptions) { - let router = options.router ?? (ObjectValue.create(new TypeSystem()) as any); - router = defineBuiltinRoutes(router); - this.router = router; - this.params = options.params ?? defaultParams; - this.paramMap = new Map(); - for (const param of this.params) { - this.paramMap.set(param.param, param); - if (param.short) this.paramMap.set(param.short, param); - } - this.caller = new ObjectValueCaller({router, wrapInternalError: (err) => err}); - this.types = router.system; - this.t = this.types.t; - this.codecs = options.codecs; - this.requestCodec = this.codecs.get(this.codecs.defaultCodec); - this.responseCodec = this.codecs.get(this.codecs.defaultCodec); - this.argv = options.argv ?? process.argv.slice(2); - this.stdin = options.stdin ?? process.stdin; - this.stdout = options.stdout ?? process.stdout; - this.stderr = options.stderr ?? process.stderr; - this.exit = options.exit ?? process.exit; - } - - public run(): void { - this.runAsync(); - } - - public param(param: string): CliParam | undefined { - return this.paramMap.get(param); - } - - public async runAsync(): Promise { - try { - const system = this.router.system; - for (const key of this.router.keys()) system.alias(key, this.router.get(key).type); - const args = parseArgs({ - args: this.argv, - strict: false, - allowPositionals: true, - }); - for (let argKey of Object.keys(args.values)) { - let pointer = ''; - const value = args.values[argKey]; - const slashIndex = argKey.indexOf('/'); - if (slashIndex !== -1) { - pointer = argKey.slice(slashIndex); - argKey = argKey.slice(0, slashIndex); - } - const param = this.param(argKey); - if (!param) { - throw new Error(`Unknown parameter "${argKey}"`); - } - const instance = param.createInstance(this, pointer, value); - this.paramInstances.push(instance); - if (instance.onParam) await instance.onParam(); - } - const method = args.positionals[0]; - if (!method) { - const param = this.param('help'); - const instance = param?.createInstance(this, '', undefined); - instance?.onParam?.(); - throw new Error('No method specified'); - } - this.request = JSON.parse(args.positionals[1] || '{}'); - await this.readStdin(); - for (const instance of this.paramInstances) if (instance.onStdin) await instance.onStdin(); - for (const instance of this.paramInstances) if (instance.onRequest) await instance.onRequest(); - try { - const ctx: CliContext = {cli: this}; - for (const instance of this.paramInstances) if (instance.onBeforeCall) await instance.onBeforeCall(method, ctx); - const value = await this.caller.call(method, this.request as any, ctx); - this.response = (value as Value).data; - for (const instance of this.paramInstances) if (instance.onResponse) await instance.onResponse(); - const buf = this.responseCodec.encode(this.response); - this.stdout.write(buf); - } catch (err) { - const error = formatError(err); - const buf = this.responseCodec.encode(error); - this.stderr.write(buf); - this.exit(1); - } - } catch (err) { - const error = formatError(err); - const buf = JSON.stringify(error, null, 4); - this.stderr.write(buf); - this.exit(1); - } - } - - public cmd(): string { - return this.options.cmd ?? ''; - } - - private async getStdin(): Promise { - const stdin = this.stdin; - if (stdin.isTTY) return Buffer.alloc(0); - const result = []; - let length = 0; - for await (const chunk of stdin) { - result.push(chunk); - length += chunk.length; - } - return Buffer.concat(result, length); - } - - private async readStdin(): Promise { - const stdin = this.stdin; - const codec = this.requestCodec; - if (stdin.isTTY) return Object.create(null); - const input = await this.getStdin(); - if (codec.id === 'json') { - const str = input.toString().trim(); - if (!str) return Object.create(null); - } - this.rawStdinInput = bufferToUint8Array(input); - this.stdinInput = codec.decode(this.rawStdinInput); - } -} diff --git a/src/json-type-cli/CliCodecs.ts b/src/json-type-cli/CliCodecs.ts deleted file mode 100644 index d44f70dbe6..0000000000 --- a/src/json-type-cli/CliCodecs.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type {CliCodec} from './types'; - -const CODEC_REGEX = /(\w{0,32})(?:\:(\w{0,32}))?/; - -export class CliCodecs { - public defaultCodec: string = 'json4'; - public readonly codecs: Map> = new Map(); - - public register(codec: CliCodec): void { - this.codecs.set(codec.id, codec); - } - - public get(id: '' | string): CliCodec { - let codec = this.codecs.get(id); - if (!id) codec = this.codecs.get(this.defaultCodec); - if (!codec) throw new Error(`Codec not found: ${id}`); - return codec; - } - - /** - * Select codecs for the given format specifier. The format specifier is a - * string of the form: - * - * - * : - * - * Examples: - * - * json - * json:json - * cbor:json - * cbor - * - * @param format Codec specifier, e.g. `json:json` or `json`. - * @returns 2-tuple of selected codecs. - */ - public getCodecs(format: unknown): [request: CliCodec, response: CliCodec] { - if (typeof format !== 'string') throw new Error(`Invalid --format type.`); - if (!format) { - const codec = this.get(''); - return [codec, codec]; - } - const match = CODEC_REGEX.exec(format); - if (!match) throw new Error(`Invalid format: ${format}`); - const request = match[1]; - const response = match[2] ?? request; - return [this.get(request), this.get(response)]; - } -} diff --git a/src/json-type-cli/RequestParam.ts b/src/json-type-cli/RequestParam.ts deleted file mode 100644 index 61dc3621ef..0000000000 --- a/src/json-type-cli/RequestParam.ts +++ /dev/null @@ -1 +0,0 @@ -export class RequestParam {} diff --git a/src/json-type-cli/codecs/cbor.ts b/src/json-type-cli/codecs/cbor.ts deleted file mode 100644 index 20078fe649..0000000000 --- a/src/json-type-cli/codecs/cbor.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder'; -import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import type {CliCodec} from '../types'; - -export class CliCodecCbor implements CliCodec<'cbor'> { - public readonly id = 'cbor'; - public readonly description = 'CBOR codec'; - protected readonly encoder: CborEncoder; - protected readonly decoder: CborDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new CborEncoder(writer); - this.decoder = new CborDecoder(); - } - - encode(value: unknown): Uint8Array { - return this.encoder.encode(value); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/codecs/json.ts b/src/json-type-cli/codecs/json.ts deleted file mode 100644 index e6823a8df6..0000000000 --- a/src/json-type-cli/codecs/json.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; -import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import type {CliCodec} from '../types'; - -export class CliCodecJson implements CliCodec<'json'> { - public readonly id = 'json'; - public readonly description = 'JSON codec, which also supports binary data'; - protected readonly encoder: JsonEncoder; - protected readonly decoder: JsonDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new JsonEncoder(writer); - this.decoder = new JsonDecoder(); - } - - encode(value: unknown): Uint8Array { - return this.encoder.encode(value); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/codecs/json2.ts b/src/json-type-cli/codecs/json2.ts deleted file mode 100644 index 3a369a0967..0000000000 --- a/src/json-type-cli/codecs/json2.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; -import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import type {CliCodec} from '../types'; - -/** - * JSON codec with 2 space pretty-printing. - */ -export class CliCodecJson2 implements CliCodec<'json2'> { - public readonly id = 'json2'; - public readonly description = 'JSON codec with 2 space pretty-printing'; - protected readonly encoder: JsonEncoder; - protected readonly decoder: JsonDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new JsonEncoder(writer); - this.decoder = new JsonDecoder(); - } - - encode(value: unknown): Uint8Array { - const uint8 = this.encoder.encode(value); - const pojo = JSON.parse(Buffer.from(uint8).toString('utf8')); - const json = JSON.stringify(pojo, null, 2) + '\n'; - return bufferToUint8Array(Buffer.from(json, 'utf8')); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/codecs/json4.ts b/src/json-type-cli/codecs/json4.ts deleted file mode 100644 index d2607f4bd5..0000000000 --- a/src/json-type-cli/codecs/json4.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder'; -import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; -import type {CliCodec} from '../types'; - -/** - * JSON codec with 4 space pretty-printing. - */ -export class CliCodecJson4 implements CliCodec<'json4'> { - public readonly id = 'json4'; - public readonly description = 'JSON codec with 4 space pretty-printing'; - protected readonly encoder: JsonEncoder; - protected readonly decoder: JsonDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new JsonEncoder(writer); - this.decoder = new JsonDecoder(); - } - - encode(value: unknown): Uint8Array { - const uint8 = this.encoder.encode(value); - const pojo = JSON.parse(Buffer.from(uint8).toString('utf8')); - const json = JSON.stringify(pojo, null, 4) + '\n'; - return bufferToUint8Array(Buffer.from(json, 'utf8')); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/codecs/msgpack.ts b/src/json-type-cli/codecs/msgpack.ts deleted file mode 100644 index bd49f2f003..0000000000 --- a/src/json-type-cli/codecs/msgpack.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack'; -import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import type {CliCodec} from '../types'; - -export class CliCodecMsgpack implements CliCodec<'msgpack'> { - public readonly id = 'msgpack'; - public readonly description = 'MessagePack codec'; - protected readonly encoder: MsgPackEncoder; - protected readonly decoder: MsgPackDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new MsgPackEncoder(writer); - this.decoder = new MsgPackDecoder(); - } - - encode(value: unknown): Uint8Array { - return this.encoder.encode(value); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/codecs/raw.ts b/src/json-type-cli/codecs/raw.ts deleted file mode 100644 index 293c6f7a61..0000000000 --- a/src/json-type-cli/codecs/raw.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {CliCodec} from '../types'; - -export class CliCodecRaw implements CliCodec<'raw'> { - public readonly id = 'raw'; - public readonly description = 'Raw data, useful for strings and binary data'; - - encode(value: unknown): Uint8Array { - if (value instanceof Uint8Array) return value; - const str = String(value); - return new TextEncoder().encode(str); - } - - decode(bytes: Uint8Array): unknown { - throw new Error('Not available'); - } -} diff --git a/src/json-type-cli/codecs/text.ts b/src/json-type-cli/codecs/text.ts deleted file mode 100644 index 8ec24938ed..0000000000 --- a/src/json-type-cli/codecs/text.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {stringify} from '../../json-text/stringify'; -import type {CliCodec} from '../types'; - -export class CliCodecText implements CliCodec<'text'> { - public readonly id = 'text'; - public readonly description = 'Formatted JSON text'; - - encode(value: unknown): Uint8Array { - const str = stringify(value); - return new TextEncoder().encode(str + '\n'); - } - - decode(bytes: Uint8Array): unknown { - throw new Error('Not implemented'); - } -} diff --git a/src/json-type-cli/codecs/tree.ts b/src/json-type-cli/codecs/tree.ts deleted file mode 100644 index 8e273442f6..0000000000 --- a/src/json-type-cli/codecs/tree.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {toTree} from '../../json-text/toTree'; -import type {CliCodec} from '../types'; - -export class CliCodecTree implements CliCodec<'tree'> { - public readonly id = 'tree'; - public readonly description = 'Formatted JSON tree'; - - encode(value: unknown): Uint8Array { - const str = toTree(value); - return new TextEncoder().encode(str + '\n'); - } - - decode(bytes: Uint8Array): unknown { - throw new Error('Not implemented'); - } -} diff --git a/src/json-type-cli/codecs/ubjson.ts b/src/json-type-cli/codecs/ubjson.ts deleted file mode 100644 index ae0d57f84f..0000000000 --- a/src/json-type-cli/codecs/ubjson.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {UbjsonDecoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonDecoder'; -import {UbjsonEncoder} from '@jsonjoy.com/json-pack/lib/ubjson/UbjsonEncoder'; -import type {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import type {CliCodec} from '../types'; - -export class CliCodecUbjson implements CliCodec<'ubjson'> { - public readonly id = 'ubjson'; - public readonly description = 'UBJSON codec'; - protected readonly encoder: UbjsonEncoder; - protected readonly decoder: UbjsonDecoder; - - constructor(protected readonly writer: Writer) { - this.encoder = new UbjsonEncoder(writer); - this.decoder = new UbjsonDecoder(); - } - - encode(value: unknown): Uint8Array { - return this.encoder.encode(value); - } - - decode(bytes: Uint8Array): unknown { - return this.decoder.read(bytes); - } -} diff --git a/src/json-type-cli/defaultCodecs.ts b/src/json-type-cli/defaultCodecs.ts deleted file mode 100644 index 944c58be3b..0000000000 --- a/src/json-type-cli/defaultCodecs.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; -import {CliCodecs} from './CliCodecs'; -import {CliCodecCbor} from './codecs/cbor'; -import {CliCodecJson} from './codecs/json'; -import {CliCodecJson2} from './codecs/json2'; -import {CliCodecJson4} from './codecs/json4'; -import {CliCodecMsgpack} from './codecs/msgpack'; -import {CliCodecRaw} from './codecs/raw'; -import {CliCodecText} from './codecs/text'; -import {CliCodecTree} from './codecs/tree'; -import {CliCodecUbjson} from './codecs/ubjson'; - -export const defaultCodecs = new CliCodecs(); - -const writer = new Writer(16 * 1024); -defaultCodecs.register(new CliCodecJson(writer)); -defaultCodecs.register(new CliCodecJson2(writer)); -defaultCodecs.register(new CliCodecJson4(writer)); -defaultCodecs.register(new CliCodecCbor(writer)); -defaultCodecs.register(new CliCodecMsgpack(writer)); -defaultCodecs.register(new CliCodecUbjson(writer)); -defaultCodecs.register(new CliCodecText()); -defaultCodecs.register(new CliCodecTree()); -defaultCodecs.register(new CliCodecRaw()); diff --git a/src/json-type-cli/defaultParams.ts b/src/json-type-cli/defaultParams.ts deleted file mode 100644 index d872a40c7b..0000000000 --- a/src/json-type-cli/defaultParams.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {CliParamBool} from './params/CliParamBool'; -import {CliParamCmd} from './params/CliParamCmd'; -import {CliParamFile} from './params/CliParamFile'; -import {CliParamFormat} from './params/CliParamFormat'; -import {CliParamHelp} from './params/CliParamHelp'; -import {CliParamJson} from './params/CliParamJson'; -import {CliParamNum} from './params/CliParamNum'; -import {CliParamPlan} from './params/CliParamPlan'; -import {CliParamStdin} from './params/CliParamStdin'; -import {CliParamStdout} from './params/CliParamStdout'; -import {CliParamStr} from './params/CliParamStr'; -import {CliParamUnd} from './params/CliParamUnd'; -import {CliParamVersion} from './params/CliParamVersion'; -import type {CliParam} from './types'; - -export const defaultParams: CliParam[] = [ - new CliParamVersion(), - new CliParamHelp(), - new CliParamNum(), - new CliParamStr(), - new CliParamBool(), - new CliParamJson(), - new CliParamUnd(), - new CliParamFile(), - new CliParamCmd(), - new CliParamFormat(), - new CliParamStdin(), - new CliParamStdout(), - new CliParamPlan(), -]; diff --git a/src/json-type-cli/index.ts b/src/json-type-cli/index.ts deleted file mode 100644 index 4e921c6c2a..0000000000 --- a/src/json-type-cli/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {defaultCodecs} from './defaultCodecs'; -import {Cli, type CliOptions} from './Cli'; -import type {ObjectValue} from '../json-type-value/ObjectValue'; - -export const createCli = >(options: Partial>) => { - const cli = new Cli({ - codecs: defaultCodecs, - ...options, - }); - return cli; -}; diff --git a/src/json-type-cli/methods.ts b/src/json-type-cli/methods.ts deleted file mode 100644 index 5cd29e3827..0000000000 --- a/src/json-type-cli/methods.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type {CliContext} from './types'; -import type {ObjectType} from '../json-type/type'; -import type {ObjectValue} from '../json-type-value/ObjectValue'; - -export const defineBuiltinRoutes = >(r: ObjectValue) => { - return r.extend((t, r) => [ - r( - '.echo', - t.Function(t.any, t.any).options({ - title: 'Echo input', - description: 'Echo the input value back to the caller', - }), - async (req) => req, - ), - - r( - '.type', - t.Function(t.undef, t.any).options({ - title: 'Type information', - description: 'Returns whole type system of this CLI.', - }), - async (request, ctx) => { - return (ctx as CliContext).cli.types.exportTypes(); - }, - ), - ]); -}; diff --git a/src/json-type-cli/params/CliParamBool.ts b/src/json-type-cli/params/CliParamBool.ts deleted file mode 100644 index 4ca3ffa98b..0000000000 --- a/src/json-type-cli/params/CliParamBool.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {toPath} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamBool implements CliParam { - public readonly param = 'bool'; - public readonly short = 'b'; - public readonly title = 'Set boolean value'; - public readonly example = '--b/foo=true'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const value = Boolean(JSON.parse(String(rawValue))); - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamCmd.ts b/src/json-type-cli/params/CliParamCmd.ts deleted file mode 100644 index 791e9ab369..0000000000 --- a/src/json-type-cli/params/CliParamCmd.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {spawn} from 'child_process'; -import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import {bufferToUint8Array} from '@jsonjoy.com/util/lib/buffers/bufferToUint8Array'; -import {listToUint8} from '@jsonjoy.com/util/lib/buffers/concat'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamCmd implements CliParam { - public readonly param = 'cmd'; - public readonly short = 'c'; - public readonly title = 'Set value by command'; - public readonly example = "--c/foo='(echo [1,2,3]):json:/1'"; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - let cmd = String(rawValue); - let codec = cli.requestCodec; - let cmdPointer: string = ''; - if (cmd[0] === '(') { - const regex = /^\((.+)\)\:([a-z0-9]*)(\:([^\:]*))$/; - const match = regex.exec(cmd); - if (match) { - const [, cmd_, cmdCodec, , cmdPointer_] = match; - cmd = cmd_; - if (cmdCodec) codec = cli.codecs.get(cmdCodec); - if (cmdPointer_) { - validateJsonPointer(cmdPointer_); - cmdPointer = cmdPointer_; - } - } - } - const uint8 = await new Promise((resolve, reject) => { - const ls = spawn(cmd, {shell: true}); - const uint8s: Uint8Array[] = []; - ls.stdout.on('data', (data) => { - uint8s.push(bufferToUint8Array(data)); - }); - ls.stderr.on('data', (data) => { - reject(bufferToUint8Array(data)); - }); - ls.on('close', (code) => { - resolve(listToUint8(uint8s)); - }); - }); - let value = codec.decode(uint8); - if (cmdPointer) value = find(value, toPath(cmdPointer)).val; - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamFile.ts b/src/json-type-cli/params/CliParamFile.ts deleted file mode 100644 index ca6a517151..0000000000 --- a/src/json-type-cli/params/CliParamFile.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import {promises} from 'fs'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamFile implements CliParam { - public readonly param = 'file'; - public readonly short = 'f'; - public readonly title = 'Read value from file'; - public readonly example = '--f/foo=test.json:json:/a/b/c'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const [filename, fileCodec, filePointer] = String(rawValue).split(':'); - const codec = fileCodec ? cli.codecs.get(fileCodec) : cli.requestCodec; - const uint8 = await promises.readFile(filename); - let value = codec.decode(uint8); - if (filePointer) { - validateJsonPointer(filePointer); - value = find(value, toPath(filePointer)).val; - } - validateJsonPointer(pointer); - const path = toPath(pointer); - applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}); - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamFormat.ts b/src/json-type-cli/params/CliParamFormat.ts deleted file mode 100644 index 85d99e8f7b..0000000000 --- a/src/json-type-cli/params/CliParamFormat.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamFormat implements CliParam { - public readonly param = 'format'; - public readonly short = 'fmt'; - public readonly title = 'Codec to use for req/res'; - public readonly example = '--fmt=json:cbor'; - public readonly help = - 'Codec format to use for encoding/decoding request/response values. To specify both request and response codecs use "", or ":" to specify them separately.'; - public readonly examples = ['--format=cbor', '--format=cbor:json', '--fmt=json:msgpack', '--fmt=json:tree']; - public readonly createInstance = (cli: Cli, pointer: string, value: unknown) => - new (class implements CliParamInstance { - public readonly onParam = async () => { - const format = String(value); - const codecs = cli.codecs.getCodecs(format); - const [requestCodec, responseCodec] = codecs; - cli.requestCodec = requestCodec; - cli.responseCodec = responseCodec; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamHelp.ts b/src/json-type-cli/params/CliParamHelp.ts deleted file mode 100644 index c1d03710af..0000000000 --- a/src/json-type-cli/params/CliParamHelp.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamHelp implements CliParam { - public readonly param = 'help'; - public readonly short = 'h'; - public readonly title = 'Print help and exit'; - public readonly createInstance = (cli: Cli, pointer: string, value: unknown) => - new (class implements CliParamInstance { - public readonly onParam = async () => { - const paramLines = cli.params.map((param) => { - let line = `--${param.param}`; - if (param.short) line += ` or --${param.short}`; - line += ` - ${param.title}`; - if (param.example) line += `, eg. ${param.example}`; - return line; - }); - const methods: string[] = cli.router.keys().sort(); - const methodLines = methods.map((m) => { - const route = cli.router.get(m).type; - const schema = route.getSchema(); - let line = `- "${m}"`; - if (schema.title) line += ` - ${schema.title}`; - return line; - }); - const cmd = cli.cmd(); - const codecLines = [...cli.codecs.codecs.values()].map((codec) => `- "${codec.id}" - ${codec.description}`); - const text = ` -JSON Type CLI uses request/response paradigm to execute CLI commands. Each -command is identified by the name. Each command receives a JSON -object as the request payload and returns a JSON object as a response. - -Request payload is composed from the following sources: (1) command line -second parameter; (2) STDIN input; (3) command line options. - -Response object is returned to STDOUT. A part of it can be extracted using -the "--stdout" or "--out" option. - -Usage: - - ${cmd} '' - echo '' | ${cmd} - ${cmd} --= - -Examples: - - ${cmd} .echo '{ "foo": 123 }' - ${cmd} .echo --num/value=123 - ${cmd} .echo --json/value='{ "foo": 123 }' --out=/value - echo '{ "foo": 123 }' | ${cmd} .echo - ${cmd} .echo --s/foo=bar --format=cbor - cat data.cbor | ${cmd} .echo --format=cbor:json - ${cmd} .echo '{"foo": 123}' --f=cbor | ${cmd} .echo --f=cbor:tree - -Options: - - ${paramLines.join('\n ')} - -Formats: - - ${codecLines.join('\n ')} - -Method help: - - ${cmd} .type --out=/ - ${cmd} .type --out=//description - ${cmd} .type --out=//req - ${cmd} .type --out=//res --format=tree - -Methods: - - ${methodLines.join('\n ')} - - `; - cli.stdout.write(text); - cli.exit(0); - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamJson.ts b/src/json-type-cli/params/CliParamJson.ts deleted file mode 100644 index fc52d2a306..0000000000 --- a/src/json-type-cli/params/CliParamJson.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {toPath} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamJson implements CliParam { - public readonly param = 'json'; - public readonly short = 'j'; - public readonly title = 'Set JSON value'; - public readonly example = '--j/foo=\'{"a":1}\''; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const value = JSON.parse(String(rawValue)); - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamNum.ts b/src/json-type-cli/params/CliParamNum.ts deleted file mode 100644 index cddcdc8ff0..0000000000 --- a/src/json-type-cli/params/CliParamNum.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {toPath} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamNum implements CliParam { - public readonly param = 'num'; - public readonly short = 'n'; - public readonly title = 'Set number value'; - public readonly example = '--n/foo=123'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const value = Number(JSON.parse(String(rawValue))); - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamPlan.ts b/src/json-type-cli/params/CliParamPlan.ts deleted file mode 100644 index d955e5531d..0000000000 --- a/src/json-type-cli/params/CliParamPlan.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {toTree} from '../../json-text/toTree'; -import {AbstractType} from '../../json-type/type/classes'; -import type {Cli} from '../Cli'; -import type {CliContext, CliParam, CliParamInstance} from '../types'; -import {formatError} from '../util'; - -export class CliParamPlan implements CliParam { - public readonly param = 'plan'; - public readonly title = 'Show execution plan'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onBeforeCall = async (method: string, ctx: CliContext) => { - const fn = cli.router.get(method).type; - if (!fn) throw new Error(`Method ${method} not found`); - const out: any = { - Method: method, - }; - try { - const validator = (fn.req as AbstractType).validator('object'); - const error = validator(cli.request); - if (error) throw error; - out.Validation = 'OK'; - } catch (error) { - out.Validation = 'Failed'; - out.ValidationError = formatError(error); - } - out.Request = cli.request; - cli.stdout.write(toTree(out) + '\n'); - cli.exit(0); - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamStdin.ts b/src/json-type-cli/params/CliParamStdin.ts deleted file mode 100644 index 93f532f484..0000000000 --- a/src/json-type-cli/params/CliParamStdin.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamStdin implements CliParam { - public readonly param = 'stdin'; - public readonly short = 'in'; - public readonly title = 'Read data from STDIN'; - public readonly example = '--in or --in/to=/from'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => { - if (rawValue === true) rawValue = ''; - return new (class implements CliParamInstance { - public readonly onStdin = async () => { - const fromPointer = String(rawValue); - validateJsonPointer(fromPointer); - validateJsonPointer(pointer); - const from = toPath(fromPointer); - const path = toPath(pointer); - const value = find(cli.stdinInput, from).val; - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); - }; -} diff --git a/src/json-type-cli/params/CliParamStdout.ts b/src/json-type-cli/params/CliParamStdout.ts deleted file mode 100644 index f1de49672b..0000000000 --- a/src/json-type-cli/params/CliParamStdout.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {find, toPath, validateJsonPointer} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamStdout implements CliParam { - public readonly param = 'stdout'; - public readonly short = 'out'; - public readonly title = 'Write data to STDOUT'; - public readonly example = '--out=/foo'; - public readonly createInstance = (cli: Cli, _: string, rawValue: unknown) => { - return new (class implements CliParamInstance { - public readonly onResponse = async () => { - const pointer = String(rawValue); - validateJsonPointer(pointer); - const path = toPath(pointer); - cli.response = find(cli.response, path).val; - }; - })(); - }; -} diff --git a/src/json-type-cli/params/CliParamStr.ts b/src/json-type-cli/params/CliParamStr.ts deleted file mode 100644 index 5ead959a93..0000000000 --- a/src/json-type-cli/params/CliParamStr.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {toPath} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamStr implements CliParam { - public readonly param = 'str'; - public readonly short = 's'; - public readonly title = 'Set string value'; - public readonly example = '--s/foo=abc'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const value = String(rawValue); - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamUnd.ts b/src/json-type-cli/params/CliParamUnd.ts deleted file mode 100644 index 1db162cd09..0000000000 --- a/src/json-type-cli/params/CliParamUnd.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {applyPatch} from '../../json-patch'; -import {toPath} from '../../json-pointer'; -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamUnd implements CliParam { - public readonly param = 'und'; - public readonly title = 'Set undefined value'; - public readonly createInstance = (cli: Cli, pointer: string, rawValue: unknown) => - new (class implements CliParamInstance { - public readonly onRequest = async () => { - const path = toPath(pointer); - cli.request = applyPatch(cli.request, [{op: 'add', path, value: undefined}], {mutate: true}).doc; - }; - })(); -} diff --git a/src/json-type-cli/params/CliParamVersion.ts b/src/json-type-cli/params/CliParamVersion.ts deleted file mode 100644 index 3cda0f8fd3..0000000000 --- a/src/json-type-cli/params/CliParamVersion.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {Cli} from '../Cli'; -import type {CliParam, CliParamInstance} from '../types'; - -export class CliParamVersion implements CliParam { - public readonly param = 'version'; - public readonly short = 'v'; - public readonly title = 'Print version and exit'; - public readonly createInstance = (cli: Cli, pointer: string, value: unknown) => - new (class implements CliParamInstance { - public readonly onParam = async () => { - const version = cli.options.version ?? '0.0.0-unknown'; - cli.stdout.write(version + '\n'); - cli.exit(0); - }; - })(); -} diff --git a/src/json-type-cli/types.ts b/src/json-type-cli/types.ts deleted file mode 100644 index e212372dc8..0000000000 --- a/src/json-type-cli/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type {ObjectValue} from '../json-type-value/ObjectValue'; -import type {Cli} from './Cli'; - -export interface CliCodec { - id: Id; - description: string; - encode: (value: unknown) => Uint8Array; - decode: (bytes: Uint8Array) => unknown; -} - -export interface CliContext = ObjectValue> { - cli: Cli; -} - -export interface CliParam { - param: string; - short?: string; - title: string; - example?: string; - examples?: string[]; - createInstance: (cli: Cli, pointer: string, value: unknown) => CliParamInstance; -} - -export interface CliParamInstance { - onParam?: () => Promise; - onStdin?: () => Promise; - onRequest?: () => Promise; - onBeforeCall?: (method: string, ctx: CliContext) => Promise; - onResponse?: () => Promise; -} diff --git a/src/json-type-cli/util.ts b/src/json-type-cli/util.ts deleted file mode 100644 index 8d4f35d624..0000000000 --- a/src/json-type-cli/util.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Value} from '../json-type-value/Value'; -import {RpcError} from '../reactive-rpc/common/rpc/caller'; - -export const formatError = (err: unknown): unknown => { - if (err instanceof Value) return formatError(err.data); - if (err instanceof RpcError) return err.toJson(); - if (err instanceof Error) return {message: err.message, stack: err.stack}; - return err; -}; diff --git a/src/json-type/system/__tests__/demo.ts b/src/json-type/system/__tests__/demo.ts index fe03179686..0b12c41afe 100644 --- a/src/json-type/system/__tests__/demo.ts +++ b/src/json-type/system/__tests__/demo.ts @@ -1,8 +1,4 @@ -import {Observable, map} from 'rxjs'; -import {TypedApiCaller} from '../../../reactive-rpc/common/rpc/caller/TypedApiCaller'; -import {FilterFunctions, SchemaOf} from '../../type'; import {TypeSystem} from '../TypeSystem'; -import {TypeOf} from '../../schema'; const createTypes = (system: TypeSystem) => { const t = system.t; @@ -59,38 +55,3 @@ system.importTypes(types); type Types = typeof types; type Ctx = {ip?: string}; - -const rpc = new TypedApiCaller, Ctx>({system}); - -// rpc.implement('') - -type Fn = TypeOf>; - -rpc.implement('MuBlockGet', { - validate: (req: unknown) => {}, - call: async (request, ctx) => { - const response = { - block: { - id: 'adf', - data: 'adf', - }, - }; - return response; - }, -}); - -rpc.implement('MuBlockListen', { - pretty: true, - call$: (request, ctx) => { - return request.pipe( - map(() => { - return { - block: { - id: 'adf', - data: 'adf', - }, - }; - }), - ); - }, -}); diff --git a/src/util/router/README.md b/src/util/router/README.md deleted file mode 100644 index 8cbc7bd186..0000000000 --- a/src/util/router/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# `json-joy` router - - -## Benchmarks - -Comparing `json-joy` against the second fastest router `find-my-way` (used in Fastify): - -``` -ts-node benchmarks/util/router/realistic.bench.ts -json-joy router x 1,799,920 ops/sec ยฑ0.74% (99 runs sampled), 556 ns/op -find-my-way x 389,132 ops/sec ยฑ4.60% (87 runs sampled), 2570 ns/op -json-joy router: GET /ping x 151,628,101 ops/sec ยฑ1.83% (87 runs sampled), 7 ns/op -find-my-way: GET /ping x 14,820,512 ops/sec ยฑ0.23% (100 runs sampled), 67 ns/op -json-joy router: GET /pong x 103,442,010 ops/sec ยฑ0.49% (101 runs sampled), 10 ns/op -find-my-way: GET /pong x 12,396,065 ops/sec ยฑ0.14% (95 runs sampled), 81 ns/op -json-joy router: POST /ping x 155,689,270 ops/sec ยฑ0.26% (96 runs sampled), 6 ns/op -find-my-way: POST /ping x 14,964,742 ops/sec ยฑ0.62% (100 runs sampled), 67 ns/op -json-joy router: POST /echo x 103,757,580 ops/sec ยฑ0.24% (94 runs sampled), 10 ns/op -find-my-way: POST /echo x 19,484,996 ops/sec ยฑ0.13% (100 runs sampled), 51 ns/op -json-joy router: GET /info x 86,407,568 ops/sec ยฑ0.45% (101 runs sampled), 12 ns/op -find-my-way: GET /info x 19,090,564 ops/sec ยฑ0.23% (99 runs sampled), 52 ns/op -json-joy router: GET /types x 82,040,568 ops/sec ยฑ0.19% (93 runs sampled), 12 ns/op -find-my-way: GET /types x 18,258,167 ops/sec ยฑ0.13% (94 runs sampled), 55 ns/op -json-joy router: PUT /events x 155,566,843 ops/sec ยฑ0.30% (95 runs sampled), 6 ns/op -find-my-way: PUT /events x 17,947,965 ops/sec ยฑ0.29% (95 runs sampled), 56 ns/op -json-joy router: GET /users x 67,873,498 ops/sec ยฑ0.19% (97 runs sampled), 15 ns/op -find-my-way: GET /users x 18,427,182 ops/sec ยฑ0.70% (97 runs sampled), 54 ns/op -json-joy router: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 24,268,077 ops/sec ยฑ0.38% (98 runs sampled), 41 ns/op -find-my-way: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 5,830,219 ops/sec ยฑ0.20% (101 runs sampled), 172 ns/op -json-joy router: GET /users/123 x 23,750,636 ops/sec ยฑ0.92% (98 runs sampled), 42 ns/op -find-my-way: GET /users/123 x 8,883,381 ops/sec ยฑ0.15% (98 runs sampled), 113 ns/op -json-joy router: DELETE /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 21,578,200 ops/sec ยฑ0.98% (96 runs sampled), 46 ns/op -find-my-way: DELETE /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 6,081,393 ops/sec ยฑ0.82% (96 runs sampled), 164 ns/op -json-joy router: POST /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 19,981,015 ops/sec ยฑ0.16% (96 runs sampled), 50 ns/op -find-my-way: POST /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx x 6,094,531 ops/sec ยฑ0.27% (96 runs sampled), 164 ns/op -json-joy router: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers x 17,566,830 ops/sec ยฑ0.92% (99 runs sampled), 57 ns/op -find-my-way: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers x 4,584,840 ops/sec ยฑ0.27% (97 runs sampled), 218 ns/op -json-joy router: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 13,432,092 ops/sec ยฑ0.26% (100 runs sampled), 74 ns/op -find-my-way: GET /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 2,959,002 ops/sec ยฑ0.17% (100 runs sampled), 338 ns/op -json-joy router: POST /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 12,074,790 ops/sec ยฑ1.05% (98 runs sampled), 83 ns/op -find-my-way: POST /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 3,211,678 ops/sec ยฑ0.31% (96 runs sampled), 311 ns/op -json-joy router: DELETE /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 12,878,647 ops/sec ยฑ0.30% (99 runs sampled), 78 ns/op -find-my-way: DELETE /users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy x 3,214,022 ops/sec ยฑ0.69% (95 runs sampled), 311 ns/op -json-joy router: GET /posts x 47,369,117 ops/sec ยฑ1.64% (83 runs sampled), 21 ns/op -find-my-way: GET /posts x 11,424,540 ops/sec ยฑ0.17% (101 runs sampled), 88 ns/op -json-joy router: GET /posts/search x 91,571,586 ops/sec ยฑ1.22% (94 runs sampled), 11 ns/op -find-my-way: GET /posts/search x 6,931,399 ops/sec ยฑ0.16% (100 runs sampled), 144 ns/op -json-joy router: POST /posts x 71,501,062 ops/sec ยฑ0.91% (93 runs sampled), 14 ns/op -find-my-way: POST /posts x 13,755,641 ops/sec ยฑ0.21% (98 runs sampled), 73 ns/op -json-joy router: GET /posts/jhasdf982lsd x 20,794,603 ops/sec ยฑ0.79% (97 runs sampled), 48 ns/op -find-my-way: GET /posts/jhasdf982lsd x 6,220,203 ops/sec ยฑ0.17% (100 runs sampled), 161 ns/op -json-joy router: POST /posts/jhasdf982lsd x 17,002,131 ops/sec ยฑ0.24% (94 runs sampled), 59 ns/op -find-my-way: POST /posts/jhasdf982lsd x 6,783,108 ops/sec ยฑ0.64% (98 runs sampled), 147 ns/op -json-joy router: DELETE /posts/jhasdf982lsd x 18,912,944 ops/sec ยฑ0.38% (95 runs sampled), 53 ns/op -find-my-way: DELETE /posts/jhasdf982lsd x 8,353,962 ops/sec ยฑ0.84% (99 runs sampled), 120 ns/op -json-joy router: GET /posts/jhasdf982lsd/tags x 16,857,334 ops/sec ยฑ4.01% (92 runs sampled), 59 ns/op -find-my-way: GET /posts/jhasdf982lsd/tags x 5,169,375 ops/sec ยฑ0.39% (98 runs sampled), 193 ns/op -json-joy router: GET /posts/jhasdf982lsd/tags/top x 16,012,780 ops/sec ยฑ0.38% (99 runs sampled), 62 ns/op -find-my-way: GET /posts/jhasdf982lsd/tags/top x 4,062,952 ops/sec ยฑ0.65% (97 runs sampled), 246 ns/op -json-joy router: GET /posts/jhasdf982lsd/tags/123 x 12,144,904 ops/sec ยฑ0.21% (100 runs sampled), 82 ns/op -find-my-way: GET /posts/jhasdf982lsd/tags/123 x 4,036,060 ops/sec ยฑ0.15% (98 runs sampled), 248 ns/op -json-joy router: DELETE /posts/jhasdf982lsd/tags/123 x 11,797,726 ops/sec ยฑ0.50% (94 runs sampled), 85 ns/op -find-my-way: DELETE /posts/jhasdf982lsd/tags/123 x 5,269,916 ops/sec ยฑ0.10% (100 runs sampled), 190 ns/op -json-joy router: POST /posts/jhasdf982lsd/tags x 15,483,784 ops/sec ยฑ0.20% (100 runs sampled), 65 ns/op -find-my-way: POST /posts/jhasdf982lsd/tags x 5,586,607 ops/sec ยฑ0.10% (100 runs sampled), 179 ns/op -json-joy router: GET /api/collections x 89,443,474 ops/sec ยฑ1.14% (93 runs sampled), 11 ns/op -find-my-way: GET /api/collections x 11,365,669 ops/sec ยฑ0.15% (102 runs sampled), 88 ns/op -json-joy router: POST /api/collections x 88,558,408 ops/sec ยฑ1.15% (93 runs sampled), 11 ns/op -find-my-way: POST /api/collections x 11,427,491 ops/sec ยฑ0.14% (98 runs sampled), 88 ns/op -json-joy router: GET /api/collections/123 x 15,327,034 ops/sec ยฑ0.21% (101 runs sampled), 65 ns/op -find-my-way: GET /api/collections/123 x 6,654,562 ops/sec ยฑ0.10% (102 runs sampled), 150 ns/op -json-joy router: PUT /api/collections/123 x 14,766,966 ops/sec ยฑ0.23% (100 runs sampled), 68 ns/op -find-my-way: PUT /api/collections/123 x 7,446,186 ops/sec ยฑ0.14% (99 runs sampled), 134 ns/op -json-joy router: POST /api/collections/123 x 13,397,211 ops/sec ยฑ0.23% (100 runs sampled), 75 ns/op -find-my-way: POST /api/collections/123 x 5,801,550 ops/sec ยฑ6.34% (92 runs sampled), 172 ns/op -json-joy router: DELETE /api/collections/123 x 16,470,990 ops/sec ยฑ0.26% (99 runs sampled), 61 ns/op -find-my-way: DELETE /api/collections/123 x 6,618,101 ops/sec ยฑ4.54% (90 runs sampled), 151 ns/op -json-joy router: GET /api/collections/123/documents x 10,819,176 ops/sec ยฑ20.48% (90 runs sampled), 92 ns/op -find-my-way: GET /api/collections/123/documents x 5,083,007 ops/sec ยฑ0.10% (100 runs sampled), 197 ns/op -json-joy router: POST /api/collections/123/documents x 11,471,312 ops/sec ยฑ0.18% (101 runs sampled), 87 ns/op -find-my-way: POST /api/collections/123/documents x 5,024,672 ops/sec ยฑ0.14% (97 runs sampled), 199 ns/op -json-joy router: GET /api/collections/123/documents/456 x 8,776,775 ops/sec ยฑ0.25% (98 runs sampled), 114 ns/op -find-my-way: GET /api/collections/123/documents/456 x 3,892,995 ops/sec ยฑ0.30% (100 runs sampled), 257 ns/op -json-joy router: PUT /api/collections/123/documents/456 x 9,946,515 ops/sec ยฑ1.36% (97 runs sampled), 101 ns/op -find-my-way: PUT /api/collections/123/documents/456 x 4,347,762 ops/sec ยฑ0.62% (98 runs sampled), 230 ns/op -json-joy router: POST /api/collections/123/documents/456 x 8,120,919 ops/sec ยฑ0.44% (99 runs sampled), 123 ns/op -find-my-way: POST /api/collections/123/documents/456 x 3,882,741 ops/sec ยฑ1.26% (92 runs sampled), 258 ns/op -json-joy router: DELETE /api/collections/123/documents/456 x 10,739,860 ops/sec ยฑ0.89% (97 runs sampled), 93 ns/op -find-my-way: DELETE /api/collections/123/documents/456 x 4,483,659 ops/sec ยฑ1.17% (98 runs sampled), 223 ns/op -json-joy router: GET /api/collections/123/documents/456/revisions x 8,553,359 ops/sec ยฑ0.91% (98 runs sampled), 117 ns/op -find-my-way: GET /api/collections/123/documents/456/revisions x 3,379,187 ops/sec ยฑ0.17% (101 runs sampled), 296 ns/op -json-joy router: GET /api/collections/123/documents/456/revisions/find x 8,092,442 ops/sec ยฑ1.10% (97 runs sampled), 124 ns/op -find-my-way: GET /api/collections/123/documents/456/revisions/find x 2,829,474 ops/sec ยฑ0.17% (100 runs sampled), 353 ns/op -json-joy router: POST /api/collections/123/documents/456/revisions x 7,750,406 ops/sec ยฑ0.93% (99 runs sampled), 129 ns/op -find-my-way: POST /api/collections/123/documents/456/revisions x 3,412,482 ops/sec ยฑ0.14% (98 runs sampled), 293 ns/op -json-joy router: GET /api/collections/123/documents/456/revisions/789 x 7,133,453 ops/sec ยฑ0.51% (98 runs sampled), 140 ns/op -find-my-way: GET /api/collections/123/documents/456/revisions/789 x 2,851,795 ops/sec ยฑ0.80% (95 runs sampled), 351 ns/op -json-joy router: DELETE /api/collections/123/documents/456/revisions/789 x 8,168,205 ops/sec ยฑ0.24% (96 runs sampled), 122 ns/op -find-my-way: DELETE /api/collections/123/documents/456/revisions/789 x 3,282,963 ops/sec ยฑ0.10% (98 runs sampled), 305 ns/op -json-joy router: GET /files/user123-movies/2019/01/01/1.mp4 x 13,990,918 ops/sec ยฑ0.19% (100 runs sampled), 71 ns/op -find-my-way: GET /files/user123-movies/2019/01/01/1.mp4 x 5,132,706 ops/sec ยฑ0.90% (101 runs sampled), 195 ns/op -json-joy router: PUT /files/user123-movies/2019/01/01/1.mp4 x 13,951,426 ops/sec ยฑ0.23% (99 runs sampled), 72 ns/op -find-my-way: PUT /files/user123-movies/2019/01/01/1.mp4 x 5,184,318 ops/sec ยฑ0.23% (99 runs sampled), 193 ns/op -json-joy router: GET /static/some/path/to/file.txt x 15,788,214 ops/sec ยฑ1.06% (99 runs sampled), 63 ns/op -find-my-way: GET /static/some/path/to/file.txt x 8,104,177 ops/sec ยฑ0.14% (100 runs sampled), 123 ns/op -Fastest is json-joy router: POST /ping,json-joy router: PUT /events,json-joy router: GET /ping -``` diff --git a/src/util/router/__bench__/realistic.bench.ts b/src/util/router/__bench__/realistic.bench.ts deleted file mode 100644 index 19a41c970d..0000000000 --- a/src/util/router/__bench__/realistic.bench.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* tslint:disable no-console */ - -import {definitions, routes} from './routes'; -import {routers} from './routers'; - -const {Suite} = require('benchmark'); -const suite = new Suite(); -const noop = () => {}; - -for (const router of routers) { - for (const definition of definitions) router.register(definition, noop); - if (router.finalize) router.finalize(); - // console.log(); - // console.log(`Structure "${router.name}":`); - // if (router.print) router.print(); - // console.log(); - // console.log(`Test "${router.name}":`); - for (const [method, path] of routes) { - const match = router.find(method, path); - // console.log(router.name, `${method} ${path}`, match); - } -} - -for (const router of routers) { - const find = router.find; - suite.add(`${router.name}`, () => { - find('DELETE', '/api/collections/123/documents/456/revisions/789'); - find('GET', '/users'); - find('GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'); - find('GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers'); - find('GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'); - find('POST', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'); - find('PUT', '/files/user123-movies/2019/01/01/1.mp4'); - find('GET', '/files/user123-movies/2019/01/01/1.mp4'); - find('GET', '/static/some/path/to/file.txt'); - find('GET', '/ping'); - find('GET', '/pong'); - find('GET', '/info'); - }); -} - -for (const [method, path] of routes) { - for (const router of routers) { - suite.add(`${router.name}: ${method} ${path}`, () => { - router.find(method, path); - }); - } -} - -suite - .on('cycle', (event: any) => { - console.log(String(event.target) + `, ${Math.round(1000000000 / event.target.hz)} ns/op`); - }) - .on('complete', () => { - console.log('Fastest is ' + suite.filter('fastest').map('name')); - }) - .run(); diff --git a/src/util/router/__bench__/router-benchmark.bench.ts b/src/util/router/__bench__/router-benchmark.bench.ts deleted file mode 100644 index 4bc3f9bd70..0000000000 --- a/src/util/router/__bench__/router-benchmark.bench.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* tslint:disable no-console */ - -import {Router} from '..'; - -const router = new Router(); - -const noop = () => {}; -router.add('GET /user', noop); -router.add('GET /user/comments', noop); -router.add('GET /user/avatar', noop); -router.add('GET /user/lookup/username/{username}', noop); -router.add('GET /user/lookup/email/{address}', noop); -router.add('GET /event/{id}', noop); -router.add('GET /event/{id}/comments', noop); -router.add('POST /event/{id}/comment', noop); -router.add('GET /map/{location}/events', noop); -router.add('GET /status', noop); -router.add('GET /very/deep/nested/route/hello/there', noop); -router.add('GET /static/{::\n}', noop); - -// router.add('GET /posts', noop); -// router.add('GET /posts/search', noop); -// router.add('GET /posts/{post}', noop); -// router.add('PUT /posts/{post}', noop); -// router.add('POST /posts/{post}', noop); -// router.add('DELETE /posts/{post}', noop); -// router.add('POST /posts', noop); -console.log(router + ''); - -const matcher = router.compile(); -console.log(matcher + ''); - -const operations = 1e6; - -console.time('short static'); -for (let i = 0; i < operations; i++) { - matcher('GET /user'); -} -console.timeEnd('short static'); - -console.time('static with same radix'); -for (let i = 0; i < operations; i++) { - matcher('GET /user/comments'); -} -console.timeEnd('static with same radix'); - -console.time('dynamic route'); -for (let i = 0; i < operations; i++) { - matcher('GET /user/lookup/username/john'); -} -console.timeEnd('dynamic route'); - -console.time('mixed static dynamic'); -for (let i = 0; i < operations; i++) { - matcher('GET /event/abcd1234/comments'); -} -console.timeEnd('mixed static dynamic'); - -console.time('long static'); -for (let i = 0; i < operations; i++) { - matcher('GET /very/deep/nested/route/hello/there'); -} -console.timeEnd('long static'); - -console.time('wildcard'); -for (let i = 0; i < operations; i++) { - matcher('GET /static/index.html'); -} -console.timeEnd('wildcard'); - -console.time('all together'); -for (let i = 0; i < operations; i++) { - matcher('GET /user'); - matcher('GET /user/comments'); - matcher('GET /user/lookup/username/john'); - matcher('GET /event/abcd1234/comments'); - matcher('GET /very/deeply/nested/route/hello/there'); - matcher('GET /static/index.html'); -} -console.timeEnd('all together'); diff --git a/src/util/router/__bench__/routers.ts b/src/util/router/__bench__/routers.ts deleted file mode 100644 index 4f83ea319a..0000000000 --- a/src/util/router/__bench__/routers.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* tslint:disable no-console */ - -import {Router} from '..'; -import findMyWay from 'find-my-way'; -import {RouteDefinition} from './routes'; - -export type RouterDefinition = { - name: string; - register: (def: RouteDefinition, data: any) => void; - finalize?: () => void; - print?: () => void; - find: (method: string, path: string) => unknown; -}; - -export const routers: RouterDefinition[] = []; - -const theRouter = new Router(); -let matcher: any; -routers.push({ - name: 'json-joy router', - register: ([method, steps]: RouteDefinition, data: any) => { - const path = steps - .map((step) => { - if (typeof step === 'string') return step; - if (Array.isArray(step)) { - if (Array.isArray(step[0])) return `{${step[0][0]}::\n}`; - else return `{${step[0]}}`; - } - return ''; - }) - .join('/'); - theRouter.add(`${method} /${path}`, data); - }, - finalize: () => { - matcher = theRouter.compile(); - }, - print: () => { - console.log(theRouter + ''); - console.log(matcher + ''); - }, - find: (method: string, path: string) => { - return matcher(method + ' ' + path); - }, -}); - -const router = findMyWay(); -// router.prettyPrint(); -routers.push({ - name: 'find-my-way', - register: ([method, steps]: RouteDefinition, data: any) => { - const path = - '/' + - steps - .map((step) => { - if (typeof step === 'string') return step; - if (Array.isArray(step)) { - if (Array.isArray(step[0])) return `*`; - else return `:${step[0]}`; - } - return ''; - }) - .join('/'); - router.on(method, path, data); - }, - find: (method: string, path: string) => { - return router.find(method as any, path); - }, -}); diff --git a/src/util/router/__bench__/routes.ts b/src/util/router/__bench__/routes.ts deleted file mode 100644 index 792303ce70..0000000000 --- a/src/util/router/__bench__/routes.ts +++ /dev/null @@ -1,113 +0,0 @@ -export type Var = [string]; -export type Wildcard = [[string]]; -export type RouteDefinition = [method: 'GET' | 'POST' | 'DELETE' | 'PUT', path: (string | Var | Wildcard)[]]; - -export const definitions: RouteDefinition[] = [ - ['GET', ['ping']], - ['GET', ['pong']], - ['POST', ['ping']], - ['POST', ['echo']], - ['GET', ['info']], - ['GET', ['types']], - ['PUT', ['events']], - - ['GET', ['users']], - ['GET', ['users', ['id']]], - ['DELETE', ['users', ['id']]], - ['POST', ['users', ['id']]], - ['GET', ['users', ['id'], 'followers']], - ['GET', ['users', ['id'], 'followers', ['friend']]], - ['POST', ['users', ['id'], 'followers', ['friend']]], - ['DELETE', ['users', ['id'], 'followers', ['friend']]], - - ['GET', ['posts']], - ['GET', ['posts', 'search']], - ['POST', ['posts']], - ['GET', ['posts', ['post']]], - ['POST', ['posts', ['post']]], - ['DELETE', ['posts', ['post']]], - - ['GET', ['posts', ['post'], 'tags']], - ['GET', ['posts', ['post'], 'tags', 'top']], - ['GET', ['posts', ['post'], 'tags', ['tag']]], - ['DELETE', ['posts', ['post'], 'tags', ['tag']]], - ['POST', ['posts', ['post'], 'tags']], - - ['GET', ['api', 'collections']], - ['POST', ['api', 'collections']], - ['GET', ['api', 'collections', ['id']]], - ['PUT', ['api', 'collections', ['id']]], - ['POST', ['api', 'collections', ['id']]], - ['DELETE', ['api', 'collections', ['id']]], - - ['GET', ['api', 'collections', ['id'], 'documents']], - ['POST', ['api', 'collections', ['id'], 'documents']], - ['GET', ['api', 'collections', ['collection'], 'documents', ['document']]], - ['PUT', ['api', 'collections', ['collection'], 'documents', ['document']]], - ['POST', ['api', 'collections', ['collection'], 'documents', ['document']]], - ['DELETE', ['api', 'collections', ['collection'], 'documents', ['document']]], - ['GET', ['api', 'collections', ['collection'], 'documents', 'search']], - - ['GET', ['api', 'collections', ['collection'], 'documents', ['document'], 'revisions']], - ['GET', ['api', 'collections', ['collection'], 'documents', ['document'], 'revisions', 'find']], - ['POST', ['api', 'collections', ['collection'], 'documents', ['document'], 'revisions']], - ['GET', ['api', 'collections', ['collection'], 'documents', ['document'], 'revisions', ['revision']]], - ['DELETE', ['api', 'collections', ['collection'], 'documents', ['document'], 'revisions', ['revision']]], - - ['GET', ['files', ['bucket'], [['path']]]], - ['PUT', ['files', ['bucket'], [['path']]]], - ['GET', ['files', 'list', ['bucket'], [['path']]]], - ['GET', ['files', 'list', [['path']]]], - - ['GET', ['static', [['path']]]], -]; - -export const routes: [method: string, path: string][] = [ - ['GET', '/ping'], - ['GET', '/pong'], - ['POST', '/ping'], - ['POST', '/echo'], - ['GET', '/info'], - ['GET', '/types'], - ['PUT', '/events'], - ['GET', '/users'], - ['GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'], - ['GET', '/users/123'], - ['DELETE', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'], - ['POST', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'], - ['GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers'], - ['GET', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'], - ['POST', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'], - ['DELETE', '/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/followers/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'], - ['GET', '/posts'], - ['GET', '/posts/search'], - ['POST', '/posts'], - ['GET', '/posts/jhasdf982lsd'], - ['POST', '/posts/jhasdf982lsd'], - ['DELETE', '/posts/jhasdf982lsd'], - ['GET', '/posts/jhasdf982lsd/tags'], - ['GET', '/posts/jhasdf982lsd/tags/top'], - ['GET', '/posts/jhasdf982lsd/tags/123'], - ['DELETE', '/posts/jhasdf982lsd/tags/123'], - ['POST', '/posts/jhasdf982lsd/tags'], - ['GET', '/api/collections'], - ['POST', '/api/collections'], - ['GET', '/api/collections/123'], - ['PUT', '/api/collections/123'], - ['POST', '/api/collections/123'], - ['DELETE', '/api/collections/123'], - ['GET', '/api/collections/123/documents'], - ['POST', '/api/collections/123/documents'], - ['GET', '/api/collections/123/documents/456'], - ['PUT', '/api/collections/123/documents/456'], - ['POST', '/api/collections/123/documents/456'], - ['DELETE', '/api/collections/123/documents/456'], - ['GET', '/api/collections/123/documents/456/revisions'], - ['GET', '/api/collections/123/documents/456/revisions/find'], - ['POST', '/api/collections/123/documents/456/revisions'], - ['GET', '/api/collections/123/documents/456/revisions/789'], - ['DELETE', '/api/collections/123/documents/456/revisions/789'], - ['GET', '/files/user123-movies/2019/01/01/1.mp4'], - ['PUT', '/files/user123-movies/2019/01/01/1.mp4'], - ['GET', '/static/some/path/to/file.txt'], -]; diff --git a/src/util/router/__tests__/Destination.spec.ts b/src/util/router/__tests__/Destination.spec.ts deleted file mode 100644 index ed2abc8d30..0000000000 --- a/src/util/router/__tests__/Destination.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {Destination} from '../router'; - -test('can create a static route', () => { - const dest = Destination.from('GET /ping', 123); - expect(dest.routes).toHaveLength(1); - expect(dest.data).toBe(123); - expect(dest.routes[0].toText()).toBe('GET /ping'); -}); - -describe('.from()', () => { - const assertFrom = (def: string, expected: string = def) => { - const dest = Destination.from(def, null); - expect(dest.routes).toHaveLength(1); - expect(dest.routes[0].toText()).toBe(expected); - }; - - test('can create a static route', () => { - assertFrom('POST /ping'); - }); - - test('can use regex for or statement', () => { - assertFrom('{:(POST|GET)} /ping'); - }); - - test('can match any method', () => { - assertFrom('{method:.+: } /echo'); - }); - - test('can specify category variations', () => { - assertFrom( - 'GET /{:(collection|collections)}/{collectionId}/{:blocks?}/{blockId}', - 'GET /{:(collection|collections)}/{collectionId::/}/{:blocks?}/{blockId::/}', - ); - }); - - test('can parse until next slash', () => { - assertFrom('GET /posts/{postId}', 'GET /posts/{postId::/}'); - }); - - test('can parse until next dot', () => { - assertFrom('GET /files/{filename}.txt', 'GET /files/{filename::.}.txt'); - }); - - test('can parse until next dot and next slash', () => { - assertFrom('GET /files/{filename}.{extension}', 'GET /files/{filename::.}.{extension::/}'); - }); - - test('can wildcard the remainder of the path', () => { - assertFrom('GET /static/{path:.+}'); - }); - - test('when parsing one step, matches until next slash', () => { - assertFrom('GET /step/{path::}', 'GET /step/{path::/}'); - }); - - test('when parsing one step, matches until next slash', () => { - assertFrom('GET /step/{path::}', 'GET /step/{path::/}'); - }); - - test('can make last parameter optional', () => { - assertFrom('GET /users{user:(/[^/]+)?}'); - }); -}); diff --git a/src/util/router/__tests__/Router.spec.ts b/src/util/router/__tests__/Router.spec.ts deleted file mode 100644 index 04b2117dab..0000000000 --- a/src/util/router/__tests__/Router.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {Router} from '../router'; - -describe('can route and extract params', () => { - const router = new Router(); - router.add(['GET /ping', 'GET /pong'], 'PING'); - router.add('GET /echo', 'ECHO'); - router.add('GET /users/{user}', 'GET_USER'); - router.add('GET /users/{user}/preview', 'USER_PREVIEW'); - router.add('GET /users/{user}/avatar/{size}.{extension}', 'USER_AVATAR_IMG_EXT'); - router.add('GET /users/{user}/avatar/{size}.png', 'USER_AVATAR_IMG'); - router.add('GET /users/{user}/avatar/{size}', 'USER_AVATAR_SIZE'); - router.add('GET /users/{user}/avatar', 'USER_AVATAR'); - router.add('POST /users/{user}/edit', 'USER_EDIT'); - router.add('POST /users/{user}/create', 'USER_CREATE'); - router.add('POST /users', 'ALL_USERS'); - router.add('POST /coordinates/{lat}-{lng}', 'COORDINATES'); - router.add('GET /๐Ÿ˜€/plus/{emoji}', 'EMOJI'); - router.add('GET /static/{path::\n}', 'STATIC'); - router.add('GET /{category}/{id}', 'DYNAMIC_CATEGORY'); - router.add('{:(POST|PUT)} /rpc/{method}', 'RPC'); - router.add('{:(POST|PUT)} /rpc2/{method}', 'RPC2'); - router.add('{httpMethod:[^ ]+} /json-rpc/{rpcMethod}', 'JSON-RPC'); - router.add('{httpMethod:*: } /json-rpc-2/{rpcMethod}', 'JSON-RPC-2'); - router.add('GET /collections/{collection}/{:blocks?}', 'COLLECTION_BLOCKS'); - router.add('GET /a/b/collections{:(/pretty)?}', 'COLLECTIONS_MAYBE_PRETTY'); - const matcher = router.compile(); - // console.log(router + ''); - // console.log(matcher + ''); - - const assertMatch = (route: string, data: unknown, params: unknown[]) => { - const match = matcher(route); - expect(typeof match).toBe('object'); - expect(match!.data).toEqual(data); - expect(match!.params || []).toEqual(params); - }; - - test('can match constant multi route destination', () => { - assertMatch('GET /ping', 'PING', []); - assertMatch('GET /pong', 'PING', []); - }); - - test('can match constant single route destination', () => { - assertMatch('GET /echo', 'ECHO', []); - }); - - test('can match a simple one parameter step route', () => { - assertMatch('GET /users/1', 'GET_USER', ['1']); - }); - - test('can match a simple single in-the-middle parameter route', () => { - assertMatch('GET /users/123/preview', 'USER_PREVIEW', ['123']); - }); - - test('can match a simple single in-the-middle parameter route - 2', () => { - assertMatch('GET /users/123/avatar', 'USER_AVATAR', ['123']); - }); - - test('can match a two parameter route', () => { - assertMatch('GET /users/123/avatar/64', 'USER_AVATAR_SIZE', ['123', '64']); - assertMatch('GET /users/xyz/avatar/square', 'USER_AVATAR_SIZE', ['xyz', 'square']); - }); - - test('can match a two parameter route with static file extension', () => { - assertMatch('GET /users/123/avatar/64x64.png', 'USER_AVATAR_IMG', ['123', '64x64']); - }); - - test('can match a multi-parameter route with file extension as parameter', () => { - assertMatch('GET /users/123/avatar/64x64.jpeg', 'USER_AVATAR_IMG_EXT', ['123', '64x64', 'jpeg']); - }); - - test('can match a POST route with a parameter', () => { - assertMatch('POST /users/123/edit', 'USER_EDIT', ['123']); - }); - - test('can match a POST route with a parameter - 2', () => { - assertMatch('POST /users/123/create', 'USER_CREATE', ['123']); - }); - - test('can match a static POST route', () => { - assertMatch('POST /users', 'ALL_USERS', []); - }); - - test('can match a coordinates pattern', () => { - assertMatch('POST /coordinates/0.832823-1.233483943', 'COORDINATES', ['0.832823', '1.233483943']); - }); - - test('can match first route step as dynamic category', () => { - assertMatch('GET /dogs/1234', 'DYNAMIC_CATEGORY', ['dogs', '1234']); - }); - - test('can match regex "or" pattern', () => { - assertMatch('POST /rpc/method1', 'RPC', ['method1']); - }); - - test('can match regex "or" pattern - 2', () => { - assertMatch('POST /rpc2/method2', 'RPC2', ['method2']); - }); - - test('can match HTTP method name and RPC method name', () => { - assertMatch('POST /json-rpc/hello-world', 'JSON-RPC', ['POST', 'hello-world']); - }); - - test('can match HTTP method name and RPC method name using "util" step', () => { - assertMatch('PUT /json-rpc-2/product.service.methodExecute', 'JSON-RPC-2', [ - 'PUT', - 'product.service.methodExecute', - ]); - }); - - test('can match when regex is last step', () => { - assertMatch('GET /collections/1234/block', 'COLLECTION_BLOCKS', ['1234']); - assertMatch('GET /collections/1234/blocks', 'COLLECTION_BLOCKS', ['1234']); - expect(() => assertMatch('GET /collections/1234/blockz', 'COLLECTION_BLOCKS', ['1234'])).toThrow(); - }); - - test('last step is optional', () => { - assertMatch('GET /a/b/collections/pretty', 'COLLECTIONS_MAYBE_PRETTY', []); - assertMatch('GET /a/b/collections', 'COLLECTIONS_MAYBE_PRETTY', []); - }); - - test('can match till end of line wildcard', () => { - assertMatch('GET /static/a', 'STATIC', ['a']); - assertMatch('GET /static/dir1/dir2/file.ext', 'STATIC', ['dir1/dir2/file.ext']); - }); - - test('should return "undefined" on non-existing route', () => { - const match = matcher('GET /this/route/does/not/exist'); - expect(match).toBe(undefined); - }); - - test('can match a route with emojis', () => { - assertMatch('GET /๐Ÿ˜€/plus/โœ…', 'EMOJI', ['โœ…']); - }); -}); - -describe('Options', () => { - test('can specify custom "defaultUntil" character', () => { - const router1 = new Router(); - router1.add('GET |users|{user}', 'USER'); - const matcher1 = router1.compile(); - const res1 = matcher1('GET |users|123|avatar'); - expect(res1).not.toBe(undefined); - const router2 = new Router({defaultUntil: '|'}); - router2.add('GET |users|{user}', 'USER'); - const matcher2 = router2.compile(); - const res2 = matcher2('GET |users|123|avatar'); - expect(res2).toBe(undefined); - }); -}); diff --git a/src/util/router/__tests__/benchmark-routes.spec.ts b/src/util/router/__tests__/benchmark-routes.spec.ts deleted file mode 100644 index 973a2c414d..0000000000 --- a/src/util/router/__tests__/benchmark-routes.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {definitions, type RouteDefinition} from '../__bench__/routes'; -import {Router} from '../router'; - -const createRouter = (definitions: RouteDefinition[]) => { - const router = new Router(); - for (const [method, steps] of definitions) { - const path = steps - .map((step) => { - if (typeof step === 'string') return step; - if (Array.isArray(step)) { - if (Array.isArray(step[0])) return `{${step[0][0]}::\n}`; - else return `{${step[0]}}`; - } - return ''; - }) - .join('/'); - router.add(`${method} /${path}`, `${method} /${path}`); - } - // console.log(router + '') - return router; -}; - -test('can execute a PUT /files route', () => { - const router = createRouter(definitions); - const matcher = router.compile(); - expect(matcher('PUT /files/bucket/my-file.exe')!.params).toEqual(['bucket', 'my-file.exe']); -}); diff --git a/src/util/router/__tests__/radix-tree.spec.ts b/src/util/router/__tests__/radix-tree.spec.ts deleted file mode 100644 index 8d46bb65e1..0000000000 --- a/src/util/router/__tests__/radix-tree.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {Router} from '../router'; - -describe('can route and extract params', () => { - const router = new Router(); - router.add('GET /ping', 'PING'); - router.add('GET /posts/{post}', 'GET_POST'); - router.add('GET /users/{user}', 'GET_USER'); - router.add('GET /users/{user}/preview', 'USER_PREVIEW'); - router.add('GET /users/all/{ids}', 'USERS_ALL'); - // console.log(router + ''); - const matcher = router.compile(); - // console.log(matcher + ''); - - const assertMatch = (route: string, data: unknown, params: unknown[]) => { - const match = matcher(route); - expect(typeof match).toBe('object'); - expect(match!.data).toEqual(data); - expect(match!.params).toEqual(params); - }; - - test('can match routes', () => { - assertMatch('GET /users/123', 'GET_USER', ['123']); - assertMatch('GET /users/456/preview', 'USER_PREVIEW', ['456']); - assertMatch('GET /posts/gg', 'GET_POST', ['gg']); - assertMatch('GET /users/all/1,2,3', 'USERS_ALL', ['1,2,3']); - }); -}); diff --git a/src/util/router/__tests__/trie-tree.spec.ts b/src/util/router/__tests__/trie-tree.spec.ts deleted file mode 100644 index 821b0d776f..0000000000 --- a/src/util/router/__tests__/trie-tree.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Router} from '../router'; - -describe('can route and extract params', () => { - const router = new Router(); - router.add('GET /events/{lat}-{lng}', 'EVENT_COORDS'); - router.add('GET /events/{id}', 'EVENT'); - router.add('GET /events/{id}/comments', 'EVENT_COMMENTS'); - // console.log(router + ''); - const matcher = router.compile(); - // console.log(matcher + ''); - - const assertMatch = (route: string, data: unknown, params: unknown[]) => { - const match = matcher(route); - expect(typeof match).toBe('object'); - expect(match!.data).toEqual(data); - expect(match!.params).toEqual(params); - }; - - test('can match routes which share parameter node', () => { - assertMatch('GET /events/123', 'EVENT', ['123']); - assertMatch('GET /events/123/comments', 'EVENT_COMMENTS', ['123']); - assertMatch('GET /events/123-456', 'EVENT_COORDS', ['123', '456']); - }); -}); diff --git a/src/util/router/__tests__/wildcard.spec.ts b/src/util/router/__tests__/wildcard.spec.ts deleted file mode 100644 index 66f420f086..0000000000 --- a/src/util/router/__tests__/wildcard.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {Router} from '../router'; - -describe('can route and extract params', () => { - const router = new Router(); - router.add('GET /files/{path::\n}', 'FILE'); - // console.log(router + ''); - const matcher = router.compile(); - // console.log(matcher + ''); - - const assertMatch = (route: string, data: unknown, params: unknown[]) => { - const match = matcher(route); - expect(typeof match).toBe('object'); - expect(match!.data).toEqual(data); - expect(match!.params).toEqual(params); - }; - - test('can match routes', () => { - assertMatch('GET /files/blob.bin', 'FILE', ['blob.bin']); - assertMatch('GET /files/folder/pics/photo.jpeg', 'FILE', ['folder/pics/photo.jpeg']); - }); -}); diff --git a/src/util/router/codegen.ts b/src/util/router/codegen.ts deleted file mode 100644 index da38825e7e..0000000000 --- a/src/util/router/codegen.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {Codegen} from '@jsonjoy.com/util/lib/codegen'; -import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; -import type {Match} from './router'; - -export type RouteMatcher = (route: string) => undefined | Match; - -export class RouterCodegenCtx { - public readonly codegen: Codegen; - - constructor() { - this.codegen = new Codegen({ - args: ['str'], - prologue: 'str = "" + str; var len = str.length|0;', - epilogue: 'return undefined;', - }); - } -} - -export class RouterCodegenOpts { - constructor( - public readonly slice: JsExpression, - public readonly offset: string, - public readonly depth: number = 0, - ) {} - - public create(offset: string): RouterCodegenOpts { - const slice = new JsExpression(() => `str.slice(${offset})`); - return new RouterCodegenOpts(slice, offset, this.depth + 1); - } -} diff --git a/src/util/router/index.ts b/src/util/router/index.ts deleted file mode 100644 index 3f02152171..0000000000 --- a/src/util/router/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './router'; diff --git a/src/util/router/router.ts b/src/util/router/router.ts deleted file mode 100644 index 143104fe3d..0000000000 --- a/src/util/router/router.ts +++ /dev/null @@ -1,141 +0,0 @@ -import {JsExpression} from '@jsonjoy.com/util/lib/codegen/util/JsExpression'; -import {printTree} from '../print/printTree'; -import {Printable} from '../print/types'; -import {RouteMatcher, RouterCodegenCtx, RouterCodegenOpts} from './codegen'; -import {ExactStep, RegexStep, UntilStep} from './steps'; -import {RoutingTreeNode} from './tree'; -import {Step} from './types'; - -export interface RouterOptions { - defaultUntil?: string; -} - -export class Router implements Printable { - public readonly destinations: Destination[] = []; - - constructor(public readonly options: RouterOptions = {}) {} - - public add(route: string | string[], data: Data) { - const destination = Destination.from(route, data, this.options.defaultUntil); - this.destinations.push(destination); - } - - public addDestination(destination: Destination) { - this.destinations.push(destination); - } - - public tree(): RoutingTreeNode { - const tree = new RoutingTreeNode(); - for (const destination of this.destinations) { - for (const route of destination.routes) { - tree.add(route, 0, destination); - } - } - return tree; - } - - public compile(): RouteMatcher { - const ctx = new RouterCodegenCtx(); - const node = new RouterCodegenOpts(new JsExpression(() => 'str'), '0'); - const tree = this.tree(); - tree.codegen(ctx, node); - return ctx.codegen.compile() as RouteMatcher; - } - - public toString(tab: string = '') { - return ( - `${this.constructor.name}` + - printTree(tab, [ - (tab) => - 'Destinations' + - printTree( - tab, - this.destinations.map((d, i) => (tab) => `[${i}]: ` + d.toString(tab + ' ')), - ), - () => '', - (tab) => 'RoutingTree' + printTree(tab, [(tab) => this.tree().toString(tab)]), - ]) - ); - } -} - -export class Destination implements Printable { - public static from(def: string | string[], data: unknown, defaultUntil?: string): Destination { - const routes = typeof def === 'string' ? [Route.from(def, defaultUntil)] : def.map((r) => Route.from(r)); - return new Destination(routes, data); - } - - public readonly match: Match; - - constructor( - public readonly routes: Route[], - public readonly data: unknown, - ) { - this.match = new Match(data, []); - } - - public toString(tab: string = '') { - return ( - `${this.constructor.name} ` + - (this.routes.length === 1 - ? this.routes[0].toString(tab) - : printTree( - tab, - this.routes.map((r) => (tab) => r.toString(tab)), - )) - ); - } -} - -export class Route implements Printable { - public static from(str: string, defaultUntil = '/'): Route { - const tokens: string[] = []; - const matches = str.match(/\{[^\}]*\}/g); - let i = 0; - for (const match of matches ?? []) { - const index = str.indexOf(match); - if (index > i) tokens.push(str.substring(i, index)); - tokens.push(match); - i = index + match.length; - } - if (i < str.length) tokens.push(str.substring(i)); - const steps: Step[] = []; - const length = tokens.length; - for (let i = 0; i < length; i++) { - const token = tokens[i]; - const isParameter = token.startsWith('{') && token.endsWith('}'); - if (isParameter) { - const content = token.substring(1, token.length - 1); - const [name = '', regex = '', until = ''] = content.split(':'); - if (!regex || regex === '*') { - const next = tokens[i + 1]; - steps.push(new UntilStep(name, until || (next ? next[0] : defaultUntil))); - } else { - steps.push(new RegexStep(name, regex, until)); - } - } else { - steps.push(new ExactStep(token)); - } - } - return new Route(steps); - } - - constructor(public readonly steps: Step[]) {} - - public toText() { - let str = ''; - for (const step in this.steps) str += this.steps[step].toText(); - return str; - } - - public toString(tab: string = '') { - return this.toText(); - } -} - -export class Match { - constructor( - public readonly data: Data, - public params: string[] | null, - ) {} -} diff --git a/src/util/router/steps.ts b/src/util/router/steps.ts deleted file mode 100644 index 500d9a2a96..0000000000 --- a/src/util/router/steps.ts +++ /dev/null @@ -1,33 +0,0 @@ -export class ExactStep { - constructor(public readonly text: string) {} - - public toText() { - return this.text; - } -} - -export class UntilStep { - constructor( - public readonly name: string, - public readonly until: string, - ) {} - - public toText() { - const until = this.until === '\n' ? '\\n' : this.until; - return `{${this.name}::${until}}`; - } -} - -export class RegexStep { - constructor( - public readonly name: string, - public readonly regex: string, - public readonly until: string, - ) {} - - public toText() { - const regex = this.regex || this.until ? ':' + this.regex : ''; - const until = this.until ? ':' + this.until : ''; - return `{${this.name}${regex}${until}}`; - } -} diff --git a/src/util/router/tree.ts b/src/util/router/tree.ts deleted file mode 100644 index 9e209cde0c..0000000000 --- a/src/util/router/tree.ts +++ /dev/null @@ -1,240 +0,0 @@ -import {emitStringMatch} from '@jsonjoy.com/util/lib/codegen/util/helpers'; -import {printTree} from '../print/printTree'; -import {Printable} from '../print/types'; -import {RadixTree} from '../trees/radix/RadixTree'; -import {TrieNode} from '../trees/trie/TrieNode'; -import {RouterCodegenCtx, RouterCodegenOpts} from './codegen'; -import {Destination, Route} from './router'; -import {ExactStep, RegexStep, UntilStep} from './steps'; - -const genExactMatchCondition = (text: string, opts: RouterCodegenOpts) => { - return emitStringMatch('str', opts.offset, text); -}; - -export class RoutingTreeNode implements Printable { - public readonly exact: Map = new Map(); - public readonly start: RadixTree = new RadixTree(); - public readonly until: [step: UntilStep, destination: RoutingTreeNode][] = []; - public readonly regex: [step: RegexStep, destination: RoutingTreeNode | Destination][] = []; - public end?: Destination; - - public add(route: Route, step: number, destination: Destination): void { - const isLast = step === route.steps.length - 1; - const match = route.steps[step]; - if (match instanceof ExactStep) { - if (isLast) { - const exact = this.exact.get(match.text.length) ?? []; - exact.push([match, destination]); - this.exact.set(match.text.length, exact); - } else { - const child = this.start.get(match.text); - if (child) child.add(route, step + 1, destination); - else { - const node = new RoutingTreeNode(); - this.start.set(match.text, node); - node.add(route, step + 1, destination); - } - } - } else if (match instanceof UntilStep) { - let until: [step: UntilStep, destination: RoutingTreeNode] | undefined = this.until.find( - ([step, dest]) => step.until === match.until && step.name === match.name && dest instanceof RoutingTreeNode, - ); - if (!until || !(until[1] instanceof RoutingTreeNode)) { - until = [match, new RoutingTreeNode()]; - this.until.push(until); - } - if (isLast) { - until[1].end = destination; - } else { - until[1].add(route, step + 1, destination); - } - } else if (match instanceof RegexStep) { - if (isLast) { - this.regex.push([match, destination]); - } else { - const regex = this.regex.find( - ([step, dest]) => - step.regex === match.regex && - step.until === match.until && - step.name === match.name && - dest instanceof RoutingTreeNode, - ); - if (regex && regex[1] instanceof RoutingTreeNode) { - regex[1].add(route, step + 1, destination); - } else { - const node = new RoutingTreeNode(); - this.regex.push([match, node]); - node.add(route, step + 1, destination); - } - } - } - } - - public codegen(ctx: RouterCodegenCtx, opts: RouterCodegenOpts): void { - const code = ctx.codegen; - if (this.exact.size) { - if (!opts.depth) { - code.switch( - 'len', - [...this.exact].map(([length, destinations]) => [ - length, - () => { - code.switch( - 'str', - destinations.map(([step, destination]) => { - const m = code.linkDependency(destination.match); - return [JSON.stringify(step.text), () => code.return(`${m}.params = null, ${m}`), true]; - }), - ); - }, - ]), - ); - } else { - for (const destinations of this.exact.values()) { - for (const [step, destination] of destinations) { - const m = code.linkDependency(destination.match); - code.if( - `(${step.text.length} + ${opts.offset} === len) && ${genExactMatchCondition(step.text, opts)}`, - () => code.return(`${m}.params = params, ${m}`), - ); - } - } - } - } - if (!opts.depth) { - code.js('var params = [];'); - } - const emitRadixTreeNode = (node: TrieNode, opts: RouterCodegenOpts) => { - const text = node.k; - const length = text.length; - const block = () => { - const offset = length ? code.var(`${opts.offset} + ${length}`) : opts.offset; - node.forChildren((child) => { - emitRadixTreeNode(child, opts.create(offset)); - }); - const routingNode = node.v as RoutingTreeNode | undefined; - if (routingNode) { - routingNode.codegen(ctx, opts.create(offset)); - } - }; - if (text) { - code.if(genExactMatchCondition(text, opts), block); - } else block(); - }; - emitRadixTreeNode(this.start, opts); - if (this.until.length) { - for (const [step, destination] of this.until) { - if (destination.end && step.until === '\n') { - const m = code.linkDependency(destination.end.match); - if (step.name) code.js(`params.push(str.slice(${opts.offset}, len));`); - code.return(`${m}.params = params, ${m}`); - } else { - // const ri = code.var(opts.offset); - // code.js(`for(; ${ri} < len && str[${ri}] !== ${JSON.stringify(step.until)}; ${ri}++);`); - // code.js(`for(; ${ri} < len && str.charCodeAt(${ri}) !== ${step.until.charCodeAt(0)}; ${ri}++);`); - const ri = code.var(`str.indexOf(${JSON.stringify(step.until)}, ${opts.offset})`); - if (destination.end) { - const m = code.linkDependency(destination.end.match); - code.if(`${ri} === -1`, () => { - if (step.name) code.js(`params.push(str.slice(${opts.offset}, len));`); - code.return(`${m}.params = params, ${m}`); - }); - } - if ( - destination.exact.size || - destination.start.size || - destination.until.length || - destination.regex.length - ) { - code.if(`${ri} > ${opts.offset}`, () => { - if (step.name) code.js(`params.push(str.slice(${opts.offset}, ${ri}));`); - destination.codegen(ctx, opts.create(ri)); - if (step.name) code.js(`params.pop();`); - }); - } - } - } - } - if (this.regex.length) { - for (const [step, destination] of this.regex) { - const isDestination = destination instanceof Destination; - const r = code.var(`str.slice(${opts.offset})`); - const regex = new RegExp('^' + step.regex + step.until + (isDestination ? '$' : '')); - const reg = code.linkDependency(regex); - const match = code.var(`${r}.match(${reg})`); - if (isDestination) { - code.if(match, () => { - const val = code.var(`${match}[1] || ${match}[0]`); - const m = code.linkDependency(destination.match); - if (step.name) code.js(`params.push(${val});`); - code.return(`${m}.params = params, ${m}`); - }); - } else { - code.if(match, () => { - const val = code.var(`${match}[1] || ${match}[0]`); - const offset = code.var(`${opts.offset} + ${val}.length`); - if (step.name) code.js(`params.push(${val});`); - destination.codegen(ctx, opts.create(offset)); - if (step.name) code.js(`params.pop();`); - }); - } - } - } - } - - public toString(tab: string): string { - return ( - `${this.constructor.name} ` + - printTree(tab, [ - !this.exact.size - ? null - : (tab) => - 'exact ' + - printTree( - tab, - [...this.exact].map( - ([length, destinations]) => - (tab) => - `${length} ` + - printTree( - tab, - destinations.map( - ([step, destination]) => - (tab) => - `${JSON.stringify(step.toText())}${ - destination instanceof Destination ? ' โ†’' : '' - } ${destination.toString(tab + ' ')}`, - ), - ), - ), - ), - !this.end ? null : (tab) => 'end โ†’ ' + this.end!.toString(tab), - !this.start.size ? null : (tab) => 'start ' + this.start.toString(tab), - !this.until.length - ? null - : (tab) => - 'until ' + - printTree( - tab, - this.until.map( - ([step, destination]) => - (tab) => - `${step.toText()}${destination instanceof Destination ? ' โ†’' : ''} ${destination.toString(tab)}`, - ), - ), - !this.regex.length - ? null - : (tab) => - 'regex ' + - printTree( - tab, - this.regex.map( - ([step, destination]) => - (tab) => - `${step.toText()}${destination instanceof Destination ? ' โ†’' : ''} ${destination.toString(tab)}`, - ), - ), - ]) - ); - } -} diff --git a/src/util/router/types.ts b/src/util/router/types.ts deleted file mode 100644 index 19ac6fcf75..0000000000 --- a/src/util/router/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {ExactStep, RegexStep, UntilStep} from './steps'; - -export type Step = ExactStep | UntilStep | RegexStep; diff --git a/src/util/rx/BufferSubject.ts b/src/util/rx/BufferSubject.ts deleted file mode 100644 index fe48dbbcda..0000000000 --- a/src/util/rx/BufferSubject.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Subject, Subscription, Subscriber} from 'rxjs'; -import {RpcError, RpcErrorCodes} from '../../reactive-rpc/common/rpc/caller'; - -export class BufferSubject extends Subject { - private buffer: T[] = []; - private isBuffering = true; - - constructor(public readonly bufferSize: number) { - super(); - } - - protected _subscribe(subscriber: Subscriber): Subscription { - // @ts-ignore - this._throwIfClosed(); - // @ts-ignore - const subscription = this._innerSubscribe(subscriber); - const {buffer} = this; - for (let i = 0; i < buffer.length && !subscriber.closed; i += 1) { - subscriber.next(buffer[i] as T); - } - // @ts-ignore - this._checkFinalizedStatuses(subscriber); - return subscription; - } - - public next(value: T): void { - if (this.isBuffering) { - if (this.buffer.length >= this.bufferSize) { - this.error(RpcError.fromCode(RpcErrorCodes.BUFFER_OVERFLOW)); - return; - } - this.buffer.push(value); - } - super.next(value); - } - - public flush(): void { - this.isBuffering = false; - this.buffer = []; - } -} diff --git a/src/util/rx/__tests__/BufferSubject.spec.ts b/src/util/rx/__tests__/BufferSubject.spec.ts deleted file mode 100644 index 9bc39467b1..0000000000 --- a/src/util/rx/__tests__/BufferSubject.spec.ts +++ /dev/null @@ -1,846 +0,0 @@ -import {BufferSubject} from '../BufferSubject'; - -test('can create subject', () => { - const subject = new BufferSubject(10); -}); - -test('can subscribe and unsubscribe', () => { - const subject = new BufferSubject(10); - const subscription = subject.subscribe(); - subscription.unsubscribe(); -}); - -test('error when buffer overflows', () => { - const subject = new BufferSubject(2); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBeInstanceOf(Error); - expect(error.mock.calls[0][0].message).toMatchInlineSnapshot(`"BUFFER_OVERFLOW"`); -}); - -test('does not error when buffer was flushed just before overflow', () => { - const subject = new BufferSubject(2); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - subject.next(1); // next - subject.next(2); // next - subject.flush(); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); -}); - -test('subscribe, next', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); -}); - -test('next, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); -}); - -test('subscribe, complete', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('complete, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('subscribe, error', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // error - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('error, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // error - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('subscribe, next, next, next', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); -}); - -test('next, subscribe, next, next', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); -}); - -test('next, next, subscribe, next', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); -}); - -test('next, next, next, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(3); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(next.mock.calls[2][0]).toBe(3); -}); - -test('subscribe, next, next, complete', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('next, subscribe, next, complete', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('next, next, subscribe, complete', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('next, next, complete, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.complete(); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(1); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(complete.mock.calls[0][0]).toBe(undefined); -}); - -test('subscribe, next, next, error', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, subscribe, next, error', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, next, subscribe, error', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, next, error, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, next, error, subscribe, flush', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - subject.flush(); // flush - expect(next).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(next.mock.calls[0][0]).toBe(1); - expect(next.mock.calls[1][0]).toBe(2); - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, next, error, flush, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.flush(); // flush - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, next, flush, error, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.flush(); // flush - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, flush, next, error, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.flush(); // flush - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('next, flush, next, error, subscribe', () => { - const subject = new BufferSubject(10); - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.flush(); // flush - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.error('ERROR'); // complete - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(0); - expect(complete).toHaveBeenCalledTimes(0); - subject.subscribe({next, error, complete}); // subscribe - expect(next).toHaveBeenCalledTimes(0); - expect(error).toHaveBeenCalledTimes(1); - expect(complete).toHaveBeenCalledTimes(0); - - expect(error.mock.calls[0][0]).toBe('ERROR'); -}); - -test('subscribe 1, next, next, subscribe 2, flush, next, unsubscribe 2, subscribe 3, next, error, subscribe 4', () => { - const subject = new BufferSubject(10); - const next1 = jest.fn(); - const error1 = jest.fn(); - const complete1 = jest.fn(); - const next2 = jest.fn(); - const error2 = jest.fn(); - const complete2 = jest.fn(); - const next3 = jest.fn(); - const error3 = jest.fn(); - const complete3 = jest.fn(); - const next4 = jest.fn(); - const error4 = jest.fn(); - const complete4 = jest.fn(); - expect(next1).toHaveBeenCalledTimes(0); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(0); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.subscribe({next: next1, error: error1, complete: complete1}); // subscribe 1 - expect(next1).toHaveBeenCalledTimes(0); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(0); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.next(1); // next - expect(next1).toHaveBeenCalledTimes(1); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(0); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.next(2); // next - expect(next1).toHaveBeenCalledTimes(2); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(0); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - const subscription2 = subject.subscribe({next: next2, error: error2, complete: complete2}); // subscribe 2 - expect(next1).toHaveBeenCalledTimes(2); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(2); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.flush(); - expect(next1).toHaveBeenCalledTimes(2); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(2); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.next(3); // next - expect(next1).toHaveBeenCalledTimes(3); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subscription2.unsubscribe(); // unsubscribe 2 - expect(next1).toHaveBeenCalledTimes(3); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.subscribe({next: next3, error: error3, complete: complete3}); // subscribe 3 - expect(next1).toHaveBeenCalledTimes(3); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(0); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.next(4); // next - expect(next1).toHaveBeenCalledTimes(4); - expect(error1).toHaveBeenCalledTimes(0); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(1); - expect(error3).toHaveBeenCalledTimes(0); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.error('ERR'); // error - expect(next1).toHaveBeenCalledTimes(4); - expect(error1).toHaveBeenCalledTimes(1); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(1); - expect(error3).toHaveBeenCalledTimes(1); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(0); - expect(complete4).toHaveBeenCalledTimes(0); - subject.subscribe({next: next4, error: error4, complete: complete4}); // subscribe 4 - expect(next1).toHaveBeenCalledTimes(4); - expect(error1).toHaveBeenCalledTimes(1); - expect(complete1).toHaveBeenCalledTimes(0); - expect(next2).toHaveBeenCalledTimes(3); - expect(error2).toHaveBeenCalledTimes(0); - expect(complete2).toHaveBeenCalledTimes(0); - expect(next3).toHaveBeenCalledTimes(1); - expect(error3).toHaveBeenCalledTimes(1); - expect(complete3).toHaveBeenCalledTimes(0); - expect(next4).toHaveBeenCalledTimes(0); - expect(error4).toHaveBeenCalledTimes(1); - expect(complete4).toHaveBeenCalledTimes(0); -});