Skip to content

Commit

Permalink
Re-organize docs
Browse files Browse the repository at this point in the history
  • Loading branch information
lxsmnsyc committed Sep 8, 2023
1 parent d8af80f commit e67edad
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 274 deletions.
138 changes: 1 addition & 137 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,146 +80,10 @@ Output (as a string):
}, h[3] = h, j.set("self", j), k.add(k).add(h), m.self = m, m))()
```

### Mutual cyclic example

```js
import { serialize } from 'seroval';

const a = new Map([['name', 'a']]);
const b = new Map([['name', 'b']]);
const c = new Map([['name', 'c']]);
const d = new Map([['name', 'd']]);

c.set('left', a);
d.set('left', a);
c.set('right', b);
d.set('right', b);
a.set('children', [c, d]);
b.set('children', [c, d]);

const result = serialize({ a, b, c, d });
console.log(result);
```

Output (as a string):

```js
((h,j,k,m,o,q)=>(q={a:h=new Map([["name","a"],["children",[j=new Map([["name","c"],["right",o=new Map([["name","b"],["children",k=[,m=new Map([["name","d"]])]]])]]),m]]]),b:o,c:j,d:m},j.set("left",h),k[0]=j,m.set("left",h).set("right",o),q))()

// Formatted
((h, j, k, m, o, q) => (q = {
a: h = new Map([
["name", "a"],
["children", [j = new Map([
["name", "c"],
["right", o = new Map([
["name", "b"],
["children", k = [, m = new Map([
["name", "d"]
])]]
])]
]), m]]
]),
b: o,
c: j,
d: m
}, j.set("left", h), k[0] = j, m.set("left", h).set("right", o), q))()
```

## Deserialization

```js
import { serialize, deserialize } from 'seroval';

const value = undefined;
console.log(deserialize(serialize(value)) === value);
```

## JSON

`serialize` and `deserialize` is great for server-to-client communication, but what about the other way? `serialize` may cause an [RCE if used as a payload for requests](https://huntr.dev/bounties/63f1ff91-48f3-4886-a179-103f1ddd8ff8). `seroval` includes `toJSON` and `fromJSON` as an alternative form of serialization.

First example above outputs the following JSON

```js
import { toJSON } from 'seroval';
// ...
const result = toJSON(object);
console.log(JSON.stringify(result));
```

```json
{"t":{"t":16,"i":0,"d":{"k":["number","string","boolean","null","undefined","bigint","array","regexp","date","map","set","self"],"v":[{"t":15,"i":1,"l":5,"a":[{"t":0,"s":0.4350045546286634},{"t":5},{"t":8},{"t":6},{"t":7}]},{"t":15,"i":2,"l":2,"a":[{"t":1,"s":"hello world"},{"t":1,"s":"\\x3Cscript>Hello World\\x3C/script>"}]},{"t":15,"i":3,"l":2,"a":[{"t":2,"s":true},{"t":2,"s":false}]},{"t":3},{"t":4},{"t":9,"s":"9007199254740991"},{"t":15,"i":4,"l":5,"a":[null,null,null,{"t":10,"i":4},{"t":14,"i":5,"d":{"k":[{"t":1,"s":"hello"},{"t":1,"s":"self"},{"t":1,"s":"mutual"}],"v":[{"t":1,"s":"world"},{"t":10,"i":5},{"t":13,"i":6,"l":4,"a":[{"t":1,"s":"hello"},{"t":1,"s":"world"},{"t":10,"i":6},{"t":10,"i":4}]}],"s":3}}]},{"t":12,"i":7,"c":"[a-z0-9]+","m":"i"},{"t":11,"i":8,"s":"2023-03-22T02:55:33.504Z"},{"t":10,"i":5},{"t":10,"i":6},{"t":10,"i":0}],"s":12}},"r":0,"i":true,"f":8191,"m":[4,5,6,0]}
```

Then you can feed it to `fromJSON`:

```js
import { fromJSON } from 'seroval';

const revived = fromJSON(result);
```

Alternatively, if you want to compile the JSON output to JS (like `deserialize`), you can use `compileJSON`

```js
import { compileJSON, deserialize } from 'seroval';

const code = compileJSON(result);
const revived = deserialize(code);
```

## Promise serialization

`seroval` allows Promise serialization through `serializeAsync` and `toJSONAsync`.

```js
import { serializeAsync } from 'seroval';

const value = Promise.resolve(100);

const result = await serializeAsync(value); // "Promise.resolve(100)"

console.log(await deserialize(result)); // 100
```

> **Note**
> `seroval` can only serialize the resolved value and so the output will always be using `Promise.resolve`. If the Promise fulfills with rejection, the rejected value is thrown before serialization happens.
## Serializable references

There are values that has no way to be serializable at all, i.e. functions, but usually in an isomorphic code, functions can exist on both client and server-side. What if we can serialize these functions in such a way we can refer to their counterparts?

`seroval` has `createReference` that you can use to map user-defined strings to their references.

```js
import { createReference } from 'seroval';

const thisIsAnIsomorphicFunction = createReference(
// This is (ideally) a unique identifier
// that is used to map the serialized value
// to its actual reference (and vice versa)
'my-function',
() => {
// Ideally this function should exist on both
// server and client, but we want to add the ability
// to serialize and deserialize this reference on
// both sides
}
);

// we can now serialize this
const serialized = toJSON(thisIsAnIsomorphicFunction); // or any of the serializer
thisIsAnIsomorphicFunction === fromJSON(serialized); // true
```

> **Note**
> It can only accept objects, functions and symbols and it doesn't actually
> serialize their values but only the string you used to identify the reference
## Docs

- [Compatibility](https://github.com/lxsmnsyc/seroval/blob/main/docs/compatibility.md)
- [Isomorphic References](https://github.com/lxsmnsyc/seroval/blob/main/docs/isomorphic-refs.md)

## Sponsors

Expand Down
30 changes: 30 additions & 0 deletions docs/isomorphic-refs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Isomorphic references

There are values that has no way to be serializable at all, i.e. functions, but usually in an isomorphic code, functions can exist on both client and server-side. What if we can serialize these functions in such a way we can refer to their counterparts?

`seroval` has `createReference` that you can use to map user-defined strings to their references.

```js
import { createReference } from 'seroval';

const thisIsAnIsomorphicFunction = createReference(
// This is (ideally) a unique identifier
// that is used to map the serialized value
// to its actual reference (and vice versa)
'my-function',
() => {
// Ideally this function should exist on both
// server and client, but we want to add the ability
// to serialize and deserialize this reference on
// both sides
}
);

// we can now serialize this
const serialized = toJSON(thisIsAnIsomorphicFunction); // or any of the serializer
thisIsAnIsomorphicFunction === fromJSON(serialized); // true
```

> **Note**
> It can only accept objects, functions and symbols and it doesn't actually
> serialize their values but only the string you used to identify the reference
138 changes: 1 addition & 137 deletions packages/seroval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,146 +80,10 @@ Output (as a string):
}, h[3] = h, j.set("self", j), k.add(k).add(h), m.self = m, m))()
```

### Mutual cyclic example

```js
import { serialize } from 'seroval';

const a = new Map([['name', 'a']]);
const b = new Map([['name', 'b']]);
const c = new Map([['name', 'c']]);
const d = new Map([['name', 'd']]);

c.set('left', a);
d.set('left', a);
c.set('right', b);
d.set('right', b);
a.set('children', [c, d]);
b.set('children', [c, d]);

const result = serialize({ a, b, c, d });
console.log(result);
```

Output (as a string):

```js
((h,j,k,m,o,q)=>(q={a:h=new Map([["name","a"],["children",[j=new Map([["name","c"],["right",o=new Map([["name","b"],["children",k=[,m=new Map([["name","d"]])]]])]]),m]]]),b:o,c:j,d:m},j.set("left",h),k[0]=j,m.set("left",h).set("right",o),q))()

// Formatted
((h, j, k, m, o, q) => (q = {
a: h = new Map([
["name", "a"],
["children", [j = new Map([
["name", "c"],
["right", o = new Map([
["name", "b"],
["children", k = [, m = new Map([
["name", "d"]
])]]
])]
]), m]]
]),
b: o,
c: j,
d: m
}, j.set("left", h), k[0] = j, m.set("left", h).set("right", o), q))()
```

## Deserialization

```js
import { serialize, deserialize } from 'seroval';

const value = undefined;
console.log(deserialize(serialize(value)) === value);
```

## JSON

`serialize` and `deserialize` is great for server-to-client communication, but what about the other way? `serialize` may cause an [RCE if used as a payload for requests](https://huntr.dev/bounties/63f1ff91-48f3-4886-a179-103f1ddd8ff8). `seroval` includes `toJSON` and `fromJSON` as an alternative form of serialization.

First example above outputs the following JSON

```js
import { toJSON } from 'seroval';
// ...
const result = toJSON(object);
console.log(JSON.stringify(result));
```

```json
{"t":{"t":16,"i":0,"d":{"k":["number","string","boolean","null","undefined","bigint","array","regexp","date","map","set","self"],"v":[{"t":15,"i":1,"l":5,"a":[{"t":0,"s":0.4350045546286634},{"t":5},{"t":8},{"t":6},{"t":7}]},{"t":15,"i":2,"l":2,"a":[{"t":1,"s":"hello world"},{"t":1,"s":"\\x3Cscript>Hello World\\x3C/script>"}]},{"t":15,"i":3,"l":2,"a":[{"t":2,"s":true},{"t":2,"s":false}]},{"t":3},{"t":4},{"t":9,"s":"9007199254740991"},{"t":15,"i":4,"l":5,"a":[null,null,null,{"t":10,"i":4},{"t":14,"i":5,"d":{"k":[{"t":1,"s":"hello"},{"t":1,"s":"self"},{"t":1,"s":"mutual"}],"v":[{"t":1,"s":"world"},{"t":10,"i":5},{"t":13,"i":6,"l":4,"a":[{"t":1,"s":"hello"},{"t":1,"s":"world"},{"t":10,"i":6},{"t":10,"i":4}]}],"s":3}}]},{"t":12,"i":7,"c":"[a-z0-9]+","m":"i"},{"t":11,"i":8,"s":"2023-03-22T02:55:33.504Z"},{"t":10,"i":5},{"t":10,"i":6},{"t":10,"i":0}],"s":12}},"r":0,"i":true,"f":8191,"m":[4,5,6,0]}
```

Then you can feed it to `fromJSON`:

```js
import { fromJSON } from 'seroval';

const revived = fromJSON(result);
```

Alternatively, if you want to compile the JSON output to JS (like `deserialize`), you can use `compileJSON`

```js
import { compileJSON, deserialize } from 'seroval';

const code = compileJSON(result);
const revived = deserialize(code);
```

## Promise serialization

`seroval` allows Promise serialization through `serializeAsync` and `toJSONAsync`.

```js
import { serializeAsync } from 'seroval';

const value = Promise.resolve(100);

const result = await serializeAsync(value); // "Promise.resolve(100)"

console.log(await deserialize(result)); // 100
```

> **Note**
> `seroval` can only serialize the resolved value and so the output will always be using `Promise.resolve`. If the Promise fulfills with rejection, the rejected value is thrown before serialization happens.
## Serializable references

There are values that has no way to be serializable at all, i.e. functions, but usually in an isomorphic code, functions can exist on both client and server-side. What if we can serialize these functions in such a way we can refer to their counterparts?

`seroval` has `createReference` that you can use to map user-defined strings to their references.

```js
import { createReference } from 'seroval';

const thisIsAnIsomorphicFunction = createReference(
// This is (ideally) a unique identifier
// that is used to map the serialized value
// to its actual reference (and vice versa)
'my-function',
() => {
// Ideally this function should exist on both
// server and client, but we want to add the ability
// to serialize and deserialize this reference on
// both sides
}
);

// we can now serialize this
const serialized = toJSON(thisIsAnIsomorphicFunction); // or any of the serializer
thisIsAnIsomorphicFunction === fromJSON(serialized); // true
```

> **Note**
> It can only accept objects, functions and symbols and it doesn't actually
> serialize their values but only the string you used to identify the reference
## Docs

- [Compatibility](https://github.com/lxsmnsyc/seroval/blob/main/docs/compatibility.md)
- [Isomorphic References](https://github.com/lxsmnsyc/seroval/blob/main/docs/isomorphic-refs.md)

## Sponsors

Expand Down

0 comments on commit e67edad

Please sign in to comment.