@appium/coresim 1.11.1 → 1.13.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +6 -1
  3. package/lib/src/commands/jpeg-stream.d.ts +2 -0
  4. package/lib/src/commands/jpeg-stream.d.ts.map +1 -1
  5. package/lib/src/commands/jpeg-stream.js +6 -2
  6. package/lib/src/commands/jpeg-stream.js.map +1 -1
  7. package/lib/src/commands/permissions.d.ts +15 -7
  8. package/lib/src/commands/permissions.d.ts.map +1 -1
  9. package/lib/src/commands/permissions.js +299 -5
  10. package/lib/src/commands/permissions.js.map +1 -1
  11. package/lib/src/commands/screenshot.d.ts +3 -3
  12. package/lib/src/commands/screenshot.d.ts.map +1 -1
  13. package/lib/src/commands/screenshot.js +10 -5
  14. package/lib/src/commands/screenshot.js.map +1 -1
  15. package/lib/src/commands/video-recording.d.ts +2 -0
  16. package/lib/src/commands/video-recording.d.ts.map +1 -1
  17. package/lib/src/commands/video-recording.js +13 -6
  18. package/lib/src/commands/video-recording.js.map +1 -1
  19. package/lib/src/commands/video-stream.d.ts +2 -0
  20. package/lib/src/commands/video-stream.d.ts.map +1 -1
  21. package/lib/src/commands/video-stream.js +6 -2
  22. package/lib/src/commands/video-stream.js.map +1 -1
  23. package/lib/src/index.d.ts +1 -1
  24. package/lib/src/index.d.ts.map +1 -1
  25. package/lib/src/index.js.map +1 -1
  26. package/lib/src/types.d.ts +25 -31
  27. package/lib/src/types.d.ts.map +1 -1
  28. package/lib/src/utils/display.d.ts +24 -0
  29. package/lib/src/utils/display.d.ts.map +1 -0
  30. package/lib/src/utils/display.js +55 -0
  31. package/lib/src/utils/display.js.map +1 -0
  32. package/lib/src/utils/index.d.ts +1 -0
  33. package/lib/src/utils/index.d.ts.map +1 -1
  34. package/lib/src/utils/index.js +1 -0
  35. package/lib/src/utils/index.js.map +1 -1
  36. package/package.json +3 -4
  37. package/prebuilds/darwin-arm64/@appium+coresim.node +0 -0
  38. package/src/commands/jpeg-stream.ts +6 -2
  39. package/src/commands/permissions.ts +324 -10
  40. package/src/commands/screenshot.ts +10 -5
  41. package/src/commands/video-recording.ts +10 -5
  42. package/src/commands/video-stream.ts +6 -2
  43. package/src/coresim.mm +7 -0
  44. package/src/index.ts +2 -0
  45. package/src/native/sim_screenshot.h +12 -7
  46. package/src/native/sim_screenshot.mm +18 -2
  47. package/src/types.ts +27 -30
  48. package/src/utils/display.ts +67 -0
  49. package/src/utils/index.ts +1 -0
package/src/types.ts CHANGED
@@ -87,9 +87,10 @@ export type SimPermissionStatus = 'unset' | 'denied' | 'granted' | 'limited';
87
87
 
88
88
  /**
89
89
  * A privacy permission grantable via `NativeSimctl.grantPermission`/`revokePermission`/
90
- * `resetPermission`. Each is backed by a row in the simulator's own TCC (privacy) database —
91
- * `location` isn't included since CoreLocation simulation has its own subsystem, not a plain TCC
92
- * row (see CLAUDE.md).
90
+ * `resetPermission`. Each except `notifications` is backed by a row in the simulator's own TCC
91
+ * (privacy) database — `location` isn't included since CoreLocation simulation has its own
92
+ * subsystem, not a plain TCC row (see CLAUDE.md). `notifications` is SpringBoard's own setting:
93
+ * changing it restarts SpringBoard, and its status can't be read back via `getPermission`.
93
94
  */
94
95
  export type SimPermissionService =
95
96
  | 'calendar'
@@ -101,6 +102,7 @@ export type SimPermissionService =
101
102
  | 'medialibrary'
102
103
  | 'microphone'
103
104
  | 'motion'
105
+ | 'notifications'
104
106
  | 'photos'
105
107
  | 'reminders'
106
108
  | 'siri'
@@ -111,23 +113,32 @@ export type SimPermissionService =
111
113
  * A device's renderable display, as returned by `NativeSimctl.getDisplays()`.
112
114
  */
113
115
  export interface SimDisplayInfo {
114
- /** Port UUID identifying this display — pass as `getScreenshot`'s `displayId` option to target it. */
116
+ /** Port UUID identifying this display — pass as a capture API’s `displayId` option to target it. */
115
117
  id: string;
116
- /** Raw `SimDisplayDescriptorState.displayClass` — 0 is always the primary display. */
118
+ /** Guest screen ID (`SimScreenProperties.screenID`), when available. Matches XCTest displayID. */
119
+ screenId?: number;
120
+ /** Raw `SimDisplayDescriptorState.displayClass` — 0 includes both built-in displays on Duo. */
117
121
  displayClass: number;
118
- /** Whether this is the primary display (`displayClass === 0`). */
122
+ /** Whether `displayClass === 0`. Multiple integrated displays may have this value. */
119
123
  isMain: boolean;
120
124
  }
121
125
 
126
+ /** Port UUID string or unsigned 32-bit guest screen ID (XCTest/WDA displayID). */
127
+ export type DisplayId = string | number;
128
+
129
+ /** Display selection shared by screenshot, recording, and streaming APIs. */
130
+ export interface DisplayOptions {
131
+ /**
132
+ * Port UUID from `getDisplays()`, or an unsigned 32-bit guest screen ID (XCTest/WDA displayID).
133
+ * See [display selection helpers](./utils/display.ts) for validation and resolution semantics.
134
+ */
135
+ displayId?: DisplayId;
136
+ }
137
+
122
138
  /** Options for `NativeSimctl.getScreenshot`. */
123
- export interface ScreenshotOptions {
139
+ export interface ScreenshotOptions extends DisplayOptions {
124
140
  /** Image encoding — defaults to `'png'`. */
125
141
  format?: 'png' | 'jpeg';
126
- /**
127
- * Which display to capture, by `id` from `getDisplays()`. Defaults to the primary display
128
- * (falling back to the first renderable display if none is primary, e.g. tvOS).
129
- */
130
- displayId?: string;
131
142
  /**
132
143
  * JPEG quality as a percentage (0 = smallest/most compressed, 100 = largest/least compressed).
133
144
  * Only meaningful with `format: 'jpeg'` — ignored for `'png'`, which is always lossless.
@@ -137,12 +148,7 @@ export interface ScreenshotOptions {
137
148
  }
138
149
 
139
150
  /** Options for `NativeSimctl.startVideoRecording`. */
140
- export interface VideoRecordingOptions {
141
- /**
142
- * Which display to record, by `id` from `getDisplays()`. Defaults to the primary display
143
- * (falling back to the first renderable display if none is primary, e.g. tvOS).
144
- */
145
- displayId?: string;
151
+ export interface VideoRecordingOptions extends DisplayOptions {
146
152
  /** Video codec — `'h264'` (default) or `'hevc'`. */
147
153
  codec?: 'h264' | 'hevc';
148
154
  /**
@@ -189,12 +195,7 @@ export interface StopVideoRecordingOptions {
189
195
  }
190
196
 
191
197
  /** Options for `NativeSimctl.startVideoStream`. */
192
- export interface VideoStreamOptions {
193
- /**
194
- * Which display to stream, by `id` from `getDisplays()`. Defaults to the primary display
195
- * (falling back to the first renderable display if none is primary, e.g. tvOS).
196
- */
197
- displayId?: string;
198
+ export interface VideoStreamOptions extends DisplayOptions {
198
199
  /** Video codec — `'h264'` (default) or `'hevc'`. */
199
200
  codec?: 'h264' | 'hevc';
200
201
  /**
@@ -213,12 +214,7 @@ export interface VideoStreamOptions {
213
214
  }
214
215
 
215
216
  /** Options for `NativeSimctl.startJpegStream`. */
216
- export interface JpegStreamOptions {
217
- /**
218
- * Which display to stream, by `id` from `getDisplays()`. Defaults to the primary display
219
- * (falling back to the first renderable display if none is primary, e.g. tvOS).
220
- */
221
- displayId?: string;
217
+ export interface JpegStreamOptions extends DisplayOptions {
222
218
  /**
223
219
  * Max frames/sec to poll the framebuffer at — an unchanged frame is never re-encoded, so this
224
220
  * is an upper bound, not a guarantee. Must be >= 1. Defaults to 60.
@@ -430,6 +426,7 @@ export interface NativeDeviceHandle {
430
426
  deviceTypeIdentifier(): string;
431
427
  runtimeIdentifier(): string;
432
428
  runtimeRootPath(): string;
429
+ dataPath(): string;
433
430
  boot(options?: Record<string, unknown>): Promise<void>;
434
431
  getBootStatus(): Promise<SimBootInfo | null>;
435
432
  shutdown(): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import type {DisplayId, NativeDeviceHandle, SimDisplayInfo} from '../types.js';
2
+
3
+ /**
4
+ * Validates a capture API's display selection without querying the device.
5
+ * Strings pass through without UUID syntax or port-existence checks.
6
+ *
7
+ * @param displayId — port UUID string, unsigned 32-bit guest screen ID, or `undefined` for default selection
8
+ * @throws {TypeError} if the value is neither a string, a number, nor `undefined`
9
+ * @throws {RangeError} if a numeric value is not an unsigned 32-bit integer
10
+ */
11
+ export function validateDisplayId(displayId: unknown): void {
12
+ if (displayId !== undefined && typeof displayId !== 'string' && typeof displayId !== 'number') {
13
+ throw new TypeError('displayId must be a port UUID string or a numeric guest screen ID');
14
+ }
15
+ if (typeof displayId === 'number' && (!Number.isInteger(displayId) || displayId < 0 || displayId > 0xffffffff)) {
16
+ throw new RangeError('A numeric displayId must be an unsigned 32-bit integer');
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Resolves a validated display selection to the port UUID expected by native capture APIs.
22
+ * UUID strings pass through without enumeration; numeric IDs are matched against a fresh list
23
+ * of guest screen IDs (XCTest/WDA displayID) on each call. An omitted ID selects the unique `isMain` (class-0) port.
24
+ * If no class-0 port exists, native capture falls back to the first renderable display.
25
+ * Call once at recording/stream startup, or on each screenshot capture.
26
+ *
27
+ * @param device — native handle for the device whose display ports are queried
28
+ * @param displayId — selection already checked by {@link validateDisplayId}
29
+ * @returns the selected port UUID, or `undefined` to retain native fallback when no default port exists
30
+ * @throws {Error} if a numeric ID has no unique match with a nonempty UUID, or multiple default ports exist
31
+ */
32
+ export async function resolveDisplayPortId(
33
+ device: NativeDeviceHandle,
34
+ displayId: DisplayId | undefined,
35
+ ): Promise<string | undefined> {
36
+ if (typeof displayId === 'string') {
37
+ return displayId;
38
+ }
39
+
40
+ const displays = await device.getDisplays();
41
+ if (typeof displayId === 'number') {
42
+ const matches = displays.filter(({screenId}) => screenId === displayId);
43
+ if (matches.length !== 1 || !matches[0].id) {
44
+ throw new Error(
45
+ `Cannot uniquely resolve guest screen ID ${displayId} to a display port. ${availableDisplayIdsHint(displays)}`,
46
+ );
47
+ }
48
+ return matches[0].id;
49
+ }
50
+
51
+ // Both integrated Duo displays have class 0. Never pick one by port order.
52
+ const defaults = displays.filter(({isMain}) => isMain);
53
+ if (defaults.length > 1) {
54
+ throw new Error(
55
+ `Multiple default display ports are available; specify displayId. ${availableDisplayIdsHint(displays)}`,
56
+ );
57
+ }
58
+ return defaults[0]?.id;
59
+ }
60
+
61
+ /** Formats the already-fetched screen ID/port UUID pairs for display-selection errors. */
62
+ function availableDisplayIdsHint(displays: SimDisplayInfo[]): string {
63
+ if (displays.length === 0) {
64
+ return 'Available display IDs: none';
65
+ }
66
+ return `Available display IDs: ${JSON.stringify(displays.map(({id, screenId}) => ({screenId, id})))}`;
67
+ }
@@ -1,2 +1,3 @@
1
1
  export {getPkgRoot, PACKAGE_NAME} from './pkg-root.js';
2
2
  export {runCatchingAsync, toTypedError} from './run-catching.js';
3
+ export {resolveDisplayPortId, validateDisplayId} from './display.js';