@limrun/api 0.58.0 → 0.59.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/src/ios-client.ts CHANGED
@@ -405,13 +405,53 @@ export type DetoxLaunchRuntime = {
405
405
 
406
406
  export type LaunchAppRuntime = DetoxLaunchRuntime;
407
407
 
408
- export type LaunchAppExitCallback = (logs: string[]) => Promise<void>;
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 LaunchAppExitOptions = {
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
- * Called when the launched app exits. The logs array contains one entry per
413
- * stdout/stderr line produced by that execution, as reported by limulator.
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 LaunchAppExitOptions = {
454
+ /** Called once when the launched app exits, crashes, or is terminated. */
415
455
  onExit?: LaunchAppExitCallback;
416
456
  };
417
457
 
@@ -722,6 +762,20 @@ export type InstanceClient = {
722
762
  */
723
763
  terminateApp: (bundleId: string) => Promise<void>;
724
764
 
765
+ /**
766
+ * Watch an app's exit or crash without launching it through {@link launchApp}, for example
767
+ * an app opened with {@link openUrl} or already running. The callback fires once, with the
768
+ * exit reason and crash details, the next time any process of the app exits. Watched apps
769
+ * report no logs.
770
+ *
771
+ * The app does not need to be running yet: if it starts later, it is picked up automatically.
772
+ *
773
+ * @param bundleId Bundle identifier of the app to watch
774
+ * @param onExit Called once when the watched app shuts down
775
+ * @returns A handle whose `stop()` cancels the watch
776
+ */
777
+ watchApp: (bundleId: string, onExit: LaunchAppExitCallback) => Promise<AppWatch>;
778
+
725
779
  /**
726
780
  * Fetch the last N lines of app logs (combined stdout/stderr)
727
781
  * @param bundleId Bundle identifier of the app
@@ -1330,6 +1384,17 @@ type SimctlRequest = {
1330
1384
  /**
1331
1385
  * Generic server response with optional error
1332
1386
  */
1387
+ /** appExit as the server sends it; its logs field is an array, unlike appLogTailResult's. */
1388
+ type AppExitMessage = {
1389
+ type: 'appExit';
1390
+ execId: string;
1391
+ bundleId: string;
1392
+ reason?: AppExitReason;
1393
+ crash?: AppCrashInfo;
1394
+ logs?: string[];
1395
+ logLineCount?: number;
1396
+ };
1397
+
1333
1398
  type ServerResponse = {
1334
1399
  state?: FoldState;
1335
1400
  type: string;
@@ -1818,14 +1883,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
1818
1883
  return false;
1819
1884
  }
1820
1885
 
1821
- const { execId, bundleId, logLineCount } = message;
1822
- if (
1823
- typeof execId !== 'string' ||
1824
- typeof bundleId !== 'string' ||
1825
- typeof logLineCount !== 'number' ||
1826
- !Number.isInteger(logLineCount) ||
1827
- logLineCount < 0
1828
- ) {
1886
+ const { execId, bundleId } = message;
1887
+ if (typeof execId !== 'string' || typeof bundleId !== 'string') {
1829
1888
  logger.warn('Received malformed appExit message:', message);
1830
1889
  return true;
1831
1890
  }
@@ -1865,6 +1924,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
1865
1924
  toggleKeyboardResult: () => undefined,
1866
1925
  launchAppResult: () => undefined,
1867
1926
  terminateAppResult: () => undefined,
1927
+ watchAppResult: () => undefined,
1928
+ unwatchAppResult: () => undefined,
1868
1929
  appLogTailResult: (msg) => msg.logs ?? '',
1869
1930
  listAppsResult: (msg) => JSON.parse(msg.apps || '[]') as InstalledApp[],
1870
1931
  listOpenFilesResult: (msg) => msg.files || [],
@@ -2071,6 +2132,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
2071
2132
  toggleKeyboard,
2072
2133
  launchApp,
2073
2134
  terminateApp,
2135
+ watchApp,
2074
2136
  appLogTail,
2075
2137
  streamAppLog,
2076
2138
  streamSyslog,
@@ -2310,33 +2372,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
2310
2372
  const onExit = launchOptions.onExit;
2311
2373
  const execId = onExit ? generateId() : undefined;
2312
2374
  if (execId && onExit) {
2313
- registerServerNotification('appExit', execId, async (message) => {
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
- });
2375
+ registerServerNotification('appExit', execId, appExitHandler(execId, onExit));
2340
2376
  }
2341
2377
  return sendRequest<void>('launchApp', {
2342
2378
  bundleId,
@@ -2351,6 +2387,57 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
2351
2387
  });
2352
2388
  };
2353
2389
 
2390
+ /** Delivers one appExit to the callback, fetching the logs from servers that predate inline logs. */
2391
+ const appExitHandler =
2392
+ (execId: string, onExit: LaunchAppExitCallback): ServerNotificationHandler =>
2393
+ async (response) => {
2394
+ const message = response as unknown as AppExitMessage;
2395
+ let logs: string[] = [];
2396
+ if (Array.isArray(message.logs)) {
2397
+ logs = message.logs.map(String);
2398
+ } else if (Number.isInteger(message.logLineCount) && (message.logLineCount ?? 0) > 0) {
2399
+ try {
2400
+ logs = splitAppLogTail(
2401
+ await sendRequest<string>('appLogTail', {
2402
+ bundleId: message.bundleId,
2403
+ lines: message.logLineCount,
2404
+ }),
2405
+ );
2406
+ } catch (error) {
2407
+ logger.error(`Failed to fetch app logs for exit execId ${execId}:`, error);
2408
+ }
2409
+ }
2410
+ const info: AppExitInfo = {
2411
+ bundleId: message.bundleId,
2412
+ // Servers before exit reasons only reported that the app ended.
2413
+ reason: message.reason ?? 'exit',
2414
+ ...(message.crash ? { crash: message.crash } : {}),
2415
+ };
2416
+ try {
2417
+ await onExit(logs, info);
2418
+ } catch (error) {
2419
+ logger.error(`Error in onExit callback for execId ${execId}:`, error);
2420
+ }
2421
+ };
2422
+
2423
+ const watchApp = async (bundleId: string, onExit: LaunchAppExitCallback): Promise<AppWatch> => {
2424
+ const execId = generateId();
2425
+ registerServerNotification('appExit', execId, appExitHandler(execId, onExit));
2426
+ try {
2427
+ await sendRequest<void>('watchApp', { bundleId, execId });
2428
+ } catch (error) {
2429
+ deleteServerNotification('appExit', execId);
2430
+ throw error;
2431
+ }
2432
+ return {
2433
+ execId,
2434
+ stop: async () => {
2435
+ deleteServerNotification('appExit', execId);
2436
+ await sendRequest<void>('unwatchApp', { execId });
2437
+ },
2438
+ };
2439
+ };
2440
+
2354
2441
  const terminateApp = (bundleId: string): Promise<void> => {
2355
2442
  return sendRequest<void>('terminateApp', { bundleId });
2356
2443
  };
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.58.0'; // x-release-please-version
1
+ export const VERSION = '0.59.0'; // x-release-please-version
package/version.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.58.0";
1
+ export declare const VERSION = "0.59.0";
2
2
  //# sourceMappingURL=version.d.mts.map
package/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.58.0";
1
+ export declare const VERSION = "0.59.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.58.0'; // x-release-please-version
4
+ exports.VERSION = '0.59.0'; // x-release-please-version
5
5
  //# sourceMappingURL=version.js.map
package/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.58.0'; // x-release-please-version
1
+ export const VERSION = '0.59.0'; // x-release-please-version
2
2
  //# sourceMappingURL=version.mjs.map