patchpack
TypeScript icon, indicating that this package has built-in type declarations

0.4.2 • Public • Published

PatchPack

npm npm CircleCI npm type definitions GitHub

A binary JsonPatch serializer based on schema. Efficiently encode state object and JsonPatch in to compact byte buffers and then decode them back in to objects on the receiver. Integrates very well with observable state and WebSockets.

Originally it was part of mosx framework, but then it moved to separate package.

Motivation

I was working on an magx game server framework that used WebSockets to syncronize state between server and clients. Syncronization principle is simple: first server sends full state to clients then on every change sends patches in JsonPatch format. I have found the problem that sending a lot of patches without serialization is a waste of bandwidth.

As state's schema is known on server side it can be sent to the clients, then state and each patch can be encoded based on that schema on server side and decoded back on client side. State schema is not static that means it must be also syncronized with clients. This sophisticated approach can significantly reduce patch size and bandwidth usage.

Concept

Installation

npm install --save patchpack

Browser

A browser version of patchpack is also available:

<script src="https://cdn.jsdelivr.net/npm/patchpack@0.4.2/browser/patchpack.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/patchpack@0.4.2/browser/patchpack.js"></script>

Example

/** Server side */
 
import { PatchPack } from "patchpack"
 
class Client {
  constructor(public name: string, public info = "") {}
}
 
// initial state
const state: any = {
  clients: {
    "1": new Client("Foo"),
    "2": new Client("Baz", "FooBaz"),
  },
  objects: [
    { id: 1, name: "Foo" },
    { id: 2, name: "Foo", foo: "Baz" },
  ],
  foo: {
    baz: false
  }
}
 
// create patchpack instance and define schema types
const ppServer = new PatchPack({
  State: ["clients", "objects", "foo"],
  Client, // it is recommended to use class in schema
  Object: ["id", "name", "foo"],
  Foo: ["baz"]
})
 
const encodedStateWithTypes = ppServer.encodeState(state)
console.log(encodedStateWithTypes.length)
// 135
 
const encodedState = ppServer.encodeState(state, false)
console.log(encodedState.length)
// 60
 
console.log(JSON.stringify(state).length)
// 165
 
const client = new Client("FooBaz", "test" )
state.clients["3"] = client
 
const patch1 = { op: "add", path: "/clients/3", value: client }
const encodedPatch1 = ppServer.encodePatch(patch1)
console.log(encodedPatch1.length)
// 22
 
console.log(JSON.stringify(patch1).length)
// 72
 
// generate patch
const patch2 = { op: "replace", path: "/foo/baz", value: true }
const encodedPatch2 = ppServer.encodePatch(patch2)
 
console.log(encodedPatch2.length)
// 5
 
console.log(JSON.stringify(patch2).length)
// 47

Send encodedStateWithTypes, encodedPatch1 and encodedPatch2 to Client and decode them:

/** Client side */
const ppClient = new PatchPack()
 
const decodedState = ppClient.decodeState(encodedStateWithTypes)
console.log(decodedState)
 
// {
//   clients: {
//     '1': { name: 'Foo', info: '' },
//     '2': { name: 'Baz', info: 'FooBaz' }
//   },
//   objects: [
//      { id: 1, name: 'Foo' },
//      { id: 2, name: 'Foo', foo: 'Baz' } ],
//   foo: { baz: false }
// }
 
const decodedPatch1 = ppClient.decodePatch(encodedPatch1)
console.log(decodedPatch1)
 
// {
//   op: 'add',
//   path: '/clients/3',
//   value: { name: 'FooBaz', info: 'test' }
// }
 
const decodedPatch2 = ppClient.decodePatch(encodedPatch2)
console.log(decodedPatch2)
 
// { op: 'replace', path: '/foo/baz', value: true }

License

MIT

Package Sidebar

Install

npm i patchpack

Weekly Downloads

1

Version

0.4.2

License

MIT

Unpacked Size

250 kB

Total Files

21

Last publish

Collaborators

  • udamir