Skip to content

Commit 3df057d

Browse files
feat(overmind): async parsing and serializing of messages
1 parent 5b55c1b commit 3df057d

File tree

8 files changed

+137
-117
lines changed

8 files changed

+137
-117
lines changed

packages/node_modules/a-json/src/index.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,17 @@ test("should stringify objects in arrays", async () => {
3535
const result = await stringify(obj);
3636
expect(result).toBe(JSON.stringify(obj));
3737
});
38+
39+
test("should stringify complex stuff", async () => {
40+
const string = '{"type":"effect","data":{"result":[{"title":"statecharts","fileName":"statecharts.md"}],"isPending":false,"error":false}}'
41+
const obj = JSON.parse(string)
42+
const result = await stringify(obj)
43+
expect(result).toBe(string)
44+
})
45+
46+
test("should handle more scenarios", async () => {
47+
const string = '{"type":"effect","data":{"namespacePath":[],"actionId":"onInitialize","executionId":0,"actionName":"onInitialize","operatorId":0,"isRunning":true,"path":[],"type":"action","effectId":14,"name":"","method":"request","args":["/backend/apis"],"isPending":true,"error":false}}'
48+
const obj = JSON.parse(string)
49+
const result = await stringify(obj)
50+
expect(result).toBe(string)
51+
})

packages/node_modules/a-json/src/index.ts

Lines changed: 50 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Symbol used as placeholders for objects and arrays
2-
const IS_ARRAY = Symbol("IS_ARRAY");
3-
const IS_OBJECT = Symbol("IS_OBJECT");
2+
const IS_ARRAY_START = Symbol("IS_ARRAY_START");
3+
const IS_ARRAY_END = Symbol("IS_ARRAY_END");
4+
const IS_OBJECT_START = Symbol("IS_OBJECT_START");
5+
const IS_OBJECT_END = Symbol("IS_OBJECT_END");
46

57
let ASYNC_BATCH_COUNT = 3000;
68
let TRACK_PERF = false;
@@ -10,8 +12,7 @@ export const tweak = (batchCount, trackPerf) => {
1012
TRACK_PERF = Boolean(trackPerf);
1113
};
1214

13-
const next =
14-
(typeof window !== "undefined" && window.requestAnimationFrame) || setTimeout;
15+
const next = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
1516

1617
/*
1718
This function takes a value which results in a Map of
@@ -21,24 +22,23 @@ const next =
2122
*/
2223
function getPaths(value, paths, replacer, currentPath = []) {
2324
const replacedValue = replacer ? replacer(currentPath, value) : value;
24-
if (
25-
typeof replacedValue === "function" ||
26-
typeof replacedValue === "symbol"
27-
) {
25+
if (typeof replacedValue === "function" || typeof replacedValue === "symbol" || replacedValue === undefined) {
2826
} else if (Array.isArray(replacedValue)) {
29-
paths.set(currentPath.slice(), IS_ARRAY);
27+
paths.set(currentPath.slice(), IS_ARRAY_START);
3028
for (let key in replacedValue) {
3129
currentPath.push(key);
32-
getPaths(replacedValue[key], paths, replacer, currentPath);
30+
getPaths(replacedValue[key], paths, replacer ? replacer.bind(replacedValue) : replacer, currentPath);
3331
currentPath.pop();
3432
}
33+
paths.set(currentPath.slice(), IS_ARRAY_END);
3534
} else if (replacedValue !== null && typeof replacedValue === "object") {
36-
paths.set(currentPath.slice(), IS_OBJECT);
35+
paths.set(currentPath.slice(), IS_OBJECT_START);
3736
for (let key in replacedValue) {
3837
currentPath.push(key);
39-
getPaths(replacedValue[key], paths, replacer, currentPath);
38+
getPaths(replacedValue[key], paths, replacer ? replacer.bind(replacedValue) : replacer, currentPath);
4039
currentPath.pop();
4140
}
41+
paths.set(currentPath.slice(), IS_OBJECT_END);
4242
} else {
4343
paths.set(currentPath.slice(), replacedValue);
4444
}
@@ -64,25 +64,27 @@ function isRef(ref) {
6464
Then it iterates these paths and produces the JSON string
6565
*/
6666
export function stringify(value, replacer?) {
67-
return new Promise(resolve => {
67+
return new Promise<string>(resolve => {
6868
next(async () => {
69-
const paths = getPaths(value, new Map(), replacer);
69+
const paths = getPaths(value, new Map(), replacer ? replacer.bind(global || window) : replacer);
7070
const structs = [];
7171
let prevKey = [];
72+
let prevValue = null
7273
let string = "";
7374
let x = 0;
74-
let start;
75+
let start
76+
7577
if (TRACK_PERF) {
76-
start = performance.now();
78+
start = performance.now()
7779
}
80+
7881
for (let [key, value] of paths) {
7982
if (x % ASYNC_BATCH_COUNT === 0) {
8083
if (TRACK_PERF) {
81-
const end = performance.now();
82-
console.log("PERF stringify", end - start);
83-
start = end;
84+
const end = performance.now()
85+
console.log("PERF - ", end - start)
86+
start = end
8487
}
85-
8688
await new Promise(resolve => next(resolve));
8789
}
8890

@@ -92,59 +94,49 @@ export function stringify(value, replacer?) {
9294

9395
prevKey = key;
9496

95-
if (
96-
// If we are moving back up the tree, previous has to have been
97-
// an object or array
98-
(key.length && currentKey.length > key.length) ||
99-
// Or we stay at the same nested level, but previous was
100-
// an object or key
101-
(currentKey.length === key.length &&
102-
(paths.get(currentKey) === IS_OBJECT ||
103-
paths.get(currentKey) === IS_ARRAY))
104-
) {
105-
string += structs.pop() === IS_OBJECT ? "}" : "]";
97+
if (value === IS_OBJECT_END) {
98+
structs.pop()
99+
string += '}'
100+
continue
106101
}
102+
if (value === IS_ARRAY_END) {
103+
structs.pop()
104+
string += ']'
105+
continue
106+
}
107+
108+
109+
prevValue = value
107110

108111
if (key.length && key.length <= currentKey.length) {
109112
string += ",";
110113
}
111114

112-
if (value === IS_OBJECT) {
113-
const currentStruct = structs[structs.length - 1];
115+
let currentStruct = structs[structs.length - 1];
116+
117+
if (value === IS_OBJECT_START) {
114118
structs.push(value);
115-
string +=
116-
key.length && currentStruct === IS_OBJECT
117-
? `"${key[key.length - 1]}":{`
118-
: "{";
119+
string += key.length && currentStruct === IS_OBJECT_START ? `"${key[key.length - 1]}":{` : "{";
119120
continue;
120121
}
121122

122-
if (value === IS_ARRAY) {
123-
const currentStruct = structs[structs.length - 1];
123+
if (value === IS_ARRAY_START) {
124124
structs.push(value);
125-
string +=
126-
key.length && currentStruct === IS_OBJECT
127-
? `"${key[key.length - 1]}":[`
128-
: "[";
125+
string += key.length && currentStruct === IS_OBJECT_START ? `"${key[key.length - 1]}":[` : "[";
129126
continue;
130127
}
131128

132-
const currentStruct = structs[structs.length - 1];
133-
if (currentStruct === IS_OBJECT) {
129+
currentStruct = structs[structs.length - 1];
130+
if (currentStruct === IS_OBJECT_START) {
134131
string += `"${key[key.length - 1]}":${JSON.stringify(value)}`;
135132
continue;
136133
}
137-
if (currentStruct === IS_ARRAY) {
134+
if (currentStruct === IS_ARRAY_START) {
138135
string += JSON.stringify(value);
139136
continue;
140137
}
141138
}
142139

143-
while (structs.length) {
144-
const struct = structs.pop();
145-
string += struct === IS_OBJECT ? "}" : "]";
146-
}
147-
148140
resolve(string);
149141
});
150142
});
@@ -163,16 +155,18 @@ export async function parse(value) {
163155
const openingArrayBrackets = [];
164156

165157
let isInString = false;
166-
let start;
158+
let start
159+
167160
if (TRACK_PERF) {
168-
start = performance.now();
161+
start = performance.now()
169162
}
163+
170164
for (let charIndex = 0; charIndex < value.length; charIndex++) {
171165
if (charIndex % ASYNC_BATCH_COUNT === 0) {
172166
if (TRACK_PERF) {
173-
const end = performance.now();
174-
console.log("PERF parse", end - start);
175-
start = end;
167+
const end = performance.now()
168+
console.log("PERF - ", end - start)
169+
start = end
176170
}
177171
await new Promise(resolve => next(resolve));
178172
}

packages/node_modules/overmind-devtools-client/DevtoolBackend.js

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const WebSocket = require('ws')
2+
const { stringify, parse } = require('a-json')
23

34
class DevtoolBackend {
45
constructor(options) {
56
this.options = options
7+
this.currentParse = Promise.resolve()
68
this.clientSockets = {}
79
this.onClientConnection = this.onClientConnection.bind(this)
810
this.onDevtoolConnection = this.onDevtoolConnection.bind(this)
@@ -58,47 +60,50 @@ class DevtoolBackend {
5860
this.devtoolSocket = ws
5961
this.devtoolSocket.on('message', this.onDevtoolMessage)
6062
}
61-
async onDevtoolMessage(message) {
62-
const parsedMessage = JSON.parse(message)
63+
onDevtoolMessage(message) {
64+
this.currentParse = this.currentParse.then(async () => {
65+
const parsedMessage = await parse(message)
6366

64-
switch (parsedMessage.type) {
65-
case 'ping':
66-
this.devtoolSocket.send(
67-
JSON.stringify({
68-
type: 'pong'
67+
switch (parsedMessage.type) {
68+
case 'ping':
69+
this.devtoolSocket.send(
70+
JSON.stringify({
71+
type: 'pong'
72+
})
73+
)
74+
break
75+
case 'storage:get':
76+
this.evaluateDevtoolMessage(parsedMessage, () =>
77+
this.options.storage.get(parsedMessage.data.key)
78+
)
79+
break
80+
case 'storage:set':
81+
this.evaluateDevtoolMessage(parsedMessage, () => {
82+
this.options.storage.set(
83+
parsedMessage.data.key,
84+
parsedMessage.data.data
85+
)
6986
})
70-
)
71-
break
72-
case 'storage:get':
73-
this.evaluateDevtoolMessage(parsedMessage, () =>
74-
this.options.storage.get(parsedMessage.data.key)
75-
)
76-
break
77-
case 'storage:set':
78-
this.evaluateDevtoolMessage(parsedMessage, () => {
79-
this.options.storage.set(
80-
parsedMessage.data.key,
81-
parsedMessage.data.data
87+
break
88+
case 'storage:clear':
89+
this.evaluateDevtoolMessage(parsedMessage, () =>
90+
this.options.storage.clear()
8291
)
83-
})
84-
break
85-
case 'storage:clear':
86-
this.evaluateDevtoolMessage(parsedMessage, () =>
87-
this.options.storage.clear()
88-
)
89-
break
90-
case 'relaunch':
91-
this.options.onRelaunch()
92-
break
93-
case 'openChromeDevtools':
94-
this.options.openChromeDevtools();
95-
break;
96-
default:
97-
console.log('DEVTOOL MESSAGE', message)
98-
this.clientSockets[parsedMessage.data.appName].send(
99-
JSON.stringify(parsedMessage.data)
100-
)
101-
}
92+
break
93+
case 'relaunch':
94+
this.options.onRelaunch()
95+
break
96+
case 'openChromeDevtools':
97+
this.options.openChromeDevtools();
98+
break;
99+
default:
100+
console.log('DEVTOOL MESSAGE', message)
101+
this.clientSockets[parsedMessage.data.appName].send(
102+
await stringify(parsedMessage.data)
103+
)
104+
}
105+
})
106+
102107
}
103108
onClientConnection(name, ws) {
104109
if (this.clientSockets[name]) {
@@ -123,12 +128,7 @@ class DevtoolBackend {
123128
})
124129
}
125130
onClientMessage(message) {
126-
this.devtoolSocket.send(`
127-
{
128-
"type": "message",
129-
"data": ${message}
130-
}
131-
`)
131+
this.devtoolSocket.send(`{"type":"message","data":${message}}`)
132132
}
133133
async evaluateDevtoolMessage(message, cb) {
134134
const result = await cb()

packages/node_modules/overmind-devtools-client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"emotion": "^9.2.12",
2121
"lodash.throttle": "^4.1.1",
2222
"overmind-react": "next",
23-
"overmind-themes": "next",
23+
"a-json": "next",
2424
"react": "16.9.0",
2525
"react-dom": "16.9.0",
2626
"react-split-pane": "^0.1.87",

packages/node_modules/overmind-devtools-client/src/BackendConnector.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AppMessage } from './overmind/types'
2+
import { parse } from 'a-json'
23

34
type Message = {
45
appName: string
@@ -9,6 +10,7 @@ type MessageCallback = (message: Message) => void
910

1011
class WebsocketConnector {
1112
private nextEvaluationId = 0
13+
private parser = Promise.resolve()
1214
private callbacks: {
1315
[event: string]: Function[]
1416
} = {}
@@ -29,13 +31,17 @@ class WebsocketConnector {
2931
}
3032
this.socket = new WebSocket(`ws://localhost:${port}?devtools=1`)
3133
this.socket.onmessage = (message) => {
32-
const parsedMessage = JSON.parse(message.data)
34+
this.parser = this.parser.then(async () => {
35+
const parsedMessage = await parse(message.data)
3336

34-
if (parsedMessage.type === 'pong') {
35-
this.schedulePing()
36-
} else {
37-
this.emit(parsedMessage.type, parsedMessage.data)
38-
}
37+
if (parsedMessage.type === 'pong') {
38+
this.schedulePing()
39+
} else {
40+
this.emit(parsedMessage.type, parsedMessage.data)
41+
}
42+
}).catch(() => {
43+
console.error(message)
44+
})
3945
}
4046
this.socket.onopen = () => {
4147
resolve()
42.5 KB
Loading

packages/node_modules/overmind/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"dependencies": {
3737
"is-plain-obj": "^1.1.0",
3838
"betsy": "next",
39+
"a-json": "next",
3940
"tslib": "^1.10.0",
4041
"proxy-state-tree": "next"
4142
},

0 commit comments

Comments
 (0)