@react-native-harness/config 1.5.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;
@@ -11,8 +12,10 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
11
12
  runner: z.ZodString;
12
13
  cli: z.ZodOptional<z.ZodString>;
13
14
  metroConfigEnhancer: z.ZodOptional<z.ZodString>;
15
+ bundlePlatform: z.ZodOptional<z.ZodString>;
14
16
  platformId: z.ZodString;
15
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>>;
16
19
  }, "strip", z.ZodTypeAny, {
17
20
  config: Record<string, any>;
18
21
  name: string;
@@ -20,7 +23,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
20
23
  platformId: string;
21
24
  cli?: string | undefined;
22
25
  metroConfigEnhancer?: string | undefined;
26
+ bundlePlatform?: string | undefined;
23
27
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
28
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
24
29
  }, {
25
30
  config: Record<string, any>;
26
31
  name: string;
@@ -28,7 +33,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
28
33
  platformId: string;
29
34
  cli?: string | undefined;
30
35
  metroConfigEnhancer?: string | undefined;
36
+ bundlePlatform?: string | undefined;
31
37
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
38
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
32
39
  }>, "many">;
33
40
  plugins: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodType<AnyHarnessPlugin, z.ZodTypeDef, AnyHarnessPlugin>, "many">>>;
34
41
  defaultRunner: z.ZodOptional<z.ZodString>;
@@ -94,7 +101,44 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
94
101
  } | undefined;
95
102
  }>>;
96
103
  forwardClientLogs: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
97
- 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
+ }>]>>>;
98
142
  include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
99
143
  }, "strip", z.ZodTypeAny, {
100
144
  entryPoint: string;
@@ -106,7 +150,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
106
150
  platformId: string;
107
151
  cli?: string | undefined;
108
152
  metroConfigEnhancer?: string | undefined;
153
+ bundlePlatform?: string | undefined;
109
154
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
155
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
110
156
  }[];
111
157
  plugins: AnyHarnessPlugin[];
112
158
  metroPort: number;
@@ -122,7 +168,16 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
122
168
  crashDetectionInterval: number;
123
169
  disableViewFlattening: boolean;
124
170
  forwardClientLogs: boolean;
125
- 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
+ };
126
181
  defaultRunner?: string | undefined;
127
182
  host?: string | undefined;
128
183
  webSocketPort?: number | undefined;
@@ -152,7 +207,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
152
207
  platformId: string;
153
208
  cli?: string | undefined;
154
209
  metroConfigEnhancer?: string | undefined;
210
+ bundlePlatform?: string | undefined;
155
211
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
212
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
156
213
  }[];
157
214
  plugins?: AnyHarnessPlugin[] | undefined;
158
215
  defaultRunner?: string | undefined;
@@ -186,7 +243,16 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
186
243
  } | undefined;
187
244
  } | undefined;
188
245
  forwardClientLogs?: boolean | undefined;
189
- 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;
190
256
  include?: string[] | undefined;
191
257
  }>, {
192
258
  entryPoint: string;
@@ -198,7 +264,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
198
264
  platformId: string;
199
265
  cli?: string | undefined;
200
266
  metroConfigEnhancer?: string | undefined;
267
+ bundlePlatform?: string | undefined;
201
268
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
269
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
202
270
  }[];
203
271
  plugins: AnyHarnessPlugin[];
204
272
  metroPort: number;
@@ -214,7 +282,16 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
214
282
  crashDetectionInterval: number;
215
283
  disableViewFlattening: boolean;
216
284
  forwardClientLogs: boolean;
217
- 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
+ };
218
295
  defaultRunner?: string | undefined;
219
296
  host?: string | undefined;
220
297
  webSocketPort?: number | undefined;
@@ -244,7 +321,9 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
244
321
  platformId: string;
245
322
  cli?: string | undefined;
246
323
  metroConfigEnhancer?: string | undefined;
324
+ bundlePlatform?: string | undefined;
247
325
  getResourceLockKey?: ((...args: unknown[]) => string | Promise<string>) | undefined;
326
+ deferMetroPrewarm?: ((...args: unknown[]) => boolean) | undefined;
248
327
  }[];
249
328
  plugins?: AnyHarnessPlugin[] | undefined;
250
329
  defaultRunner?: string | undefined;
@@ -278,16 +357,42 @@ export declare const ConfigSchema: z.ZodEffects<z.ZodObject<{
278
357
  } | undefined;
279
358
  } | undefined;
280
359
  forwardClientLogs?: boolean | undefined;
281
- 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;
282
370
  include?: string[] | undefined;
283
371
  }>;
284
372
  export type Config = z.infer<typeof ConfigSchema>;
285
373
  /**
286
374
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
287
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
288
- * 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'`).
289
378
  */
290
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;
291
396
  /**
292
397
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
293
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;AA+BvC,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
@@ -15,6 +15,9 @@ const RunnerSchema = z.object({
15
15
  // runner. Set by a platform factory (`HarnessPlatform.metroConfigEnhancer`);
16
16
  // imported and run by the bundler while it composes the config.
17
17
  metroConfigEnhancer: z.string().optional(),
18
+ // The Metro platform the app requests when it differs from platformId
19
+ // (e.g. Vega apps request "kepler"). Set by the platform factory.
20
+ bundlePlatform: z.string().optional(),
18
21
  platformId: z.string(),
19
22
  // Set by the platform factories (`HarnessPlatform.getResourceLockKey`) to
20
23
  // scope the run's resource lock — e.g. per emulator/simulator/device rather
@@ -25,8 +28,16 @@ const RunnerSchema = z.object({
25
28
  .args()
26
29
  .returns(z.union([z.string(), z.promise(z.string())]))
27
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(),
28
34
  });
29
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');
30
41
  export const ConfigSchema = z
31
42
  .object({
32
43
  entryPoint: z.string().min(1, 'Entry point is required'),
@@ -37,13 +48,7 @@ export const ConfigSchema = z
37
48
  plugins: z.array(PluginSchema).optional().default([]),
38
49
  defaultRunner: z.string().optional(),
39
50
  host: z.string().min(1, 'Host is required').optional(),
40
- metroPort: z
41
- .number()
42
- .int('Metro port must be an integer')
43
- .min(1, 'Metro port must be at least 1')
44
- .max(65535, 'Metro port must be at most 65535')
45
- .optional()
46
- .default(DEFAULT_METRO_PORT),
51
+ metroPort: MetroPortSchema.optional().default(DEFAULT_METRO_PORT),
47
52
  webSocketPort: z
48
53
  .number()
49
54
  .optional()
@@ -174,13 +179,53 @@ export const ConfigSchema = z
174
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. ' +
175
180
  "When enabled, app console output is attached to the active test result's console output."),
176
181
  diagnostics: z
177
- .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
+ ])
178
220
  .optional()
179
221
  .default(false)
180
- .describe('Enable diagnostics tracing for the harness session. Records spans for ' +
181
- 'session setup, Metro bundling, bridge/client events, and per-file test runs, ' +
182
- 'then writes a Chrome Trace Event JSON file and prints a summary after each run. ' +
183
- '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.'),
184
229
  // Deprecated property - used for migration detection
185
230
  include: z.array(z.string()).optional(),
186
231
  })
@@ -195,16 +240,39 @@ export const ConfigSchema = z
195
240
  });
196
241
  /**
197
242
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
198
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
199
- * 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'`).
200
246
  */
201
247
  export const isDiagnosticsEnabled = (config, env = process.env) => {
202
- 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) {
203
253
  return true;
204
254
  }
205
255
  const envValue = env.RN_HARNESS_DIAGNOSTICS;
206
256
  return !!envValue && envValue !== '0' && envValue !== 'false';
207
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
+ };
208
276
  /**
209
277
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
210
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.5.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.5.0",
21
- "@react-native-harness/plugins": "1.5.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
@@ -22,6 +22,9 @@ const RunnerSchema = z.object({
22
22
  // runner. Set by a platform factory (`HarnessPlatform.metroConfigEnhancer`);
23
23
  // imported and run by the bundler while it composes the config.
24
24
  metroConfigEnhancer: z.string().optional(),
25
+ // The Metro platform the app requests when it differs from platformId
26
+ // (e.g. Vega apps request "kepler"). Set by the platform factory.
27
+ bundlePlatform: z.string().optional(),
25
28
  platformId: z.string(),
26
29
  // Set by the platform factories (`HarnessPlatform.getResourceLockKey`) to
27
30
  // scope the run's resource lock — e.g. per emulator/simulator/device rather
@@ -32,6 +35,9 @@ const RunnerSchema = z.object({
32
35
  .args()
33
36
  .returns(z.union([z.string(), z.promise(z.string())]))
34
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(),
35
41
  });
36
42
 
37
43
  type AnyHarnessPlugin = HarnessPlugin<object, unknown>;
@@ -41,6 +47,12 @@ const PluginSchema = z.custom<AnyHarnessPlugin>(
41
47
  'Invalid Harness plugin'
42
48
  );
43
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
+
44
56
  export const ConfigSchema = z
45
57
  .object({
46
58
  entryPoint: z.string().min(1, 'Entry point is required'),
@@ -51,13 +63,7 @@ export const ConfigSchema = z
51
63
  plugins: z.array(PluginSchema).optional().default([]),
52
64
  defaultRunner: z.string().optional(),
53
65
  host: z.string().min(1, 'Host is required').optional(),
54
- metroPort: z
55
- .number()
56
- .int('Metro port must be an integer')
57
- .min(1, 'Metro port must be at least 1')
58
- .max(65535, 'Metro port must be at most 65535')
59
- .optional()
60
- .default(DEFAULT_METRO_PORT),
66
+ metroPort: MetroPortSchema.optional().default(DEFAULT_METRO_PORT),
61
67
  webSocketPort: z
62
68
  .number()
63
69
  .optional()
@@ -221,14 +227,66 @@ export const ConfigSchema = z
221
227
  ),
222
228
 
223
229
  diagnostics: z
224
- .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
+ ])
225
280
  .optional()
226
281
  .default(false)
227
282
  .describe(
228
- 'Enable diagnostics tracing for the harness session. Records spans for ' +
229
- 'session setup, Metro bundling, bridge/client events, and per-file test runs, ' +
230
- 'then writes a Chrome Trace Event JSON file and prints a summary after each run. ' +
231
- '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.'
232
290
  ),
233
291
 
234
292
  // Deprecated property - used for migration detection
@@ -253,14 +311,19 @@ export type Config = z.infer<typeof ConfigSchema>;
253
311
 
254
312
  /**
255
313
  * Resolves whether diagnostics tracing is enabled: either explicitly via the
256
- * `diagnostics` config option, or via the `RN_HARNESS_DIAGNOSTICS` environment
257
- * 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'`).
258
317
  */
259
318
  export const isDiagnosticsEnabled = (
260
319
  config: Pick<Config, 'diagnostics'> | undefined,
261
320
  env: NodeJS.ProcessEnv = process.env
262
321
  ): boolean => {
263
- 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) {
264
327
  return true;
265
328
  }
266
329
 
@@ -268,6 +331,39 @@ export const isDiagnosticsEnabled = (
268
331
  return !!envValue && envValue !== '0' && envValue !== 'false';
269
332
  };
270
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
+
271
367
  /**
272
368
  * Resolves the effective value of `skipAlreadyIncludedModules`, reconciling
273
369
  * it with the deprecated `unstable__skipAlreadyIncludedModules` alias: