-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
95 lines (88 loc) · 2.33 KB
/
index.js
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
let _target, isAttached;
const callbacks = {};
const reducePath = list => list.reduce((o, prop) => (o ? o[prop] : o), _target);
async function messageHandler(event) {
const data = event.data;
let id, type, cb;
if (id = data && data.id) {
if ((type = data.type) && _target) {
data.path = data.path || [];
const msg = { id };
const ref = reducePath(data.path);
const refParent = reducePath(data.path.slice(0, -1));
switch (type) {
case 'G': // Get
msg.value = ref;
break;
case 'S': // Set
const prop = data.path.length && data.path.pop();
if (prop) {
refParent[prop] = data.value;
}
break;
case 'A': // Apply
try {
msg.value = await ref.apply(refParent, data.args || []);
} catch (err) {
msg.error = err.toString();
}
break;
}
event.source.postMessage(msg, '*');
} else if (cb = callbacks[id]) {
delete callbacks[id];
if (data.error) {
cb[1](new Error(data.error));
} else {
cb[0](data.value);
}
}
}
}
function attach() {
if (!isAttached) {
self.addEventListener('message', messageHandler);
isAttached = true;
}
}
function createRemote(w) {
const uid = `${Date.now()}-${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
let c = 0;
attach();
return request => {
const args = request.args || [];
const id = `${uid}-${++c}`;
return new Promise((resolve, reject) => {
callbacks[id] = [resolve, reject];
w.postMessage(Object.assign({}, request, { id, args }), '*');
});
};
}
function proxy(remote, path) {
path = path || [];
return new Proxy(function () { }, {
get(_, prop, rec) {
if (prop === 'then') {
if (path.length === 0) {
return { then: () => rec };
}
const p = remote({ type: 'G', path });
return p.then.bind(p);
}
return proxy(remote, path.concat(prop));
},
set(_, prop, value) {
return remote({ type: 'S', path: path.concat(prop), value });
},
apply(_, __, args) {
return remote({ type: 'A', path, args });
}
});
}
export function link(endPoint) {
return proxy(createRemote(endPoint));
}
export function expose(target) {
_target = target;
attach();
}