rimless 0.6.0 → 0.7.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
@@ -14,9 +14,9 @@
14
14
  [![Commitizen friendly][commitizen-image]][commitizen-url]
15
15
  ![npm bundle size](https://img.shields.io/bundlephobia/minzip/rimless)
16
16
 
17
- > Rimless makes event based communication easy with a promise-based API wrapping [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Works with both **iframes** and **webworkers**.
17
+ > Rimless makes event based communication easy with a promise-based API wrapping [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Works with **iframes**, **webworkers**, and **Node.js worker threads**.
18
18
 
19
- You can use `rimless` to call remote procedures, exchange data or expose local functions with **iframes**/**webworkers**.
19
+ You can use `rimless` to call remote procedures, exchange data or expose local functions with **iframes**, **webworkers**, or **Node.js workers**.
20
20
 
21
21
  You can see it in action in the code sandbox below:
22
22
 
@@ -148,7 +148,7 @@ guest.connect();
148
148
 
149
149
  ### Exposing an API
150
150
 
151
- To do anything meaningful with this connection you need to provide a schema that defines **the API** of the host/iframe/webworker. Any serializeable values as well as functions are ok to use. In the example below the host website provides a function that will update its background color when invoked.
151
+ To do anything meaningful with this connection you need to provide a schema that defines **the API** of the host/iframe/webworker. Any serializable values as well as functions are ok to use. In the example below the host website provides a function that will update its background color when invoked.
152
152
 
153
153
  ```js
154
154
  import { host } from "rimless";
@@ -203,7 +203,7 @@ const api = {
203
203
  */
204
204
  setColor: (color, remote) => {
205
205
  document.body.style.background = color;
206
- remote.logMessage("Background updated ✔︎");
206
+ remote.logMessage("Background updated ");
207
207
  },
208
208
  };
209
209
 
@@ -223,7 +223,7 @@ const api = {
223
223
  const { remote } = await guest.connect(api);
224
224
 
225
225
  // Ask the host to change its background.
226
- // Afterwards, the guest will receive logMessage("Background updated ✔︎").
226
+ // Afterwards, the guest will receive logMessage("Background updated ").
227
227
  remote.setColor("#011627");
228
228
  ```
229
229
 
@@ -259,7 +259,7 @@ Now both can make use of the APIs they have shared with each other, e.g.
259
259
 
260
260
  ## Limitations
261
261
 
262
- All parameters passed through `postMessage` need to be serializeable. This applies also for all return values of the functions you expose.
262
+ All parameters passed through `postMessage` need to be serializable. This applies also for all return values of the functions you expose.
263
263
 
264
264
  ```js
265
265
  // someFunction would return undefined when called in the remote.
@@ -275,9 +275,11 @@ This library is inspired by [Postmate](https://www.npmjs.com/package/postmate) a
275
275
  ### So why does this library exist?
276
276
 
277
277
  - works with webworkers!
278
+ - works with Node.js worker threads
278
279
  - does not create the iframe (easier to work with libraries like react)
279
280
  - works with iframes using srcdoc
280
281
  - works with multiple iframes from the same origin
282
+ - remote RPC handlers receive the caller's API as the last argument
281
283
 
282
284
  ---
283
285
 
@@ -295,11 +297,10 @@ host.connect(iframe, {
295
297
  });
296
298
  ```
297
299
 
298
- | Name | Type | Description | Required |
299
- | --------- | ------------------------------- | ------------------------------------ | -------- |
300
- | `guest` | `HTMLIFrameElement` or `Worker` | Target of the connection | required |
301
- | `schema` | `object` | schema of the api you want to expose | - |
302
- | `options` | `object` | - | - |
300
+ | Name | Type | Description | Required |
301
+ | -------- | ------------------------------- | ------------------------------------ | -------- |
302
+ | `guest` | `HTMLIFrameElement` or `Worker` | Target of the connection | required |
303
+ | `schema` | `object` | schema of the api you want to expose | - |
303
304
 
304
305
  > ### `guest.connect`
305
306
 
@@ -311,10 +312,50 @@ guest.connect({
311
312
  });
312
313
  ```
313
314
 
314
- | Name | Type | Description | Default |
315
- | --------- | -------- | ------------------------------------ | ------- |
316
- | `schema` | `object` | schema of the api you want to expose | - |
317
- | `options` | `object` | - | - |
315
+ | Name | Type | Description | Default |
316
+ | --------------- | -------- | -------------------------------------------- | ------- |
317
+ | `schema` | `object` | schema of the api you want to expose | - |
318
+ | `eventHandlers` | `object` | lifecycle callbacks like `onConnectionSetup` | - |
319
+
320
+ `onConnectionSetup(remote)` is called once the handshake completes. It receives the remote API so you can perform setup logic before using the connection.
321
+
322
+ > ### `withTransferable`
323
+
324
+ Wrap RPC payloads that contain [transferable objects](https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects) so ownership moves between contexts instead of cloning the data. Useful for `ArrayBuffer`, `MessagePort`, `ReadableStream`, and similar types.
325
+
326
+ ```js
327
+ import { withTransferable } from "rimless";
328
+
329
+ const buffer = await file.arrayBuffer();
330
+
331
+ await connection.remote.processFile(
332
+ withTransferable((transfer) => ({
333
+ name: file.name,
334
+ buffer: transfer(buffer), // moves the ArrayBuffer to the worker
335
+ })),
336
+ );
337
+ ```
338
+
339
+ `withTransferable` invokes your callback with a `transfer` helper. Call it for every object you want to mark as transferable; the helper returns the same value so you can build payloads inline. The metadata is attached to the wrapper object without mutating your data.
340
+
341
+ Workers can also return transferables back to the caller:
342
+
343
+ ```js
344
+ import { guest, withTransferable } from "rimless";
345
+
346
+ const api = {
347
+ async processFile({ name, buffer }) {
348
+ const size = buffer.byteLength;
349
+
350
+ return withTransferable((transfer) => ({
351
+ summary: `Processed ${name} (${size} bytes)`,
352
+ buffer: transfer(buffer), // hands ownership back to the host
353
+ }));
354
+ },
355
+ };
356
+
357
+ await guest.connect(api);
358
+ ```
318
359
 
319
360
  ---
320
361
 
package/lib/helpers.d.ts CHANGED
@@ -43,8 +43,9 @@ export declare function getTargetHost(): any;
43
43
  * @param target The target to send the message to
44
44
  * @param message The message to send
45
45
  * @param origin Optional origin for iframe communication
46
+ * @param transferables Optional transferables for postMessage
46
47
  */
47
- export declare function postMessageToTarget(target: Target, message: any, origin?: string): void;
48
+ export declare function postMessageToTarget(target: Target, message: any, origin?: string, transferables?: Transferable[]): void;
48
49
  export declare function isNodeWorker(guest: Guest | Target): guest is NodeWorker;
49
50
  export declare function isWorkerLike(guest: Guest): guest is WorkerLike;
50
51
  export declare function addEventListener(target: Target, event: string, handler: EventListenerOrEventListenerObject): void;
package/lib/host.d.ts CHANGED
@@ -3,7 +3,7 @@ import { Guest, Connection, Schema } from './types';
3
3
  * Perform a handshake with the target iframe, when the handshake is confirmed
4
4
  * resolve the connection object containing RPCs and properties
5
5
  *
6
- * @param iframe
6
+ * @param guest
7
7
  * @param schema
8
8
  * @returns Promise
9
9
  */
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { default as guest } from './guest';
2
2
  import { default as host } from './host';
3
- export { host, guest };
3
+ import { withTransferable } from './rpc';
4
+ export { host, guest, withTransferable };
4
5
  export * from './types';
package/lib/rimless.js CHANGED
@@ -1,252 +1,260 @@
1
- function I() {
1
+ function _() {
2
2
  return typeof window > "u" && typeof self < "u";
3
3
  }
4
- function g() {
5
- var n;
6
- return typeof process < "u" && !!((n = process.versions) != null && n.node);
4
+ function D() {
5
+ return typeof process < "u" && !!process.versions?.node;
7
6
  }
8
- function J() {
7
+ function K() {
9
8
  return window.self !== window.top;
10
9
  }
11
- function _(n) {
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]);
10
+ function k(n) {
11
+ const r = {};
12
+ return function o(e, s = "") {
13
+ Object.keys(e).forEach((t) => {
14
+ const c = s ? `${s}.${t}` : t;
15
+ e[t] === Object(e[t]) && o(e[t], c), typeof e[t] == "function" && (r[c] = e[t], delete e[t]);
17
16
  });
18
- }(n), o;
17
+ }(n), r;
19
18
  }
20
- const G = /^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/, K = { "http:": "80", "https:": "443" };
21
- function U(n) {
19
+ const U = /^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/, W = { "http:": "80", "https:": "443" };
20
+ function Q(n) {
22
21
  if (!n) return null;
23
- const o = G.exec(n);
24
- if (!o) return null;
25
- const [, t = "http:", e, , s] = o;
26
- if (t === "file:")
22
+ const r = U.exec(n);
23
+ if (!r) return null;
24
+ const [, o = "http:", e, , s] = r;
25
+ if (o === "file:")
27
26
  return "file://";
28
- const r = s && s !== K[t] ? `:${s}` : "";
29
- return `${t}//${e}${r}`;
27
+ const t = s && s !== W[o] ? `:${s}` : "";
28
+ return `${o}//${e}${t}`;
30
29
  }
31
- function W(n, o, t) {
30
+ function Y(n, r, o) {
32
31
  if (!n || typeof n != "object") return n;
33
- const e = Array.isArray(o) ? o : o.split(".").map((r) => r.match(/^\d+$/) ? Number(r) : r);
32
+ const e = Array.isArray(r) ? r : r.split(".").map((t) => t.match(/^\d+$/) ? Number(t) : t);
34
33
  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]);
34
+ for (let t = 0; t < e.length; t++) {
35
+ const c = e[t];
36
+ t === e.length - 1 ? s[c] = o : ((!s[c] || typeof s[c] != "object") && (s[c] = typeof e[t + 1] == "number" ? [] : {}), s = s[c]);
38
37
  }
39
38
  return n;
40
39
  }
41
- function T(n = 10) {
42
- const o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
43
- let t = "";
40
+ function C(n = 10) {
41
+ const r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
42
+ let o = "";
44
43
  for (let e = 0; e < n; e++)
45
- t += o.charAt(Math.floor(Math.random() * o.length));
46
- return t;
44
+ o += r.charAt(Math.floor(Math.random() * r.length));
45
+ return o;
47
46
  }
48
47
  let y = null;
49
- if (g())
48
+ if (D())
50
49
  try {
51
50
  y = require("worker_threads").parentPort;
52
51
  } catch {
53
52
  }
54
- function Q() {
55
- if (g())
53
+ function $() {
54
+ if (D())
56
55
  return y;
57
- if (I())
56
+ if (_())
58
57
  return self;
59
- if (J())
58
+ if (K())
60
59
  return window.parent;
61
60
  throw new Error("No valid target found for postMessage");
62
61
  }
63
- function w(n, o, t) {
62
+ function g(n, r, o, e) {
64
63
  if (!n)
65
64
  throw new Error("Rimless Error: No target specified for postMessage");
66
- if (g() && n === y) {
67
- n.postMessage(JSON.parse(JSON.stringify(o)));
65
+ if (D() && n === y) {
66
+ n.postMessage(r, { transfer: e });
68
67
  return;
69
68
  }
70
- if (I()) {
71
- n.postMessage(JSON.parse(JSON.stringify(o)));
69
+ if (_()) {
70
+ n.postMessage(r, { transfer: e });
72
71
  return;
73
72
  }
74
73
  if (n.postMessage) {
75
- n.postMessage(JSON.parse(JSON.stringify(o)), { targetOrigin: t || "*" });
74
+ n.postMessage(r, { targetOrigin: o || "*", transfer: e });
76
75
  return;
77
76
  }
78
77
  throw new Error("Rimless Error: Invalid target for postMessage");
79
78
  }
80
- function L(n) {
79
+ function H(n) {
81
80
  return y !== null && n === y;
82
81
  }
83
- function $(n) {
84
- return L(n) || typeof Worker < "u" && n instanceof Worker;
82
+ function x(n) {
83
+ return H(n) || typeof Worker < "u" && n instanceof Worker;
85
84
  }
86
- function M(n, o, t) {
87
- L(n) ? n.on(o, t) : "addEventListener" in n && n.addEventListener(o, t);
85
+ function L(n, r, o) {
86
+ H(n) ? n.on(r, o) : "addEventListener" in n && n.addEventListener(r, o);
88
87
  }
89
- function O(n, o, t) {
90
- L(n) ? n.off(o, t) : "removeEventListener" in n && n.removeEventListener(o, t);
88
+ function N(n, r, o) {
89
+ H(n) ? n.off(r, o) : "removeEventListener" in n && n.removeEventListener(r, o);
91
90
  }
92
91
  function P(n) {
93
92
  return n.data || n;
94
93
  }
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 = {
103
- action: a.RPC_RESOLVE,
94
+ var h = /* @__PURE__ */ ((n) => (n.MESSAGE = "message", n))(h || {}), u = /* @__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))(u || {});
95
+ const w = Symbol();
96
+ function O(n = {}, r, o, e, s) {
97
+ const t = [];
98
+ for (const [c, f] of Object.entries(n)) {
99
+ async function a(i) {
100
+ const E = P(i), { action: m, callID: R, connectionID: l, callName: d, args: p = [] } = E;
101
+ if (m !== u.RPC_REQUEST || !R || !d || d !== c || l !== r) return;
102
+ const S = {
103
+ action: u.RPC_RESOLVE,
104
104
  callID: R,
105
- callName: h,
106
- connectionID: p,
105
+ callName: d,
106
+ connectionID: l,
107
107
  error: null,
108
108
  result: null
109
109
  };
110
+ let A;
110
111
  try {
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)));
112
+ S.result = await f(...p, s), S.result && S.result[w] && (A = S.result[w] ?? [], delete S.result[w]);
113
+ } catch (I) {
114
+ S.action = u.RPC_REJECT, S.error = JSON.parse(JSON.stringify(I, Object.getOwnPropertyNames(I)));
115
115
  }
116
- w(e, E, i == null ? void 0 : i.origin);
116
+ g(e, S, i?.origin, A);
117
117
  }
118
- M(t, d.MESSAGE, f), r.push(() => O(t, d.MESSAGE, f));
118
+ L(o, h.MESSAGE, a), t.push(() => N(o, h.MESSAGE, a));
119
119
  }
120
- return () => r.forEach((c) => c());
121
- }
122
- function x(n, o, t, e = [], s, r) {
123
- return (...c) => new Promise((l, f) => {
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);
120
+ return () => t.forEach((c) => c());
121
+ }
122
+ function J(n, r, o, e = [], s, t) {
123
+ return (...c) => new Promise((f, a) => {
124
+ const i = C();
125
+ function E(l) {
126
+ const d = P(l), { callID: p, connectionID: S, callName: A, result: I, error: G, action: T } = d;
127
+ if (!(!p || !A) && A === n && p === i && S === r) {
128
+ if (T === u.RPC_RESOLVE) return f(I);
129
+ if (T === u.RPC_REJECT) return a(G);
130
130
  }
131
131
  }
132
132
  const m = {
133
- action: a.RPC_REQUEST,
134
- args: JSON.parse(JSON.stringify(c)),
133
+ action: u.RPC_REQUEST,
134
+ args: c,
135
135
  callID: i,
136
136
  callName: n,
137
- connectionID: o
138
- };
139
- M(s, d.MESSAGE, u), e.push(() => O(s, d.MESSAGE, u)), w(r, m, t == null ? void 0 : t.origin);
137
+ connectionID: r
138
+ }, R = c.reduce(
139
+ (l, d) => d[w]?.length ? l.concat(d[w]) : l,
140
+ // @ts-expect-error: we know this is an array of transferables (if it exists)
141
+ c[w] ?? []
142
+ );
143
+ L(s, h.MESSAGE, E), e.push(() => N(s, h.MESSAGE, E)), g(t, m, o?.origin, R);
140
144
  });
141
145
  }
142
- function C(n = {}, o = [], t, e, s, r) {
143
- const c = { ...n }, l = [];
144
- for (const f of o) {
145
- const i = x(f, t, e, l, s, r);
146
- W(c, f, i);
146
+ function v(n = {}, r = [], o, e, s, t) {
147
+ const c = { ...n }, f = [];
148
+ for (const a of r) {
149
+ const i = J(a, o, e, f, s, t);
150
+ Y(c, a, i);
147
151
  }
148
152
  return {
149
153
  remote: c,
150
- unregisterRemote: () => l.forEach((f) => f())
154
+ unregisterRemote: () => f.forEach((a) => a())
151
155
  };
152
156
  }
153
- function Y(n = {}, o) {
154
- return new Promise(async (t) => {
155
- const e = _(n), s = Q(), r = self || window;
156
- async function c(f) {
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(
157
+ const F = (n) => {
158
+ const r = [], e = n((s) => (r.push(s), s));
159
+ return Object.assign(e, { [w]: r });
160
+ };
161
+ function V(n = {}, r) {
162
+ return new Promise(async (o) => {
163
+ const e = k(n), s = $(), t = self || window;
164
+ async function c(a) {
165
+ const i = P(a);
166
+ if (i?.action !== u.HANDSHAKE_REPLY) return;
167
+ const { remote: E, unregisterRemote: m } = v(
161
168
  i.schema,
162
169
  i.methodNames,
163
170
  i.connectionID,
164
- f,
165
- r,
171
+ a,
172
+ t,
166
173
  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 = {
170
- action: a.HANDSHAKE_REPLY,
174
+ ), R = O(e, i.connectionID, t, s, E);
175
+ await r?.onConnectionSetup?.(E);
176
+ const l = {
177
+ action: u.HANDSHAKE_REPLY,
171
178
  connectionID: i.connectionID
172
179
  };
173
- w(s, p, f == null ? void 0 : f.origin);
174
- const N = { remote: u, close: () => {
175
- O(r, d.MESSAGE, c), m(), R();
180
+ g(s, l, a?.origin);
181
+ const p = { remote: E, close: () => {
182
+ N(t, h.MESSAGE, c), m(), R();
176
183
  }, id: i.connectionID };
177
- return t(N);
184
+ return o(p);
178
185
  }
179
- M(r, d.MESSAGE, c);
180
- const l = {
181
- action: a.HANDSHAKE_REQUEST,
186
+ L(t, h.MESSAGE, c);
187
+ const f = {
188
+ action: u.HANDSHAKE_REQUEST,
182
189
  methodNames: Object.keys(e),
183
190
  schema: n
184
191
  };
185
- w(s, l);
192
+ g(s, f);
186
193
  });
187
194
  }
188
- const F = {
189
- connect: Y
190
- }, A = {};
191
- function V(n, o) {
192
- if (L(n) || typeof Worker < "u" && n instanceof Worker)
195
+ const B = {
196
+ connect: V
197
+ }, M = {};
198
+ function b(n, r) {
199
+ if (H(n) || typeof Worker < "u" && n instanceof Worker)
193
200
  return !0;
194
- const t = n;
201
+ const o = n;
195
202
  try {
196
- const e = t.src, s = U(e), r = o.origin === s, c = o.source === t.contentWindow;
197
- return r && c || !e;
203
+ const e = o.src, s = typeof o.srcdoc == "string" && o.srcdoc.length > 0, t = Q(e), c = r.origin === t, f = r.source === o.contentWindow;
204
+ return s || e === "about:blank" ? f : c && f || !e;
198
205
  } catch (e) {
199
206
  return console.warn("Error checking iframe target:", e), !1;
200
207
  }
201
208
  }
202
- function q(n, o = {}) {
209
+ function q(n, r = {}) {
203
210
  if (!n) throw new Error("a target is required");
204
- const t = $(n), e = t || g() ? n : window;
211
+ const o = x(n), e = o || D() ? n : window;
205
212
  return new Promise((s) => {
206
- const r = T();
207
- function c(f) {
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,
216
- f,
213
+ const t = C();
214
+ function c(a) {
215
+ const i = o || D() ? n : a.source;
216
+ if (!o && !D() && !b(n, a)) return;
217
+ const E = P(a);
218
+ if (E?.action !== u.HANDSHAKE_REQUEST || M[t]) return;
219
+ const m = k(r), { remote: R, unregisterRemote: l } = v(
220
+ E.schema,
221
+ E.methodNames,
222
+ t,
223
+ a,
217
224
  e,
218
225
  i
219
- ), h = k(m, r, e, i, R), N = {
220
- action: a.HANDSHAKE_REPLY,
221
- connectionID: r,
222
- schema: o,
226
+ ), d = O(m, t, e, i, R), p = {
227
+ action: u.HANDSHAKE_REPLY,
228
+ connectionID: t,
229
+ schema: r,
223
230
  methodNames: Object.keys(m)
224
231
  };
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;
232
+ g(i, p, a.origin);
233
+ const A = { remote: R, close: () => {
234
+ delete M[t], N(e, h.MESSAGE, c), N(e, h.MESSAGE, f), l(), d(), o && n.terminate();
235
+ }, id: t };
236
+ M[t] = A;
230
237
  }
231
- M(e, d.MESSAGE, c);
232
- function l(f) {
233
- const i = P(f);
234
- if ((i == null ? void 0 : i.action) === a.HANDSHAKE_REPLY && r === i.connectionID) {
235
- if (!A[i.connectionID])
238
+ L(e, h.MESSAGE, c);
239
+ function f(a) {
240
+ const i = P(a);
241
+ if (i?.action === u.HANDSHAKE_REPLY && t === i.connectionID) {
242
+ if (!M[i.connectionID])
236
243
  throw new Error("Rimless Error: No connection found for this connectionID");
237
- return s(A[i.connectionID]);
244
+ return s(M[i.connectionID]);
238
245
  }
239
246
  }
240
- M(e, d.MESSAGE, l);
247
+ L(e, h.MESSAGE, f);
241
248
  });
242
249
  }
243
- const b = {
250
+ const z = {
244
251
  connect: q
245
252
  };
246
253
  export {
247
- a as actions,
248
- d as events,
249
- F as guest,
250
- b as host
254
+ u as actions,
255
+ h as events,
256
+ B as guest,
257
+ z as host,
258
+ F as withTransferable
251
259
  };
252
260
  //# 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 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
+ {"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 * @param transferables Optional transferables for postMessage\n */\nexport function postMessageToTarget(\n target: Target,\n message: any,\n origin?: string,\n transferables?: Transferable[],\n): 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(message, { transfer: transferables });\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(message, { transfer: transferables });\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(message, { targetOrigin: origin || \"*\", transfer: transferables });\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/** Private symbol to which we will assign transferable objects */\nconst SYM_TRANSFERABLES = Symbol();\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 // when a host function returns transferable results to the remote, the\n // transferables are assigned to a special symbol on each function's result\n let transferables: Transferable[] | undefined = undefined;\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 payload.result = await method(...args, remote);\n\n if (payload.result && payload.result[SYM_TRANSFERABLES]) {\n transferables = payload.result[SYM_TRANSFERABLES] ?? [];\n delete payload.result[SYM_TRANSFERABLES];\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, transferables);\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,\n callID: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n // if the arguments have transferables, post them as well\n const transferables = args.reduce(\n (transferables, arg) =>\n arg[SYM_TRANSFERABLES]?.length ? transferables.concat(arg[SYM_TRANSFERABLES]) : transferables,\n // @ts-expect-error: we know this is an array of transferables (if it exists)\n args[SYM_TRANSFERABLES] ?? []\n );\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, payload, event?.origin, transferables);\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\n/**\n * This function is used by API schema declarations and remote function calls alike to\n * indicate which variables should be declared as transferable over `postMessage` calls.\n *\n * @param cb a function that takes a transfer function as an argument and returns an object\n * (in the loose, `typeof foo === \"object\"` sense)\n * @return result the callback's return value, with an extra array of transferable objects\n * assigned to rimless' private symbol `SYM_TRANSFERABLES`\n *\n * The `transfer(...)` function is called with an object to transfer; if there\n * are many objects to transfer, you may call it multiple times. It will always\n * return the input object. Calling `transfer` only modifies the callback\n * result, not the transferred object itself (or objects themselves).\n *\n * @example\n * host.connect({\n * foo: (...args) => {\n * const foo = new ArrayBuffer(8);\n * return withTransferable((transfer) => transfer(foo));\n * }),\n * });\n *\n * @example\n * host.remote.foo(withTransferable((transfer) => ({\n * stream: transfer(new ReadableStream()),\n * })));\n */\nexport const withTransferable = <Transferable, Result extends object>(\n cb: (transfer: <T extends Transferable>(transferable: T) => T) => Result\n) => {\n const transferables: Transferable[] = [];\n const transfer = <T extends Transferable>(transferable: T) => {\n transferables.push(transferable);\n return transferable;\n };\n\n const result = cb(transfer);\n\n return Object.assign(result, { [SYM_TRANSFERABLES]: transferables });\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 hasInlineContent = typeof iframe.srcdoc === \"string\" && iframe.srcdoc.length > 0;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n // For inline iframes (srcdoc/about:blank) we can only rely on source matching\n if (hasInlineContent || childURL === \"about:blank\") {\n return hasProperSource;\n }\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 guest\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 removeEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\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","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","transferables","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","SYM_TRANSFERABLES","registerLocalMethods","rpcConnectionID","listenTo","sendTo","remote","listeners","methodName","method","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","arg","registerRemoteMethods","schema","methodNames","rpc","withTransferable","cb","transferable","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterRemote","unregisterLocal","connection","connections","isValidTarget","iframe","childURL","hasInlineContent","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
@@ -1,2 +1,2 @@
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}({});
1
+ var rimless=function(w){"use strict";function _(){return typeof window>"u"&&typeof self<"u"}function p(){return typeof process<"u"&&!!process.versions?.node}function K(){return window.self!==window.top}function k(n){const r={};return function o(e,s=""){Object.keys(e).forEach(t=>{const c=s?`${s}.${t}`:t;e[t]===Object(e[t])&&o(e[t],c),typeof e[t]=="function"&&(r[c]=e[t],delete e[t])})}(n),r}const U=/^(https?:|file:)?\/\/([^/:]+)?(:(\d+))?/,W={"http:":"80","https:":"443"};function Q(n){if(!n)return null;const r=U.exec(n);if(!r)return null;const[,o="http:",e,,s]=r;if(o==="file:")return"file://";const t=s&&s!==W[o]?`:${s}`:"";return`${o}//${e}${t}`}function b(n,r,o){if(!n||typeof n!="object")return n;const e=Array.isArray(r)?r:r.split(".").map(t=>t.match(/^\d+$/)?Number(t):t);let s=n;for(let t=0;t<e.length;t++){const c=e[t];t===e.length-1?s[c]=o:((!s[c]||typeof s[c]!="object")&&(s[c]=typeof e[t+1]=="number"?[]:{}),s=s[c])}return n}function O(n=10){const r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let o="";for(let e=0;e<n;e++)o+=r.charAt(Math.floor(Math.random()*r.length));return o}let y=null;if(p())try{y=require("worker_threads").parentPort}catch{}function Y(){if(p())return y;if(_())return self;if(K())return window.parent;throw new Error("No valid target found for postMessage")}function D(n,r,o,e){if(!n)throw new Error("Rimless Error: No target specified for postMessage");if(p()&&n===y){n.postMessage(r,{transfer:e});return}if(_()){n.postMessage(r,{transfer:e});return}if(n.postMessage){n.postMessage(r,{targetOrigin:o||"*",transfer:e});return}throw new Error("Rimless Error: Invalid target for postMessage")}function H(n){return y!==null&&n===y}function $(n){return H(n)||typeof Worker<"u"&&n instanceof Worker}function P(n,r,o){H(n)?n.on(r,o):"addEventListener"in n&&n.addEventListener(r,o)}function L(n,r,o){H(n)?n.off(r,o):"removeEventListener"in n&&n.removeEventListener(r,o)}function N(n){return n.data||n}var l=(n=>(n.MESSAGE="message",n))(l||{}),f=(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))(f||{});const M=Symbol();function C(n={},r,o,e,s){const t=[];for(const[c,u]of Object.entries(n)){async function a(i){const E=N(i),{action:m,callID:R,connectionID:d,callName:S,args:A=[]}=E;if(m!==f.RPC_REQUEST||!R||!S||S!==c||d!==r)return;const h={action:f.RPC_RESOLVE,callID:R,callName:S,connectionID:d,error:null,result:null};let g;try{h.result=await u(...A,s),h.result&&h.result[M]&&(g=h.result[M]??[],delete h.result[M])}catch(T){h.action=f.RPC_REJECT,h.error=JSON.parse(JSON.stringify(T,Object.getOwnPropertyNames(T)))}D(e,h,i?.origin,g)}P(o,l.MESSAGE,a),t.push(()=>L(o,l.MESSAGE,a))}return()=>t.forEach(c=>c())}function J(n,r,o,e=[],s,t){return(...c)=>new Promise((u,a)=>{const i=O();function E(d){const S=N(d),{callID:A,connectionID:h,callName:g,result:T,error:X,action:G}=S;if(!(!A||!g)&&g===n&&A===i&&h===r){if(G===f.RPC_RESOLVE)return u(T);if(G===f.RPC_REJECT)return a(X)}}const m={action:f.RPC_REQUEST,args:c,callID:i,callName:n,connectionID:r},R=c.reduce((d,S)=>S[M]?.length?d.concat(S[M]):d,c[M]??[]);P(s,l.MESSAGE,E),e.push(()=>L(s,l.MESSAGE,E)),D(t,m,o?.origin,R)})}function v(n={},r=[],o,e,s,t){const c={...n},u=[];for(const a of r){const i=J(a,o,e,u,s,t);b(c,a,i)}return{remote:c,unregisterRemote:()=>u.forEach(a=>a())}}const V=n=>{const r=[],e=n(s=>(r.push(s),s));return Object.assign(e,{[M]:r})};function q(n={},r){return new Promise(async o=>{const e=k(n),s=Y(),t=self||window;async function c(a){const i=N(a);if(i?.action!==f.HANDSHAKE_REPLY)return;const{remote:E,unregisterRemote:m}=v(i.schema,i.methodNames,i.connectionID,a,t,s),R=C(e,i.connectionID,t,s,E);await r?.onConnectionSetup?.(E);const d={action:f.HANDSHAKE_REPLY,connectionID:i.connectionID};D(s,d,a?.origin);const A={remote:E,close:()=>{L(t,l.MESSAGE,c),m(),R()},id:i.connectionID};return o(A)}P(t,l.MESSAGE,c);const u={action:f.HANDSHAKE_REQUEST,methodNames:Object.keys(e),schema:n};D(s,u)})}const F={connect:q},I={};function B(n,r){if(H(n)||typeof Worker<"u"&&n instanceof Worker)return!0;const o=n;try{const e=o.src,s=typeof o.srcdoc=="string"&&o.srcdoc.length>0,t=Q(e),c=r.origin===t,u=r.source===o.contentWindow;return s||e==="about:blank"?u:c&&u||!e}catch(e){return console.warn("Error checking iframe target:",e),!1}}function j(n,r={}){if(!n)throw new Error("a target is required");const o=$(n),e=o||p()?n:window;return new Promise(s=>{const t=O();function c(a){const i=o||p()?n:a.source;if(!o&&!p()&&!B(n,a))return;const E=N(a);if(E?.action!==f.HANDSHAKE_REQUEST||I[t])return;const m=k(r),{remote:R,unregisterRemote:d}=v(E.schema,E.methodNames,t,a,e,i),S=C(m,t,e,i,R),A={action:f.HANDSHAKE_REPLY,connectionID:t,schema:r,methodNames:Object.keys(m)};D(i,A,a.origin);const g={remote:R,close:()=>{delete I[t],L(e,l.MESSAGE,c),L(e,l.MESSAGE,u),d(),S(),o&&n.terminate()},id:t};I[t]=g}P(e,l.MESSAGE,c);function u(a){const i=N(a);if(i?.action===f.HANDSHAKE_REPLY&&t===i.connectionID){if(!I[i.connectionID])throw new Error("Rimless Error: No connection found for this connectionID");return s(I[i.connectionID])}}P(e,l.MESSAGE,u)})}const z={connect:j};return w.actions=f,w.events=l,w.guest=F,w.host=z,w.withTransferable=V,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"}),w}({});
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 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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"}
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 * @param transferables Optional transferables for postMessage\n */\nexport function postMessageToTarget(\n target: Target,\n message: any,\n origin?: string,\n transferables?: Transferable[],\n): 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(message, { transfer: transferables });\n return;\n }\n\n // Web Worker\n if (isWorker()) {\n target.postMessage(message, { transfer: transferables });\n return;\n }\n\n // iframe or window\n if (target.postMessage) {\n target.postMessage(message, { targetOrigin: origin || \"*\", transfer: transferables });\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/** Private symbol to which we will assign transferable objects */\nconst SYM_TRANSFERABLES = Symbol();\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 // when a host function returns transferable results to the remote, the\n // transferables are assigned to a special symbol on each function's result\n let transferables: Transferable[] | undefined = undefined;\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 payload.result = await method(...args, remote);\n\n if (payload.result && payload.result[SYM_TRANSFERABLES]) {\n transferables = payload.result[SYM_TRANSFERABLES] ?? [];\n delete payload.result[SYM_TRANSFERABLES];\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, transferables);\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,\n callID: requestID,\n callName: rpcCallName,\n connectionID: rpcConnectionID,\n };\n\n // if the arguments have transferables, post them as well\n const transferables = args.reduce(\n (transferables, arg) =>\n arg[SYM_TRANSFERABLES]?.length ? transferables.concat(arg[SYM_TRANSFERABLES]) : transferables,\n // @ts-expect-error: we know this is an array of transferables (if it exists)\n args[SYM_TRANSFERABLES] ?? []\n );\n\n addEventListener(listenTo, events.MESSAGE, handleResponse);\n listeners.push(() => removeEventListener(listenTo, events.MESSAGE, handleResponse));\n\n postMessageToTarget(sendTo, payload, event?.origin, transferables);\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\n/**\n * This function is used by API schema declarations and remote function calls alike to\n * indicate which variables should be declared as transferable over `postMessage` calls.\n *\n * @param cb a function that takes a transfer function as an argument and returns an object\n * (in the loose, `typeof foo === \"object\"` sense)\n * @return result the callback's return value, with an extra array of transferable objects\n * assigned to rimless' private symbol `SYM_TRANSFERABLES`\n *\n * The `transfer(...)` function is called with an object to transfer; if there\n * are many objects to transfer, you may call it multiple times. It will always\n * return the input object. Calling `transfer` only modifies the callback\n * result, not the transferred object itself (or objects themselves).\n *\n * @example\n * host.connect({\n * foo: (...args) => {\n * const foo = new ArrayBuffer(8);\n * return withTransferable((transfer) => transfer(foo));\n * }),\n * });\n *\n * @example\n * host.remote.foo(withTransferable((transfer) => ({\n * stream: transfer(new ReadableStream()),\n * })));\n */\nexport const withTransferable = <Transferable, Result extends object>(\n cb: (transfer: <T extends Transferable>(transferable: T) => T) => Result\n) => {\n const transferables: Transferable[] = [];\n const transfer = <T extends Transferable>(transferable: T) => {\n transferables.push(transferable);\n return transferable;\n };\n\n const result = cb(transfer);\n\n return Object.assign(result, { [SYM_TRANSFERABLES]: transferables });\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 hasInlineContent = typeof iframe.srcdoc === \"string\" && iframe.srcdoc.length > 0;\n const childOrigin = getOriginFromURL(childURL);\n const hasProperOrigin = event.origin === childOrigin;\n const hasProperSource = event.source === iframe.contentWindow;\n\n // For inline iframes (srcdoc/about:blank) we can only rely on source matching\n if (hasInlineContent || childURL === \"about:blank\") {\n return hasProperSource;\n }\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 guest\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 removeEventListener(listenTo, events.MESSAGE, handleHandshakeReply);\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","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","transferables","isNodeWorker","guest","isWorkerLike","addEventListener","event","handler","removeEventListener","getEventData","events","actions","SYM_TRANSFERABLES","registerLocalMethods","rpcConnectionID","listenTo","sendTo","remote","listeners","methodName","method","handleCall","eventData","action","callID","connectionID","callName","args","payload","error","unregister","createRPC","rpcCallName","resolve","reject","requestID","handleResponse","arg","registerRemoteMethods","schema","methodNames","rpc","withTransferable","cb","transferable","connect","eventHandlers","localMethods","handleHandshakeResponse","unregisterRemote","unregisterLocal","connection","connections","isValidTarget","iframe","childURL","hasInlineContent","childOrigin","hasProperOrigin","hasProperSource","guestIsWorker","handleHandshake","handleHandshakeReply","host"],"mappings":"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"}
package/lib/rpc.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Environment, RimlessEvent, Schema, Target } from './types';
2
+ /** Private symbol to which we will assign transferable objects */
3
+ declare const SYM_TRANSFERABLES: unique symbol;
2
4
  /**
3
5
  * for each function in methods
4
6
  * 1. subscribe to an event that the remote can call
@@ -24,7 +26,7 @@ export declare function registerLocalMethods(methods: Record<string, (...args: a
24
26
  *
25
27
  * @returns a promise with the result of the RPC
26
28
  */
27
- export declare function createRPC(rpcCallName: string, rpcConnectionID: string, event: RimlessEvent, listeners: Array<() => void> | undefined, listenTo: Environment, sendTo: Target): (...args: any) => Promise<unknown>;
29
+ export declare function createRPC(rpcCallName: string, rpcConnectionID: string, event: RimlessEvent, listeners: Array<() => void> | undefined, listenTo: Environment, sendTo: Target): (...args: any[]) => Promise<unknown>;
28
30
  /**
29
31
  * create an object based on the remote schema's methods. Functions in that object will
30
32
  * emit an event that will trigger the RPC on the remote.
@@ -41,3 +43,34 @@ export declare function registerRemoteMethods(schema: Schema | undefined, method
41
43
  };
42
44
  unregisterRemote: () => void;
43
45
  };
46
+ /**
47
+ * This function is used by API schema declarations and remote function calls alike to
48
+ * indicate which variables should be declared as transferable over `postMessage` calls.
49
+ *
50
+ * @param cb a function that takes a transfer function as an argument and returns an object
51
+ * (in the loose, `typeof foo === "object"` sense)
52
+ * @return result the callback's return value, with an extra array of transferable objects
53
+ * assigned to rimless' private symbol `SYM_TRANSFERABLES`
54
+ *
55
+ * The `transfer(...)` function is called with an object to transfer; if there
56
+ * are many objects to transfer, you may call it multiple times. It will always
57
+ * return the input object. Calling `transfer` only modifies the callback
58
+ * result, not the transferred object itself (or objects themselves).
59
+ *
60
+ * @example
61
+ * host.connect({
62
+ * foo: (...args) => {
63
+ * const foo = new ArrayBuffer(8);
64
+ * return withTransferable((transfer) => transfer(foo));
65
+ * }),
66
+ * });
67
+ *
68
+ * @example
69
+ * host.remote.foo(withTransferable((transfer) => ({
70
+ * stream: transfer(new ReadableStream()),
71
+ * })));
72
+ */
73
+ export declare const withTransferable: <Transferable, Result extends object>(cb: (transfer: <T extends Transferable>(transferable: T) => T) => Result) => Result & {
74
+ [SYM_TRANSFERABLES]: Transferable[];
75
+ };
76
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rimless",
3
3
  "author": "Aurélien Franky",
4
- "version": "0.6.0",
4
+ "version": "0.7.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`",
@@ -15,10 +15,11 @@
15
15
  "scripts": {
16
16
  "dev": "vite",
17
17
  "format": "npx prettier --check \"./src/**/*.{js,jsx,ts,tsx,css,scss,html,md}\" --write",
18
- "build": "tsc -b && vite build",
18
+ "build": "tsc -b && tsc --noEmit -p tsconfig.test.json && vite build",
19
19
  "prepublishOnly": "npm run build",
20
20
  "lint": "eslint . --ignore-pattern='lib/*' --ignore-pattern='lib/**' --ignore-pattern '**/*.min.js'",
21
21
  "test": "vitest run",
22
+ "test:coverage": "vitest run --coverage",
22
23
  "preview": "vite preview",
23
24
  "storybook": "storybook dev -p 6006",
24
25
  "build-storybook": "storybook build",
@@ -29,32 +30,30 @@
29
30
  "url": "https://github.com/au-re/rimless.git"
30
31
  },
31
32
  "devDependencies": {
32
- "@chromatic-com/storybook": "^1.7.0",
33
- "@eslint/js": "^9.9.0",
34
- "@storybook/addon-essentials": "^8.2.9",
35
- "@storybook/addon-interactions": "^8.2.9",
36
- "@storybook/addon-links": "^8.2.9",
37
- "@storybook/addon-onboarding": "^8.2.9",
38
- "@storybook/blocks": "^8.2.9",
39
- "@storybook/react": "^8.2.9",
40
- "@storybook/react-vite": "^8.2.9",
41
- "@storybook/test": "^8.2.9",
33
+ "@chromatic-com/storybook": "^4.1.1",
34
+ "@eslint/js": "^9.38.0",
35
+ "@storybook/addon-docs": "^9.1.15",
36
+ "@storybook/addon-links": "^9.1.15",
37
+ "@storybook/addon-onboarding": "^9.1.15",
38
+ "@storybook/react-vite": "^9.1.15",
42
39
  "@types/lodash.set": "^4.3.9",
43
- "@types/node": "^22.7.5",
44
- "@vitejs/plugin-react": "^4.3.1",
45
- "eslint": "^9.12.0",
46
- "globals": "^15.11.0",
47
- "happy-dom": "^15.7.4",
48
- "prettier": "^3.5.3",
49
- "storybook": "^8.2.9",
50
- "terser": "^5.34.1",
51
- "typescript": "^5.5.3",
52
- "typescript-eslint": "^8.8.1",
53
- "vite": "^6.3.3",
54
- "vite-plugin-dts": "^4.2.4",
55
- "vitest": "^2.0.5"
40
+ "@types/node": "^24.9.1",
41
+ "@vitejs/plugin-react": "^5.1.0",
42
+ "@vitest/coverage-v8": "^4.0.3",
43
+ "eslint": "^9.38.0",
44
+ "eslint-plugin-storybook": "9.1.15",
45
+ "globals": "^16.4.0",
46
+ "happy-dom": "^20.0.8",
47
+ "prettier": "^3.6.2",
48
+ "storybook": "^9.1.15",
49
+ "terser": "^5.44.0",
50
+ "typescript": "^5.9.3",
51
+ "typescript-eslint": "^8.46.2",
52
+ "vite": "^7.1.12",
53
+ "vite-plugin-dts": "^4.5.4",
54
+ "vitest": "^4.0.3"
56
55
  },
57
56
  "volta": {
58
- "node": "18.19.0"
57
+ "node": "22.21.0"
59
58
  }
60
59
  }