@react-native-harness/config 1.6.0 → 1.7.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.
@@ -0,0 +1,14 @@
1
+ export declare const harnessCliOptions: {
2
+ readonly harnessRunner: {
3
+ readonly type: "string";
4
+ readonly description: "Specify which Harness runner to use";
5
+ readonly requiresArg: true;
6
+ };
7
+ readonly metroPort: {
8
+ readonly type: "number";
9
+ readonly description: "Set the preferred Metro port for this run";
10
+ readonly requiresArg: true;
11
+ readonly coerce: (value: unknown) => number;
12
+ };
13
+ };
14
+ //# sourceMappingURL=cli-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-options.d.ts","sourceRoot":"","sources":["../src/cli-options.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,iBAAiB;;;;;;;;;;iCAWV,OAAO,KAAG,MAAM;;CAU1B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { MetroPortSchema } from './types.js';
2
+ // The options Harness adds to Jest's command line. The CLI registers them with
3
+ // Jest's argument parser and the Jest integration reads them back, so an option
4
+ // listed here is both accepted and read.
5
+ export const harnessCliOptions = {
6
+ harnessRunner: {
7
+ type: 'string',
8
+ description: 'Specify which Harness runner to use',
9
+ requiresArg: true,
10
+ },
11
+ metroPort: {
12
+ type: 'number',
13
+ description: 'Set the preferred Metro port for this run',
14
+ requiresArg: true,
15
+ // Rejects a port the config would reject, before Jest starts any work.
16
+ coerce: (value) => {
17
+ const result = MetroPortSchema.safeParse(value);
18
+ if (!result.success) {
19
+ throw new Error(`Invalid --metroPort: ${result.error.issues[0]?.message}`);
20
+ }
21
+ return result.data;
22
+ },
23
+ },
24
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { getConfig } from './reader.js';
2
- export type { Config } from './types.js';
3
- export { ConfigSchema, DEFAULT_METRO_PORT, isDiagnosticsEnabled, resolveMetroCacheEnabled, resolveSkipAlreadyIncludedModules, } from './types.js';
2
+ export { harnessCliOptions } from './cli-options.js';
3
+ export type { Config, ResolvedDiagnosticsOptions } from './types.js';
4
+ export { ConfigSchema, DEFAULT_METRO_PORT, isDiagnosticsEnabled, resolveDiagnosticsOptions, resolveMetroCacheEnabled, resolveSkipAlreadyIncludedModules, } from './types.js';
4
5
  export { ConfigValidationError, ConfigNotFoundError, ConfigLoadError, } from './errors.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,GAChB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,MAAM,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,GAChB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { getConfig } from './reader.js';
2
- export { ConfigSchema, DEFAULT_METRO_PORT, isDiagnosticsEnabled, resolveMetroCacheEnabled, resolveSkipAlreadyIncludedModules, } from './types.js';
2
+ export { harnessCliOptions } from './cli-options.js';
3
+ export { ConfigSchema, DEFAULT_METRO_PORT, isDiagnosticsEnabled, resolveDiagnosticsOptions, resolveMetroCacheEnabled, resolveSkipAlreadyIncludedModules, } from './types.js';
3
4
  export { ConfigValidationError, ConfigNotFoundError, ConfigLoadError, } from './errors.js';
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import type { HarnessPlugin } from '@react-native-harness/plugins';
3
3
  export declare const DEFAULT_METRO_PORT = 8081;
4
4
  type AnyHarnessPlugin = HarnessPlugin<object, unknown>;
5
+ export declare const MetroPortSchema: z.ZodNumber;
5
6
  export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
6
7
  entryPoint: z.ZodString;
7
8
  appRegistryComponentName: z.ZodString;
@@ -14,24 +15,27 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
14
15
  bundlePlatform: z.ZodOptional<z.ZodString>;
15
16
  platformId: z.ZodString;
16
17
  getResourceLockKey: z.ZodOptional<z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnion<[z.ZodString, z.ZodPromise<z.ZodString>]>>>;
18
+ deferMetroPrewarm: z.ZodOptional<z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodBoolean>>;
17
19
  }, "strip", z.ZodTypeAny, {
18
- name: string;
19
20
  config: Record<string, any>;
21
+ name: string;
20
22
  runner: string;
21
23
  platformId: string;
22
24
  cli?: string | undefined;
23
25
  metroConfigEnhancer?: string | undefined;
24
26
  bundlePlatform?: string | undefined;
25
27
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
28
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
26
29
  }, {
27
- name: string;
28
30
  config: Record<string, any>;
31
+ name: string;
29
32
  runner: string;
30
33
  platformId: string;
31
34
  cli?: string | undefined;
32
35
  metroConfigEnhancer?: string | undefined;
33
36
  bundlePlatform?: string | undefined;
34
37
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
38
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
35
39
  }>, "many">;
36
40
  plugins: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodType<AnyHarnessPlugin, z.ZodTypeDef, AnyHarnessPlugin>, "many">>>;
37
41
  defaultRunner: z.ZodOptional<z.ZodString>;
@@ -97,20 +101,58 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
97
101
  } | undefined;
98
102
  }>>;
99
103
  forwardClientLogs: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
100
- diagnostics: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
104
+ diagnostics: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
105
+ enabled: z.ZodOptional<z.ZodBoolean>;
106
+ probes: z.ZodOptional<z.ZodObject<{
107
+ host: z.ZodOptional<z.ZodBoolean>;
108
+ node: z.ZodOptional<z.ZodBoolean>;
109
+ snapshots: z.ZodOptional<z.ZodBoolean>;
110
+ platform: z.ZodOptional<z.ZodBoolean>;
111
+ }, "strict", z.ZodTypeAny, {
112
+ host?: boolean | undefined;
113
+ node?: boolean | undefined;
114
+ snapshots?: boolean | undefined;
115
+ platform?: boolean | undefined;
116
+ }, {
117
+ host?: boolean | undefined;
118
+ node?: boolean | undefined;
119
+ snapshots?: boolean | undefined;
120
+ platform?: boolean | undefined;
121
+ }>>;
122
+ sampleIntervalMs: z.ZodOptional<z.ZodNumber>;
123
+ }, "strict", z.ZodTypeAny, {
124
+ enabled?: boolean | undefined;
125
+ probes?: {
126
+ host?: boolean | undefined;
127
+ node?: boolean | undefined;
128
+ snapshots?: boolean | undefined;
129
+ platform?: boolean | undefined;
130
+ } | undefined;
131
+ sampleIntervalMs?: number | undefined;
132
+ }, {
133
+ enabled?: boolean | undefined;
134
+ probes?: {
135
+ host?: boolean | undefined;
136
+ node?: boolean | undefined;
137
+ snapshots?: boolean | undefined;
138
+ platform?: boolean | undefined;
139
+ } | undefined;
140
+ sampleIntervalMs?: number | undefined;
141
+ }>]>>>;
101
142
  include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
102
143
  }, "strip", z.ZodTypeAny, {
103
144
  entryPoint: string;
104
145
  appRegistryComponentName: string;
105
146
  runners: {
106
- name: string;
107
147
  config: Record<string, any>;
148
+ name: string;
108
149
  runner: string;
109
150
  platformId: string;
110
151
  cli?: string | undefined;
111
152
  metroConfigEnhancer?: string | undefined;
112
153
  bundlePlatform?: string | undefined;
113
154
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
155
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
114
156
  }[];
115
157
  plugins: AnyHarnessPlugin[];
116
158
  metroPort: number;
@@ -126,7 +168,16 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
126
168
  crashDetectionInterval: number;
127
169
  disableViewFlattening: boolean;
128
170
  forwardClientLogs: boolean;
129
- diagnostics: boolean;
171
+ diagnostics: boolean | {
172
+ enabled?: boolean | undefined;
173
+ probes?: {
174
+ host?: boolean | undefined;
175
+ node?: boolean | undefined;
176
+ snapshots?: boolean | undefined;
177
+ platform?: boolean | undefined;
178
+ } | undefined;
179
+ sampleIntervalMs?: number | undefined;
180
+ };
130
181
  defaultRunner?: string | undefined;
131
182
  host?: string | undefined;
132
183
  webSocketPort?: number | undefined;
@@ -150,14 +201,15 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
150
201
  entryPoint: string;
151
202
  appRegistryComponentName: string;
152
203
  runners: {
153
- name: string;
154
204
  config: Record<string, any>;
205
+ name: string;
155
206
  runner: string;
156
207
  platformId: string;
157
208
  cli?: string | undefined;
158
209
  metroConfigEnhancer?: string | undefined;
159
210
  bundlePlatform?: string | undefined;
160
211
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
212
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
161
213
  }[];
162
214
  plugins?: AnyHarnessPlugin[] | undefined;
163
215
  defaultRunner?: string | undefined;
@@ -191,20 +243,30 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
191
243
  } | undefined;
192
244
  } | undefined;
193
245
  forwardClientLogs?: boolean | undefined;
194
- diagnostics?: boolean | undefined;
246
+ diagnostics?: boolean | {
247
+ enabled?: boolean | undefined;
248
+ probes?: {
249
+ host?: boolean | undefined;
250
+ node?: boolean | undefined;
251
+ snapshots?: boolean | undefined;
252
+ platform?: boolean | undefined;
253
+ } | undefined;
254
+ sampleIntervalMs?: number | undefined;
255
+ } | undefined;
195
256
  include?: string[] | undefined;
196
257
  }>, {
197
258
  entryPoint: string;
198
259
  appRegistryComponentName: string;
199
260
  runners: {
200
- name: string;
201
261
  config: Record<string, any>;
262
+ name: string;
202
263
  runner: string;
203
264
  platformId: string;
204
265
  cli?: string | undefined;
205
266
  metroConfigEnhancer?: string | undefined;
206
267
  bundlePlatform?: string | undefined;
207
268
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
269
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
208
270
  }[];
209
271
  plugins: AnyHarnessPlugin[];
210
272
  metroPort: number;
@@ -220,7 +282,16 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
220
282
  crashDetectionInterval: number;
221
283
  disableViewFlattening: boolean;
222
284
  forwardClientLogs: boolean;
223
- diagnostics: boolean;
285
+ diagnostics: boolean | {
286
+ enabled?: boolean | undefined;
287
+ probes?: {
288
+ host?: boolean | undefined;
289
+ node?: boolean | undefined;
290
+ snapshots?: boolean | undefined;
291
+ platform?: boolean | undefined;
292
+ } | undefined;
293
+ sampleIntervalMs?: number | undefined;
294
+ };
224
295
  defaultRunner?: string | undefined;
225
296
  host?: string | undefined;
226
297
  webSocketPort?: number | undefined;
@@ -244,14 +315,15 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
244
315
  entryPoint: string;
245
316
  appRegistryComponentName: string;
246
317
  runners: {
247
- name: string;
248
318
  config: Record<string, any>;
319
+ name: string;
249
320
  runner: string;
250
321
  platformId: string;
251
322
  cli?: string | undefined;
252
323
  metroConfigEnhancer?: string | undefined;
253
324
  bundlePlatform?: string | undefined;
254
325
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
326
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
255
327
  }[];
256
328
  plugins?: AnyHarnessPlugin[] | undefined;
257
329
  defaultRunner?: string | undefined;
@@ -285,16 +357,42 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
285
357
  } | undefined;
286
358
  } | undefined;
287
359
  forwardClientLogs?: boolean | undefined;
288
- diagnostics?: boolean | undefined;
360
+ diagnostics?: boolean | {
361
+ enabled?: boolean | undefined;
362
+ probes?: {
363
+ host?: boolean | undefined;
364
+ node?: boolean | undefined;
365
+ snapshots?: boolean | undefined;
366
+ platform?: boolean | undefined;
367
+ } | undefined;
368
+ sampleIntervalMs?: number | undefined;
369
+ } | undefined;
289
370
  include?: string[] | undefined;
290
371
  }>;
291
372
  export type Config = z.infer<typeof ConfigSchema>;
292
373
  /**
293
374
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
294
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
295
- * variable (any value other than unset, `''`, `'0'`, or `'false'`).
375
+ * `diagnostics` config option (`true`, or an object that does not set
376
+ * `enabled: false`), or via the `RN_HARNESS_DIAGNOSTICS` environment variable
377
+ * (any value other than unset, `''`, `'0'`, or `'false'`).
296
378
  */
297
379
  export declare const isDiagnosticsEnabled: (config: Pick<Config, "diagnostics"> | undefined, env?: NodeJS.ProcessEnv) => boolean;
380
+ export type ResolvedDiagnosticsOptions = {
381
+ probes: {
382
+ host: boolean;
383
+ node: boolean;
384
+ snapshots: boolean;
385
+ platform: boolean;
386
+ };
387
+ /** Undefined means "use the default sampling interval". */
388
+ sampleIntervalMs: number | undefined;
389
+ };
390
+ /**
391
+ * Resolves the diagnostics tuning options. Every probe is on unless the object
392
+ * form of `diagnostics` switches it off, which is also what the boolean form
393
+ * and the `RN_HARNESS_DIAGNOSTICS` environment variable mean.
394
+ */
395
+ export declare const resolveDiagnosticsOptions: (config: Pick<Config, "diagnostics"> | undefined) => ResolvedDiagnosticsOptions;
298
396
  /**
299
397
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
300
398
  * it with the deprecated `unstable__skipAlreadyIncludedModules` alias:
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAInE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAkCvC,KAAK,gBAAgB,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAOvD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8MtB,CAAC;AAEJ,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,EAC/C,MAAK,MAAM,CAAC,UAAwB,KACnC,OAOF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iCAAiC,GAC5C,QAAQ,IAAI,CACV,MAAM,EACN,4BAA4B,GAAG,sCAAsC,CACtE,KACA,OAiBF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,4BAA4B,CAAC,KAC3D,OAiBF,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAInE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAqCvC,KAAK,gBAAgB,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAOvD,eAAO,MAAM,eAAe,aAIqB,CAAC;AAElD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4PtB,CAAC;AAEJ,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,EAC/C,MAAK,MAAM,CAAC,UAAwB,KACnC,OAWF,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE;QACN,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,EAAE,OAAO,CAAC;QACd,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GACpC,QAAQ,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,KAC9C,0BAaF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iCAAiC,GAC5C,QAAQ,IAAI,CACV,MAAM,EACN,4BAA4B,GAAG,sCAAsC,CACtE,KACA,OAiBF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,4BAA4B,CAAC,KAC3D,OAiBF,CAAC"}
package/dist/types.js CHANGED
@@ -28,8 +28,16 @@ const RunnerSchema = z.object({
28
28
  .args()
29
29
  .returns(z.union([z.string(), z.promise(z.string())]))
30
30
  .optional(),
31
+ // Set by platform factories (`HarnessPlatform.deferMetroPrewarm`) to start
32
+ // the Metro prewarm only after the platform is ready on small hosts.
33
+ deferMetroPrewarm: z.function().args().returns(z.boolean()).optional(),
31
34
  });
32
35
  const PluginSchema = z.custom((value) => isHarnessPlugin(value), 'Invalid Harness plugin');
36
+ export const MetroPortSchema = z
37
+ .number()
38
+ .int('Metro port must be an integer')
39
+ .min(1, 'Metro port must be at least 1')
40
+ .max(65535, 'Metro port must be at most 65535');
33
41
  export const ConfigSchema = z
34
42
  .object({
35
43
  entryPoint: z.string().min(1, 'Entry point is required'),
@@ -40,13 +48,7 @@ export const ConfigSchema = z
40
48
  plugins: z.array(PluginSchema).optional().default([]),
41
49
  defaultRunner: z.string().optional(),
42
50
  host: z.string().min(1, 'Host is required').optional(),
43
- metroPort: z
44
- .number()
45
- .int('Metro port must be an integer')
46
- .min(1, 'Metro port must be at least 1')
47
- .max(65535, 'Metro port must be at most 65535')
48
- .optional()
49
- .default(DEFAULT_METRO_PORT),
51
+ metroPort: MetroPortSchema.optional().default(DEFAULT_METRO_PORT),
50
52
  webSocketPort: z
51
53
  .number()
52
54
  .optional()
@@ -177,13 +179,53 @@ export const ConfigSchema = z
177
179
  .describe('Enable forwarding of console.log, console.warn, console.error, and other console method calls from the React Native app during the active test run. ' +
178
180
  "When enabled, app console output is attached to the active test result's console output."),
179
181
  diagnostics: z
180
- .boolean()
182
+ .union([
183
+ z.boolean(),
184
+ z
185
+ .object({
186
+ enabled: z
187
+ .boolean()
188
+ .optional()
189
+ .describe('Turns diagnostics on or off. Defaults to true when the object form is used.'),
190
+ probes: z
191
+ .object({
192
+ host: z
193
+ .boolean()
194
+ .optional()
195
+ .describe('Sample host load, CPU split and memory pressure. Defaults to true.'),
196
+ node: z
197
+ .boolean()
198
+ .optional()
199
+ .describe('Sample Harness process memory and event loop lag. Defaults to true.'),
200
+ snapshots: z
201
+ .boolean()
202
+ .optional()
203
+ .describe('Record a process listing at run milestones (macOS). Defaults to true.'),
204
+ platform: z
205
+ .boolean()
206
+ .optional()
207
+ .describe('Record platform specific measurements, such as the process count inside an iOS simulator. Defaults to true.'),
208
+ })
209
+ .strict()
210
+ .optional(),
211
+ sampleIntervalMs: z
212
+ .number()
213
+ .int()
214
+ .min(250, 'sampleIntervalMs must be at least 250')
215
+ .optional()
216
+ .describe('How often host and process metrics are sampled, in milliseconds. Defaults to 5000.'),
217
+ })
218
+ .strict(),
219
+ ])
181
220
  .optional()
182
221
  .default(false)
183
- .describe('Enable diagnostics tracing for the harness session. Records spans for ' +
184
- 'session setup, Metro bundling, bridge/client events, and per-file test runs, ' +
185
- 'then writes a Chrome Trace Event JSON file and prints a summary after each run. ' +
186
- 'Can also be enabled via the RN_HARNESS_DIAGNOSTICS environment variable.'),
222
+ .describe('Enable diagnostics for the harness session. Records spans for session ' +
223
+ 'setup, Metro bundling, bridge/client events, and per-file test runs, ' +
224
+ 'samples host and process resource metrics, then writes a Chrome Trace ' +
225
+ 'Event JSON file, an incremental JSONL log and a KPI summary, and prints ' +
226
+ 'a summary after each run. Pass an object to tune the probes and the ' +
227
+ 'sampling interval. Can also be enabled via the RN_HARNESS_DIAGNOSTICS ' +
228
+ 'environment variable.'),
187
229
  // Deprecated property - used for migration detection
188
230
  include: z.array(z.string()).optional(),
189
231
  })
@@ -198,16 +240,39 @@ export const ConfigSchema = z
198
240
  });
199
241
  /**
200
242
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
201
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
202
- * variable (any value other than unset, `''`, `'0'`, or `'false'`).
243
+ * `diagnostics` config option (`true`, or an object that does not set
244
+ * `enabled: false`), or via the `RN_HARNESS_DIAGNOSTICS` environment variable
245
+ * (any value other than unset, `''`, `'0'`, or `'false'`).
203
246
  */
204
247
  export const isDiagnosticsEnabled = (config, env = process.env) => {
205
- if (config?.diagnostics === true) {
248
+ const diagnostics = config?.diagnostics;
249
+ if (diagnostics === true) {
250
+ return true;
251
+ }
252
+ if (typeof diagnostics === 'object' && diagnostics.enabled !== false) {
206
253
  return true;
207
254
  }
208
255
  const envValue = env.RN_HARNESS_DIAGNOSTICS;
209
256
  return !!envValue && envValue !== '0' && envValue !== 'false';
210
257
  };
258
+ /**
259
+ * Resolves the diagnostics tuning options. Every probe is on unless the object
260
+ * form of `diagnostics` switches it off, which is also what the boolean form
261
+ * and the `RN_HARNESS_DIAGNOSTICS` environment variable mean.
262
+ */
263
+ export const resolveDiagnosticsOptions = (config) => {
264
+ const diagnostics = config?.diagnostics;
265
+ const options = typeof diagnostics === 'object' ? diagnostics : undefined;
266
+ return {
267
+ probes: {
268
+ host: options?.probes?.host ?? true,
269
+ node: options?.probes?.node ?? true,
270
+ snapshots: options?.probes?.snapshots ?? true,
271
+ platform: options?.probes?.platform ?? true,
272
+ },
273
+ sampleIntervalMs: options?.sampleIntervalMs,
274
+ };
275
+ };
211
276
  /**
212
277
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
213
278
  * it with the deprecated `unstable__skipAlreadyIncludedModules` alias:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-harness/config",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -17,8 +17,8 @@
17
17
  "dependencies": {
18
18
  "tslib": "^2.3.0",
19
19
  "zod": "^3.25.67",
20
- "@react-native-harness/tools": "1.6.0",
21
- "@react-native-harness/plugins": "1.6.0"
20
+ "@react-native-harness/plugins": "1.7.0",
21
+ "@react-native-harness/tools": "1.7.0"
22
22
  },
23
23
  "license": "MIT"
24
24
  }
@@ -0,0 +1,27 @@
1
+ import { MetroPortSchema } from './types.js';
2
+
3
+ // The options Harness adds to Jest's command line. The CLI registers them with
4
+ // Jest's argument parser and the Jest integration reads them back, so an option
5
+ // listed here is both accepted and read.
6
+ export const harnessCliOptions = {
7
+ harnessRunner: {
8
+ type: 'string',
9
+ description: 'Specify which Harness runner to use',
10
+ requiresArg: true,
11
+ },
12
+ metroPort: {
13
+ type: 'number',
14
+ description: 'Set the preferred Metro port for this run',
15
+ requiresArg: true,
16
+ // Rejects a port the config would reject, before Jest starts any work.
17
+ coerce: (value: unknown): number => {
18
+ const result = MetroPortSchema.safeParse(value);
19
+ if (!result.success) {
20
+ throw new Error(
21
+ `Invalid --metroPort: ${result.error.issues[0]?.message}`
22
+ );
23
+ }
24
+ return result.data;
25
+ },
26
+ },
27
+ } as const;
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { getConfig } from './reader.js';
2
- export type { Config } from './types.js';
2
+ export { harnessCliOptions } from './cli-options.js';
3
+ export type { Config, ResolvedDiagnosticsOptions } from './types.js';
3
4
  export {
4
5
  ConfigSchema,
5
6
  DEFAULT_METRO_PORT,
6
7
  isDiagnosticsEnabled,
8
+ resolveDiagnosticsOptions,
7
9
  resolveMetroCacheEnabled,
8
10
  resolveSkipAlreadyIncludedModules,
9
11
  } from './types.js';
package/src/types.ts CHANGED
@@ -35,6 +35,9 @@ const RunnerSchema = z.object({
35
35
  .args()
36
36
  .returns(z.union([z.string(), z.promise(z.string())]))
37
37
  .optional(),
38
+ // Set by platform factories (`HarnessPlatform.deferMetroPrewarm`) to start
39
+ // the Metro prewarm only after the platform is ready on small hosts.
40
+ deferMetroPrewarm: z.function().args().returns(z.boolean()).optional(),
38
41
  });
39
42
 
40
43
  type AnyHarnessPlugin = HarnessPlugin<object, unknown>;
@@ -44,6 +47,12 @@ const PluginSchema = z.custom<AnyHarnessPlugin>(
44
47
  'Invalid Harness plugin'
45
48
  );
46
49
 
50
+ export const MetroPortSchema = z
51
+ .number()
52
+ .int('Metro port must be an integer')
53
+ .min(1, 'Metro port must be at least 1')
54
+ .max(65535, 'Metro port must be at most 65535');
55
+
47
56
  export const ConfigSchema = z
48
57
  .object({
49
58
  entryPoint: z.string().min(1, 'Entry point is required'),
@@ -54,13 +63,7 @@ export const ConfigSchema = z
54
63
  plugins: z.array(PluginSchema).optional().default([]),
55
64
  defaultRunner: z.string().optional(),
56
65
  host: z.string().min(1, 'Host is required').optional(),
57
- metroPort: z
58
- .number()
59
- .int('Metro port must be an integer')
60
- .min(1, 'Metro port must be at least 1')
61
- .max(65535, 'Metro port must be at most 65535')
62
- .optional()
63
- .default(DEFAULT_METRO_PORT),
66
+ metroPort: MetroPortSchema.optional().default(DEFAULT_METRO_PORT),
64
67
  webSocketPort: z
65
68
  .number()
66
69
  .optional()
@@ -224,14 +227,66 @@ export const ConfigSchema = z
224
227
  ),
225
228
 
226
229
  diagnostics: z
227
- .boolean()
230
+ .union([
231
+ z.boolean(),
232
+ z
233
+ .object({
234
+ enabled: z
235
+ .boolean()
236
+ .optional()
237
+ .describe(
238
+ 'Turns diagnostics on or off. Defaults to true when the object form is used.'
239
+ ),
240
+ probes: z
241
+ .object({
242
+ host: z
243
+ .boolean()
244
+ .optional()
245
+ .describe(
246
+ 'Sample host load, CPU split and memory pressure. Defaults to true.'
247
+ ),
248
+ node: z
249
+ .boolean()
250
+ .optional()
251
+ .describe(
252
+ 'Sample Harness process memory and event loop lag. Defaults to true.'
253
+ ),
254
+ snapshots: z
255
+ .boolean()
256
+ .optional()
257
+ .describe(
258
+ 'Record a process listing at run milestones (macOS). Defaults to true.'
259
+ ),
260
+ platform: z
261
+ .boolean()
262
+ .optional()
263
+ .describe(
264
+ 'Record platform specific measurements, such as the process count inside an iOS simulator. Defaults to true.'
265
+ ),
266
+ })
267
+ .strict()
268
+ .optional(),
269
+ sampleIntervalMs: z
270
+ .number()
271
+ .int()
272
+ .min(250, 'sampleIntervalMs must be at least 250')
273
+ .optional()
274
+ .describe(
275
+ 'How often host and process metrics are sampled, in milliseconds. Defaults to 5000.'
276
+ ),
277
+ })
278
+ .strict(),
279
+ ])
228
280
  .optional()
229
281
  .default(false)
230
282
  .describe(
231
- 'Enable diagnostics tracing for the harness session. Records spans for ' +
232
- 'session setup, Metro bundling, bridge/client events, and per-file test runs, ' +
233
- 'then writes a Chrome Trace Event JSON file and prints a summary after each run. ' +
234
- 'Can also be enabled via the RN_HARNESS_DIAGNOSTICS environment variable.'
283
+ 'Enable diagnostics for the harness session. Records spans for session ' +
284
+ 'setup, Metro bundling, bridge/client events, and per-file test runs, ' +
285
+ 'samples host and process resource metrics, then writes a Chrome Trace ' +
286
+ 'Event JSON file, an incremental JSONL log and a KPI summary, and prints ' +
287
+ 'a summary after each run. Pass an object to tune the probes and the ' +
288
+ 'sampling interval. Can also be enabled via the RN_HARNESS_DIAGNOSTICS ' +
289
+ 'environment variable.'
235
290
  ),
236
291
 
237
292
  // Deprecated property - used for migration detection
@@ -256,14 +311,19 @@ export type Config = z.infer<typeof ConfigSchema>;
256
311
 
257
312
  /**
258
313
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
259
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
260
- * variable (any value other than unset, `''`, `'0'`, or `'false'`).
314
+ * `diagnostics` config option (`true`, or an object that does not set
315
+ * `enabled: false`), or via the `RN_HARNESS_DIAGNOSTICS` environment variable
316
+ * (any value other than unset, `''`, `'0'`, or `'false'`).
261
317
  */
262
318
  export const isDiagnosticsEnabled = (
263
319
  config: Pick<Config, 'diagnostics'> | undefined,
264
320
  env: NodeJS.ProcessEnv = process.env
265
321
  ): boolean => {
266
- if (config?.diagnostics === true) {
322
+ const diagnostics = config?.diagnostics;
323
+ if (diagnostics === true) {
324
+ return true;
325
+ }
326
+ if (typeof diagnostics === 'object' && diagnostics.enabled !== false) {
267
327
  return true;
268
328
  }
269
329
 
@@ -271,6 +331,39 @@ export const isDiagnosticsEnabled = (
271
331
  return !!envValue && envValue !== '0' && envValue !== 'false';
272
332
  };
273
333
 
334
+ export type ResolvedDiagnosticsOptions = {
335
+ probes: {
336
+ host: boolean;
337
+ node: boolean;
338
+ snapshots: boolean;
339
+ platform: boolean;
340
+ };
341
+ /** Undefined means "use the default sampling interval". */
342
+ sampleIntervalMs: number | undefined;
343
+ };
344
+
345
+ /**
346
+ * Resolves the diagnostics tuning options. Every probe is on unless the object
347
+ * form of `diagnostics` switches it off, which is also what the boolean form
348
+ * and the `RN_HARNESS_DIAGNOSTICS` environment variable mean.
349
+ */
350
+ export const resolveDiagnosticsOptions = (
351
+ config: Pick<Config, 'diagnostics'> | undefined
352
+ ): ResolvedDiagnosticsOptions => {
353
+ const diagnostics = config?.diagnostics;
354
+ const options = typeof diagnostics === 'object' ? diagnostics : undefined;
355
+
356
+ return {
357
+ probes: {
358
+ host: options?.probes?.host ?? true,
359
+ node: options?.probes?.node ?? true,
360
+ snapshots: options?.probes?.snapshots ?? true,
361
+ platform: options?.probes?.platform ?? true,
362
+ },
363
+ sampleIntervalMs: options?.sampleIntervalMs,
364
+ };
365
+ };
366
+
274
367
  /**
275
368
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
276
369
  * it with the deprecated `unstable__skipAlreadyIncludedModules` alias: