appium-remote-debugger 12.2.10 → 13.0.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 +10 -0
- package/build/lib/mixins/connect.d.ts.map +1 -1
- package/build/lib/mixins/connect.js +32 -37
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/execute.d.ts.map +1 -1
- package/build/lib/mixins/execute.js +14 -12
- package/build/lib/mixins/execute.js.map +1 -1
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +9 -96
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/lib/protocol/index.d.ts +3 -1
- package/build/lib/protocol/index.d.ts.map +1 -1
- package/build/lib/protocol/index.js +22 -23
- package/build/lib/protocol/index.js.map +1 -1
- package/build/lib/rpc/index.d.ts +1 -2
- package/build/lib/rpc/index.d.ts.map +1 -1
- package/build/lib/rpc/index.js +1 -36
- package/build/lib/rpc/index.js.map +1 -1
- package/build/lib/rpc/remote-messages.d.ts +24 -18
- package/build/lib/rpc/remote-messages.d.ts.map +1 -1
- package/build/lib/rpc/remote-messages.js +24 -11
- package/build/lib/rpc/remote-messages.js.map +1 -1
- package/build/lib/rpc/rpc-client-real-device.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-real-device.js +1 -3
- package/build/lib/rpc/rpc-client-real-device.js.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client-simulator.js +1 -3
- package/build/lib/rpc/rpc-client-simulator.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts +63 -44
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +209 -122
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/rpc/rpc-message-handler.d.ts +8 -5
- package/build/lib/rpc/rpc-message-handler.d.ts.map +1 -1
- package/build/lib/rpc/rpc-message-handler.js +61 -75
- package/build/lib/rpc/rpc-message-handler.js.map +1 -1
- package/build/lib/types.d.ts +28 -3
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts +4 -3
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +4 -2
- package/build/lib/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/mixins/connect.js +34 -38
- package/lib/mixins/execute.js +15 -12
- package/lib/mixins/navigate.js +11 -99
- package/lib/protocol/index.js +22 -24
- package/lib/rpc/index.js +1 -3
- package/lib/rpc/remote-messages.js +28 -11
- package/lib/rpc/rpc-client-real-device.js +1 -3
- package/lib/rpc/rpc-client-simulator.js +1 -3
- package/lib/rpc/rpc-client.js +244 -133
- package/lib/rpc/rpc-message-handler.js +62 -74
- package/lib/types.ts +33 -3
- package/lib/utils.js +4 -2
- package/package.json +2 -1
- package/build/lib/rpc/constants.d.ts +0 -2
- package/build/lib/rpc/constants.d.ts.map +0 -1
- package/build/lib/rpc/constants.js +0 -5
- package/build/lib/rpc/constants.js.map +0 -1
- package/lib/rpc/constants.js +0 -1
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RpcClient = void 0;
|
|
6
|
+
exports.RpcClient = exports.EMPTY_PAGE_DICTIONARY_ERROR = exports.NEW_APP_CONNECTED_ERROR = void 0;
|
|
7
|
+
exports.toLockKey = toLockKey;
|
|
7
8
|
const remote_messages_1 = require("./remote-messages");
|
|
8
9
|
const asyncbox_1 = require("asyncbox");
|
|
9
10
|
const logger_1 = __importDefault(require("../logger"));
|
|
@@ -12,7 +13,7 @@ const bluebird_1 = __importDefault(require("bluebird"));
|
|
|
12
13
|
const rpc_message_handler_1 = __importDefault(require("./rpc-message-handler"));
|
|
13
14
|
const support_1 = require("@appium/support");
|
|
14
15
|
const node_events_1 = require("node:events");
|
|
15
|
-
const
|
|
16
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
16
17
|
const DATA_LOG_LENGTH = { length: 200 };
|
|
17
18
|
const WAIT_FOR_TARGET_TIMEOUT_MS = 10000;
|
|
18
19
|
const WAIT_FOR_TARGET_INTERVAL_MS = 100;
|
|
@@ -25,6 +26,8 @@ const NO_TARGET_PRESENT_YET_ERRORS = [
|
|
|
25
26
|
`some arguments of method`,
|
|
26
27
|
`missing target`,
|
|
27
28
|
];
|
|
29
|
+
exports.NEW_APP_CONNECTED_ERROR = 'New application has connected';
|
|
30
|
+
exports.EMPTY_PAGE_DICTIONARY_ERROR = 'Empty page dictionary received';
|
|
28
31
|
/**
|
|
29
32
|
* @param {boolean} isSafari
|
|
30
33
|
* @param {string} platformVersion
|
|
@@ -63,7 +66,6 @@ class RpcClient {
|
|
|
63
66
|
this._targets = {};
|
|
64
67
|
this._targetSubscriptions = new node_events_1.EventEmitter();
|
|
65
68
|
// start with a best guess for the protocol
|
|
66
|
-
this._shouldCheckForTarget = !!opts.shouldCheckForTarget;
|
|
67
69
|
this.isTargetBased = platformVersion ? isTargetBased(isSafari, platformVersion) : true;
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
@@ -76,23 +78,14 @@ class RpcClient {
|
|
|
76
78
|
* @returns {boolean}
|
|
77
79
|
*/
|
|
78
80
|
get needsTarget() {
|
|
79
|
-
return this.
|
|
81
|
+
return this.isTargetBased;
|
|
80
82
|
}
|
|
81
83
|
/**
|
|
82
|
-
* @returns {
|
|
84
|
+
* @returns {AppToTargetsMap}
|
|
83
85
|
*/
|
|
84
86
|
get targets() {
|
|
85
87
|
return this._targets;
|
|
86
88
|
}
|
|
87
|
-
/**
|
|
88
|
-
* @returns {boolean}
|
|
89
|
-
*/
|
|
90
|
-
get shouldCheckForTarget() {
|
|
91
|
-
return this._shouldCheckForTarget;
|
|
92
|
-
}
|
|
93
|
-
set shouldCheckForTarget(shouldCheckForTarget) {
|
|
94
|
-
this._shouldCheckForTarget = !!shouldCheckForTarget;
|
|
95
|
-
}
|
|
96
89
|
/**
|
|
97
90
|
* @returns {boolean}
|
|
98
91
|
*/
|
|
@@ -178,47 +171,51 @@ class RpcClient {
|
|
|
178
171
|
*
|
|
179
172
|
* @param {import('../types').AppIdKey} appIdKey
|
|
180
173
|
* @param {import('../types').PageIdKey} pageIdKey
|
|
181
|
-
* @returns {Promise<
|
|
174
|
+
* @returns {Promise<import('../types').TargetId | undefined>}
|
|
182
175
|
*/
|
|
183
176
|
async waitForTarget(appIdKey, pageIdKey) {
|
|
184
177
|
if (!this.needsTarget) {
|
|
185
178
|
logger_1.default.debug(`Target-based communication is not needed, skipping wait for target`);
|
|
186
179
|
return;
|
|
187
180
|
}
|
|
188
|
-
|
|
181
|
+
let target = this.getTarget(appIdKey, pageIdKey);
|
|
189
182
|
if (target) {
|
|
190
183
|
logger_1.default.debug(`The target '${target}' for app '${appIdKey}' and page '${pageIdKey}' already exists, no need to wait`);
|
|
191
|
-
return;
|
|
184
|
+
return target;
|
|
192
185
|
}
|
|
193
186
|
// otherwise waiting is necessary to see what the target is
|
|
194
187
|
try {
|
|
195
|
-
await (0, asyncbox_1.waitForCondition)(() =>
|
|
188
|
+
await (0, asyncbox_1.waitForCondition)(() => {
|
|
189
|
+
target = this.getTarget(appIdKey, pageIdKey);
|
|
190
|
+
return !lodash_1.default.isEmpty(target);
|
|
191
|
+
}, {
|
|
196
192
|
waitMs: WAIT_FOR_TARGET_TIMEOUT_MS,
|
|
197
193
|
intervalMs: WAIT_FOR_TARGET_INTERVAL_MS,
|
|
198
|
-
error: 'No targets found, unable to communicate with device',
|
|
199
194
|
});
|
|
195
|
+
return target;
|
|
200
196
|
}
|
|
201
197
|
catch (err) {
|
|
202
198
|
if (!err.message.includes('Condition unmet')) {
|
|
203
199
|
throw err;
|
|
204
200
|
}
|
|
205
|
-
throw new Error(
|
|
201
|
+
throw new Error(`No targets could be matched for the app '${appIdKey}' and page '${pageIdKey}' after ` +
|
|
202
|
+
`${WAIT_FOR_TARGET_TIMEOUT_MS}ms. This is likely a bug.`);
|
|
206
203
|
}
|
|
207
204
|
}
|
|
208
205
|
/**
|
|
209
206
|
*
|
|
210
207
|
* @param {string} command
|
|
211
208
|
* @param {import('../types').RemoteCommandOpts} opts
|
|
212
|
-
* @param {boolean} [waitForResponse]
|
|
209
|
+
* @param {boolean} [waitForResponse=true]
|
|
213
210
|
* @returns {Promise<any>}
|
|
214
211
|
*/
|
|
215
212
|
async send(command, opts, waitForResponse = true) {
|
|
216
213
|
const timer = new support_1.timing.Timer().start();
|
|
217
|
-
const { appIdKey, pageIdKey } = opts;
|
|
218
214
|
try {
|
|
219
215
|
return await this.sendToDevice(command, opts, waitForResponse);
|
|
220
216
|
}
|
|
221
217
|
catch (err) {
|
|
218
|
+
const { appIdKey, pageIdKey } = opts;
|
|
222
219
|
const messageLc = (err.message || '').toLowerCase();
|
|
223
220
|
if (messageLc.includes(NO_TARGET_SUPPORTED_ERROR)) {
|
|
224
221
|
logger_1.default.info('The target device does not support Target based communication. ' +
|
|
@@ -239,11 +236,13 @@ class RpcClient {
|
|
|
239
236
|
}
|
|
240
237
|
/**
|
|
241
238
|
*
|
|
239
|
+
* @template {boolean} TWaitForResponse
|
|
242
240
|
* @param {string} command
|
|
243
241
|
* @param {import('../types').RemoteCommandOpts} opts
|
|
244
|
-
* @param {
|
|
245
|
-
* @returns {Promise<any>}
|
|
242
|
+
* @param {TWaitForResponse} [waitForResponse=true]
|
|
243
|
+
* @returns {Promise<TWaitForResponse extends true ? import('../types').RemoteCommandOpts : any>}
|
|
246
244
|
*/
|
|
245
|
+
// @ts-ignore Compiler issue
|
|
247
246
|
async sendToDevice(command, opts, waitForResponse = true) {
|
|
248
247
|
return await new bluebird_1.default(async (resolve, reject) => {
|
|
249
248
|
// promise to be resolved whenever remote debugger
|
|
@@ -264,23 +263,39 @@ class RpcClient {
|
|
|
264
263
|
}
|
|
265
264
|
const appIdKey = opts.appIdKey;
|
|
266
265
|
const pageIdKey = opts.pageIdKey;
|
|
267
|
-
const targetId = this.getTarget(appIdKey, pageIdKey);
|
|
266
|
+
const targetId = opts.targetId ?? this.getTarget(appIdKey, pageIdKey);
|
|
268
267
|
// retrieve the correct command to send
|
|
268
|
+
/** @type {import('../types').RemoteCommandOpts} */
|
|
269
269
|
const fullOpts = lodash_1.default.defaults({
|
|
270
270
|
connId: this.connId,
|
|
271
271
|
senderId: this.senderId,
|
|
272
272
|
targetId,
|
|
273
273
|
id: msgId,
|
|
274
274
|
}, opts);
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
/** @type {import('../types').RawRemoteCommand} */
|
|
276
|
+
let cmd;
|
|
277
|
+
try {
|
|
278
|
+
// @ts-ignore remoteMessages must be defined
|
|
279
|
+
cmd = this.remoteMessages.getRemoteCommand(command, fullOpts);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
logger_1.default.error(err);
|
|
283
|
+
return reject(err);
|
|
284
|
+
}
|
|
285
|
+
/** @type {import('../types').RemoteCommand} */
|
|
286
|
+
const finalCommand = {
|
|
287
|
+
__argument: lodash_1.default.omit(cmd.__argument, ['WIRSocketDataKey']),
|
|
288
|
+
__selector: cmd.__selector,
|
|
289
|
+
};
|
|
290
|
+
const hasSocketData = lodash_1.default.isPlainObject(cmd.__argument?.WIRSocketDataKey);
|
|
291
|
+
if (hasSocketData) {
|
|
278
292
|
// make sure the message being sent has all the information that is needed
|
|
293
|
+
// @ts-ignore We have asserted it's a plain object above
|
|
279
294
|
if (lodash_1.default.isNil(cmd.__argument.WIRSocketDataKey.id)) {
|
|
295
|
+
// @ts-ignore We have already asserted it's a plain object above
|
|
280
296
|
cmd.__argument.WIRSocketDataKey.id = wrapperMsgId;
|
|
281
297
|
}
|
|
282
|
-
|
|
283
|
-
Buffer.from(JSON.stringify(cmd.__argument.WIRSocketDataKey));
|
|
298
|
+
finalCommand.__argument.WIRSocketDataKey = Buffer.from(JSON.stringify(cmd.__argument.WIRSocketDataKey));
|
|
284
299
|
}
|
|
285
300
|
let messageHandled = true;
|
|
286
301
|
if (!waitForResponse) {
|
|
@@ -292,7 +307,8 @@ class RpcClient {
|
|
|
292
307
|
if (err) {
|
|
293
308
|
// we are not waiting for this, and if it errors it is most likely
|
|
294
309
|
// a protocol change. Log and check during testing
|
|
295
|
-
logger_1.default.error(`Received error from send that is not being waited for (id: ${msgId}):
|
|
310
|
+
logger_1.default.error(`Received error from send that is not being waited for (id: ${msgId}): ` +
|
|
311
|
+
lodash_1.default.truncate(JSON.stringify(err), DATA_LOG_LENGTH));
|
|
296
312
|
// reject, though it is very rare that this will be triggered, since
|
|
297
313
|
// the promise is resolved directlty after send. On the off chance,
|
|
298
314
|
// though, it will alert of a protocol change.
|
|
@@ -308,10 +324,11 @@ class RpcClient {
|
|
|
308
324
|
return reject(err);
|
|
309
325
|
}
|
|
310
326
|
logger_1.default.debug(`Received response from send (id: ${msgId}): '${lodash_1.default.truncate(JSON.stringify(args), DATA_LOG_LENGTH)}'`);
|
|
327
|
+
// @ts-ignore This is ok
|
|
311
328
|
resolve(args);
|
|
312
329
|
});
|
|
313
330
|
}
|
|
314
|
-
else if (
|
|
331
|
+
else if (hasSocketData) {
|
|
315
332
|
// @ts-ignore messageHandler must be defined
|
|
316
333
|
this.messageHandler.once(msgId.toString(), function (err, value) {
|
|
317
334
|
if (err) {
|
|
@@ -332,11 +349,12 @@ class RpcClient {
|
|
|
332
349
|
` (id: ${msgId}): '${command}'`;
|
|
333
350
|
logger_1.default.debug(msg);
|
|
334
351
|
try {
|
|
335
|
-
|
|
352
|
+
await this.sendMessage(finalCommand);
|
|
336
353
|
if (!messageHandled) {
|
|
337
354
|
// There are no handlers waiting for a response before resolving,
|
|
338
355
|
// and no errors sending the message over the socket, so resolve
|
|
339
|
-
|
|
356
|
+
// @ts-ignore This is ok
|
|
357
|
+
resolve(fullOpts);
|
|
340
358
|
}
|
|
341
359
|
}
|
|
342
360
|
catch (err) {
|
|
@@ -368,61 +386,99 @@ class RpcClient {
|
|
|
368
386
|
}
|
|
369
387
|
/**
|
|
370
388
|
*
|
|
371
|
-
* @param {Error
|
|
372
|
-
* @param {
|
|
373
|
-
* @param {
|
|
374
|
-
* @returns {void}
|
|
389
|
+
* @param {Error | undefined} err
|
|
390
|
+
* @param {import('../types').AppIdKey} app
|
|
391
|
+
* @param {import('../types').TargetInfo} targetInfo
|
|
392
|
+
* @returns {Promise<void>}
|
|
375
393
|
*/
|
|
376
|
-
addTarget(err, app, targetInfo) {
|
|
394
|
+
async addTarget(err, app, targetInfo) {
|
|
377
395
|
if (lodash_1.default.isNil(targetInfo?.targetId)) {
|
|
378
|
-
logger_1.default.
|
|
396
|
+
logger_1.default.info(`Received 'Target.targetCreated' event for app '${app}' with no target. Skipping`);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (!this._pendingTargetNotification) {
|
|
400
|
+
logger_1.default.info(`Received 'Target.targetCreated' event for app '${app}' with no pending request: ${JSON.stringify(targetInfo)}`);
|
|
379
401
|
return;
|
|
380
402
|
}
|
|
381
|
-
|
|
382
|
-
|
|
403
|
+
const [appIdKey, pageIdKey] = this._pendingTargetNotification;
|
|
404
|
+
if (appIdKey !== app) {
|
|
405
|
+
logger_1.default.info(`Received 'Target.targetCreated' event for app '${app}' with no pending request: ${JSON.stringify(targetInfo)}`);
|
|
383
406
|
return;
|
|
384
407
|
}
|
|
385
408
|
if (targetInfo.isProvisional) {
|
|
386
|
-
logger_1.default.debug(`Provisional target created for app '${
|
|
409
|
+
logger_1.default.debug(`Provisional target created for app '${appIdKey}' and page '${pageIdKey}': '${targetInfo.targetId}'`);
|
|
410
|
+
if (!targetInfo.isPaused) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
logger_1.default.debug(`The target ${targetInfo.targetId}@${appIdKey} is paused`);
|
|
414
|
+
const appTargetsMap = this.targets[appIdKey];
|
|
415
|
+
if (appTargetsMap) {
|
|
416
|
+
await appTargetsMap.lock.acquire(toLockKey(appIdKey, pageIdKey), async () => {
|
|
417
|
+
try {
|
|
418
|
+
await this._initializePage(appIdKey, pageIdKey, targetInfo.targetId);
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
await this._resumeTarget(appIdKey, pageIdKey, targetInfo.targetId);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
387
425
|
return;
|
|
388
426
|
}
|
|
389
|
-
// @ts-ignore this.pendingTargetNotification must be defined here
|
|
390
|
-
const [appIdKey, pageIdKey] = this.pendingTargetNotification;
|
|
391
427
|
logger_1.default.debug(`Target created for app '${appIdKey}' and page '${pageIdKey}': ${JSON.stringify(targetInfo)}`);
|
|
392
428
|
if (lodash_1.default.has(this.targets[appIdKey], pageIdKey)) {
|
|
393
429
|
logger_1.default.debug(`There is already a target for this app and page ('${this.targets[appIdKey][pageIdKey]}'). This might cause problems`);
|
|
394
430
|
}
|
|
395
|
-
|
|
431
|
+
const lock = new async_lock_1.default();
|
|
432
|
+
this.targets[app] = this.targets[app] || { lock };
|
|
396
433
|
this.targets[appIdKey][pageIdKey] = targetInfo.targetId;
|
|
434
|
+
await lock.acquire(toLockKey(appIdKey, pageIdKey), async () => {
|
|
435
|
+
try {
|
|
436
|
+
await this.send('Target.setPauseOnStart', {
|
|
437
|
+
pauseOnStart: true,
|
|
438
|
+
appIdKey,
|
|
439
|
+
pageIdKey,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
catch { }
|
|
443
|
+
try {
|
|
444
|
+
await this._initializePage(appIdKey, pageIdKey);
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
if (targetInfo.isPaused) {
|
|
448
|
+
await this._resumeTarget(appIdKey, pageIdKey);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
397
452
|
}
|
|
398
453
|
/**
|
|
399
454
|
*
|
|
400
|
-
* @param {Error
|
|
401
|
-
* @param {
|
|
402
|
-
* @param {
|
|
403
|
-
* @
|
|
404
|
-
* @returns {void}
|
|
455
|
+
* @param {Error | undefined} err
|
|
456
|
+
* @param {import('../types').AppIdKey} app
|
|
457
|
+
* @param {import('../types').ProvisionalTargetInfo} targetInfo
|
|
458
|
+
* @returns {Promise<void>}
|
|
405
459
|
*/
|
|
406
|
-
updateTarget(err, app,
|
|
460
|
+
async updateTarget(err, app, targetInfo) {
|
|
461
|
+
const { oldTargetId, newTargetId, } = targetInfo;
|
|
407
462
|
logger_1.default.debug(`Target updated for app '${app}'. Old target: '${oldTargetId}', new target: '${newTargetId}'`);
|
|
408
|
-
|
|
463
|
+
const appTargetsMap = this.targets[app];
|
|
464
|
+
if (!appTargetsMap) {
|
|
409
465
|
logger_1.default.warn(`No existing target for app '${app}'. Not sure what to do`);
|
|
410
466
|
return;
|
|
411
467
|
}
|
|
412
468
|
// save this, to be used if/when the existing target is destroyed
|
|
413
|
-
|
|
469
|
+
appTargetsMap.provisional = {
|
|
414
470
|
oldTargetId,
|
|
415
471
|
newTargetId,
|
|
416
472
|
};
|
|
417
473
|
}
|
|
418
474
|
/**
|
|
419
475
|
*
|
|
420
|
-
* @param {Error
|
|
421
|
-
* @param {
|
|
422
|
-
* @param {
|
|
423
|
-
* @returns {void}
|
|
476
|
+
* @param {Error | undefined} err
|
|
477
|
+
* @param {import('../types').AppIdKey} app
|
|
478
|
+
* @param {import('../types').TargetInfo} targetInfo
|
|
479
|
+
* @returns {Promise<void>}
|
|
424
480
|
*/
|
|
425
|
-
removeTarget(err, app, targetInfo) {
|
|
481
|
+
async removeTarget(err, app, targetInfo) {
|
|
426
482
|
if (lodash_1.default.isNil(targetInfo?.targetId)) {
|
|
427
483
|
logger_1.default.debug(`Received 'Target.targetDestroyed' event with no target. Skipping`);
|
|
428
484
|
return;
|
|
@@ -433,30 +489,17 @@ class RpcClient {
|
|
|
433
489
|
const { oldTargetId, newTargetId } = this.targets[app].provisional;
|
|
434
490
|
delete this.targets[app].provisional;
|
|
435
491
|
// we do not know the page, so go through and find the existing target
|
|
436
|
-
const
|
|
437
|
-
for (const [page, targetId] of lodash_1.default.toPairs(
|
|
492
|
+
const appTargetsMap = this.targets[app];
|
|
493
|
+
for (const [page, targetId] of lodash_1.default.toPairs(appTargetsMap)) {
|
|
438
494
|
if (targetId === oldTargetId) {
|
|
439
|
-
logger_1.default.debug(`Found provisional target for app '${app}'.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
(async () => {
|
|
443
|
-
if (this.fullPageInitialization) {
|
|
444
|
-
await this.initializePageFull(opts.appIdKey, opts.pageIdKey);
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
await this.initializePage(opts.appIdKey, opts.pageIdKey);
|
|
448
|
-
}
|
|
449
|
-
this._targetSubscriptions.emit(constants_1.ON_TARGET_PROVISIONED_EVENT, {
|
|
450
|
-
...opts,
|
|
451
|
-
oldTargetId,
|
|
452
|
-
targetId: newTargetId,
|
|
453
|
-
});
|
|
454
|
-
})();
|
|
495
|
+
logger_1.default.debug(`Found provisional target for app '${app}'. ` +
|
|
496
|
+
`Old target: '${oldTargetId}', new target: '${newTargetId}'. Updating`);
|
|
497
|
+
appTargetsMap[page] = newTargetId;
|
|
455
498
|
return;
|
|
456
499
|
}
|
|
457
500
|
}
|
|
458
501
|
logger_1.default.warn(`Provisional target for app '${app}' found, but no suitable existing target found. This may cause problems`);
|
|
459
|
-
logger_1.default.warn(`Old target: '${oldTargetId}', new target: '${newTargetId}'. Existing targets: ${JSON.stringify(
|
|
502
|
+
logger_1.default.warn(`Old target: '${oldTargetId}', new target: '${newTargetId}'. Existing targets: ${JSON.stringify(appTargetsMap)}`);
|
|
460
503
|
}
|
|
461
504
|
// if there is no waiting provisional target, just get rid of the existing one
|
|
462
505
|
const targets = this.targets[app];
|
|
@@ -485,9 +528,7 @@ class RpcClient {
|
|
|
485
528
|
* @returns {Promise<void>}
|
|
486
529
|
*/
|
|
487
530
|
async selectPage(appIdKey, pageIdKey) {
|
|
488
|
-
|
|
489
|
-
this.pendingTargetNotification = [appIdKey, pageIdKey];
|
|
490
|
-
this.shouldCheckForTarget = false;
|
|
531
|
+
this._pendingTargetNotification = [appIdKey, pageIdKey];
|
|
491
532
|
// go through the steps that the Desktop Safari system
|
|
492
533
|
// goes through to initialize the Web Inspector session
|
|
493
534
|
const sendOpts = {
|
|
@@ -502,47 +543,15 @@ class RpcClient {
|
|
|
502
543
|
}
|
|
503
544
|
await this.send('setSenderKey', sendOpts);
|
|
504
545
|
logger_1.default.debug('Sender key set');
|
|
546
|
+
if (!this.isTargetBased) {
|
|
547
|
+
await this._initializePage(appIdKey, pageIdKey);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
505
550
|
if (this.isTargetBased && support_1.util.compareVersions(this.platformVersion, '<', MIN_PLATFORM_NO_TARGET_EXISTS)) {
|
|
506
551
|
await this.send('Target.exists', sendOpts, false);
|
|
507
552
|
}
|
|
508
|
-
this.shouldCheckForTarget = true;
|
|
509
553
|
await this.waitForTarget(appIdKey, pageIdKey);
|
|
510
|
-
|
|
511
|
-
await this.initializePageFull(appIdKey, pageIdKey);
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
await this.initializePage(appIdKey, pageIdKey);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Perform the minimal initialization to get the Web Inspector working
|
|
519
|
-
* @param {import('../types').AppIdKey} appIdKey
|
|
520
|
-
* @param {import('../types').PageIdKey} pageIdKey
|
|
521
|
-
* @returns {Promise<void>}
|
|
522
|
-
*/
|
|
523
|
-
async initializePage(appIdKey, pageIdKey) {
|
|
524
|
-
const sendOpts = {
|
|
525
|
-
appIdKey,
|
|
526
|
-
pageIdKey,
|
|
527
|
-
};
|
|
528
|
-
// The sequence of domains is important
|
|
529
|
-
for (const domain of [
|
|
530
|
-
'Inspector.enable',
|
|
531
|
-
'Page.enable',
|
|
532
|
-
'Runtime.enable',
|
|
533
|
-
'Network.enable',
|
|
534
|
-
'Heap.enable',
|
|
535
|
-
'Debugger.enable',
|
|
536
|
-
'Console.enable',
|
|
537
|
-
'Inspector.initialized',
|
|
538
|
-
]) {
|
|
539
|
-
try {
|
|
540
|
-
await this.send(domain, sendOpts);
|
|
541
|
-
}
|
|
542
|
-
catch (err) {
|
|
543
|
-
logger_1.default.info(`Cannot enable domain '${domain}' during initialization: ${err.message}`);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
554
|
+
await this.waitForPageInitialization(appIdKey, pageIdKey);
|
|
546
555
|
}
|
|
547
556
|
/**
|
|
548
557
|
* Mimic every step that Desktop Safari Develop tools uses to initialize a
|
|
@@ -550,13 +559,38 @@ class RpcClient {
|
|
|
550
559
|
*
|
|
551
560
|
* @param {import('../types').AppIdKey} appIdKey
|
|
552
561
|
* @param {import('../types').PageIdKey} pageIdKey
|
|
562
|
+
* @param {import('../types').TargetId} [targetId]
|
|
553
563
|
* @returns {Promise<void>}
|
|
554
564
|
*/
|
|
555
|
-
async
|
|
565
|
+
async _initializePage(appIdKey, pageIdKey, targetId) {
|
|
556
566
|
const sendOpts = {
|
|
557
567
|
appIdKey,
|
|
558
568
|
pageIdKey,
|
|
569
|
+
targetId,
|
|
559
570
|
};
|
|
571
|
+
logger_1.default.debug(`Initializing page '${pageIdKey}' for app '${appIdKey}'`);
|
|
572
|
+
if (!this.fullPageInitialization) {
|
|
573
|
+
// The sequence of domains is important
|
|
574
|
+
for (const domain of [
|
|
575
|
+
'Inspector.enable',
|
|
576
|
+
'Page.enable',
|
|
577
|
+
'Runtime.enable',
|
|
578
|
+
'Network.enable',
|
|
579
|
+
'Heap.enable',
|
|
580
|
+
'Debugger.enable',
|
|
581
|
+
'Console.enable',
|
|
582
|
+
'Inspector.initialized',
|
|
583
|
+
]) {
|
|
584
|
+
try {
|
|
585
|
+
await this.send(domain, sendOpts);
|
|
586
|
+
}
|
|
587
|
+
catch (err) {
|
|
588
|
+
logger_1.default.info(`Cannot enable domain '${domain}' during initialization: ${err.message}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
logger_1.default.debug(`Simple initialization of page '${pageIdKey}' for app '${appIdKey}' completed`);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
560
594
|
// The sequence of commands here is important
|
|
561
595
|
const domainsToOptsMap = {
|
|
562
596
|
'Inspector.enable': sendOpts,
|
|
@@ -634,6 +668,7 @@ class RpcClient {
|
|
|
634
668
|
logger_1.default.info(`Cannot enable domain '${domain}' during full initialization: ${err.message}`);
|
|
635
669
|
}
|
|
636
670
|
}
|
|
671
|
+
logger_1.default.debug(`Full initialization of page '${pageIdKey}' for app '${appIdKey}' completed`);
|
|
637
672
|
}
|
|
638
673
|
/**
|
|
639
674
|
*
|
|
@@ -658,7 +693,7 @@ class RpcClient {
|
|
|
658
693
|
logger_1.default.debug(`We were notified we might have connected to the wrong app. ` +
|
|
659
694
|
`Using id ${correctAppIdKey} instead of ${oldAppIdKey}`);
|
|
660
695
|
}
|
|
661
|
-
reject(new Error(
|
|
696
|
+
reject(new Error(exports.NEW_APP_CONNECTED_ERROR));
|
|
662
697
|
};
|
|
663
698
|
this.messageHandler?.prependOnceListener('_rpc_applicationConnected:', onAppChange);
|
|
664
699
|
// do the actual connecting to the app
|
|
@@ -668,7 +703,7 @@ class RpcClient {
|
|
|
668
703
|
// sometimes the connect logic happens, but with an empty dictionary
|
|
669
704
|
// which leads to the remote debugger getting disconnected, and into a loop
|
|
670
705
|
if (lodash_1.default.isEmpty(pageDict)) {
|
|
671
|
-
reject(new Error(
|
|
706
|
+
reject(new Error(exports.EMPTY_PAGE_DICTIONARY_ERROR));
|
|
672
707
|
}
|
|
673
708
|
else {
|
|
674
709
|
resolve([connectedAppIdKey, pageDict]);
|
|
@@ -711,8 +746,54 @@ class RpcClient {
|
|
|
711
746
|
// { scriptId: '13', url: '', startLine: 0, startColumn: 0, endLine: 82, endColumn: 3 }
|
|
712
747
|
logger_1.default.debug(`Script parsed: ${JSON.stringify(scriptInfo)}`);
|
|
713
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
*
|
|
751
|
+
* @param {import('../types').AppIdKey} appIdKey
|
|
752
|
+
* @param {import('../types').PageIdKey} pageIdKey
|
|
753
|
+
* @param {import('../types').TargetId} [targetId]
|
|
754
|
+
* @returns {Promise<void>}
|
|
755
|
+
*/
|
|
756
|
+
async _resumeTarget(appIdKey, pageIdKey, targetId) {
|
|
757
|
+
try {
|
|
758
|
+
await this.send('Target.resume', {
|
|
759
|
+
appIdKey,
|
|
760
|
+
pageIdKey,
|
|
761
|
+
targetId,
|
|
762
|
+
});
|
|
763
|
+
logger_1.default.debug(`Successfully resumed the target ${targetId}@${appIdKey}`);
|
|
764
|
+
}
|
|
765
|
+
catch { }
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
*
|
|
769
|
+
* @param {import('../types').AppIdKey} appIdKey
|
|
770
|
+
* @param {import('../types').PageIdKey} pageIdKey
|
|
771
|
+
*/
|
|
772
|
+
async waitForPageInitialization(appIdKey, pageIdKey) {
|
|
773
|
+
const appTargetsMap = this.targets[appIdKey];
|
|
774
|
+
if (!appTargetsMap) {
|
|
775
|
+
throw new Error(`No targets found for app '${appIdKey}'`);
|
|
776
|
+
}
|
|
777
|
+
/** @type {AsyncLock | undefined} */
|
|
778
|
+
const lock = appTargetsMap.lock;
|
|
779
|
+
if (lock?.isBusy()) {
|
|
780
|
+
const timer = new support_1.timing.Timer().start();
|
|
781
|
+
logger_1.default.debug(`Waiting until page ${pageIdKey}@${appIdKey} is fully initialized`);
|
|
782
|
+
await lock.acquire(toLockKey(appIdKey, pageIdKey), async () => await bluebird_1.default.delay(0));
|
|
783
|
+
logger_1.default.debug(`The initalization of the page ${pageIdKey}@${appIdKey} took ${timer.getDuration().asMilliSeconds}ms`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
714
786
|
}
|
|
715
787
|
exports.RpcClient = RpcClient;
|
|
788
|
+
/**
|
|
789
|
+
*
|
|
790
|
+
* @param {import('../types').AppIdKey} appIdKey
|
|
791
|
+
* @param {import('../types').PageIdKey} pageIdKey
|
|
792
|
+
* @returns {string}
|
|
793
|
+
*/
|
|
794
|
+
function toLockKey(appIdKey, pageIdKey) {
|
|
795
|
+
return `${appIdKey}-${pageIdKey}`;
|
|
796
|
+
}
|
|
716
797
|
exports.default = RpcClient;
|
|
717
798
|
/**
|
|
718
799
|
* @typedef {Object} RpcClientOptions
|
|
@@ -725,6 +806,12 @@ exports.default = RpcClient;
|
|
|
725
806
|
* @property {number} [socketChunkSize]
|
|
726
807
|
* @property {boolean} [fullPageInitialization=false]
|
|
727
808
|
* @property {string} [udid]
|
|
728
|
-
* @property {boolean} [shouldCheckForTarget]
|
|
729
809
|
*/
|
|
810
|
+
/**
|
|
811
|
+
* @typedef {{[key: import('../types').PageIdKey]: import('../types').TargetId}} PageDict
|
|
812
|
+
*/
|
|
813
|
+
/**
|
|
814
|
+
* @typedef {PageDict & {provisional?: import('../types').ProvisionalTargetInfo, lock: import('async-lock').default}} PagesToTargets
|
|
815
|
+
* @typedef {{[key: import('../types').AppIdKey]: PagesToTargets}} AppToTargetsMap
|
|
816
|
+
*/
|
|
730
817
|
//# sourceMappingURL=rpc-client.js.map
|