@appium/coresim 1.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.
Files changed (138) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +66 -0
  3. package/binding.gyp +53 -0
  4. package/lib/src/commands/app.d.ts +88 -0
  5. package/lib/src/commands/app.d.ts.map +1 -0
  6. package/lib/src/commands/app.js +133 -0
  7. package/lib/src/commands/app.js.map +1 -0
  8. package/lib/src/commands/biometric.d.ts +33 -0
  9. package/lib/src/commands/biometric.d.ts.map +1 -0
  10. package/lib/src/commands/biometric.js +44 -0
  11. package/lib/src/commands/biometric.js.map +1 -0
  12. package/lib/src/commands/darwin-notification.d.ts +36 -0
  13. package/lib/src/commands/darwin-notification.d.ts.map +1 -0
  14. package/lib/src/commands/darwin-notification.js +35 -0
  15. package/lib/src/commands/darwin-notification.js.map +1 -0
  16. package/lib/src/commands/interaction.d.ts +48 -0
  17. package/lib/src/commands/interaction.d.ts.map +1 -0
  18. package/lib/src/commands/interaction.js +48 -0
  19. package/lib/src/commands/interaction.js.map +1 -0
  20. package/lib/src/commands/keychain.d.ts +25 -0
  21. package/lib/src/commands/keychain.d.ts.map +1 -0
  22. package/lib/src/commands/keychain.js +52 -0
  23. package/lib/src/commands/keychain.js.map +1 -0
  24. package/lib/src/commands/lifecycle.d.ts +101 -0
  25. package/lib/src/commands/lifecycle.d.ts.map +1 -0
  26. package/lib/src/commands/lifecycle.js +165 -0
  27. package/lib/src/commands/lifecycle.js.map +1 -0
  28. package/lib/src/commands/media.d.ts +28 -0
  29. package/lib/src/commands/media.d.ts.map +1 -0
  30. package/lib/src/commands/media.js +27 -0
  31. package/lib/src/commands/media.js.map +1 -0
  32. package/lib/src/commands/misc.d.ts +15 -0
  33. package/lib/src/commands/misc.d.ts.map +1 -0
  34. package/lib/src/commands/misc.js +12 -0
  35. package/lib/src/commands/misc.js.map +1 -0
  36. package/lib/src/commands/pasteboard.d.ts +24 -0
  37. package/lib/src/commands/pasteboard.d.ts.map +1 -0
  38. package/lib/src/commands/pasteboard.js +22 -0
  39. package/lib/src/commands/pasteboard.js.map +1 -0
  40. package/lib/src/commands/permissions.d.ts +48 -0
  41. package/lib/src/commands/permissions.d.ts.map +1 -0
  42. package/lib/src/commands/permissions.js +84 -0
  43. package/lib/src/commands/permissions.js.map +1 -0
  44. package/lib/src/commands/process.d.ts +16 -0
  45. package/lib/src/commands/process.d.ts.map +1 -0
  46. package/lib/src/commands/process.js +68 -0
  47. package/lib/src/commands/process.js.map +1 -0
  48. package/lib/src/commands/screenshot.d.ts +29 -0
  49. package/lib/src/commands/screenshot.d.ts.map +1 -0
  50. package/lib/src/commands/screenshot.js +30 -0
  51. package/lib/src/commands/screenshot.js.map +1 -0
  52. package/lib/src/commands/spawn.d.ts +48 -0
  53. package/lib/src/commands/spawn.d.ts.map +1 -0
  54. package/lib/src/commands/spawn.js +108 -0
  55. package/lib/src/commands/spawn.js.map +1 -0
  56. package/lib/src/commands/ui.d.ts +42 -0
  57. package/lib/src/commands/ui.d.ts.map +1 -0
  58. package/lib/src/commands/ui.js +44 -0
  59. package/lib/src/commands/ui.js.map +1 -0
  60. package/lib/src/commands/webinspector.d.ts +19 -0
  61. package/lib/src/commands/webinspector.d.ts.map +1 -0
  62. package/lib/src/commands/webinspector.js +16 -0
  63. package/lib/src/commands/webinspector.js.map +1 -0
  64. package/lib/src/errors.d.ts +51 -0
  65. package/lib/src/errors.d.ts.map +1 -0
  66. package/lib/src/errors.js +82 -0
  67. package/lib/src/errors.js.map +1 -0
  68. package/lib/src/index.d.ts +7 -0
  69. package/lib/src/index.d.ts.map +1 -0
  70. package/lib/src/index.js +5 -0
  71. package/lib/src/index.js.map +1 -0
  72. package/lib/src/native-simctl.d.ts +62 -0
  73. package/lib/src/native-simctl.d.ts.map +1 -0
  74. package/lib/src/native-simctl.js +221 -0
  75. package/lib/src/native-simctl.js.map +1 -0
  76. package/lib/src/types.d.ts +267 -0
  77. package/lib/src/types.d.ts.map +1 -0
  78. package/lib/src/types.js +29 -0
  79. package/lib/src/types.js.map +1 -0
  80. package/lib/src/utils/index.d.ts +3 -0
  81. package/lib/src/utils/index.d.ts.map +1 -0
  82. package/lib/src/utils/index.js +3 -0
  83. package/lib/src/utils/index.js.map +1 -0
  84. package/lib/src/utils/pkg-root.d.ts +6 -0
  85. package/lib/src/utils/pkg-root.d.ts.map +1 -0
  86. package/lib/src/utils/pkg-root.js +12 -0
  87. package/lib/src/utils/pkg-root.js.map +1 -0
  88. package/lib/src/utils/run-catching.d.ts +3 -0
  89. package/lib/src/utils/run-catching.d.ts.map +1 -0
  90. package/lib/src/utils/run-catching.js +11 -0
  91. package/lib/src/utils/run-catching.js.map +1 -0
  92. package/package.json +73 -0
  93. package/scripts/install.mjs +22 -0
  94. package/src/commands/app.ts +186 -0
  95. package/src/commands/biometric.ts +69 -0
  96. package/src/commands/darwin-notification.ts +51 -0
  97. package/src/commands/interaction.ts +74 -0
  98. package/src/commands/keychain.ts +63 -0
  99. package/src/commands/lifecycle.ts +210 -0
  100. package/src/commands/media.ts +38 -0
  101. package/src/commands/misc.ts +20 -0
  102. package/src/commands/pasteboard.ts +31 -0
  103. package/src/commands/permissions.ts +122 -0
  104. package/src/commands/process.ts +80 -0
  105. package/src/commands/screenshot.ts +46 -0
  106. package/src/commands/spawn.ts +133 -0
  107. package/src/commands/ui.ts +61 -0
  108. package/src/commands/webinspector.ts +23 -0
  109. package/src/coresim.mm +1066 -0
  110. package/src/errors.ts +93 -0
  111. package/src/index.ts +23 -0
  112. package/src/native/async_bridge.h +164 -0
  113. package/src/native/nserror_bridge.h +64 -0
  114. package/src/native/nserror_bridge.mm +30 -0
  115. package/src/native/objc_runtime.h +45 -0
  116. package/src/native/objc_runtime.mm +76 -0
  117. package/src/native/safe_dispatch.h +38 -0
  118. package/src/native/sim_device.h +143 -0
  119. package/src/native/sim_device.mm +355 -0
  120. package/src/native/sim_device_set.h +16 -0
  121. package/src/native/sim_device_set.mm +40 -0
  122. package/src/native/sim_pasteboard.h +16 -0
  123. package/src/native/sim_pasteboard.mm +295 -0
  124. package/src/native/sim_process.h +13 -0
  125. package/src/native/sim_process.mm +139 -0
  126. package/src/native/sim_screenshot.h +28 -0
  127. package/src/native/sim_screenshot.mm +226 -0
  128. package/src/native/sim_service_context.h +42 -0
  129. package/src/native/sim_service_context.mm +92 -0
  130. package/src/native/tcc_privacy.h +35 -0
  131. package/src/native/tcc_privacy.mm +256 -0
  132. package/src/native/value_bridge.h +18 -0
  133. package/src/native/value_bridge.mm +111 -0
  134. package/src/native-simctl.ts +288 -0
  135. package/src/types.ts +302 -0
  136. package/src/utils/index.ts +2 -0
  137. package/src/utils/pkg-root.ts +14 -0
  138. package/src/utils/run-catching.ts +10 -0
@@ -0,0 +1,111 @@
1
+ #include "value_bridge.h"
2
+
3
+ #include <string>
4
+
5
+ namespace coresim {
6
+
7
+ namespace {
8
+
9
+ // A plain `.c_str()`/`UTF8String` round trip truncates at the first embedded NUL byte, since both
10
+ // treat the buffer as a null-terminated C string — but a JS string (and an NSString) can validly
11
+ // contain U+0000 mid-string. Converting via the actual byte length on both sides preserves it.
12
+ NSString* ToNSString(const std::string& utf8) {
13
+ return [[NSString alloc] initWithBytes:utf8.data() length:utf8.size() encoding:NSUTF8StringEncoding];
14
+ }
15
+
16
+ std::string ToStdString(NSString* str) {
17
+ return std::string(str.UTF8String, [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
18
+ }
19
+
20
+ } // namespace
21
+
22
+ NSObject* JsValueToNSObject(Napi::Env env, Napi::Value value) {
23
+ if (value.IsNull() || value.IsUndefined()) {
24
+ return [NSNull null];
25
+ }
26
+ if (value.IsBoolean()) {
27
+ return @(value.As<Napi::Boolean>().Value());
28
+ }
29
+ if (value.IsNumber()) {
30
+ return @(value.As<Napi::Number>().DoubleValue());
31
+ }
32
+ if (value.IsString()) {
33
+ return ToNSString(value.As<Napi::String>().Utf8Value());
34
+ }
35
+ if (value.IsBuffer()) {
36
+ auto buffer = value.As<Napi::Buffer<uint8_t>>();
37
+ return [NSData dataWithBytes:buffer.Data() length:buffer.Length()];
38
+ }
39
+ if (value.IsArray()) {
40
+ auto array = value.As<Napi::Array>();
41
+ NSMutableArray* result = [NSMutableArray arrayWithCapacity:array.Length()];
42
+ for (uint32_t i = 0; i < array.Length(); i++) {
43
+ [result addObject:JsValueToNSObject(env, array.Get(i))];
44
+ }
45
+ return result;
46
+ }
47
+ if (value.IsObject()) {
48
+ auto object = value.As<Napi::Object>();
49
+ Napi::Array keys = object.GetPropertyNames();
50
+ NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:keys.Length()];
51
+ for (uint32_t i = 0; i < keys.Length(); i++) {
52
+ Napi::Value key = keys.Get(i);
53
+ NSString* nsKey = ToNSString(key.As<Napi::String>().Utf8Value());
54
+ result[nsKey] = JsValueToNSObject(env, object.Get(key));
55
+ }
56
+ return result;
57
+ }
58
+ return [NSNull null];
59
+ }
60
+
61
+ Napi::Value NSObjectToJsValue(Napi::Env env, id object) {
62
+ if (object == nil || object == [NSNull null]) {
63
+ return env.Null();
64
+ }
65
+ if ([object isKindOfClass:[NSString class]]) {
66
+ return Napi::String::New(env, ToStdString((NSString*)object));
67
+ }
68
+ if ([object isKindOfClass:[NSNumber class]]) {
69
+ NSNumber* number = (NSNumber*)object;
70
+ if (strcmp(number.objCType, @encode(BOOL)) == 0 || strcmp(number.objCType, @encode(char)) == 0) {
71
+ return Napi::Boolean::New(env, number.boolValue);
72
+ }
73
+ return Napi::Number::New(env, number.doubleValue);
74
+ }
75
+ if ([object isKindOfClass:[NSArray class]]) {
76
+ NSArray* array = (NSArray*)object;
77
+ Napi::Array result = Napi::Array::New(env, array.count);
78
+ for (NSUInteger i = 0; i < array.count; i++) {
79
+ result[static_cast<uint32_t>(i)] = NSObjectToJsValue(env, array[i]);
80
+ }
81
+ return result;
82
+ }
83
+ if ([object isKindOfClass:[NSDictionary class]]) {
84
+ NSDictionary* dict = (NSDictionary*)object;
85
+ Napi::Object result = Napi::Object::New(env);
86
+ for (NSString* key in dict) {
87
+ // Napi::Object::Set's named-property overloads forward to napi_set_named_property, which
88
+ // (per the underlying N-API C function's own signature) only ever accepts a null-terminated
89
+ // C string for the key — there's no byte-length variant, so an embedded-NUL key can't go
90
+ // through that path no matter what we do here. Building the key as a proper Napi::String
91
+ // first and setting it via the napi_value-keyed overload (napi_set_property) sidesteps that
92
+ // limitation entirely.
93
+ Napi::String jsKey = Napi::String::New(env, ToStdString(key));
94
+ result.Set(jsKey, NSObjectToJsValue(env, dict[key]));
95
+ }
96
+ return result;
97
+ }
98
+ if ([object isKindOfClass:[NSURL class]]) {
99
+ return Napi::String::New(env, ToStdString([(NSURL*)object absoluteString]));
100
+ }
101
+ if ([object isKindOfClass:[NSUUID class]]) {
102
+ return Napi::String::New(env, ToStdString([(NSUUID*)object UUIDString]));
103
+ }
104
+ if ([object isKindOfClass:[NSData class]]) {
105
+ NSData* data = (NSData*)object;
106
+ return Napi::Buffer<uint8_t>::Copy(env, static_cast<const uint8_t*>(data.bytes), data.length);
107
+ }
108
+ return Napi::String::New(env, ToStdString([object description]));
109
+ }
110
+
111
+ } // namespace coresim
@@ -0,0 +1,288 @@
1
+ import {execFileSync} from 'node:child_process';
2
+ import {createRequire} from 'node:module';
3
+
4
+ import {util} from '@appium/support';
5
+
6
+ import {
7
+ appInfo,
8
+ getAppContainer,
9
+ installApp,
10
+ installedApps,
11
+ isAppInstalled,
12
+ launchApp,
13
+ removeApp,
14
+ terminateApp,
15
+ } from './commands/app.js';
16
+ import {enrollBiometric, isBiometricEnrolled, sendBiometricMatch} from './commands/biometric.js';
17
+ import {
18
+ getDarwinNotificationState,
19
+ postDarwinNotification,
20
+ setDarwinNotificationState,
21
+ } from './commands/darwin-notification.js';
22
+ import {clearLocation, getEnv, openUrl, pushNotification, setLocation} from './commands/interaction.js';
23
+ import {addCertificate, addRootCertificate, resetKeychain} from './commands/keychain.js';
24
+ import {
25
+ bootDevice,
26
+ createDevice,
27
+ deleteDevice,
28
+ eraseDevice,
29
+ getBootStatus,
30
+ getDevices,
31
+ getSupportedDeviceTypes,
32
+ getSupportedRuntimes,
33
+ shutdownAllDevices,
34
+ shutdownDevice,
35
+ waitForBoot,
36
+ } from './commands/lifecycle.js';
37
+ import {addMedia, addPhoto, addVideo} from './commands/media.js';
38
+ import {shake} from './commands/misc.js';
39
+ import {getPasteboard, setPasteboard} from './commands/pasteboard.js';
40
+ import {getPermission, grantPermission, resetPermission, revokePermission} from './commands/permissions.js';
41
+ import {listProcesses} from './commands/process.js';
42
+ import {getDisplays, getScreenshot} from './commands/screenshot.js';
43
+ import {spawnProcess} from './commands/spawn.js';
44
+ import {
45
+ getAppearance,
46
+ getContentSize,
47
+ getIncreaseContrast,
48
+ setAppearance,
49
+ setContentSize,
50
+ setIncreaseContrast,
51
+ } from './commands/ui.js';
52
+ import {getWebInspectorSocket} from './commands/webinspector.js';
53
+ // Bare re-imports so `declare module './native-simctl.js'` augmentations in the command modules
54
+ // (which add their methods to NativeSimctl's type) reach downstream consumers' emitted .d.ts
55
+ // import graph — the named imports above alone aren't part of NativeSimctl's emitted type
56
+ // surface, so `tsc` would otherwise drop them from the declaration output.
57
+ import './commands/app.js';
58
+ import './commands/biometric.js';
59
+ import './commands/darwin-notification.js';
60
+ import './commands/interaction.js';
61
+ import './commands/keychain.js';
62
+ import './commands/lifecycle.js';
63
+ import './commands/media.js';
64
+ import './commands/misc.js';
65
+ import './commands/pasteboard.js';
66
+ import './commands/permissions.js';
67
+ import './commands/process.js';
68
+ import './commands/screenshot.js';
69
+ import './commands/spawn.js';
70
+ import './commands/ui.js';
71
+ import './commands/webinspector.js';
72
+ import {NativeSimUnavailableError} from './errors.js';
73
+ import type {
74
+ NativeCoreSimModule,
75
+ NativeDeviceHandle,
76
+ NativeDeviceSetHandle,
77
+ NativeServiceContextHandle,
78
+ } from './types.js';
79
+ import {getPkgRoot, PACKAGE_NAME, runCatchingAsync} from './utils/index.js';
80
+
81
+ const require = createRequire(import.meta.url);
82
+
83
+ /**
84
+ * Drives `CoreSimulator.framework` directly — no `simctl`/`xcrun` subprocess — for the operations
85
+ * `node-simctl`'s `Simctl` class exposes over the CLI. Devices are addressed by UDID; each call
86
+ * re-resolves the native handle from the live device set, so a device deleted by another process
87
+ * surfaces as a normal "not found" error rather than stale state.
88
+ *
89
+ * Every method that can trigger a CoreSimulator dispatch is `async`: the native layer runs the
90
+ * underlying call on a libuv threadpool thread (see coresim.mm/async_bridge.h), so a slow
91
+ * install/erase/create/etc. never blocks Node's event loop.
92
+ *
93
+ * The public command surface (everything except the constructor, `frameworkVersion`, and the
94
+ * `_`-prefixed internal helpers below) lives in `src/commands/*.ts`, grouped by topic and mixed
95
+ * into this class via `Object.assign(NativeSimctl.prototype, ...)` at the bottom of this file —
96
+ * mirroring how `@appium/base-driver`'s `BaseDriver` composes its own command surface from
97
+ * `basedriver/commands/*.ts`. Each command module also declares a `declare module
98
+ * './native-simctl.js'` augmentation so `NativeSimctl`'s emitted type includes methods it doesn't
99
+ * textually define.
100
+ */
101
+ export class NativeSimctl {
102
+ private cachedServiceContext: Promise<NativeServiceContextHandle> | undefined;
103
+
104
+ /**
105
+ * @param developerDir — defaults to `xcode-select -p` (the active Xcode's Developer dir).
106
+ * Constructing a NativeSimctl must never throw — the platform check and the native
107
+ * sharedServiceContext call both happen lazily in the `_serviceContext` getter below, so only a
108
+ * method that actually needs the simulator (getDevices(), createDevice(), ...) can raise.
109
+ */
110
+ constructor(private readonly developerDir?: string) {}
111
+
112
+ /** @returns `CFBundleVersion` of the loaded `CoreSimulator.framework` (e.g. `"1171.6"`). */
113
+ static async frameworkVersion(): Promise<string> {
114
+ return runCatchingAsync(() => loadNative().frameworkVersion());
115
+ }
116
+
117
+ /**
118
+ * @internal Not part of the public API — exposed (not `private`) only so `src/commands/*.ts`
119
+ * mixins can call it. Resolves and memoizes the native `SimServiceContext` on first access —
120
+ * never in the constructor. `loadNative()` must run (and throw its typed platform error) before
121
+ * `defaultDeveloperDir()` ever shells out to `xcode-select`, which doesn't exist off macOS — so
122
+ * the fallback is resolved as an argument to the native call, not eagerly, preserving that
123
+ * evaluation order. Caching the in-flight promise (not just its resolved value) means concurrent
124
+ * callers before the first resolution still only trigger one `sharedServiceContext` call. On
125
+ * rejection the cache is cleared again — a Promise is truthy even once rejected, so leaving a
126
+ * failed one cached would otherwise make every later call reuse that same stale rejection
127
+ * forever instead of retrying once the underlying condition clears.
128
+ */
129
+ async _serviceContext(): Promise<NativeServiceContextHandle> {
130
+ if (!this.cachedServiceContext) {
131
+ this.cachedServiceContext = runCatchingAsync(() =>
132
+ loadNative().sharedServiceContext(this.developerDir ?? defaultDeveloperDir()),
133
+ ).catch((err: unknown) => {
134
+ this.cachedServiceContext = undefined;
135
+ throw err;
136
+ });
137
+ }
138
+ return this.cachedServiceContext;
139
+ }
140
+
141
+ /** @internal Not part of the public API — exposed only so `src/commands/*.ts` mixins can call it. */
142
+ async _deviceSet(): Promise<NativeDeviceSetHandle> {
143
+ const context = await this._serviceContext();
144
+ return runCatchingAsync(() => context.defaultDeviceSet());
145
+ }
146
+
147
+ /** @internal Not part of the public API — exposed only so `src/commands/*.ts` mixins can call it. */
148
+ async _findDevice(udid: string): Promise<NativeDeviceHandle> {
149
+ return runCatchingAsync(async () => {
150
+ const deviceSet = await this._deviceSet();
151
+ const device = (await deviceSet.devices()).find((candidate) => candidate.udid() === udid);
152
+ if (!device) {
153
+ throw new Error(`No simulator device found with udid '${udid}'`);
154
+ }
155
+ return device;
156
+ });
157
+ }
158
+ }
159
+
160
+ Object.assign(NativeSimctl.prototype, {
161
+ // lifecycle
162
+ getDevices,
163
+ getSupportedDeviceTypes,
164
+ getSupportedRuntimes,
165
+ createDevice,
166
+ deleteDevice,
167
+ bootDevice,
168
+ getBootStatus,
169
+ waitForBoot,
170
+ shutdownDevice,
171
+ shutdownAllDevices,
172
+ eraseDevice,
173
+
174
+ // app
175
+ installApp,
176
+ removeApp,
177
+ launchApp,
178
+ terminateApp,
179
+ isAppInstalled,
180
+ appInfo,
181
+ installedApps,
182
+ getAppContainer,
183
+
184
+ // media
185
+ addMedia,
186
+ addPhoto,
187
+ addVideo,
188
+
189
+ // biometric
190
+ isBiometricEnrolled,
191
+ enrollBiometric,
192
+ sendBiometricMatch,
193
+
194
+ // misc
195
+ shake,
196
+
197
+ // interaction
198
+ getEnv,
199
+ openUrl,
200
+ setLocation,
201
+ clearLocation,
202
+ pushNotification,
203
+
204
+ // keychain
205
+ addCertificate,
206
+ addRootCertificate,
207
+ resetKeychain,
208
+
209
+ // ui
210
+ getAppearance,
211
+ setAppearance,
212
+ getIncreaseContrast,
213
+ setIncreaseContrast,
214
+ getContentSize,
215
+ setContentSize,
216
+
217
+ // permissions
218
+ grantPermission,
219
+ revokePermission,
220
+ resetPermission,
221
+ getPermission,
222
+
223
+ // process
224
+ listProcesses,
225
+
226
+ // darwin notification
227
+ getDarwinNotificationState,
228
+ setDarwinNotificationState,
229
+ postDarwinNotification,
230
+
231
+ // pasteboard
232
+ getPasteboard,
233
+ setPasteboard,
234
+
235
+ // screenshot
236
+ getScreenshot,
237
+ getDisplays,
238
+
239
+ // webinspector
240
+ getWebInspectorSocket,
241
+
242
+ // spawn
243
+ spawnProcess,
244
+ });
245
+
246
+ // Loaded lazily (not at module import time) so merely depending on/importing @appium/coresim —
247
+ // e.g. for its error classes or types — works on any platform; only actually using it (booting a
248
+ // simulator, etc.) requires macOS, where CoreSimulator.framework exists. `npm install` never
249
+ // attempts to build the addon on other platforms either (see scripts/install.mjs). Any macOS arch
250
+ // works — node-gyp-build falls back to compiling from source on an arch with no matching
251
+ // prebuild (e.g. an Intel Mac, since only darwin-arm64 is prebuilt today). util.memoize only
252
+ // caches a normal return, never a throw (see @appium/support's util.js), so the platform check
253
+ // below re-runs — and re-throws — on every call until it actually succeeds once.
254
+ const loadNative = util.memoize(function loadNative(): NativeCoreSimModule {
255
+ if (process.platform !== 'darwin') {
256
+ throw new NativeSimUnavailableError(
257
+ `${PACKAGE_NAME} requires macOS — CoreSimulator.framework does not exist on ${process.platform}`,
258
+ 'platform',
259
+ process.platform,
260
+ 'n/a',
261
+ );
262
+ }
263
+ return require('node-gyp-build')(getPkgRoot()) as NativeCoreSimModule;
264
+ });
265
+
266
+ const DEFAULT_DEVELOPER_DIR_TIMEOUT_MS = 15_000;
267
+
268
+ function defaultDeveloperDir(): string {
269
+ try {
270
+ return execFileSync('xcode-select', ['-p'], {
271
+ encoding: 'utf8',
272
+ timeout: DEFAULT_DEVELOPER_DIR_TIMEOUT_MS,
273
+ }).trim();
274
+ } catch (err) {
275
+ // A non-zero exit (e.g. CLT installed but no Xcode selected) already carries a clear stderr
276
+ // message from xcode-select itself; only ENOENT (the binary isn't on PATH at all) and
277
+ // ETIMEDOUT need a friendlier message than Node's raw "spawnSync xcode-select ENOENT/ETIMEDOUT".
278
+ if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
279
+ throw new Error('xcode-select not found on PATH — install Xcode or the Xcode Command Line Tools', {
280
+ cause: err,
281
+ });
282
+ }
283
+ if ((err as NodeJS.ErrnoException)?.code === 'ETIMEDOUT') {
284
+ throw new Error(`xcode-select -p did not respond within ${DEFAULT_DEVELOPER_DIR_TIMEOUT_MS}ms`, {cause: err});
285
+ }
286
+ throw err;
287
+ }
288
+ }