rimless 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/guest.d.ts +2 -2
- package/lib/rimless.js +108 -105
- package/lib/rimless.js.map +1 -1
- package/lib/rimless.min.js +1 -1
- package/lib/rimless.min.js.map +1 -1
- package/lib/types.d.ts +3 -0
- package/package.json +1 -1
package/lib/guest.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IConnection, ISchema } from './types';
|
|
2
|
-
declare function connect(schema?: ISchema): Promise<IConnection>;
|
|
1
|
+
import { EventHandlers, IConnection, ISchema } from './types';
|
|
2
|
+
declare function connect(schema?: ISchema, eventHandlers?: EventHandlers): Promise<IConnection>;
|
|
3
3
|
declare const _default: {
|
|
4
4
|
connect: typeof connect;
|
|
5
5
|
};
|
package/lib/rimless.js
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
1
|
function A() {
|
|
2
2
|
return typeof WorkerGlobalScope < "u" && self instanceof WorkerGlobalScope;
|
|
3
3
|
}
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
return function s(
|
|
7
|
-
Object.keys(
|
|
8
|
-
const c =
|
|
9
|
-
|
|
4
|
+
function m(e) {
|
|
5
|
+
const n = [];
|
|
6
|
+
return function s(r, o = "") {
|
|
7
|
+
Object.keys(r).forEach((t) => {
|
|
8
|
+
const c = o ? `${o}.${t}` : t;
|
|
9
|
+
r[t] === Object(r[t]) && s(r[t], c), typeof r[t] == "function" && n.push(c);
|
|
10
10
|
});
|
|
11
|
-
}(e),
|
|
11
|
+
}(e), n;
|
|
12
12
|
}
|
|
13
|
-
const I = /^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/,
|
|
14
|
-
function
|
|
15
|
-
const { location:
|
|
16
|
-
let
|
|
17
|
-
if (s ? [,
|
|
13
|
+
const I = /^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/, T = { "http:": "80", "https:": "443" };
|
|
14
|
+
function _(e) {
|
|
15
|
+
const { location: n } = document, s = I.exec(e || "");
|
|
16
|
+
let r, o, t;
|
|
17
|
+
if (s ? [, r = n.protocol, o, , t] = s : (r = n.protocol, o = n.hostname, t = n.port), r === "file:")
|
|
18
18
|
return "null";
|
|
19
|
-
const c = t && t !==
|
|
20
|
-
return `${
|
|
19
|
+
const c = t && t !== T[r] ? `:${t}` : "";
|
|
20
|
+
return `${r}//${o}${c}`;
|
|
21
21
|
}
|
|
22
|
-
var
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
let
|
|
26
|
-
for (const t of
|
|
27
|
-
if (
|
|
22
|
+
var E = /* @__PURE__ */ ((e) => (e.MESSAGE = "message", e))(E || {}), S = /* @__PURE__ */ ((e) => (e.HANDSHAKE_REQUEST = "RIMLESS/HANDSHAKE_REQUEST", e.HANDSHAKE_REPLY = "RIMLESS/HANDSHAKE_REPLY", e.RPC_REQUEST = "RIMLESS/RPC_REQUEST", e.RPC_RESOLVE = "RIMLESS/RPC_RESOLVE", e.RPC_REJECT = "RIMLESS/RPC_REJECT", e))(S || {});
|
|
23
|
+
function C(e, n, s) {
|
|
24
|
+
const r = Array.isArray(n) ? n : n.split(".").filter(Boolean);
|
|
25
|
+
let o = e;
|
|
26
|
+
for (const t of r)
|
|
27
|
+
if (o = o == null ? void 0 : o[t], o === void 0)
|
|
28
28
|
return s;
|
|
29
|
-
return
|
|
29
|
+
return o;
|
|
30
30
|
}
|
|
31
|
-
function D(e,
|
|
31
|
+
function D(e, n, s) {
|
|
32
32
|
if (!e || typeof e != "object") return e;
|
|
33
|
-
const
|
|
34
|
-
let
|
|
35
|
-
for (let t = 0; t <
|
|
36
|
-
const c =
|
|
37
|
-
t ===
|
|
33
|
+
const r = Array.isArray(n) ? n : n.split(".").map((t) => t.match(/^\d+$/) ? Number(t) : t);
|
|
34
|
+
let o = e;
|
|
35
|
+
for (let t = 0; t < r.length; t++) {
|
|
36
|
+
const c = r[t];
|
|
37
|
+
t === r.length - 1 ? o[c] = s : ((!o[c] || typeof o[c] != "object") && (o[c] = typeof r[t + 1] == "number" ? [] : {}), o = o[c]);
|
|
38
38
|
}
|
|
39
39
|
return e;
|
|
40
40
|
}
|
|
41
|
-
function
|
|
42
|
-
const
|
|
41
|
+
function L(e = 10) {
|
|
42
|
+
const n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
43
43
|
let s = "";
|
|
44
|
-
for (let
|
|
45
|
-
s +=
|
|
44
|
+
for (let r = 0; r < e; r++)
|
|
45
|
+
s += n.charAt(Math.floor(Math.random() * n.length));
|
|
46
46
|
return s;
|
|
47
47
|
}
|
|
48
|
-
function y(e = {},
|
|
49
|
-
const
|
|
50
|
-
return
|
|
48
|
+
function y(e = {}, n = [], s, r) {
|
|
49
|
+
const o = [];
|
|
50
|
+
return n.forEach((t) => {
|
|
51
51
|
async function c(i) {
|
|
52
|
-
const { action:
|
|
53
|
-
if (
|
|
54
|
-
const
|
|
55
|
-
action:
|
|
56
|
-
callID:
|
|
57
|
-
callName:
|
|
58
|
-
connectionID:
|
|
52
|
+
const { action: f, callID: a, connectionID: u, callName: d, args: p = [] } = i.data;
|
|
53
|
+
if (f !== S.RPC_REQUEST || !a || !d || d !== t || u !== s) return;
|
|
54
|
+
const l = {
|
|
55
|
+
action: S.RPC_RESOLVE,
|
|
56
|
+
callID: a,
|
|
57
|
+
callName: d,
|
|
58
|
+
connectionID: u,
|
|
59
59
|
error: null,
|
|
60
60
|
result: null
|
|
61
61
|
};
|
|
62
62
|
try {
|
|
63
|
-
const R = await
|
|
64
|
-
|
|
63
|
+
const R = await C(e, t)(...p);
|
|
64
|
+
l.result = JSON.parse(JSON.stringify(R));
|
|
65
65
|
} catch (R) {
|
|
66
|
-
|
|
66
|
+
l.error = JSON.parse(JSON.stringify(R, Object.getOwnPropertyNames(R)));
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
r ? r.postMessage(l) : A() ? self.postMessage(l) : i.source.postMessage(l, i.origin);
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
}), () =>
|
|
70
|
+
r ? r.addEventListener(E.MESSAGE, c) : self.addEventListener(E.MESSAGE, c), o.push(() => self.removeEventListener(E.MESSAGE, c));
|
|
71
|
+
}), () => o.forEach((t) => t());
|
|
72
72
|
}
|
|
73
|
-
function
|
|
73
|
+
function w(e, n, s, r = [], o) {
|
|
74
74
|
return (...t) => new Promise((c, i) => {
|
|
75
|
-
const
|
|
76
|
-
function
|
|
77
|
-
const { callID: p, connectionID:
|
|
78
|
-
if (!(!p || !R) && R === e &&
|
|
79
|
-
if (g ===
|
|
80
|
-
if (g ===
|
|
75
|
+
const f = L();
|
|
76
|
+
function a(d) {
|
|
77
|
+
const { callID: p, connectionID: l, callName: R, result: N, error: O, action: g } = d.data;
|
|
78
|
+
if (!(!p || !R) && R === e && l === n) {
|
|
79
|
+
if (g === S.RPC_RESOLVE) return c(N);
|
|
80
|
+
if (g === S.RPC_REJECT) return i(O);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
const
|
|
84
|
-
action:
|
|
83
|
+
const u = {
|
|
84
|
+
action: S.RPC_REQUEST,
|
|
85
85
|
args: JSON.parse(JSON.stringify(t)),
|
|
86
|
-
callID:
|
|
86
|
+
callID: f,
|
|
87
87
|
callName: e,
|
|
88
|
-
connectionID:
|
|
88
|
+
connectionID: n
|
|
89
89
|
};
|
|
90
|
-
|
|
90
|
+
o ? o.addEventListener(E.MESSAGE, a) : self.addEventListener(E.MESSAGE, a), r.push(() => self.removeEventListener(E.MESSAGE, a)), o ? o.postMessage(u) : A() ? self.postMessage(u) : (s.source || s.target).postMessage(u, s.origin);
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
|
-
function P(e = {},
|
|
93
|
+
function P(e = {}, n = [], s, r, o) {
|
|
94
94
|
const t = { ...e }, c = [];
|
|
95
|
-
return
|
|
96
|
-
const
|
|
97
|
-
D(t, i,
|
|
95
|
+
return n.forEach((i) => {
|
|
96
|
+
const f = w(i, s, r, c, o);
|
|
97
|
+
D(t, i, f);
|
|
98
98
|
}), {
|
|
99
99
|
remote: t,
|
|
100
100
|
unregisterRemote: () => c.forEach((i) => i())
|
|
@@ -102,80 +102,83 @@ function P(e = {}, o = [], s, n, r) {
|
|
|
102
102
|
}
|
|
103
103
|
const G = 10, J = 3e3;
|
|
104
104
|
let M = null, h = !1;
|
|
105
|
-
function
|
|
106
|
-
return new Promise((
|
|
107
|
-
const
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
function U(e = {}, n) {
|
|
106
|
+
return new Promise((s, r) => {
|
|
107
|
+
const o = m(e);
|
|
108
|
+
async function t(i) {
|
|
109
|
+
var l;
|
|
110
|
+
if (i.data.action !== S.HANDSHAKE_REPLY) return;
|
|
111
|
+
const f = y(e, o, i.data.connectionID), { remote: a, unregisterRemote: u } = P(
|
|
112
|
+
i.data.schema,
|
|
113
|
+
i.data.methods,
|
|
114
|
+
i.data.connectionID,
|
|
115
|
+
i
|
|
116
|
+
);
|
|
117
|
+
await ((l = n == null ? void 0 : n.onConnectionSetup) == null ? void 0 : l.call(n, a));
|
|
118
|
+
const d = () => {
|
|
119
|
+
self.removeEventListener(E.MESSAGE, t), u(), f();
|
|
117
120
|
};
|
|
118
|
-
return h = !0,
|
|
121
|
+
return h = !0, s({ remote: a, close: d });
|
|
119
122
|
}
|
|
120
|
-
self.addEventListener(
|
|
121
|
-
const
|
|
122
|
-
action:
|
|
123
|
-
methods:
|
|
123
|
+
self.addEventListener(E.MESSAGE, t);
|
|
124
|
+
const c = {
|
|
125
|
+
action: S.HANDSHAKE_REQUEST,
|
|
126
|
+
methods: o,
|
|
124
127
|
schema: JSON.parse(JSON.stringify(e))
|
|
125
128
|
};
|
|
126
129
|
M = setInterval(() => {
|
|
127
130
|
if (h) return clearInterval(M);
|
|
128
|
-
A() ? self.postMessage(
|
|
131
|
+
A() ? self.postMessage(c) : window.parent.postMessage(c, "*");
|
|
129
132
|
}, G), setTimeout(() => {
|
|
130
|
-
h ||
|
|
133
|
+
h || r("connection timeout");
|
|
131
134
|
}, J);
|
|
132
135
|
});
|
|
133
136
|
}
|
|
134
137
|
const k = {
|
|
135
|
-
connect:
|
|
138
|
+
connect: U
|
|
136
139
|
};
|
|
137
|
-
function
|
|
138
|
-
const s = e.getAttribute("src"),
|
|
139
|
-
return
|
|
140
|
+
function H(e, n) {
|
|
141
|
+
const s = e.getAttribute("src"), r = _(s), o = n.origin === r, t = n.source === e.contentWindow;
|
|
142
|
+
return o && t;
|
|
140
143
|
}
|
|
141
|
-
function Q(e,
|
|
144
|
+
function Q(e, n = {}) {
|
|
142
145
|
if (!e) throw new Error("a target is required");
|
|
143
|
-
const s = e.onerror !== void 0 && e.onmessage !== void 0,
|
|
144
|
-
return new Promise((
|
|
145
|
-
const t =
|
|
146
|
+
const s = e.onerror !== void 0 && e.onmessage !== void 0, r = s ? e : window;
|
|
147
|
+
return new Promise((o) => {
|
|
148
|
+
const t = L();
|
|
146
149
|
function c(i) {
|
|
147
|
-
if (!s && !
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
if (!s && !H(e, i) || i.data.action !== S.HANDSHAKE_REQUEST) return;
|
|
151
|
+
const f = m(n), a = y(
|
|
152
|
+
n,
|
|
153
|
+
f,
|
|
151
154
|
t,
|
|
152
155
|
s ? e : void 0
|
|
153
|
-
), { remote:
|
|
156
|
+
), { remote: u, unregisterRemote: d } = P(
|
|
154
157
|
i.data.schema,
|
|
155
158
|
i.data.methods,
|
|
156
159
|
t,
|
|
157
160
|
i,
|
|
158
161
|
s ? e : void 0
|
|
159
162
|
), p = {
|
|
160
|
-
action:
|
|
163
|
+
action: S.HANDSHAKE_REPLY,
|
|
161
164
|
connectionID: t,
|
|
162
|
-
methods:
|
|
163
|
-
schema: JSON.parse(JSON.stringify(
|
|
165
|
+
methods: f,
|
|
166
|
+
schema: JSON.parse(JSON.stringify(n))
|
|
164
167
|
};
|
|
165
|
-
return s ? e.postMessage(p) : i.source.postMessage(p, i.origin),
|
|
166
|
-
|
|
168
|
+
return s ? e.postMessage(p) : i.source.postMessage(p, i.origin), o({ remote: u, close: () => {
|
|
169
|
+
r.removeEventListener(E.MESSAGE, c), d(), a(), s && e.terminate();
|
|
167
170
|
} });
|
|
168
171
|
}
|
|
169
|
-
|
|
172
|
+
r.addEventListener(E.MESSAGE, c);
|
|
170
173
|
});
|
|
171
174
|
}
|
|
172
|
-
const
|
|
175
|
+
const K = {
|
|
173
176
|
connect: Q
|
|
174
177
|
};
|
|
175
178
|
export {
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
S as actions,
|
|
180
|
+
E as events,
|
|
178
181
|
k as guest,
|
|
179
|
-
|
|
182
|
+
K as host
|
|
180
183
|
};
|
|
181
184
|
//# sourceMappingURL=rimless.js.map
|
package/lib/rimless.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rimless.js","sources":["../src/helpers.ts","../src/types.ts","../src/utils.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["export const CONNECTION_TIMEOUT = 1000;\n\n/**\n * check if the remote is trusted\n *\n * @param event\n */\nexport function isTrustedRemote(_event: any) {\n // TODO: implement\n return true;\n}\n\n/**\n * check if run in a webworker\n *\n * @param event\n */\nexport function isWorker() {\n return typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope;\n}\n\n/**\n * we cannot send functions through postMessage\n * extract the path to all functions in the schema\n *\n * @param obj\n */\nexport function extractMethods(obj: any) {\n const paths: string[] = [];\n (function parse(obj: any, path = \"\") {\n Object.keys(obj).forEach((prop) => {\n const propPath = path ? `${path}.${prop}` : prop;\n if (obj[prop] === Object(obj[prop])) {\n parse(obj[prop], propPath);\n }\n if (typeof obj[prop] === \"function\") {\n paths.push(propPath);\n }\n });\n })(obj);\n return paths;\n}\n\nconst urlRegex = /^(https?:|file:)?\\/\\/([^/:]+)?(:(\\d+))?/;\nconst ports: any = { \"http:\": \"80\", \"https:\": \"443\" };\n\n/**\n * convert the url into an origin (remove paths)\n *\n * @param url\n */\nexport function getOriginFromURL(url: string | null) {\n const { location } = document;\n\n const regexResult = urlRegex.exec(url || \"\");\n let protocol;\n let hostname;\n let port;\n\n if (regexResult) {\n // It's an absolute URL. Use the parsed info.\n // regexResult[1] will be undefined if the URL starts with //\n [, protocol = location.protocol, hostname, , port] = regexResult;\n } else {\n // It's a relative path. Use the current location's info.\n protocol = location.protocol;\n hostname = location.hostname;\n port = location.port;\n }\n\n // If the protocol is file, the origin is \"null\"\n // The origin of a document with file protocol is an opaque origin\n // and its serialization \"null\" [1]\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin\n if (protocol === \"file:\") {\n return \"null\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n // or it won't match the message's event.origin.\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n","export enum events {\n MESSAGE = \"message\",\n}\n\nexport enum actions {\n HANDSHAKE_REQUEST = \"RIMLESS/HANDSHAKE_REQUEST\",\n HANDSHAKE_REPLY = \"RIMLESS/HANDSHAKE_REPLY\",\n RPC_REQUEST = \"RIMLESS/RPC_REQUEST\",\n RPC_RESOLVE = \"RIMLESS/RPC_RESOLVE\",\n RPC_REJECT = \"RIMLESS/RPC_REJECT\",\n}\n\nexport interface ISchema {\n [prop: string]: any;\n}\n\nexport interface IConnection {\n remote: ISchema;\n close: () => void;\n}\n\nexport interface IConnections {\n [connectionID: string]: ISchema;\n}\n\nexport interface IEvent extends EventListener {\n source?: Window;\n origin?: string;\n data?: IHandshakeRequestPayload | IHandshakeConfirmationPayload | IRPCRequestPayload | IRPCResolvePayload;\n}\n\nexport interface IHandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID?: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IHandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IRPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID?: string;\n}\n\nexport interface IRPCResolvePayload {\n action: actions.RPC_RESOLVE | actions.RPC_REJECT;\n result?: any | null;\n error?: Error | null;\n callID: string;\n callName: string;\n connectionID: string;\n}\n","export function get(obj: any, path: string | Array<string | number>, defaultValue?: any): any {\n const keys = Array.isArray(path) ? path : path.split(\".\").filter(Boolean);\n let result = obj;\n\n for (const key of keys) {\n result = result?.[key];\n if (result === undefined) {\n return defaultValue;\n }\n }\n\n return result;\n}\n\nexport function set(obj: any, path: string | (string | number)[], value: any): any {\n if (!obj || typeof obj !== \"object\") return obj;\n\n const pathArray = Array.isArray(path) ? path : path.split(\".\").map((key) => (key.match(/^\\d+$/) ? Number(key) : key));\n\n let current = obj;\n\n for (let i = 0; i < pathArray.length; i++) {\n const key = pathArray[i];\n\n if (i === pathArray.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== \"object\") {\n current[key] = typeof pathArray[i + 1] === \"number\" ? [] : {};\n }\n current = current[key];\n }\n }\n\n return obj;\n}\n\nexport function generateId(length: number = 10): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = \"\";\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n","import { isTrustedRemote, isWorker } from \"./helpers\";\nimport { actions, events, IRPCRequestPayload, IRPCResolvePayload, ISchema } from \"./types\";\nimport { generateId, get, set } from \"./utils\";\n\n/**\n * for each function in the schema\n * 1. subscribe to an event that the remote can call\n * 2. listen for calls from the remote. When called execute the function and emit the results.\n *\n * @param methods an array of method ids from the local schema\n * @param _connectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n guest?: Worker\n): any {\n const listeners: any[] = [];\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const { action, callID, connectionID, callName, args = [] } = event.data as IRPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== _connectionID) return;\n\n const payload: IRPCResolvePayload = {\n action: actions.RPC_RESOLVE,\n callID,\n callName,\n connectionID,\n error: null,\n result: null,\n };\n\n // run function and return the results to the remote\n try {\n const result = await get(schema, methodName)(...args);\n payload.result = JSON.parse(JSON.stringify(result));\n } catch (error) {\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n }\n\n // subscribe to the call event\n if (guest) guest.addEventListener(events.MESSAGE, handleCall);\n else self.addEventListener(events.MESSAGE, handleCall);\n\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleCall));\n });\n\n return () => listeners.forEach((unregister) => unregister());\n}\n\n/**\n * Create a function that will make an RPC request to the remote with some arguments.\n * Listen to an event that returns the results from the remote.\n *\n * @param _callName\n * @param _connectionID\n * @param event\n * @param listeners\n * @param guest\n *\n * @returns a promise with the result of the RPC\n */\nexport function createRPC(\n _callName: string,\n _connectionID: string,\n event: any,\n listeners: Array<() => void> = [],\n guest?: Worker\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const callID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const { callID, connectionID, callName, result, error, action } = event.data as IRPCResolvePayload;\n\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== _callName) return;\n if (connectionID !== _connectionID) return;\n\n // resolve the response\n if (action === actions.RPC_RESOLVE) return resolve(result);\n if (action === actions.RPC_REJECT) return reject(error);\n }\n\n // send the RPC request with arguments\n const payload = {\n action: actions.RPC_REQUEST,\n args: JSON.parse(JSON.stringify(args)),\n callID,\n callName: _callName,\n connectionID: _connectionID,\n };\n\n if (guest) guest.addEventListener(events.MESSAGE, handleResponse);\n else self.addEventListener(events.MESSAGE, handleResponse);\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleResponse));\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else (event.source || event.target).postMessage(payload, event.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema and methods. Functions in that object will\n * emit an event that will trigger the RPC on the remote.\n *\n * @param schema\n * @param methods\n * @param _connectionID\n * @param event\n * @param guest\n */\nexport function registerRemoteMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n event: any,\n guest?: Worker\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, _connectionID, event, listeners, guest);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, isWorker } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, ISchema } from \"./types\";\n\nconst REQUEST_INTERVAL = 10;\nconst TIMEOUT_INTERVAL = 3000;\n\nlet interval: any = null;\nlet connected = false;\n\nfunction connect(schema: ISchema = {}): Promise<IConnection> {\n return new Promise((resolve, reject) => {\n const localMethods = extractMethods(schema);\n\n // on handshake response\n function handleHandshakeResponse(event: any) {\n if (event.data.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, event.data.connectionID);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n event.data.connectionID,\n event\n );\n\n // close the connection and all listeners when called\n const close = () => {\n self.removeEventListener(events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n connected = true;\n\n // resolve connection object\n const connection = { remote, close };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE REPLY MESSAGES\n self.addEventListener(events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n interval = setInterval(() => {\n if (connected) return clearInterval(interval);\n\n // publish the HANDSHAKE REQUEST\n if (isWorker()) (self as any).postMessage(payload);\n else window.parent.postMessage(payload, \"*\");\n }, REQUEST_INTERVAL);\n\n // timeout the connection after a time\n setTimeout(() => {\n if (!connected) reject(\"connection timeout\");\n }, TIMEOUT_INTERVAL);\n });\n}\n\nexport default {\n connect,\n};\n","import { extractMethods, getOriginFromURL } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, IConnections, ISchema } from \"./types\";\nimport { generateId } from \"./utils\";\n\nconst connections: IConnections = {};\n\nfunction isValidTarget(iframe: HTMLIFrameElement, event: any) {\n const childURL = iframe.getAttribute(\"src\");\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return hasProperOrigin && hasProperSource;\n}\n\n/**\n * Perform a handshake with the target iframe, when the handshake is confirmed\n * resolve the connection object containing RPCs and properties\n *\n * @param iframe\n * @param schema\n * @returns Promise\n */\nfunction connect(guest: HTMLIFrameElement | Worker, schema: ISchema = {}): Promise<IConnection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = (guest as Worker).onerror !== undefined && (guest as Worker).onmessage !== undefined;\n const listeners = guestIsWorker ? guest : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n if (!guestIsWorker && !isValidTarget(guest as HTMLIFrameElement, event)) return;\n if (event.data.action !== actions.HANDSHAKE_REQUEST) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(\n schema,\n localMethods,\n connectionID,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n connectionID,\n event,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n // confirm the connection\n if (guestIsWorker) (guest as Worker).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n listeners.removeEventListener(events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n };\n\n // resolve connection object\n const connection: IConnection = { remote, close };\n connections[connectionID] = connection;\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE MESSAGES\n listeners.addEventListener(events.MESSAGE, handleHandshake);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","location","regexResult","protocol","hostname","port","portSuffix","events","actions","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","registerLocalMethods","schema","methods","_connectionID","guest","listeners","methodName","handleCall","event","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","_callName","resolve","reject","handleResponse","registerRemoteMethods","remote","rpc","REQUEST_INTERVAL","TIMEOUT_INTERVAL","interval","connected","connect","localMethods","handleHandshakeResponse","unregisterLocal","unregisterRemote","close","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","host"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rimless.js","sources":["../src/helpers.ts","../src/types.ts","../src/utils.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["export const CONNECTION_TIMEOUT = 1000;\n\n/**\n * check if the remote is trusted\n *\n * @param event\n */\nexport function isTrustedRemote(_event: any) {\n // TODO: implement\n return true;\n}\n\n/**\n * check if run in a webworker\n *\n * @param event\n */\nexport function isWorker() {\n return typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope;\n}\n\n/**\n * we cannot send functions through postMessage\n * extract the path to all functions in the schema\n *\n * @param obj\n */\nexport function extractMethods(obj: any) {\n const paths: string[] = [];\n (function parse(obj: any, path = \"\") {\n Object.keys(obj).forEach((prop) => {\n const propPath = path ? `${path}.${prop}` : prop;\n if (obj[prop] === Object(obj[prop])) {\n parse(obj[prop], propPath);\n }\n if (typeof obj[prop] === \"function\") {\n paths.push(propPath);\n }\n });\n })(obj);\n return paths;\n}\n\nconst urlRegex = /^(https?:|file:)?\\/\\/([^/:]+)?(:(\\d+))?/;\nconst ports: any = { \"http:\": \"80\", \"https:\": \"443\" };\n\n/**\n * convert the url into an origin (remove paths)\n *\n * @param url\n */\nexport function getOriginFromURL(url: string | null) {\n const { location } = document;\n\n const regexResult = urlRegex.exec(url || \"\");\n let protocol;\n let hostname;\n let port;\n\n if (regexResult) {\n // It's an absolute URL. Use the parsed info.\n // regexResult[1] will be undefined if the URL starts with //\n [, protocol = location.protocol, hostname, , port] = regexResult;\n } else {\n // It's a relative path. Use the current location's info.\n protocol = location.protocol;\n hostname = location.hostname;\n port = location.port;\n }\n\n // If the protocol is file, the origin is \"null\"\n // The origin of a document with file protocol is an opaque origin\n // and its serialization \"null\" [1]\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin\n if (protocol === \"file:\") {\n return \"null\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n // or it won't match the message's event.origin.\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n","export enum events {\n MESSAGE = \"message\",\n}\n\nexport enum actions {\n HANDSHAKE_REQUEST = \"RIMLESS/HANDSHAKE_REQUEST\",\n HANDSHAKE_REPLY = \"RIMLESS/HANDSHAKE_REPLY\",\n RPC_REQUEST = \"RIMLESS/RPC_REQUEST\",\n RPC_RESOLVE = \"RIMLESS/RPC_RESOLVE\",\n RPC_REJECT = \"RIMLESS/RPC_REJECT\",\n}\n\nexport interface ISchema {\n [prop: string]: any;\n}\n\nexport interface IConnection {\n remote: ISchema;\n close: () => void;\n}\n\nexport interface IConnections {\n [connectionID: string]: ISchema;\n}\n\nexport interface IEvent extends EventListener {\n source?: Window;\n origin?: string;\n data?: IHandshakeRequestPayload | IHandshakeConfirmationPayload | IRPCRequestPayload | IRPCResolvePayload;\n}\n\nexport interface IHandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID?: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IHandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IRPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID?: string;\n}\n\nexport interface IRPCResolvePayload {\n action: actions.RPC_RESOLVE | actions.RPC_REJECT;\n result?: any | null;\n error?: Error | null;\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface EventHandlers {\n onConnectionSetup: (remote: ISchema) => Promise<void>;\n}\n","export function get(obj: any, path: string | Array<string | number>, defaultValue?: any): any {\n const keys = Array.isArray(path) ? path : path.split(\".\").filter(Boolean);\n let result = obj;\n\n for (const key of keys) {\n result = result?.[key];\n if (result === undefined) {\n return defaultValue;\n }\n }\n\n return result;\n}\n\nexport function set(obj: any, path: string | (string | number)[], value: any): any {\n if (!obj || typeof obj !== \"object\") return obj;\n\n const pathArray = Array.isArray(path) ? path : path.split(\".\").map((key) => (key.match(/^\\d+$/) ? Number(key) : key));\n\n let current = obj;\n\n for (let i = 0; i < pathArray.length; i++) {\n const key = pathArray[i];\n\n if (i === pathArray.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== \"object\") {\n current[key] = typeof pathArray[i + 1] === \"number\" ? [] : {};\n }\n current = current[key];\n }\n }\n\n return obj;\n}\n\nexport function generateId(length: number = 10): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = \"\";\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n","import { isTrustedRemote, isWorker } from \"./helpers\";\nimport { actions, events, IRPCRequestPayload, IRPCResolvePayload, ISchema } from \"./types\";\nimport { generateId, get, set } from \"./utils\";\n\n/**\n * for each function in the schema\n * 1. subscribe to an event that the remote can call\n * 2. listen for calls from the remote. When called execute the function and emit the results.\n *\n * @param methods an array of method ids from the local schema\n * @param _connectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n guest?: Worker\n): any {\n const listeners: any[] = [];\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const { action, callID, connectionID, callName, args = [] } = event.data as IRPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== _connectionID) return;\n\n const payload: IRPCResolvePayload = {\n action: actions.RPC_RESOLVE,\n callID,\n callName,\n connectionID,\n error: null,\n result: null,\n };\n\n // run function and return the results to the remote\n try {\n const result = await get(schema, methodName)(...args);\n payload.result = JSON.parse(JSON.stringify(result));\n } catch (error) {\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n }\n\n // subscribe to the call event\n if (guest) guest.addEventListener(events.MESSAGE, handleCall);\n else self.addEventListener(events.MESSAGE, handleCall);\n\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleCall));\n });\n\n return () => listeners.forEach((unregister) => unregister());\n}\n\n/**\n * Create a function that will make an RPC request to the remote with some arguments.\n * Listen to an event that returns the results from the remote.\n *\n * @param _callName\n * @param _connectionID\n * @param event\n * @param listeners\n * @param guest\n *\n * @returns a promise with the result of the RPC\n */\nexport function createRPC(\n _callName: string,\n _connectionID: string,\n event: any,\n listeners: Array<() => void> = [],\n guest?: Worker\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const callID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const { callID, connectionID, callName, result, error, action } = event.data as IRPCResolvePayload;\n\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== _callName) return;\n if (connectionID !== _connectionID) return;\n\n // resolve the response\n if (action === actions.RPC_RESOLVE) return resolve(result);\n if (action === actions.RPC_REJECT) return reject(error);\n }\n\n // send the RPC request with arguments\n const payload = {\n action: actions.RPC_REQUEST,\n args: JSON.parse(JSON.stringify(args)),\n callID,\n callName: _callName,\n connectionID: _connectionID,\n };\n\n if (guest) guest.addEventListener(events.MESSAGE, handleResponse);\n else self.addEventListener(events.MESSAGE, handleResponse);\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleResponse));\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else (event.source || event.target).postMessage(payload, event.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema and methods. Functions in that object will\n * emit an event that will trigger the RPC on the remote.\n *\n * @param schema\n * @param methods\n * @param _connectionID\n * @param event\n * @param guest\n */\nexport function registerRemoteMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n event: any,\n guest?: Worker\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, _connectionID, event, listeners, guest);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, isWorker } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, IConnection, ISchema } from \"./types\";\n\nconst REQUEST_INTERVAL = 10;\nconst TIMEOUT_INTERVAL = 3000;\n\nlet interval: any = null;\nlet connected = false;\n\nfunction connect(schema: ISchema = {}, eventHandlers?: EventHandlers): Promise<IConnection> {\n return new Promise((resolve, reject) => {\n const localMethods = extractMethods(schema);\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n if (event.data.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, event.data.connectionID);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n event.data.connectionID,\n event\n );\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // close the connection and all listeners when called\n const close = () => {\n self.removeEventListener(events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n connected = true;\n\n // resolve connection object\n const connection = { remote, close };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE REPLY MESSAGES\n self.addEventListener(events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n interval = setInterval(() => {\n if (connected) return clearInterval(interval);\n\n // publish the HANDSHAKE REQUEST\n if (isWorker()) (self as any).postMessage(payload);\n else window.parent.postMessage(payload, \"*\");\n }, REQUEST_INTERVAL);\n\n // timeout the connection after a time\n setTimeout(() => {\n if (!connected) reject(\"connection timeout\");\n }, TIMEOUT_INTERVAL);\n });\n}\n\nexport default {\n connect,\n};\n","import { extractMethods, getOriginFromURL } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, IConnections, ISchema } from \"./types\";\nimport { generateId } from \"./utils\";\n\nconst connections: IConnections = {};\n\nfunction isValidTarget(iframe: HTMLIFrameElement, event: any) {\n const childURL = iframe.getAttribute(\"src\");\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return hasProperOrigin && hasProperSource;\n}\n\n/**\n * Perform a handshake with the target iframe, when the handshake is confirmed\n * resolve the connection object containing RPCs and properties\n *\n * @param iframe\n * @param schema\n * @returns Promise\n */\nfunction connect(guest: HTMLIFrameElement | Worker, schema: ISchema = {}): Promise<IConnection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = (guest as Worker).onerror !== undefined && (guest as Worker).onmessage !== undefined;\n const listeners = guestIsWorker ? guest : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n if (!guestIsWorker && !isValidTarget(guest as HTMLIFrameElement, event)) return;\n if (event.data.action !== actions.HANDSHAKE_REQUEST) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(\n schema,\n localMethods,\n connectionID,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n connectionID,\n event,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n // confirm the connection\n if (guestIsWorker) (guest as Worker).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n listeners.removeEventListener(events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) (guest as Worker).terminate();\n };\n\n // resolve connection object\n const connection: IConnection = { remote, close };\n connections[connectionID] = connection;\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE MESSAGES\n listeners.addEventListener(events.MESSAGE, handleHandshake);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","location","regexResult","protocol","hostname","port","portSuffix","events","actions","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","registerLocalMethods","schema","methods","_connectionID","guest","listeners","methodName","handleCall","event","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","_callName","resolve","reject","handleResponse","registerRemoteMethods","remote","rpc","REQUEST_INTERVAL","TIMEOUT_INTERVAL","interval","connected","connect","eventHandlers","localMethods","handleHandshakeResponse","_a","unregisterLocal","unregisterRemote","close","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","host"],"mappings":"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"}
|
package/lib/rimless.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var rimless=function(
|
|
1
|
+
var rimless=function(g){"use strict";function p(){return typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope}function M(e){const n=[];return function s(r,o=""){Object.keys(r).forEach(t=>{const c=o?`${o}.${t}`:t;r[t]===Object(r[t])&&s(r[t],c),typeof r[t]=="function"&&n.push(c)})}(e),n}const N=/^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/,I={"http:":"80","https:":"443"};function T(e){const{location:n}=document,s=N.exec(e||"");let r,o,t;if(s?[,r=n.protocol,o,,t]=s:(r=n.protocol,o=n.hostname,t=n.port),r==="file:")return"null";const c=t&&t!==I[r]?`:${t}`:"";return`${r}//${o}${c}`}var a=(e=>(e.MESSAGE="message",e))(a||{}),l=(e=>(e.HANDSHAKE_REQUEST="RIMLESS/HANDSHAKE_REQUEST",e.HANDSHAKE_REPLY="RIMLESS/HANDSHAKE_REPLY",e.RPC_REQUEST="RIMLESS/RPC_REQUEST",e.RPC_RESOLVE="RIMLESS/RPC_RESOLVE",e.RPC_REJECT="RIMLESS/RPC_REJECT",e))(l||{});function _(e,n,s){const r=Array.isArray(n)?n:n.split(".").filter(Boolean);let o=e;for(const t of r)if(o=o==null?void 0:o[t],o===void 0)return s;return o}function C(e,n,s){if(!e||typeof e!="object")return e;const r=Array.isArray(n)?n:n.split(".").map(t=>t.match(/^\d+$/)?Number(t):t);let o=e;for(let t=0;t<r.length;t++){const c=r[t];t===r.length-1?o[c]=s:((!o[c]||typeof o[c]!="object")&&(o[c]=typeof r[t+1]=="number"?[]:{}),o=o[c])}return e}function m(e=10){const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let s="";for(let r=0;r<e;r++)s+=n.charAt(Math.floor(Math.random()*n.length));return s}function L(e={},n=[],s,r){const o=[];return n.forEach(t=>{async function c(i){const{action:u,callID:E,connectionID:S,callName:d,args:h=[]}=i.data;if(u!==l.RPC_REQUEST||!E||!d||d!==t||S!==s)return;const f={action:l.RPC_RESOLVE,callID:E,callName:d,connectionID:S,error:null,result:null};try{const R=await _(e,t)(...h);f.result=JSON.parse(JSON.stringify(R))}catch(R){f.error=JSON.parse(JSON.stringify(R,Object.getOwnPropertyNames(R)))}r?r.postMessage(f):p()?self.postMessage(f):i.source.postMessage(f,i.origin)}r?r.addEventListener(a.MESSAGE,c):self.addEventListener(a.MESSAGE,c),o.push(()=>self.removeEventListener(a.MESSAGE,c))}),()=>o.forEach(t=>t())}function D(e,n,s,r=[],o){return(...t)=>new Promise((c,i)=>{const u=m();function E(d){const{callID:h,connectionID:f,callName:R,result:K,error:V,action:O}=d.data;if(!(!h||!R)&&R===e&&f===n){if(O===l.RPC_RESOLVE)return c(K);if(O===l.RPC_REJECT)return i(V)}}const S={action:l.RPC_REQUEST,args:JSON.parse(JSON.stringify(t)),callID:u,callName:e,connectionID:n};o?o.addEventListener(a.MESSAGE,E):self.addEventListener(a.MESSAGE,E),r.push(()=>self.removeEventListener(a.MESSAGE,E)),o?o.postMessage(S):p()?self.postMessage(S):(s.source||s.target).postMessage(S,s.origin)})}function y(e={},n=[],s,r,o){const t={...e},c=[];return n.forEach(i=>{const u=D(i,s,r,c,o);C(t,i,u)}),{remote:t,unregisterRemote:()=>c.forEach(i=>i())}}const w=10,G=3e3;let P=null,A=!1;function J(e={},n){return new Promise((s,r)=>{const o=M(e);async function t(i){var f;if(i.data.action!==l.HANDSHAKE_REPLY)return;const u=L(e,o,i.data.connectionID),{remote:E,unregisterRemote:S}=y(i.data.schema,i.data.methods,i.data.connectionID,i);await((f=n==null?void 0:n.onConnectionSetup)==null?void 0:f.call(n,E));const d=()=>{self.removeEventListener(a.MESSAGE,t),S(),u()};return A=!0,s({remote:E,close:d})}self.addEventListener(a.MESSAGE,t);const c={action:l.HANDSHAKE_REQUEST,methods:o,schema:JSON.parse(JSON.stringify(e))};P=setInterval(()=>{if(A)return clearInterval(P);p()?self.postMessage(c):window.parent.postMessage(c,"*")},w),setTimeout(()=>{A||r("connection timeout")},G)})}const U={connect:J};function H(e,n){const s=e.getAttribute("src"),r=T(s),o=n.origin===r,t=n.source===e.contentWindow;return o&&t}function Q(e,n={}){if(!e)throw new Error("a target is required");const s=e.onerror!==void 0&&e.onmessage!==void 0,r=s?e:window;return new Promise(o=>{const t=m();function c(i){if(!s&&!H(e,i)||i.data.action!==l.HANDSHAKE_REQUEST)return;const u=M(n),E=L(n,u,t,s?e:void 0),{remote:S,unregisterRemote:d}=y(i.data.schema,i.data.methods,t,i,s?e:void 0),h={action:l.HANDSHAKE_REPLY,connectionID:t,methods:u,schema:JSON.parse(JSON.stringify(n))};return s?e.postMessage(h):i.source.postMessage(h,i.origin),o({remote:S,close:()=>{r.removeEventListener(a.MESSAGE,c),d(),E(),s&&e.terminate()}})}r.addEventListener(a.MESSAGE,c)})}const k={connect:Q};return g.actions=l,g.events=a,g.guest=U,g.host=k,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"}),g}({});
|
|
2
2
|
//# sourceMappingURL=rimless.min.js.map
|
package/lib/rimless.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rimless.min.js","sources":["../src/helpers.ts","../src/types.ts","../src/utils.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["export const CONNECTION_TIMEOUT = 1000;\n\n/**\n * check if the remote is trusted\n *\n * @param event\n */\nexport function isTrustedRemote(_event: any) {\n // TODO: implement\n return true;\n}\n\n/**\n * check if run in a webworker\n *\n * @param event\n */\nexport function isWorker() {\n return typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope;\n}\n\n/**\n * we cannot send functions through postMessage\n * extract the path to all functions in the schema\n *\n * @param obj\n */\nexport function extractMethods(obj: any) {\n const paths: string[] = [];\n (function parse(obj: any, path = \"\") {\n Object.keys(obj).forEach((prop) => {\n const propPath = path ? `${path}.${prop}` : prop;\n if (obj[prop] === Object(obj[prop])) {\n parse(obj[prop], propPath);\n }\n if (typeof obj[prop] === \"function\") {\n paths.push(propPath);\n }\n });\n })(obj);\n return paths;\n}\n\nconst urlRegex = /^(https?:|file:)?\\/\\/([^/:]+)?(:(\\d+))?/;\nconst ports: any = { \"http:\": \"80\", \"https:\": \"443\" };\n\n/**\n * convert the url into an origin (remove paths)\n *\n * @param url\n */\nexport function getOriginFromURL(url: string | null) {\n const { location } = document;\n\n const regexResult = urlRegex.exec(url || \"\");\n let protocol;\n let hostname;\n let port;\n\n if (regexResult) {\n // It's an absolute URL. Use the parsed info.\n // regexResult[1] will be undefined if the URL starts with //\n [, protocol = location.protocol, hostname, , port] = regexResult;\n } else {\n // It's a relative path. Use the current location's info.\n protocol = location.protocol;\n hostname = location.hostname;\n port = location.port;\n }\n\n // If the protocol is file, the origin is \"null\"\n // The origin of a document with file protocol is an opaque origin\n // and its serialization \"null\" [1]\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin\n if (protocol === \"file:\") {\n return \"null\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n // or it won't match the message's event.origin.\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n","export enum events {\n MESSAGE = \"message\",\n}\n\nexport enum actions {\n HANDSHAKE_REQUEST = \"RIMLESS/HANDSHAKE_REQUEST\",\n HANDSHAKE_REPLY = \"RIMLESS/HANDSHAKE_REPLY\",\n RPC_REQUEST = \"RIMLESS/RPC_REQUEST\",\n RPC_RESOLVE = \"RIMLESS/RPC_RESOLVE\",\n RPC_REJECT = \"RIMLESS/RPC_REJECT\",\n}\n\nexport interface ISchema {\n [prop: string]: any;\n}\n\nexport interface IConnection {\n remote: ISchema;\n close: () => void;\n}\n\nexport interface IConnections {\n [connectionID: string]: ISchema;\n}\n\nexport interface IEvent extends EventListener {\n source?: Window;\n origin?: string;\n data?: IHandshakeRequestPayload | IHandshakeConfirmationPayload | IRPCRequestPayload | IRPCResolvePayload;\n}\n\nexport interface IHandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID?: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IHandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IRPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID?: string;\n}\n\nexport interface IRPCResolvePayload {\n action: actions.RPC_RESOLVE | actions.RPC_REJECT;\n result?: any | null;\n error?: Error | null;\n callID: string;\n callName: string;\n connectionID: string;\n}\n","export function get(obj: any, path: string | Array<string | number>, defaultValue?: any): any {\n const keys = Array.isArray(path) ? path : path.split(\".\").filter(Boolean);\n let result = obj;\n\n for (const key of keys) {\n result = result?.[key];\n if (result === undefined) {\n return defaultValue;\n }\n }\n\n return result;\n}\n\nexport function set(obj: any, path: string | (string | number)[], value: any): any {\n if (!obj || typeof obj !== \"object\") return obj;\n\n const pathArray = Array.isArray(path) ? path : path.split(\".\").map((key) => (key.match(/^\\d+$/) ? Number(key) : key));\n\n let current = obj;\n\n for (let i = 0; i < pathArray.length; i++) {\n const key = pathArray[i];\n\n if (i === pathArray.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== \"object\") {\n current[key] = typeof pathArray[i + 1] === \"number\" ? [] : {};\n }\n current = current[key];\n }\n }\n\n return obj;\n}\n\nexport function generateId(length: number = 10): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = \"\";\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n","import { isTrustedRemote, isWorker } from \"./helpers\";\nimport { actions, events, IRPCRequestPayload, IRPCResolvePayload, ISchema } from \"./types\";\nimport { generateId, get, set } from \"./utils\";\n\n/**\n * for each function in the schema\n * 1. subscribe to an event that the remote can call\n * 2. listen for calls from the remote. When called execute the function and emit the results.\n *\n * @param methods an array of method ids from the local schema\n * @param _connectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n guest?: Worker\n): any {\n const listeners: any[] = [];\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const { action, callID, connectionID, callName, args = [] } = event.data as IRPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== _connectionID) return;\n\n const payload: IRPCResolvePayload = {\n action: actions.RPC_RESOLVE,\n callID,\n callName,\n connectionID,\n error: null,\n result: null,\n };\n\n // run function and return the results to the remote\n try {\n const result = await get(schema, methodName)(...args);\n payload.result = JSON.parse(JSON.stringify(result));\n } catch (error) {\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n }\n\n // subscribe to the call event\n if (guest) guest.addEventListener(events.MESSAGE, handleCall);\n else self.addEventListener(events.MESSAGE, handleCall);\n\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleCall));\n });\n\n return () => listeners.forEach((unregister) => unregister());\n}\n\n/**\n * Create a function that will make an RPC request to the remote with some arguments.\n * Listen to an event that returns the results from the remote.\n *\n * @param _callName\n * @param _connectionID\n * @param event\n * @param listeners\n * @param guest\n *\n * @returns a promise with the result of the RPC\n */\nexport function createRPC(\n _callName: string,\n _connectionID: string,\n event: any,\n listeners: Array<() => void> = [],\n guest?: Worker\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const callID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const { callID, connectionID, callName, result, error, action } = event.data as IRPCResolvePayload;\n\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== _callName) return;\n if (connectionID !== _connectionID) return;\n\n // resolve the response\n if (action === actions.RPC_RESOLVE) return resolve(result);\n if (action === actions.RPC_REJECT) return reject(error);\n }\n\n // send the RPC request with arguments\n const payload = {\n action: actions.RPC_REQUEST,\n args: JSON.parse(JSON.stringify(args)),\n callID,\n callName: _callName,\n connectionID: _connectionID,\n };\n\n if (guest) guest.addEventListener(events.MESSAGE, handleResponse);\n else self.addEventListener(events.MESSAGE, handleResponse);\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleResponse));\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else (event.source || event.target).postMessage(payload, event.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema and methods. Functions in that object will\n * emit an event that will trigger the RPC on the remote.\n *\n * @param schema\n * @param methods\n * @param _connectionID\n * @param event\n * @param guest\n */\nexport function registerRemoteMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n event: any,\n guest?: Worker\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, _connectionID, event, listeners, guest);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, isWorker } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, ISchema } from \"./types\";\n\nconst REQUEST_INTERVAL = 10;\nconst TIMEOUT_INTERVAL = 3000;\n\nlet interval: any = null;\nlet connected = false;\n\nfunction connect(schema: ISchema = {}): Promise<IConnection> {\n return new Promise((resolve, reject) => {\n const localMethods = extractMethods(schema);\n\n // on handshake response\n function handleHandshakeResponse(event: any) {\n if (event.data.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, event.data.connectionID);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n event.data.connectionID,\n event\n );\n\n // close the connection and all listeners when called\n const close = () => {\n self.removeEventListener(events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n connected = true;\n\n // resolve connection object\n const connection = { remote, close };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE REPLY MESSAGES\n self.addEventListener(events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n interval = setInterval(() => {\n if (connected) return clearInterval(interval);\n\n // publish the HANDSHAKE REQUEST\n if (isWorker()) (self as any).postMessage(payload);\n else window.parent.postMessage(payload, \"*\");\n }, REQUEST_INTERVAL);\n\n // timeout the connection after a time\n setTimeout(() => {\n if (!connected) reject(\"connection timeout\");\n }, TIMEOUT_INTERVAL);\n });\n}\n\nexport default {\n connect,\n};\n","import { extractMethods, getOriginFromURL } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, IConnections, ISchema } from \"./types\";\nimport { generateId } from \"./utils\";\n\nconst connections: IConnections = {};\n\nfunction isValidTarget(iframe: HTMLIFrameElement, event: any) {\n const childURL = iframe.getAttribute(\"src\");\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return hasProperOrigin && hasProperSource;\n}\n\n/**\n * Perform a handshake with the target iframe, when the handshake is confirmed\n * resolve the connection object containing RPCs and properties\n *\n * @param iframe\n * @param schema\n * @returns Promise\n */\nfunction connect(guest: HTMLIFrameElement | Worker, schema: ISchema = {}): Promise<IConnection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = (guest as Worker).onerror !== undefined && (guest as Worker).onmessage !== undefined;\n const listeners = guestIsWorker ? guest : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n if (!guestIsWorker && !isValidTarget(guest as HTMLIFrameElement, event)) return;\n if (event.data.action !== actions.HANDSHAKE_REQUEST) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(\n schema,\n localMethods,\n connectionID,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n connectionID,\n event,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n // confirm the connection\n if (guestIsWorker) (guest as Worker).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n listeners.removeEventListener(events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n };\n\n // resolve connection object\n const connection: IConnection = { remote, close };\n connections[connectionID] = connection;\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE MESSAGES\n listeners.addEventListener(events.MESSAGE, handleHandshake);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","location","regexResult","protocol","hostname","port","portSuffix","events","actions","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","registerLocalMethods","schema","methods","_connectionID","guest","listeners","methodName","handleCall","event","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","_callName","resolve","reject","handleResponse","registerRemoteMethods","remote","rpc","REQUEST_INTERVAL","TIMEOUT_INTERVAL","interval","connected","connect","localMethods","handleHandshakeResponse","unregisterLocal","unregisterRemote","close","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","host"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rimless.min.js","sources":["../src/helpers.ts","../src/types.ts","../src/utils.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["export const CONNECTION_TIMEOUT = 1000;\n\n/**\n * check if the remote is trusted\n *\n * @param event\n */\nexport function isTrustedRemote(_event: any) {\n // TODO: implement\n return true;\n}\n\n/**\n * check if run in a webworker\n *\n * @param event\n */\nexport function isWorker() {\n return typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope;\n}\n\n/**\n * we cannot send functions through postMessage\n * extract the path to all functions in the schema\n *\n * @param obj\n */\nexport function extractMethods(obj: any) {\n const paths: string[] = [];\n (function parse(obj: any, path = \"\") {\n Object.keys(obj).forEach((prop) => {\n const propPath = path ? `${path}.${prop}` : prop;\n if (obj[prop] === Object(obj[prop])) {\n parse(obj[prop], propPath);\n }\n if (typeof obj[prop] === \"function\") {\n paths.push(propPath);\n }\n });\n })(obj);\n return paths;\n}\n\nconst urlRegex = /^(https?:|file:)?\\/\\/([^/:]+)?(:(\\d+))?/;\nconst ports: any = { \"http:\": \"80\", \"https:\": \"443\" };\n\n/**\n * convert the url into an origin (remove paths)\n *\n * @param url\n */\nexport function getOriginFromURL(url: string | null) {\n const { location } = document;\n\n const regexResult = urlRegex.exec(url || \"\");\n let protocol;\n let hostname;\n let port;\n\n if (regexResult) {\n // It's an absolute URL. Use the parsed info.\n // regexResult[1] will be undefined if the URL starts with //\n [, protocol = location.protocol, hostname, , port] = regexResult;\n } else {\n // It's a relative path. Use the current location's info.\n protocol = location.protocol;\n hostname = location.hostname;\n port = location.port;\n }\n\n // If the protocol is file, the origin is \"null\"\n // The origin of a document with file protocol is an opaque origin\n // and its serialization \"null\" [1]\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin\n if (protocol === \"file:\") {\n return \"null\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n // or it won't match the message's event.origin.\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n","export enum events {\n MESSAGE = \"message\",\n}\n\nexport enum actions {\n HANDSHAKE_REQUEST = \"RIMLESS/HANDSHAKE_REQUEST\",\n HANDSHAKE_REPLY = \"RIMLESS/HANDSHAKE_REPLY\",\n RPC_REQUEST = \"RIMLESS/RPC_REQUEST\",\n RPC_RESOLVE = \"RIMLESS/RPC_RESOLVE\",\n RPC_REJECT = \"RIMLESS/RPC_REJECT\",\n}\n\nexport interface ISchema {\n [prop: string]: any;\n}\n\nexport interface IConnection {\n remote: ISchema;\n close: () => void;\n}\n\nexport interface IConnections {\n [connectionID: string]: ISchema;\n}\n\nexport interface IEvent extends EventListener {\n source?: Window;\n origin?: string;\n data?: IHandshakeRequestPayload | IHandshakeConfirmationPayload | IRPCRequestPayload | IRPCResolvePayload;\n}\n\nexport interface IHandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID?: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IHandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: any[];\n schema: ISchema;\n}\n\nexport interface IRPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID?: string;\n}\n\nexport interface IRPCResolvePayload {\n action: actions.RPC_RESOLVE | actions.RPC_REJECT;\n result?: any | null;\n error?: Error | null;\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface EventHandlers {\n onConnectionSetup: (remote: ISchema) => Promise<void>;\n}\n","export function get(obj: any, path: string | Array<string | number>, defaultValue?: any): any {\n const keys = Array.isArray(path) ? path : path.split(\".\").filter(Boolean);\n let result = obj;\n\n for (const key of keys) {\n result = result?.[key];\n if (result === undefined) {\n return defaultValue;\n }\n }\n\n return result;\n}\n\nexport function set(obj: any, path: string | (string | number)[], value: any): any {\n if (!obj || typeof obj !== \"object\") return obj;\n\n const pathArray = Array.isArray(path) ? path : path.split(\".\").map((key) => (key.match(/^\\d+$/) ? Number(key) : key));\n\n let current = obj;\n\n for (let i = 0; i < pathArray.length; i++) {\n const key = pathArray[i];\n\n if (i === pathArray.length - 1) {\n current[key] = value;\n } else {\n if (!current[key] || typeof current[key] !== \"object\") {\n current[key] = typeof pathArray[i + 1] === \"number\" ? [] : {};\n }\n current = current[key];\n }\n }\n\n return obj;\n}\n\nexport function generateId(length: number = 10): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = \"\";\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n","import { isTrustedRemote, isWorker } from \"./helpers\";\nimport { actions, events, IRPCRequestPayload, IRPCResolvePayload, ISchema } from \"./types\";\nimport { generateId, get, set } from \"./utils\";\n\n/**\n * for each function in the schema\n * 1. subscribe to an event that the remote can call\n * 2. listen for calls from the remote. When called execute the function and emit the results.\n *\n * @param methods an array of method ids from the local schema\n * @param _connectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n guest?: Worker\n): any {\n const listeners: any[] = [];\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const { action, callID, connectionID, callName, args = [] } = event.data as IRPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== _connectionID) return;\n\n const payload: IRPCResolvePayload = {\n action: actions.RPC_RESOLVE,\n callID,\n callName,\n connectionID,\n error: null,\n result: null,\n };\n\n // run function and return the results to the remote\n try {\n const result = await get(schema, methodName)(...args);\n payload.result = JSON.parse(JSON.stringify(result));\n } catch (error) {\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n }\n\n // subscribe to the call event\n if (guest) guest.addEventListener(events.MESSAGE, handleCall);\n else self.addEventListener(events.MESSAGE, handleCall);\n\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleCall));\n });\n\n return () => listeners.forEach((unregister) => unregister());\n}\n\n/**\n * Create a function that will make an RPC request to the remote with some arguments.\n * Listen to an event that returns the results from the remote.\n *\n * @param _callName\n * @param _connectionID\n * @param event\n * @param listeners\n * @param guest\n *\n * @returns a promise with the result of the RPC\n */\nexport function createRPC(\n _callName: string,\n _connectionID: string,\n event: any,\n listeners: Array<() => void> = [],\n guest?: Worker\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const callID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const { callID, connectionID, callName, result, error, action } = event.data as IRPCResolvePayload;\n\n if (!isTrustedRemote(event)) return;\n if (!callID || !callName) return;\n if (callName !== _callName) return;\n if (connectionID !== _connectionID) return;\n\n // resolve the response\n if (action === actions.RPC_RESOLVE) return resolve(result);\n if (action === actions.RPC_REJECT) return reject(error);\n }\n\n // send the RPC request with arguments\n const payload = {\n action: actions.RPC_REQUEST,\n args: JSON.parse(JSON.stringify(args)),\n callID,\n callName: _callName,\n connectionID: _connectionID,\n };\n\n if (guest) guest.addEventListener(events.MESSAGE, handleResponse);\n else self.addEventListener(events.MESSAGE, handleResponse);\n listeners.push(() => self.removeEventListener(events.MESSAGE, handleResponse));\n\n if (guest) guest.postMessage(payload);\n else if (isWorker()) (self as any).postMessage(payload);\n else (event.source || event.target).postMessage(payload, event.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema and methods. Functions in that object will\n * emit an event that will trigger the RPC on the remote.\n *\n * @param schema\n * @param methods\n * @param _connectionID\n * @param event\n * @param guest\n */\nexport function registerRemoteMethods(\n schema: ISchema = {},\n methods: any[] = [],\n _connectionID: string,\n event: any,\n guest?: Worker\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, _connectionID, event, listeners, guest);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, isWorker } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, IConnection, ISchema } from \"./types\";\n\nconst REQUEST_INTERVAL = 10;\nconst TIMEOUT_INTERVAL = 3000;\n\nlet interval: any = null;\nlet connected = false;\n\nfunction connect(schema: ISchema = {}, eventHandlers?: EventHandlers): Promise<IConnection> {\n return new Promise((resolve, reject) => {\n const localMethods = extractMethods(schema);\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n if (event.data.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, event.data.connectionID);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n event.data.connectionID,\n event\n );\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // close the connection and all listeners when called\n const close = () => {\n self.removeEventListener(events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n connected = true;\n\n // resolve connection object\n const connection = { remote, close };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE REPLY MESSAGES\n self.addEventListener(events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n interval = setInterval(() => {\n if (connected) return clearInterval(interval);\n\n // publish the HANDSHAKE REQUEST\n if (isWorker()) (self as any).postMessage(payload);\n else window.parent.postMessage(payload, \"*\");\n }, REQUEST_INTERVAL);\n\n // timeout the connection after a time\n setTimeout(() => {\n if (!connected) reject(\"connection timeout\");\n }, TIMEOUT_INTERVAL);\n });\n}\n\nexport default {\n connect,\n};\n","import { extractMethods, getOriginFromURL } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, IConnection, IConnections, ISchema } from \"./types\";\nimport { generateId } from \"./utils\";\n\nconst connections: IConnections = {};\n\nfunction isValidTarget(iframe: HTMLIFrameElement, event: any) {\n const childURL = iframe.getAttribute(\"src\");\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return hasProperOrigin && hasProperSource;\n}\n\n/**\n * Perform a handshake with the target iframe, when the handshake is confirmed\n * resolve the connection object containing RPCs and properties\n *\n * @param iframe\n * @param schema\n * @returns Promise\n */\nfunction connect(guest: HTMLIFrameElement | Worker, schema: ISchema = {}): Promise<IConnection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = (guest as Worker).onerror !== undefined && (guest as Worker).onmessage !== undefined;\n const listeners = guestIsWorker ? guest : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n if (!guestIsWorker && !isValidTarget(guest as HTMLIFrameElement, event)) return;\n if (event.data.action !== actions.HANDSHAKE_REQUEST) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(\n schema,\n localMethods,\n connectionID,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n event.data.schema,\n event.data.methods,\n connectionID,\n event,\n guestIsWorker ? (guest as Worker) : undefined\n );\n\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n methods: localMethods,\n schema: JSON.parse(JSON.stringify(schema)),\n };\n\n // confirm the connection\n if (guestIsWorker) (guest as Worker).postMessage(payload);\n else event.source.postMessage(payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n listeners.removeEventListener(events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) (guest as Worker).terminate();\n };\n\n // resolve connection object\n const connection: IConnection = { remote, close };\n connections[connectionID] = connection;\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE MESSAGES\n listeners.addEventListener(events.MESSAGE, handleHandshake);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","location","regexResult","protocol","hostname","port","portSuffix","events","actions","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","registerLocalMethods","schema","methods","_connectionID","guest","listeners","methodName","handleCall","event","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","_callName","resolve","reject","handleResponse","registerRemoteMethods","remote","rpc","REQUEST_INTERVAL","TIMEOUT_INTERVAL","interval","connected","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterLocal","unregisterRemote","_a","close","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","host"],"mappings":"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"}
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rimless",
|
|
3
3
|
"author": "Aurélien Franky",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/au-re/rimless",
|
|
7
7
|
"description": "event base communication made easy with a promise-based API wrapping `postMessage`",
|