penpal 7.0.3 → 7.0.4
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/dist/penpal.cjs +953 -0
- package/dist/penpal.cjs.map +1 -0
- package/dist/penpal.d.cts +234 -0
- package/dist/penpal.d.ts +234 -0
- package/dist/penpal.js +890 -1033
- package/dist/penpal.js.map +1 -0
- package/dist/penpal.min.js +1 -1
- package/dist/penpal.min.js.map +1 -1
- package/dist/penpal.mjs +943 -0
- package/dist/penpal.mjs.map +1 -0
- package/package.json +13 -21
- package/cjs/CallOptions.d.ts +0 -10
- package/cjs/CallOptions.js +0 -16
- package/cjs/ErrorCodeObj.d.ts +0 -9
- package/cjs/ErrorCodeObj.js +0 -14
- package/cjs/PenpalBugError.d.ts +0 -8
- package/cjs/PenpalBugError.js +0 -12
- package/cjs/PenpalError.d.ts +0 -6
- package/cjs/PenpalError.js +0 -11
- package/cjs/Reply.d.ts +0 -9
- package/cjs/Reply.js +0 -16
- package/cjs/backwardCompatibility.d.ts +0 -56
- package/cjs/backwardCompatibility.js +0 -134
- package/cjs/connect.d.ts +0 -33
- package/cjs/connect.js +0 -78
- package/cjs/connectCallHandler.d.ts +0 -8
- package/cjs/connectCallHandler.js +0 -90
- package/cjs/connectRemoteProxy.d.ts +0 -12
- package/cjs/connectRemoteProxy.js +0 -139
- package/cjs/debug.d.ts +0 -3
- package/cjs/debug.js +0 -8
- package/cjs/errorSerialization.d.ts +0 -9
- package/cjs/errorSerialization.js +0 -26
- package/cjs/generateId.d.ts +0 -8
- package/cjs/generateId.js +0 -11
- package/cjs/getPromiseWithResolvers.d.ts +0 -6
- package/cjs/getPromiseWithResolvers.js +0 -19
- package/cjs/guards.d.ts +0 -10
- package/cjs/guards.js +0 -40
- package/cjs/index.d.ts +0 -12
- package/cjs/index.js +0 -21
- package/cjs/indexForBundle.d.ts +0 -31
- package/cjs/indexForBundle.js +0 -22
- package/cjs/messengers/Messenger.d.ts +0 -14
- package/cjs/messengers/Messenger.js +0 -2
- package/cjs/messengers/PortMessenger.d.ts +0 -21
- package/cjs/messengers/PortMessenger.js +0 -47
- package/cjs/messengers/WindowMessenger.d.ts +0 -29
- package/cjs/messengers/WindowMessenger.js +0 -178
- package/cjs/messengers/WorkerMessenger.d.ts +0 -23
- package/cjs/messengers/WorkerMessenger.js +0 -86
- package/cjs/methodSerialization.d.ts +0 -22
- package/cjs/methodSerialization.js +0 -48
- package/cjs/namespace.d.ts +0 -2
- package/cjs/namespace.js +0 -3
- package/cjs/once.d.ts +0 -2
- package/cjs/once.js +0 -15
- package/cjs/shakeHands.d.ts +0 -76
- package/cjs/shakeHands.js +0 -190
- package/cjs/types.d.ts +0 -89
- package/cjs/types.js +0 -2
- package/lib/CallOptions.d.ts +0 -10
- package/lib/CallOptions.js +0 -14
- package/lib/ErrorCodeObj.d.ts +0 -9
- package/lib/ErrorCodeObj.js +0 -12
- package/lib/PenpalBugError.d.ts +0 -8
- package/lib/PenpalBugError.js +0 -10
- package/lib/PenpalError.d.ts +0 -6
- package/lib/PenpalError.js +0 -9
- package/lib/Reply.d.ts +0 -9
- package/lib/Reply.js +0 -14
- package/lib/backwardCompatibility.d.ts +0 -56
- package/lib/backwardCompatibility.js +0 -128
- package/lib/connect.d.ts +0 -33
- package/lib/connect.js +0 -76
- package/lib/connectCallHandler.d.ts +0 -8
- package/lib/connectCallHandler.js +0 -88
- package/lib/connectRemoteProxy.d.ts +0 -12
- package/lib/connectRemoteProxy.js +0 -137
- package/lib/debug.d.ts +0 -3
- package/lib/debug.js +0 -6
- package/lib/errorSerialization.d.ts +0 -9
- package/lib/errorSerialization.js +0 -21
- package/lib/generateId.d.ts +0 -8
- package/lib/generateId.js +0 -9
- package/lib/getPromiseWithResolvers.d.ts +0 -6
- package/lib/getPromiseWithResolvers.js +0 -17
- package/lib/guards.d.ts +0 -10
- package/lib/guards.js +0 -28
- package/lib/index.d.ts +0 -12
- package/lib/index.js +0 -9
- package/lib/indexForBundle.d.ts +0 -31
- package/lib/indexForBundle.js +0 -20
- package/lib/messengers/Messenger.d.ts +0 -14
- package/lib/messengers/Messenger.js +0 -1
- package/lib/messengers/PortMessenger.d.ts +0 -21
- package/lib/messengers/PortMessenger.js +0 -45
- package/lib/messengers/WindowMessenger.d.ts +0 -29
- package/lib/messengers/WindowMessenger.js +0 -176
- package/lib/messengers/WorkerMessenger.d.ts +0 -23
- package/lib/messengers/WorkerMessenger.js +0 -84
- package/lib/methodSerialization.d.ts +0 -22
- package/lib/methodSerialization.js +0 -42
- package/lib/namespace.d.ts +0 -2
- package/lib/namespace.js +0 -1
- package/lib/once.d.ts +0 -2
- package/lib/once.js +0 -13
- package/lib/shakeHands.d.ts +0 -76
- package/lib/shakeHands.js +0 -188
- package/lib/types.d.ts +0 -89
- package/lib/types.js +0 -1
package/dist/penpal.cjs
ADDED
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/PenpalError.ts
|
|
4
|
+
var PenpalError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "PenpalError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var PenpalError_default = PenpalError;
|
|
13
|
+
|
|
14
|
+
// src/errorSerialization.ts
|
|
15
|
+
var serializeError = (error) => ({
|
|
16
|
+
name: error.name,
|
|
17
|
+
message: error.message,
|
|
18
|
+
stack: error.stack,
|
|
19
|
+
penpalCode: error instanceof PenpalError_default ? error.code : void 0
|
|
20
|
+
});
|
|
21
|
+
var deserializeError = ({
|
|
22
|
+
name,
|
|
23
|
+
message,
|
|
24
|
+
stack,
|
|
25
|
+
penpalCode
|
|
26
|
+
}) => {
|
|
27
|
+
const deserializedError = penpalCode ? new PenpalError_default(penpalCode, message) : new Error(message);
|
|
28
|
+
deserializedError.name = name;
|
|
29
|
+
deserializedError.stack = stack;
|
|
30
|
+
return deserializedError;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/Reply.ts
|
|
34
|
+
var brand = Symbol("Reply");
|
|
35
|
+
var Reply = class {
|
|
36
|
+
value;
|
|
37
|
+
transferables;
|
|
38
|
+
// Allows TypeScript to distinguish between an actual instance of this
|
|
39
|
+
// class versus an object that looks structurally similar.
|
|
40
|
+
// eslint-disable-next-line no-unused-private-class-members
|
|
41
|
+
#brand = brand;
|
|
42
|
+
constructor(value, options) {
|
|
43
|
+
this.value = value;
|
|
44
|
+
this.transferables = options?.transferables;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var Reply_default = Reply;
|
|
48
|
+
|
|
49
|
+
// src/namespace.ts
|
|
50
|
+
var namespace_default = "penpal";
|
|
51
|
+
|
|
52
|
+
// src/guards.ts
|
|
53
|
+
var isObject = (value) => {
|
|
54
|
+
return typeof value === "object" && value !== null;
|
|
55
|
+
};
|
|
56
|
+
var isFunction = (value) => {
|
|
57
|
+
return typeof value === "function";
|
|
58
|
+
};
|
|
59
|
+
var isMessage = (data) => {
|
|
60
|
+
return isObject(data) && data.namespace === namespace_default;
|
|
61
|
+
};
|
|
62
|
+
var isSynMessage = (message) => {
|
|
63
|
+
return message.type === "SYN";
|
|
64
|
+
};
|
|
65
|
+
var isAck1Message = (message) => {
|
|
66
|
+
return message.type === "ACK1";
|
|
67
|
+
};
|
|
68
|
+
var isAck2Message = (message) => {
|
|
69
|
+
return message.type === "ACK2";
|
|
70
|
+
};
|
|
71
|
+
var isCallMessage = (message) => {
|
|
72
|
+
return message.type === "CALL";
|
|
73
|
+
};
|
|
74
|
+
var isReplyMessage = (message) => {
|
|
75
|
+
return message.type === "REPLY";
|
|
76
|
+
};
|
|
77
|
+
var isDestroyMessage = (message) => {
|
|
78
|
+
return message.type === "DESTROY";
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/methodSerialization.ts
|
|
82
|
+
var extractMethodPathsFromMethods = (methods, currentPath = []) => {
|
|
83
|
+
const methodPaths = [];
|
|
84
|
+
for (const key of Object.keys(methods)) {
|
|
85
|
+
const value = methods[key];
|
|
86
|
+
if (isFunction(value)) {
|
|
87
|
+
methodPaths.push([...currentPath, key]);
|
|
88
|
+
} else if (isObject(value)) {
|
|
89
|
+
methodPaths.push(
|
|
90
|
+
...extractMethodPathsFromMethods(value, [...currentPath, key])
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return methodPaths;
|
|
95
|
+
};
|
|
96
|
+
var getMethodAtMethodPath = (methodPath, methods) => {
|
|
97
|
+
const result = methodPath.reduce(
|
|
98
|
+
(acc, pathSegment) => {
|
|
99
|
+
return isObject(acc) ? acc[pathSegment] : void 0;
|
|
100
|
+
},
|
|
101
|
+
methods
|
|
102
|
+
);
|
|
103
|
+
return isFunction(result) ? result : void 0;
|
|
104
|
+
};
|
|
105
|
+
var formatMethodPath = (methodPath) => {
|
|
106
|
+
return methodPath.join(".");
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/connectCallHandler.ts
|
|
110
|
+
var createErrorReplyMessage = (channel, callId, error) => ({
|
|
111
|
+
namespace: namespace_default,
|
|
112
|
+
channel,
|
|
113
|
+
type: "REPLY",
|
|
114
|
+
callId,
|
|
115
|
+
isError: true,
|
|
116
|
+
...error instanceof Error ? { value: serializeError(error), isSerializedErrorInstance: true } : { value: error }
|
|
117
|
+
});
|
|
118
|
+
var connectCallHandler = (messenger, methods, channel, log) => {
|
|
119
|
+
let isDestroyed = false;
|
|
120
|
+
const handleMessage = async (message) => {
|
|
121
|
+
if (isDestroyed) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!isCallMessage(message)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
log?.(`Received ${formatMethodPath(message.methodPath)}() call`, message);
|
|
128
|
+
const { methodPath, args, id: callId } = message;
|
|
129
|
+
let replyMessage;
|
|
130
|
+
let transferables;
|
|
131
|
+
try {
|
|
132
|
+
const method = getMethodAtMethodPath(methodPath, methods);
|
|
133
|
+
if (!method) {
|
|
134
|
+
throw new PenpalError_default(
|
|
135
|
+
"METHOD_NOT_FOUND",
|
|
136
|
+
`Method \`${formatMethodPath(methodPath)}\` is not found.`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
let value = await method(...args);
|
|
140
|
+
if (value instanceof Reply_default) {
|
|
141
|
+
transferables = value.transferables;
|
|
142
|
+
value = await value.value;
|
|
143
|
+
}
|
|
144
|
+
replyMessage = {
|
|
145
|
+
namespace: namespace_default,
|
|
146
|
+
channel,
|
|
147
|
+
type: "REPLY",
|
|
148
|
+
callId,
|
|
149
|
+
value
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
replyMessage = createErrorReplyMessage(channel, callId, error);
|
|
153
|
+
}
|
|
154
|
+
if (isDestroyed) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
log?.(`Sending ${formatMethodPath(methodPath)}() reply`, replyMessage);
|
|
159
|
+
messenger.sendMessage(replyMessage, transferables);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error.name === "DataCloneError") {
|
|
162
|
+
replyMessage = createErrorReplyMessage(channel, callId, error);
|
|
163
|
+
log?.(`Sending ${formatMethodPath(methodPath)}() reply`, replyMessage);
|
|
164
|
+
messenger.sendMessage(replyMessage);
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
messenger.addMessageHandler(handleMessage);
|
|
170
|
+
return () => {
|
|
171
|
+
isDestroyed = true;
|
|
172
|
+
messenger.removeMessageHandler(handleMessage);
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
var connectCallHandler_default = connectCallHandler;
|
|
176
|
+
|
|
177
|
+
// src/generateId.ts
|
|
178
|
+
var generateId_default = crypto.randomUUID?.bind(crypto) ?? (() => new Array(4).fill(0).map(
|
|
179
|
+
() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)
|
|
180
|
+
).join("-"));
|
|
181
|
+
|
|
182
|
+
// src/CallOptions.ts
|
|
183
|
+
var brand2 = Symbol("CallOptions");
|
|
184
|
+
var CallOptions = class {
|
|
185
|
+
transferables;
|
|
186
|
+
timeout;
|
|
187
|
+
// Allows TypeScript to distinguish between an actual instance of this
|
|
188
|
+
// class versus an object that looks structurally similar.
|
|
189
|
+
// eslint-disable-next-line no-unused-private-class-members
|
|
190
|
+
#brand = brand2;
|
|
191
|
+
constructor(options) {
|
|
192
|
+
this.transferables = options?.transferables;
|
|
193
|
+
this.timeout = options?.timeout;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var CallOptions_default = CallOptions;
|
|
197
|
+
|
|
198
|
+
// src/connectRemoteProxy.ts
|
|
199
|
+
var methodsToTreatAsNative = /* @__PURE__ */ new Set(["apply", "call", "bind"]);
|
|
200
|
+
var createRemoteProxy = (callback, log, path = []) => {
|
|
201
|
+
return new Proxy(
|
|
202
|
+
path.length ? () => {
|
|
203
|
+
} : /* @__PURE__ */ Object.create(null),
|
|
204
|
+
{
|
|
205
|
+
get(target, prop) {
|
|
206
|
+
if (prop === "then") {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (path.length && methodsToTreatAsNative.has(prop)) {
|
|
210
|
+
return Reflect.get(target, prop);
|
|
211
|
+
}
|
|
212
|
+
return createRemoteProxy(callback, log, [...path, prop]);
|
|
213
|
+
},
|
|
214
|
+
apply(target, _thisArg, args) {
|
|
215
|
+
return callback(path, args);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
var getDestroyedConnectionMethodCallError = (methodPath) => {
|
|
221
|
+
return new PenpalError_default(
|
|
222
|
+
"CONNECTION_DESTROYED",
|
|
223
|
+
`Method call ${formatMethodPath(
|
|
224
|
+
methodPath
|
|
225
|
+
)}() failed due to destroyed connection`
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
var connectRemoteProxy = (messenger, channel, log) => {
|
|
229
|
+
let isDestroyed = false;
|
|
230
|
+
const replyHandlers = /* @__PURE__ */ new Map();
|
|
231
|
+
const handleMessage = (message) => {
|
|
232
|
+
if (!isReplyMessage(message)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const { callId, value, isError, isSerializedErrorInstance } = message;
|
|
236
|
+
const replyHandler = replyHandlers.get(callId);
|
|
237
|
+
if (!replyHandler) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
replyHandlers.delete(callId);
|
|
241
|
+
log?.(
|
|
242
|
+
`Received ${formatMethodPath(replyHandler.methodPath)}() call`,
|
|
243
|
+
message
|
|
244
|
+
);
|
|
245
|
+
if (isError) {
|
|
246
|
+
replyHandler.reject(
|
|
247
|
+
isSerializedErrorInstance ? deserializeError(value) : value
|
|
248
|
+
);
|
|
249
|
+
} else {
|
|
250
|
+
replyHandler.resolve(value);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
messenger.addMessageHandler(handleMessage);
|
|
254
|
+
const remoteProxy = createRemoteProxy((methodPath, args) => {
|
|
255
|
+
if (isDestroyed) {
|
|
256
|
+
throw getDestroyedConnectionMethodCallError(methodPath);
|
|
257
|
+
}
|
|
258
|
+
const callId = generateId_default();
|
|
259
|
+
const lastArg = args[args.length - 1];
|
|
260
|
+
const lastArgIsOptions = lastArg instanceof CallOptions_default;
|
|
261
|
+
const { timeout, transferables } = lastArgIsOptions ? lastArg : {};
|
|
262
|
+
const argsWithoutOptions = lastArgIsOptions ? args.slice(0, -1) : args;
|
|
263
|
+
return new Promise((resolve, reject) => {
|
|
264
|
+
const timeoutId = timeout !== void 0 ? window.setTimeout(() => {
|
|
265
|
+
replyHandlers.delete(callId);
|
|
266
|
+
reject(
|
|
267
|
+
new PenpalError_default(
|
|
268
|
+
"METHOD_CALL_TIMEOUT",
|
|
269
|
+
`Method call ${formatMethodPath(
|
|
270
|
+
methodPath
|
|
271
|
+
)}() timed out after ${timeout}ms`
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
}, timeout) : void 0;
|
|
275
|
+
replyHandlers.set(callId, { methodPath, resolve, reject, timeoutId });
|
|
276
|
+
try {
|
|
277
|
+
const callMessage = {
|
|
278
|
+
namespace: namespace_default,
|
|
279
|
+
channel,
|
|
280
|
+
type: "CALL",
|
|
281
|
+
id: callId,
|
|
282
|
+
methodPath,
|
|
283
|
+
args: argsWithoutOptions
|
|
284
|
+
};
|
|
285
|
+
log?.(`Sending ${formatMethodPath(methodPath)}() call`, callMessage);
|
|
286
|
+
messenger.sendMessage(callMessage, transferables);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
reject(
|
|
289
|
+
new PenpalError_default("TRANSMISSION_FAILED", error.message)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}, log);
|
|
294
|
+
const destroy = () => {
|
|
295
|
+
isDestroyed = true;
|
|
296
|
+
messenger.removeMessageHandler(handleMessage);
|
|
297
|
+
for (const { methodPath, reject, timeoutId } of replyHandlers.values()) {
|
|
298
|
+
clearTimeout(timeoutId);
|
|
299
|
+
reject(getDestroyedConnectionMethodCallError(methodPath));
|
|
300
|
+
}
|
|
301
|
+
replyHandlers.clear();
|
|
302
|
+
};
|
|
303
|
+
return {
|
|
304
|
+
remoteProxy,
|
|
305
|
+
destroy
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
var connectRemoteProxy_default = connectRemoteProxy;
|
|
309
|
+
|
|
310
|
+
// src/getPromiseWithResolvers.ts
|
|
311
|
+
var getPromiseWithResolvers = () => {
|
|
312
|
+
let resolve;
|
|
313
|
+
let reject;
|
|
314
|
+
const promise = new Promise((res, rej) => {
|
|
315
|
+
resolve = res;
|
|
316
|
+
reject = rej;
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
promise,
|
|
320
|
+
resolve,
|
|
321
|
+
reject
|
|
322
|
+
};
|
|
323
|
+
};
|
|
324
|
+
var getPromiseWithResolvers_default = getPromiseWithResolvers;
|
|
325
|
+
|
|
326
|
+
// src/PenpalBugError.ts
|
|
327
|
+
var PenpalBugError = class extends Error {
|
|
328
|
+
constructor(message) {
|
|
329
|
+
super(
|
|
330
|
+
`You've hit a bug in Penpal. Please file an issue with the following information: ${message}`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var PenpalBugError_default = PenpalBugError;
|
|
335
|
+
|
|
336
|
+
// src/backwardCompatibility.ts
|
|
337
|
+
var DEPRECATED_PENPAL_PARTICIPANT_ID = "deprecated-penpal";
|
|
338
|
+
var isDeprecatedMessage = (data) => {
|
|
339
|
+
return isObject(data) && "penpal" in data;
|
|
340
|
+
};
|
|
341
|
+
var upgradeMethodPath = (methodPath) => methodPath.split(".");
|
|
342
|
+
var downgradeMethodPath = (methodPath) => methodPath.join(".");
|
|
343
|
+
var getUnexpectedMessageError = (message) => {
|
|
344
|
+
return new PenpalBugError_default(
|
|
345
|
+
`Unexpected message to translate: ${JSON.stringify(message)}`
|
|
346
|
+
);
|
|
347
|
+
};
|
|
348
|
+
var upgradeMessage = (message) => {
|
|
349
|
+
if (message.penpal === "syn" /* Syn */) {
|
|
350
|
+
return {
|
|
351
|
+
namespace: namespace_default,
|
|
352
|
+
channel: void 0,
|
|
353
|
+
type: "SYN",
|
|
354
|
+
participantId: DEPRECATED_PENPAL_PARTICIPANT_ID
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (message.penpal === "ack" /* Ack */) {
|
|
358
|
+
return {
|
|
359
|
+
namespace: namespace_default,
|
|
360
|
+
channel: void 0,
|
|
361
|
+
type: "ACK2"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (message.penpal === "call" /* Call */) {
|
|
365
|
+
return {
|
|
366
|
+
namespace: namespace_default,
|
|
367
|
+
channel: void 0,
|
|
368
|
+
type: "CALL",
|
|
369
|
+
// Actually converting the ID to a string would break communication.
|
|
370
|
+
id: message.id,
|
|
371
|
+
methodPath: upgradeMethodPath(message.methodName),
|
|
372
|
+
args: message.args
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
if (message.penpal === "reply" /* Reply */) {
|
|
376
|
+
if (message.resolution === "fulfilled" /* Fulfilled */) {
|
|
377
|
+
return {
|
|
378
|
+
namespace: namespace_default,
|
|
379
|
+
channel: void 0,
|
|
380
|
+
type: "REPLY",
|
|
381
|
+
// Actually converting the ID to a string would break communication.
|
|
382
|
+
callId: message.id,
|
|
383
|
+
value: message.returnValue
|
|
384
|
+
};
|
|
385
|
+
} else {
|
|
386
|
+
return {
|
|
387
|
+
namespace: namespace_default,
|
|
388
|
+
channel: void 0,
|
|
389
|
+
type: "REPLY",
|
|
390
|
+
// Actually converting the ID to a string would break communication.
|
|
391
|
+
callId: message.id,
|
|
392
|
+
isError: true,
|
|
393
|
+
...message.returnValueIsError ? {
|
|
394
|
+
value: message.returnValue,
|
|
395
|
+
isSerializedErrorInstance: true
|
|
396
|
+
} : {
|
|
397
|
+
value: message.returnValue
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
throw getUnexpectedMessageError(message);
|
|
403
|
+
};
|
|
404
|
+
var downgradeMessage = (message) => {
|
|
405
|
+
if (isAck1Message(message)) {
|
|
406
|
+
return {
|
|
407
|
+
penpal: "synAck" /* SynAck */,
|
|
408
|
+
methodNames: message.methodPaths.map(downgradeMethodPath)
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (isCallMessage(message)) {
|
|
412
|
+
return {
|
|
413
|
+
penpal: "call" /* Call */,
|
|
414
|
+
// Actually converting the ID to a number would break communication.
|
|
415
|
+
id: message.id,
|
|
416
|
+
methodName: downgradeMethodPath(message.methodPath),
|
|
417
|
+
args: message.args
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
if (isReplyMessage(message)) {
|
|
421
|
+
if (message.isError) {
|
|
422
|
+
return {
|
|
423
|
+
penpal: "reply" /* Reply */,
|
|
424
|
+
// Actually converting the ID to a number would break communication.
|
|
425
|
+
id: message.callId,
|
|
426
|
+
resolution: "rejected" /* Rejected */,
|
|
427
|
+
...message.isSerializedErrorInstance ? {
|
|
428
|
+
returnValue: message.value,
|
|
429
|
+
returnValueIsError: true
|
|
430
|
+
} : { returnValue: message.value }
|
|
431
|
+
};
|
|
432
|
+
} else {
|
|
433
|
+
return {
|
|
434
|
+
penpal: "reply" /* Reply */,
|
|
435
|
+
// Actually converting the ID to a number would break communication.
|
|
436
|
+
id: message.callId,
|
|
437
|
+
resolution: "fulfilled" /* Fulfilled */,
|
|
438
|
+
returnValue: message.value
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
throw getUnexpectedMessageError(message);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// src/shakeHands.ts
|
|
446
|
+
var shakeHands = ({
|
|
447
|
+
messenger,
|
|
448
|
+
methods,
|
|
449
|
+
timeout,
|
|
450
|
+
channel,
|
|
451
|
+
log
|
|
452
|
+
}) => {
|
|
453
|
+
const participantId = generateId_default();
|
|
454
|
+
let remoteParticipantId;
|
|
455
|
+
const destroyHandlers = [];
|
|
456
|
+
let isComplete = false;
|
|
457
|
+
const methodPaths = extractMethodPathsFromMethods(methods);
|
|
458
|
+
const { promise, resolve, reject } = getPromiseWithResolvers_default();
|
|
459
|
+
const timeoutId = timeout !== void 0 ? setTimeout(() => {
|
|
460
|
+
reject(
|
|
461
|
+
new PenpalError_default(
|
|
462
|
+
"CONNECTION_TIMEOUT",
|
|
463
|
+
`Connection timed out after ${timeout}ms`
|
|
464
|
+
)
|
|
465
|
+
);
|
|
466
|
+
}, timeout) : void 0;
|
|
467
|
+
const destroy = () => {
|
|
468
|
+
for (const destroyHandler of destroyHandlers) {
|
|
469
|
+
destroyHandler();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const connectCallHandlerAndMethodProxies = () => {
|
|
473
|
+
if (isComplete) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
destroyHandlers.push(connectCallHandler_default(messenger, methods, channel, log));
|
|
477
|
+
const { remoteProxy, destroy: destroyMethodProxies } = connectRemoteProxy_default(messenger, channel, log);
|
|
478
|
+
destroyHandlers.push(destroyMethodProxies);
|
|
479
|
+
clearTimeout(timeoutId);
|
|
480
|
+
isComplete = true;
|
|
481
|
+
resolve({
|
|
482
|
+
remoteProxy,
|
|
483
|
+
destroy
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
const sendSynMessage = () => {
|
|
487
|
+
const synMessage = {
|
|
488
|
+
namespace: namespace_default,
|
|
489
|
+
type: "SYN",
|
|
490
|
+
channel,
|
|
491
|
+
participantId
|
|
492
|
+
};
|
|
493
|
+
log?.(`Sending handshake SYN`, synMessage);
|
|
494
|
+
try {
|
|
495
|
+
messenger.sendMessage(synMessage);
|
|
496
|
+
} catch (error) {
|
|
497
|
+
reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
const handleSynMessage = (message) => {
|
|
501
|
+
log?.(`Received handshake SYN`, message);
|
|
502
|
+
if (message.participantId === remoteParticipantId && // TODO: Used for backward-compatibility. Remove in next major version.
|
|
503
|
+
remoteParticipantId !== DEPRECATED_PENPAL_PARTICIPANT_ID) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
remoteParticipantId = message.participantId;
|
|
507
|
+
sendSynMessage();
|
|
508
|
+
const isHandshakeLeader = participantId > remoteParticipantId || // TODO: Used for backward-compatibility. Remove in next major version.
|
|
509
|
+
remoteParticipantId === DEPRECATED_PENPAL_PARTICIPANT_ID;
|
|
510
|
+
if (!isHandshakeLeader) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const ack1Message = {
|
|
514
|
+
namespace: namespace_default,
|
|
515
|
+
channel,
|
|
516
|
+
type: "ACK1",
|
|
517
|
+
methodPaths
|
|
518
|
+
};
|
|
519
|
+
log?.(`Sending handshake ACK1`, ack1Message);
|
|
520
|
+
try {
|
|
521
|
+
messenger.sendMessage(ack1Message);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const handleAck1Message = (message) => {
|
|
528
|
+
log?.(`Received handshake ACK1`, message);
|
|
529
|
+
const ack2Message = {
|
|
530
|
+
namespace: namespace_default,
|
|
531
|
+
channel,
|
|
532
|
+
type: "ACK2"
|
|
533
|
+
};
|
|
534
|
+
log?.(`Sending handshake ACK2`, ack2Message);
|
|
535
|
+
try {
|
|
536
|
+
messenger.sendMessage(ack2Message);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
connectCallHandlerAndMethodProxies();
|
|
542
|
+
};
|
|
543
|
+
const handleAck2Message = (message) => {
|
|
544
|
+
log?.(`Received handshake ACK2`, message);
|
|
545
|
+
connectCallHandlerAndMethodProxies();
|
|
546
|
+
};
|
|
547
|
+
const handleMessage = (message) => {
|
|
548
|
+
if (isSynMessage(message)) {
|
|
549
|
+
handleSynMessage(message);
|
|
550
|
+
}
|
|
551
|
+
if (isAck1Message(message)) {
|
|
552
|
+
handleAck1Message(message);
|
|
553
|
+
}
|
|
554
|
+
if (isAck2Message(message)) {
|
|
555
|
+
handleAck2Message(message);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
messenger.addMessageHandler(handleMessage);
|
|
559
|
+
destroyHandlers.push(() => messenger.removeMessageHandler(handleMessage));
|
|
560
|
+
sendSynMessage();
|
|
561
|
+
return promise;
|
|
562
|
+
};
|
|
563
|
+
var shakeHands_default = shakeHands;
|
|
564
|
+
|
|
565
|
+
// src/once.ts
|
|
566
|
+
var once = (fn) => {
|
|
567
|
+
let isCalled = false;
|
|
568
|
+
let result;
|
|
569
|
+
return (...args) => {
|
|
570
|
+
if (!isCalled) {
|
|
571
|
+
isCalled = true;
|
|
572
|
+
result = fn(...args);
|
|
573
|
+
}
|
|
574
|
+
return result;
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
var once_default = once;
|
|
578
|
+
|
|
579
|
+
// src/connect.ts
|
|
580
|
+
var usedMessengers = /* @__PURE__ */ new WeakSet();
|
|
581
|
+
var connect = ({
|
|
582
|
+
messenger,
|
|
583
|
+
methods = {},
|
|
584
|
+
timeout,
|
|
585
|
+
channel,
|
|
586
|
+
log
|
|
587
|
+
}) => {
|
|
588
|
+
if (!messenger) {
|
|
589
|
+
throw new PenpalError_default("INVALID_ARGUMENT", "messenger must be defined");
|
|
590
|
+
}
|
|
591
|
+
if (usedMessengers.has(messenger)) {
|
|
592
|
+
throw new PenpalError_default(
|
|
593
|
+
"INVALID_ARGUMENT",
|
|
594
|
+
"A messenger can only be used for a single connection"
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
usedMessengers.add(messenger);
|
|
598
|
+
const connectionDestroyedHandlers = [messenger.destroy];
|
|
599
|
+
const destroyConnection = once_default((notifyOtherParticipant) => {
|
|
600
|
+
if (notifyOtherParticipant) {
|
|
601
|
+
const destroyMessage = {
|
|
602
|
+
namespace: namespace_default,
|
|
603
|
+
channel,
|
|
604
|
+
type: "DESTROY"
|
|
605
|
+
};
|
|
606
|
+
try {
|
|
607
|
+
messenger.sendMessage(destroyMessage);
|
|
608
|
+
} catch (_) {
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
for (const connectionDestroyedHandler of connectionDestroyedHandlers) {
|
|
612
|
+
connectionDestroyedHandler();
|
|
613
|
+
}
|
|
614
|
+
log?.("Connection destroyed");
|
|
615
|
+
});
|
|
616
|
+
const validateReceivedMessage = (data) => {
|
|
617
|
+
return isMessage(data) && data.channel === channel;
|
|
618
|
+
};
|
|
619
|
+
const promise = (async () => {
|
|
620
|
+
try {
|
|
621
|
+
messenger.initialize({ log, validateReceivedMessage });
|
|
622
|
+
messenger.addMessageHandler((message) => {
|
|
623
|
+
if (isDestroyMessage(message)) {
|
|
624
|
+
destroyConnection(false);
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
const { remoteProxy, destroy } = await shakeHands_default({
|
|
628
|
+
messenger,
|
|
629
|
+
methods,
|
|
630
|
+
timeout,
|
|
631
|
+
channel,
|
|
632
|
+
log
|
|
633
|
+
});
|
|
634
|
+
connectionDestroyedHandlers.push(destroy);
|
|
635
|
+
return remoteProxy;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
destroyConnection(true);
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
640
|
+
})();
|
|
641
|
+
return {
|
|
642
|
+
promise,
|
|
643
|
+
// Why we don't reject the connection promise when consumer calls destroy():
|
|
644
|
+
// https://github.com/Aaronius/penpal/issues/51
|
|
645
|
+
destroy: () => {
|
|
646
|
+
destroyConnection(true);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
};
|
|
650
|
+
var connect_default = connect;
|
|
651
|
+
|
|
652
|
+
// src/messengers/WindowMessenger.ts
|
|
653
|
+
var WindowMessenger = class {
|
|
654
|
+
#remoteWindow;
|
|
655
|
+
#allowedOrigins;
|
|
656
|
+
#log;
|
|
657
|
+
#validateReceivedMessage;
|
|
658
|
+
#concreteRemoteOrigin;
|
|
659
|
+
#messageCallbacks = /* @__PURE__ */ new Set();
|
|
660
|
+
#port;
|
|
661
|
+
// TODO: Used for backward-compatibility. Remove in next major version.
|
|
662
|
+
#isChildUsingDeprecatedProtocol = false;
|
|
663
|
+
constructor({ remoteWindow, allowedOrigins }) {
|
|
664
|
+
if (!remoteWindow) {
|
|
665
|
+
throw new PenpalError_default("INVALID_ARGUMENT", "remoteWindow must be defined");
|
|
666
|
+
}
|
|
667
|
+
this.#remoteWindow = remoteWindow;
|
|
668
|
+
this.#allowedOrigins = allowedOrigins?.length ? allowedOrigins : [window.origin];
|
|
669
|
+
}
|
|
670
|
+
initialize = ({
|
|
671
|
+
log,
|
|
672
|
+
validateReceivedMessage
|
|
673
|
+
}) => {
|
|
674
|
+
this.#log = log;
|
|
675
|
+
this.#validateReceivedMessage = validateReceivedMessage;
|
|
676
|
+
window.addEventListener("message", this.#handleMessageFromRemoteWindow);
|
|
677
|
+
};
|
|
678
|
+
sendMessage = (message, transferables) => {
|
|
679
|
+
if (isSynMessage(message)) {
|
|
680
|
+
const originForSending = this.#getOriginForSendingMessage(message);
|
|
681
|
+
this.#remoteWindow.postMessage(message, {
|
|
682
|
+
targetOrigin: originForSending,
|
|
683
|
+
transfer: transferables
|
|
684
|
+
});
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (isAck1Message(message) || // If the child is using a previous version of Penpal, we need to
|
|
688
|
+
// downgrade the message and send it through the window rather than
|
|
689
|
+
// the port because older versions of Penpal don't use MessagePorts.
|
|
690
|
+
this.#isChildUsingDeprecatedProtocol) {
|
|
691
|
+
const payload = this.#isChildUsingDeprecatedProtocol ? downgradeMessage(message) : message;
|
|
692
|
+
const originForSending = this.#getOriginForSendingMessage(message);
|
|
693
|
+
this.#remoteWindow.postMessage(payload, {
|
|
694
|
+
targetOrigin: originForSending,
|
|
695
|
+
transfer: transferables
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (isAck2Message(message)) {
|
|
700
|
+
const { port1, port2 } = new MessageChannel();
|
|
701
|
+
this.#port = port1;
|
|
702
|
+
port1.addEventListener("message", this.#handleMessageFromPort);
|
|
703
|
+
port1.start();
|
|
704
|
+
const transferablesToSend = [port2, ...transferables || []];
|
|
705
|
+
const originForSending = this.#getOriginForSendingMessage(message);
|
|
706
|
+
this.#remoteWindow.postMessage(message, {
|
|
707
|
+
targetOrigin: originForSending,
|
|
708
|
+
transfer: transferablesToSend
|
|
709
|
+
});
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (this.#port) {
|
|
713
|
+
this.#port.postMessage(message, {
|
|
714
|
+
transfer: transferables
|
|
715
|
+
});
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
throw new PenpalBugError_default("Port is undefined");
|
|
719
|
+
};
|
|
720
|
+
addMessageHandler = (callback) => {
|
|
721
|
+
this.#messageCallbacks.add(callback);
|
|
722
|
+
};
|
|
723
|
+
removeMessageHandler = (callback) => {
|
|
724
|
+
this.#messageCallbacks.delete(callback);
|
|
725
|
+
};
|
|
726
|
+
destroy = () => {
|
|
727
|
+
window.removeEventListener("message", this.#handleMessageFromRemoteWindow);
|
|
728
|
+
this.#destroyPort();
|
|
729
|
+
this.#messageCallbacks.clear();
|
|
730
|
+
};
|
|
731
|
+
#isAllowedOrigin = (origin) => {
|
|
732
|
+
return this.#allowedOrigins.some(
|
|
733
|
+
(allowedOrigin) => allowedOrigin instanceof RegExp ? allowedOrigin.test(origin) : allowedOrigin === origin || allowedOrigin === "*"
|
|
734
|
+
);
|
|
735
|
+
};
|
|
736
|
+
#getOriginForSendingMessage = (message) => {
|
|
737
|
+
if (isSynMessage(message)) {
|
|
738
|
+
return "*";
|
|
739
|
+
}
|
|
740
|
+
if (!this.#concreteRemoteOrigin) {
|
|
741
|
+
throw new PenpalBugError_default("Concrete remote origin not set");
|
|
742
|
+
}
|
|
743
|
+
return this.#concreteRemoteOrigin === "null" && this.#allowedOrigins.includes("*") ? "*" : this.#concreteRemoteOrigin;
|
|
744
|
+
};
|
|
745
|
+
#destroyPort = () => {
|
|
746
|
+
this.#port?.removeEventListener("message", this.#handleMessageFromPort);
|
|
747
|
+
this.#port?.close();
|
|
748
|
+
this.#port = void 0;
|
|
749
|
+
};
|
|
750
|
+
#handleMessageFromRemoteWindow = ({
|
|
751
|
+
source,
|
|
752
|
+
origin,
|
|
753
|
+
ports,
|
|
754
|
+
data
|
|
755
|
+
}) => {
|
|
756
|
+
if (source !== this.#remoteWindow) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
if (isDeprecatedMessage(data)) {
|
|
760
|
+
this.#log?.(
|
|
761
|
+
"Please upgrade the child window to the latest version of Penpal."
|
|
762
|
+
);
|
|
763
|
+
this.#isChildUsingDeprecatedProtocol = true;
|
|
764
|
+
data = upgradeMessage(data);
|
|
765
|
+
}
|
|
766
|
+
if (!this.#validateReceivedMessage?.(data)) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
if (!this.#isAllowedOrigin(origin)) {
|
|
770
|
+
this.#log?.(
|
|
771
|
+
`Received a message from origin \`${origin}\` which did not match allowed origins \`[${this.#allowedOrigins.join(", ")}]\``
|
|
772
|
+
);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (isSynMessage(data)) {
|
|
776
|
+
this.#destroyPort();
|
|
777
|
+
this.#concreteRemoteOrigin = origin;
|
|
778
|
+
}
|
|
779
|
+
if (isAck2Message(data) && // Previous versions of Penpal don't use MessagePorts and do all
|
|
780
|
+
// communication through the window.
|
|
781
|
+
!this.#isChildUsingDeprecatedProtocol) {
|
|
782
|
+
this.#port = ports[0];
|
|
783
|
+
if (!this.#port) {
|
|
784
|
+
throw new PenpalBugError_default("No port received on ACK2");
|
|
785
|
+
}
|
|
786
|
+
this.#port.addEventListener("message", this.#handleMessageFromPort);
|
|
787
|
+
this.#port.start();
|
|
788
|
+
}
|
|
789
|
+
for (const callback of this.#messageCallbacks) {
|
|
790
|
+
callback(data);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
#handleMessageFromPort = ({ data }) => {
|
|
794
|
+
if (!this.#validateReceivedMessage?.(data)) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
for (const callback of this.#messageCallbacks) {
|
|
798
|
+
callback(data);
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
};
|
|
802
|
+
var WindowMessenger_default = WindowMessenger;
|
|
803
|
+
|
|
804
|
+
// src/messengers/WorkerMessenger.ts
|
|
805
|
+
var WorkerMessenger = class {
|
|
806
|
+
#worker;
|
|
807
|
+
#validateReceivedMessage;
|
|
808
|
+
#messageCallbacks = /* @__PURE__ */ new Set();
|
|
809
|
+
#port;
|
|
810
|
+
constructor({ worker }) {
|
|
811
|
+
if (!worker) {
|
|
812
|
+
throw new PenpalError_default("INVALID_ARGUMENT", "worker must be defined");
|
|
813
|
+
}
|
|
814
|
+
this.#worker = worker;
|
|
815
|
+
}
|
|
816
|
+
initialize = ({ validateReceivedMessage }) => {
|
|
817
|
+
this.#validateReceivedMessage = validateReceivedMessage;
|
|
818
|
+
this.#worker.addEventListener("message", this.#handleMessage);
|
|
819
|
+
};
|
|
820
|
+
sendMessage = (message, transferables) => {
|
|
821
|
+
if (isSynMessage(message) || isAck1Message(message)) {
|
|
822
|
+
this.#worker.postMessage(message, { transfer: transferables });
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (isAck2Message(message)) {
|
|
826
|
+
const { port1, port2 } = new MessageChannel();
|
|
827
|
+
this.#port = port1;
|
|
828
|
+
port1.addEventListener("message", this.#handleMessage);
|
|
829
|
+
port1.start();
|
|
830
|
+
this.#worker.postMessage(message, {
|
|
831
|
+
transfer: [port2, ...transferables || []]
|
|
832
|
+
});
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (this.#port) {
|
|
836
|
+
this.#port.postMessage(message, {
|
|
837
|
+
transfer: transferables
|
|
838
|
+
});
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
throw new PenpalBugError_default("Port is undefined");
|
|
842
|
+
};
|
|
843
|
+
addMessageHandler = (callback) => {
|
|
844
|
+
this.#messageCallbacks.add(callback);
|
|
845
|
+
};
|
|
846
|
+
removeMessageHandler = (callback) => {
|
|
847
|
+
this.#messageCallbacks.delete(callback);
|
|
848
|
+
};
|
|
849
|
+
destroy = () => {
|
|
850
|
+
this.#worker.removeEventListener("message", this.#handleMessage);
|
|
851
|
+
this.#destroyPort();
|
|
852
|
+
this.#messageCallbacks.clear();
|
|
853
|
+
};
|
|
854
|
+
#destroyPort = () => {
|
|
855
|
+
this.#port?.removeEventListener("message", this.#handleMessage);
|
|
856
|
+
this.#port?.close();
|
|
857
|
+
this.#port = void 0;
|
|
858
|
+
};
|
|
859
|
+
#handleMessage = ({ ports, data }) => {
|
|
860
|
+
if (!this.#validateReceivedMessage?.(data)) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (isSynMessage(data)) {
|
|
864
|
+
this.#destroyPort();
|
|
865
|
+
}
|
|
866
|
+
if (isAck2Message(data)) {
|
|
867
|
+
this.#port = ports[0];
|
|
868
|
+
if (!this.#port) {
|
|
869
|
+
throw new PenpalBugError_default("No port received on ACK2");
|
|
870
|
+
}
|
|
871
|
+
this.#port.addEventListener("message", this.#handleMessage);
|
|
872
|
+
this.#port.start();
|
|
873
|
+
}
|
|
874
|
+
for (const callback of this.#messageCallbacks) {
|
|
875
|
+
callback(data);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
};
|
|
879
|
+
var WorkerMessenger_default = WorkerMessenger;
|
|
880
|
+
|
|
881
|
+
// src/messengers/PortMessenger.ts
|
|
882
|
+
var PortMessenger = class {
|
|
883
|
+
#port;
|
|
884
|
+
#validateReceivedMessage;
|
|
885
|
+
#messageCallbacks = /* @__PURE__ */ new Set();
|
|
886
|
+
constructor({ port }) {
|
|
887
|
+
if (!port) {
|
|
888
|
+
throw new PenpalError_default("INVALID_ARGUMENT", "port must be defined");
|
|
889
|
+
}
|
|
890
|
+
this.#port = port;
|
|
891
|
+
}
|
|
892
|
+
initialize = ({ validateReceivedMessage }) => {
|
|
893
|
+
this.#validateReceivedMessage = validateReceivedMessage;
|
|
894
|
+
this.#port.addEventListener("message", this.#handleMessage);
|
|
895
|
+
this.#port.start();
|
|
896
|
+
};
|
|
897
|
+
sendMessage = (message, transferables) => {
|
|
898
|
+
this.#port?.postMessage(message, {
|
|
899
|
+
transfer: transferables
|
|
900
|
+
});
|
|
901
|
+
};
|
|
902
|
+
addMessageHandler = (callback) => {
|
|
903
|
+
this.#messageCallbacks.add(callback);
|
|
904
|
+
};
|
|
905
|
+
removeMessageHandler = (callback) => {
|
|
906
|
+
this.#messageCallbacks.delete(callback);
|
|
907
|
+
};
|
|
908
|
+
destroy = () => {
|
|
909
|
+
this.#port.removeEventListener("message", this.#handleMessage);
|
|
910
|
+
this.#port.close();
|
|
911
|
+
this.#messageCallbacks.clear();
|
|
912
|
+
};
|
|
913
|
+
#handleMessage = ({ data }) => {
|
|
914
|
+
if (!this.#validateReceivedMessage?.(data)) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
for (const callback of this.#messageCallbacks) {
|
|
918
|
+
callback(data);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
};
|
|
922
|
+
var PortMessenger_default = PortMessenger;
|
|
923
|
+
|
|
924
|
+
// src/ErrorCodeObj.ts
|
|
925
|
+
var ErrorCodeObj = {
|
|
926
|
+
ConnectionDestroyed: "CONNECTION_DESTROYED",
|
|
927
|
+
ConnectionTimeout: "CONNECTION_TIMEOUT",
|
|
928
|
+
InvalidArgument: "INVALID_ARGUMENT",
|
|
929
|
+
MethodCallTimeout: "METHOD_CALL_TIMEOUT",
|
|
930
|
+
MethodNotFound: "METHOD_NOT_FOUND",
|
|
931
|
+
TransmissionFailed: "TRANSMISSION_FAILED"
|
|
932
|
+
};
|
|
933
|
+
var ErrorCodeObj_default = ErrorCodeObj;
|
|
934
|
+
|
|
935
|
+
// src/debug.ts
|
|
936
|
+
var debug = (prefix) => {
|
|
937
|
+
return (...args) => {
|
|
938
|
+
console.log(`\u270D\uFE0F %c${prefix}%c`, "font-weight: bold;", "", ...args);
|
|
939
|
+
};
|
|
940
|
+
};
|
|
941
|
+
var debug_default = debug;
|
|
942
|
+
|
|
943
|
+
exports.CallOptions = CallOptions_default;
|
|
944
|
+
exports.ErrorCode = ErrorCodeObj_default;
|
|
945
|
+
exports.PenpalError = PenpalError_default;
|
|
946
|
+
exports.PortMessenger = PortMessenger_default;
|
|
947
|
+
exports.Reply = Reply_default;
|
|
948
|
+
exports.WindowMessenger = WindowMessenger_default;
|
|
949
|
+
exports.WorkerMessenger = WorkerMessenger_default;
|
|
950
|
+
exports.connect = connect_default;
|
|
951
|
+
exports.debug = debug_default;
|
|
952
|
+
//# sourceMappingURL=penpal.cjs.map
|
|
953
|
+
//# sourceMappingURL=penpal.cjs.map
|