@mongosh/node-runtime-worker-thread 2.3.0 → 2.3.2

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.
Files changed (58) hide show
  1. package/AUTHORS +1 -0
  2. package/dist/153.js +1 -0
  3. package/dist/41.js +1 -0
  4. package/dist/502.js +1 -0
  5. package/dist/{578.js → 503.js} +1 -1
  6. package/dist/{43.js → 527.js} +1 -1
  7. package/dist/534.js +1 -0
  8. package/dist/711.js +1 -0
  9. package/dist/739.js +1 -0
  10. package/dist/index.d.ts +6 -8
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/report.html +2 -2
  14. package/dist/rpc.d.ts +6 -15
  15. package/dist/rpc.js +7 -71
  16. package/dist/rpc.js.map +1 -1
  17. package/dist/worker-process-mongosh-bus.d.ts +7 -0
  18. package/dist/{child-process-mongosh-bus.js → worker-process-mongosh-bus.js} +8 -8
  19. package/dist/worker-process-mongosh-bus.js.map +1 -0
  20. package/dist/worker-runtime.js +16 -16
  21. package/dist/worker-runtime.js.map +1 -1
  22. package/dist/{child-process-evaluation-listener.d.ts → worker-thread-evaluation-listener.d.ts} +2 -4
  23. package/dist/{child-process-evaluation-listener.js → worker-thread-evaluation-listener.js} +6 -6
  24. package/dist/worker-thread-evaluation-listener.js.map +1 -0
  25. package/package.json +13 -12
  26. package/src/index.spec.ts +148 -147
  27. package/src/index.ts +98 -120
  28. package/src/lock.spec.ts +1 -1
  29. package/src/rpc.spec.ts +24 -90
  30. package/src/rpc.ts +17 -98
  31. package/src/{child-process-mongosh-bus.ts → worker-process-mongosh-bus.ts} +5 -6
  32. package/src/worker-runtime.spec.ts +19 -29
  33. package/src/worker-runtime.ts +15 -22
  34. package/src/{child-process-evaluation-listener.ts → worker-thread-evaluation-listener.ts} +3 -4
  35. package/tests/register-worker.js +1 -0
  36. package/tsconfig.json +2 -1
  37. package/webpack.config.js +4 -6
  38. package/__fixtures__/script-that-throws.js +0 -1
  39. package/dist/354.js +0 -1
  40. package/dist/528.js +0 -1
  41. package/dist/650.js +0 -1
  42. package/dist/722.js +0 -1
  43. package/dist/777.js +0 -1
  44. package/dist/942.js +0 -1
  45. package/dist/child-process-evaluation-listener.js.map +0 -1
  46. package/dist/child-process-mongosh-bus.d.ts +0 -9
  47. package/dist/child-process-mongosh-bus.js.map +0 -1
  48. package/dist/child-process-proxy.d.ts +0 -1
  49. package/dist/child-process-proxy.js +0 -1
  50. package/dist/child-process-proxy.js.map +0 -1
  51. package/dist/spawn-child-from-source.d.ts +0 -5
  52. package/dist/spawn-child-from-source.js +0 -74
  53. package/dist/spawn-child-from-source.js.map +0 -1
  54. package/src/child-process-proxy.spec.ts +0 -84
  55. package/src/child-process-proxy.ts +0 -124
  56. package/src/spawn-child-from-source.spec.ts +0 -90
  57. package/src/spawn-child-from-source.ts +0 -102
  58. package/tsconfig.test.json +0 -11
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  /* istanbul ignore file */
2
- /* ^^^ we test the dist directly, so isntanbul can't calculate the coverage correctly */
2
+ /* ^^^ we test the dist directly, so istanbul can't calculate the coverage correctly */
3
3
 
4
- import type { ChildProcess, SpawnOptionsWithoutStdio } from 'child_process';
5
- import { spawn } from 'child_process';
6
4
  import type {
7
5
  Runtime,
8
6
  RuntimeEvaluationListener,
@@ -11,184 +9,162 @@ import type {
11
9
  import type { MongoshBus } from '@mongosh/types';
12
10
  import path from 'path';
13
11
  import { EventEmitter, once } from 'events';
14
- import { kill } from './spawn-child-from-source';
12
+ import { pathToFileURL } from 'url';
15
13
  import type { Caller } from './rpc';
16
- import { createCaller, cancel } from './rpc';
17
- import { ChildProcessEvaluationListener } from './child-process-evaluation-listener';
14
+ import { createCaller, cancel, exposeAll } from './rpc';
18
15
  import type { WorkerRuntime as WorkerThreadWorkerRuntime } from './worker-runtime';
19
16
  import {
20
17
  deserializeEvaluationResult,
21
18
  serializeConnectOptions,
22
19
  } from './serializer';
23
- import { ChildProcessMongoshBus } from './child-process-mongosh-bus';
24
20
  import type { CompassServiceProvider } from '@mongosh/service-provider-server';
21
+ import type { InterruptHandle } from 'interruptor';
22
+ import { interrupt as nativeInterrupt } from 'interruptor';
23
+ import { WorkerThreadEvaluationListener } from './worker-thread-evaluation-listener';
24
+ import { WorkerProcessMongoshBus } from './worker-process-mongosh-bus';
25
25
 
26
26
  type DevtoolsConnectOptions = Parameters<
27
27
  (typeof CompassServiceProvider)['connect']
28
28
  >[1];
29
- type ChildProcessRuntime = Caller<WorkerThreadWorkerRuntime>;
30
-
31
- function parseStderrToError(str: string): Error | null {
32
- const [, errorMessageWithStack] = str
33
- .split(/^\s*\^\s*$/m)
34
- .map((part) => part.trim());
35
-
36
- if (errorMessageWithStack) {
37
- const e = new Error();
38
- const errorHeader =
39
- errorMessageWithStack.substring(
40
- 0,
41
- errorMessageWithStack.search(/^\s*at/m)
42
- ) || errorMessageWithStack;
43
-
44
- const [name, ...message] = errorHeader.split(': ');
45
-
46
- e.name = name;
47
- e.message = message.join(': ').trim();
48
- e.stack = errorMessageWithStack;
49
-
50
- return e;
51
- }
52
-
53
- return null;
54
- }
29
+ type WorkerThreadRuntime = Caller<WorkerThreadWorkerRuntime>;
55
30
 
56
31
  class WorkerRuntime implements Runtime {
57
32
  private initOptions: {
58
33
  uri: string;
59
34
  driverOptions: DevtoolsConnectOptions;
60
35
  cliOptions: { nodb?: boolean };
61
- spawnOptions: SpawnOptionsWithoutStdio;
36
+ workerOptions: WorkerOptions;
62
37
  };
63
38
 
64
39
  evaluationListener: RuntimeEvaluationListener | null = null;
65
40
 
66
41
  private eventEmitter: MongoshBus;
67
42
 
68
- private childProcessMongoshBus!: ChildProcessMongoshBus;
43
+ private workerProcess!: Worker;
69
44
 
70
- private childProcessEvaluationListener!: ChildProcessEvaluationListener;
45
+ private workerProcessRuntime!: WorkerThreadRuntime;
71
46
 
72
- private childProcess!: ChildProcess;
47
+ private initWorkerPromise: Promise<void>;
73
48
 
74
- private childProcessRuntime!: ChildProcessRuntime;
49
+ private workerThreadEvaluationListener!: WorkerThreadEvaluationListener;
75
50
 
76
- private initWorkerPromise: Promise<void>;
51
+ private workerProcessMongoshBus!: WorkerProcessMongoshBus;
77
52
 
78
- private childProcessProxySrcPath: string =
79
- process.env
80
- .CHILD_PROCESS_PROXY_SRC_PATH_DO_NOT_USE_THIS_EXCEPT_FOR_TESTING ||
81
- path.resolve(__dirname, 'child-process-proxy.js');
53
+ private workerProcessPath = path.resolve(__dirname, 'worker-runtime.js');
82
54
 
83
55
  constructor(
84
56
  uri: string,
85
57
  driverOptions: DevtoolsConnectOptions,
86
58
  cliOptions: { nodb?: boolean } = {},
87
- spawnOptions: SpawnOptionsWithoutStdio = {},
59
+ workerOptions: WorkerOptions = {},
88
60
  eventEmitter: MongoshBus = new EventEmitter()
89
61
  ) {
90
- this.initOptions = { uri, driverOptions, cliOptions, spawnOptions };
62
+ this.initOptions = { uri, driverOptions, cliOptions, workerOptions };
91
63
  this.eventEmitter = eventEmitter;
92
64
  this.initWorkerPromise = this.initWorker();
93
65
  }
94
66
 
95
67
  private async initWorker() {
96
- const { uri, driverOptions, cliOptions, spawnOptions } = this.initOptions;
97
-
98
- this.childProcess = spawn(
99
- process.execPath,
100
- [this.childProcessProxySrcPath],
101
- {
102
- stdio: ['inherit', 'inherit', 'pipe', 'ipc'],
103
- ...spawnOptions,
104
- }
68
+ const workerProcess = new Worker(
69
+ pathToFileURL(this.workerProcessPath).href,
70
+ this.initOptions.workerOptions
105
71
  );
106
72
 
107
- const waitForReadyMessage = async () => {
108
- let msg: string;
109
- while (([msg] = await once(this.childProcess, 'message'))) {
110
- if (msg === 'ready') return;
111
- }
112
- };
113
-
114
- let spawnError = '';
73
+ const workerReadyPromise = async (): Promise<void> => {
74
+ const waitForReadyMessage = async () => {
75
+ let msg: {
76
+ data: string;
77
+ };
78
+ while (([msg] = await once(workerProcess, 'message'))) {
79
+ if (msg?.data === 'ready') return;
80
+ }
81
+ };
82
+
83
+ const waitForError = async () => {
84
+ const [err] = await once(workerProcess, 'error');
85
+ if (err) {
86
+ err.message = `Worker thread failed to start with error: ${
87
+ (err as Error).message
88
+ }`;
89
+ throw err;
90
+ }
91
+ };
115
92
 
116
- this.childProcess?.stderr?.setEncoding('utf8')?.on('data', (chunk) => {
117
- spawnError += chunk;
118
- });
93
+ await Promise.race([waitForReadyMessage(), waitForError()]);
94
+ };
119
95
 
120
- const waitForError = async () => {
121
- const [exitCode] = await once(this.childProcess, 'exit');
96
+ await workerReadyPromise();
122
97
 
123
- if (exitCode) {
124
- let error = parseStderrToError(spawnError);
98
+ const { interrupt } = createCaller(['interrupt'], workerProcess);
125
99
 
126
- if (error) {
127
- error.message = `Child process failed to start with the following error: ${error.message}`;
128
- } else {
129
- error = new Error(
130
- `Worker runtime failed to start: child process exited with code ${
131
- exitCode as number | string
132
- }`
133
- );
134
- }
100
+ let interruptHandle: InterruptHandle | null = null;
135
101
 
136
- throw error;
102
+ this.workerProcessRuntime = Object.assign(
103
+ createCaller(
104
+ [
105
+ 'init',
106
+ 'evaluate',
107
+ 'getCompletions',
108
+ 'getShellPrompt',
109
+ 'setEvaluationListener',
110
+ 'interrupt',
111
+ ],
112
+ workerProcess
113
+ ),
114
+ {
115
+ interrupt(): boolean {
116
+ if (interruptHandle) {
117
+ nativeInterrupt(interruptHandle);
118
+ return true;
119
+ }
120
+
121
+ return interrupt();
122
+ },
137
123
  }
138
- };
139
-
140
- await Promise.race([waitForReadyMessage(), waitForError()]);
141
-
142
- // We expect the amount of listeners to be more than the default value of 10
143
- // but probably not more than ~25 (all exposed methods on
144
- // ChildProcessEvaluationListener and ChildProcessMongoshBus + any
145
- // concurrent in-flight calls on ChildProcessRuntime) at once
146
- this.childProcess.setMaxListeners(25);
147
-
148
- this.childProcessRuntime = createCaller(
149
- [
150
- 'init',
151
- 'evaluate',
152
- 'getCompletions',
153
- 'setEvaluationListener',
154
- 'getShellPrompt',
155
- 'interrupt',
156
- ],
157
- this.childProcess
158
124
  );
159
125
 
160
- this.childProcessEvaluationListener = new ChildProcessEvaluationListener(
126
+ this.workerThreadEvaluationListener = new WorkerThreadEvaluationListener(
161
127
  this,
162
- this.childProcess
128
+ workerProcess
129
+ );
130
+
131
+ exposeAll(
132
+ {
133
+ onRunInterruptible(handle: InterruptHandle | null) {
134
+ interruptHandle = handle;
135
+ },
136
+ },
137
+ workerProcess
163
138
  );
164
139
 
165
- this.childProcessMongoshBus = new ChildProcessMongoshBus(
140
+ this.workerProcessMongoshBus = new WorkerProcessMongoshBus(
166
141
  this.eventEmitter,
167
- this.childProcess
142
+ workerProcess
168
143
  );
169
144
 
170
- await this.childProcessRuntime.init(
171
- uri,
172
- serializeConnectOptions(driverOptions),
173
- cliOptions
145
+ await this.workerProcessRuntime.init(
146
+ this.initOptions.uri,
147
+ serializeConnectOptions(this.initOptions.driverOptions),
148
+ this.initOptions.cliOptions
174
149
  );
150
+ this.workerProcess = workerProcess;
175
151
  }
176
152
 
177
153
  async evaluate(code: string): Promise<RuntimeEvaluationResult> {
178
154
  await this.initWorkerPromise;
179
155
  return deserializeEvaluationResult(
180
- await this.childProcessRuntime.evaluate(code)
156
+ await this.workerProcessRuntime.evaluate(code)
181
157
  );
182
158
  }
183
159
 
184
160
  async getCompletions(code: string) {
185
161
  await this.initWorkerPromise;
186
- return await this.childProcessRuntime.getCompletions(code);
162
+ return await this.workerProcessRuntime.getCompletions(code);
187
163
  }
188
164
 
189
165
  async getShellPrompt() {
190
166
  await this.initWorkerPromise;
191
- return await this.childProcessRuntime.getShellPrompt();
167
+ return await this.workerProcessRuntime.getShellPrompt();
192
168
  }
193
169
 
194
170
  setEvaluationListener(listener: RuntimeEvaluationListener | null) {
@@ -201,28 +177,30 @@ class WorkerRuntime implements Runtime {
201
177
  try {
202
178
  await this.initWorkerPromise;
203
179
  } catch {
204
- // In case child process encountered an error during init we still want
205
- // to clean up whatever possible
180
+ // In case the worker thread encountered an error during init
181
+ // we still want to clean up whatever possible.
206
182
  }
207
183
 
208
- await kill(this.childProcess);
184
+ if (this.workerProcessRuntime) {
185
+ this.workerProcessRuntime[cancel]();
186
+ }
209
187
 
210
- if (this.childProcessRuntime) {
211
- this.childProcessRuntime[cancel]();
188
+ if (this.workerProcess) {
189
+ this.workerProcess.terminate();
212
190
  }
213
191
 
214
- if (this.childProcessEvaluationListener) {
215
- this.childProcessEvaluationListener.terminate();
192
+ if (this.workerThreadEvaluationListener) {
193
+ this.workerThreadEvaluationListener.terminate();
216
194
  }
217
195
 
218
- if (this.childProcessMongoshBus) {
219
- this.childProcessMongoshBus.terminate();
196
+ if (this.workerProcessMongoshBus) {
197
+ this.workerProcessMongoshBus.terminate();
220
198
  }
221
199
  }
222
200
 
223
201
  async interrupt() {
224
202
  await this.initWorkerPromise;
225
- return this.childProcessRuntime.interrupt();
203
+ return this.workerProcessRuntime.interrupt();
226
204
  }
227
205
 
228
206
  async waitForRuntimeToBeReady() {
package/src/lock.spec.ts CHANGED
@@ -25,7 +25,7 @@ describe('Lock', function () {
25
25
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
26
26
  lock.lock();
27
27
 
28
- let err: Error;
28
+ let err!: Error;
29
29
 
30
30
  try {
31
31
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
package/src/rpc.spec.ts CHANGED
@@ -1,58 +1,24 @@
1
1
  import { expect } from 'chai';
2
2
  import { EventEmitter } from 'events';
3
3
 
4
- import type { Caller, Exposed } from './rpc';
5
- import {
6
- createCaller,
7
- exposeAll,
8
- close,
9
- cancel,
10
- serialize,
11
- deserialize,
12
- removeTrailingUndefined,
13
- } from './rpc';
14
-
15
- function createMockRpcMesageBus() {
16
- const bus = new (class Bus extends EventEmitter {
17
- send(data: any) {
18
- this.emit('message', data);
19
- }
20
- })();
21
- return bus;
4
+ import type { Caller, Exposed, RPCMessageBus } from './rpc';
5
+ import { createCaller, exposeAll, close, cancel } from './rpc';
6
+
7
+ function createMockRpcMesageBus(): RPCMessageBus {
8
+ const ee = new EventEmitter();
9
+ return {
10
+ addEventListener: ee.on.bind(ee),
11
+ removeEventListener: ee.off.bind(ee),
12
+ postMessage: (data: unknown) => ee.emit('message', data),
13
+ };
22
14
  }
23
15
 
24
16
  function sleep(ms: number) {
25
17
  return new Promise((resolve) => setTimeout(resolve, ms));
26
18
  }
27
19
 
28
- describe('rpc helpers', function () {
29
- describe('serialize', function () {
30
- it('returns base64 representation of an input', function () {
31
- expect(serialize('Hello')).to.match(/data:;base64,\/w[08]iBUhlbGxv/);
32
- });
33
- });
34
-
35
- describe('deserialize', function () {
36
- it("converts base64 representation of input back to it's original form", function () {
37
- expect(deserialize(serialize('Hello'))).to.equal('Hello');
38
- });
39
-
40
- it("returns original string if it's not a base64 data uri", function () {
41
- expect(deserialize('Hi')).to.equal('Hi');
42
- });
43
- });
44
-
45
- describe('removeTrailingUndefined', function () {
46
- it('removes trailing undefineds from an array', function () {
47
- expect(
48
- removeTrailingUndefined([1, 2, 3, undefined, undefined, undefined])
49
- ).to.deep.equal([1, 2, 3]);
50
- });
51
- });
52
- });
53
-
54
20
  describe('rpc', function () {
55
- let messageBus: EventEmitter;
21
+ let messageBus: RPCMessageBus | null;
56
22
  let caller: Caller<{
57
23
  meow(...args: any[]): string;
58
24
  throws(...args: any[]): never;
@@ -61,22 +27,21 @@ describe('rpc', function () {
61
27
  woof(...args: any[]): string;
62
28
  neverResolves(...args: any[]): void;
63
29
  }>;
64
- let exposed: Exposed<unknown>;
30
+ let exposed: Exposed<unknown>; // adding `| null` breaks TS type inference
65
31
 
66
32
  afterEach(function () {
67
33
  if (messageBus) {
68
- messageBus.removeAllListeners();
69
34
  messageBus = null;
70
35
  }
71
36
 
72
37
  if (caller) {
73
38
  caller[cancel]();
74
- caller = null;
39
+ caller = null as any;
75
40
  }
76
41
 
77
42
  if (exposed) {
78
43
  exposed[close]();
79
- exposed = null;
44
+ exposed = null as any;
80
45
  }
81
46
  });
82
47
 
@@ -109,7 +74,7 @@ describe('rpc', function () {
109
74
  messageBus
110
75
  );
111
76
 
112
- let err: Error;
77
+ let err!: Error;
113
78
 
114
79
  try {
115
80
  // eslint-disable-next-line @typescript-eslint/await-thenable
@@ -126,7 +91,7 @@ describe('rpc', function () {
126
91
  .match(/TypeError: Uh-oh, error!\r?\n\s+at throws/);
127
92
  });
128
93
 
129
- it('throws on client if arguments are not serializable', async function () {
94
+ it('allows undefined response', async function () {
130
95
  messageBus = createMockRpcMesageBus();
131
96
  caller = createCaller(['callMe'], messageBus);
132
97
 
@@ -139,21 +104,11 @@ describe('rpc', function () {
139
104
  messageBus
140
105
  );
141
106
 
142
- let err: Error;
143
-
144
- try {
145
- await caller.callMe((a: number, b: number) => a + b);
146
- } catch (e: any) {
147
- err = e;
148
- }
149
-
150
- expect(err).to.be.instanceof(Error);
151
- expect(err)
152
- .to.have.property('message')
153
- .match(/could not be cloned/);
107
+ expect(await caller.callMe((a: number, b: number) => a + b)).to.be
108
+ .undefined;
154
109
  });
155
110
 
156
- it('throws on client if retured value from the server is not serializable', async function () {
111
+ it('allows function response', async function () {
157
112
  messageBus = createMockRpcMesageBus();
158
113
  caller = createCaller(['returnsFunction'], messageBus);
159
114
 
@@ -166,18 +121,7 @@ describe('rpc', function () {
166
121
  messageBus
167
122
  );
168
123
 
169
- let err: Error;
170
-
171
- try {
172
- await caller.returnsFunction();
173
- } catch (e: any) {
174
- err = e;
175
- }
176
-
177
- expect(err).to.be.instanceof(Error);
178
- expect(err)
179
- .to.have.property('message')
180
- .match(/could not be cloned/);
124
+ expect(await caller.returnsFunction()).to.be.instanceof(Function);
181
125
  });
182
126
 
183
127
  describe('createCaller', function () {
@@ -192,7 +136,7 @@ describe('rpc', function () {
192
136
  messageBus = createMockRpcMesageBus();
193
137
  caller = createCaller(['meow'], messageBus);
194
138
 
195
- messageBus.on('message', (data) => {
139
+ messageBus.addEventListener('message', (data: unknown) => {
196
140
  expect(data).to.have.property('func', 'meow');
197
141
  done();
198
142
  });
@@ -206,7 +150,7 @@ describe('rpc', function () {
206
150
  it('stops all in-flight evaluations', async function () {
207
151
  messageBus = createMockRpcMesageBus();
208
152
  caller = createCaller(['neverResolves'], messageBus);
209
- let err: Error;
153
+ let err!: Error;
210
154
  try {
211
155
  await Promise.all([
212
156
  caller.neverResolves(),
@@ -238,7 +182,7 @@ describe('rpc', function () {
238
182
  messageBus
239
183
  );
240
184
 
241
- messageBus.on('message', (data: any) => {
185
+ messageBus.addEventListener('message', (data: any) => {
242
186
  // Due to how our mocks implemented we have to introduce an if here to
243
187
  // skip our own message being received by the message bus
244
188
  if (data.sender === 'postmsg-rpc/server') {
@@ -251,21 +195,11 @@ describe('rpc', function () {
251
195
  }
252
196
  });
253
197
 
254
- messageBus.emit('message', {
198
+ messageBus.postMessage({
255
199
  sender: 'postmsg-rpc/client',
256
200
  func: 'meow',
257
201
  id: '123abc',
258
202
  });
259
203
  });
260
-
261
- describe('close', function () {
262
- it('disables all exposed listeners', function () {
263
- messageBus = createMockRpcMesageBus();
264
- exposed = exposeAll({ doSomething() {} }, messageBus);
265
- expect(messageBus.listenerCount('message')).to.equal(1);
266
- exposed[close]();
267
- expect(messageBus.listenerCount('message')).to.equal(0);
268
- });
269
- });
270
204
  });
271
205
  });
package/src/rpc.ts CHANGED
@@ -1,30 +1,12 @@
1
- import v8 from 'v8';
2
1
  import { expose, caller } from 'postmsg-rpc';
3
2
  import { deserializeError, serializeError } from './serializer';
4
- import type {
5
- MessageData,
6
- PostmsgRpcOptions,
7
- ServerMessageData,
8
- ClientMessageData,
9
- } from 'postmsg-rpc';
3
+ import type { PostmsgRpcOptions } from 'postmsg-rpc';
10
4
 
11
- export function serialize(data: unknown): string {
12
- return `data:;base64,${v8.serialize(data).toString('base64')}`;
13
- }
14
-
15
- export function deserialize<T = unknown>(str: string): T | string {
16
- if (/^data:;base64,.+/.test(str)) {
17
- return v8.deserialize(
18
- Buffer.from(str.replace('data:;base64,', ''), 'base64')
19
- );
20
- }
21
- return str;
22
- }
23
-
24
- type RPCMessageBus = { on: Function; off: Function } & (
25
- | { postMessage: Function; send?: never }
26
- | { postMessage?: never; send?: Function }
27
- );
5
+ export type RPCMessageBus = {
6
+ postMessage: Function;
7
+ addEventListener: Function;
8
+ removeEventListener: Function;
9
+ };
28
10
 
29
11
  enum RPCMessageTypes {
30
12
  Message,
@@ -47,81 +29,15 @@ function isRPCError(data: any): data is RPCError {
47
29
  );
48
30
  }
49
31
 
50
- function isMessageData(data: any): data is MessageData {
51
- return data && typeof data === 'object' && 'id' in data && 'sender' in data;
52
- }
53
-
54
- function isServerMessageData(data: any): data is ServerMessageData {
55
- return isMessageData(data) && data.sender === 'postmsg-rpc/server';
56
- }
57
-
58
- function isClientMessageData(data: any): data is ClientMessageData {
59
- return isMessageData(data) && data.sender === 'postmsg-rpc/client';
60
- }
61
-
62
- export function removeTrailingUndefined(arr: unknown[]): unknown[] {
63
- if (Array.isArray(arr)) {
64
- arr = [...arr];
65
- while (arr.length > 0 && arr[arr.length - 1] === undefined) {
66
- arr.pop();
67
- }
68
- }
69
- return arr;
70
- }
71
-
72
- function send(messageBus: RPCMessageBus, data: any): void {
73
- if (
74
- 'postMessage' in messageBus &&
75
- typeof messageBus.postMessage === 'function'
76
- ) {
77
- messageBus.postMessage(data);
78
- }
79
-
80
- if ('send' in messageBus && typeof messageBus.send === 'function') {
81
- messageBus.send(data);
82
- }
83
- }
84
-
85
32
  function getRPCOptions(messageBus: RPCMessageBus): PostmsgRpcOptions {
86
33
  return {
87
- addListener: messageBus.on.bind(messageBus),
88
- removeListener: messageBus.off.bind(messageBus),
34
+ addListener: messageBus.addEventListener.bind(messageBus),
35
+ removeListener: messageBus.removeEventListener.bind(messageBus),
89
36
  postMessage(data) {
90
- if (isClientMessageData(data) && Array.isArray(data.args)) {
91
- data.args = serialize(removeTrailingUndefined(data.args));
92
- }
93
-
94
- if (isServerMessageData(data)) {
95
- // If serialization of the response failed for some reason (e.g., the
96
- // value is not serializable) we want to propagate the error back to the
97
- // client that issued the remote call instead of throwing on the server
98
- // that was executing the method.
99
- try {
100
- data.res = serialize(data.res);
101
- } catch (e: any) {
102
- data.res = serialize({
103
- type: RPCMessageTypes.Error,
104
- payload: serializeError(e),
105
- });
106
- }
107
- }
108
-
109
- return send(messageBus, data);
37
+ return messageBus.postMessage(data);
110
38
  },
111
- getMessageData(data) {
112
- if (
113
- isClientMessageData(data) &&
114
- data.args &&
115
- typeof data.args === 'string'
116
- ) {
117
- data.args = deserialize(data.args);
118
- }
119
-
120
- if (isServerMessageData(data) && typeof data.res === 'string') {
121
- data.res = deserialize(data.res);
122
- }
123
-
124
- return data;
39
+ getMessageData(event) {
40
+ return (event as { data: unknown }).data ?? event;
125
41
  },
126
42
  };
127
43
  }
@@ -134,7 +50,10 @@ export type Exposed<T> = { [k in keyof T]: T[k] & { close(): void } } & {
134
50
  [close]: () => void;
135
51
  };
136
52
 
137
- export function exposeAll<O>(obj: O, messageBus: RPCMessageBus): Exposed<O> {
53
+ export function exposeAll<O extends object>(
54
+ obj: O,
55
+ messageBus: RPCMessageBus
56
+ ): Exposed<O> {
138
57
  Object.entries(obj as Record<string, any>).forEach(([key, val]) => {
139
58
  const { close } = expose(
140
59
  key,
@@ -151,7 +70,7 @@ export function exposeAll<O>(obj: O, messageBus: RPCMessageBus): Exposed<O> {
151
70
  },
152
71
  getRPCOptions(messageBus)
153
72
  );
154
- (val as any).close = close;
73
+ val.close = close;
155
74
  });
156
75
  Object.defineProperty(obj, close, {
157
76
  enumerable: false,
@@ -171,7 +90,7 @@ export type Caller<
171
90
  Keys extends keyof Impl = keyof Impl
172
91
  > = CancelableMethods<Pick<Impl, Keys>> & { [cancel]: () => void };
173
92
 
174
- export function createCaller<Impl extends {}>(
93
+ export function createCaller<Impl extends object>(
175
94
  methodNames: Extract<keyof Impl, string>[],
176
95
  messageBus: RPCMessageBus,
177
96
  processors: Partial<