osra 0.2.0 → 0.2.1
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 +284 -34
- package/build/extension/background.js +2 -0
- package/build/extension/background.js.map +1 -0
- package/build/extension/chunks/index.js +2 -0
- package/build/extension/chunks/index.js.map +1 -0
- package/build/extension/content.js +2 -0
- package/build/extension/content.js.map +1 -0
- package/build/extension/manifest.json +21 -0
- package/build/extension/popup.html +120 -0
- package/build/extension/popup.js +2 -0
- package/build/extension/popup.js.map +1 -0
- package/build/extension-test/background.js +753 -0
- package/build/extension-test/background.js.map +1 -0
- package/build/extension-test/content.js +4585 -0
- package/build/extension-test/content.js.map +1 -0
- package/build/extension-test/manifest.json +22 -0
- package/build/extension-test/popup.html +106 -0
- package/build/extension-test/popup.js +4610 -0
- package/build/extension-test/popup.js.map +1 -0
- package/build/extension-test-firefox/background.js +5464 -0
- package/build/extension-test-firefox/background.js.map +1 -0
- package/build/extension-test-firefox/content.js +5286 -0
- package/build/extension-test-firefox/content.js.map +1 -0
- package/build/extension-test-firefox/manifest.json +27 -0
- package/build/extension-test-firefox/popup.html +106 -0
- package/build/extension-test-firefox/popup.js +5213 -0
- package/build/extension-test-firefox/popup.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.js +30 -30
- package/build/index.js.map +1 -1
- package/build/test.js +24987 -0
- package/build/test.js.map +1 -0
- package/package.json +9 -4
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
const OSRA_KEY = "__OSRA_KEY__";
|
|
2
|
+
const OSRA_DEFAULT_KEY = "__OSRA_DEFAULT_KEY__";
|
|
3
|
+
const OSRA_BOX = "__OSRA_BOX__";
|
|
4
|
+
|
|
5
|
+
const makeMessageChannelAllocator = () => {
|
|
6
|
+
const channels = /* @__PURE__ */ new Map();
|
|
7
|
+
const result = {
|
|
8
|
+
getUniqueUuid: () => {
|
|
9
|
+
let uuid = globalThis.crypto.randomUUID();
|
|
10
|
+
while (channels.has(uuid)) {
|
|
11
|
+
uuid = globalThis.crypto.randomUUID();
|
|
12
|
+
}
|
|
13
|
+
return uuid;
|
|
14
|
+
},
|
|
15
|
+
set: (uuid, messagePorts) => {
|
|
16
|
+
channels.set(uuid, { uuid, ...messagePorts });
|
|
17
|
+
},
|
|
18
|
+
alloc: (uuid = result.getUniqueUuid(), messagePorts) => {
|
|
19
|
+
if (messagePorts) {
|
|
20
|
+
channels.set(uuid, { uuid, ...messagePorts });
|
|
21
|
+
return { uuid, ...messagePorts };
|
|
22
|
+
}
|
|
23
|
+
const messageChannel = new MessageChannel();
|
|
24
|
+
const allocatedMessageChannel = {
|
|
25
|
+
uuid,
|
|
26
|
+
port1: messageChannel.port1,
|
|
27
|
+
port2: messageChannel.port2
|
|
28
|
+
};
|
|
29
|
+
channels.set(uuid, allocatedMessageChannel);
|
|
30
|
+
return allocatedMessageChannel;
|
|
31
|
+
},
|
|
32
|
+
has: (uuid) => channels.has(uuid),
|
|
33
|
+
get: (uuid) => channels.get(uuid),
|
|
34
|
+
free: (uuid) => channels.delete(uuid),
|
|
35
|
+
getOrAlloc: (uuid = result.getUniqueUuid(), messagePorts) => {
|
|
36
|
+
const existingChannel = result.get(uuid);
|
|
37
|
+
if (existingChannel) return existingChannel;
|
|
38
|
+
return result.alloc(uuid, messagePorts);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const checkOsraMessageKey = (message, key) => isOsraMessage(message) && message[OSRA_KEY] === key;
|
|
45
|
+
const registerOsraMessageListener = ({ listener, transport, remoteName, key = OSRA_KEY, unregisterSignal }) => {
|
|
46
|
+
const registerListenerOnReceiveTransport = (receiveTransport) => {
|
|
47
|
+
if (typeof receiveTransport === "function") {
|
|
48
|
+
receiveTransport(listener);
|
|
49
|
+
} else if (isWebExtensionPort(receiveTransport) || isWebExtensionOnConnect(receiveTransport) || isWebExtensionOnMessage(receiveTransport)) {
|
|
50
|
+
const listenOnWebExtOnMessage = (onMessage, port) => {
|
|
51
|
+
const _listener = (message, sender) => {
|
|
52
|
+
if (!checkOsraMessageKey(message, key)) return;
|
|
53
|
+
if (remoteName && message.name !== remoteName) return;
|
|
54
|
+
listener(message, { port, sender });
|
|
55
|
+
};
|
|
56
|
+
onMessage.addListener(_listener);
|
|
57
|
+
if (unregisterSignal) {
|
|
58
|
+
unregisterSignal.addEventListener(
|
|
59
|
+
"abort",
|
|
60
|
+
() => onMessage.removeListener(_listener)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
if (isWebExtensionOnConnect(receiveTransport)) {
|
|
65
|
+
const _listener = (port) => {
|
|
66
|
+
listenOnWebExtOnMessage(port.onMessage, port);
|
|
67
|
+
};
|
|
68
|
+
receiveTransport.addListener(_listener);
|
|
69
|
+
if (unregisterSignal) {
|
|
70
|
+
unregisterSignal.addEventListener(
|
|
71
|
+
"abort",
|
|
72
|
+
() => receiveTransport.removeListener(_listener)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
} else if (isWebExtensionOnMessage(receiveTransport)) {
|
|
76
|
+
listenOnWebExtOnMessage(receiveTransport);
|
|
77
|
+
} else {
|
|
78
|
+
listenOnWebExtOnMessage(receiveTransport.onMessage);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
const _listener = (event) => {
|
|
82
|
+
if (!checkOsraMessageKey(event.data, key)) return;
|
|
83
|
+
if (remoteName && event.data.name !== remoteName) return;
|
|
84
|
+
listener(event.data, { receiveTransport, source: event.source });
|
|
85
|
+
};
|
|
86
|
+
receiveTransport.addEventListener("message", _listener);
|
|
87
|
+
if (unregisterSignal) {
|
|
88
|
+
unregisterSignal.addEventListener(
|
|
89
|
+
"abort",
|
|
90
|
+
() => receiveTransport.removeEventListener("message", _listener)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
if (isCustomTransport(transport)) {
|
|
96
|
+
registerListenerOnReceiveTransport(transport.receive);
|
|
97
|
+
} else {
|
|
98
|
+
registerListenerOnReceiveTransport(transport);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const sendOsraMessage = (transport, message, origin = "*", transferables = []) => {
|
|
102
|
+
const sendToEmitTransport = (emitTransport) => {
|
|
103
|
+
if (typeof emitTransport === "function") {
|
|
104
|
+
emitTransport(message, transferables);
|
|
105
|
+
} else if (isWebExtensionPort(emitTransport)) {
|
|
106
|
+
emitTransport.postMessage(message);
|
|
107
|
+
} else if (isWindow(emitTransport)) {
|
|
108
|
+
emitTransport.postMessage(message, origin, transferables);
|
|
109
|
+
} else if (isWebSocket(emitTransport)) {
|
|
110
|
+
emitTransport.send(JSON.stringify(message));
|
|
111
|
+
} else if (isSharedWorker(emitTransport)) {
|
|
112
|
+
emitTransport.port.postMessage(message, transferables);
|
|
113
|
+
} else {
|
|
114
|
+
emitTransport.postMessage(message, transferables);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
if (isCustomTransport(transport)) {
|
|
118
|
+
sendToEmitTransport(transport.emit);
|
|
119
|
+
} else {
|
|
120
|
+
sendToEmitTransport(transport);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const typedArrayConstructors = [
|
|
125
|
+
Int8Array,
|
|
126
|
+
Uint8Array,
|
|
127
|
+
Uint8ClampedArray,
|
|
128
|
+
Int16Array,
|
|
129
|
+
Uint16Array,
|
|
130
|
+
Int32Array,
|
|
131
|
+
Uint32Array,
|
|
132
|
+
Float16Array,
|
|
133
|
+
Float32Array,
|
|
134
|
+
Float64Array,
|
|
135
|
+
BigInt64Array,
|
|
136
|
+
BigUint64Array
|
|
137
|
+
];
|
|
138
|
+
[
|
|
139
|
+
new Int8Array(),
|
|
140
|
+
new Uint8Array(),
|
|
141
|
+
new Uint8ClampedArray(),
|
|
142
|
+
new Int16Array(),
|
|
143
|
+
new Uint16Array(),
|
|
144
|
+
new Int32Array(),
|
|
145
|
+
new Uint32Array(),
|
|
146
|
+
new Float16Array(),
|
|
147
|
+
new Float32Array(),
|
|
148
|
+
new Float64Array(),
|
|
149
|
+
new BigInt64Array(),
|
|
150
|
+
new BigUint64Array()
|
|
151
|
+
];
|
|
152
|
+
const typedArrayToType = (value) => {
|
|
153
|
+
const type = value instanceof Int8Array ? "Int8Array" : value instanceof Uint8Array ? "Uint8Array" : value instanceof Uint8ClampedArray ? "Uint8ClampedArray" : value instanceof Int16Array ? "Int16Array" : value instanceof Uint16Array ? "Uint16Array" : value instanceof Int32Array ? "Int32Array" : value instanceof Uint32Array ? "Uint32Array" : value instanceof Float16Array ? "Float16Array" : value instanceof Float32Array ? "Float32Array" : value instanceof Float64Array ? "Float64Array" : value instanceof BigInt64Array ? "BigInt64Array" : value instanceof BigUint64Array ? "BigUint64Array" : void 0;
|
|
154
|
+
if (type === void 0) throw new Error("Unknown typed array type");
|
|
155
|
+
return type;
|
|
156
|
+
};
|
|
157
|
+
const typedArrayTypeToTypedArrayConstructor = (value) => {
|
|
158
|
+
const typedArray = value === "Int8Array" ? Int8Array : value === "Uint8Array" ? Uint8Array : value === "Uint8ClampedArray" ? Uint8ClampedArray : value === "Int16Array" ? Int16Array : value === "Uint16Array" ? Uint16Array : value === "Int32Array" ? Int32Array : value === "Uint32Array" ? Uint32Array : value === "Float16Array" ? Float16Array : value === "Float32Array" ? Float32Array : value === "Float64Array" ? Float64Array : value === "BigInt64Array" ? BigInt64Array : value === "BigUint64Array" ? BigUint64Array : void 0;
|
|
159
|
+
if (typedArray === void 0) throw new Error("Unknown typed array type");
|
|
160
|
+
return typedArray;
|
|
161
|
+
};
|
|
162
|
+
const isTypedArray = (value) => typedArrayConstructors.some((typedArray) => value instanceof typedArray);
|
|
163
|
+
const isWebSocket = (value) => value instanceof WebSocket;
|
|
164
|
+
const isServiceWorkerContainer = (value) => globalThis.ServiceWorkerContainer && value instanceof ServiceWorkerContainer;
|
|
165
|
+
const isWorker = (value) => globalThis.Worker && value instanceof Worker;
|
|
166
|
+
const isDedicatedWorker = (value) => globalThis.DedicatedWorkerGlobalScope && value instanceof DedicatedWorkerGlobalScope;
|
|
167
|
+
const isSharedWorker = (value) => globalThis.SharedWorker && value instanceof SharedWorker;
|
|
168
|
+
const isMessagePort = (value) => value instanceof MessagePort;
|
|
169
|
+
const isPromise = (value) => value instanceof Promise;
|
|
170
|
+
const isFunction = (value) => typeof value === "function";
|
|
171
|
+
const isArrayBuffer = (value) => value instanceof ArrayBuffer;
|
|
172
|
+
const isReadableStream = (value) => value instanceof ReadableStream;
|
|
173
|
+
const isDate = (value) => value instanceof Date;
|
|
174
|
+
const isError = (value) => value instanceof Error;
|
|
175
|
+
const isAlwaysBox = (value) => isFunction(value) || isPromise(value) || isTypedArray(value) || isDate(value) || isError(value);
|
|
176
|
+
const isOsraMessage = (value) => Boolean(
|
|
177
|
+
value && typeof value === "object" && value[OSRA_KEY]
|
|
178
|
+
);
|
|
179
|
+
const isClonable = (value) => globalThis.SharedArrayBuffer && value instanceof globalThis.SharedArrayBuffer ? true : false;
|
|
180
|
+
const isTransferable = (value) => globalThis.ArrayBuffer && value instanceof globalThis.ArrayBuffer ? true : globalThis.MessagePort && value instanceof globalThis.MessagePort ? true : globalThis.ReadableStream && value instanceof globalThis.ReadableStream ? true : globalThis.WritableStream && value instanceof globalThis.WritableStream ? true : globalThis.TransformStream && value instanceof globalThis.TransformStream ? true : globalThis.ImageBitmap && value instanceof globalThis.ImageBitmap ? true : false;
|
|
181
|
+
const isWebExtensionPort = (value, connectPort = false) => {
|
|
182
|
+
return Boolean(
|
|
183
|
+
value && typeof value === "object" && value.name && value.disconnect && value.postMessage && (connectPort ? value.sender && value.onMessage && value.onDisconnect : true)
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
const isWebExtensionOnConnect = (value) => Boolean(
|
|
187
|
+
value && typeof value === "object" && value.addListener && value.hasListener && value.removeListener
|
|
188
|
+
);
|
|
189
|
+
const isWebExtensionOnMessage = (value) => Boolean(
|
|
190
|
+
value && typeof value === "object" && value.addListener && value.hasListener && value.removeListener
|
|
191
|
+
);
|
|
192
|
+
const isWindow = (value) => {
|
|
193
|
+
return Boolean(
|
|
194
|
+
value && typeof value === "object" && value.document && value.location && value.navigator && value.screen && value.history
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
const isEmitJsonOnlyTransport = (value) => isWebSocket(value) || isWebExtensionPort(value);
|
|
198
|
+
const isReceiveJsonOnlyTransport = (value) => isWebSocket(value) || isWebExtensionPort(value) || isWebExtensionOnConnect(value) || isWebExtensionOnMessage(value);
|
|
199
|
+
const isJsonOnlyTransport = (value) => isEmitJsonOnlyTransport(value) || isReceiveJsonOnlyTransport(value);
|
|
200
|
+
const isEmitTransport = (value) => isEmitJsonOnlyTransport(value) || isWindow(value) || isServiceWorkerContainer(value) || isWorker(value) || isDedicatedWorker(value) || isSharedWorker(value) || isMessagePort(value) || isCustomEmitTransport(value);
|
|
201
|
+
const isReceiveTransport = (value) => isReceiveJsonOnlyTransport(value) || isWindow(value) || isServiceWorkerContainer(value) || isWorker(value) || isDedicatedWorker(value) || isSharedWorker(value) || isMessagePort(value) || isCustomReceiveTransport(value);
|
|
202
|
+
const isCustomEmitTransport = (value) => Boolean(
|
|
203
|
+
value && typeof value === "object" && ("emit" in value && (isEmitTransport(value.emit) || typeof value.emit === "function"))
|
|
204
|
+
);
|
|
205
|
+
const isCustomReceiveTransport = (value) => Boolean(
|
|
206
|
+
value && typeof value === "object" && ("receive" in value && (isReceiveTransport(value.receive) || typeof value.receive === "function"))
|
|
207
|
+
);
|
|
208
|
+
const isCustomTransport = (value) => isCustomEmitTransport(value) || isCustomReceiveTransport(value);
|
|
209
|
+
const isRevivable = (value) => isMessagePort(value) || isFunction(value) || isPromise(value) || isTypedArray(value) || isArrayBuffer(value) || isReadableStream(value) || isDate(value) || isError(value);
|
|
210
|
+
const isRevivableBox = (value) => value && typeof value === "object" && OSRA_BOX in value && value[OSRA_BOX] === "revivable";
|
|
211
|
+
const isRevivableMessagePortBox = (value) => isRevivableBox(value) && value.type === "messagePort";
|
|
212
|
+
const isRevivablePromiseBox = (value) => isRevivableBox(value) && value.type === "promise";
|
|
213
|
+
const isRevivableFunctionBox = (value) => isRevivableBox(value) && value.type === "function";
|
|
214
|
+
const isRevivableTypedArrayBox = (value) => isRevivableBox(value) && value.type === "typedArray";
|
|
215
|
+
const isRevivableArrayBufferBox = (value) => isRevivableBox(value) && value.type === "arrayBuffer";
|
|
216
|
+
const isRevivableReadableStreamBox = (value) => isRevivableBox(value) && value.type === "readableStream";
|
|
217
|
+
const isRevivableErrorBox = (value) => isRevivableBox(value) && value.type === "error";
|
|
218
|
+
const isRevivableDateBox = (value) => isRevivableBox(value) && value.type === "date";
|
|
219
|
+
|
|
220
|
+
const getTransferableObjects = (value) => {
|
|
221
|
+
const transferables = [];
|
|
222
|
+
const recurse = (value2) => isClonable(value2) ? void 0 : isTransferable(value2) ? transferables.push(value2) : Array.isArray(value2) ? value2.map(recurse) : value2 && typeof value2 === "object" ? Object.values(value2).map(recurse) : void 0;
|
|
223
|
+
recurse(value);
|
|
224
|
+
return transferables;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const probePlatformCapabilityUtil = (value, transfer = false) => {
|
|
228
|
+
const { port1, port2 } = new MessageChannel();
|
|
229
|
+
const result = new Promise(
|
|
230
|
+
(resolve) => port1.addEventListener(
|
|
231
|
+
"message",
|
|
232
|
+
(message) => resolve(message.data)
|
|
233
|
+
)
|
|
234
|
+
);
|
|
235
|
+
port1.start();
|
|
236
|
+
port2.postMessage(value, transfer ? getTransferableObjects(value) : []);
|
|
237
|
+
return result;
|
|
238
|
+
};
|
|
239
|
+
const probeMessagePortTransfer = async () => {
|
|
240
|
+
const { port1 } = new MessageChannel();
|
|
241
|
+
const port = await probePlatformCapabilityUtil(port1, true);
|
|
242
|
+
return port instanceof MessagePort;
|
|
243
|
+
};
|
|
244
|
+
const probeArrayBufferClone = async () => {
|
|
245
|
+
const buffer = new ArrayBuffer(1);
|
|
246
|
+
const arrayBuffer = await probePlatformCapabilityUtil(buffer);
|
|
247
|
+
return arrayBuffer instanceof ArrayBuffer;
|
|
248
|
+
};
|
|
249
|
+
const probeArrayBufferTransfer = async () => {
|
|
250
|
+
const buffer = new ArrayBuffer(1);
|
|
251
|
+
const arrayBuffer = await probePlatformCapabilityUtil(buffer, true);
|
|
252
|
+
return arrayBuffer instanceof ArrayBuffer;
|
|
253
|
+
};
|
|
254
|
+
const probeTransferableStream = async () => {
|
|
255
|
+
const stream = new ReadableStream({
|
|
256
|
+
start(controller) {
|
|
257
|
+
controller.enqueue(new Uint8Array(1));
|
|
258
|
+
controller.close();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
const transferableStream = await probePlatformCapabilityUtil(stream, true);
|
|
262
|
+
return transferableStream instanceof ReadableStream;
|
|
263
|
+
};
|
|
264
|
+
const probePlatformCapabilities = async () => {
|
|
265
|
+
const [
|
|
266
|
+
messagePort,
|
|
267
|
+
arrayBuffer,
|
|
268
|
+
transferable,
|
|
269
|
+
transferableStream
|
|
270
|
+
] = await Promise.all([
|
|
271
|
+
probeMessagePortTransfer().catch(() => false),
|
|
272
|
+
probeArrayBufferClone().catch(() => false),
|
|
273
|
+
probeArrayBufferTransfer().catch(() => false),
|
|
274
|
+
probeTransferableStream().catch(() => false)
|
|
275
|
+
]);
|
|
276
|
+
return {
|
|
277
|
+
jsonOnly: !messagePort && !arrayBuffer && !transferable && !transferableStream,
|
|
278
|
+
messagePort,
|
|
279
|
+
arrayBuffer,
|
|
280
|
+
transferable,
|
|
281
|
+
transferableStream
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const boxMessagePort = (value, context) => {
|
|
286
|
+
const messagePort = value;
|
|
287
|
+
const { uuid: portId } = context.messageChannels.alloc(void 0, { port1: messagePort });
|
|
288
|
+
messagePort.addEventListener("message", ({ data }) => {
|
|
289
|
+
context.sendMessage({
|
|
290
|
+
type: "message",
|
|
291
|
+
remoteUuid: context.remoteUuid,
|
|
292
|
+
data: isRevivableBox(data) ? data : recursiveBox(data, context),
|
|
293
|
+
portId
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
messagePort.start();
|
|
297
|
+
context.eventTarget.addEventListener("message", function listener({ detail: message }) {
|
|
298
|
+
if (message.type === "message-port-close") {
|
|
299
|
+
if (message.portId !== portId) return;
|
|
300
|
+
context.eventTarget.removeEventListener("message", listener);
|
|
301
|
+
messagePort.close();
|
|
302
|
+
context.messageChannels.free(portId);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (message.type !== "message" || message.portId !== portId) return;
|
|
306
|
+
messagePort.postMessage(message.data, getTransferableObjects(message.data));
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
type: "messagePort",
|
|
310
|
+
portId
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
const reviveMessagePort = (value, context) => {
|
|
314
|
+
const { port1: userPort, port2: internalPort } = new MessageChannel();
|
|
315
|
+
internalPort.addEventListener("message", ({ data }) => {
|
|
316
|
+
context.sendMessage({
|
|
317
|
+
type: "message",
|
|
318
|
+
remoteUuid: context.remoteUuid,
|
|
319
|
+
data: isRevivableBox(data) ? data : recursiveBox(data, context),
|
|
320
|
+
portId: value.portId
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
internalPort.start();
|
|
324
|
+
const existingChannel = context.messageChannels.get(value.portId);
|
|
325
|
+
const { port1 } = existingChannel ? existingChannel : context.messageChannels.alloc(value.portId);
|
|
326
|
+
port1.addEventListener("message", function listener({ data: message }) {
|
|
327
|
+
if (message.type === "message-port-close") {
|
|
328
|
+
if (message.portId !== value.portId) return;
|
|
329
|
+
port1.removeEventListener("message", listener);
|
|
330
|
+
internalPort.close();
|
|
331
|
+
context.messageChannels.free(value.portId);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (message.type !== "message" || message.portId !== value.portId) return;
|
|
335
|
+
if (context.messagePorts.has(userPort)) {
|
|
336
|
+
internalPort.postMessage(message.data);
|
|
337
|
+
} else {
|
|
338
|
+
const revivedData = recursiveRevive(message.data, context);
|
|
339
|
+
internalPort.postMessage(revivedData, getTransferableObjects(revivedData));
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
port1.start();
|
|
343
|
+
return userPort;
|
|
344
|
+
};
|
|
345
|
+
const boxPromise = (value, context) => {
|
|
346
|
+
const { port1: localPort, port2: remotePort } = new MessageChannel();
|
|
347
|
+
context.messagePorts.add(remotePort);
|
|
348
|
+
const sendResult = (result) => {
|
|
349
|
+
const boxedResult = recursiveBox(result, context);
|
|
350
|
+
localPort.postMessage(boxedResult, getTransferableObjects(boxedResult));
|
|
351
|
+
localPort.close();
|
|
352
|
+
};
|
|
353
|
+
value.then((data) => sendResult({ type: "resolve", data })).catch((error) => sendResult({ type: "reject", error: error.stack }));
|
|
354
|
+
return {
|
|
355
|
+
type: "promise",
|
|
356
|
+
port: remotePort
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
const revivePromise = (value, context) => {
|
|
360
|
+
context.messagePorts.add(value.port);
|
|
361
|
+
return new Promise((resolve, reject) => {
|
|
362
|
+
value.port.addEventListener("message", ({ data }) => {
|
|
363
|
+
const result = recursiveRevive(data, context);
|
|
364
|
+
if (result.type === "resolve") {
|
|
365
|
+
resolve(result.data);
|
|
366
|
+
} else {
|
|
367
|
+
reject(result.error);
|
|
368
|
+
}
|
|
369
|
+
value.port.close();
|
|
370
|
+
}, { once: true });
|
|
371
|
+
value.port.start();
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
const boxFunction = (value, context) => {
|
|
375
|
+
const { port1: localPort, port2: remotePort } = new MessageChannel();
|
|
376
|
+
context.messagePorts.add(remotePort);
|
|
377
|
+
localPort.addEventListener("message", ({ data }) => {
|
|
378
|
+
const [returnValuePort, args] = recursiveRevive(data, context);
|
|
379
|
+
const result = (async () => value(...args))();
|
|
380
|
+
const boxedResult = recursiveBox(result, context);
|
|
381
|
+
returnValuePort.postMessage(boxedResult, getTransferableObjects(boxedResult));
|
|
382
|
+
});
|
|
383
|
+
localPort.start();
|
|
384
|
+
return {
|
|
385
|
+
type: "function",
|
|
386
|
+
port: remotePort
|
|
387
|
+
};
|
|
388
|
+
};
|
|
389
|
+
const reviveFunction = (value, context) => {
|
|
390
|
+
const func = (...args) => new Promise((resolve, reject) => {
|
|
391
|
+
const { port1: returnValueLocalPort, port2: returnValueRemotePort } = new MessageChannel();
|
|
392
|
+
context.messagePorts.add(returnValueRemotePort);
|
|
393
|
+
const callContext = recursiveBox([returnValueRemotePort, args], context);
|
|
394
|
+
value.port.postMessage(callContext, getTransferableObjects(callContext));
|
|
395
|
+
returnValueLocalPort.addEventListener("message", ({ data }) => {
|
|
396
|
+
if (!isRevivablePromiseBox(data)) throw new Error(`Proxied function did not return a promise`);
|
|
397
|
+
const result = recursiveRevive(data, context);
|
|
398
|
+
result.then(resolve).catch(reject).finally(() => returnValueLocalPort.close());
|
|
399
|
+
});
|
|
400
|
+
returnValueLocalPort.start();
|
|
401
|
+
});
|
|
402
|
+
return func;
|
|
403
|
+
};
|
|
404
|
+
const boxTypedArray = (value, context) => {
|
|
405
|
+
return {
|
|
406
|
+
type: "typedArray",
|
|
407
|
+
typedArrayType: typedArrayToType(value),
|
|
408
|
+
arrayBuffer: value.buffer
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
const reviveTypedArray = (value, context) => {
|
|
412
|
+
const TypedArrayConstructor = typedArrayTypeToTypedArrayConstructor(value.typedArrayType);
|
|
413
|
+
const result = new TypedArrayConstructor(value.arrayBuffer);
|
|
414
|
+
return result;
|
|
415
|
+
};
|
|
416
|
+
const boxArrayBuffer = (value, context) => {
|
|
417
|
+
return {
|
|
418
|
+
type: "arrayBuffer",
|
|
419
|
+
base64Buffer: new Uint8Array(value).toBase64()
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
const reviveArrayBuffer = (value, context) => {
|
|
423
|
+
return Uint8Array.fromBase64(value.base64Buffer).buffer;
|
|
424
|
+
};
|
|
425
|
+
const boxError = (value, context) => {
|
|
426
|
+
return {
|
|
427
|
+
type: "error",
|
|
428
|
+
message: value.message,
|
|
429
|
+
stack: value.stack || value.toString()
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
const reviveError = (value, context) => {
|
|
433
|
+
return new Error(value.message, { cause: value.stack });
|
|
434
|
+
};
|
|
435
|
+
const boxReadableStream = (value, context) => {
|
|
436
|
+
const { port1: localPort, port2: remotePort } = new MessageChannel();
|
|
437
|
+
context.messagePorts.add(remotePort);
|
|
438
|
+
const reader = value.getReader();
|
|
439
|
+
localPort.addEventListener("message", async ({ data }) => {
|
|
440
|
+
const { type } = recursiveRevive(data, context);
|
|
441
|
+
if (type === "pull") {
|
|
442
|
+
const pullResult = reader.read();
|
|
443
|
+
const boxedResult = recursiveBox(pullResult, context);
|
|
444
|
+
localPort.postMessage(boxedResult, getTransferableObjects(boxedResult));
|
|
445
|
+
} else {
|
|
446
|
+
reader.cancel();
|
|
447
|
+
localPort.close();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
localPort.start();
|
|
451
|
+
return {
|
|
452
|
+
type: "readableStream",
|
|
453
|
+
port: remotePort
|
|
454
|
+
};
|
|
455
|
+
};
|
|
456
|
+
const reviveReadableStream = (value, context) => {
|
|
457
|
+
context.messagePorts.add(value.port);
|
|
458
|
+
value.port.start();
|
|
459
|
+
return new ReadableStream({
|
|
460
|
+
start(controller) {
|
|
461
|
+
},
|
|
462
|
+
pull(controller) {
|
|
463
|
+
return new Promise((resolve, reject) => {
|
|
464
|
+
value.port.addEventListener("message", async ({ data }) => {
|
|
465
|
+
if (!isRevivablePromiseBox(data)) throw new Error(`Proxied function did not return a promise`);
|
|
466
|
+
const result = recursiveRevive(data, context);
|
|
467
|
+
result.then((result2) => {
|
|
468
|
+
if (result2.done) controller.close();
|
|
469
|
+
else controller.enqueue(result2.value);
|
|
470
|
+
resolve();
|
|
471
|
+
}).catch(reject);
|
|
472
|
+
}, { once: true });
|
|
473
|
+
value.port.postMessage(recursiveBox({ type: "pull" }, context));
|
|
474
|
+
});
|
|
475
|
+
},
|
|
476
|
+
cancel() {
|
|
477
|
+
value.port.postMessage(recursiveBox({ type: "cancel" }, context));
|
|
478
|
+
value.port.close();
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
};
|
|
482
|
+
const boxDate = (value, context) => {
|
|
483
|
+
return {
|
|
484
|
+
type: "date",
|
|
485
|
+
ISOString: value.toISOString()
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
const reviveDate = (value, context) => {
|
|
489
|
+
return new Date(value.ISOString);
|
|
490
|
+
};
|
|
491
|
+
const box = (value, context) => {
|
|
492
|
+
if (isAlwaysBox(value) || isReadableStream(value) && !context.platformCapabilities.transferableStream) {
|
|
493
|
+
return {
|
|
494
|
+
[OSRA_BOX]: "revivable",
|
|
495
|
+
...isFunction(value) ? boxFunction(value, context) : isPromise(value) ? boxPromise(value, context) : isTypedArray(value) ? boxTypedArray(value) : isReadableStream(value) ? boxReadableStream(value, context) : isDate(value) ? boxDate(value) : isError(value) ? boxError(value) : value
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
[OSRA_BOX]: "revivable",
|
|
500
|
+
..."isJson" in context.transport && context.transport.isJson ? isMessagePort(value) ? boxMessagePort(value, context) : isArrayBuffer(value) ? boxArrayBuffer(value) : isReadableStream(value) ? boxReadableStream(value, context) : { type: "unknown", value } : {
|
|
501
|
+
type: isMessagePort(value) ? "messagePort" : isArrayBuffer(value) ? "arrayBuffer" : isReadableStream(value) ? "readableStream" : "unknown",
|
|
502
|
+
value
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
};
|
|
506
|
+
const recursiveBox = (value, context) => {
|
|
507
|
+
const boxedValue = isRevivable(value) ? box(value, context) : value;
|
|
508
|
+
return Array.isArray(boxedValue) ? boxedValue.map((value2) => recursiveBox(value2, context)) : boxedValue && typeof boxedValue === "object" && Object.getPrototypeOf(boxedValue) === Object.prototype ? Object.fromEntries(
|
|
509
|
+
Object.entries(boxedValue).map(([key, value2]) => [
|
|
510
|
+
key,
|
|
511
|
+
isRevivableBox(boxedValue) && boxedValue.type === "messagePort" && boxedValue.value instanceof MessagePort || isRevivableBox(boxedValue) && boxedValue.type === "arrayBuffer" && boxedValue.value instanceof ArrayBuffer || isRevivableBox(boxedValue) && boxedValue.type === "readableStream" && boxedValue.value instanceof ReadableStream ? value2 : recursiveBox(value2, context)
|
|
512
|
+
])
|
|
513
|
+
) : boxedValue;
|
|
514
|
+
};
|
|
515
|
+
const revive = (box2, context) => {
|
|
516
|
+
if (isRevivable(box2.value)) return box2.value;
|
|
517
|
+
return isRevivableMessagePortBox(box2) ? reviveMessagePort(box2, context) : isRevivableFunctionBox(box2) ? reviveFunction(box2, context) : isRevivablePromiseBox(box2) ? revivePromise(box2, context) : isRevivableErrorBox(box2) ? reviveError(box2) : isRevivableTypedArrayBox(box2) ? reviveTypedArray(box2) : isRevivableArrayBufferBox(box2) ? reviveArrayBuffer(box2) : isRevivableReadableStreamBox(box2) ? reviveReadableStream(box2, context) : isRevivableDateBox(box2) ? reviveDate(box2) : box2;
|
|
518
|
+
};
|
|
519
|
+
const recursiveRevive = (value, context) => {
|
|
520
|
+
const recursedValue = isTransferable(value) ? value : Array.isArray(value) ? value.map((value2) => recursiveRevive(value2, context)) : value && typeof value === "object" ? Object.fromEntries(
|
|
521
|
+
Object.entries(value).map(([key, value2]) => [
|
|
522
|
+
key,
|
|
523
|
+
recursiveRevive(value2, context)
|
|
524
|
+
])
|
|
525
|
+
) : value;
|
|
526
|
+
return isRevivableBox(recursedValue) ? revive(recursedValue, context) : recursedValue;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const startBidirectionalConnection = ({ transport, value, uuid, remoteUuid, platformCapabilities, eventTarget, send, close }) => {
|
|
530
|
+
const revivableContext = {
|
|
531
|
+
platformCapabilities,
|
|
532
|
+
transport,
|
|
533
|
+
remoteUuid,
|
|
534
|
+
messagePorts: /* @__PURE__ */ new Set(),
|
|
535
|
+
messageChannels: makeMessageChannelAllocator(),
|
|
536
|
+
sendMessage: send,
|
|
537
|
+
eventTarget
|
|
538
|
+
};
|
|
539
|
+
let initResolve;
|
|
540
|
+
const initMessage = new Promise((resolve, reject) => {
|
|
541
|
+
initResolve = resolve;
|
|
542
|
+
});
|
|
543
|
+
eventTarget.addEventListener("message", ({ detail }) => {
|
|
544
|
+
if (detail.type === "init") {
|
|
545
|
+
initResolve(detail);
|
|
546
|
+
return;
|
|
547
|
+
} else if (detail.type === "message") {
|
|
548
|
+
const messageChannel = revivableContext.messageChannels.getOrAlloc(detail.portId);
|
|
549
|
+
messageChannel.port2?.postMessage(detail);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
send({
|
|
553
|
+
type: "init",
|
|
554
|
+
remoteUuid,
|
|
555
|
+
data: recursiveBox(value, revivableContext)
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
revivableContext,
|
|
559
|
+
close: () => {
|
|
560
|
+
},
|
|
561
|
+
remoteValue: initMessage.then((initMessage2) => recursiveRevive(initMessage2.data, revivableContext))
|
|
562
|
+
};
|
|
563
|
+
};
|
|
564
|
+
const startUnidirectionalEmittingConnection = ({ value, uuid, platformCapabilities, send, close }) => {
|
|
565
|
+
return {
|
|
566
|
+
close: () => {
|
|
567
|
+
},
|
|
568
|
+
remoteValueProxy: new Proxy(
|
|
569
|
+
new Function(),
|
|
570
|
+
{
|
|
571
|
+
apply: (target, thisArg, args) => {
|
|
572
|
+
},
|
|
573
|
+
get: (target, prop) => {
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
var e=class extends EventTarget{dispatchTypedEvent(s,t){return super.dispatchEvent(t)}};
|
|
581
|
+
|
|
582
|
+
const expose = async (value, {
|
|
583
|
+
transport: _transport,
|
|
584
|
+
name,
|
|
585
|
+
remoteName,
|
|
586
|
+
key = OSRA_DEFAULT_KEY,
|
|
587
|
+
origin = "*",
|
|
588
|
+
unregisterSignal,
|
|
589
|
+
platformCapabilities: _platformCapabilities,
|
|
590
|
+
transferAll,
|
|
591
|
+
logger
|
|
592
|
+
}) => {
|
|
593
|
+
const transport = {
|
|
594
|
+
isJson: "isJson" in _transport && _transport.isJson !== void 0 ? _transport.isJson : isJsonOnlyTransport(_transport),
|
|
595
|
+
...isCustomTransport(_transport) ? _transport : {
|
|
596
|
+
emit: _transport,
|
|
597
|
+
receive: _transport
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
const platformCapabilities = _platformCapabilities ?? await probePlatformCapabilities();
|
|
601
|
+
const connectionContexts = /* @__PURE__ */ new Map();
|
|
602
|
+
let resolveRemoteValue;
|
|
603
|
+
const remoteValuePromise = new Promise((resolve) => {
|
|
604
|
+
resolveRemoteValue = resolve;
|
|
605
|
+
});
|
|
606
|
+
let uuid = globalThis.crypto.randomUUID();
|
|
607
|
+
const sendMessage = (transport2, message) => {
|
|
608
|
+
const transferables = getTransferableObjects(message);
|
|
609
|
+
sendOsraMessage(
|
|
610
|
+
transport2,
|
|
611
|
+
{
|
|
612
|
+
[OSRA_KEY]: key,
|
|
613
|
+
name,
|
|
614
|
+
uuid,
|
|
615
|
+
...message
|
|
616
|
+
},
|
|
617
|
+
origin,
|
|
618
|
+
transferables
|
|
619
|
+
);
|
|
620
|
+
};
|
|
621
|
+
const listener = async (message, messageContext) => {
|
|
622
|
+
if (message.uuid === uuid) return;
|
|
623
|
+
if (!isEmitTransport(transport)) {
|
|
624
|
+
throw new Error("Unidirectional receiving mode not implemented");
|
|
625
|
+
}
|
|
626
|
+
if (message.type === "announce") {
|
|
627
|
+
if (!message.remoteUuid) {
|
|
628
|
+
sendMessage(transport, { type: "announce", remoteUuid: message.uuid });
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (message.remoteUuid !== uuid) return;
|
|
632
|
+
if (connectionContexts.has(message.uuid)) {
|
|
633
|
+
sendMessage(
|
|
634
|
+
transport,
|
|
635
|
+
{ type: "reject-uuid-taken", remoteUuid: message.uuid }
|
|
636
|
+
);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const eventTarget = new e();
|
|
640
|
+
const connectionContext = {
|
|
641
|
+
type: "bidirectional",
|
|
642
|
+
eventTarget,
|
|
643
|
+
connection: startBidirectionalConnection({
|
|
644
|
+
transport,
|
|
645
|
+
value,
|
|
646
|
+
uuid,
|
|
647
|
+
remoteUuid: message.uuid,
|
|
648
|
+
platformCapabilities,
|
|
649
|
+
eventTarget,
|
|
650
|
+
send: (message2) => sendMessage(transport, message2),
|
|
651
|
+
close: () => void connectionContexts.delete(message.uuid)
|
|
652
|
+
})
|
|
653
|
+
};
|
|
654
|
+
connectionContexts.set(message.uuid, connectionContext);
|
|
655
|
+
connectionContext.connection.remoteValue.then(
|
|
656
|
+
(remoteValue) => resolveRemoteValue(remoteValue)
|
|
657
|
+
);
|
|
658
|
+
} else if (message.type === "reject-uuid-taken") {
|
|
659
|
+
if (message.remoteUuid !== uuid) return;
|
|
660
|
+
uuid = globalThis.crypto.randomUUID();
|
|
661
|
+
sendMessage(transport, { type: "announce" });
|
|
662
|
+
} else if (message.type === "close") {
|
|
663
|
+
if (message.remoteUuid !== uuid) return;
|
|
664
|
+
const connectionContext = connectionContexts.get(message.uuid);
|
|
665
|
+
if (!connectionContext) {
|
|
666
|
+
console.warn(`Connection not found for remoteUuid: ${message.uuid}`);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
connectionContext.connection.close();
|
|
670
|
+
connectionContexts.delete(message.uuid);
|
|
671
|
+
} else {
|
|
672
|
+
if (message.remoteUuid !== uuid) return;
|
|
673
|
+
const connection = connectionContexts.get(message.uuid);
|
|
674
|
+
if (!connection) {
|
|
675
|
+
console.warn(`Connection not found for remoteUuid: ${message.uuid}`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (connection.type !== "unidirectional-emitting") {
|
|
679
|
+
connection.eventTarget.dispatchTypedEvent(
|
|
680
|
+
"message",
|
|
681
|
+
new CustomEvent("message", { detail: message })
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
if (isReceiveTransport(transport)) {
|
|
687
|
+
registerOsraMessageListener({
|
|
688
|
+
listener,
|
|
689
|
+
transport,
|
|
690
|
+
remoteName,
|
|
691
|
+
key,
|
|
692
|
+
unregisterSignal
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
if (isEmitTransport(transport)) {
|
|
696
|
+
sendMessage(transport, { type: "announce" });
|
|
697
|
+
}
|
|
698
|
+
if (isEmitTransport(transport) && !isReceiveTransport(transport)) {
|
|
699
|
+
const { remoteValueProxy } = startUnidirectionalEmittingConnection({
|
|
700
|
+
value,
|
|
701
|
+
uuid,
|
|
702
|
+
platformCapabilities,
|
|
703
|
+
send: (message) => sendMessage(transport, message),
|
|
704
|
+
close: () => connectionContexts.delete(uuid)
|
|
705
|
+
});
|
|
706
|
+
return remoteValueProxy;
|
|
707
|
+
}
|
|
708
|
+
return remoteValuePromise;
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
const api = {
|
|
712
|
+
echo: async (data) => data,
|
|
713
|
+
add: async (a, b) => a + b,
|
|
714
|
+
math: {
|
|
715
|
+
multiply: async (a, b) => a * b,
|
|
716
|
+
divide: async (a, b) => a / b
|
|
717
|
+
},
|
|
718
|
+
createCallback: async () => async () => 42,
|
|
719
|
+
callWithCallback: async (cb) => cb(),
|
|
720
|
+
getDate: async () => /* @__PURE__ */ new Date(),
|
|
721
|
+
getError: async () => new Error("Test error"),
|
|
722
|
+
throwError: async () => {
|
|
723
|
+
throw new Error("Thrown error");
|
|
724
|
+
},
|
|
725
|
+
processBuffer: async (data) => new Uint8Array(data.map((x) => x * 2)),
|
|
726
|
+
getBuffer: async () => {
|
|
727
|
+
const buffer = new ArrayBuffer(16);
|
|
728
|
+
new Uint8Array(buffer).forEach((_, i, arr) => arr[i] = i);
|
|
729
|
+
return buffer;
|
|
730
|
+
},
|
|
731
|
+
getPromise: async () => Promise.resolve(123),
|
|
732
|
+
getStream: async () => new ReadableStream({
|
|
733
|
+
start(controller) {
|
|
734
|
+
controller.enqueue(new Uint8Array([1, 2, 3]));
|
|
735
|
+
controller.enqueue(new Uint8Array([4, 5, 6]));
|
|
736
|
+
controller.close();
|
|
737
|
+
}
|
|
738
|
+
})
|
|
739
|
+
};
|
|
740
|
+
const jsonOnlyCapabilities = {
|
|
741
|
+
jsonOnly: true,
|
|
742
|
+
messagePort: false,
|
|
743
|
+
arrayBuffer: false,
|
|
744
|
+
transferable: false,
|
|
745
|
+
transferableStream: false
|
|
746
|
+
};
|
|
747
|
+
chrome.runtime.onConnect.addListener((port) => {
|
|
748
|
+
expose(api, {
|
|
749
|
+
transport: { isJson: true, emit: port, receive: port },
|
|
750
|
+
platformCapabilities: jsonOnlyCapabilities
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
//# sourceMappingURL=background.js.map
|