appium-xcuitest-driver 12.2.1 → 12.3.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 +6 -0
- package/npm-shrinkwrap.json +11 -11
- package/package.json +1 -1
- package/scripts/lib/retry-policy.mjs +127 -0
- package/scripts/tunnel-creation.mjs +109 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [12.3.0](https://github.com/appium/appium-xcuitest-driver/compare/v12.2.1...v12.3.0) (2026-08-08)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* Add customizable exponential backoff for tunnel reconnect retries ([#2936](https://github.com/appium/appium-xcuitest-driver/issues/2936)) ([84ed39d](https://github.com/appium/appium-xcuitest-driver/commit/84ed39dcaf274368f28fed85c980cf7d23ce470e))
|
|
6
|
+
|
|
1
7
|
## [12.2.1](https://github.com/appium/appium-xcuitest-driver/compare/v12.2.0...v12.2.1) (2026-08-07)
|
|
2
8
|
|
|
3
9
|
### Miscellaneous Chores
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-xcuitest-driver",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "appium-xcuitest-driver",
|
|
9
|
-
"version": "12.
|
|
9
|
+
"version": "12.3.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@appium/css-locator-to-native": "^1.0.1",
|
|
@@ -572,9 +572,9 @@
|
|
|
572
572
|
}
|
|
573
573
|
},
|
|
574
574
|
"node_modules/appium-remote-debugger": {
|
|
575
|
-
"version": "16.
|
|
576
|
-
"resolved": "https://registry.npmjs.org/appium-remote-debugger/-/appium-remote-debugger-16.
|
|
577
|
-
"integrity": "sha512-
|
|
575
|
+
"version": "16.4.0",
|
|
576
|
+
"resolved": "https://registry.npmjs.org/appium-remote-debugger/-/appium-remote-debugger-16.4.0.tgz",
|
|
577
|
+
"integrity": "sha512-Rr0u0CNZPte2aEBozGIUJxRclzyqNUkBBrDZLo4rxwwEfnalFcpWfTJa9/f1YtkdakMfTRysJA3YFwmGuVFQ8w==",
|
|
578
578
|
"license": "Apache-2.0",
|
|
579
579
|
"dependencies": {
|
|
580
580
|
"@appium/base-driver": "^10.0.0-rc.1",
|
|
@@ -786,9 +786,9 @@
|
|
|
786
786
|
}
|
|
787
787
|
},
|
|
788
788
|
"node_modules/bare-url": {
|
|
789
|
-
"version": "2.
|
|
790
|
-
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.
|
|
791
|
-
"integrity": "sha512-
|
|
789
|
+
"version": "2.5.1",
|
|
790
|
+
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.1.tgz",
|
|
791
|
+
"integrity": "sha512-cD5ciQuKlx+eumTCfqbfiL+fhQm+dHbVNB/cX4+d+I/nx4JsVop9VoFEPAs5RJ6I84QR6bZIBjpd2kjDOYeWcg==",
|
|
792
792
|
"license": "Apache-2.0",
|
|
793
793
|
"dependencies": {
|
|
794
794
|
"bare-path": "^3.0.0"
|
|
@@ -3620,9 +3620,9 @@
|
|
|
3620
3620
|
"license": "ISC"
|
|
3621
3621
|
},
|
|
3622
3622
|
"node_modules/ws": {
|
|
3623
|
-
"version": "8.21.
|
|
3624
|
-
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.
|
|
3625
|
-
"integrity": "sha512-
|
|
3623
|
+
"version": "8.21.3",
|
|
3624
|
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
|
|
3625
|
+
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
|
3626
3626
|
"license": "MIT",
|
|
3627
3627
|
"engines": {
|
|
3628
3628
|
"node": ">=10.0.0"
|
package/package.json
CHANGED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export const DEFAULT_DISCONNECT_RETRY_INTERVAL_MS = 1000;
|
|
2
|
+
export const DEFAULT_DISCONNECT_RETRY_BACKOFF_MULTIPLIER = 2;
|
|
3
|
+
export const DEFAULT_DISCONNECT_RETRY_BACKOFF_MAX_INTERVAL_MS = 30_000;
|
|
4
|
+
export const DEFAULT_DISCONNECT_RETRY_BACKOFF_JITTER = 0.5;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @abstract
|
|
8
|
+
* Common attempt-counting behavior shared by all retry policies. Subclasses only need to implement
|
|
9
|
+
* `getDelayMs()`.
|
|
10
|
+
*/
|
|
11
|
+
export class RetryPolicy {
|
|
12
|
+
/**
|
|
13
|
+
* @param {{maxAttempts: number | null, intervalMs: number}} opts - maxAttempts: null disables
|
|
14
|
+
* retries entirely; 0 means unlimited retries
|
|
15
|
+
*/
|
|
16
|
+
constructor({maxAttempts, intervalMs}) {
|
|
17
|
+
if (new.target === RetryPolicy) {
|
|
18
|
+
throw new TypeError('RetryPolicy is abstract and cannot be instantiated directly');
|
|
19
|
+
}
|
|
20
|
+
this.maxAttempts = maxAttempts;
|
|
21
|
+
this._intervalMs = intervalMs;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get isEnabled() {
|
|
25
|
+
return this.maxAttempts !== null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {number} attemptsMade - number of attempts already made
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
hasAttemptsRemaining(attemptsMade) {
|
|
33
|
+
const {maxAttempts} = this;
|
|
34
|
+
if (maxAttempts === null) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return maxAttempts === 0 || attemptsMade < maxAttempts;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @abstract
|
|
42
|
+
* @param {number} _attempt - 1-based number of the attempt about to be made
|
|
43
|
+
* @returns {number}
|
|
44
|
+
*/
|
|
45
|
+
getDelayMs(_attempt) {
|
|
46
|
+
throw new Error('Not implemented');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fixed-interval retry policy: waits the same delay between every attempt.
|
|
52
|
+
*/
|
|
53
|
+
export class FixedIntervalRetryPolicy extends RetryPolicy {
|
|
54
|
+
/**
|
|
55
|
+
* @param {number} _attempt - 1-based number of the attempt about to be made
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
58
|
+
getDelayMs(_attempt) {
|
|
59
|
+
return this._intervalMs;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Exponential-backoff retry policy: the delay grows by a multiplier on each attempt, capped at a
|
|
65
|
+
* configured maximum.
|
|
66
|
+
*/
|
|
67
|
+
export class ExponentialBackoffRetryPolicy extends RetryPolicy {
|
|
68
|
+
/**
|
|
69
|
+
* @param {{maxAttempts: number | null, intervalMs: number, backoffMultiplier: number, backoffMaxIntervalMs: number, jitter: number}} opts
|
|
70
|
+
* - jitter: fraction of the delay to randomize away, in [0, 1]. 0 always returns the exact
|
|
71
|
+
* computed delay; 1 returns a value uniformly distributed between 0 and the computed delay.
|
|
72
|
+
* Jitter spreads out devices that dropped together instead of having them retry in lockstep and
|
|
73
|
+
* stay synchronised once they reach the cap.
|
|
74
|
+
*/
|
|
75
|
+
constructor({maxAttempts, intervalMs, backoffMultiplier, backoffMaxIntervalMs, jitter}) {
|
|
76
|
+
super({maxAttempts, intervalMs});
|
|
77
|
+
if (backoffMaxIntervalMs < intervalMs) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Invalid disconnect retry backoff max interval: ${backoffMaxIntervalMs}. ` +
|
|
80
|
+
`Expected a value >= the initial interval (${intervalMs}).`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (jitter < 0 || jitter > 1) {
|
|
84
|
+
throw new Error(`Invalid disconnect retry backoff jitter: ${jitter}. Expected a value between 0 and 1.`);
|
|
85
|
+
}
|
|
86
|
+
this._backoffMultiplier = backoffMultiplier;
|
|
87
|
+
this._backoffMaxIntervalMs = backoffMaxIntervalMs;
|
|
88
|
+
this._jitter = jitter;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {number} attempt - 1-based number of the attempt about to be made
|
|
93
|
+
* @returns {number}
|
|
94
|
+
*/
|
|
95
|
+
getDelayMs(attempt) {
|
|
96
|
+
const delayMs = this._intervalMs * this._backoffMultiplier ** (attempt - 1);
|
|
97
|
+
const cappedDelayMs = Math.min(delayMs, this._backoffMaxIntervalMs);
|
|
98
|
+
return Math.round(cappedDelayMs * (1 - this._jitter * Math.random()));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {{strategy: 'fixed' | 'exponential', maxAttempts: number | null, intervalMs: number, backoffMultiplier?: number, backoffMaxIntervalMs?: number, jitter?: number}} opts
|
|
104
|
+
* @returns {RetryPolicy}
|
|
105
|
+
*/
|
|
106
|
+
export function createDisconnectRetryPolicy({
|
|
107
|
+
strategy,
|
|
108
|
+
maxAttempts,
|
|
109
|
+
intervalMs,
|
|
110
|
+
backoffMultiplier = DEFAULT_DISCONNECT_RETRY_BACKOFF_MULTIPLIER,
|
|
111
|
+
backoffMaxIntervalMs = DEFAULT_DISCONNECT_RETRY_BACKOFF_MAX_INTERVAL_MS,
|
|
112
|
+
jitter = DEFAULT_DISCONNECT_RETRY_BACKOFF_JITTER,
|
|
113
|
+
}) {
|
|
114
|
+
if (strategy === 'fixed') {
|
|
115
|
+
return new FixedIntervalRetryPolicy({maxAttempts, intervalMs});
|
|
116
|
+
}
|
|
117
|
+
if (strategy === 'exponential') {
|
|
118
|
+
return new ExponentialBackoffRetryPolicy({
|
|
119
|
+
maxAttempts,
|
|
120
|
+
intervalMs,
|
|
121
|
+
backoffMultiplier,
|
|
122
|
+
backoffMaxIntervalMs,
|
|
123
|
+
jitter,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Invalid disconnect retry strategy: ${strategy}. Expected 'fixed' or 'exponential'.`);
|
|
127
|
+
}
|
|
@@ -27,6 +27,14 @@ import {Command} from 'commander';
|
|
|
27
27
|
|
|
28
28
|
import {parsePositiveIntegerOption} from './lib/options.mjs';
|
|
29
29
|
import {startTimeoutProgressLogger} from './lib/progress.mjs';
|
|
30
|
+
import {
|
|
31
|
+
createDisconnectRetryPolicy,
|
|
32
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_JITTER,
|
|
33
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_MAX_INTERVAL_MS,
|
|
34
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_MULTIPLIER,
|
|
35
|
+
DEFAULT_DISCONNECT_RETRY_INTERVAL_MS,
|
|
36
|
+
FixedIntervalRetryPolicy,
|
|
37
|
+
} from './lib/retry-policy.mjs';
|
|
30
38
|
import {assertRoot} from './lib/root.mjs';
|
|
31
39
|
|
|
32
40
|
const log = logger.getLogger('TunnelCreation');
|
|
@@ -65,9 +73,11 @@ class TunnelCreator {
|
|
|
65
73
|
/** @type {Map<string, import('appium-ios-remotexpc').AppleTVTunnelService>} */
|
|
66
74
|
this._appletvTunnelServicesByUdid = new Map();
|
|
67
75
|
this._isCleaningUp = false;
|
|
68
|
-
/** @type {
|
|
69
|
-
this.
|
|
70
|
-
|
|
76
|
+
/** @type {import('./lib/retry-policy.mjs').RetryPolicy} */
|
|
77
|
+
this._disconnectRetryPolicy = new FixedIntervalRetryPolicy({
|
|
78
|
+
maxAttempts: null,
|
|
79
|
+
intervalMs: DEFAULT_DISCONNECT_RETRY_INTERVAL_MS,
|
|
80
|
+
});
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
get tunnelRegistryPort() {
|
|
@@ -83,12 +93,10 @@ class TunnelCreator {
|
|
|
83
93
|
}
|
|
84
94
|
|
|
85
95
|
/**
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {number} intervalMs
|
|
96
|
+
* @param {import('./lib/retry-policy.mjs').RetryPolicy} policy
|
|
88
97
|
*/
|
|
89
|
-
setDisconnectRetryPolicy(
|
|
90
|
-
this.
|
|
91
|
-
this._disconnectRetryIntervalMs = intervalMs;
|
|
98
|
+
setDisconnectRetryPolicy(policy) {
|
|
99
|
+
this._disconnectRetryPolicy = policy;
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
/**
|
|
@@ -576,10 +584,6 @@ class TunnelCreator {
|
|
|
576
584
|
if (!this._isRetryEnabledForUdid(udid)) {
|
|
577
585
|
return;
|
|
578
586
|
}
|
|
579
|
-
const maxAttempts = this._disconnectRetryMaxAttempts;
|
|
580
|
-
if (maxAttempts === null) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
587
|
|
|
584
588
|
const device = this._usbDevicesByUdid.get(udid);
|
|
585
589
|
const isAppleTV = this._appleTVDeviceIds.has(udid);
|
|
@@ -587,17 +591,19 @@ class TunnelCreator {
|
|
|
587
591
|
return;
|
|
588
592
|
}
|
|
589
593
|
|
|
590
|
-
|
|
594
|
+
const policy = this._disconnectRetryPolicy;
|
|
595
|
+
let attemptsMade = 0;
|
|
591
596
|
while (!this._isCleaningUp) {
|
|
592
|
-
if (
|
|
597
|
+
if (!policy.hasAttemptsRemaining(attemptsMade)) {
|
|
593
598
|
log.warn(`Retry limit reached for ${udid}; keeping it removed from the registry`);
|
|
594
599
|
return;
|
|
595
600
|
}
|
|
596
|
-
|
|
601
|
+
attemptsMade += 1;
|
|
602
|
+
const delayMs = policy.getDelayMs(attemptsMade);
|
|
597
603
|
log.warn(
|
|
598
|
-
`Retrying tunnel creation for ${udid} (attempt ${
|
|
604
|
+
`Retrying tunnel creation for ${udid} (attempt ${attemptsMade}${policy.maxAttempts === 0 ? ', unlimited' : `/${policy.maxAttempts}`}) in ${delayMs}ms...`,
|
|
599
605
|
);
|
|
600
|
-
await sleep(
|
|
606
|
+
await sleep(delayMs);
|
|
601
607
|
|
|
602
608
|
try {
|
|
603
609
|
this._registryServer?.markTunnelPending(udid);
|
|
@@ -635,8 +641,7 @@ class TunnelCreator {
|
|
|
635
641
|
*/
|
|
636
642
|
_isRetryEnabledForUdid(udid) {
|
|
637
643
|
return (
|
|
638
|
-
(this._usbDevicesByUdid.has(udid) || this._appleTVDeviceIds.has(udid)) &&
|
|
639
|
-
this._disconnectRetryMaxAttempts !== null
|
|
644
|
+
(this._usbDevicesByUdid.has(udid) || this._appleTVDeviceIds.has(udid)) && this._disconnectRetryPolicy.isEnabled
|
|
640
645
|
);
|
|
641
646
|
}
|
|
642
647
|
|
|
@@ -800,6 +805,43 @@ function parseNonNegativeIntegerOption(value, label) {
|
|
|
800
805
|
return count;
|
|
801
806
|
}
|
|
802
807
|
|
|
808
|
+
/**
|
|
809
|
+
* @param {string} value
|
|
810
|
+
* @returns {'fixed' | 'exponential'}
|
|
811
|
+
*/
|
|
812
|
+
function parseRetryStrategyOption(value) {
|
|
813
|
+
if (value !== 'fixed' && value !== 'exponential') {
|
|
814
|
+
throw new Error(`Invalid disconnect retry strategy: ${value}. Expected 'fixed' or 'exponential'.`);
|
|
815
|
+
}
|
|
816
|
+
return value;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* @param {string} value
|
|
821
|
+
* @param {string} label
|
|
822
|
+
* @returns {number}
|
|
823
|
+
*/
|
|
824
|
+
function parseRetryBackoffMultiplierOption(value, label) {
|
|
825
|
+
const multiplier = Number.parseFloat(value);
|
|
826
|
+
if (!Number.isFinite(multiplier) || multiplier <= 1) {
|
|
827
|
+
throw new Error(`Invalid ${label}: ${value}. Expected a number greater than 1.`);
|
|
828
|
+
}
|
|
829
|
+
return multiplier;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* @param {string} value
|
|
834
|
+
* @param {string} label
|
|
835
|
+
* @returns {number}
|
|
836
|
+
*/
|
|
837
|
+
function parseRetryBackoffJitterOption(value, label) {
|
|
838
|
+
const jitter = Number.parseFloat(value);
|
|
839
|
+
if (!Number.isFinite(jitter) || jitter < 0 || jitter > 1) {
|
|
840
|
+
throw new Error(`Invalid ${label}: ${value}. Expected a number between 0 and 1.`);
|
|
841
|
+
}
|
|
842
|
+
return jitter;
|
|
843
|
+
}
|
|
844
|
+
|
|
803
845
|
/**
|
|
804
846
|
* @param {string} value
|
|
805
847
|
* @param {string[]} previous
|
|
@@ -906,11 +948,38 @@ async function main() {
|
|
|
906
948
|
'Max tunnel recreation attempts after unexpected disconnect: 0 = unlimited; omit to disable retries',
|
|
907
949
|
(value) => parseNonNegativeIntegerOption(value, 'disconnect retry max attempts'),
|
|
908
950
|
)
|
|
951
|
+
.option(
|
|
952
|
+
'--disconnect-retry-strategy <strategy>',
|
|
953
|
+
"Delay strategy between tunnel recreation attempts: 'fixed' or 'exponential'",
|
|
954
|
+
(value) => parseRetryStrategyOption(value),
|
|
955
|
+
'fixed',
|
|
956
|
+
)
|
|
909
957
|
.option(
|
|
910
958
|
'--disconnect-retry-interval-ms <ms>',
|
|
911
|
-
'Delay between tunnel recreation attempts in milliseconds
|
|
959
|
+
'Delay between tunnel recreation attempts in milliseconds. ' +
|
|
960
|
+
'With --disconnect-retry-strategy exponential, this is the initial delay, before it starts growing',
|
|
912
961
|
(value) => parsePositiveIntegerOption(value, 'disconnect retry interval'),
|
|
913
|
-
|
|
962
|
+
DEFAULT_DISCONNECT_RETRY_INTERVAL_MS,
|
|
963
|
+
)
|
|
964
|
+
.option(
|
|
965
|
+
'--disconnect-retry-backoff-multiplier <factor>',
|
|
966
|
+
'Factor the delay grows by on each attempt when --disconnect-retry-strategy is exponential (must be > 1)',
|
|
967
|
+
(value) => parseRetryBackoffMultiplierOption(value, 'disconnect retry backoff multiplier'),
|
|
968
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_MULTIPLIER,
|
|
969
|
+
)
|
|
970
|
+
.option(
|
|
971
|
+
'--disconnect-retry-backoff-max-interval-ms <ms>',
|
|
972
|
+
'Upper bound on the delay between tunnel recreation attempts when --disconnect-retry-strategy is exponential',
|
|
973
|
+
(value) => parsePositiveIntegerOption(value, 'disconnect retry backoff max interval'),
|
|
974
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_MAX_INTERVAL_MS,
|
|
975
|
+
)
|
|
976
|
+
.option(
|
|
977
|
+
'--disconnect-retry-backoff-jitter <factor>',
|
|
978
|
+
'Fraction of the delay to randomize away on each attempt when --disconnect-retry-strategy is exponential, ' +
|
|
979
|
+
'between 0 (no jitter) and 1 (full jitter). Spreads out devices that dropped together instead of having ' +
|
|
980
|
+
'them retry in lockstep',
|
|
981
|
+
(value) => parseRetryBackoffJitterOption(value, 'disconnect retry backoff jitter'),
|
|
982
|
+
DEFAULT_DISCONNECT_RETRY_BACKOFF_JITTER,
|
|
914
983
|
);
|
|
915
984
|
|
|
916
985
|
program.parse(process.argv);
|
|
@@ -922,6 +991,24 @@ async function main() {
|
|
|
922
991
|
const shouldRunUsbFlow = !hasRequestedAppleTVIds || hasRequestedUdids;
|
|
923
992
|
const shouldRunAppleTVFlow = !hasRequestedUdids || hasRequestedAppleTVIds;
|
|
924
993
|
|
|
994
|
+
/** @type {import('./lib/retry-policy.mjs').RetryPolicy} */
|
|
995
|
+
let disconnectRetryPolicy;
|
|
996
|
+
try {
|
|
997
|
+
disconnectRetryPolicy = createDisconnectRetryPolicy({
|
|
998
|
+
strategy: options.disconnectRetryStrategy,
|
|
999
|
+
maxAttempts: options.disconnectRetryMaxAttempts ?? null,
|
|
1000
|
+
intervalMs: options.disconnectRetryIntervalMs,
|
|
1001
|
+
backoffMultiplier: options.disconnectRetryBackoffMultiplier,
|
|
1002
|
+
backoffMaxIntervalMs: options.disconnectRetryBackoffMaxIntervalMs,
|
|
1003
|
+
jitter: options.disconnectRetryBackoffJitter,
|
|
1004
|
+
});
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
// program.error() never returns (it exits the process); the throw below is unreachable and
|
|
1007
|
+
// only here so TS can see disconnectRetryPolicy is always assigned past this point.
|
|
1008
|
+
program.error(/** @type {Error} */ (err).message);
|
|
1009
|
+
throw err;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
925
1012
|
await assertRoot(path.parse(fileURLToPath(import.meta.url)).name);
|
|
926
1013
|
|
|
927
1014
|
const tunnelCreator = new TunnelCreator({
|
|
@@ -953,10 +1040,7 @@ async function main() {
|
|
|
953
1040
|
});
|
|
954
1041
|
}
|
|
955
1042
|
|
|
956
|
-
tunnelCreator.setDisconnectRetryPolicy(
|
|
957
|
-
options.disconnectRetryMaxAttempts ?? null,
|
|
958
|
-
options.disconnectRetryIntervalMs,
|
|
959
|
-
);
|
|
1043
|
+
tunnelCreator.setDisconnectRetryPolicy(disconnectRetryPolicy);
|
|
960
1044
|
|
|
961
1045
|
await tunnelCreator.startRegistryServer();
|
|
962
1046
|
|