@limrun/api 0.58.1 → 0.60.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 +75 -0
- package/destination-tunnel-dialer.d.mts +7 -0
- package/destination-tunnel-dialer.d.mts.map +1 -1
- package/destination-tunnel-dialer.d.ts +7 -0
- package/destination-tunnel-dialer.d.ts.map +1 -1
- package/destination-tunnel-dialer.js +15 -4
- package/destination-tunnel-dialer.js.map +1 -1
- package/destination-tunnel-dialer.mjs +13 -3
- package/destination-tunnel-dialer.mjs.map +1 -1
- package/destination-tunnel.d.mts +4 -0
- package/destination-tunnel.d.mts.map +1 -1
- package/destination-tunnel.d.ts +4 -0
- package/destination-tunnel.d.ts.map +1 -1
- package/destination-tunnel.js +2 -0
- package/destination-tunnel.js.map +1 -1
- package/destination-tunnel.mjs +2 -0
- package/destination-tunnel.mjs.map +1 -1
- package/index.d.mts +1 -0
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +4 -2
- package/index.js.map +1 -1
- package/index.mjs +1 -0
- package/index.mjs.map +1 -1
- package/internal/destination-tunnel-management.d.mts +2 -0
- package/internal/destination-tunnel-management.d.mts.map +1 -1
- package/internal/destination-tunnel-management.d.ts +2 -0
- package/internal/destination-tunnel-management.d.ts.map +1 -1
- package/internal/destination-tunnel-management.js +5 -0
- package/internal/destination-tunnel-management.js.map +1 -1
- package/internal/destination-tunnel-management.mjs +6 -1
- package/internal/destination-tunnel-management.mjs.map +1 -1
- package/internal/destination-tunnel-url.d.mts +2 -0
- package/internal/destination-tunnel-url.d.mts.map +1 -1
- package/internal/destination-tunnel-url.d.ts +2 -0
- package/internal/destination-tunnel-url.d.ts.map +1 -1
- package/internal/destination-tunnel-url.js +9 -2
- package/internal/destination-tunnel-url.js.map +1 -1
- package/internal/destination-tunnel-url.mjs +8 -2
- package/internal/destination-tunnel-url.mjs.map +1 -1
- package/ios-client.d.mts +58 -7
- package/ios-client.d.mts.map +1 -1
- package/ios-client.d.ts +58 -7
- package/ios-client.d.ts.map +1 -1
- package/ios-client.js +58 -30
- package/ios-client.js.map +1 -1
- package/ios-client.mjs +58 -30
- package/ios-client.mjs.map +1 -1
- package/ios-launch-environment.d.mts +3 -0
- package/ios-launch-environment.d.mts.map +1 -0
- package/ios-launch-environment.d.ts +3 -0
- package/ios-launch-environment.d.ts.map +1 -0
- package/ios-launch-environment.js +14 -0
- package/ios-launch-environment.js.map +1 -0
- package/ios-launch-environment.mjs +11 -0
- package/ios-launch-environment.mjs.map +1 -0
- package/package.json +22 -2
- package/resources/android-instances.d.mts +12 -0
- package/resources/android-instances.d.mts.map +1 -1
- package/resources/android-instances.d.ts +12 -0
- package/resources/android-instances.d.ts.map +1 -1
- package/resources/ios-instances.d.mts +12 -0
- package/resources/ios-instances.d.mts.map +1 -1
- package/resources/ios-instances.d.ts +12 -0
- package/resources/ios-instances.d.ts.map +1 -1
- package/src/destination-tunnel-dialer.ts +18 -3
- package/src/destination-tunnel.ts +6 -0
- package/src/index.ts +6 -0
- package/src/internal/destination-tunnel-management.ts +15 -1
- package/src/internal/destination-tunnel-url.ts +15 -2
- package/src/ios-client.ts +137 -43
- package/src/ios-launch-environment.ts +11 -0
- package/src/resources/android-instances.ts +14 -0
- package/src/resources/ios-instances.ts +14 -0
- package/src/tunnel-connector.ts +677 -0
- package/src/tunnel.ts +8 -6
- package/src/version.ts +1 -1
- package/tunnel-connector.d.mts +77 -0
- package/tunnel-connector.d.mts.map +1 -0
- package/tunnel-connector.d.ts +77 -0
- package/tunnel-connector.d.ts.map +1 -0
- package/tunnel-connector.js +561 -0
- package/tunnel-connector.js.map +1 -0
- package/tunnel-connector.mjs +557 -0
- package/tunnel-connector.mjs.map +1 -0
- package/tunnel.d.mts +2 -0
- package/tunnel.d.mts.map +1 -1
- package/tunnel.d.ts +2 -0
- package/tunnel.d.ts.map +1 -1
- package/tunnel.js +8 -6
- package/tunnel.js.map +1 -1
- package/tunnel.mjs +7 -6
- package/tunnel.mjs.map +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/ios-client.ts
CHANGED
|
@@ -405,26 +405,68 @@ export type DetoxLaunchRuntime = {
|
|
|
405
405
|
|
|
406
406
|
export type LaunchAppRuntime = DetoxLaunchRuntime;
|
|
407
407
|
|
|
408
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Why a launched or watched app shut down.
|
|
410
|
+
* - `crash`: the process died on a fatal signal; `crash` details are attached.
|
|
411
|
+
* - `exit`: the process ended without a crash and was not stopped through the SDK.
|
|
412
|
+
* - `terminated`: the app was stopped via {@link InstanceClient.terminateApp} or a relaunch.
|
|
413
|
+
*/
|
|
414
|
+
export type AppExitReason = 'crash' | 'exit' | 'terminated';
|
|
409
415
|
|
|
410
|
-
type
|
|
416
|
+
export type AppCrashInfo = {
|
|
417
|
+
processName: string;
|
|
418
|
+
pid: number;
|
|
419
|
+
/** Exception type and signal, for example `EXC_BAD_ACCESS (SIGSEGV) at 0x10`. */
|
|
420
|
+
shortMsg: string;
|
|
421
|
+
/** The crash message the app left, such as an uncaught exception's reason, else `shortMsg`. */
|
|
422
|
+
longMsg: string;
|
|
411
423
|
/**
|
|
412
|
-
*
|
|
413
|
-
*
|
|
424
|
+
* The crashed thread's frames, with symbol names for the app's own code, followed by the
|
|
425
|
+
* binary images they belong to. Symbolicate file and line with your dSYM, for example
|
|
426
|
+
* `atos -o App.dSYM -l <load address> <frame address>`.
|
|
414
427
|
*/
|
|
428
|
+
stackTrace: string;
|
|
429
|
+
timeMillis: number;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
export type AppExitInfo = {
|
|
433
|
+
bundleId: string;
|
|
434
|
+
reason: AppExitReason;
|
|
435
|
+
crash?: AppCrashInfo;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Called when the app shuts down. For a launched app the logs array contains one entry per
|
|
440
|
+
* stdout/stderr line that launch produced; a watched app reports no logs. `info` carries the
|
|
441
|
+
* exit reason plus crash details when applicable.
|
|
442
|
+
*/
|
|
443
|
+
export type LaunchAppExitCallback = (logs: string[], info: AppExitInfo) => Promise<void> | void;
|
|
444
|
+
|
|
445
|
+
/** Handle for an app watch registered via {@link InstanceClient.watchApp}. */
|
|
446
|
+
export type AppWatch = {
|
|
447
|
+
/** Identifier of this watch on the server. */
|
|
448
|
+
execId: string;
|
|
449
|
+
/** Cancels the watch; the onExit callback will not be invoked afterwards. */
|
|
450
|
+
stop: () => Promise<void>;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
type LaunchAppCommonOptions = {
|
|
454
|
+
/** App environment variables. Requires relaunch; reserved keys and existing launch variables are ignored. */
|
|
455
|
+
env?: Record<string, string>;
|
|
456
|
+
/** Called once when the launched app exits, crashes, or is terminated. */
|
|
415
457
|
onExit?: LaunchAppExitCallback;
|
|
416
458
|
};
|
|
417
459
|
|
|
418
|
-
type StandardLaunchAppOptions =
|
|
460
|
+
type StandardLaunchAppOptions = LaunchAppCommonOptions & {
|
|
419
461
|
/**
|
|
420
462
|
* Launch behavior when the app may already be running.
|
|
421
|
-
* Defaults to `ForegroundIfRunning`
|
|
463
|
+
* Defaults to `ForegroundIfRunning`, or `RelaunchIfRunning` when env is nonempty.
|
|
422
464
|
*/
|
|
423
465
|
mode?: LaunchAppMode;
|
|
424
466
|
runtime?: undefined;
|
|
425
467
|
};
|
|
426
468
|
|
|
427
|
-
type RuntimeLaunchAppOptions =
|
|
469
|
+
type RuntimeLaunchAppOptions = LaunchAppCommonOptions & {
|
|
428
470
|
/** Runtime launches must relaunch so runtime injection is applied. */
|
|
429
471
|
mode?: Extract<LaunchAppMode, 'RelaunchIfRunning'>;
|
|
430
472
|
/** Optional app runtime to attach during launch. */
|
|
@@ -722,6 +764,20 @@ export type InstanceClient = {
|
|
|
722
764
|
*/
|
|
723
765
|
terminateApp: (bundleId: string) => Promise<void>;
|
|
724
766
|
|
|
767
|
+
/**
|
|
768
|
+
* Watch an app's exit or crash without launching it through {@link launchApp}, for example
|
|
769
|
+
* an app opened with {@link openUrl} or already running. The callback fires once, with the
|
|
770
|
+
* exit reason and crash details, the next time any process of the app exits. Watched apps
|
|
771
|
+
* report no logs.
|
|
772
|
+
*
|
|
773
|
+
* The app does not need to be running yet: if it starts later, it is picked up automatically.
|
|
774
|
+
*
|
|
775
|
+
* @param bundleId Bundle identifier of the app to watch
|
|
776
|
+
* @param onExit Called once when the watched app shuts down
|
|
777
|
+
* @returns A handle whose `stop()` cancels the watch
|
|
778
|
+
*/
|
|
779
|
+
watchApp: (bundleId: string, onExit: LaunchAppExitCallback) => Promise<AppWatch>;
|
|
780
|
+
|
|
725
781
|
/**
|
|
726
782
|
* Fetch the last N lines of app logs (combined stdout/stderr)
|
|
727
783
|
* @param bundleId Bundle identifier of the app
|
|
@@ -1330,6 +1386,17 @@ type SimctlRequest = {
|
|
|
1330
1386
|
/**
|
|
1331
1387
|
* Generic server response with optional error
|
|
1332
1388
|
*/
|
|
1389
|
+
/** appExit as the server sends it; its logs field is an array, unlike appLogTailResult's. */
|
|
1390
|
+
type AppExitMessage = {
|
|
1391
|
+
type: 'appExit';
|
|
1392
|
+
execId: string;
|
|
1393
|
+
bundleId: string;
|
|
1394
|
+
reason?: AppExitReason;
|
|
1395
|
+
crash?: AppCrashInfo;
|
|
1396
|
+
logs?: string[];
|
|
1397
|
+
logLineCount?: number;
|
|
1398
|
+
};
|
|
1399
|
+
|
|
1333
1400
|
type ServerResponse = {
|
|
1334
1401
|
state?: FoldState;
|
|
1335
1402
|
type: string;
|
|
@@ -1818,14 +1885,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1818
1885
|
return false;
|
|
1819
1886
|
}
|
|
1820
1887
|
|
|
1821
|
-
const { execId, bundleId
|
|
1822
|
-
if (
|
|
1823
|
-
typeof execId !== 'string' ||
|
|
1824
|
-
typeof bundleId !== 'string' ||
|
|
1825
|
-
typeof logLineCount !== 'number' ||
|
|
1826
|
-
!Number.isInteger(logLineCount) ||
|
|
1827
|
-
logLineCount < 0
|
|
1828
|
-
) {
|
|
1888
|
+
const { execId, bundleId } = message;
|
|
1889
|
+
if (typeof execId !== 'string' || typeof bundleId !== 'string') {
|
|
1829
1890
|
logger.warn('Received malformed appExit message:', message);
|
|
1830
1891
|
return true;
|
|
1831
1892
|
}
|
|
@@ -1865,6 +1926,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1865
1926
|
toggleKeyboardResult: () => undefined,
|
|
1866
1927
|
launchAppResult: () => undefined,
|
|
1867
1928
|
terminateAppResult: () => undefined,
|
|
1929
|
+
watchAppResult: () => undefined,
|
|
1930
|
+
unwatchAppResult: () => undefined,
|
|
1868
1931
|
appLogTailResult: (msg) => msg.logs ?? '',
|
|
1869
1932
|
listAppsResult: (msg) => JSON.parse(msg.apps || '[]') as InstalledApp[],
|
|
1870
1933
|
listOpenFilesResult: (msg) => msg.files || [],
|
|
@@ -2071,6 +2134,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2071
2134
|
toggleKeyboard,
|
|
2072
2135
|
launchApp,
|
|
2073
2136
|
terminateApp,
|
|
2137
|
+
watchApp,
|
|
2074
2138
|
appLogTail,
|
|
2075
2139
|
streamAppLog,
|
|
2076
2140
|
streamSyslog,
|
|
@@ -2306,42 +2370,21 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2306
2370
|
new Error('launchApp runtime launches require RelaunchIfRunning so runtime injection is applied.'),
|
|
2307
2371
|
);
|
|
2308
2372
|
}
|
|
2309
|
-
const
|
|
2373
|
+
const hasEnvironment = Object.keys(launchOptions.env ?? {}).length > 0;
|
|
2374
|
+
const mode =
|
|
2375
|
+
launchOptions.runtime ? 'RelaunchIfRunning' : (
|
|
2376
|
+
launchOptions.mode ?? (hasEnvironment ? 'RelaunchIfRunning' : undefined)
|
|
2377
|
+
);
|
|
2310
2378
|
const onExit = launchOptions.onExit;
|
|
2311
2379
|
const execId = onExit ? generateId() : undefined;
|
|
2312
2380
|
if (execId && onExit) {
|
|
2313
|
-
registerServerNotification('appExit', execId,
|
|
2314
|
-
const { bundleId, logLineCount } = message;
|
|
2315
|
-
if (
|
|
2316
|
-
typeof bundleId !== 'string' ||
|
|
2317
|
-
typeof logLineCount !== 'number' ||
|
|
2318
|
-
!Number.isInteger(logLineCount) ||
|
|
2319
|
-
logLineCount < 0
|
|
2320
|
-
) {
|
|
2321
|
-
logger.warn(`Received malformed appExit payload for execId ${execId}:`, message);
|
|
2322
|
-
return;
|
|
2323
|
-
}
|
|
2324
|
-
|
|
2325
|
-
let logs: string[] = [];
|
|
2326
|
-
try {
|
|
2327
|
-
logs = splitAppLogTail(
|
|
2328
|
-
await sendRequest<string>('appLogTail', { bundleId, lines: logLineCount }),
|
|
2329
|
-
);
|
|
2330
|
-
} catch (error) {
|
|
2331
|
-
logger.error(`Failed to fetch app logs for exit execId ${execId}:`, error);
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
|
-
try {
|
|
2335
|
-
await onExit(logs);
|
|
2336
|
-
} catch (error) {
|
|
2337
|
-
logger.error(`Error in onExit callback for execId ${execId}:`, error);
|
|
2338
|
-
}
|
|
2339
|
-
});
|
|
2381
|
+
registerServerNotification('appExit', execId, appExitHandler(execId, onExit));
|
|
2340
2382
|
}
|
|
2341
2383
|
return sendRequest<void>('launchApp', {
|
|
2342
2384
|
bundleId,
|
|
2343
2385
|
mode,
|
|
2344
2386
|
runtime: launchOptions.runtime,
|
|
2387
|
+
env: launchOptions.env,
|
|
2345
2388
|
execId,
|
|
2346
2389
|
}).catch((error) => {
|
|
2347
2390
|
if (execId) {
|
|
@@ -2351,6 +2394,57 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2351
2394
|
});
|
|
2352
2395
|
};
|
|
2353
2396
|
|
|
2397
|
+
/** Delivers one appExit to the callback, fetching the logs from servers that predate inline logs. */
|
|
2398
|
+
const appExitHandler =
|
|
2399
|
+
(execId: string, onExit: LaunchAppExitCallback): ServerNotificationHandler =>
|
|
2400
|
+
async (response) => {
|
|
2401
|
+
const message = response as unknown as AppExitMessage;
|
|
2402
|
+
let logs: string[] = [];
|
|
2403
|
+
if (Array.isArray(message.logs)) {
|
|
2404
|
+
logs = message.logs.map(String);
|
|
2405
|
+
} else if (Number.isInteger(message.logLineCount) && (message.logLineCount ?? 0) > 0) {
|
|
2406
|
+
try {
|
|
2407
|
+
logs = splitAppLogTail(
|
|
2408
|
+
await sendRequest<string>('appLogTail', {
|
|
2409
|
+
bundleId: message.bundleId,
|
|
2410
|
+
lines: message.logLineCount,
|
|
2411
|
+
}),
|
|
2412
|
+
);
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
logger.error(`Failed to fetch app logs for exit execId ${execId}:`, error);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
const info: AppExitInfo = {
|
|
2418
|
+
bundleId: message.bundleId,
|
|
2419
|
+
// Servers before exit reasons only reported that the app ended.
|
|
2420
|
+
reason: message.reason ?? 'exit',
|
|
2421
|
+
...(message.crash ? { crash: message.crash } : {}),
|
|
2422
|
+
};
|
|
2423
|
+
try {
|
|
2424
|
+
await onExit(logs, info);
|
|
2425
|
+
} catch (error) {
|
|
2426
|
+
logger.error(`Error in onExit callback for execId ${execId}:`, error);
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2430
|
+
const watchApp = async (bundleId: string, onExit: LaunchAppExitCallback): Promise<AppWatch> => {
|
|
2431
|
+
const execId = generateId();
|
|
2432
|
+
registerServerNotification('appExit', execId, appExitHandler(execId, onExit));
|
|
2433
|
+
try {
|
|
2434
|
+
await sendRequest<void>('watchApp', { bundleId, execId });
|
|
2435
|
+
} catch (error) {
|
|
2436
|
+
deleteServerNotification('appExit', execId);
|
|
2437
|
+
throw error;
|
|
2438
|
+
}
|
|
2439
|
+
return {
|
|
2440
|
+
execId,
|
|
2441
|
+
stop: async () => {
|
|
2442
|
+
deleteServerNotification('appExit', execId);
|
|
2443
|
+
await sendRequest<void>('unwatchApp', { execId });
|
|
2444
|
+
},
|
|
2445
|
+
};
|
|
2446
|
+
};
|
|
2447
|
+
|
|
2354
2448
|
const terminateApp = (bundleId: string): Promise<void> => {
|
|
2355
2449
|
return sendRequest<void>('terminateApp', { bundleId });
|
|
2356
2450
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Parse repeatable KEY=VALUE entries; the last occurrence of a key wins. */
|
|
2
|
+
export function parseLaunchEnvironment(entries: string[]): Record<string, string> {
|
|
3
|
+
const env = Object.fromEntries(
|
|
4
|
+
entries.map((entry) => {
|
|
5
|
+
const separator = entry.indexOf('=');
|
|
6
|
+
if (separator < 1) throw new Error('launch environment must use KEY=VALUE');
|
|
7
|
+
return [entry.slice(0, separator), entry.slice(separator + 1)];
|
|
8
|
+
}),
|
|
9
|
+
);
|
|
10
|
+
return env;
|
|
11
|
+
}
|
|
@@ -88,6 +88,13 @@ export namespace AndroidInstance {
|
|
|
88
88
|
* 10m, 3h. Default is "0" which means no hard timeout.
|
|
89
89
|
*/
|
|
90
90
|
hardTimeout?: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Names a persistent tunnel (`lim tunnel run --name`) that serves this
|
|
94
|
+
* instance. Creation fails while that tunnel is offline, and the instance
|
|
95
|
+
* becomes ready only after the tunnel attaches.
|
|
96
|
+
*/
|
|
97
|
+
tunnel?: string;
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
export interface Status {
|
|
@@ -241,6 +248,13 @@ export namespace AndroidInstanceCreateParams {
|
|
|
241
248
|
region?: string;
|
|
242
249
|
|
|
243
250
|
sandbox?: Spec.Sandbox;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Names a persistent tunnel (`lim tunnel run --name`) that serves this
|
|
254
|
+
* instance. Creation fails while that tunnel is offline, and the instance
|
|
255
|
+
* becomes ready only after the tunnel attaches.
|
|
256
|
+
*/
|
|
257
|
+
tunnel?: string;
|
|
244
258
|
}
|
|
245
259
|
|
|
246
260
|
export namespace Spec {
|
|
@@ -88,6 +88,13 @@ export namespace IosInstance {
|
|
|
88
88
|
* 10m, 3h. Default is "0" which means no hard timeout.
|
|
89
89
|
*/
|
|
90
90
|
hardTimeout?: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Names a persistent tunnel (`lim tunnel run --name`) that serves this
|
|
94
|
+
* instance. Creation fails while that tunnel is offline, and the instance
|
|
95
|
+
* becomes ready only after the tunnel attaches.
|
|
96
|
+
*/
|
|
97
|
+
tunnel?: string;
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
export interface Status {
|
|
@@ -230,6 +237,13 @@ export namespace IosInstanceCreateParams {
|
|
|
230
237
|
* the first.
|
|
231
238
|
*/
|
|
232
239
|
region?: string;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Names a persistent tunnel (`lim tunnel run --name`) that serves this
|
|
243
|
+
* instance. Creation fails while that tunnel is offline, and the instance
|
|
244
|
+
* becomes ready only after the tunnel attaches.
|
|
245
|
+
*/
|
|
246
|
+
tunnel?: string;
|
|
233
247
|
}
|
|
234
248
|
|
|
235
249
|
export namespace Spec {
|