chrome-in-iframe 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/CHANGELOG.zh-CN.md +104 -7
- package/README.md +12 -10
- package/README.zh-CN.md +13 -11
- package/dist/api.d.ts +2 -0
- package/dist/channel/channel.d.ts +6 -2
- package/dist/channel/deserializer.d.ts +7 -1
- package/dist/channel/listener.d.ts +1 -1
- package/dist/channel/path.d.ts +1 -1
- package/dist/channel/sender.d.ts +1 -1
- package/dist/channel/serializer.d.ts +4 -1
- package/dist/channel/types.d.ts +23 -4
- package/dist/channel/utils.d.ts +4 -1
- package/dist/client/context.d.ts +1 -1
- package/dist/client/endpoint.d.ts +3 -2
- package/dist/client/proxy.d.ts +1 -1
- package/dist/index.cjs +2153 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +937 -563
- package/dist/processor/accessProperty.d.ts +4 -4
- package/dist/processor/helpers.d.ts +21 -0
- package/dist/processor/invoke.d.ts +4 -4
- package/dist/processor/invokeCallback.d.ts +4 -4
- package/dist/processor/lifecycle.d.ts +4 -2
- package/dist/processor/readProperty.d.ts +1 -1
- package/dist/processor/registry.d.ts +1 -1
- package/package.json +6 -3
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2153 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TRANSPORT_DETACHED = Symbol.for('chrome-in-iframe.transport-detached');
|
|
4
|
+
function isTransportDetachedError(err) {
|
|
5
|
+
return err instanceof Error && err[TRANSPORT_DETACHED] === true;
|
|
6
|
+
}
|
|
7
|
+
function createTransportDetachedError(message) {
|
|
8
|
+
const err = new Error(message);
|
|
9
|
+
Object.defineProperty(err, TRANSPORT_DETACHED, { value: true, enumerable: false });
|
|
10
|
+
return err;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let urlAlphabet =
|
|
14
|
+
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';
|
|
15
|
+
|
|
16
|
+
/* @ts-self-types="./index.d.ts" */
|
|
17
|
+
let nanoid = (size = 21) => {
|
|
18
|
+
let id = '';
|
|
19
|
+
let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));
|
|
20
|
+
while (size--) {
|
|
21
|
+
id += urlAlphabet[bytes[size] & 63];
|
|
22
|
+
}
|
|
23
|
+
return id
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const PREFIX = 'chrome-in-iframe';
|
|
27
|
+
const defaultLogger = (scope, ...args) => {
|
|
28
|
+
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
|
|
29
|
+
console.warn(`[${PREFIX}] ${scope}`, ...args);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
let activeLogger = defaultLogger;
|
|
33
|
+
function setLogger(logger) {
|
|
34
|
+
activeLogger = logger;
|
|
35
|
+
}
|
|
36
|
+
function warn(scope, ...args) {
|
|
37
|
+
if (!activeLogger)
|
|
38
|
+
return;
|
|
39
|
+
activeLogger(scope, ...args);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const WELL_KNOWN_SYMBOLS = {
|
|
43
|
+
asyncIterator: Symbol.asyncIterator,
|
|
44
|
+
hasInstance: Symbol.hasInstance,
|
|
45
|
+
isConcatSpreadable: Symbol.isConcatSpreadable,
|
|
46
|
+
iterator: Symbol.iterator,
|
|
47
|
+
match: Symbol.match,
|
|
48
|
+
matchAll: Symbol.matchAll,
|
|
49
|
+
replace: Symbol.replace,
|
|
50
|
+
search: Symbol.search,
|
|
51
|
+
species: Symbol.species,
|
|
52
|
+
split: Symbol.split,
|
|
53
|
+
toPrimitive: Symbol.toPrimitive,
|
|
54
|
+
toStringTag: Symbol.toStringTag,
|
|
55
|
+
unscopables: Symbol.unscopables,
|
|
56
|
+
};
|
|
57
|
+
const WELL_KNOWN_SYMBOL_NAMES = new Map(Object.entries(WELL_KNOWN_SYMBOLS).map(([name, symbol]) => [symbol, name]));
|
|
58
|
+
function getWellKnownSymbolName(value) {
|
|
59
|
+
return WELL_KNOWN_SYMBOL_NAMES.get(value);
|
|
60
|
+
}
|
|
61
|
+
function getWellKnownSymbol(name) {
|
|
62
|
+
return WELL_KNOWN_SYMBOLS[name];
|
|
63
|
+
}
|
|
64
|
+
function encodeSymbolToken(value, context) {
|
|
65
|
+
const globalKey = Symbol.keyFor(value);
|
|
66
|
+
if (globalKey)
|
|
67
|
+
return `global:${globalKey}`;
|
|
68
|
+
const wellKnownName = getWellKnownSymbolName(value);
|
|
69
|
+
if (wellKnownName)
|
|
70
|
+
return `wellKnown:${wellKnownName}`;
|
|
71
|
+
throw new TypeError(context ? `Cannot serialize non-global symbol ${context}` : 'Cannot serialize non-global symbol');
|
|
72
|
+
}
|
|
73
|
+
function decodeSymbolToken(value, context) {
|
|
74
|
+
if (value.startsWith('global:')) {
|
|
75
|
+
return Symbol.for(value.slice('global:'.length));
|
|
76
|
+
}
|
|
77
|
+
if (value.startsWith('wellKnown:')) {
|
|
78
|
+
const symbol = getWellKnownSymbol(value.slice('wellKnown:'.length));
|
|
79
|
+
if (symbol)
|
|
80
|
+
return symbol;
|
|
81
|
+
}
|
|
82
|
+
throw new TypeError(context ? `Cannot deserialize symbol ${context}` : 'Cannot deserialize symbol');
|
|
83
|
+
}
|
|
84
|
+
function setOwnProperty(target, key, value) {
|
|
85
|
+
Object.defineProperty(target, key, {
|
|
86
|
+
value,
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function serializeThrownError(err) {
|
|
93
|
+
if (err instanceof Error) {
|
|
94
|
+
return { message: err.message, stack: err.stack };
|
|
95
|
+
}
|
|
96
|
+
return { message: String(err) };
|
|
97
|
+
}
|
|
98
|
+
function isPromiseLike(value) {
|
|
99
|
+
return (value !== null &&
|
|
100
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
101
|
+
typeof value.then === 'function');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function serializePath(path) {
|
|
105
|
+
return path.map((key) => {
|
|
106
|
+
if (typeof key === 'string')
|
|
107
|
+
return key;
|
|
108
|
+
return {
|
|
109
|
+
$type: 'symbol',
|
|
110
|
+
value: encodeSymbolToken(key, 'path key'),
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function deserializePath(path) {
|
|
115
|
+
return path.map((key) => {
|
|
116
|
+
if (typeof key === 'string')
|
|
117
|
+
return key;
|
|
118
|
+
return decodeSymbolToken(key.value, 'path key');
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function isSerializedPath(value) {
|
|
122
|
+
return Array.isArray(value) && value.every(isSerializedPathKey);
|
|
123
|
+
}
|
|
124
|
+
function isSerializedPathKey(value) {
|
|
125
|
+
if (typeof value === 'string')
|
|
126
|
+
return true;
|
|
127
|
+
if (typeof value !== 'object' || value === null)
|
|
128
|
+
return false;
|
|
129
|
+
const record = value;
|
|
130
|
+
return record.$type === 'symbol' && typeof record.value === 'string';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function createMessageSender(poster, key, instanceId) {
|
|
134
|
+
return {
|
|
135
|
+
sendMessage(type, data, targetInstanceId) {
|
|
136
|
+
const body = {
|
|
137
|
+
type,
|
|
138
|
+
key,
|
|
139
|
+
data: serializeMessageData(type, data),
|
|
140
|
+
senderInstanceId: instanceId,
|
|
141
|
+
};
|
|
142
|
+
if (targetInstanceId !== undefined)
|
|
143
|
+
body.targetInstanceId = targetInstanceId;
|
|
144
|
+
poster.postMessage(JSON.stringify(body));
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function serializeMessageData(type, data) {
|
|
149
|
+
if (type === 'invokeRequest') {
|
|
150
|
+
const invokeData = data;
|
|
151
|
+
return {
|
|
152
|
+
...invokeData,
|
|
153
|
+
path: serializePath(invokeData.path),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (type === 'accessPropertyRequest') {
|
|
157
|
+
const accessData = data;
|
|
158
|
+
return {
|
|
159
|
+
...accessData,
|
|
160
|
+
path: serializePath(accessData.path),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const MAX_PATH_LENGTH = 64;
|
|
167
|
+
const MAX_INVOKE_ARGS = 1024;
|
|
168
|
+
const MAX_MESSAGE_LENGTH = 1000000;
|
|
169
|
+
const MAX_RELEASE_IDS = 10000;
|
|
170
|
+
const MAX_ID_LENGTH = 200;
|
|
171
|
+
const MAX_TYPE_LENGTH = 64;
|
|
172
|
+
const MESSAGE_SPECS = {
|
|
173
|
+
connectRequest: { validate: isConnectMessage },
|
|
174
|
+
connectResponse: { validate: isConnectMessage },
|
|
175
|
+
invokeRequest: {
|
|
176
|
+
validate: isInvokeRequest,
|
|
177
|
+
preDeserialize: (data) => ({ ...data, path: deserializePath(data.path) }),
|
|
178
|
+
errorResponse: { type: 'invokeResponse', extractId: (data) => data.id },
|
|
179
|
+
},
|
|
180
|
+
invokeResponse: { validate: isResponse },
|
|
181
|
+
accessPropertyRequest: {
|
|
182
|
+
validate: isAccessPropertyRequest,
|
|
183
|
+
preDeserialize: (data) => ({ ...data, path: deserializePath(data.path) }),
|
|
184
|
+
errorResponse: { type: 'accessPropertyResponse', extractId: (data) => data.id },
|
|
185
|
+
},
|
|
186
|
+
accessPropertyResponse: { validate: isResponse },
|
|
187
|
+
invokeFunctionByIdRequest: {
|
|
188
|
+
validate: isCallbackRequest,
|
|
189
|
+
errorResponse: { type: 'invokeFunctionByIdResponse', extractId: (data) => data.callId },
|
|
190
|
+
},
|
|
191
|
+
invokeFunctionByIdResponse: { validate: isResponse },
|
|
192
|
+
releaseCallbacks: { validate: isReleaseCallbacks },
|
|
193
|
+
destroyEndpoint: { validate: isDestroyEndpoint },
|
|
194
|
+
};
|
|
195
|
+
function createMessageChannel(poster, key, instanceId, context, processorRegistry) {
|
|
196
|
+
const sender = createMessageSender(poster, key, instanceId);
|
|
197
|
+
const keyMatch = `"key":${JSON.stringify(key)}`;
|
|
198
|
+
const listener = (event) => {
|
|
199
|
+
if (typeof event.data !== 'string')
|
|
200
|
+
return;
|
|
201
|
+
if (event.data.length === 0 || event.data.charCodeAt(0) !== 123)
|
|
202
|
+
return;
|
|
203
|
+
if (event.data.length > MAX_MESSAGE_LENGTH) {
|
|
204
|
+
warn('createMessageChannel', `dropping oversized message (${event.data.length} > ${MAX_MESSAGE_LENGTH})`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (event.data.indexOf(keyMatch) === -1)
|
|
208
|
+
return;
|
|
209
|
+
let body;
|
|
210
|
+
try {
|
|
211
|
+
body = JSON.parse(event.data);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
warn('createMessageChannel', 'failed to parse incoming message as JSON', err);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (!isMessageEnvelope(body))
|
|
218
|
+
return;
|
|
219
|
+
if (body.key !== key)
|
|
220
|
+
return;
|
|
221
|
+
if (body.senderInstanceId === instanceId)
|
|
222
|
+
return;
|
|
223
|
+
if (body.targetInstanceId !== undefined && body.targetInstanceId !== instanceId)
|
|
224
|
+
return;
|
|
225
|
+
const handler = processorRegistry.get(body.type);
|
|
226
|
+
if (!handler)
|
|
227
|
+
return;
|
|
228
|
+
if (!isValidMessagePayload(body))
|
|
229
|
+
return;
|
|
230
|
+
const meta = { senderInstanceId: body.senderInstanceId };
|
|
231
|
+
context.noteRemoteSeen(body.senderInstanceId);
|
|
232
|
+
try {
|
|
233
|
+
handler(deserializeMessageData(body), context, meta);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
sendProcessorError(body, sender, err);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
poster.addEventListener('message', listener);
|
|
240
|
+
return {
|
|
241
|
+
getSender() {
|
|
242
|
+
return sender;
|
|
243
|
+
},
|
|
244
|
+
destroy() {
|
|
245
|
+
poster.removeEventListener('message', listener);
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function isMessageEnvelope(value) {
|
|
250
|
+
if (!isRecord(value))
|
|
251
|
+
return false;
|
|
252
|
+
if (typeof value.type !== 'string' || value.type.length === 0 || value.type.length > MAX_TYPE_LENGTH)
|
|
253
|
+
return false;
|
|
254
|
+
if (typeof value.key !== 'string')
|
|
255
|
+
return false;
|
|
256
|
+
if (typeof value.senderInstanceId !== 'string' ||
|
|
257
|
+
value.senderInstanceId.length === 0 ||
|
|
258
|
+
value.senderInstanceId.length > MAX_ID_LENGTH)
|
|
259
|
+
return false;
|
|
260
|
+
if ('targetInstanceId' in value &&
|
|
261
|
+
value.targetInstanceId !== undefined &&
|
|
262
|
+
(typeof value.targetInstanceId !== 'string' || value.targetInstanceId.length > MAX_ID_LENGTH))
|
|
263
|
+
return false;
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
function isValidMessagePayload(value) {
|
|
267
|
+
const spec = MESSAGE_SPECS[value.type];
|
|
268
|
+
return spec ? spec.validate(value.data) : false;
|
|
269
|
+
}
|
|
270
|
+
function isConnectMessage(value) {
|
|
271
|
+
return isRecord(value) && isMessageId(value.instanceId);
|
|
272
|
+
}
|
|
273
|
+
function isInvokeRequest(value) {
|
|
274
|
+
return (isRecord(value) &&
|
|
275
|
+
isMessageId(value.id) &&
|
|
276
|
+
isSerializedPath(value.path) &&
|
|
277
|
+
value.path.length <= MAX_PATH_LENGTH &&
|
|
278
|
+
Array.isArray(value.args) &&
|
|
279
|
+
value.args.length <= MAX_INVOKE_ARGS);
|
|
280
|
+
}
|
|
281
|
+
function isAccessPropertyRequest(value) {
|
|
282
|
+
return (isRecord(value) &&
|
|
283
|
+
isMessageId(value.id) &&
|
|
284
|
+
isSerializedPath(value.path) &&
|
|
285
|
+
value.path.length <= MAX_PATH_LENGTH);
|
|
286
|
+
}
|
|
287
|
+
function isCallbackRequest(value) {
|
|
288
|
+
return (isRecord(value) &&
|
|
289
|
+
isMessageId(value.id) &&
|
|
290
|
+
isMessageId(value.callId) &&
|
|
291
|
+
Array.isArray(value.args) &&
|
|
292
|
+
value.args.length <= MAX_INVOKE_ARGS);
|
|
293
|
+
}
|
|
294
|
+
function isResponse(value) {
|
|
295
|
+
return (isRecord(value) &&
|
|
296
|
+
isMessageId(value.id) &&
|
|
297
|
+
(!('error' in value) || isSerializedError(value.error)) &&
|
|
298
|
+
('data' in value || 'error' in value));
|
|
299
|
+
}
|
|
300
|
+
function isReleaseCallbacks(value) {
|
|
301
|
+
return (isRecord(value) && Array.isArray(value.ids) && value.ids.length <= MAX_RELEASE_IDS && value.ids.every(isMessageId));
|
|
302
|
+
}
|
|
303
|
+
function isDestroyEndpoint(value) {
|
|
304
|
+
return isRecord(value) && isMessageId(value.instanceId);
|
|
305
|
+
}
|
|
306
|
+
function isSerializedError(value) {
|
|
307
|
+
return (isRecord(value) && typeof value.message === 'string' && (!('stack' in value) || typeof value.stack === 'string'));
|
|
308
|
+
}
|
|
309
|
+
function isMessageId(value) {
|
|
310
|
+
return typeof value === 'string' && value.length > 0 && value.length <= MAX_ID_LENGTH;
|
|
311
|
+
}
|
|
312
|
+
function deserializeMessageData(body) {
|
|
313
|
+
const spec = MESSAGE_SPECS[body.type];
|
|
314
|
+
if (spec.preDeserialize) {
|
|
315
|
+
return spec.preDeserialize(body.data);
|
|
316
|
+
}
|
|
317
|
+
return body.data;
|
|
318
|
+
}
|
|
319
|
+
function isRecord(value) {
|
|
320
|
+
return typeof value === 'object' && value !== null;
|
|
321
|
+
}
|
|
322
|
+
function sendProcessorError(body, sender, err) {
|
|
323
|
+
const spec = MESSAGE_SPECS[body.type];
|
|
324
|
+
if (!spec.errorResponse)
|
|
325
|
+
return;
|
|
326
|
+
const error = serializeThrownError(err);
|
|
327
|
+
if (body.type === 'invokeFunctionByIdRequest') {
|
|
328
|
+
const data = body.data;
|
|
329
|
+
warn('sendProcessorError', `callback '${data.id}' failed: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
sender.sendMessage(spec.errorResponse.type, { id: spec.errorResponse.extractId(body.data), error }, body.senderInstanceId);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function isListenerRegistrationPath(path) {
|
|
335
|
+
const last = path[path.length - 1];
|
|
336
|
+
return last === 'addListener';
|
|
337
|
+
}
|
|
338
|
+
function isListenerRemovalPath(path) {
|
|
339
|
+
const last = path[path.length - 1];
|
|
340
|
+
return last === 'removeListener';
|
|
341
|
+
}
|
|
342
|
+
function isLikelyListenerPath(path) {
|
|
343
|
+
const last = path[path.length - 1];
|
|
344
|
+
return typeof last === 'string' && /^on[A-Z]/.test(last);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const TYPE_KEY = '$cii$';
|
|
348
|
+
const MAX_SERIALIZE_DEPTH = 200;
|
|
349
|
+
function serialize(arg, registerFunction, options = {}) {
|
|
350
|
+
return serialize0(arg, undefined, registerFunction, options, new WeakSet(), 0);
|
|
351
|
+
}
|
|
352
|
+
function serialize0(arg, owner, registerFunction, options, seen, depth) {
|
|
353
|
+
if (depth > MAX_SERIALIZE_DEPTH) {
|
|
354
|
+
throw new TypeError(`Serialization exceeded maximum depth of ${MAX_SERIALIZE_DEPTH}`);
|
|
355
|
+
}
|
|
356
|
+
if (arg === undefined)
|
|
357
|
+
return { [TYPE_KEY]: 'undef' };
|
|
358
|
+
if (arg === null)
|
|
359
|
+
return null;
|
|
360
|
+
const typeOf = typeof arg;
|
|
361
|
+
if (typeOf === 'string')
|
|
362
|
+
return arg;
|
|
363
|
+
if (typeOf === 'boolean')
|
|
364
|
+
return arg;
|
|
365
|
+
if (typeOf === 'number')
|
|
366
|
+
return serializeNumber(arg);
|
|
367
|
+
if (typeOf === 'bigint')
|
|
368
|
+
return { [TYPE_KEY]: 'big', v: arg.toString() };
|
|
369
|
+
if (typeOf === 'function') {
|
|
370
|
+
const id = registerFunction(arg, owner, options);
|
|
371
|
+
return options.persistent ? { [TYPE_KEY]: 'fn', id, persistent: true } : { [TYPE_KEY]: 'fn', id };
|
|
372
|
+
}
|
|
373
|
+
if (typeOf === 'symbol')
|
|
374
|
+
return { [TYPE_KEY]: 'sym', v: encodeSymbolToken(arg) };
|
|
375
|
+
if (typeOf === 'object') {
|
|
376
|
+
if (seen.has(arg)) {
|
|
377
|
+
throw new TypeError('Cannot serialize circular structure');
|
|
378
|
+
}
|
|
379
|
+
seen.add(arg);
|
|
380
|
+
try {
|
|
381
|
+
if (Array.isArray(arg)) {
|
|
382
|
+
return arg.map((item) => serialize0(item, arg, registerFunction, options, seen, depth + 1));
|
|
383
|
+
}
|
|
384
|
+
const proto = Object.getPrototypeOf(arg);
|
|
385
|
+
if (proto === Object.prototype || proto === null) {
|
|
386
|
+
return serializePlainObject(arg, registerFunction, options, seen, depth);
|
|
387
|
+
}
|
|
388
|
+
if (arg instanceof Error)
|
|
389
|
+
return serializeError(arg, owner, registerFunction, options, seen, depth);
|
|
390
|
+
if (arg instanceof Date)
|
|
391
|
+
return { [TYPE_KEY]: 'date', v: arg.getTime() };
|
|
392
|
+
if (arg instanceof RegExp)
|
|
393
|
+
return { [TYPE_KEY]: 're', source: arg.source, flags: arg.flags };
|
|
394
|
+
if (arg instanceof Map) {
|
|
395
|
+
const entries = [];
|
|
396
|
+
for (const [k, v] of arg.entries()) {
|
|
397
|
+
entries.push([
|
|
398
|
+
serialize0(k, arg, registerFunction, options, seen, depth + 1),
|
|
399
|
+
serialize0(v, arg, registerFunction, options, seen, depth + 1),
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
return { [TYPE_KEY]: 'map', entries };
|
|
403
|
+
}
|
|
404
|
+
if (arg instanceof Set) {
|
|
405
|
+
const values = [];
|
|
406
|
+
for (const v of arg.values()) {
|
|
407
|
+
values.push(serialize0(v, arg, registerFunction, options, seen, depth + 1));
|
|
408
|
+
}
|
|
409
|
+
return { [TYPE_KEY]: 'set', values };
|
|
410
|
+
}
|
|
411
|
+
return serializePlainObject(arg, registerFunction, options, seen, depth);
|
|
412
|
+
}
|
|
413
|
+
finally {
|
|
414
|
+
seen.delete(arg);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
function serializeError(err, owner, registerFunction, options, seen, depth) {
|
|
420
|
+
const result = { [TYPE_KEY]: 'err', message: err.message };
|
|
421
|
+
if (err.name && err.name !== 'Error')
|
|
422
|
+
result.name = err.name;
|
|
423
|
+
if (err.stack !== undefined)
|
|
424
|
+
result.stack = err.stack;
|
|
425
|
+
const cause = err.cause;
|
|
426
|
+
if (cause !== undefined) {
|
|
427
|
+
result.cause = serialize0(cause, owner, registerFunction, options, seen, depth + 1);
|
|
428
|
+
}
|
|
429
|
+
const RESERVED_ERROR_KEYS = new Set(['message', 'name', 'stack', 'cause']);
|
|
430
|
+
const extras = [];
|
|
431
|
+
for (const k of Reflect.ownKeys(err)) {
|
|
432
|
+
if (typeof k === 'symbol')
|
|
433
|
+
continue;
|
|
434
|
+
if (RESERVED_ERROR_KEYS.has(k))
|
|
435
|
+
continue;
|
|
436
|
+
if (!isEnumerable(err, k))
|
|
437
|
+
continue;
|
|
438
|
+
const value = err[k];
|
|
439
|
+
extras.push([k, serialize0(value, owner, registerFunction, options, seen, depth + 1)]);
|
|
440
|
+
}
|
|
441
|
+
if (extras.length > 0)
|
|
442
|
+
result.extras = extras;
|
|
443
|
+
return result;
|
|
444
|
+
}
|
|
445
|
+
function serializePlainObject(source, registerFunction, options, seen, depth) {
|
|
446
|
+
const record = source;
|
|
447
|
+
const ownKeys = Reflect.ownKeys(source).filter((k) => isEnumerable(source, k));
|
|
448
|
+
const symbolKeys = [];
|
|
449
|
+
const stringKeys = [];
|
|
450
|
+
for (const k of ownKeys) {
|
|
451
|
+
if (typeof k === 'symbol') {
|
|
452
|
+
if (isSerializableSymbol(k))
|
|
453
|
+
symbolKeys.push(k);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
stringKeys.push(k);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const hasReservedKey = Object.prototype.hasOwnProperty.call(record, TYPE_KEY);
|
|
460
|
+
if (symbolKeys.length > 0 || hasReservedKey) {
|
|
461
|
+
const entries = [];
|
|
462
|
+
for (const k of stringKeys) {
|
|
463
|
+
entries.push([k, serialize0(record[k], source, registerFunction, options, seen, depth + 1)]);
|
|
464
|
+
}
|
|
465
|
+
for (const k of symbolKeys) {
|
|
466
|
+
entries.push([
|
|
467
|
+
{ [TYPE_KEY]: 'sym', v: encodeSymbolToken(k) },
|
|
468
|
+
serialize0(record[k], source, registerFunction, options, seen, depth + 1),
|
|
469
|
+
]);
|
|
470
|
+
}
|
|
471
|
+
return { [TYPE_KEY]: 'obj', entries };
|
|
472
|
+
}
|
|
473
|
+
const result = {};
|
|
474
|
+
for (const k of stringKeys) {
|
|
475
|
+
const value = serialize0(record[k], source, registerFunction, options, seen, depth + 1);
|
|
476
|
+
if (k === '__proto__') {
|
|
477
|
+
setOwnProperty(result, k, value);
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
result[k] = value;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
485
|
+
function isSerializableSymbol(value) {
|
|
486
|
+
if (Symbol.keyFor(value) !== undefined)
|
|
487
|
+
return true;
|
|
488
|
+
return getWellKnownSymbolName(value) !== undefined;
|
|
489
|
+
}
|
|
490
|
+
function isEnumerable(target, key) {
|
|
491
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
492
|
+
return descriptor ? descriptor.enumerable === true : false;
|
|
493
|
+
}
|
|
494
|
+
function serializeNumber(value) {
|
|
495
|
+
if (Number.isNaN(value))
|
|
496
|
+
return { [TYPE_KEY]: 'num', v: 'NaN' };
|
|
497
|
+
if (value === Infinity)
|
|
498
|
+
return { [TYPE_KEY]: 'num', v: 'Infinity' };
|
|
499
|
+
if (value === -Infinity)
|
|
500
|
+
return { [TYPE_KEY]: 'num', v: '-Infinity' };
|
|
501
|
+
if (Object.is(value, -0))
|
|
502
|
+
return { [TYPE_KEY]: 'num', v: '-0' };
|
|
503
|
+
return value;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const MAX_DESERIALIZE_DEPTH = 200;
|
|
507
|
+
const ERROR_FACTORIES = {
|
|
508
|
+
Error: (m) => new Error(m),
|
|
509
|
+
TypeError: (m) => new TypeError(m),
|
|
510
|
+
RangeError: (m) => new RangeError(m),
|
|
511
|
+
SyntaxError: (m) => new SyntaxError(m),
|
|
512
|
+
ReferenceError: (m) => new ReferenceError(m),
|
|
513
|
+
URIError: (m) => new URIError(m),
|
|
514
|
+
EvalError: (m) => new EvalError(m),
|
|
515
|
+
};
|
|
516
|
+
const globalScope = globalThis;
|
|
517
|
+
const AggregateErrorCtorRef = globalScope.AggregateError;
|
|
518
|
+
if (typeof AggregateErrorCtorRef === 'function') {
|
|
519
|
+
ERROR_FACTORIES.AggregateError = (m) => new AggregateErrorCtorRef([], m);
|
|
520
|
+
}
|
|
521
|
+
const DOMExceptionCtorRef = globalScope.DOMException;
|
|
522
|
+
if (typeof DOMExceptionCtorRef === 'function') {
|
|
523
|
+
ERROR_FACTORIES.DOMException = (m) => new DOMExceptionCtorRef(m);
|
|
524
|
+
}
|
|
525
|
+
function deserialize(arg, generateCallback, getRemoteCallback) {
|
|
526
|
+
return deserialize0(arg, generateCallback, getRemoteCallback, 0);
|
|
527
|
+
}
|
|
528
|
+
function deserialize0(arg, generateCallback, getRemoteCallback, depth) {
|
|
529
|
+
if (depth > MAX_DESERIALIZE_DEPTH) {
|
|
530
|
+
throw new TypeError(`Deserialization exceeded maximum depth of ${MAX_DESERIALIZE_DEPTH}`);
|
|
531
|
+
}
|
|
532
|
+
if (arg === null)
|
|
533
|
+
return null;
|
|
534
|
+
if (typeof arg !== 'object')
|
|
535
|
+
return arg;
|
|
536
|
+
if (Array.isArray(arg)) {
|
|
537
|
+
return arg.map((item) => deserialize0(item, generateCallback, getRemoteCallback, depth + 1));
|
|
538
|
+
}
|
|
539
|
+
const source = arg;
|
|
540
|
+
const tag = source[TYPE_KEY];
|
|
541
|
+
if (typeof tag === 'string') {
|
|
542
|
+
return deserializeTagged(tag, source, generateCallback, getRemoteCallback, depth);
|
|
543
|
+
}
|
|
544
|
+
return deserializePlainObject(source, generateCallback, getRemoteCallback, depth);
|
|
545
|
+
}
|
|
546
|
+
function deserializePlainObject(source, generateCallback, getRemoteCallback, depth) {
|
|
547
|
+
const result = {};
|
|
548
|
+
for (const key of Object.keys(source)) {
|
|
549
|
+
const value = deserialize0(source[key], generateCallback, getRemoteCallback, depth + 1);
|
|
550
|
+
if (key === '__proto__') {
|
|
551
|
+
setOwnProperty(result, key, value);
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
result[key] = value;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
function deserializeTagged(tag, source, generateCallback, getRemoteCallback, depth) {
|
|
560
|
+
switch (tag) {
|
|
561
|
+
case 'undef':
|
|
562
|
+
return undefined;
|
|
563
|
+
case 'big':
|
|
564
|
+
return deserializeBigInt(source.v);
|
|
565
|
+
case 'sym':
|
|
566
|
+
return deserializeSymbol(expectString(source.v, 'symbol'));
|
|
567
|
+
case 'num':
|
|
568
|
+
return deserializeNumber(expectString(source.v, 'number'));
|
|
569
|
+
case 'err':
|
|
570
|
+
return deserializeError(source, generateCallback, getRemoteCallback, depth);
|
|
571
|
+
case 'date':
|
|
572
|
+
return deserializeDate(source.v);
|
|
573
|
+
case 're':
|
|
574
|
+
return deserializeRegExp(source.source, source.flags);
|
|
575
|
+
case 'map':
|
|
576
|
+
return deserializeMap(source.entries, generateCallback, getRemoteCallback, depth);
|
|
577
|
+
case 'set':
|
|
578
|
+
return deserializeSet(source.values, generateCallback, getRemoteCallback, depth);
|
|
579
|
+
case 'fn':
|
|
580
|
+
return deserializeFunction(source, generateCallback, getRemoteCallback);
|
|
581
|
+
case 'obj':
|
|
582
|
+
return deserializeWrappedObject(source.entries, generateCallback, getRemoteCallback, depth);
|
|
583
|
+
default:
|
|
584
|
+
return deserializePlainObject(source, generateCallback, getRemoteCallback, depth);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function deserializeBigInt(raw) {
|
|
588
|
+
if (typeof raw !== 'string')
|
|
589
|
+
throw new TypeError('Invalid bigint payload');
|
|
590
|
+
try {
|
|
591
|
+
return BigInt(raw);
|
|
592
|
+
}
|
|
593
|
+
catch {
|
|
594
|
+
throw new TypeError(`Invalid bigint payload: ${raw}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function deserializeDate(raw) {
|
|
598
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
|
|
599
|
+
throw new TypeError('Invalid date payload');
|
|
600
|
+
}
|
|
601
|
+
return new Date(raw);
|
|
602
|
+
}
|
|
603
|
+
function deserializeRegExp(rawSource, rawFlags) {
|
|
604
|
+
if (typeof rawSource !== 'string')
|
|
605
|
+
throw new TypeError('Invalid regexp source');
|
|
606
|
+
if (typeof rawFlags !== 'string')
|
|
607
|
+
throw new TypeError('Invalid regexp flags');
|
|
608
|
+
try {
|
|
609
|
+
return new RegExp(rawSource, rawFlags);
|
|
610
|
+
}
|
|
611
|
+
catch (err) {
|
|
612
|
+
throw new TypeError(`Invalid regexp payload: ${err instanceof Error ? err.message : String(err)}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function deserializeMap(entries, generateCallback, getRemoteCallback, depth) {
|
|
616
|
+
const result = new Map();
|
|
617
|
+
if (!Array.isArray(entries))
|
|
618
|
+
return result;
|
|
619
|
+
for (const entry of entries) {
|
|
620
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
621
|
+
continue;
|
|
622
|
+
const k = deserialize0(entry[0], generateCallback, getRemoteCallback, depth + 1);
|
|
623
|
+
const v = deserialize0(entry[1], generateCallback, getRemoteCallback, depth + 1);
|
|
624
|
+
result.set(k, v);
|
|
625
|
+
}
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
function deserializeSet(values, generateCallback, getRemoteCallback, depth) {
|
|
629
|
+
const result = new Set();
|
|
630
|
+
if (!Array.isArray(values))
|
|
631
|
+
return result;
|
|
632
|
+
for (const v of values) {
|
|
633
|
+
result.add(deserialize0(v, generateCallback, getRemoteCallback, depth + 1));
|
|
634
|
+
}
|
|
635
|
+
return result;
|
|
636
|
+
}
|
|
637
|
+
function deserializeFunction(source, generateCallback, getRemoteCallback) {
|
|
638
|
+
const id = source.id;
|
|
639
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
640
|
+
throw new TypeError('Invalid function payload');
|
|
641
|
+
}
|
|
642
|
+
const persistent = source.persistent === true;
|
|
643
|
+
const fnOptions = { persistent };
|
|
644
|
+
const invoke = (args) => {
|
|
645
|
+
if (persistent)
|
|
646
|
+
return generateCallback(id, args, fnOptions);
|
|
647
|
+
return generateCallback(id, args);
|
|
648
|
+
};
|
|
649
|
+
return getRemoteCallback ? getRemoteCallback(id, invoke, fnOptions) : (...args) => invoke(args);
|
|
650
|
+
}
|
|
651
|
+
function rebuildErrorFromSerialized(serialized) {
|
|
652
|
+
const name = typeof serialized.name === 'string' && serialized.name ? serialized.name : 'Error';
|
|
653
|
+
const factory = ERROR_FACTORIES[name] ?? ERROR_FACTORIES.Error;
|
|
654
|
+
const error = factory(serialized.message);
|
|
655
|
+
if (name && error.name !== name) {
|
|
656
|
+
try {
|
|
657
|
+
error.name = name;
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
try {
|
|
661
|
+
Object.defineProperty(error, 'name', { value: name, configurable: true, writable: true });
|
|
662
|
+
}
|
|
663
|
+
catch (err) {
|
|
664
|
+
warn('rebuildErrorFromSerialized', 'failed to set error.name', name, err);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (typeof serialized.stack === 'string')
|
|
669
|
+
error.stack = serialized.stack;
|
|
670
|
+
return error;
|
|
671
|
+
}
|
|
672
|
+
function deserializeError(source, generateCallback, getRemoteCallback, depth) {
|
|
673
|
+
const error = rebuildErrorFromSerialized({
|
|
674
|
+
message: typeof source.message === 'string' ? source.message : '',
|
|
675
|
+
name: typeof source.name === 'string' ? source.name : undefined,
|
|
676
|
+
stack: typeof source.stack === 'string' ? source.stack : undefined,
|
|
677
|
+
});
|
|
678
|
+
if (source.cause !== undefined) {
|
|
679
|
+
error.cause = deserialize0(source.cause, generateCallback, getRemoteCallback, depth + 1);
|
|
680
|
+
}
|
|
681
|
+
if (Array.isArray(source.extras)) {
|
|
682
|
+
for (const entry of source.extras) {
|
|
683
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
684
|
+
continue;
|
|
685
|
+
const [rawKey, rawValue] = entry;
|
|
686
|
+
if (typeof rawKey !== 'string')
|
|
687
|
+
continue;
|
|
688
|
+
try {
|
|
689
|
+
setOwnProperty(error, rawKey, deserialize0(rawValue, generateCallback, getRemoteCallback, depth + 1));
|
|
690
|
+
}
|
|
691
|
+
catch (err) {
|
|
692
|
+
warn('deserializeError', 'failed to set error property', rawKey, err);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return error;
|
|
697
|
+
}
|
|
698
|
+
function deserializeWrappedObject(entries, generateCallback, getRemoteCallback, depth) {
|
|
699
|
+
const result = {};
|
|
700
|
+
if (!Array.isArray(entries))
|
|
701
|
+
return result;
|
|
702
|
+
for (const entry of entries) {
|
|
703
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
704
|
+
continue;
|
|
705
|
+
const rawKey = entry[0];
|
|
706
|
+
const value = deserialize0(entry[1], generateCallback, getRemoteCallback, depth + 1);
|
|
707
|
+
const key = resolveObjectKey(rawKey);
|
|
708
|
+
if (key === undefined) {
|
|
709
|
+
warn('deserializeWrappedObject', 'dropping wrapped-object entry with unresolvable key', rawKey);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
setOwnProperty(result, key, value);
|
|
713
|
+
}
|
|
714
|
+
return result;
|
|
715
|
+
}
|
|
716
|
+
function resolveObjectKey(raw) {
|
|
717
|
+
if (typeof raw === 'string')
|
|
718
|
+
return raw;
|
|
719
|
+
if (raw !== null && typeof raw === 'object') {
|
|
720
|
+
const tagged = raw;
|
|
721
|
+
if (tagged[TYPE_KEY] === 'sym' && typeof tagged.v === 'string') {
|
|
722
|
+
try {
|
|
723
|
+
return deserializeSymbol(tagged.v);
|
|
724
|
+
}
|
|
725
|
+
catch (err) {
|
|
726
|
+
warn('resolveObjectKey', 'failed to deserialize symbol key', tagged.v, err);
|
|
727
|
+
return undefined;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return undefined;
|
|
732
|
+
}
|
|
733
|
+
function expectString(raw, label) {
|
|
734
|
+
if (typeof raw !== 'string')
|
|
735
|
+
throw new TypeError(`Invalid ${label} payload`);
|
|
736
|
+
return raw;
|
|
737
|
+
}
|
|
738
|
+
function deserializeNumber(raw) {
|
|
739
|
+
if (raw === 'NaN')
|
|
740
|
+
return NaN;
|
|
741
|
+
if (raw === 'Infinity')
|
|
742
|
+
return Infinity;
|
|
743
|
+
if (raw === '-Infinity')
|
|
744
|
+
return -Infinity;
|
|
745
|
+
if (raw === '-0')
|
|
746
|
+
return -0;
|
|
747
|
+
return Number(raw);
|
|
748
|
+
}
|
|
749
|
+
function deserializeSymbol(value) {
|
|
750
|
+
return decodeSymbolToken(value);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function createResponder(ctx, targetInstanceId, responseType, id) {
|
|
754
|
+
const sender = () => ctx.getMessageChannel().getSender();
|
|
755
|
+
const sendError = (error) => {
|
|
756
|
+
sender().sendMessage(responseType, { id, error }, targetInstanceId);
|
|
757
|
+
};
|
|
758
|
+
return {
|
|
759
|
+
respondError(message) {
|
|
760
|
+
sendError({ message });
|
|
761
|
+
},
|
|
762
|
+
respondThrown(err) {
|
|
763
|
+
sendError(serializeThrownError(err));
|
|
764
|
+
},
|
|
765
|
+
respondSuccess(value) {
|
|
766
|
+
try {
|
|
767
|
+
sender().sendMessage(responseType, { id, data: ctx.serializeForRemote(value, targetInstanceId) }, targetInstanceId);
|
|
768
|
+
}
|
|
769
|
+
catch (err) {
|
|
770
|
+
sendError(serializeThrownError(err));
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function createScopedRemoteCallback(ctx, remoteInstanceId) {
|
|
776
|
+
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
777
|
+
}
|
|
778
|
+
function createGenerateCallback(ctx, remoteInstanceId) {
|
|
779
|
+
return (id, args, options) => ctx.invokeFunctionById(id, args, options, remoteInstanceId);
|
|
780
|
+
}
|
|
781
|
+
function safeInvokeReject(callbacks, scope, warn) {
|
|
782
|
+
if (!callbacks.onReject)
|
|
783
|
+
return;
|
|
784
|
+
try {
|
|
785
|
+
callbacks.onReject();
|
|
786
|
+
}
|
|
787
|
+
catch (err) {
|
|
788
|
+
warn(scope, 'onReject hook threw', err);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
function safeInvokeResolve(callbacks, scope, warn) {
|
|
792
|
+
if (!callbacks.onResolve)
|
|
793
|
+
return;
|
|
794
|
+
try {
|
|
795
|
+
callbacks.onResolve();
|
|
796
|
+
}
|
|
797
|
+
catch (err) {
|
|
798
|
+
warn(scope, 'onResolve hook threw', err);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function createResponseHandler(scope, warn) {
|
|
802
|
+
return function handleResponse(data, ctx, meta) {
|
|
803
|
+
const pending = ctx.getAndRemovePendingPromise(data.id, meta.senderInstanceId);
|
|
804
|
+
if (!pending)
|
|
805
|
+
return;
|
|
806
|
+
if (data.error) {
|
|
807
|
+
const err = rebuildErrorFromSerialized(data.error);
|
|
808
|
+
safeInvokeReject(pending, scope, warn);
|
|
809
|
+
pending.reject(err);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
try {
|
|
813
|
+
safeInvokeResolve(pending, scope, warn);
|
|
814
|
+
pending.resolve(deserialize(data.data, createGenerateCallback(ctx, meta.senderInstanceId), createScopedRemoteCallback(ctx, meta.senderInstanceId)));
|
|
815
|
+
}
|
|
816
|
+
catch (err) {
|
|
817
|
+
safeInvokeReject(pending, scope, warn);
|
|
818
|
+
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const DANGEROUS_KEYS = new Set([
|
|
824
|
+
'__proto__',
|
|
825
|
+
'constructor',
|
|
826
|
+
'prototype',
|
|
827
|
+
'__defineGetter__',
|
|
828
|
+
'__defineSetter__',
|
|
829
|
+
'__lookupGetter__',
|
|
830
|
+
'__lookupSetter__',
|
|
831
|
+
]);
|
|
832
|
+
function readProperty(target, key) {
|
|
833
|
+
if (typeof key === 'string' && DANGEROUS_KEYS.has(key)) {
|
|
834
|
+
throw new Error(`Access to dangerous property '${key}' is denied`);
|
|
835
|
+
}
|
|
836
|
+
if (target === undefined || target === null)
|
|
837
|
+
return undefined;
|
|
838
|
+
return target[key];
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function handleAccessPropertyRequest(data, ctx, meta) {
|
|
842
|
+
if (ctx.isDestroyed())
|
|
843
|
+
return;
|
|
844
|
+
const target = ctx.getDelegateTarget();
|
|
845
|
+
const { respondError, respondThrown } = createResponder(ctx, meta.senderInstanceId, 'accessPropertyResponse', data.id);
|
|
846
|
+
if (data.path.length === 0) {
|
|
847
|
+
respondError('Property path must not be empty');
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (target === undefined || target === null) {
|
|
851
|
+
respondError('No delegate target is configured');
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
let current = target;
|
|
856
|
+
let owner = target;
|
|
857
|
+
for (let i = 0; i < data.path.length; i++) {
|
|
858
|
+
const key = data.path[i];
|
|
859
|
+
if (current === undefined || current === null) {
|
|
860
|
+
respondError(`Cannot read property '${String(key)}' of ${current === null ? 'null' : 'undefined'} (at '${data.path
|
|
861
|
+
.slice(0, i)
|
|
862
|
+
.map(String)
|
|
863
|
+
.join('.')}')`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
owner = current;
|
|
867
|
+
current = readProperty(current, key);
|
|
868
|
+
}
|
|
869
|
+
const value = typeof current === 'function' ? ctx.bindMethod(current, owner) : current;
|
|
870
|
+
ctx
|
|
871
|
+
.getMessageChannel()
|
|
872
|
+
.getSender()
|
|
873
|
+
.sendMessage('accessPropertyResponse', {
|
|
874
|
+
id: data.id,
|
|
875
|
+
data: ctx.serializeForRemote(value, meta.senderInstanceId, {
|
|
876
|
+
persistent: isLikelyListenerPath(data.path),
|
|
877
|
+
}),
|
|
878
|
+
}, meta.senderInstanceId);
|
|
879
|
+
}
|
|
880
|
+
catch (err) {
|
|
881
|
+
respondThrown(err);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const handleAccessPropertyResponse = createResponseHandler('handleAccessPropertyResponse', warn);
|
|
885
|
+
|
|
886
|
+
function handleInvokeRequest(data, ctx, meta) {
|
|
887
|
+
if (ctx.isDestroyed())
|
|
888
|
+
return;
|
|
889
|
+
const target = ctx.getDelegateTarget();
|
|
890
|
+
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
891
|
+
const generateCallback = createGenerateCallback(ctx, meta.senderInstanceId);
|
|
892
|
+
const { respondError, respondThrown, respondSuccess } = createResponder(ctx, meta.senderInstanceId, 'invokeResponse', data.id);
|
|
893
|
+
if (target === undefined || target === null) {
|
|
894
|
+
respondError('No delegate target is configured');
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
if (data.path.length === 0) {
|
|
898
|
+
respondError('Invocation path must not be empty');
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
let current = target;
|
|
902
|
+
for (let i = 0; i < data.path.length - 1; i++) {
|
|
903
|
+
current = readProperty(current, data.path[i]);
|
|
904
|
+
if (current === undefined || current === null) {
|
|
905
|
+
respondError(`Cannot read property '${String(data.path[i + 1])}' of ${current === null ? 'null' : 'undefined'} (at '${data.path
|
|
906
|
+
.slice(0, i + 1)
|
|
907
|
+
.map(String)
|
|
908
|
+
.join('.')}')`);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const lastKey = data.path[data.path.length - 1];
|
|
913
|
+
const fn = readProperty(current, lastKey);
|
|
914
|
+
if (typeof fn !== 'function') {
|
|
915
|
+
respondError(`'${String(lastKey)}' is not a function`);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
let deserializedArgs;
|
|
919
|
+
try {
|
|
920
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, generateCallback, scopedRemoteCallback));
|
|
921
|
+
}
|
|
922
|
+
catch (err) {
|
|
923
|
+
respondThrown(err);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
let result;
|
|
927
|
+
try {
|
|
928
|
+
result = Reflect.apply(fn, current, deserializedArgs);
|
|
929
|
+
}
|
|
930
|
+
catch (err) {
|
|
931
|
+
respondThrown(err);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (isPromiseLike(result)) {
|
|
935
|
+
Promise.resolve(result)
|
|
936
|
+
.then((value) => {
|
|
937
|
+
respondSuccess(value);
|
|
938
|
+
})
|
|
939
|
+
.catch((err) => {
|
|
940
|
+
respondThrown(err);
|
|
941
|
+
});
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
respondSuccess(result);
|
|
945
|
+
}
|
|
946
|
+
const handleInvokeResponse = createResponseHandler('handleInvokeResponse', warn);
|
|
947
|
+
|
|
948
|
+
function handleCallbackInvoke(data, ctx, meta) {
|
|
949
|
+
if (ctx.isDestroyed())
|
|
950
|
+
return;
|
|
951
|
+
const { respondError, respondThrown, respondSuccess } = createResponder(ctx, meta.senderInstanceId, 'invokeFunctionByIdResponse', data.callId);
|
|
952
|
+
const persistentEntry = ctx.getPersistentFunctionCache().get(data.id);
|
|
953
|
+
const entry = persistentEntry ?? ctx.getFunctionCache().get(data.id);
|
|
954
|
+
if (!entry) {
|
|
955
|
+
const message = `Callback '${data.id}' is not available or has expired`;
|
|
956
|
+
warn('handleCallbackInvoke', message);
|
|
957
|
+
respondError(message);
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
961
|
+
const generateCallback = createGenerateCallback(ctx, meta.senderInstanceId);
|
|
962
|
+
let deserializedArgs;
|
|
963
|
+
try {
|
|
964
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, generateCallback, scopedRemoteCallback));
|
|
965
|
+
}
|
|
966
|
+
catch (err) {
|
|
967
|
+
respondThrown(err);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
try {
|
|
971
|
+
const result = Reflect.apply(entry.fn, entry.thisArg, deserializedArgs);
|
|
972
|
+
if (isPromiseLike(result)) {
|
|
973
|
+
Promise.resolve(result)
|
|
974
|
+
.then((value) => {
|
|
975
|
+
respondSuccess(value);
|
|
976
|
+
})
|
|
977
|
+
.catch((err) => {
|
|
978
|
+
respondThrown(err);
|
|
979
|
+
});
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
respondSuccess(result);
|
|
983
|
+
}
|
|
984
|
+
catch (err) {
|
|
985
|
+
respondThrown(err);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const handleCallbackInvokeResponse = createResponseHandler('handleCallbackInvokeResponse', warn);
|
|
989
|
+
|
|
990
|
+
const RELEASE_CHUNK_SIZE = 1000;
|
|
991
|
+
const scheduleNextChunk = typeof queueMicrotask === 'function' ? (cb) => queueMicrotask(cb) : (cb) => setTimeout(cb, 0);
|
|
992
|
+
function handleConnectRequest(_data, ctx, meta) {
|
|
993
|
+
ctx.noteFreshConnect(meta.senderInstanceId);
|
|
994
|
+
ctx.getMessageChannel().getSender().sendMessage('connectResponse', {
|
|
995
|
+
instanceId: ctx.getInstanceId(),
|
|
996
|
+
}, meta.senderInstanceId);
|
|
997
|
+
}
|
|
998
|
+
function handleConnectResponse(_data, ctx, meta) {
|
|
999
|
+
ctx.bindRemote(meta.senderInstanceId);
|
|
1000
|
+
}
|
|
1001
|
+
function handleReleaseCallbacks(data, ctx, meta) {
|
|
1002
|
+
const ids = data.ids;
|
|
1003
|
+
const total = ids.length;
|
|
1004
|
+
ctx.releaseRemoteCallbacks(meta.senderInstanceId, ids);
|
|
1005
|
+
let cursor = 0;
|
|
1006
|
+
const sender = meta.senderInstanceId;
|
|
1007
|
+
const processChunk = () => {
|
|
1008
|
+
if (ctx.isDestroyed())
|
|
1009
|
+
return;
|
|
1010
|
+
const end = Math.min(cursor + RELEASE_CHUNK_SIZE, total);
|
|
1011
|
+
for (let i = cursor; i < end; i++) {
|
|
1012
|
+
ctx.dropLocalCallback(ids[i], sender);
|
|
1013
|
+
}
|
|
1014
|
+
cursor = end;
|
|
1015
|
+
if (cursor < total)
|
|
1016
|
+
scheduleNextChunk(processChunk);
|
|
1017
|
+
};
|
|
1018
|
+
processChunk();
|
|
1019
|
+
}
|
|
1020
|
+
function handleDestroyEndpoint(data, ctx, meta) {
|
|
1021
|
+
if (data.instanceId !== meta.senderInstanceId)
|
|
1022
|
+
return;
|
|
1023
|
+
ctx.handleRemoteDestroy(meta.senderInstanceId);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function createProcessorRegistry() {
|
|
1027
|
+
const handlers = new Map();
|
|
1028
|
+
return {
|
|
1029
|
+
register(type, handler) {
|
|
1030
|
+
handlers.set(type, { handler: handler });
|
|
1031
|
+
},
|
|
1032
|
+
get(type) {
|
|
1033
|
+
return handlers.get(type)?.handler;
|
|
1034
|
+
},
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
var C={hasSubscribers:false},S=C;var L=typeof performance=="object"&&performance&&typeof performance.now=="function"?performance:Date;var j=new Set,M=typeof process=="object"&&process?process:{},G=(u,e,t,i)=>{typeof M.emitWarning=="function"?M.emitWarning(u,e,t,i):console.error(`[${t}] ${e}: ${u}`);},P=u=>!j.has(u),F=u=>!!u&&u===Math.floor(u)&&u>0&&isFinite(u),I=u=>F(u)?u<=Math.pow(2,8)?Uint8Array:u<=Math.pow(2,16)?Uint16Array:u<=Math.pow(2,32)?Uint32Array:u<=Number.MAX_SAFE_INTEGER?O:null:null,O=class extends Array{constructor(e){super(e),this.fill(0);}},R=class u{heap;length;static#o=false;static create(e){let t=I(e);if(!t)return [];u.#o=true;let i=new u(e,t);return u.#o=false,i}constructor(e,t){if(!u.#o)throw new TypeError("instantiate Stack using Stack.create(n)");this.heap=new t(e),this.length=0;}push(e){this.heap[this.length++]=e;}pop(){return this.heap[--this.length]}},U=class u{#o;#u;#w;#x;#S;#M;#U;#m;get perf(){return this.#m}ttl;ttlResolution;ttlAutopurge;updateAgeOnGet;updateAgeOnHas;allowStale;noDisposeOnSet;noUpdateTTL;maxEntrySize;sizeCalculation;noDeleteOnFetchRejection;noDeleteOnStaleGet;allowStaleOnFetchAbort;allowStaleOnFetchRejection;ignoreFetchAbort;#n;#b;#s;#i;#t;#a;#c;#l;#h;#y;#r;#_;#F;#d;#g;#T;#W;#f;#j;static unsafeExposeInternals(e){return {starts:e.#F,ttls:e.#d,autopurgeTimers:e.#g,sizes:e.#_,keyMap:e.#s,keyList:e.#i,valList:e.#t,next:e.#a,prev:e.#c,get head(){return e.#l},get tail(){return e.#h},free:e.#y,isBackgroundFetch:t=>e.#e(t),backgroundFetch:(t,i,s,n)=>e.#P(t,i,s,n),moveToTail:t=>e.#L(t),indexes:t=>e.#A(t),rindexes:t=>e.#z(t),isStale:t=>e.#p(t)}}get max(){return this.#o}get maxSize(){return this.#u}get calculatedSize(){return this.#b}get size(){return this.#n}get fetchMethod(){return this.#M}get memoMethod(){return this.#U}get dispose(){return this.#w}get onInsert(){return this.#x}get disposeAfter(){return this.#S}constructor(e){let{max:t=0,ttl:i,ttlResolution:s=1,ttlAutopurge:n,updateAgeOnGet:o,updateAgeOnHas:r,allowStale:h,dispose:l,onInsert:c,disposeAfter:f,noDisposeOnSet:g,noUpdateTTL:p,maxSize:T=0,maxEntrySize:w=0,sizeCalculation:y,fetchMethod:a,memoMethod:m,noDeleteOnFetchRejection:_,noDeleteOnStaleGet:b,allowStaleOnFetchRejection:d,allowStaleOnFetchAbort:A,ignoreFetchAbort:z,perf:x}=e;if(x!==void 0&&typeof x?.now!="function")throw new TypeError("perf option must have a now() method if specified");if(this.#m=x??L,t!==0&&!F(t))throw new TypeError("max option must be a nonnegative integer");let v=t?I(t):Array;if(!v)throw new Error("invalid max value: "+t);if(this.#o=t,this.#u=T,this.maxEntrySize=w||this.#u,this.sizeCalculation=y,this.sizeCalculation){if(!this.#u&&!this.maxEntrySize)throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");if(typeof this.sizeCalculation!="function")throw new TypeError("sizeCalculation set to non-function")}if(m!==void 0&&typeof m!="function")throw new TypeError("memoMethod must be a function if defined");if(this.#U=m,a!==void 0&&typeof a!="function")throw new TypeError("fetchMethod must be a function if specified");if(this.#M=a,this.#W=!!a,this.#s=new Map,this.#i=Array.from({length:t}).fill(void 0),this.#t=Array.from({length:t}).fill(void 0),this.#a=new v(t),this.#c=new v(t),this.#l=0,this.#h=0,this.#y=R.create(t),this.#n=0,this.#b=0,typeof l=="function"&&(this.#w=l),typeof c=="function"&&(this.#x=c),typeof f=="function"?(this.#S=f,this.#r=[]):(this.#S=void 0,this.#r=void 0),this.#T=!!this.#w,this.#j=!!this.#x,this.#f=!!this.#S,this.noDisposeOnSet=!!g,this.noUpdateTTL=!!p,this.noDeleteOnFetchRejection=!!_,this.allowStaleOnFetchRejection=!!d,this.allowStaleOnFetchAbort=!!A,this.ignoreFetchAbort=!!z,this.maxEntrySize!==0){if(this.#u!==0&&!F(this.#u))throw new TypeError("maxSize must be a positive integer if specified");if(!F(this.maxEntrySize))throw new TypeError("maxEntrySize must be a positive integer if specified");this.#X();}if(this.allowStale=!!h,this.noDeleteOnStaleGet=!!b,this.updateAgeOnGet=!!o,this.updateAgeOnHas=!!r,this.ttlResolution=F(s)||s===0?s:1,this.ttlAutopurge=!!n,this.ttl=i||0,this.ttl){if(!F(this.ttl))throw new TypeError("ttl must be a positive integer if specified");this.#H();}if(this.#o===0&&this.ttl===0&&this.#u===0)throw new TypeError("At least one of max, maxSize, or ttl is required");if(!this.ttlAutopurge&&!this.#o&&!this.#u){let E="LRU_CACHE_UNBOUNDED";P(E)&&(j.add(E),G("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.","UnboundedCacheWarning",E,u));}}getRemainingTTL(e){return this.#s.has(e)?1/0:0}#H(){let e=new O(this.#o),t=new O(this.#o);this.#d=e,this.#F=t;let i=this.ttlAutopurge?Array.from({length:this.#o}):void 0;this.#g=i,this.#N=(r,h,l=this.#m.now())=>{t[r]=h!==0?l:0,e[r]=h,s(r,h);},this.#D=r=>{t[r]=e[r]!==0?this.#m.now():0,s(r,e[r]);};let s=this.ttlAutopurge?(r,h)=>{if(i?.[r]&&(clearTimeout(i[r]),i[r]=void 0),h&&h!==0&&i){let l=setTimeout(()=>{this.#p(r)&&this.#v(this.#i[r],"expire");},h+1);l.unref&&l.unref(),i[r]=l;}}:()=>{};this.#E=(r,h)=>{if(e[h]){let l=e[h],c=t[h];if(!l||!c)return;r.ttl=l,r.start=c,r.now=n||o();let f=r.now-c;r.remainingTTL=l-f;}};let n=0,o=()=>{let r=this.#m.now();if(this.ttlResolution>0){n=r;let h=setTimeout(()=>n=0,this.ttlResolution);h.unref&&h.unref();}return r};this.getRemainingTTL=r=>{let h=this.#s.get(r);if(h===void 0)return 0;let l=e[h],c=t[h];if(!l||!c)return 1/0;let f=(n||o())-c;return l-f},this.#p=r=>{let h=t[r],l=e[r];return !!l&&!!h&&(n||o())-h>l};}#D=()=>{};#E=()=>{};#N=()=>{};#p=()=>false;#X(){let e=new O(this.#o);this.#b=0,this.#_=e,this.#R=t=>{this.#b-=e[t],e[t]=0;},this.#k=(t,i,s,n)=>{if(this.#e(i))return 0;if(!F(s))if(n){if(typeof n!="function")throw new TypeError("sizeCalculation must be a function");if(s=n(i,t),!F(s))throw new TypeError("sizeCalculation return invalid (expect positive integer)")}else throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");return s},this.#I=(t,i,s)=>{if(e[t]=i,this.#u){let n=this.#u-e[t];for(;this.#b>n;)this.#G(true);}this.#b+=e[t],s&&(s.entrySize=i,s.totalCalculatedSize=this.#b);};}#R=e=>{};#I=(e,t,i)=>{};#k=(e,t,i,s)=>{if(i||s)throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");return 0};*#A({allowStale:e=this.allowStale}={}){if(this.#n)for(let t=this.#h;this.#V(t)&&((e||!this.#p(t))&&(yield t),t!==this.#l);)t=this.#c[t];}*#z({allowStale:e=this.allowStale}={}){if(this.#n)for(let t=this.#l;this.#V(t)&&((e||!this.#p(t))&&(yield t),t!==this.#h);)t=this.#a[t];}#V(e){return e!==void 0&&this.#s.get(this.#i[e])===e}*entries(){for(let e of this.#A())this.#t[e]!==void 0&&this.#i[e]!==void 0&&!this.#e(this.#t[e])&&(yield [this.#i[e],this.#t[e]]);}*rentries(){for(let e of this.#z())this.#t[e]!==void 0&&this.#i[e]!==void 0&&!this.#e(this.#t[e])&&(yield [this.#i[e],this.#t[e]]);}*keys(){for(let e of this.#A()){let t=this.#i[e];t!==void 0&&!this.#e(this.#t[e])&&(yield t);}}*rkeys(){for(let e of this.#z()){let t=this.#i[e];t!==void 0&&!this.#e(this.#t[e])&&(yield t);}}*values(){for(let e of this.#A())this.#t[e]!==void 0&&!this.#e(this.#t[e])&&(yield this.#t[e]);}*rvalues(){for(let e of this.#z())this.#t[e]!==void 0&&!this.#e(this.#t[e])&&(yield this.#t[e]);}[Symbol.iterator](){return this.entries()}[Symbol.toStringTag]="LRUCache";find(e,t={}){for(let i of this.#A()){let s=this.#t[i],n=this.#e(s)?s.__staleWhileFetching:s;if(n!==void 0&&e(n,this.#i[i],this))return this.#C(this.#i[i],t)}}forEach(e,t=this){for(let i of this.#A()){let s=this.#t[i],n=this.#e(s)?s.__staleWhileFetching:s;n!==void 0&&e.call(t,n,this.#i[i],this);}}rforEach(e,t=this){for(let i of this.#z()){let s=this.#t[i],n=this.#e(s)?s.__staleWhileFetching:s;n!==void 0&&e.call(t,n,this.#i[i],this);}}purgeStale(){let e=false;for(let t of this.#z({allowStale:true}))this.#p(t)&&(this.#v(this.#i[t],"expire"),e=true);return e}info(e){let t=this.#s.get(e);if(t===void 0)return;let i=this.#t[t],s=this.#e(i)?i.__staleWhileFetching:i;if(s===void 0)return;let n={value:s};if(this.#d&&this.#F){let o=this.#d[t],r=this.#F[t];if(o&&r){let h=o-(this.#m.now()-r);n.ttl=h,n.start=Date.now();}}return this.#_&&(n.size=this.#_[t]),n}dump(){let e=[];for(let t of this.#A({allowStale:true})){let i=this.#i[t],s=this.#t[t],n=this.#e(s)?s.__staleWhileFetching:s;if(n===void 0||i===void 0)continue;let o={value:n};if(this.#d&&this.#F){o.ttl=this.#d[t];let r=this.#m.now()-this.#F[t];o.start=Math.floor(Date.now()-r);}this.#_&&(o.size=this.#_[t]),e.unshift([i,o]);}return e}load(e){this.clear();for(let[t,i]of e){if(i.start){let s=Date.now()-i.start;i.start=this.#m.now()-s;}this.#O(t,i.value,i);}}set(e,t,i={}){let{status:s=void 0}=i;i.status=s,s&&(s.op="set",s.key=e,t!==void 0&&(s.value=t));let n=this.#O(e,t,i);return n}#O(e,t,i={}){let{ttl:s=this.ttl,start:n,noDisposeOnSet:o=this.noDisposeOnSet,sizeCalculation:r=this.sizeCalculation,status:h}=i;if(t===void 0)return h&&(h.set="deleted"),this.delete(e),this;let{noUpdateTTL:l=this.noUpdateTTL}=i;h&&!this.#e(t)&&(h.value=t);let c=this.#k(e,t,i.size||0,r,h);if(this.maxEntrySize&&c>this.maxEntrySize)return this.#v(e,"set"),h&&(h.set="miss",h.maxEntrySizeExceeded=true),this;let f=this.#n===0?void 0:this.#s.get(e);if(f===void 0)f=this.#n===0?this.#h:this.#y.length!==0?this.#y.pop():this.#n===this.#o?this.#G(false):this.#n,this.#i[f]=e,this.#t[f]=t,this.#s.set(e,f),this.#a[this.#h]=f,this.#c[f]=this.#h,this.#h=f,this.#n++,this.#I(f,c,h),h&&(h.set="add"),l=false,this.#j&&this.#x?.(t,e,"add");else {this.#L(f);let g=this.#t[f];if(t!==g){if(this.#W&&this.#e(g)){g.__abortController.abort(new Error("replaced"));let{__staleWhileFetching:p}=g;p!==void 0&&!o&&(this.#T&&this.#w?.(p,e,"set"),this.#f&&this.#r?.push([p,e,"set"]));}else o||(this.#T&&this.#w?.(g,e,"set"),this.#f&&this.#r?.push([g,e,"set"]));if(this.#R(f),this.#I(f,c,h),this.#t[f]=t,h){h.set="replace";let p=g&&this.#e(g)?g.__staleWhileFetching:g;p!==void 0&&(h.oldValue=p);}}else h&&(h.set="update");this.#j&&this.onInsert?.(t,e,t===g?"update":"replace");}if(s!==0&&!this.#d&&this.#H(),this.#d&&(l||this.#N(f,s,n),h&&this.#E(h,f)),!o&&this.#f&&this.#r){let g=this.#r,p;for(;p=g?.shift();)this.#S?.(...p);}return this}pop(){try{for(;this.#n;){let e=this.#t[this.#l];if(this.#G(!0),this.#e(e)){if(e.__staleWhileFetching)return e.__staleWhileFetching}else if(e!==void 0)return e}}finally{if(this.#f&&this.#r){let e=this.#r,t;for(;t=e?.shift();)this.#S?.(...t);}}}#G(e){let t=this.#l,i=this.#i[t],s=this.#t[t];return this.#W&&this.#e(s)?s.__abortController.abort(new Error("evicted")):(this.#T||this.#f)&&(this.#T&&this.#w?.(s,i,"evict"),this.#f&&this.#r?.push([s,i,"evict"])),this.#R(t),this.#g?.[t]&&(clearTimeout(this.#g[t]),this.#g[t]=void 0),e&&(this.#i[t]=void 0,this.#t[t]=void 0,this.#y.push(t)),this.#n===1?(this.#l=this.#h=0,this.#y.length=0):this.#l=this.#a[t],this.#s.delete(i),this.#n--,t}has(e,t={}){let{status:i=void 0}=t;t.status=i,i&&(i.op="has",i.key=e);let s=this.#Y(e,t);return s}#Y(e,t={}){let{updateAgeOnHas:i=this.updateAgeOnHas,status:s}=t,n=this.#s.get(e);if(n!==void 0){let o=this.#t[n];if(this.#e(o)&&o.__staleWhileFetching===void 0)return false;if(this.#p(n))s&&(s.has="stale",this.#E(s,n));else return i&&this.#D(n),s&&(s.has="hit",this.#E(s,n)),true}else s&&(s.has="miss");return false}peek(e,t={}){let{status:i=void 0}=t;i&&(i.op="peek",i.key=e),t.status=i;let s=this.#J(e,t);return s}#J(e,t){let{status:i,allowStale:s=this.allowStale}=t,n=this.#s.get(e);if(n===void 0||!s&&this.#p(n)){i&&(i.peek=n===void 0?"miss":"stale");return}let o=this.#t[n],r=this.#e(o)?o.__staleWhileFetching:o;return i&&(r!==void 0?(i.peek="hit",i.value=r):i.peek="miss"),r}#P(e,t,i,s){let n=t===void 0?void 0:this.#t[t];if(this.#e(n))return n;let o=new AbortController,{signal:r}=i;r?.addEventListener("abort",()=>o.abort(r.reason),{signal:o.signal});let h={signal:o.signal,options:i,context:s},l=(w,y=false)=>{let{aborted:a}=o.signal,m=i.ignoreFetchAbort&&w!==void 0,_=i.ignoreFetchAbort||!!(i.allowStaleOnFetchAbort&&w!==void 0);if(i.status&&(a&&!y?(i.status.fetchAborted=true,i.status.fetchError=o.signal.reason,m&&(i.status.fetchAbortIgnored=true)):i.status.fetchResolved=true),a&&!m&&!y)return f(o.signal.reason,_);let b=p,d=this.#t[t];return (d===p||d===void 0&&m&&y)&&(w===void 0?b.__staleWhileFetching!==void 0?this.#t[t]=b.__staleWhileFetching:this.#v(e,"fetch"):(i.status&&(i.status.fetchUpdated=true),this.#O(e,w,h.options))),w},c=w=>(i.status&&(i.status.fetchRejected=true,i.status.fetchError=w),f(w,false)),f=(w,y)=>{let{aborted:a}=o.signal,m=a&&i.allowStaleOnFetchAbort,_=m||i.allowStaleOnFetchRejection,b=_||i.noDeleteOnFetchRejection,d=p;if(this.#t[t]===p&&(!b||!y&&d.__staleWhileFetching===void 0?this.#v(e,"fetch"):m||(this.#t[t]=d.__staleWhileFetching)),_)return i.status&&d.__staleWhileFetching!==void 0&&(i.status.returnedStale=true),d.__staleWhileFetching;if(d.__returned===d)throw w},g=(w,y)=>{let a=this.#M?.(e,n,h);a&&a instanceof Promise&&a.then(m=>w(m===void 0?void 0:m),y),o.signal.addEventListener("abort",()=>{(!i.ignoreFetchAbort||i.allowStaleOnFetchAbort)&&(w(void 0),i.allowStaleOnFetchAbort&&(w=m=>l(m,true)));});};i.status&&(i.status.fetchDispatched=true);let p=new Promise(g).then(l,c),T=Object.assign(p,{__abortController:o,__staleWhileFetching:n,__returned:void 0});return t===void 0?(this.#O(e,T,{...h.options,status:void 0}),t=this.#s.get(e)):this.#t[t]=T,T}#e(e){if(!this.#W)return false;let t=e;return !!t&&t instanceof Promise&&t.hasOwnProperty("__staleWhileFetching")&&t.__abortController instanceof AbortController}fetch(e,t={}){let {status:s=void 0}=t;t.status=s,s&&t.context&&(s.context=t.context);let n=this.#B(e,t);return n}async#B(e,t={}){let{allowStale:i=this.allowStale,updateAgeOnGet:s=this.updateAgeOnGet,noDeleteOnStaleGet:n=this.noDeleteOnStaleGet,ttl:o=this.ttl,noDisposeOnSet:r=this.noDisposeOnSet,size:h=0,sizeCalculation:l=this.sizeCalculation,noUpdateTTL:c=this.noUpdateTTL,noDeleteOnFetchRejection:f=this.noDeleteOnFetchRejection,allowStaleOnFetchRejection:g=this.allowStaleOnFetchRejection,ignoreFetchAbort:p=this.ignoreFetchAbort,allowStaleOnFetchAbort:T=this.allowStaleOnFetchAbort,context:w,forceRefresh:y=false,status:a,signal:m}=t;if(a&&(a.op="fetch",a.key=e,y&&(a.forceRefresh=true)),!this.#W)return a&&(a.fetch="get"),this.#C(e,{allowStale:i,updateAgeOnGet:s,noDeleteOnStaleGet:n,status:a});let _={allowStale:i,updateAgeOnGet:s,noDeleteOnStaleGet:n,ttl:o,noDisposeOnSet:r,size:h,sizeCalculation:l,noUpdateTTL:c,noDeleteOnFetchRejection:f,allowStaleOnFetchRejection:g,allowStaleOnFetchAbort:T,ignoreFetchAbort:p,status:a,signal:m},b=this.#s.get(e);if(b===void 0){a&&(a.fetch="miss");let d=this.#P(e,b,_,w);return d.__returned=d}else {let d=this.#t[b];if(this.#e(d)){let E=i&&d.__staleWhileFetching!==void 0;return a&&(a.fetch="inflight",E&&(a.returnedStale=true)),E?d.__staleWhileFetching:d.__returned=d}let A=this.#p(b);if(!y&&!A)return a&&(a.fetch="hit"),this.#L(b),s&&this.#D(b),a&&this.#E(a,b),d;let z=this.#P(e,b,_,w),v=z.__staleWhileFetching!==void 0&&i;return a&&(a.fetch=A?"stale":"refresh",v&&A&&(a.returnedStale=true)),v?z.__staleWhileFetching:z.__returned=z}}forceFetch(e,t={}){let {status:s=void 0}=t;t.status=s,s&&t.context&&(s.context=t.context);let n=this.#K(e,t);return n}async#K(e,t={}){let i=await this.#B(e,t);if(i===void 0)throw new Error("fetch() returned undefined");return i}memo(e,t={}){let{status:i=void 0}=t;t.status=i,i&&(i.op="memo",i.key=e,t.context&&(i.context=t.context));let s=this.#Q(e,t);return i&&(i.value=s),s}#Q(e,t={}){let i=this.#U;if(!i)throw new Error("no memoMethod provided to constructor");let{context:s,status:n,forceRefresh:o,...r}=t;n&&o&&(n.forceRefresh=true);let h=this.#C(e,r),l=o||h===void 0;if(n&&(n.memo=l?"miss":"hit",l||(n.value=h)),!l)return h;let c=i(e,h,{options:r,context:s});return n&&(n.value=c),this.#O(e,c,r),c}get(e,t={}){let{status:i=void 0}=t;t.status=i,i&&(i.op="get",i.key=e);let s=this.#C(e,t);return i&&(s!==void 0&&(i.value=s),S.hasSubscribers),s}#C(e,t={}){let{allowStale:i=this.allowStale,updateAgeOnGet:s=this.updateAgeOnGet,noDeleteOnStaleGet:n=this.noDeleteOnStaleGet,status:o}=t,r=this.#s.get(e);if(r===void 0){o&&(o.get="miss");return}let h=this.#t[r],l=this.#e(h);return o&&this.#E(o,r),this.#p(r)?l?(o&&(o.get="stale-fetching"),i&&h.__staleWhileFetching!==void 0?(o&&(o.returnedStale=true),h.__staleWhileFetching):void 0):(n||this.#v(e,"expire"),o&&(o.get="stale"),i?(o&&(o.returnedStale=true),h):void 0):(o&&(o.get=l?"fetching":"hit"),this.#L(r),s&&this.#D(r),l?h.__staleWhileFetching:h)}#$(e,t){this.#c[t]=e,this.#a[e]=t;}#L(e){e!==this.#h&&(e===this.#l?this.#l=this.#a[e]:this.#$(this.#c[e],this.#a[e]),this.#$(this.#h,e),this.#h=e);}delete(e){return this.#v(e,"delete")}#v(e,t){let i=false;if(this.#n!==0){let s=this.#s.get(e);if(s!==void 0)if(this.#g?.[s]&&(clearTimeout(this.#g?.[s]),this.#g[s]=void 0),i=true,this.#n===1)this.#q(t);else {this.#R(s);let n=this.#t[s];if(this.#e(n)?n.__abortController.abort(new Error("deleted")):(this.#T||this.#f)&&(this.#T&&this.#w?.(n,e,t),this.#f&&this.#r?.push([n,e,t])),this.#s.delete(e),this.#i[s]=void 0,this.#t[s]=void 0,s===this.#h)this.#h=this.#c[s];else if(s===this.#l)this.#l=this.#a[s];else {let o=this.#c[s];this.#a[o]=this.#a[s];let r=this.#a[s];this.#c[r]=this.#c[s];}this.#n--,this.#y.push(s);}}if(this.#f&&this.#r?.length){let s=this.#r,n;for(;n=s?.shift();)this.#S?.(...n);}return i}clear(){return this.#q("delete")}#q(e){for(let t of this.#z({allowStale:true})){let i=this.#t[t];if(this.#e(i))i.__abortController.abort(new Error("deleted"));else {let s=this.#i[t];this.#T&&this.#w?.(i,s,e),this.#f&&this.#r?.push([i,s,e]);}}if(this.#s.clear(),this.#t.fill(void 0),this.#i.fill(void 0),this.#d&&this.#F){this.#d.fill(0),this.#F.fill(0);for(let t of this.#g??[])t!==void 0&&clearTimeout(t);this.#g?.fill(void 0);}if(this.#_&&this.#_.fill(0),this.#l=0,this.#h=0,this.#y.length=0,this.#b=0,this.#n=0,this.#f&&this.#r){let t=this.#r,i;for(;i=t?.shift();)this.#S?.(...i);}}};
|
|
1039
|
+
|
|
1040
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
1041
|
+
const DEFAULT_FUNCTION_CACHE_MAX = 100;
|
|
1042
|
+
const DEFAULT_FUNCTION_CACHE_TTL = 5 * 60 * 1000;
|
|
1043
|
+
const DEFAULT_REMOTE_CALLBACK_CACHE_MAX = 500;
|
|
1044
|
+
const DEFAULT_REMOTE_CALLBACK_CACHE_TTL = 5 * 60 * 1000;
|
|
1045
|
+
const RELEASE_CALLBACKS_CHUNK_SIZE = 10000;
|
|
1046
|
+
const BROADCAST_OWNER = Symbol('chrome-in-iframe.broadcast-owner');
|
|
1047
|
+
function resolveTimeout(raw) {
|
|
1048
|
+
if (raw === undefined)
|
|
1049
|
+
return DEFAULT_TIMEOUT;
|
|
1050
|
+
if (typeof raw !== 'number')
|
|
1051
|
+
return DEFAULT_TIMEOUT;
|
|
1052
|
+
if (raw === Infinity)
|
|
1053
|
+
return Infinity;
|
|
1054
|
+
if (!Number.isFinite(raw) || raw <= 0)
|
|
1055
|
+
return DEFAULT_TIMEOUT;
|
|
1056
|
+
return raw;
|
|
1057
|
+
}
|
|
1058
|
+
function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
1059
|
+
const remoteCacheMax = options.remoteCallbackCacheMax ?? DEFAULT_REMOTE_CALLBACK_CACHE_MAX;
|
|
1060
|
+
const remoteCacheTtl = options.remoteCallbackCacheTtl ?? DEFAULT_REMOTE_CALLBACK_CACHE_TTL;
|
|
1061
|
+
const callTimeout = resolveTimeout(options.timeout);
|
|
1062
|
+
const pending = new Map();
|
|
1063
|
+
const functionIds = new WeakMap();
|
|
1064
|
+
const persistentFunctionCache = new Map();
|
|
1065
|
+
const persistentRefcount = new Map();
|
|
1066
|
+
const functionCache = new U({
|
|
1067
|
+
max: options.functionCacheMax ?? DEFAULT_FUNCTION_CACHE_MAX,
|
|
1068
|
+
ttl: options.functionCacheTtl ?? DEFAULT_FUNCTION_CACHE_TTL,
|
|
1069
|
+
dispose: (value, key, reason) => {
|
|
1070
|
+
if (reason === 'set' || reason === 'delete')
|
|
1071
|
+
return;
|
|
1072
|
+
if (persistentFunctionCache.has(key))
|
|
1073
|
+
return;
|
|
1074
|
+
removeFunctionIdMapping(value.fn, value.thisArg);
|
|
1075
|
+
},
|
|
1076
|
+
});
|
|
1077
|
+
const remoteCallbacksByOwner = new Map();
|
|
1078
|
+
const persistentRemoteCallbacksByOwner = new Map();
|
|
1079
|
+
const boundMethodCache = new WeakMap();
|
|
1080
|
+
const callbackOwners = new Map();
|
|
1081
|
+
const knownRemotes = new Set();
|
|
1082
|
+
const remoteTargetWaiters = new Set();
|
|
1083
|
+
let boundRemoteInstanceId;
|
|
1084
|
+
let remoteTargetWaitDisabled = false;
|
|
1085
|
+
let destroyed = false;
|
|
1086
|
+
const armTimer = (onTimeout) => {
|
|
1087
|
+
if (callTimeout === Infinity)
|
|
1088
|
+
return null;
|
|
1089
|
+
return setTimeout(onTimeout, callTimeout);
|
|
1090
|
+
};
|
|
1091
|
+
const cancelTimer = (timer) => {
|
|
1092
|
+
if (timer !== null)
|
|
1093
|
+
clearTimeout(timer);
|
|
1094
|
+
};
|
|
1095
|
+
const getPreferredRemote = () => {
|
|
1096
|
+
if (boundRemoteInstanceId !== undefined)
|
|
1097
|
+
return boundRemoteInstanceId;
|
|
1098
|
+
if (knownRemotes.size !== 1)
|
|
1099
|
+
return undefined;
|
|
1100
|
+
return knownRemotes.values().next().value;
|
|
1101
|
+
};
|
|
1102
|
+
const resolveRemoteTargetWaiters = (remoteInstanceId) => {
|
|
1103
|
+
if (remoteTargetWaiters.size === 0)
|
|
1104
|
+
return;
|
|
1105
|
+
const waiters = Array.from(remoteTargetWaiters);
|
|
1106
|
+
remoteTargetWaiters.clear();
|
|
1107
|
+
for (const resolve of waiters)
|
|
1108
|
+
resolve(remoteInstanceId);
|
|
1109
|
+
};
|
|
1110
|
+
const waitForRemoteTarget = () => {
|
|
1111
|
+
const preferred = getPreferredRemote();
|
|
1112
|
+
if (preferred !== undefined)
|
|
1113
|
+
return Promise.resolve(preferred);
|
|
1114
|
+
if (destroyed)
|
|
1115
|
+
return Promise.resolve(undefined);
|
|
1116
|
+
return new Promise((resolve) => {
|
|
1117
|
+
remoteTargetWaiters.add(resolve);
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1120
|
+
const recordCallbackOwners = (ids, owner) => {
|
|
1121
|
+
if (ids.length === 0)
|
|
1122
|
+
return;
|
|
1123
|
+
if (owner === 'broadcast') {
|
|
1124
|
+
for (const id of ids)
|
|
1125
|
+
callbackOwners.set(id, BROADCAST_OWNER);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
for (const id of ids) {
|
|
1129
|
+
const cur = callbackOwners.get(id);
|
|
1130
|
+
if (cur === BROADCAST_OWNER)
|
|
1131
|
+
continue;
|
|
1132
|
+
if (cur === undefined) {
|
|
1133
|
+
callbackOwners.set(id, owner);
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (typeof cur === 'string') {
|
|
1137
|
+
if (cur === owner)
|
|
1138
|
+
continue;
|
|
1139
|
+
callbackOwners.set(id, new Set([cur, owner]));
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
cur.add(owner);
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
const bindMethod = (fn, owner) => {
|
|
1146
|
+
if (owner === null || typeof owner !== 'object') {
|
|
1147
|
+
return (...args) => Reflect.apply(fn, owner, args);
|
|
1148
|
+
}
|
|
1149
|
+
let perOwner = boundMethodCache.get(fn);
|
|
1150
|
+
if (!perOwner) {
|
|
1151
|
+
perOwner = new WeakMap();
|
|
1152
|
+
boundMethodCache.set(fn, perOwner);
|
|
1153
|
+
}
|
|
1154
|
+
const cached = perOwner.get(owner);
|
|
1155
|
+
if (cached)
|
|
1156
|
+
return cached;
|
|
1157
|
+
const bound = (...args) => Reflect.apply(fn, owner, args);
|
|
1158
|
+
perOwner.set(owner, bound);
|
|
1159
|
+
return bound;
|
|
1160
|
+
};
|
|
1161
|
+
const removeFunctionIdMapping = (fn, thisArg) => {
|
|
1162
|
+
const perOwner = functionIds.get(fn);
|
|
1163
|
+
if (!perOwner)
|
|
1164
|
+
return;
|
|
1165
|
+
perOwner.delete(thisArg);
|
|
1166
|
+
if (perOwner.size === 0)
|
|
1167
|
+
functionIds.delete(fn);
|
|
1168
|
+
};
|
|
1169
|
+
const getOrCreateRemoteLru = (remoteId) => {
|
|
1170
|
+
let lru = remoteCallbacksByOwner.get(remoteId);
|
|
1171
|
+
if (!lru) {
|
|
1172
|
+
lru = new U({ max: remoteCacheMax, ttl: remoteCacheTtl });
|
|
1173
|
+
remoteCallbacksByOwner.set(remoteId, lru);
|
|
1174
|
+
}
|
|
1175
|
+
return lru;
|
|
1176
|
+
};
|
|
1177
|
+
const getOrCreatePersistentRemoteMap = (remoteId) => {
|
|
1178
|
+
let map = persistentRemoteCallbacksByOwner.get(remoteId);
|
|
1179
|
+
if (!map) {
|
|
1180
|
+
map = new Map();
|
|
1181
|
+
persistentRemoteCallbacksByOwner.set(remoteId, map);
|
|
1182
|
+
}
|
|
1183
|
+
return map;
|
|
1184
|
+
};
|
|
1185
|
+
const registerFunction = (fn, thisArg, callbackOptions = {}) => {
|
|
1186
|
+
let perOwner = functionIds.get(fn);
|
|
1187
|
+
if (!perOwner) {
|
|
1188
|
+
perOwner = new Map();
|
|
1189
|
+
functionIds.set(fn, perOwner);
|
|
1190
|
+
}
|
|
1191
|
+
let id = perOwner.get(thisArg);
|
|
1192
|
+
if (!id) {
|
|
1193
|
+
id = createId('cb');
|
|
1194
|
+
perOwner.set(thisArg, id);
|
|
1195
|
+
}
|
|
1196
|
+
const entry = { fn, thisArg };
|
|
1197
|
+
if (callbackOptions.persistent) {
|
|
1198
|
+
persistentFunctionCache.set(id, entry);
|
|
1199
|
+
functionCache.delete(id);
|
|
1200
|
+
persistentRefcount.set(id, (persistentRefcount.get(id) ?? 0) + 1);
|
|
1201
|
+
}
|
|
1202
|
+
else if (!persistentFunctionCache.has(id)) {
|
|
1203
|
+
functionCache.set(id, entry);
|
|
1204
|
+
}
|
|
1205
|
+
return id;
|
|
1206
|
+
};
|
|
1207
|
+
const releasePersistentRegistrationById = (id) => {
|
|
1208
|
+
if (!persistentRefcount.has(id))
|
|
1209
|
+
return undefined;
|
|
1210
|
+
const next = (persistentRefcount.get(id) ?? 0) - 1;
|
|
1211
|
+
if (next > 0) {
|
|
1212
|
+
persistentRefcount.set(id, next);
|
|
1213
|
+
return undefined;
|
|
1214
|
+
}
|
|
1215
|
+
persistentRefcount.delete(id);
|
|
1216
|
+
const entry = persistentFunctionCache.get(id);
|
|
1217
|
+
persistentFunctionCache.delete(id);
|
|
1218
|
+
functionCache.delete(id);
|
|
1219
|
+
if (entry)
|
|
1220
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1221
|
+
return id;
|
|
1222
|
+
};
|
|
1223
|
+
const dropLocalCallback = (id, requesterInstanceId) => {
|
|
1224
|
+
if (requesterInstanceId !== undefined) {
|
|
1225
|
+
const cur = callbackOwners.get(id);
|
|
1226
|
+
if (cur === BROADCAST_OWNER) {
|
|
1227
|
+
const remaining = new Set();
|
|
1228
|
+
for (const r of knownRemotes) {
|
|
1229
|
+
if (r !== requesterInstanceId)
|
|
1230
|
+
remaining.add(r);
|
|
1231
|
+
}
|
|
1232
|
+
if (remaining.size > 0) {
|
|
1233
|
+
callbackOwners.set(id, remaining.size === 1 ? remaining.values().next().value : remaining);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
else if (typeof cur === 'string') {
|
|
1238
|
+
if (cur !== requesterInstanceId)
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
else if (cur instanceof Set) {
|
|
1242
|
+
if (!cur.has(requesterInstanceId))
|
|
1243
|
+
return;
|
|
1244
|
+
cur.delete(requesterInstanceId);
|
|
1245
|
+
if (cur.size > 1)
|
|
1246
|
+
return;
|
|
1247
|
+
if (cur.size === 1) {
|
|
1248
|
+
callbackOwners.set(id, cur.values().next().value);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (persistentRefcount.has(id)) {
|
|
1254
|
+
const next = (persistentRefcount.get(id) ?? 0) - 1;
|
|
1255
|
+
if (next > 0) {
|
|
1256
|
+
persistentRefcount.set(id, next);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
callbackOwners.delete(id);
|
|
1261
|
+
const entry = persistentFunctionCache.get(id) ?? functionCache.get(id);
|
|
1262
|
+
persistentRefcount.delete(id);
|
|
1263
|
+
persistentFunctionCache.delete(id);
|
|
1264
|
+
functionCache.delete(id);
|
|
1265
|
+
if (entry)
|
|
1266
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1267
|
+
};
|
|
1268
|
+
const getRemoteCallback = (id, invoke, callbackOptions, remoteInstanceId) => {
|
|
1269
|
+
const opts = callbackOptions ?? {};
|
|
1270
|
+
const lru = remoteCallbacksByOwner.get(remoteInstanceId);
|
|
1271
|
+
const pmap = persistentRemoteCallbacksByOwner.get(remoteInstanceId);
|
|
1272
|
+
let callback = pmap?.get(id) ?? lru?.get(id);
|
|
1273
|
+
if (!callback) {
|
|
1274
|
+
callback = (...args) => invoke(args);
|
|
1275
|
+
if (opts.persistent) {
|
|
1276
|
+
getOrCreatePersistentRemoteMap(remoteInstanceId).set(id, callback);
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
getOrCreateRemoteLru(remoteInstanceId).set(id, callback);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
else if (opts.persistent && !pmap?.has(id)) {
|
|
1283
|
+
lru?.delete(id);
|
|
1284
|
+
getOrCreatePersistentRemoteMap(remoteInstanceId).set(id, callback);
|
|
1285
|
+
}
|
|
1286
|
+
return callback;
|
|
1287
|
+
};
|
|
1288
|
+
const notifyReleaseCallbacks = (ids) => {
|
|
1289
|
+
if (ids.length === 0)
|
|
1290
|
+
return;
|
|
1291
|
+
try {
|
|
1292
|
+
const sender = getMessageChannel().getSender();
|
|
1293
|
+
for (let cursor = 0; cursor < ids.length; cursor += RELEASE_CALLBACKS_CHUNK_SIZE) {
|
|
1294
|
+
sender.sendMessage('releaseCallbacks', { ids: ids.slice(cursor, cursor + RELEASE_CALLBACKS_CHUNK_SIZE) });
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
catch (err) {
|
|
1298
|
+
warn('notifyReleaseCallbacks', 'failed to release callbacks', err);
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
const safeOnReject = (callbacks) => {
|
|
1302
|
+
if (!callbacks.onReject)
|
|
1303
|
+
return;
|
|
1304
|
+
try {
|
|
1305
|
+
callbacks.onReject();
|
|
1306
|
+
}
|
|
1307
|
+
catch (err) {
|
|
1308
|
+
warn('rejectPending', 'onReject hook threw', err);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
const rejectPending = (id, error) => {
|
|
1312
|
+
const callbacks = pending.get(id);
|
|
1313
|
+
if (!callbacks)
|
|
1314
|
+
return;
|
|
1315
|
+
cancelTimer(callbacks.timer);
|
|
1316
|
+
pending.delete(id);
|
|
1317
|
+
safeOnReject(callbacks);
|
|
1318
|
+
callbacks.reject(error);
|
|
1319
|
+
};
|
|
1320
|
+
return {
|
|
1321
|
+
getDelegateTarget() {
|
|
1322
|
+
return options.delegateTarget;
|
|
1323
|
+
},
|
|
1324
|
+
getMessageChannel() {
|
|
1325
|
+
return getMessageChannel();
|
|
1326
|
+
},
|
|
1327
|
+
getInstanceId() {
|
|
1328
|
+
return instanceId;
|
|
1329
|
+
},
|
|
1330
|
+
getFunctionCache() {
|
|
1331
|
+
return functionCache;
|
|
1332
|
+
},
|
|
1333
|
+
getPersistentFunctionCache() {
|
|
1334
|
+
return persistentFunctionCache;
|
|
1335
|
+
},
|
|
1336
|
+
registerFunction(fn, thisArg, callbackOptions) {
|
|
1337
|
+
return registerFunction(fn, thisArg, callbackOptions);
|
|
1338
|
+
},
|
|
1339
|
+
serializeForRemote(value, target, callbackOptions) {
|
|
1340
|
+
const ids = [];
|
|
1341
|
+
const tracking = (fn, thisArg, opts) => {
|
|
1342
|
+
const id = registerFunction(fn, thisArg, opts);
|
|
1343
|
+
ids.push(id);
|
|
1344
|
+
return id;
|
|
1345
|
+
};
|
|
1346
|
+
const result = serialize(value, tracking, callbackOptions);
|
|
1347
|
+
recordCallbackOwners(ids, target);
|
|
1348
|
+
return result;
|
|
1349
|
+
},
|
|
1350
|
+
getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId) {
|
|
1351
|
+
return getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId);
|
|
1352
|
+
},
|
|
1353
|
+
getAndRemovePendingPromise(id, senderInstanceId) {
|
|
1354
|
+
const callbacks = pending.get(id);
|
|
1355
|
+
if (!callbacks)
|
|
1356
|
+
return undefined;
|
|
1357
|
+
if (callbacks.expectedRemote !== undefined &&
|
|
1358
|
+
senderInstanceId !== undefined &&
|
|
1359
|
+
callbacks.expectedRemote !== senderInstanceId) {
|
|
1360
|
+
return undefined;
|
|
1361
|
+
}
|
|
1362
|
+
cancelTimer(callbacks.timer);
|
|
1363
|
+
pending.delete(id);
|
|
1364
|
+
return callbacks;
|
|
1365
|
+
},
|
|
1366
|
+
invoke(path, args) {
|
|
1367
|
+
return new Promise((resolve, reject) => {
|
|
1368
|
+
if (destroyed) {
|
|
1369
|
+
reject(new Error('Channel has been destroyed'));
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const id = createId('req');
|
|
1373
|
+
const persistent = isListenerRegistrationPath(path);
|
|
1374
|
+
const registeredIds = [];
|
|
1375
|
+
const collectingRegister = (fn, thisArg, opts) => {
|
|
1376
|
+
const cbId = registerFunction(fn, thisArg, opts);
|
|
1377
|
+
registeredIds.push(cbId);
|
|
1378
|
+
return cbId;
|
|
1379
|
+
};
|
|
1380
|
+
const releaseRegisteredPersistents = () => {
|
|
1381
|
+
const released = [];
|
|
1382
|
+
for (const cbId of registeredIds) {
|
|
1383
|
+
const releasedId = releasePersistentRegistrationById(cbId);
|
|
1384
|
+
if (releasedId)
|
|
1385
|
+
released.push(releasedId);
|
|
1386
|
+
}
|
|
1387
|
+
return released;
|
|
1388
|
+
};
|
|
1389
|
+
const cleanupRegisteredOnFailure = () => {
|
|
1390
|
+
const released = [];
|
|
1391
|
+
for (const cbId of registeredIds) {
|
|
1392
|
+
if (persistentRefcount.has(cbId)) {
|
|
1393
|
+
const releasedId = releasePersistentRegistrationById(cbId);
|
|
1394
|
+
if (releasedId)
|
|
1395
|
+
released.push(releasedId);
|
|
1396
|
+
}
|
|
1397
|
+
else {
|
|
1398
|
+
const entry = functionCache.get(cbId);
|
|
1399
|
+
if (entry) {
|
|
1400
|
+
functionCache.delete(cbId);
|
|
1401
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
callbackOwners.delete(cbId);
|
|
1405
|
+
}
|
|
1406
|
+
return released;
|
|
1407
|
+
};
|
|
1408
|
+
const timer = armTimer(() => {
|
|
1409
|
+
rejectPending(id, new Error(`Call timed out: ${String(path.join('.'))}`));
|
|
1410
|
+
});
|
|
1411
|
+
let serializedArgs;
|
|
1412
|
+
try {
|
|
1413
|
+
serializePath(path);
|
|
1414
|
+
serializedArgs = args.map((arg) => serialize(arg, collectingRegister, { persistent }));
|
|
1415
|
+
}
|
|
1416
|
+
catch (err) {
|
|
1417
|
+
cancelTimer(timer);
|
|
1418
|
+
const released = cleanupRegisteredOnFailure();
|
|
1419
|
+
if (released.length > 0)
|
|
1420
|
+
notifyReleaseCallbacks(released);
|
|
1421
|
+
reject(err);
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
pending.set(id, {
|
|
1425
|
+
resolve,
|
|
1426
|
+
reject,
|
|
1427
|
+
timer,
|
|
1428
|
+
onResolve: () => {
|
|
1429
|
+
if (isListenerRemovalPath(path)) {
|
|
1430
|
+
const released = releaseRegisteredPersistents();
|
|
1431
|
+
if (released.length > 0)
|
|
1432
|
+
notifyReleaseCallbacks(released);
|
|
1433
|
+
}
|
|
1434
|
+
},
|
|
1435
|
+
onReject: () => {
|
|
1436
|
+
if (isListenerRegistrationPath(path)) {
|
|
1437
|
+
const released = releaseRegisteredPersistents();
|
|
1438
|
+
if (released.length > 0)
|
|
1439
|
+
notifyReleaseCallbacks(released);
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
});
|
|
1443
|
+
const sendRequest = (targetInstanceId, requireTarget = true) => {
|
|
1444
|
+
const callbacks = pending.get(id);
|
|
1445
|
+
if (!callbacks)
|
|
1446
|
+
return;
|
|
1447
|
+
if (targetInstanceId === undefined && requireTarget) {
|
|
1448
|
+
rejectPending(id, new Error('No remote endpoint is available'));
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
if (targetInstanceId !== undefined)
|
|
1452
|
+
callbacks.expectedRemote = targetInstanceId;
|
|
1453
|
+
try {
|
|
1454
|
+
const channel = getMessageChannel();
|
|
1455
|
+
recordCallbackOwners(registeredIds, targetInstanceId ?? 'broadcast');
|
|
1456
|
+
channel.getSender().sendMessage('invokeRequest', {
|
|
1457
|
+
id,
|
|
1458
|
+
path,
|
|
1459
|
+
args: serializedArgs,
|
|
1460
|
+
}, targetInstanceId);
|
|
1461
|
+
}
|
|
1462
|
+
catch (err) {
|
|
1463
|
+
cancelTimer(timer);
|
|
1464
|
+
pending.delete(id);
|
|
1465
|
+
const released = cleanupRegisteredOnFailure();
|
|
1466
|
+
if (released.length > 0)
|
|
1467
|
+
notifyReleaseCallbacks(released);
|
|
1468
|
+
reject(err);
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1471
|
+
const targetInstanceId = getPreferredRemote();
|
|
1472
|
+
if (targetInstanceId !== undefined) {
|
|
1473
|
+
sendRequest(targetInstanceId);
|
|
1474
|
+
}
|
|
1475
|
+
else if (remoteTargetWaitDisabled) {
|
|
1476
|
+
sendRequest(undefined, false);
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
void waitForRemoteTarget().then(sendRequest);
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
},
|
|
1483
|
+
accessProperty(path) {
|
|
1484
|
+
return new Promise((resolve, reject) => {
|
|
1485
|
+
if (destroyed) {
|
|
1486
|
+
reject(new Error('Channel has been destroyed'));
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
const id = createId('req');
|
|
1490
|
+
const timer = armTimer(() => {
|
|
1491
|
+
rejectPending(id, new Error(`Property access timed out: ${String(path.join('.'))}`));
|
|
1492
|
+
});
|
|
1493
|
+
pending.set(id, { resolve, reject, timer });
|
|
1494
|
+
let pathSerializationError;
|
|
1495
|
+
try {
|
|
1496
|
+
serializePath(path);
|
|
1497
|
+
}
|
|
1498
|
+
catch (err) {
|
|
1499
|
+
pathSerializationError = err;
|
|
1500
|
+
}
|
|
1501
|
+
if (pathSerializationError !== undefined) {
|
|
1502
|
+
cancelTimer(timer);
|
|
1503
|
+
pending.delete(id);
|
|
1504
|
+
reject(pathSerializationError);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
const sendRequest = (targetInstanceId, requireTarget = true) => {
|
|
1508
|
+
const callbacks = pending.get(id);
|
|
1509
|
+
if (!callbacks)
|
|
1510
|
+
return;
|
|
1511
|
+
if (targetInstanceId === undefined && requireTarget) {
|
|
1512
|
+
rejectPending(id, new Error('No remote endpoint is available'));
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
if (targetInstanceId !== undefined)
|
|
1516
|
+
callbacks.expectedRemote = targetInstanceId;
|
|
1517
|
+
try {
|
|
1518
|
+
getMessageChannel().getSender().sendMessage('accessPropertyRequest', {
|
|
1519
|
+
id,
|
|
1520
|
+
path,
|
|
1521
|
+
}, targetInstanceId);
|
|
1522
|
+
}
|
|
1523
|
+
catch (err) {
|
|
1524
|
+
cancelTimer(timer);
|
|
1525
|
+
pending.delete(id);
|
|
1526
|
+
reject(err);
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
const targetInstanceId = getPreferredRemote();
|
|
1530
|
+
if (targetInstanceId !== undefined) {
|
|
1531
|
+
sendRequest(targetInstanceId);
|
|
1532
|
+
}
|
|
1533
|
+
else if (remoteTargetWaitDisabled) {
|
|
1534
|
+
sendRequest(undefined, false);
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
void waitForRemoteTarget().then(sendRequest);
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
},
|
|
1541
|
+
invokeFunctionById(callbackId, args, callbackOptions = {}, remoteInstanceId) {
|
|
1542
|
+
return new Promise((resolve, reject) => {
|
|
1543
|
+
if (destroyed) {
|
|
1544
|
+
reject(new Error('Channel has been destroyed'));
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
const callId = createId('cbcall');
|
|
1548
|
+
const timer = armTimer(() => {
|
|
1549
|
+
rejectPending(callId, new Error(`Callback call timed out: ${callbackId}`));
|
|
1550
|
+
});
|
|
1551
|
+
pending.set(callId, { resolve, reject, timer, expectedRemote: remoteInstanceId });
|
|
1552
|
+
const registeredIds = [];
|
|
1553
|
+
const collectingRegister = (fn, thisArg, opts) => {
|
|
1554
|
+
const cbId = registerFunction(fn, thisArg, opts);
|
|
1555
|
+
registeredIds.push(cbId);
|
|
1556
|
+
return cbId;
|
|
1557
|
+
};
|
|
1558
|
+
try {
|
|
1559
|
+
const serializedArgs = args.map((arg) => serialize(arg, collectingRegister, callbackOptions));
|
|
1560
|
+
const targetInstanceId = remoteInstanceId ?? getPreferredRemote();
|
|
1561
|
+
recordCallbackOwners(registeredIds, targetInstanceId ?? 'broadcast');
|
|
1562
|
+
getMessageChannel().getSender().sendMessage('invokeFunctionByIdRequest', {
|
|
1563
|
+
id: callbackId,
|
|
1564
|
+
callId,
|
|
1565
|
+
args: serializedArgs,
|
|
1566
|
+
}, targetInstanceId);
|
|
1567
|
+
}
|
|
1568
|
+
catch (err) {
|
|
1569
|
+
cancelTimer(timer);
|
|
1570
|
+
pending.delete(callId);
|
|
1571
|
+
for (const cbId of registeredIds) {
|
|
1572
|
+
if (persistentRefcount.has(cbId)) {
|
|
1573
|
+
releasePersistentRegistrationById(cbId);
|
|
1574
|
+
}
|
|
1575
|
+
else {
|
|
1576
|
+
const entry = functionCache.get(cbId);
|
|
1577
|
+
if (entry) {
|
|
1578
|
+
functionCache.delete(cbId);
|
|
1579
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
callbackOwners.delete(cbId);
|
|
1583
|
+
}
|
|
1584
|
+
reject(err);
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
},
|
|
1588
|
+
handleRemoteDestroy(remoteInstanceId) {
|
|
1589
|
+
remoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1590
|
+
persistentRemoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1591
|
+
knownRemotes.delete(remoteInstanceId);
|
|
1592
|
+
if (boundRemoteInstanceId === remoteInstanceId) {
|
|
1593
|
+
boundRemoteInstanceId = undefined;
|
|
1594
|
+
const preferred = getPreferredRemote();
|
|
1595
|
+
if (preferred !== undefined)
|
|
1596
|
+
boundRemoteInstanceId = preferred;
|
|
1597
|
+
}
|
|
1598
|
+
for (const [id, owners] of callbackOwners) {
|
|
1599
|
+
if (owners === remoteInstanceId) {
|
|
1600
|
+
callbackOwners.delete(id);
|
|
1601
|
+
}
|
|
1602
|
+
else if (owners instanceof Set && owners.has(remoteInstanceId)) {
|
|
1603
|
+
owners.delete(remoteInstanceId);
|
|
1604
|
+
if (owners.size === 0)
|
|
1605
|
+
callbackOwners.delete(id);
|
|
1606
|
+
else if (owners.size === 1)
|
|
1607
|
+
callbackOwners.set(id, owners.values().next().value);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
const reason = new Error(`Remote endpoint ${remoteInstanceId} was destroyed`);
|
|
1611
|
+
const noKnownRemotesLeft = knownRemotes.size === 0;
|
|
1612
|
+
for (const [id, callbacks] of pending) {
|
|
1613
|
+
const targeted = callbacks.expectedRemote === remoteInstanceId;
|
|
1614
|
+
const stranded = callbacks.expectedRemote === undefined && noKnownRemotesLeft;
|
|
1615
|
+
if (!targeted && !stranded)
|
|
1616
|
+
continue;
|
|
1617
|
+
cancelTimer(callbacks.timer);
|
|
1618
|
+
pending.delete(id);
|
|
1619
|
+
safeOnReject(callbacks);
|
|
1620
|
+
callbacks.reject(reason);
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
bindRemote(remoteInstanceId) {
|
|
1624
|
+
if (!remoteInstanceId || remoteInstanceId === instanceId)
|
|
1625
|
+
return;
|
|
1626
|
+
knownRemotes.add(remoteInstanceId);
|
|
1627
|
+
if (boundRemoteInstanceId === undefined) {
|
|
1628
|
+
boundRemoteInstanceId = remoteInstanceId;
|
|
1629
|
+
resolveRemoteTargetWaiters(remoteInstanceId);
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
noteFreshConnect(remoteInstanceId) {
|
|
1633
|
+
if (!remoteInstanceId || remoteInstanceId === instanceId)
|
|
1634
|
+
return;
|
|
1635
|
+
const isNew = !knownRemotes.has(remoteInstanceId);
|
|
1636
|
+
knownRemotes.add(remoteInstanceId);
|
|
1637
|
+
if (boundRemoteInstanceId === remoteInstanceId)
|
|
1638
|
+
return;
|
|
1639
|
+
if (boundRemoteInstanceId === undefined || isNew) {
|
|
1640
|
+
boundRemoteInstanceId = remoteInstanceId;
|
|
1641
|
+
resolveRemoteTargetWaiters(remoteInstanceId);
|
|
1642
|
+
}
|
|
1643
|
+
},
|
|
1644
|
+
disableRemoteTargetWait() {
|
|
1645
|
+
remoteTargetWaitDisabled = true;
|
|
1646
|
+
resolveRemoteTargetWaiters(undefined);
|
|
1647
|
+
},
|
|
1648
|
+
releaseRemoteCallbacks(remoteInstanceId, ids) {
|
|
1649
|
+
const lru = remoteCallbacksByOwner.get(remoteInstanceId);
|
|
1650
|
+
const pmap = persistentRemoteCallbacksByOwner.get(remoteInstanceId);
|
|
1651
|
+
if (!lru && !pmap)
|
|
1652
|
+
return;
|
|
1653
|
+
for (const id of ids) {
|
|
1654
|
+
lru?.delete(id);
|
|
1655
|
+
pmap?.delete(id);
|
|
1656
|
+
}
|
|
1657
|
+
},
|
|
1658
|
+
dropLocalCallback(id, requesterInstanceId) {
|
|
1659
|
+
dropLocalCallback(id, requesterInstanceId);
|
|
1660
|
+
},
|
|
1661
|
+
noteRemoteSeen(remoteInstanceId) {
|
|
1662
|
+
if (remoteInstanceId && remoteInstanceId !== instanceId) {
|
|
1663
|
+
knownRemotes.add(remoteInstanceId);
|
|
1664
|
+
const preferred = getPreferredRemote();
|
|
1665
|
+
if (preferred !== undefined)
|
|
1666
|
+
resolveRemoteTargetWaiters(preferred);
|
|
1667
|
+
}
|
|
1668
|
+
},
|
|
1669
|
+
bindMethod(fn, owner) {
|
|
1670
|
+
return bindMethod(fn, owner);
|
|
1671
|
+
},
|
|
1672
|
+
getRemoteCallbackCounts(remoteInstanceId) {
|
|
1673
|
+
return {
|
|
1674
|
+
lru: remoteCallbacksByOwner.get(remoteInstanceId)?.size ?? 0,
|
|
1675
|
+
persistent: persistentRemoteCallbacksByOwner.get(remoteInstanceId)?.size ?? 0,
|
|
1676
|
+
};
|
|
1677
|
+
},
|
|
1678
|
+
isDestroyed() {
|
|
1679
|
+
return destroyed;
|
|
1680
|
+
},
|
|
1681
|
+
destroy(notifyRemote = true) {
|
|
1682
|
+
if (destroyed)
|
|
1683
|
+
return;
|
|
1684
|
+
destroyed = true;
|
|
1685
|
+
if (notifyRemote) {
|
|
1686
|
+
const heldRemoteIdSet = new Set();
|
|
1687
|
+
for (const lru of remoteCallbacksByOwner.values()) {
|
|
1688
|
+
for (const id of lru.keys())
|
|
1689
|
+
heldRemoteIdSet.add(id);
|
|
1690
|
+
}
|
|
1691
|
+
for (const map of persistentRemoteCallbacksByOwner.values()) {
|
|
1692
|
+
for (const id of map.keys())
|
|
1693
|
+
heldRemoteIdSet.add(id);
|
|
1694
|
+
}
|
|
1695
|
+
if (heldRemoteIdSet.size > 0) {
|
|
1696
|
+
notifyReleaseCallbacks(Array.from(heldRemoteIdSet));
|
|
1697
|
+
}
|
|
1698
|
+
try {
|
|
1699
|
+
getMessageChannel().getSender().sendMessage('destroyEndpoint', { instanceId });
|
|
1700
|
+
}
|
|
1701
|
+
catch (err) {
|
|
1702
|
+
if (!isTransportDetachedError(err)) {
|
|
1703
|
+
warn('destroy', 'failed to notify remote of destroy', err);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
for (const [id, callbacks] of pending) {
|
|
1708
|
+
cancelTimer(callbacks.timer);
|
|
1709
|
+
safeOnReject(callbacks);
|
|
1710
|
+
callbacks.reject(new Error(`Channel has been destroyed before call ${id} completed`));
|
|
1711
|
+
}
|
|
1712
|
+
pending.clear();
|
|
1713
|
+
functionCache.clear();
|
|
1714
|
+
persistentFunctionCache.clear();
|
|
1715
|
+
persistentRefcount.clear();
|
|
1716
|
+
callbackOwners.clear();
|
|
1717
|
+
knownRemotes.clear();
|
|
1718
|
+
boundRemoteInstanceId = undefined;
|
|
1719
|
+
resolveRemoteTargetWaiters(undefined);
|
|
1720
|
+
remoteCallbacksByOwner.clear();
|
|
1721
|
+
persistentRemoteCallbacksByOwner.clear();
|
|
1722
|
+
},
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function createId(prefix) {
|
|
1726
|
+
return `${prefix}-${nanoid()}`;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
const EMPTY_TARGET = () => undefined;
|
|
1730
|
+
const INTERCEPTED_STRING_PROPS = new Set([
|
|
1731
|
+
'then',
|
|
1732
|
+
'catch',
|
|
1733
|
+
'finally',
|
|
1734
|
+
'toJSON',
|
|
1735
|
+
'toString',
|
|
1736
|
+
'valueOf',
|
|
1737
|
+
'nodeType',
|
|
1738
|
+
'tagName',
|
|
1739
|
+
'asymmetricMatch',
|
|
1740
|
+
'$$typeof',
|
|
1741
|
+
'@@__IMMUTABLE_ITERABLE__@@',
|
|
1742
|
+
'@@__IMMUTABLE_RECORD__@@',
|
|
1743
|
+
'length',
|
|
1744
|
+
'name',
|
|
1745
|
+
'prototype',
|
|
1746
|
+
'arguments',
|
|
1747
|
+
'caller',
|
|
1748
|
+
'bind',
|
|
1749
|
+
'call',
|
|
1750
|
+
'apply',
|
|
1751
|
+
]);
|
|
1752
|
+
const INTERCEPTED_NONE_SYMBOLS = new Set([
|
|
1753
|
+
Symbol.toStringTag,
|
|
1754
|
+
Symbol.iterator,
|
|
1755
|
+
Symbol.asyncIterator,
|
|
1756
|
+
Symbol.isConcatSpreadable,
|
|
1757
|
+
]);
|
|
1758
|
+
const PROXY_DESCRIPTION = '[chrome-in-iframe proxy]';
|
|
1759
|
+
function toPrimitiveDescriptor() {
|
|
1760
|
+
return (hint) => {
|
|
1761
|
+
if (hint === 'number')
|
|
1762
|
+
return NaN;
|
|
1763
|
+
return PROXY_DESCRIPTION;
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
function resolvePath(node) {
|
|
1767
|
+
const path = [];
|
|
1768
|
+
let current = node;
|
|
1769
|
+
while (current) {
|
|
1770
|
+
path.unshift(current.key);
|
|
1771
|
+
current = current.parent;
|
|
1772
|
+
}
|
|
1773
|
+
return path;
|
|
1774
|
+
}
|
|
1775
|
+
function createClientProxy(invoke, accessProperty) {
|
|
1776
|
+
const normalizePropertyPath = (basePath, parts) => {
|
|
1777
|
+
if (parts.length === 1 && typeof parts[0] === 'string') {
|
|
1778
|
+
return [...basePath, ...parts[0].split('.').filter(Boolean)];
|
|
1779
|
+
}
|
|
1780
|
+
const extraPath = parts.flatMap((part) => {
|
|
1781
|
+
if (Array.isArray(part))
|
|
1782
|
+
return part.filter(isPathKey);
|
|
1783
|
+
if (typeof part === 'string')
|
|
1784
|
+
return [part];
|
|
1785
|
+
if (typeof part === 'symbol')
|
|
1786
|
+
return [part];
|
|
1787
|
+
return [];
|
|
1788
|
+
});
|
|
1789
|
+
return [...basePath, ...extraPath];
|
|
1790
|
+
};
|
|
1791
|
+
function createNode(node) {
|
|
1792
|
+
const stringChildren = new Map();
|
|
1793
|
+
const symbolChildren = new Map();
|
|
1794
|
+
let getCache;
|
|
1795
|
+
const handler = {
|
|
1796
|
+
get(_target, prop) {
|
|
1797
|
+
if (typeof prop === 'string') {
|
|
1798
|
+
if (INTERCEPTED_STRING_PROPS.has(prop))
|
|
1799
|
+
return undefined;
|
|
1800
|
+
if (prop === '$get') {
|
|
1801
|
+
if (!getCache) {
|
|
1802
|
+
getCache = (...parts) => {
|
|
1803
|
+
if (!accessProperty) {
|
|
1804
|
+
return Promise.reject(new Error('Property access is not configured'));
|
|
1805
|
+
}
|
|
1806
|
+
const basePath = node ? resolvePath(node) : [];
|
|
1807
|
+
return accessProperty(normalizePropertyPath(basePath, parts));
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
return getCache;
|
|
1811
|
+
}
|
|
1812
|
+
const cached = stringChildren.get(prop);
|
|
1813
|
+
if (cached !== undefined)
|
|
1814
|
+
return cached;
|
|
1815
|
+
const childNode = node ? { parent: node, key: prop } : { key: prop };
|
|
1816
|
+
const child = createNode(childNode);
|
|
1817
|
+
stringChildren.set(prop, child);
|
|
1818
|
+
return child;
|
|
1819
|
+
}
|
|
1820
|
+
if (prop === Symbol.toPrimitive)
|
|
1821
|
+
return toPrimitiveDescriptor();
|
|
1822
|
+
if (INTERCEPTED_NONE_SYMBOLS.has(prop))
|
|
1823
|
+
return undefined;
|
|
1824
|
+
const cached = symbolChildren.get(prop);
|
|
1825
|
+
if (cached !== undefined)
|
|
1826
|
+
return cached;
|
|
1827
|
+
const childNode = node ? { parent: node, key: prop } : { key: prop };
|
|
1828
|
+
const child = createNode(childNode);
|
|
1829
|
+
symbolChildren.set(prop, child);
|
|
1830
|
+
return child;
|
|
1831
|
+
},
|
|
1832
|
+
apply(_target, _thisArg, argArray) {
|
|
1833
|
+
const path = node ? resolvePath(node) : [];
|
|
1834
|
+
return invoke(path, argArray);
|
|
1835
|
+
},
|
|
1836
|
+
};
|
|
1837
|
+
return new Proxy(EMPTY_TARGET, handler);
|
|
1838
|
+
}
|
|
1839
|
+
return createNode();
|
|
1840
|
+
}
|
|
1841
|
+
function isPathKey(value) {
|
|
1842
|
+
return typeof value === 'string' || typeof value === 'symbol';
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
function createEndpoint(options) {
|
|
1846
|
+
const processorRegistry = createProcessorRegistry();
|
|
1847
|
+
const channelRef = {};
|
|
1848
|
+
const getChannel = () => {
|
|
1849
|
+
if (!channelRef.current) {
|
|
1850
|
+
throw new Error('Channel has not been initialized');
|
|
1851
|
+
}
|
|
1852
|
+
return channelRef.current;
|
|
1853
|
+
};
|
|
1854
|
+
const instanceId = `ep-${nanoid()}`;
|
|
1855
|
+
const context = createClientContext(getChannel, instanceId, options.contextOptions);
|
|
1856
|
+
const channel = createMessageChannel(options.poster, options.key ?? 'default', instanceId, context, processorRegistry);
|
|
1857
|
+
channelRef.current = channel;
|
|
1858
|
+
processorRegistry.register('connectRequest', handleConnectRequest);
|
|
1859
|
+
processorRegistry.register('connectResponse', handleConnectResponse);
|
|
1860
|
+
processorRegistry.register('invokeRequest', handleInvokeRequest);
|
|
1861
|
+
processorRegistry.register('invokeResponse', handleInvokeResponse);
|
|
1862
|
+
processorRegistry.register('accessPropertyRequest', handleAccessPropertyRequest);
|
|
1863
|
+
processorRegistry.register('accessPropertyResponse', handleAccessPropertyResponse);
|
|
1864
|
+
processorRegistry.register('invokeFunctionByIdRequest', handleCallbackInvoke);
|
|
1865
|
+
processorRegistry.register('invokeFunctionByIdResponse', handleCallbackInvokeResponse);
|
|
1866
|
+
processorRegistry.register('releaseCallbacks', handleReleaseCallbacks);
|
|
1867
|
+
processorRegistry.register('destroyEndpoint', handleDestroyEndpoint);
|
|
1868
|
+
const proxy = createClientProxy((path, args) => context.invoke(path, args), (path) => context.accessProperty(path));
|
|
1869
|
+
try {
|
|
1870
|
+
channel.getSender().sendMessage('connectRequest', { instanceId });
|
|
1871
|
+
}
|
|
1872
|
+
catch {
|
|
1873
|
+
context.disableRemoteTargetWait();
|
|
1874
|
+
}
|
|
1875
|
+
let destroyed = false;
|
|
1876
|
+
return {
|
|
1877
|
+
proxy,
|
|
1878
|
+
getContext: () => context,
|
|
1879
|
+
isDestroyed: () => destroyed,
|
|
1880
|
+
destroy() {
|
|
1881
|
+
if (destroyed)
|
|
1882
|
+
return;
|
|
1883
|
+
destroyed = true;
|
|
1884
|
+
channel.destroy();
|
|
1885
|
+
context.destroy(true);
|
|
1886
|
+
},
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
function setupInMainWindow(options) {
|
|
1891
|
+
const resolveTargetOrigin = createTargetOriginResolver(() => options.iframe, options.targetOrigin, options.strictOrigin === true);
|
|
1892
|
+
return setupConnection(options, {
|
|
1893
|
+
resolveTargetOrigin,
|
|
1894
|
+
postMessage: (message) => {
|
|
1895
|
+
const contentWindow = options.iframe.contentWindow;
|
|
1896
|
+
if (!contentWindow) {
|
|
1897
|
+
throw createTransportDetachedError('chrome-in-iframe: iframe contentWindow is not available');
|
|
1898
|
+
}
|
|
1899
|
+
contentWindow.postMessage(message, resolveTargetOrigin());
|
|
1900
|
+
},
|
|
1901
|
+
getExpectedSource: () => options.iframe.contentWindow,
|
|
1902
|
+
delegateTarget: options.delegateTarget,
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
function setupInIframe(options = {}) {
|
|
1906
|
+
const resolveTargetOrigin = createParentTargetOriginResolver(options.targetOrigin, options.strictOrigin === true);
|
|
1907
|
+
return setupConnection(options, {
|
|
1908
|
+
resolveTargetOrigin,
|
|
1909
|
+
postMessage: (message) => {
|
|
1910
|
+
window.parent.postMessage(message, resolveTargetOrigin());
|
|
1911
|
+
},
|
|
1912
|
+
getExpectedSource: () => window.parent,
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
function setupConnection(options, sides) {
|
|
1916
|
+
const allowedOrigin = createAllowedOrigin(options.allowedOrigin, sides.resolveTargetOrigin);
|
|
1917
|
+
const poster = createWindowPoster({
|
|
1918
|
+
postMessage: sides.postMessage,
|
|
1919
|
+
getExpectedSource: sides.getExpectedSource,
|
|
1920
|
+
allowedOrigin,
|
|
1921
|
+
});
|
|
1922
|
+
const endpoint = createEndpoint({
|
|
1923
|
+
poster,
|
|
1924
|
+
key: options.key,
|
|
1925
|
+
contextOptions: {
|
|
1926
|
+
delegateTarget: sides.delegateTarget,
|
|
1927
|
+
timeout: options.timeout,
|
|
1928
|
+
functionCacheMax: options.functionCacheMax,
|
|
1929
|
+
functionCacheTtl: options.functionCacheTtl,
|
|
1930
|
+
remoteCallbackCacheMax: options.remoteCallbackCacheMax,
|
|
1931
|
+
remoteCallbackCacheTtl: options.remoteCallbackCacheTtl,
|
|
1932
|
+
},
|
|
1933
|
+
});
|
|
1934
|
+
const proxy = endpoint.proxy;
|
|
1935
|
+
return {
|
|
1936
|
+
proxy,
|
|
1937
|
+
get: proxy.$get,
|
|
1938
|
+
destroy: endpoint.destroy,
|
|
1939
|
+
isDestroyed: endpoint.isDestroyed,
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
function exposeChromeInIframe(options) {
|
|
1943
|
+
return setupInMainWindow({
|
|
1944
|
+
...options,
|
|
1945
|
+
delegateTarget: options.chromeApi ?? getGlobalChrome(),
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
function connectChromeInIframe(options = {}) {
|
|
1949
|
+
const handle = setupInIframe(options);
|
|
1950
|
+
return {
|
|
1951
|
+
...handle,
|
|
1952
|
+
chrome: handle.proxy,
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
function createWindowPoster(options) {
|
|
1956
|
+
const subscriptions = new Map();
|
|
1957
|
+
return {
|
|
1958
|
+
postMessage(message) {
|
|
1959
|
+
options.postMessage(message);
|
|
1960
|
+
},
|
|
1961
|
+
addEventListener(name, callback) {
|
|
1962
|
+
const existing = subscriptions.get(callback);
|
|
1963
|
+
if (existing)
|
|
1964
|
+
windowDispatcher.remove(name, existing);
|
|
1965
|
+
const sub = {
|
|
1966
|
+
isExpectedSource: (source) => source === options.getExpectedSource(),
|
|
1967
|
+
isAllowedOrigin: (origin) => isAllowedOrigin(origin, options.allowedOrigin),
|
|
1968
|
+
deliver: callback,
|
|
1969
|
+
};
|
|
1970
|
+
subscriptions.set(callback, sub);
|
|
1971
|
+
windowDispatcher.add(name, sub);
|
|
1972
|
+
},
|
|
1973
|
+
removeEventListener(name, callback) {
|
|
1974
|
+
const sub = subscriptions.get(callback);
|
|
1975
|
+
if (!sub)
|
|
1976
|
+
return;
|
|
1977
|
+
windowDispatcher.remove(name, sub);
|
|
1978
|
+
subscriptions.delete(callback);
|
|
1979
|
+
},
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
const windowDispatcher = createWindowDispatcher();
|
|
1983
|
+
function createWindowDispatcher() {
|
|
1984
|
+
const perName = new Map();
|
|
1985
|
+
const domListeners = new Map();
|
|
1986
|
+
const ensureDomListener = (name) => {
|
|
1987
|
+
if (domListeners.has(name))
|
|
1988
|
+
return;
|
|
1989
|
+
if (typeof window === 'undefined')
|
|
1990
|
+
return;
|
|
1991
|
+
const handler = (event) => {
|
|
1992
|
+
const messageEvent = event;
|
|
1993
|
+
const subs = perName.get(name);
|
|
1994
|
+
if (!subs || subs.size === 0)
|
|
1995
|
+
return;
|
|
1996
|
+
for (const sub of subs) {
|
|
1997
|
+
if (!sub.isExpectedSource(messageEvent.source))
|
|
1998
|
+
continue;
|
|
1999
|
+
if (!sub.isAllowedOrigin(messageEvent.origin))
|
|
2000
|
+
continue;
|
|
2001
|
+
try {
|
|
2002
|
+
sub.deliver({ data: messageEvent.data });
|
|
2003
|
+
}
|
|
2004
|
+
catch (err) {
|
|
2005
|
+
warn('windowDispatcher', 'subscriber threw while delivering message', err);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
domListeners.set(name, handler);
|
|
2010
|
+
window.addEventListener(name, handler);
|
|
2011
|
+
};
|
|
2012
|
+
return {
|
|
2013
|
+
add(name, sub) {
|
|
2014
|
+
let subs = perName.get(name);
|
|
2015
|
+
if (!subs) {
|
|
2016
|
+
subs = new Set();
|
|
2017
|
+
perName.set(name, subs);
|
|
2018
|
+
}
|
|
2019
|
+
subs.add(sub);
|
|
2020
|
+
ensureDomListener(name);
|
|
2021
|
+
},
|
|
2022
|
+
remove(name, sub) {
|
|
2023
|
+
const subs = perName.get(name);
|
|
2024
|
+
if (!subs)
|
|
2025
|
+
return;
|
|
2026
|
+
subs.delete(sub);
|
|
2027
|
+
if (subs.size > 0)
|
|
2028
|
+
return;
|
|
2029
|
+
perName.delete(name);
|
|
2030
|
+
const handler = domListeners.get(name);
|
|
2031
|
+
if (handler && typeof window !== 'undefined') {
|
|
2032
|
+
window.removeEventListener(name, handler);
|
|
2033
|
+
}
|
|
2034
|
+
domListeners.delete(name);
|
|
2035
|
+
},
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
function isAllowedOrigin(origin, allowedOrigin) {
|
|
2039
|
+
if (allowedOrigin === undefined)
|
|
2040
|
+
return true;
|
|
2041
|
+
if (typeof allowedOrigin === 'function')
|
|
2042
|
+
return allowedOrigin(origin);
|
|
2043
|
+
if (Array.isArray(allowedOrigin))
|
|
2044
|
+
return allowedOrigin.includes(origin);
|
|
2045
|
+
return allowedOrigin === origin;
|
|
2046
|
+
}
|
|
2047
|
+
function createAllowedOrigin(explicit, resolveTargetOrigin) {
|
|
2048
|
+
if (explicit !== undefined)
|
|
2049
|
+
return explicit;
|
|
2050
|
+
return (origin) => {
|
|
2051
|
+
const targetOrigin = resolveTargetOrigin();
|
|
2052
|
+
if (targetOrigin === '*') {
|
|
2053
|
+
if (typeof location !== 'undefined' && location.origin && location.origin !== 'null') {
|
|
2054
|
+
return origin === location.origin;
|
|
2055
|
+
}
|
|
2056
|
+
return false;
|
|
2057
|
+
}
|
|
2058
|
+
return targetOrigin === origin;
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
function getGlobalChrome() {
|
|
2062
|
+
const chromeApi = globalThis.chrome;
|
|
2063
|
+
if (!chromeApi) {
|
|
2064
|
+
throw new Error('chrome-in-iframe: global `chrome` API is not available. ' +
|
|
2065
|
+
'Make sure this runs inside a Chrome extension page, or pass a custom ' +
|
|
2066
|
+
'`chromeApi` option (e.g. for tests / non-extension environments).');
|
|
2067
|
+
}
|
|
2068
|
+
return chromeApi;
|
|
2069
|
+
}
|
|
2070
|
+
function createTargetOriginResolver(getIframe, explicit, strict) {
|
|
2071
|
+
if (explicit)
|
|
2072
|
+
return () => explicit;
|
|
2073
|
+
return freezeResolvedOrigin(() => deriveOriginFromIframe(getIframe(), strict === true));
|
|
2074
|
+
}
|
|
2075
|
+
function createParentTargetOriginResolver(explicit, strict) {
|
|
2076
|
+
if (explicit)
|
|
2077
|
+
return () => explicit;
|
|
2078
|
+
return freezeResolvedOrigin(() => deriveParentOrigin(strict === true));
|
|
2079
|
+
}
|
|
2080
|
+
function freezeResolvedOrigin(resolve) {
|
|
2081
|
+
let frozen;
|
|
2082
|
+
return () => {
|
|
2083
|
+
if (frozen !== undefined)
|
|
2084
|
+
return frozen;
|
|
2085
|
+
const resolved = resolve();
|
|
2086
|
+
if (resolved !== '*')
|
|
2087
|
+
frozen = resolved;
|
|
2088
|
+
return resolved;
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
function deriveOriginFromIframe(iframe, strict) {
|
|
2092
|
+
const src = iframe.src || iframe.getAttribute('src') || '';
|
|
2093
|
+
const origin = parseOrigin(src);
|
|
2094
|
+
if (origin)
|
|
2095
|
+
return origin;
|
|
2096
|
+
try {
|
|
2097
|
+
if (iframe.contentWindow && iframe.contentWindow.location) {
|
|
2098
|
+
const loc = iframe.contentWindow.location;
|
|
2099
|
+
if (loc.origin && loc.origin !== 'null')
|
|
2100
|
+
return loc.origin;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
catch (err) {
|
|
2104
|
+
warn('deriveOriginFromIframe', 'unable to access cross-origin contentWindow.location', err);
|
|
2105
|
+
}
|
|
2106
|
+
if (strict) {
|
|
2107
|
+
throw new Error('chrome-in-iframe: unable to derive iframe origin automatically; pass `targetOrigin` explicitly or set `strictOrigin: false`.');
|
|
2108
|
+
}
|
|
2109
|
+
warn('deriveOriginFromIframe', "unable to derive iframe origin automatically; falling back to '*'. " +
|
|
2110
|
+
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
2111
|
+
return '*';
|
|
2112
|
+
}
|
|
2113
|
+
function deriveParentOrigin(strict) {
|
|
2114
|
+
if (typeof document !== 'undefined') {
|
|
2115
|
+
const referrer = document.referrer;
|
|
2116
|
+
const origin = parseOrigin(referrer);
|
|
2117
|
+
if (origin)
|
|
2118
|
+
return origin;
|
|
2119
|
+
}
|
|
2120
|
+
if (typeof location !== 'undefined' && location.ancestorOrigins && location.ancestorOrigins.length > 0) {
|
|
2121
|
+
const first = location.ancestorOrigins[0];
|
|
2122
|
+
if (first && first !== 'null')
|
|
2123
|
+
return first;
|
|
2124
|
+
}
|
|
2125
|
+
if (strict) {
|
|
2126
|
+
throw new Error('chrome-in-iframe: unable to derive parent origin automatically; pass `targetOrigin` explicitly or set `strictOrigin: false`.');
|
|
2127
|
+
}
|
|
2128
|
+
warn('deriveParentOrigin', "unable to derive parent origin automatically; falling back to '*'. " +
|
|
2129
|
+
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
2130
|
+
return '*';
|
|
2131
|
+
}
|
|
2132
|
+
function parseOrigin(url) {
|
|
2133
|
+
if (!url)
|
|
2134
|
+
return null;
|
|
2135
|
+
try {
|
|
2136
|
+
const parsed = new URL(url, typeof location !== 'undefined' ? location.href : 'http://localhost');
|
|
2137
|
+
if (parsed.origin && parsed.origin !== 'null')
|
|
2138
|
+
return parsed.origin;
|
|
2139
|
+
}
|
|
2140
|
+
catch (err) {
|
|
2141
|
+
warn('parseOrigin', 'failed to parse URL', url, err);
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
exports.TRANSPORT_DETACHED = TRANSPORT_DETACHED;
|
|
2148
|
+
exports.connectChromeInIframe = connectChromeInIframe;
|
|
2149
|
+
exports.exposeChromeInIframe = exposeChromeInIframe;
|
|
2150
|
+
exports.isTransportDetachedError = isTransportDetachedError;
|
|
2151
|
+
exports.setLogger = setLogger;
|
|
2152
|
+
exports.setupInIframe = setupInIframe;
|
|
2153
|
+
exports.setupInMainWindow = setupInMainWindow;
|