rimless 0.5.1 → 0.6.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/README.md CHANGED
@@ -42,50 +42,69 @@ or from a CDN
42
42
 
43
43
  ## Example Usage
44
44
 
45
- **in the host website**
45
+ Below is a minimal but complete round‑trip that shows how each side can:
46
+
47
+ 1. expose variables and functions,
48
+ 2. read the other side’s variables,
49
+ 3. invoke the other side’s functions,
50
+ 4. finally close the link.
51
+
52
+ **Host (page that embeds the iframe)**
46
53
 
47
54
  ```js
48
55
  import { host } from "rimless";
49
56
 
50
- const connection = await host.connect(iframe, {
57
+ const iframe = document.getElementById("myIframe");
58
+
59
+ // Everything inside this object is exported to the guest
60
+ const hostApi = {
51
61
  someHostVariable: 12,
52
62
  someHostFunction: (value) => `hello ${value}`,
53
- });
63
+ };
54
64
 
55
- // access variables on the iframe
56
- console.log(connection.remote.someGuestVariable); // 42
65
+ const connection = await host.connect(iframe, hostApi);
57
66
 
58
- // call remote procedures on the iframe
59
- const result = await connection.remote.someGuestFunction("here");
67
+ // ↘︎ Access data that the iframe exposed
68
+ console.log(connection.remote.someGuestVariable); // → 42
60
69
 
61
- console.log(result); // hello here
70
+ // ↘︎ Call a guest‑side RPC and await its result
71
+ const result = await connection.remote.someGuestFunction("here");
72
+ console.log(result); // → "hello here"
62
73
 
63
- // close the connection
74
+ // Done talking? Tear down the channel.
64
75
  connection.close();
65
76
  ```
66
77
 
67
- **in the iframe**
78
+ **Guest (code that runs inside the iframe)**
68
79
 
69
80
  ```js
70
81
  import { guest } from "rimless";
71
82
 
72
- const connection = await guest.connect({
83
+ // The object you pass to guest.connect is your public surface
84
+ const guestApi = {
73
85
  someGuestVariable: 42,
74
86
  someGuestFunction: (value) => `hello ${value}`,
75
- });
87
+ };
76
88
 
77
- // access variables on the host
78
- console.log(connection.remote.someHostVariable); // 12
89
+ const connection = await guest.connect(guestApi);
79
90
 
80
- // call remote procedures on host
81
- const res = await connection.remote.someHostFunction("there");
91
+ // ↗︎ Read a host‑side value
92
+ console.log(connection.remote.someHostVariable); // → 12
82
93
 
83
- console.log(res); // hello there
94
+ // ↗︎ Invoke a host‑side RPC
95
+ const res = await connection.remote.someHostFunction("there");
96
+ console.log(res); // → "hello there"
84
97
 
85
- // close the connection
98
+ // Close when finished to free resources
86
99
  connection.close();
87
100
  ```
88
101
 
102
+ **What to remember**
103
+
104
+ - `connection.remote` is the automatically generated proxy for the other side’s exports.
105
+ - Every remote call returns a Promise, so feel free to await it.
106
+ - Always call `connection.close()` when you no longer need the tunnel—this removes event listeners and avoids memory leaks.
107
+
89
108
  ---
90
109
 
91
110
  ## Getting Started
@@ -161,6 +180,59 @@ guest.connect().then((connection) => {
161
180
  });
162
181
  ```
163
182
 
183
+ ### Calling the remote from an RPC
184
+
185
+ Every RPC handler you expose receives the caller’s method collection as its last parameter—conventionally named `remote`.
186
+ That means an RPC can immediately call back into the opposite context to acknowledge success, return extra data, or kick‑off a follow‑up action.
187
+
188
+ **Why it’s useful**
189
+
190
+ - Confirm completion – send a quick “done!” message when a long‑running task finishes.
191
+ - Chain operations – perform a host‑side update, then ask the guest to re‑render.
192
+ - Stream results – push incremental data to the caller instead of waiting for one big response.
193
+
194
+ ```js
195
+ // host (parent window)
196
+ import { host } from "rimless";
197
+
198
+ const api = {
199
+ /**
200
+ * Change the page background, then notify the guest.
201
+ * @param {string} color Hex or CSS color string
202
+ * @param {object} remote Automatically injected guest‑side RPCs
203
+ */
204
+ setColor: (color, remote) => {
205
+ document.body.style.background = color;
206
+ remote.logMessage("Background updated ✔︎");
207
+ },
208
+ };
209
+
210
+ const iframe = document.getElementById("myIframe");
211
+ host.connect(iframe, api);
212
+ ```
213
+
214
+ ```js
215
+ // guest (inside the iframe)
216
+ import { guest } from "rimless";
217
+
218
+ const api = {
219
+ /** Show messages from the host */
220
+ logMessage: (msg) => console.log(msg),
221
+ };
222
+
223
+ const { remote } = await guest.connect(api);
224
+
225
+ // Ask the host to change its background.
226
+ // Afterwards, the guest will receive logMessage("Background updated ✔︎").
227
+ remote.setColor("#011627");
228
+ ```
229
+
230
+ **Key points**
231
+
232
+ - Handler signature – (…args, remote); you can ignore remote if you don’t need it.
233
+ - Promises everywhere – RPC calls return promises, so you can await remote.someMethod().
234
+ - Keep it short – avoid deep call‑chains that bounce endlessly between host and guest.
235
+
164
236
  ### Closing a connection
165
237
 
166
238
  Closing a connection will remove all event listeners that were registered.
package/lib/helpers.d.ts CHANGED
@@ -23,7 +23,7 @@ export declare function isIframe(): boolean;
23
23
  *
24
24
  * @param obj
25
25
  */
26
- export declare function extractMethods(obj: any): string[];
26
+ export declare function extractMethods(obj: any): Record<string, (...args: any) => any>;
27
27
  /**
28
28
  * convert the url into an origin (remove paths)
29
29
  *
package/lib/rimless.js CHANGED
@@ -1,258 +1,252 @@
1
1
  function I() {
2
2
  return typeof window > "u" && typeof self < "u";
3
3
  }
4
- function y() {
4
+ function g() {
5
5
  var n;
6
6
  return typeof process < "u" && !!((n = process.versions) != null && n.node);
7
7
  }
8
- function H() {
8
+ function J() {
9
9
  return window.self !== window.top;
10
10
  }
11
11
  function _(n) {
12
- const r = [];
13
- return function t(e, i = "") {
14
- Object.keys(e).forEach((o) => {
15
- const c = i ? `${i}.${o}` : o;
16
- e[o] === Object(e[o]) && t(e[o], c), typeof e[o] == "function" && r.push(c);
12
+ const o = {};
13
+ return function t(e, s = "") {
14
+ Object.keys(e).forEach((r) => {
15
+ const c = s ? `${s}.${r}` : r;
16
+ e[r] === Object(e[r]) && t(e[r], c), typeof e[r] == "function" && (o[c] = e[r], delete e[r]);
17
17
  });
18
- }(n), r;
18
+ }(n), o;
19
19
  }
20
20
  const G = /^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/, K = { "http:": "80", "https:": "443" };
21
21
  function U(n) {
22
22
  if (!n) return null;
23
- const r = G.exec(n);
24
- if (!r) return null;
25
- const [, t = "http:", e, , i] = r;
23
+ const o = G.exec(n);
24
+ if (!o) return null;
25
+ const [, t = "http:", e, , s] = o;
26
26
  if (t === "file:")
27
27
  return "file://";
28
- const o = i && i !== K[t] ? `:${i}` : "";
29
- return `${t}//${e}${o}`;
30
- }
31
- function W(n, r, t) {
32
- const e = Array.isArray(r) ? r : r.split(".").filter(Boolean);
33
- let i = n;
34
- for (const o of e)
35
- if (i = i == null ? void 0 : i[o], i === void 0)
36
- return t;
37
- return i;
28
+ const r = s && s !== K[t] ? `:${s}` : "";
29
+ return `${t}//${e}${r}`;
38
30
  }
39
- function Q(n, r, t) {
31
+ function W(n, o, t) {
40
32
  if (!n || typeof n != "object") return n;
41
- const e = Array.isArray(r) ? r : r.split(".").map((o) => o.match(/^\d+$/) ? Number(o) : o);
42
- let i = n;
43
- for (let o = 0; o < e.length; o++) {
44
- const c = e[o];
45
- o === e.length - 1 ? i[c] = t : ((!i[c] || typeof i[c] != "object") && (i[c] = typeof e[o + 1] == "number" ? [] : {}), i = i[c]);
33
+ const e = Array.isArray(o) ? o : o.split(".").map((r) => r.match(/^\d+$/) ? Number(r) : r);
34
+ let s = n;
35
+ for (let r = 0; r < e.length; r++) {
36
+ const c = e[r];
37
+ r === e.length - 1 ? s[c] = t : ((!s[c] || typeof s[c] != "object") && (s[c] = typeof e[r + 1] == "number" ? [] : {}), s = s[c]);
46
38
  }
47
39
  return n;
48
40
  }
49
41
  function T(n = 10) {
50
- const r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
42
+ const o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
51
43
  let t = "";
52
44
  for (let e = 0; e < n; e++)
53
- t += r.charAt(Math.floor(Math.random() * r.length));
45
+ t += o.charAt(Math.floor(Math.random() * o.length));
54
46
  return t;
55
47
  }
56
- let w = null;
57
- if (y())
48
+ let y = null;
49
+ if (g())
58
50
  try {
59
- w = require("worker_threads").parentPort;
51
+ y = require("worker_threads").parentPort;
60
52
  } catch {
61
53
  }
62
- function $() {
63
- if (y())
64
- return w;
54
+ function Q() {
55
+ if (g())
56
+ return y;
65
57
  if (I())
66
58
  return self;
67
- if (H())
59
+ if (J())
68
60
  return window.parent;
69
61
  throw new Error("No valid target found for postMessage");
70
62
  }
71
- function N(n, r, t) {
63
+ function w(n, o, t) {
72
64
  if (!n)
73
65
  throw new Error("Rimless Error: No target specified for postMessage");
74
- if (y() && n === w) {
75
- n.postMessage(JSON.parse(JSON.stringify(r)));
66
+ if (g() && n === y) {
67
+ n.postMessage(JSON.parse(JSON.stringify(o)));
76
68
  return;
77
69
  }
78
70
  if (I()) {
79
- n.postMessage(JSON.parse(JSON.stringify(r)));
71
+ n.postMessage(JSON.parse(JSON.stringify(o)));
80
72
  return;
81
73
  }
82
74
  if (n.postMessage) {
83
- n.postMessage(JSON.parse(JSON.stringify(r)), { targetOrigin: t || "*" });
75
+ n.postMessage(JSON.parse(JSON.stringify(o)), { targetOrigin: t || "*" });
84
76
  return;
85
77
  }
86
78
  throw new Error("Rimless Error: Invalid target for postMessage");
87
79
  }
88
80
  function L(n) {
89
- return w !== null && n === w;
81
+ return y !== null && n === y;
90
82
  }
91
- function x(n) {
83
+ function $(n) {
92
84
  return L(n) || typeof Worker < "u" && n instanceof Worker;
93
85
  }
94
- function m(n, r, t) {
95
- L(n) ? n.on(r, t) : "addEventListener" in n && n.addEventListener(r, t);
86
+ function M(n, o, t) {
87
+ L(n) ? n.on(o, t) : "addEventListener" in n && n.addEventListener(o, t);
96
88
  }
97
- function D(n, r, t) {
98
- L(n) ? n.off(r, t) : "removeEventListener" in n && n.removeEventListener(r, t);
89
+ function O(n, o, t) {
90
+ L(n) ? n.off(o, t) : "removeEventListener" in n && n.removeEventListener(o, t);
99
91
  }
100
- function M(n) {
92
+ function P(n) {
101
93
  return n.data || n;
102
94
  }
103
- var R = /* @__PURE__ */ ((n) => (n.MESSAGE = "message", n))(R || {}), a = /* @__PURE__ */ ((n) => (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))(a || {});
104
- function J(n = {}, r = [], t, e, i) {
105
- const o = [];
106
- return r.forEach((c) => {
107
- async function l(f) {
108
- const s = M(f), { action: E, callID: d, connectionID: p, callName: h, args: g = [] } = s;
109
- if (E !== a.RPC_REQUEST || !d || !h || h !== c || p !== t) return;
110
- const S = {
95
+ var d = /* @__PURE__ */ ((n) => (n.MESSAGE = "message", n))(d || {}), a = /* @__PURE__ */ ((n) => (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))(a || {});
96
+ function k(n = {}, o, t, e, s) {
97
+ const r = [];
98
+ for (const [c, l] of Object.entries(n)) {
99
+ async function f(i) {
100
+ const u = P(i), { action: m, callID: R, connectionID: p, callName: h, args: N = [] } = u;
101
+ if (m !== a.RPC_REQUEST || !R || !h || h !== c || p !== o) return;
102
+ const E = {
111
103
  action: a.RPC_RESOLVE,
112
- callID: d,
104
+ callID: R,
113
105
  callName: h,
114
106
  connectionID: p,
115
107
  error: null,
116
108
  result: null
117
109
  };
118
110
  try {
119
- const u = await W(n, c)(...g);
120
- u ? S.result = JSON.parse(JSON.stringify(u)) : S.result = u;
121
- } catch (u) {
122
- S.action = a.RPC_REJECT, S.error = JSON.parse(JSON.stringify(u, Object.getOwnPropertyNames(u)));
111
+ const S = await l(...N, s);
112
+ S ? E.result = JSON.parse(JSON.stringify(S)) : E.result = S;
113
+ } catch (S) {
114
+ E.action = a.RPC_REJECT, E.error = JSON.parse(JSON.stringify(S, Object.getOwnPropertyNames(S)));
123
115
  }
124
- N(i, S, f == null ? void 0 : f.origin);
116
+ w(e, E, i == null ? void 0 : i.origin);
125
117
  }
126
- m(e, R.MESSAGE, l), o.push(() => D(e, R.MESSAGE, l));
127
- }), () => o.forEach((c) => c());
118
+ M(t, d.MESSAGE, f), r.push(() => O(t, d.MESSAGE, f));
119
+ }
120
+ return () => r.forEach((c) => c());
128
121
  }
129
- function Y(n, r, t, e = [], i, o) {
122
+ function x(n, o, t, e = [], s, r) {
130
123
  return (...c) => new Promise((l, f) => {
131
- const s = T();
132
- function E(p) {
133
- const h = M(p), { callID: g, connectionID: S, callName: u, result: P, error: C, action: O } = h;
134
- if (!(!g || !u) && u === n && g === s && S === r) {
135
- if (O === a.RPC_RESOLVE) return l(P);
136
- if (O === a.RPC_REJECT) return f(C);
124
+ const i = T();
125
+ function u(R) {
126
+ const p = P(R), { callID: h, connectionID: N, callName: E, result: S, error: H, action: D } = p;
127
+ if (!(!h || !E) && E === n && h === i && N === o) {
128
+ if (D === a.RPC_RESOLVE) return l(S);
129
+ if (D === a.RPC_REJECT) return f(H);
137
130
  }
138
131
  }
139
- const d = {
132
+ const m = {
140
133
  action: a.RPC_REQUEST,
141
134
  args: JSON.parse(JSON.stringify(c)),
142
- callID: s,
135
+ callID: i,
143
136
  callName: n,
144
- connectionID: r
137
+ connectionID: o
145
138
  };
146
- m(i, R.MESSAGE, E), e.push(() => D(i, R.MESSAGE, E)), N(o, d, t == null ? void 0 : t.origin);
139
+ M(s, d.MESSAGE, u), e.push(() => O(s, d.MESSAGE, u)), w(r, m, t == null ? void 0 : t.origin);
147
140
  });
148
141
  }
149
- function k(n = {}, r = [], t, e, i, o) {
142
+ function C(n = {}, o = [], t, e, s, r) {
150
143
  const c = { ...n }, l = [];
151
- return r.forEach((f) => {
152
- const s = Y(f, t, e, l, i, o);
153
- Q(c, f, s);
154
- }), {
144
+ for (const f of o) {
145
+ const i = x(f, t, e, l, s, r);
146
+ W(c, f, i);
147
+ }
148
+ return {
155
149
  remote: c,
156
150
  unregisterRemote: () => l.forEach((f) => f())
157
151
  };
158
152
  }
159
- function V(n = {}, r) {
153
+ function Y(n = {}, o) {
160
154
  return new Promise(async (t) => {
161
- const e = _(n), i = $(), o = self || window;
155
+ const e = _(n), s = Q(), r = self || window;
162
156
  async function c(f) {
163
- var u;
164
- const s = M(f);
165
- if ((s == null ? void 0 : s.action) !== a.HANDSHAKE_REPLY) return;
166
- const E = J(n, e, s.connectionID, o, i), { remote: d, unregisterRemote: p } = k(
167
- s.schema,
168
- s.methods,
169
- s.connectionID,
157
+ var E;
158
+ const i = P(f);
159
+ if ((i == null ? void 0 : i.action) !== a.HANDSHAKE_REPLY) return;
160
+ const { remote: u, unregisterRemote: m } = C(
161
+ i.schema,
162
+ i.methodNames,
163
+ i.connectionID,
170
164
  f,
171
- o,
172
- i
173
- );
174
- await ((u = r == null ? void 0 : r.onConnectionSetup) == null ? void 0 : u.call(r, d));
175
- const h = {
165
+ r,
166
+ s
167
+ ), R = k(e, i.connectionID, r, s, u);
168
+ await ((E = o == null ? void 0 : o.onConnectionSetup) == null ? void 0 : E.call(o, u));
169
+ const p = {
176
170
  action: a.HANDSHAKE_REPLY,
177
- connectionID: s.connectionID
171
+ connectionID: i.connectionID
178
172
  };
179
- N(i, h, f == null ? void 0 : f.origin);
180
- const S = { remote: d, close: () => {
181
- self.removeEventListener(R.MESSAGE, c), p(), E();
182
- }, id: s.connectionID };
183
- return t(S);
173
+ w(s, p, f == null ? void 0 : f.origin);
174
+ const N = { remote: u, close: () => {
175
+ O(r, d.MESSAGE, c), m(), R();
176
+ }, id: i.connectionID };
177
+ return t(N);
184
178
  }
185
- self.addEventListener(R.MESSAGE, c);
179
+ M(r, d.MESSAGE, c);
186
180
  const l = {
187
181
  action: a.HANDSHAKE_REQUEST,
188
- methods: e,
189
- schema: JSON.parse(JSON.stringify(n))
182
+ methodNames: Object.keys(e),
183
+ schema: n
190
184
  };
191
- N(i, l);
185
+ w(s, l);
192
186
  });
193
187
  }
194
188
  const F = {
195
- connect: V
189
+ connect: Y
196
190
  }, A = {};
197
- function q(n, r) {
191
+ function V(n, o) {
198
192
  if (L(n) || typeof Worker < "u" && n instanceof Worker)
199
193
  return !0;
200
194
  const t = n;
201
195
  try {
202
- const e = t.src, i = U(e), o = r.origin === i, c = r.source === t.contentWindow;
203
- return o && c || !e;
196
+ const e = t.src, s = U(e), r = o.origin === s, c = o.source === t.contentWindow;
197
+ return r && c || !e;
204
198
  } catch (e) {
205
199
  return console.warn("Error checking iframe target:", e), !1;
206
200
  }
207
201
  }
208
- function B(n, r = {}) {
202
+ function q(n, o = {}) {
209
203
  if (!n) throw new Error("a target is required");
210
- const t = x(n), e = t || y() ? n : window;
211
- return new Promise((i) => {
212
- const o = T();
204
+ const t = $(n), e = t || g() ? n : window;
205
+ return new Promise((s) => {
206
+ const r = T();
213
207
  function c(f) {
214
- const s = t || y() ? n : f.source;
215
- if (!t && !y() && !q(n, f)) return;
216
- const E = M(f);
217
- if ((E == null ? void 0 : E.action) !== a.HANDSHAKE_REQUEST || A[o]) return;
218
- const d = _(r), p = J(r, d, o, e, s), { remote: h, unregisterRemote: g } = k(
219
- E.schema,
220
- E.methods,
221
- o,
208
+ const i = t || g() ? n : f.source;
209
+ if (!t && !g() && !V(n, f)) return;
210
+ const u = P(f);
211
+ if ((u == null ? void 0 : u.action) !== a.HANDSHAKE_REQUEST || A[r]) return;
212
+ const m = _(o), { remote: R, unregisterRemote: p } = C(
213
+ u.schema,
214
+ u.methodNames,
215
+ r,
222
216
  f,
223
217
  e,
224
- s
225
- ), S = {
218
+ i
219
+ ), h = k(m, r, e, i, R), N = {
226
220
  action: a.HANDSHAKE_REPLY,
227
- connectionID: o,
228
- schema: r,
229
- methods: d
221
+ connectionID: r,
222
+ schema: o,
223
+ methodNames: Object.keys(m)
230
224
  };
231
- N(s, S, f.origin);
232
- const P = { remote: h, close: () => {
233
- delete A[o], D(e, R.MESSAGE, c), g(), p(), t && n.terminate();
234
- }, id: o };
235
- A[o] = P;
225
+ w(i, N, f.origin);
226
+ const S = { remote: R, close: () => {
227
+ delete A[r], O(e, d.MESSAGE, c), p(), h(), t && n.terminate();
228
+ }, id: r };
229
+ A[r] = S;
236
230
  }
237
- m(e, R.MESSAGE, c);
231
+ M(e, d.MESSAGE, c);
238
232
  function l(f) {
239
- const s = M(f);
240
- if ((s == null ? void 0 : s.action) === a.HANDSHAKE_REPLY && o === s.connectionID) {
241
- if (!A[s.connectionID])
233
+ const i = P(f);
234
+ if ((i == null ? void 0 : i.action) === a.HANDSHAKE_REPLY && r === i.connectionID) {
235
+ if (!A[i.connectionID])
242
236
  throw new Error("Rimless Error: No connection found for this connectionID");
243
- return i(A[s.connectionID]);
237
+ return s(A[i.connectionID]);
244
238
  }
245
239
  }
246
- m(e, R.MESSAGE, l);
240
+ M(e, d.MESSAGE, l);
247
241
  });
248
242
  }
249
- const z = {
250
- connect: B
243
+ const b = {
244
+ connect: q
251
245
  };
252
246
  export {
253
247
  a as actions,
254
- R as events,
248
+ d as events,
255
249
  F as guest,
256
- z as host
250
+ b as host
257
251
  };
258
252
  //# sourceMappingURL=rimless.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rimless.js","sources":["../src/helpers.ts","../src/types.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["import { Guest, NodeWorker, Target, WorkerLike } from \"./types\";\n\n/**\n * check if run in a webworker\n *\n * @returns boolean\n */\nexport function isWorker(): boolean {\n return typeof window === \"undefined\" && typeof self !== \"undefined\";\n}\n\n/**\n * check if run in a Node.js environment\n *\n * @returns boolean\n */\nexport function isNodeEnv(): boolean {\n return typeof process !== \"undefined\" && !!(process as any).versions?.node;\n}\n\n/**\n * check if run in an iframe\n *\n * @returns boolean\n */\nexport function isIframe() {\n return window.self !== window.top;\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 if (!url) return null;\n\n const regexResult = urlRegex.exec(url);\n if (!regexResult) return null;\n\n const [, protocol = \"http:\", hostname, , port] = regexResult;\n\n // If the protocol is file, return file://\n if (protocol === \"file:\") {\n return \"file://\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n\nexport 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\nlet parentPort: any = null;\n\nif (isNodeEnv()) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const workerThreads = require(\"worker_threads\");\n parentPort = workerThreads.parentPort;\n } catch (e) {\n // Not in worker thread context\n }\n}\n\n/**\n * Get the appropriate target host for messaging based on the current environment\n * @returns The messaging target for the current environment\n */\nexport function getTargetHost(): any {\n if (isNodeEnv()) {\n return parentPort;\n }\n\n if (isWorker()) {\n return self;\n }\n\n if (isIframe()) {\n return window.parent;\n }\n\n throw new Error(\"No valid target found for postMessage\");\n}\n\n/**\n * Send a message to a target, handling different environments (iframe, web worker, node worker)\n * @param target The target to send the message to\n * @param message The message to send\n * @param origin Optional origin for iframe communication\n */\nexport function postMessageToTarget(target: Target, message: any, origin?: string): void {\n if (!target) {\n throw new Error(\"Rimless Error: No target specified for postMessage\");\n }\n\n // Node.js Worker\n if (isNodeEnv() && target === parentPort) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || \"*\" });\n return;\n }\n\n throw new Error(\"Rimless Error: Invalid target for postMessage\");\n}\n\nexport function isNodeWorker(guest: Guest | Target): guest is NodeWorker {\n return parentPort !== null && guest === parentPort;\n}\n\nexport function isWorkerLike(guest: Guest): guest is WorkerLike {\n return isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker);\n}\n\nexport function addEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.on(event, handler);\n } else if (\"addEventListener\" in target) {\n target.addEventListener(event, handler);\n }\n}\n\nexport function removeEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.off(event, handler);\n } else if (\"removeEventListener\" in target) {\n target.removeEventListener(event, handler);\n }\n}\n\n/**\n * Normalize message event data across Web and Node.js environments\n * In web, data is in event.data\n * In Node.js, the event itself contains the data\n */\nexport function getEventData(event: any) {\n return event.data || event;\n}\n","export interface NodeWorker {\n on(event: string, handler: any): void;\n off(event: string, handler: any): void;\n postMessage(message: any): void;\n terminate(): void;\n}\n\nexport type WorkerLike = Worker | NodeWorker;\n\nexport 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 type Schema = Record<string, any>;\n\nexport interface Connection {\n id: string;\n remote: Schema;\n close: () => void;\n}\n\nexport type Connections = Record<string, Connection>;\n\nexport interface RimlessEvent extends EventListener {\n source?: Window;\n origin?: string;\n data: HandshakeRequestPayload | HandshakeConfirmationPayload | RPCRequestPayload | RPCResolvePayload;\n}\n\nexport interface HandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID: string;\n methods: string[];\n schema: Schema;\n}\n\nexport interface HandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: string[];\n schema: Schema;\n}\n\nexport interface RPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface RPCResolvePayload {\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: Schema) => Promise<void>;\n}\n\nexport type Guest = WorkerLike | HTMLIFrameElement;\nexport type Target = Window | WorkerLike;\nexport type Environment = Window | WorkerLike;\n","import {\n addEventListener,\n generateId,\n get,\n getEventData,\n postMessageToTarget,\n removeEventListener,\n set,\n} from \"./helpers\";\nimport {\n actions,\n Environment,\n events,\n RimlessEvent,\n RPCRequestPayload,\n RPCResolvePayload,\n Schema,\n Target,\n} from \"./types\";\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 rpcConnectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: Schema = {},\n methods: string[] = [],\n rpcConnectionID: string,\n listenTo: Environment,\n sendTo: Target,\n) {\n const listeners: any[] = [];\n\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const eventData = getEventData(event);\n const { action, callID, connectionID, callName, args = [] } = eventData as RPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== rpcConnectionID) return;\n\n const payload: RPCResolvePayload = {\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\n if (!result) {\n // if the result is falsy (null, undefined, \"\", etc), set it directly\n payload.result = result;\n } else {\n // otherwise parse a stringified version of it\n payload.result = JSON.parse(JSON.stringify(result));\n }\n } catch (error) {\n payload.action = actions.RPC_REJECT;\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n postMessageToTarget(sendTo, payload, event?.origin);\n }\n\n // subscribe to the call event\n addEventListener(listenTo, events.MESSAGE, handleCall);\n listeners.push(() => removeEventListener(listenTo, 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 rpcCallName\n * @param rpcConnectionID\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 rpcCallName: string,\n rpcConnectionID: string,\n event: RimlessEvent,\n listeners: Array<() => void> = [],\n listenTo: Environment,\n sendTo: Target,\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const requestID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const eventData = getEventData(event);\n const { callID, connectionID, callName, result, error, action } = eventData as RPCResolvePayload;\n\n if (!callID || !callName) return;\n if (callName !== rpcCallName) return;\n if (callID !== requestID) return;\n if (connectionID !== rpcConnectionID) 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: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, 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: Schema = {},\n methods: string[] = [],\n connectionID: string,\n event: RimlessEvent,\n listenTo: Environment,\n sendTo: Target,\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, connectionID, event, listeners, listenTo, sendTo);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, getEventData, getTargetHost, postMessageToTarget } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, Connection, Schema } from \"./types\";\n\nfunction connect(schema: Schema = {}, eventHandlers?: EventHandlers): Promise<Connection> {\n return new Promise(async (resolve) => {\n const localMethods = extractMethods(schema);\n const sendTo = getTargetHost();\n const listenTo = self || window;\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, eventData.connectionID, listenTo, sendTo);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methods,\n eventData.connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // send a HANDSHAKE REPLY to the host\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID: eventData.connectionID,\n };\n\n postMessageToTarget(sendTo, payload, event?.origin);\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 // resolve connection object\n const connection = { remote, close, id: eventData.connectionID };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE RESPONSE 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 postMessageToTarget(sendTo, payload);\n });\n}\n\nexport default {\n connect,\n};\n","import {\n addEventListener,\n extractMethods,\n generateId,\n getEventData,\n getOriginFromURL,\n isNodeEnv,\n isNodeWorker,\n isWorkerLike,\n postMessageToTarget,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, Guest, Connection, Connections, Schema } from \"./types\";\n\nconst connections: Connections = {};\n\nfunction isValidTarget(guest: Guest, event: any) {\n // If it's a worker, we don't need to validate origin\n if (isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker)) {\n return true;\n }\n\n // For iframes, check origin and source\n const iframe = guest as HTMLIFrameElement;\n try {\n const childURL = iframe.src;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return (hasProperOrigin && hasProperSource) || !childURL;\n } catch (e) {\n console.warn(\"Error checking iframe target:\", e);\n return false;\n }\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: Guest, schema: Schema = {}): Promise<Connection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = isWorkerLike(guest);\n\n const listenTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n const sendTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : event.source;\n\n if (!guestIsWorker && !isNodeEnv() && !isValidTarget(guest, event)) return;\n\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REQUEST) return;\n if (connections[connectionID]) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(schema, localMethods, connectionID, listenTo, sendTo);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methods,\n connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // send a HANDSHAKE REPLY to the guest\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n schema,\n methods: localMethods,\n };\n\n postMessageToTarget(sendTo, payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n delete connections[connectionID];\n removeEventListener(listenTo, events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) {\n (guest as Worker).terminate();\n }\n };\n\n const connection: Connection = { remote, close, id: connectionID };\n connections[connectionID] = connection;\n }\n\n // subscribe to HANDSHAKE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshake);\n\n // on handshake reply\n function handleHandshakeReply(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n if (connectionID !== eventData.connectionID) return;\n\n if (!connections[eventData.connectionID]) {\n throw new Error(\"Rimless Error: No connection found for this connectionID\");\n }\n\n return resolve(connections[eventData.connectionID]);\n }\n\n addEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","isNodeEnv","_a","isIframe","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","regexResult","protocol","hostname","port","portSuffix","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","parentPort","getTargetHost","postMessageToTarget","target","message","origin","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","registerLocalMethods","schema","methods","rpcConnectionID","listenTo","sendTo","listeners","methodName","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","registerRemoteMethods","remote","rpc","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterLocal","unregisterRemote","connection","connections","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
1
+ {"version":3,"file":"rimless.js","sources":["../src/helpers.ts","../src/types.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["import { Guest, NodeWorker, Target, WorkerLike } from \"./types\";\n\n/**\n * check if run in a webworker\n *\n * @returns boolean\n */\nexport function isWorker(): boolean {\n return typeof window === \"undefined\" && typeof self !== \"undefined\";\n}\n\n/**\n * check if run in a Node.js environment\n *\n * @returns boolean\n */\nexport function isNodeEnv(): boolean {\n return typeof process !== \"undefined\" && !!(process as any).versions?.node;\n}\n\n/**\n * check if run in an iframe\n *\n * @returns boolean\n */\nexport function isIframe() {\n return window.self !== window.top;\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 methods: Record<string, (...args: any) => any> = {};\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 methods[propPath] = obj[prop];\n delete obj[prop];\n }\n });\n })(obj);\n return methods;\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 if (!url) return null;\n\n const regexResult = urlRegex.exec(url);\n if (!regexResult) return null;\n\n const [, protocol = \"http:\", hostname, , port] = regexResult;\n\n // If the protocol is file, return file://\n if (protocol === \"file:\") {\n return \"file://\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n\nexport 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\nlet parentPort: any = null;\n\nif (isNodeEnv()) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const workerThreads = require(\"worker_threads\");\n parentPort = workerThreads.parentPort;\n } catch (e) {\n // Not in worker thread context\n }\n}\n\n/**\n * Get the appropriate target host for messaging based on the current environment\n * @returns The messaging target for the current environment\n */\nexport function getTargetHost(): any {\n if (isNodeEnv()) {\n return parentPort;\n }\n\n if (isWorker()) {\n return self;\n }\n\n if (isIframe()) {\n return window.parent;\n }\n\n throw new Error(\"No valid target found for postMessage\");\n}\n\n/**\n * Send a message to a target, handling different environments (iframe, web worker, node worker)\n * @param target The target to send the message to\n * @param message The message to send\n * @param origin Optional origin for iframe communication\n */\nexport function postMessageToTarget(target: Target, message: any, origin?: string): void {\n if (!target) {\n throw new Error(\"Rimless Error: No target specified for postMessage\");\n }\n\n // Node.js Worker\n if (isNodeEnv() && target === parentPort) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || \"*\" });\n return;\n }\n\n throw new Error(\"Rimless Error: Invalid target for postMessage\");\n}\n\nexport function isNodeWorker(guest: Guest | Target): guest is NodeWorker {\n return parentPort !== null && guest === parentPort;\n}\n\nexport function isWorkerLike(guest: Guest): guest is WorkerLike {\n return isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker);\n}\n\nexport function addEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.on(event, handler);\n } else if (\"addEventListener\" in target) {\n target.addEventListener(event, handler);\n }\n}\n\nexport function removeEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.off(event, handler);\n } else if (\"removeEventListener\" in target) {\n target.removeEventListener(event, handler);\n }\n}\n\n/**\n * Normalize message event data across Web and Node.js environments\n * In web, data is in event.data\n * In Node.js, the event itself contains the data\n */\nexport function getEventData(event: any) {\n return event.data || event;\n}\n","export interface NodeWorker {\n on(event: string, handler: any): void;\n off(event: string, handler: any): void;\n postMessage(message: any): void;\n terminate(): void;\n}\n\nexport type WorkerLike = Worker | NodeWorker;\n\nexport 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 type Schema = Record<string, any>;\n\nexport interface Connection {\n id: string;\n remote: Schema;\n close: () => void;\n}\n\nexport type Connections = Record<string, Connection>;\n\nexport interface RimlessEvent extends EventListener {\n source?: Window;\n origin?: string;\n data: HandshakeRequestPayload | HandshakeConfirmationPayload | RPCRequestPayload | RPCResolvePayload;\n}\n\nexport interface HandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID: string;\n methodNames: string[];\n schema: Schema;\n}\n\nexport interface HandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methodNames: string[];\n schema: Schema;\n}\n\nexport interface RPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface RPCResolvePayload {\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: Schema) => Promise<void>;\n}\n\nexport type Guest = WorkerLike | HTMLIFrameElement;\nexport type Target = Window | WorkerLike;\nexport type Environment = Window | WorkerLike;\n","import { addEventListener, generateId, getEventData, postMessageToTarget, removeEventListener, set } from \"./helpers\";\nimport {\n actions,\n Environment,\n events,\n RimlessEvent,\n RPCRequestPayload,\n RPCResolvePayload,\n Schema,\n Target,\n} from \"./types\";\n\n/**\n * for each function in methods\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 object of method ids : methods from the local schema\n * @param rpcConnectionID\n * @param listenTo Environment to listen for incoming messages\n * @param sendTo Target to send outgoing messages\n * @param remote The remote API object for the current connection\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n methods: Record<string, (...args: any[]) => any> = {},\n rpcConnectionID: string,\n listenTo: Environment,\n sendTo: Target,\n remote: Schema, // Add remote parameter\n) {\n const listeners: any[] = [];\n for (const [methodName, method] of Object.entries(methods)) {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const eventData = getEventData(event);\n const { action, callID, connectionID, callName, args = [] } = eventData as RPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== rpcConnectionID) return;\n\n const payload: RPCResolvePayload = {\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 // Pass the remote object as the LAST argument to the local method\n const result = await method(...args, remote);\n\n if (!result) {\n // if the result is falsy (null, undefined, \"\", etc), set it directly\n payload.result = result;\n } else {\n // otherwise parse a stringified version of it\n payload.result = JSON.parse(JSON.stringify(result));\n }\n } catch (error) {\n payload.action = actions.RPC_REJECT;\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n postMessageToTarget(sendTo, payload, event?.origin);\n }\n\n // subscribe to the call event\n addEventListener(listenTo, events.MESSAGE, handleCall);\n listeners.push(() => removeEventListener(listenTo, 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 rpcCallName\n * @param rpcConnectionID\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 rpcCallName: string,\n rpcConnectionID: string,\n event: RimlessEvent,\n listeners: Array<() => void> = [],\n listenTo: Environment,\n sendTo: Target,\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const requestID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const eventData = getEventData(event);\n const { callID, connectionID, callName, result, error, action } = eventData as RPCResolvePayload;\n\n if (!callID || !callName) return;\n if (callName !== rpcCallName) return;\n if (callID !== requestID) return;\n if (connectionID !== rpcConnectionID) 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: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, payload, event?.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema's 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: Schema = {},\n methodNames: Iterable<string> = [],\n connectionID: string,\n event: RimlessEvent,\n listenTo: Environment,\n sendTo: Target,\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n for (const methodName of methodNames) {\n const rpc = createRPC(methodName, connectionID, event, listeners, listenTo, sendTo);\n set(remote, methodName, rpc);\n }\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import {\n extractMethods,\n getEventData,\n getTargetHost,\n postMessageToTarget,\n addEventListener,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, Connection, Schema } from \"./types\";\n\nfunction connect(schema: Schema = {}, eventHandlers?: EventHandlers): Promise<Connection> {\n return new Promise(async (resolve) => {\n const localMethods = extractMethods(schema);\n const sendTo = getTargetHost();\n const listenTo = self || window;\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methodNames,\n eventData.connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // register local methods, passing the remote object\n const unregisterLocal = registerLocalMethods(localMethods, eventData.connectionID, listenTo, sendTo, remote);\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // send a HANDSHAKE REPLY to the host\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID: eventData.connectionID,\n };\n\n postMessageToTarget(sendTo, payload, event?.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n removeEventListener(listenTo, events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n // resolve connection object\n const connection = { remote, close, id: eventData.connectionID };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE RESPONSE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methodNames: Object.keys(localMethods),\n schema: schema,\n };\n\n postMessageToTarget(sendTo, payload);\n });\n}\n\nexport default {\n connect,\n};\n","import {\n addEventListener,\n extractMethods,\n generateId,\n getEventData,\n getOriginFromURL,\n isNodeEnv,\n isNodeWorker,\n isWorkerLike,\n postMessageToTarget,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, Guest, Connection, Connections, Schema } from \"./types\";\n\nconst connections: Connections = {};\n\nfunction isValidTarget(guest: Guest, event: any) {\n // If it's a worker, we don't need to validate origin\n if (isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker)) {\n return true;\n }\n\n // For iframes, check origin and source\n const iframe = guest as HTMLIFrameElement;\n try {\n const childURL = iframe.src;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return (hasProperOrigin && hasProperSource) || !childURL;\n } catch (e) {\n console.warn(\"Error checking iframe target:\", e);\n return false;\n }\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: Guest, schema: Schema = {}): Promise<Connection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = isWorkerLike(guest);\n\n const listenTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n const sendTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : event.source;\n\n if (!guestIsWorker && !isNodeEnv() && !isValidTarget(guest, event)) return;\n\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REQUEST) return;\n if (connections[connectionID]) return;\n\n // Extract local methods first (doesn't need remote yet)\n const localMethods = extractMethods(schema);\n\n // Register remote methods first to get the remote object\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methodNames,\n connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // Now register local methods, passing the remote object\n const unregisterLocal = registerLocalMethods(localMethods, connectionID, listenTo, sendTo, remote);\n\n // send a HANDSHAKE REPLY to the guest\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n schema: schema,\n methodNames: Object.keys(localMethods),\n };\n\n postMessageToTarget(sendTo, payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n delete connections[connectionID];\n removeEventListener(listenTo, events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) {\n (guest as Worker).terminate();\n }\n };\n\n const connection: Connection = { remote, close, id: connectionID };\n connections[connectionID] = connection;\n }\n\n // subscribe to HANDSHAKE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshake);\n\n // on handshake reply\n function handleHandshakeReply(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n if (connectionID !== eventData.connectionID) return;\n\n if (!connections[eventData.connectionID]) {\n throw new Error(\"Rimless Error: No connection found for this connectionID\");\n }\n\n return resolve(connections[eventData.connectionID]);\n }\n\n addEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","isNodeEnv","_a","isIframe","extractMethods","obj","methods","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","regexResult","protocol","hostname","port","portSuffix","set","value","pathArray","key","current","i","generateId","length","chars","result","parentPort","getTargetHost","postMessageToTarget","target","message","origin","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","registerLocalMethods","rpcConnectionID","listenTo","sendTo","remote","listeners","methodName","method","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","registerRemoteMethods","schema","methodNames","rpc","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterRemote","unregisterLocal","connection","connections","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var rimless=function(y){"use strict";function I(){return typeof window>"u"&&typeof self<"u"}function A(){var n;return typeof process<"u"&&!!((n=process.versions)!=null&&n.node)}function H(){return window.self!==window.top}function _(n){const r=[];return function t(e,i=""){Object.keys(e).forEach(o=>{const c=i?`${i}.${o}`:o;e[o]===Object(e[o])&&t(e[o],c),typeof e[o]=="function"&&r.push(c)})}(n),r}const G=/^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/,K={"http:":"80","https:":"443"};function U(n){if(!n)return null;const r=G.exec(n);if(!r)return null;const[,t="http:",e,,i]=r;if(t==="file:")return"file://";const o=i&&i!==K[t]?`:${i}`:"";return`${t}//${e}${o}`}function W(n,r,t){const e=Array.isArray(r)?r:r.split(".").filter(Boolean);let i=n;for(const o of e)if(i=i==null?void 0:i[o],i===void 0)return t;return i}function Q(n,r,t){if(!n||typeof n!="object")return n;const e=Array.isArray(r)?r:r.split(".").map(o=>o.match(/^\d+$/)?Number(o):o);let i=n;for(let o=0;o<e.length;o++){const c=e[o];o===e.length-1?i[c]=t:((!i[c]||typeof i[c]!="object")&&(i[c]=typeof e[o+1]=="number"?[]:{}),i=i[c])}return n}function T(n=10){const r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let t="";for(let e=0;e<n;e++)t+=r.charAt(Math.floor(Math.random()*r.length));return t}let w=null;if(A())try{w=require("worker_threads").parentPort}catch{}function $(){if(A())return w;if(I())return self;if(H())return window.parent;throw new Error("No valid target found for postMessage")}function m(n,r,t){if(!n)throw new Error("Rimless Error: No target specified for postMessage");if(A()&&n===w){n.postMessage(JSON.parse(JSON.stringify(r)));return}if(I()){n.postMessage(JSON.parse(JSON.stringify(r)));return}if(n.postMessage){n.postMessage(JSON.parse(JSON.stringify(r)),{targetOrigin:t||"*"});return}throw new Error("Rimless Error: Invalid target for postMessage")}function L(n){return w!==null&&n===w}function Y(n){return L(n)||typeof Worker<"u"&&n instanceof Worker}function P(n,r,t){L(n)?n.on(r,t):"addEventListener"in n&&n.addEventListener(r,t)}function O(n,r,t){L(n)?n.off(r,t):"removeEventListener"in n&&n.removeEventListener(r,t)}function M(n){return n.data||n}var l=(n=>(n.MESSAGE="message",n))(l||{}),u=(n=>(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))(u||{});function k(n={},r=[],t,e,i){const o=[];return r.forEach(c=>{async function S(f){const s=M(f),{action:E,callID:R,connectionID:p,callName:h,args:g=[]}=s;if(E!==u.RPC_REQUEST||!R||!h||h!==c||p!==t)return;const d={action:u.RPC_RESOLVE,callID:R,callName:h,connectionID:p,error:null,result:null};try{const a=await W(n,c)(...g);a?d.result=JSON.parse(JSON.stringify(a)):d.result=a}catch(a){d.action=u.RPC_REJECT,d.error=JSON.parse(JSON.stringify(a,Object.getOwnPropertyNames(a)))}m(i,d,f==null?void 0:f.origin)}P(e,l.MESSAGE,S),o.push(()=>O(e,l.MESSAGE,S))}),()=>o.forEach(c=>c())}function V(n,r,t,e=[],i,o){return(...c)=>new Promise((S,f)=>{const s=T();function E(p){const h=M(p),{callID:g,connectionID:d,callName:a,result:D,error:z,action:C}=h;if(!(!g||!a)&&a===n&&g===s&&d===r){if(C===u.RPC_RESOLVE)return S(D);if(C===u.RPC_REJECT)return f(z)}}const R={action:u.RPC_REQUEST,args:JSON.parse(JSON.stringify(c)),callID:s,callName:n,connectionID:r};P(i,l.MESSAGE,E),e.push(()=>O(i,l.MESSAGE,E)),m(o,R,t==null?void 0:t.origin)})}function J(n={},r=[],t,e,i,o){const c={...n},S=[];return r.forEach(f=>{const s=V(f,t,e,S,i,o);Q(c,f,s)}),{remote:c,unregisterRemote:()=>S.forEach(f=>f())}}function q(n={},r){return new Promise(async t=>{const e=_(n),i=$(),o=self||window;async function c(f){var a;const s=M(f);if((s==null?void 0:s.action)!==u.HANDSHAKE_REPLY)return;const E=k(n,e,s.connectionID,o,i),{remote:R,unregisterRemote:p}=J(s.schema,s.methods,s.connectionID,f,o,i);await((a=r==null?void 0:r.onConnectionSetup)==null?void 0:a.call(r,R));const h={action:u.HANDSHAKE_REPLY,connectionID:s.connectionID};m(i,h,f==null?void 0:f.origin);const d={remote:R,close:()=>{self.removeEventListener(l.MESSAGE,c),p(),E()},id:s.connectionID};return t(d)}self.addEventListener(l.MESSAGE,c);const S={action:u.HANDSHAKE_REQUEST,methods:e,schema:JSON.parse(JSON.stringify(n))};m(i,S)})}const b={connect:q},N={};function B(n,r){if(L(n)||typeof Worker<"u"&&n instanceof Worker)return!0;const t=n;try{const e=t.src,i=U(e),o=r.origin===i,c=r.source===t.contentWindow;return o&&c||!e}catch(e){return console.warn("Error checking iframe target:",e),!1}}function F(n,r={}){if(!n)throw new Error("a target is required");const t=Y(n),e=t||A()?n:window;return new Promise(i=>{const o=T();function c(f){const s=t||A()?n:f.source;if(!t&&!A()&&!B(n,f))return;const E=M(f);if((E==null?void 0:E.action)!==u.HANDSHAKE_REQUEST||N[o])return;const R=_(r),p=k(r,R,o,e,s),{remote:h,unregisterRemote:g}=J(E.schema,E.methods,o,f,e,s),d={action:u.HANDSHAKE_REPLY,connectionID:o,schema:r,methods:R};m(s,d,f.origin);const D={remote:h,close:()=>{delete N[o],O(e,l.MESSAGE,c),g(),p(),t&&n.terminate()},id:o};N[o]=D}P(e,l.MESSAGE,c);function S(f){const s=M(f);if((s==null?void 0:s.action)===u.HANDSHAKE_REPLY&&o===s.connectionID){if(!N[s.connectionID])throw new Error("Rimless Error: No connection found for this connectionID");return i(N[s.connectionID])}}P(e,l.MESSAGE,S)})}const x={connect:F};return y.actions=u,y.events=l,y.guest=b,y.host=x,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"}),y}({});
1
+ var rimless=function(p){"use strict";function I(){return typeof window>"u"&&typeof self<"u"}function y(){var n;return typeof process<"u"&&!!((n=process.versions)!=null&&n.node)}function J(){return window.self!==window.top}function _(n){const e={};return function t(o,s=""){Object.keys(o).forEach(r=>{const c=s?`${s}.${r}`:r;o[r]===Object(o[r])&&t(o[r],c),typeof o[r]=="function"&&(e[c]=o[r],delete o[r])})}(n),e}const G=/^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/,K={"http:":"80","https:":"443"};function U(n){if(!n)return null;const e=G.exec(n);if(!e)return null;const[,t="http:",o,,s]=e;if(t==="file:")return"file://";const r=s&&s!==K[t]?`:${s}`:"";return`${t}//${o}${r}`}function W(n,e,t){if(!n||typeof n!="object")return n;const o=Array.isArray(e)?e:e.split(".").map(r=>r.match(/^\d+$/)?Number(r):r);let s=n;for(let r=0;r<o.length;r++){const c=o[r];r===o.length-1?s[c]=t:((!s[c]||typeof s[c]!="object")&&(s[c]=typeof o[r+1]=="number"?[]:{}),s=s[c])}return n}function T(n=10){const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let t="";for(let o=0;o<n;o++)t+=e.charAt(Math.floor(Math.random()*e.length));return t}let A=null;if(y())try{A=require("worker_threads").parentPort}catch{}function Q(){if(y())return A;if(I())return self;if(J())return window.parent;throw new Error("No valid target found for postMessage")}function w(n,e,t){if(!n)throw new Error("Rimless Error: No target specified for postMessage");if(y()&&n===A){n.postMessage(JSON.parse(JSON.stringify(e)));return}if(I()){n.postMessage(JSON.parse(JSON.stringify(e)));return}if(n.postMessage){n.postMessage(JSON.parse(JSON.stringify(e)),{targetOrigin:t||"*"});return}throw new Error("Rimless Error: Invalid target for postMessage")}function L(n){return A!==null&&n===A}function $(n){return L(n)||typeof Worker<"u"&&n instanceof Worker}function M(n,e,t){L(n)?n.on(e,t):"addEventListener"in n&&n.addEventListener(e,t)}function D(n,e,t){L(n)?n.off(e,t):"removeEventListener"in n&&n.removeEventListener(e,t)}function P(n){return n.data||n}var l=(n=>(n.MESSAGE="message",n))(l||{}),u=(n=>(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))(u||{});function k(n={},e,t,o,s){const r=[];for(const[c,S]of Object.entries(n)){async function f(i){const a=P(i),{action:m,callID:h,connectionID:g,callName:R,args:N=[]}=a;if(m!==u.RPC_REQUEST||!h||!R||R!==c||g!==e)return;const E={action:u.RPC_RESOLVE,callID:h,callName:R,connectionID:g,error:null,result:null};try{const d=await S(...N,s);d?E.result=JSON.parse(JSON.stringify(d)):E.result=d}catch(d){E.action=u.RPC_REJECT,E.error=JSON.parse(JSON.stringify(d,Object.getOwnPropertyNames(d)))}w(o,E,i==null?void 0:i.origin)}M(t,l.MESSAGE,f),r.push(()=>D(t,l.MESSAGE,f))}return()=>r.forEach(c=>c())}function Y(n,e,t,o=[],s,r){return(...c)=>new Promise((S,f)=>{const i=T();function a(h){const g=P(h),{callID:R,connectionID:N,callName:E,result:d,error:z,action:H}=g;if(!(!R||!E)&&E===n&&R===i&&N===e){if(H===u.RPC_RESOLVE)return S(d);if(H===u.RPC_REJECT)return f(z)}}const m={action:u.RPC_REQUEST,args:JSON.parse(JSON.stringify(c)),callID:i,callName:n,connectionID:e};M(s,l.MESSAGE,a),o.push(()=>D(s,l.MESSAGE,a)),w(r,m,t==null?void 0:t.origin)})}function C(n={},e=[],t,o,s,r){const c={...n},S=[];for(const f of e){const i=Y(f,t,o,S,s,r);W(c,f,i)}return{remote:c,unregisterRemote:()=>S.forEach(f=>f())}}function V(n={},e){return new Promise(async t=>{const o=_(n),s=Q(),r=self||window;async function c(f){var E;const i=P(f);if((i==null?void 0:i.action)!==u.HANDSHAKE_REPLY)return;const{remote:a,unregisterRemote:m}=C(i.schema,i.methodNames,i.connectionID,f,r,s),h=k(o,i.connectionID,r,s,a);await((E=e==null?void 0:e.onConnectionSetup)==null?void 0:E.call(e,a));const g={action:u.HANDSHAKE_REPLY,connectionID:i.connectionID};w(s,g,f==null?void 0:f.origin);const N={remote:a,close:()=>{D(r,l.MESSAGE,c),m(),h()},id:i.connectionID};return t(N)}M(r,l.MESSAGE,c);const S={action:u.HANDSHAKE_REQUEST,methodNames:Object.keys(o),schema:n};w(s,S)})}const q={connect:V},O={};function b(n,e){if(L(n)||typeof Worker<"u"&&n instanceof Worker)return!0;const t=n;try{const o=t.src,s=U(o),r=e.origin===s,c=e.source===t.contentWindow;return r&&c||!o}catch(o){return console.warn("Error checking iframe target:",o),!1}}function F(n,e={}){if(!n)throw new Error("a target is required");const t=$(n),o=t||y()?n:window;return new Promise(s=>{const r=T();function c(f){const i=t||y()?n:f.source;if(!t&&!y()&&!b(n,f))return;const a=P(f);if((a==null?void 0:a.action)!==u.HANDSHAKE_REQUEST||O[r])return;const m=_(e),{remote:h,unregisterRemote:g}=C(a.schema,a.methodNames,r,f,o,i),R=k(m,r,o,i,h),N={action:u.HANDSHAKE_REPLY,connectionID:r,schema:e,methodNames:Object.keys(m)};w(i,N,f.origin);const d={remote:h,close:()=>{delete O[r],D(o,l.MESSAGE,c),g(),R(),t&&n.terminate()},id:r};O[r]=d}M(o,l.MESSAGE,c);function S(f){const i=P(f);if((i==null?void 0:i.action)===u.HANDSHAKE_REPLY&&r===i.connectionID){if(!O[i.connectionID])throw new Error("Rimless Error: No connection found for this connectionID");return s(O[i.connectionID])}}M(o,l.MESSAGE,S)})}const x={connect:F};return p.actions=u,p.events=l,p.guest=q,p.host=x,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"}),p}({});
2
2
  //# sourceMappingURL=rimless.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rimless.min.js","sources":["../src/helpers.ts","../src/types.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["import { Guest, NodeWorker, Target, WorkerLike } from \"./types\";\n\n/**\n * check if run in a webworker\n *\n * @returns boolean\n */\nexport function isWorker(): boolean {\n return typeof window === \"undefined\" && typeof self !== \"undefined\";\n}\n\n/**\n * check if run in a Node.js environment\n *\n * @returns boolean\n */\nexport function isNodeEnv(): boolean {\n return typeof process !== \"undefined\" && !!(process as any).versions?.node;\n}\n\n/**\n * check if run in an iframe\n *\n * @returns boolean\n */\nexport function isIframe() {\n return window.self !== window.top;\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 if (!url) return null;\n\n const regexResult = urlRegex.exec(url);\n if (!regexResult) return null;\n\n const [, protocol = \"http:\", hostname, , port] = regexResult;\n\n // If the protocol is file, return file://\n if (protocol === \"file:\") {\n return \"file://\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n\nexport 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\nlet parentPort: any = null;\n\nif (isNodeEnv()) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const workerThreads = require(\"worker_threads\");\n parentPort = workerThreads.parentPort;\n } catch (e) {\n // Not in worker thread context\n }\n}\n\n/**\n * Get the appropriate target host for messaging based on the current environment\n * @returns The messaging target for the current environment\n */\nexport function getTargetHost(): any {\n if (isNodeEnv()) {\n return parentPort;\n }\n\n if (isWorker()) {\n return self;\n }\n\n if (isIframe()) {\n return window.parent;\n }\n\n throw new Error(\"No valid target found for postMessage\");\n}\n\n/**\n * Send a message to a target, handling different environments (iframe, web worker, node worker)\n * @param target The target to send the message to\n * @param message The message to send\n * @param origin Optional origin for iframe communication\n */\nexport function postMessageToTarget(target: Target, message: any, origin?: string): void {\n if (!target) {\n throw new Error(\"Rimless Error: No target specified for postMessage\");\n }\n\n // Node.js Worker\n if (isNodeEnv() && target === parentPort) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || \"*\" });\n return;\n }\n\n throw new Error(\"Rimless Error: Invalid target for postMessage\");\n}\n\nexport function isNodeWorker(guest: Guest | Target): guest is NodeWorker {\n return parentPort !== null && guest === parentPort;\n}\n\nexport function isWorkerLike(guest: Guest): guest is WorkerLike {\n return isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker);\n}\n\nexport function addEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.on(event, handler);\n } else if (\"addEventListener\" in target) {\n target.addEventListener(event, handler);\n }\n}\n\nexport function removeEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.off(event, handler);\n } else if (\"removeEventListener\" in target) {\n target.removeEventListener(event, handler);\n }\n}\n\n/**\n * Normalize message event data across Web and Node.js environments\n * In web, data is in event.data\n * In Node.js, the event itself contains the data\n */\nexport function getEventData(event: any) {\n return event.data || event;\n}\n","export interface NodeWorker {\n on(event: string, handler: any): void;\n off(event: string, handler: any): void;\n postMessage(message: any): void;\n terminate(): void;\n}\n\nexport type WorkerLike = Worker | NodeWorker;\n\nexport 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 type Schema = Record<string, any>;\n\nexport interface Connection {\n id: string;\n remote: Schema;\n close: () => void;\n}\n\nexport type Connections = Record<string, Connection>;\n\nexport interface RimlessEvent extends EventListener {\n source?: Window;\n origin?: string;\n data: HandshakeRequestPayload | HandshakeConfirmationPayload | RPCRequestPayload | RPCResolvePayload;\n}\n\nexport interface HandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID: string;\n methods: string[];\n schema: Schema;\n}\n\nexport interface HandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methods: string[];\n schema: Schema;\n}\n\nexport interface RPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface RPCResolvePayload {\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: Schema) => Promise<void>;\n}\n\nexport type Guest = WorkerLike | HTMLIFrameElement;\nexport type Target = Window | WorkerLike;\nexport type Environment = Window | WorkerLike;\n","import {\n addEventListener,\n generateId,\n get,\n getEventData,\n postMessageToTarget,\n removeEventListener,\n set,\n} from \"./helpers\";\nimport {\n actions,\n Environment,\n events,\n RimlessEvent,\n RPCRequestPayload,\n RPCResolvePayload,\n Schema,\n Target,\n} from \"./types\";\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 rpcConnectionID\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n schema: Schema = {},\n methods: string[] = [],\n rpcConnectionID: string,\n listenTo: Environment,\n sendTo: Target,\n) {\n const listeners: any[] = [];\n\n methods.forEach((methodName) => {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const eventData = getEventData(event);\n const { action, callID, connectionID, callName, args = [] } = eventData as RPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== rpcConnectionID) return;\n\n const payload: RPCResolvePayload = {\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\n if (!result) {\n // if the result is falsy (null, undefined, \"\", etc), set it directly\n payload.result = result;\n } else {\n // otherwise parse a stringified version of it\n payload.result = JSON.parse(JSON.stringify(result));\n }\n } catch (error) {\n payload.action = actions.RPC_REJECT;\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n postMessageToTarget(sendTo, payload, event?.origin);\n }\n\n // subscribe to the call event\n addEventListener(listenTo, events.MESSAGE, handleCall);\n listeners.push(() => removeEventListener(listenTo, 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 rpcCallName\n * @param rpcConnectionID\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 rpcCallName: string,\n rpcConnectionID: string,\n event: RimlessEvent,\n listeners: Array<() => void> = [],\n listenTo: Environment,\n sendTo: Target,\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const requestID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const eventData = getEventData(event);\n const { callID, connectionID, callName, result, error, action } = eventData as RPCResolvePayload;\n\n if (!callID || !callName) return;\n if (callName !== rpcCallName) return;\n if (callID !== requestID) return;\n if (connectionID !== rpcConnectionID) 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: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, 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: Schema = {},\n methods: string[] = [],\n connectionID: string,\n event: RimlessEvent,\n listenTo: Environment,\n sendTo: Target,\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n methods.forEach((methodName) => {\n const rpc = createRPC(methodName, connectionID, event, listeners, listenTo, sendTo);\n set(remote, methodName, rpc);\n });\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import { extractMethods, getEventData, getTargetHost, postMessageToTarget } from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, Connection, Schema } from \"./types\";\n\nfunction connect(schema: Schema = {}, eventHandlers?: EventHandlers): Promise<Connection> {\n return new Promise(async (resolve) => {\n const localMethods = extractMethods(schema);\n const sendTo = getTargetHost();\n const listenTo = self || window;\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n\n // register local methods\n const unregisterLocal = registerLocalMethods(schema, localMethods, eventData.connectionID, listenTo, sendTo);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methods,\n eventData.connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // send a HANDSHAKE REPLY to the host\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID: eventData.connectionID,\n };\n\n postMessageToTarget(sendTo, payload, event?.origin);\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 // resolve connection object\n const connection = { remote, close, id: eventData.connectionID };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE RESPONSE 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 postMessageToTarget(sendTo, payload);\n });\n}\n\nexport default {\n connect,\n};\n","import {\n addEventListener,\n extractMethods,\n generateId,\n getEventData,\n getOriginFromURL,\n isNodeEnv,\n isNodeWorker,\n isWorkerLike,\n postMessageToTarget,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, Guest, Connection, Connections, Schema } from \"./types\";\n\nconst connections: Connections = {};\n\nfunction isValidTarget(guest: Guest, event: any) {\n // If it's a worker, we don't need to validate origin\n if (isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker)) {\n return true;\n }\n\n // For iframes, check origin and source\n const iframe = guest as HTMLIFrameElement;\n try {\n const childURL = iframe.src;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return (hasProperOrigin && hasProperSource) || !childURL;\n } catch (e) {\n console.warn(\"Error checking iframe target:\", e);\n return false;\n }\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: Guest, schema: Schema = {}): Promise<Connection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = isWorkerLike(guest);\n\n const listenTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n const sendTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : event.source;\n\n if (!guestIsWorker && !isNodeEnv() && !isValidTarget(guest, event)) return;\n\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REQUEST) return;\n if (connections[connectionID]) return;\n\n // register local methods\n const localMethods = extractMethods(schema);\n const unregisterLocal = registerLocalMethods(schema, localMethods, connectionID, listenTo, sendTo);\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methods,\n connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // send a HANDSHAKE REPLY to the guest\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n schema,\n methods: localMethods,\n };\n\n postMessageToTarget(sendTo, payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n delete connections[connectionID];\n removeEventListener(listenTo, events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) {\n (guest as Worker).terminate();\n }\n };\n\n const connection: Connection = { remote, close, id: connectionID };\n connections[connectionID] = connection;\n }\n\n // subscribe to HANDSHAKE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshake);\n\n // on handshake reply\n function handleHandshakeReply(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n if (connectionID !== eventData.connectionID) return;\n\n if (!connections[eventData.connectionID]) {\n throw new Error(\"Rimless Error: No connection found for this connectionID\");\n }\n\n return resolve(connections[eventData.connectionID]);\n }\n\n addEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","isNodeEnv","_a","isIframe","extractMethods","obj","paths","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","regexResult","protocol","hostname","port","portSuffix","get","defaultValue","keys","result","key","set","value","pathArray","current","i","generateId","length","chars","parentPort","getTargetHost","postMessageToTarget","target","message","origin","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","registerLocalMethods","schema","methods","rpcConnectionID","listenTo","sendTo","listeners","methodName","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","registerRemoteMethods","remote","rpc","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterLocal","unregisterRemote","connection","connections","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
1
+ {"version":3,"file":"rimless.min.js","sources":["../src/helpers.ts","../src/types.ts","../src/rpc.ts","../src/guest.ts","../src/host.ts"],"sourcesContent":["import { Guest, NodeWorker, Target, WorkerLike } from \"./types\";\n\n/**\n * check if run in a webworker\n *\n * @returns boolean\n */\nexport function isWorker(): boolean {\n return typeof window === \"undefined\" && typeof self !== \"undefined\";\n}\n\n/**\n * check if run in a Node.js environment\n *\n * @returns boolean\n */\nexport function isNodeEnv(): boolean {\n return typeof process !== \"undefined\" && !!(process as any).versions?.node;\n}\n\n/**\n * check if run in an iframe\n *\n * @returns boolean\n */\nexport function isIframe() {\n return window.self !== window.top;\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 methods: Record<string, (...args: any) => any> = {};\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 methods[propPath] = obj[prop];\n delete obj[prop];\n }\n });\n })(obj);\n return methods;\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 if (!url) return null;\n\n const regexResult = urlRegex.exec(url);\n if (!regexResult) return null;\n\n const [, protocol = \"http:\", hostname, , port] = regexResult;\n\n // If the protocol is file, return file://\n if (protocol === \"file:\") {\n return \"file://\";\n }\n\n // If the port is the default for the protocol, we don't want to add it to the origin string\n const portSuffix = port && port !== ports[protocol] ? `:${port}` : \"\";\n return `${protocol}//${hostname}${portSuffix}`;\n}\n\nexport 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\nlet parentPort: any = null;\n\nif (isNodeEnv()) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const workerThreads = require(\"worker_threads\");\n parentPort = workerThreads.parentPort;\n } catch (e) {\n // Not in worker thread context\n }\n}\n\n/**\n * Get the appropriate target host for messaging based on the current environment\n * @returns The messaging target for the current environment\n */\nexport function getTargetHost(): any {\n if (isNodeEnv()) {\n return parentPort;\n }\n\n if (isWorker()) {\n return self;\n }\n\n if (isIframe()) {\n return window.parent;\n }\n\n throw new Error(\"No valid target found for postMessage\");\n}\n\n/**\n * Send a message to a target, handling different environments (iframe, web worker, node worker)\n * @param target The target to send the message to\n * @param message The message to send\n * @param origin Optional origin for iframe communication\n */\nexport function postMessageToTarget(target: Target, message: any, origin?: string): void {\n if (!target) {\n throw new Error(\"Rimless Error: No target specified for postMessage\");\n }\n\n // Node.js Worker\n if (isNodeEnv() && target === parentPort) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(JSON.parse(JSON.stringify(message)));\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(JSON.parse(JSON.stringify(message)), { targetOrigin: origin || \"*\" });\n return;\n }\n\n throw new Error(\"Rimless Error: Invalid target for postMessage\");\n}\n\nexport function isNodeWorker(guest: Guest | Target): guest is NodeWorker {\n return parentPort !== null && guest === parentPort;\n}\n\nexport function isWorkerLike(guest: Guest): guest is WorkerLike {\n return isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker);\n}\n\nexport function addEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.on(event, handler);\n } else if (\"addEventListener\" in target) {\n target.addEventListener(event, handler);\n }\n}\n\nexport function removeEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject) {\n if (isNodeWorker(target)) {\n target.off(event, handler);\n } else if (\"removeEventListener\" in target) {\n target.removeEventListener(event, handler);\n }\n}\n\n/**\n * Normalize message event data across Web and Node.js environments\n * In web, data is in event.data\n * In Node.js, the event itself contains the data\n */\nexport function getEventData(event: any) {\n return event.data || event;\n}\n","export interface NodeWorker {\n on(event: string, handler: any): void;\n off(event: string, handler: any): void;\n postMessage(message: any): void;\n terminate(): void;\n}\n\nexport type WorkerLike = Worker | NodeWorker;\n\nexport 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 type Schema = Record<string, any>;\n\nexport interface Connection {\n id: string;\n remote: Schema;\n close: () => void;\n}\n\nexport type Connections = Record<string, Connection>;\n\nexport interface RimlessEvent extends EventListener {\n source?: Window;\n origin?: string;\n data: HandshakeRequestPayload | HandshakeConfirmationPayload | RPCRequestPayload | RPCResolvePayload;\n}\n\nexport interface HandshakeRequestPayload {\n action: actions.HANDSHAKE_REQUEST;\n connectionID: string;\n methodNames: string[];\n schema: Schema;\n}\n\nexport interface HandshakeConfirmationPayload {\n action: actions.HANDSHAKE_REPLY;\n connectionID: string;\n methodNames: string[];\n schema: Schema;\n}\n\nexport interface RPCRequestPayload {\n action: actions.RPC_REQUEST;\n args: any[];\n callID: string;\n callName: string;\n connectionID: string;\n}\n\nexport interface RPCResolvePayload {\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: Schema) => Promise<void>;\n}\n\nexport type Guest = WorkerLike | HTMLIFrameElement;\nexport type Target = Window | WorkerLike;\nexport type Environment = Window | WorkerLike;\n","import { addEventListener, generateId, getEventData, postMessageToTarget, removeEventListener, set } from \"./helpers\";\nimport {\n actions,\n Environment,\n events,\n RimlessEvent,\n RPCRequestPayload,\n RPCResolvePayload,\n Schema,\n Target,\n} from \"./types\";\n\n/**\n * for each function in methods\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 object of method ids : methods from the local schema\n * @param rpcConnectionID\n * @param listenTo Environment to listen for incoming messages\n * @param sendTo Target to send outgoing messages\n * @param remote The remote API object for the current connection\n * @return a function to cancel all subscriptions\n */\nexport function registerLocalMethods(\n methods: Record<string, (...args: any[]) => any> = {},\n rpcConnectionID: string,\n listenTo: Environment,\n sendTo: Target,\n remote: Schema, // Add remote parameter\n) {\n const listeners: any[] = [];\n for (const [methodName, method] of Object.entries(methods)) {\n // handle a remote calling a local method\n async function handleCall(event: any) {\n const eventData = getEventData(event);\n const { action, callID, connectionID, callName, args = [] } = eventData as RPCRequestPayload;\n\n if (action !== actions.RPC_REQUEST) return;\n if (!callID || !callName) return;\n if (callName !== methodName) return;\n if (connectionID !== rpcConnectionID) return;\n\n const payload: RPCResolvePayload = {\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 // Pass the remote object as the LAST argument to the local method\n const result = await method(...args, remote);\n\n if (!result) {\n // if the result is falsy (null, undefined, \"\", etc), set it directly\n payload.result = result;\n } else {\n // otherwise parse a stringified version of it\n payload.result = JSON.parse(JSON.stringify(result));\n }\n } catch (error) {\n payload.action = actions.RPC_REJECT;\n payload.error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));\n }\n\n postMessageToTarget(sendTo, payload, event?.origin);\n }\n\n // subscribe to the call event\n addEventListener(listenTo, events.MESSAGE, handleCall);\n listeners.push(() => removeEventListener(listenTo, 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 rpcCallName\n * @param rpcConnectionID\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 rpcCallName: string,\n rpcConnectionID: string,\n event: RimlessEvent,\n listeners: Array<() => void> = [],\n listenTo: Environment,\n sendTo: Target,\n) {\n return (...args: any) => {\n return new Promise((resolve, reject) => {\n const requestID = generateId();\n\n // on RPC response\n function handleResponse(event: any) {\n const eventData = getEventData(event);\n const { callID, connectionID, callName, result, error, action } = eventData as RPCResolvePayload;\n\n if (!callID || !callName) return;\n if (callName !== rpcCallName) return;\n if (callID !== requestID) return;\n if (connectionID !== rpcConnectionID) 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: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, payload, event?.origin);\n });\n };\n}\n\n/**\n * create an object based on the remote schema's 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: Schema = {},\n methodNames: Iterable<string> = [],\n connectionID: string,\n event: RimlessEvent,\n listenTo: Environment,\n sendTo: Target,\n) {\n const remote = { ...schema };\n const listeners: Array<() => void> = [];\n\n for (const methodName of methodNames) {\n const rpc = createRPC(methodName, connectionID, event, listeners, listenTo, sendTo);\n set(remote, methodName, rpc);\n }\n\n return {\n remote,\n unregisterRemote: () => listeners.forEach((unregister) => unregister()),\n };\n}\n","import {\n extractMethods,\n getEventData,\n getTargetHost,\n postMessageToTarget,\n addEventListener,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, EventHandlers, events, Connection, Schema } from \"./types\";\n\nfunction connect(schema: Schema = {}, eventHandlers?: EventHandlers): Promise<Connection> {\n return new Promise(async (resolve) => {\n const localMethods = extractMethods(schema);\n const sendTo = getTargetHost();\n const listenTo = self || window;\n\n // on handshake response\n async function handleHandshakeResponse(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n\n // register remote methods\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methodNames,\n eventData.connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // register local methods, passing the remote object\n const unregisterLocal = registerLocalMethods(localMethods, eventData.connectionID, listenTo, sendTo, remote);\n\n await eventHandlers?.onConnectionSetup?.(remote);\n\n // send a HANDSHAKE REPLY to the host\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID: eventData.connectionID,\n };\n\n postMessageToTarget(sendTo, payload, event?.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n removeEventListener(listenTo, events.MESSAGE, handleHandshakeResponse);\n unregisterRemote();\n unregisterLocal();\n };\n\n // resolve connection object\n const connection = { remote, close, id: eventData.connectionID };\n return resolve(connection);\n }\n\n // subscribe to HANDSHAKE RESPONSE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshakeResponse);\n\n const payload = {\n action: actions.HANDSHAKE_REQUEST,\n methodNames: Object.keys(localMethods),\n schema: schema,\n };\n\n postMessageToTarget(sendTo, payload);\n });\n}\n\nexport default {\n connect,\n};\n","import {\n addEventListener,\n extractMethods,\n generateId,\n getEventData,\n getOriginFromURL,\n isNodeEnv,\n isNodeWorker,\n isWorkerLike,\n postMessageToTarget,\n removeEventListener,\n} from \"./helpers\";\nimport { registerLocalMethods, registerRemoteMethods } from \"./rpc\";\nimport { actions, events, Guest, Connection, Connections, Schema } from \"./types\";\n\nconst connections: Connections = {};\n\nfunction isValidTarget(guest: Guest, event: any) {\n // If it's a worker, we don't need to validate origin\n if (isNodeWorker(guest) || (typeof Worker !== \"undefined\" && guest instanceof Worker)) {\n return true;\n }\n\n // For iframes, check origin and source\n const iframe = guest as HTMLIFrameElement;\n try {\n const childURL = iframe.src;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n return (hasProperOrigin && hasProperSource) || !childURL;\n } catch (e) {\n console.warn(\"Error checking iframe target:\", e);\n return false;\n }\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: Guest, schema: Schema = {}): Promise<Connection> {\n if (!guest) throw new Error(\"a target is required\");\n\n const guestIsWorker = isWorkerLike(guest);\n\n const listenTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : window;\n\n return new Promise((resolve) => {\n const connectionID = generateId();\n\n // on handshake request\n function handleHandshake(event: any) {\n const sendTo = guestIsWorker || isNodeEnv() ? (guest as Worker) : event.source;\n\n if (!guestIsWorker && !isNodeEnv() && !isValidTarget(guest, event)) return;\n\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REQUEST) return;\n if (connections[connectionID]) return;\n\n // Extract local methods first (doesn't need remote yet)\n const localMethods = extractMethods(schema);\n\n // Register remote methods first to get the remote object\n const { remote, unregisterRemote } = registerRemoteMethods(\n eventData.schema,\n eventData.methodNames,\n connectionID,\n event,\n listenTo,\n sendTo,\n );\n\n // Now register local methods, passing the remote object\n const unregisterLocal = registerLocalMethods(localMethods, connectionID, listenTo, sendTo, remote);\n\n // send a HANDSHAKE REPLY to the guest\n const payload = {\n action: actions.HANDSHAKE_REPLY,\n connectionID,\n schema: schema,\n methodNames: Object.keys(localMethods),\n };\n\n postMessageToTarget(sendTo, payload, event.origin);\n\n // close the connection and all listeners when called\n const close = () => {\n delete connections[connectionID];\n removeEventListener(listenTo, events.MESSAGE, handleHandshake);\n unregisterRemote();\n unregisterLocal();\n if (guestIsWorker) {\n (guest as Worker).terminate();\n }\n };\n\n const connection: Connection = { remote, close, id: connectionID };\n connections[connectionID] = connection;\n }\n\n // subscribe to HANDSHAKE MESSAGES\n addEventListener(listenTo, events.MESSAGE, handleHandshake);\n\n // on handshake reply\n function handleHandshakeReply(event: any) {\n const eventData = getEventData(event);\n if (eventData?.action !== actions.HANDSHAKE_REPLY) return;\n if (connectionID !== eventData.connectionID) return;\n\n if (!connections[eventData.connectionID]) {\n throw new Error(\"Rimless Error: No connection found for this connectionID\");\n }\n\n return resolve(connections[eventData.connectionID]);\n }\n\n addEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\n });\n}\n\nexport default {\n connect,\n};\n"],"names":["isWorker","isNodeEnv","_a","isIframe","extractMethods","obj","methods","parse","path","prop","propPath","urlRegex","ports","getOriginFromURL","url","regexResult","protocol","hostname","port","portSuffix","set","value","pathArray","key","current","i","generateId","length","chars","result","parentPort","getTargetHost","postMessageToTarget","target","message","origin","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","registerLocalMethods","rpcConnectionID","listenTo","sendTo","remote","listeners","methodName","method","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","registerRemoteMethods","schema","methodNames","rpc","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterRemote","unregisterLocal","connection","connections","isValidTarget","iframe","childURL","childOrigin","hasProperOrigin","hasProperSource","e","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
package/lib/rpc.d.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import { Environment, RimlessEvent, Schema, Target } from './types';
2
2
  /**
3
- * for each function in the schema
3
+ * for each function in methods
4
4
  * 1. subscribe to an event that the remote can call
5
5
  * 2. listen for calls from the remote. When called execute the function and emit the results.
6
6
  *
7
- * @param methods an array of method ids from the local schema
7
+ * @param methods an object of method ids : methods from the local schema
8
8
  * @param rpcConnectionID
9
+ * @param listenTo Environment to listen for incoming messages
10
+ * @param sendTo Target to send outgoing messages
11
+ * @param remote The remote API object for the current connection
9
12
  * @return a function to cancel all subscriptions
10
13
  */
11
- export declare function registerLocalMethods(schema: Schema | undefined, methods: string[] | undefined, rpcConnectionID: string, listenTo: Environment, sendTo: Target): () => void;
14
+ export declare function registerLocalMethods(methods: Record<string, (...args: any[]) => any> | undefined, rpcConnectionID: string, listenTo: Environment, sendTo: Target, remote: Schema): () => void;
12
15
  /**
13
16
  * Create a function that will make an RPC request to the remote with some arguments.
14
17
  * Listen to an event that returns the results from the remote.
@@ -23,7 +26,7 @@ export declare function registerLocalMethods(schema: Schema | undefined, methods
23
26
  */
24
27
  export declare function createRPC(rpcCallName: string, rpcConnectionID: string, event: RimlessEvent, listeners: Array<() => void> | undefined, listenTo: Environment, sendTo: Target): (...args: any) => Promise<unknown>;
25
28
  /**
26
- * create an object based on the remote schema and methods. Functions in that object will
29
+ * create an object based on the remote schema's methods. Functions in that object will
27
30
  * emit an event that will trigger the RPC on the remote.
28
31
  *
29
32
  * @param schema
@@ -32,7 +35,7 @@ export declare function createRPC(rpcCallName: string, rpcConnectionID: string,
32
35
  * @param event
33
36
  * @param guest
34
37
  */
35
- export declare function registerRemoteMethods(schema: Schema | undefined, methods: string[] | undefined, connectionID: string, event: RimlessEvent, listenTo: Environment, sendTo: Target): {
38
+ export declare function registerRemoteMethods(schema: Schema | undefined, methodNames: Iterable<string> | undefined, connectionID: string, event: RimlessEvent, listenTo: Environment, sendTo: Target): {
36
39
  remote: {
37
40
  [x: string]: any;
38
41
  };
package/lib/types.d.ts CHANGED
@@ -30,13 +30,13 @@ export interface RimlessEvent extends EventListener {
30
30
  export interface HandshakeRequestPayload {
31
31
  action: actions.HANDSHAKE_REQUEST;
32
32
  connectionID: string;
33
- methods: string[];
33
+ methodNames: string[];
34
34
  schema: Schema;
35
35
  }
36
36
  export interface HandshakeConfirmationPayload {
37
37
  action: actions.HANDSHAKE_REPLY;
38
38
  connectionID: string;
39
- methods: string[];
39
+ methodNames: string[];
40
40
  schema: Schema;
41
41
  }
42
42
  export interface RPCRequestPayload {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rimless",
3
3
  "author": "Aurélien Franky",
4
- "version": "0.5.1",
4
+ "version": "0.6.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`",