Skip to content

Commit c4ba740

Browse files
committed
Major overhaul
1 parent 839e30b commit c4ba740

File tree

5 files changed

+809
-2029
lines changed

5 files changed

+809
-2029
lines changed

README.md

Lines changed: 117 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,125 @@
11
# Marshaller [![NPM version][npm-image]][npm-url]
2-
> A class that maps between a variety of data types.
2+
> A class that can map various data types back and forth between a string representation that can be transferred over the wire or evaluated using eval() or new Function() expressions and back to the native environment. This is equivalent to `JSON.parse()` and `JSON.stringify()` except with a much broader scope.
33
44
## Installation
55
Simply do: `npm install @wessberg/marshaller`.
66

77
## DISCLAIMER
88

9-
This is an early version. There are still some data types that aren't fully handled.
10-
You can use this now in production, but keep track of the roadmap inside this readme.
9+
This is an early version. There may still be bugs. If you run into some, please submit an issue on GitHub.
1110

1211
## Usage
13-
```javascript
12+
```typescript
1413
const marshaller = new Marshaller();
1514

16-
marshaller.marshal("true", Boolean); // true
17-
marshaller.marshal("0", Boolean); // false
18-
marshaller.marshal([1, 2, false, true], String); // [ 1, 2, false, true]
19-
marshaller.marshal("Infinity"); // Auto-mapped to number - Infinity.
20-
marshaller.marshal<number, string>(123, "a hint") // "123", generic typecasting.
15+
// Convert even complex data into a string representation, for example so it can be sent over the network.
16+
marshaller.marshal({
17+
a: 1,
18+
b: new Date(),
19+
c: /foo/,
20+
d: [1, "1", new Set([1, 2, 3])],
21+
e: new (class Foo {})
22+
}); // returns: {"a": 1, "b": new Date("2017-06-27T16:55:07.357Z"), "c": /foo/, "d": [1,"1",new Set([1,2,3])], "e": new (class Foo {static get __INSTANCE__VALUES_MAP () {return {}}})()}
23+
24+
// Convert the data back into complex types for the host environment.
25+
const unmarshalled = marshaller.unmarshal(`{"a": 1, "b": new Date("2017-06-27T16:55:07.357Z"), "c": /foo/, "d": [1,"1",new Set([1,2,3])], "e": new (class Foo {static get __INSTANCE__VALUES_MAP () {return {}}})()}`);
26+
27+
// The data will have proper types
28+
typeof unmarshalled.a === "number"; // true
29+
unmarshalled.b instanceof Date; // true
30+
unmarshalled.c instanceof RegExp; // true
31+
Array.isArray(unmarshalled.d); // true
32+
const [first, second, third] = unmarshalled.d;
33+
34+
// The Set will have the correct entries
35+
third instanceof Set // true
36+
third.has(2) // true
37+
38+
// Even the class instance can be reconstructed, including its members.
39+
unmarshalled.e instanceof Foo // true
2140
```
2241

23-
As you can see, you simply pass some arbitrary data and *optionally* the type
24-
you want to map to. If you don't provide any, `Marshaller` will attempt to find
25-
the most appropriate type to map to using heuristics. The second argument, the hint,
26-
accepts either a constructor for a type or a concrete instance of one.
42+
As you can see, there is really no limit to the kind of data you can marshall/unmarshall.
43+
The library was written with the primary purpose of being able to cast anything to/from a string representation
44+
so it could be networked, even instances of classes with mutated properties. This makes it possible to send class instances back and
45+
forth between a client and server.
46+
47+
For example:
48+
49+
### Client
50+
51+
```typescript
52+
class Foo {
53+
public isMutated: boolean = false;
54+
}
55+
56+
const marshaller = new Marshaller();
57+
const instance = new Foo();
58+
instance.isMutated = true;
59+
60+
// Marshal the class instance so it can be sent over the wire
61+
const marshalled = marshaller.marshal(instance);
62+
63+
// Send it over HTTP...
64+
```
65+
66+
### Server
67+
68+
```typescript
69+
const marshaller = new Marshaller();
70+
71+
// Upon receiving a request from the client...
72+
const unmarshalled = marshaller.unmarshal(marshalledPayload);
73+
unmarshalled instanceof Foo; // true
74+
unmarshalled.isMutated; // true
75+
```
76+
77+
## Use cases
78+
79+
This API is relatively low-level and allows for designing high-level APIs that abstracts away any need
80+
to go to a string representation and back. Some include transferring data over the network while others include
81+
storing data in a serialized database (such as `localStorage`).
82+
83+
An example could be a controller for a REST API endpoint, e.g.:
84+
85+
```typescript
86+
class TodoController extends Controller {
87+
88+
@PUTEndpoint("/api/todos/:id")
89+
async putTodoItem (todo: TodoItem) {
90+
await put("todos", todo.id, todo);
91+
}
92+
}
93+
```
94+
95+
Where the base controller unmarshals the input data before passing it on to user-facing controllers.
96+
There are many use cases for marshalling data though, and yours may be different.
2797

2898
## API
29-
`marshal<T, U> (data: T, hint?: U|Newable<U>): U | null|undefined`
99+
100+
### `marshal()`
101+
102+
`marshal<T> (data: T): string`
30103

31104
#### Params
32105
##### `data: T`
33106
The input data.
34107

35-
##### `hint?: U|Newable<U>`
36-
An optional hint to define which data type to marshal to. If omitted, the data will be parsed
37-
into the type `Marshaller` finds most appropriate.
108+
#### Returns
109+
##### `string`
110+
The marshalled string representation of the data.
111+
112+
### `unmarshal()`
113+
114+
`unmarshal<T> (data: string): T|{}|null|undefined`
115+
116+
#### Params
117+
##### `data: string`
118+
The marshalled input data
38119

39120
#### Returns
40-
##### `U`
41-
The marshalled version of the input data.
121+
##### `T|{}|null|undefined`
122+
The unmarshalled data. Can be anything.
42123

43124
## Roadmap
44125
* [X] Casting from/to `Set`.
@@ -55,12 +136,26 @@ The marshalled version of the input data.
55136
* [X] Casting from/to `Date`.
56137
* [X] Casting from/to `Function`
57138
* [X] Casting from/to `Map`
58-
* [ ] Casting from/to `WeakSet`
59-
* [ ] Casting from/to `WeakMap`
60-
* [ ] Casting from/to `RegExp`
139+
* [X] Casting from/to `WeakSet` (*)
140+
* [X] Casting from/to `WeakMap` (*)
141+
* [X] Casting from/to `RegExp`
142+
143+
The (*) means that the types cannot be restored to their initial state since the keys are weak and not iterable. There are no way of restoring the state. Instead, new instances will be created.
61144

62145
## Changelog:
63146

147+
**v1.1.0**:
148+
149+
- Major overhaul. Where the Marshaller could previously map between any types, the Marshaller now has a sharp focus on being able to marshal any data to a string representation and be able to unmarshal the data back into the native representation in a non-destructive operation (e.g. all data should be re-retrievable).
150+
151+
- `RegExp`, `WeakMap` and `WeakSet` is now supported.
152+
153+
- Smaller size.
154+
155+
**v1.0.25**:
156+
157+
- Class instances can now be marshalled to strings and marshalled back into a native representation while stile preserving the instance values that has been set over time. This allows for, among other things, sending an instance of a class over the wire and then "reassembling" the class and instance values. For the user, this feels like sending a complex class instance via HTTP.
158+
64159
**v1.0.24**:
65160

66161
- Added mapping to/from `Date`.

0 commit comments

Comments
 (0)