@percy/core 1.32.0-beta.0 → 1.32.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/percy.js CHANGED
@@ -20,6 +20,7 @@ import { gatherSnapshots, createSnapshotsQueue, validateSnapshotOptions } from '
20
20
  import { discoverSnapshotResources, createDiscoveryQueue, RESOURCE_CACHE_KEY, CACHE_STATS_KEY, DISK_SPILL_KEY } from './discovery.js';
21
21
  import Monitoring from '@percy/monitoring';
22
22
  import { WaitForJob } from './wait-for-job.js';
23
+ import { closeGrpcClientCache } from './maestro-hierarchy.js';
23
24
  const MAX_SUGGESTION_CALLS = 10;
24
25
 
25
26
  // If no activity is done for 5 mins, we will stop monitoring
@@ -121,6 +122,16 @@ export class Percy {
121
122
  this.monitoringCheckLastExecutedAt = null;
122
123
  this.sdkInfoDisplayed = false;
123
124
 
125
+ // Per-Percy gRPC client cache for the Android view-hierarchy resolver
126
+ // (D9 in 2026-05-07-002 plan). Owns transport state — channels hold open
127
+ // sockets, must be closed in stop(). Module-scoped state would leak
128
+ // between concurrent Percy instances and cause cross-instance shutdown
129
+ // races. The drift envelope (maestroHierarchyDrift in maestro-hierarchy.js)
130
+ // stays module-scoped because drift is observability state — surfaced
131
+ // process-wide on /percy/healthcheck. Two scopes, two reasons.
132
+ this.grpcClientCache = new Map();
133
+ this.grpcClientCache.shutdownInProgress = false;
134
+
124
135
  // Domain validation state for auto domain allow-listing
125
136
  this.domainValidation = {
126
137
  autoConfiguredHosts: new Set(),
@@ -429,6 +440,13 @@ export class Percy {
429
440
  await _classPrivateFieldGet(_discovery, this).end();
430
441
  await _classPrivateFieldGet(_snapshots, this).end();
431
442
 
443
+ // Close gRPC channels for the Android view-hierarchy resolver. Set the
444
+ // shutdown flag first so any in-flight runAndroidGrpcDump() that hits
445
+ // CANCELLED returns {kind:'unavailable', reason:'shutdown'} instead of
446
+ // triggering the fallback chain on a tearing-down process (R-7).
447
+ this.grpcClientCache.shutdownInProgress = true;
448
+ closeGrpcClientCache(this.grpcClientCache);
449
+
432
450
  // mark instance as stopped
433
451
  this.readyState = 3;
434
452
  } catch (err) {
@@ -0,0 +1,47 @@
1
+ # Vendored protobuf — `maestro_android.proto`
2
+
3
+ Direct copy of the protobuf schema served by `dev.mobile.maestro` on Android
4
+ devices, used by `@percy/core`'s element-region resolver to call
5
+ `maestro_android.MaestroDriver/viewHierarchy` directly over gRPC instead of
6
+ spawning the full `maestro` CLI (~9s JVM cold start per call → <100ms direct
7
+ gRPC call).
8
+
9
+ ## Source
10
+
11
+ - **Upstream file:** `maestro-proto/src/main/proto/maestro_android.proto`
12
+ - **Upstream repo:** [`mobile-dev-inc/Maestro`](https://github.com/mobile-dev-inc/Maestro)
13
+ - **Commit SHA at copy time:** `bc8bde1b5cb7f2d4076047c0a9db094ece47512f` (2025-05-26)
14
+ - **Closest CLI release:** `cli-2.5.1`
15
+ - **Copy date:** 2026-04-29
16
+
17
+ ## What we use
18
+
19
+ Only `MaestroDriver/viewHierarchy(ViewHierarchyRequest) returns (ViewHierarchyResponse)`
20
+ and the `string hierarchy = 1` field on the response. The rest of the proto
21
+ is included unchanged so future updates can be a clean upstream re-copy
22
+ without surgical edits.
23
+
24
+ ## Drift policy
25
+
26
+ - The proto **must** be re-vendored from upstream whenever the Maestro CLI
27
+ version deployed on BrowserStack hosts is bumped past the version recorded
28
+ above. PRs that update this file must paste the upstream SHA and CLI tag.
29
+ - The runtime parser (`@grpc/proto-loader`) silently drops unknown fields.
30
+ If `viewHierarchy`'s response field is renumbered, retyped, or replaced,
31
+ decode errors surface as `dump-error (grpc-decode)` and a
32
+ `maestroHierarchyDrift` flag appears on the `/percy/healthcheck` response.
33
+ See `docs/solutions/integration-issues/percy-labels-cli-schema-rejection-2026-04-23.md`
34
+ for context on why we monitor schema drift loudly.
35
+
36
+ ## How to refresh
37
+
38
+ ```sh
39
+ curl -fsSL "https://raw.githubusercontent.com/mobile-dev-inc/Maestro/main/maestro-proto/src/main/proto/maestro_android.proto" \
40
+ -o packages/core/src/proto/maestro_android.proto
41
+ # Update the SHA + CLI tag above; PR must show the diff and the new pin.
42
+ ```
43
+
44
+ The file is loaded at module init via `@grpc/proto-loader`'s `loadSync` from
45
+ `packages/core/src/maestro-hierarchy.js`. Babel CLI's `copyFiles: true`
46
+ (scripts/build.js:26) preserves the relative layout so it lands at
47
+ `dist/proto/maestro_android.proto` after `yarn build`.
@@ -0,0 +1,116 @@
1
+ syntax = "proto3";
2
+
3
+ package maestro_android;
4
+
5
+ service MaestroDriver {
6
+
7
+ rpc deviceInfo(DeviceInfoRequest) returns (DeviceInfo) {}
8
+
9
+ rpc viewHierarchy(ViewHierarchyRequest) returns (ViewHierarchyResponse) {}
10
+
11
+ rpc screenshot(ScreenshotRequest) returns (ScreenshotResponse) {}
12
+
13
+ rpc tap(TapRequest) returns (TapResponse) {}
14
+
15
+ rpc inputText(InputTextRequest) returns (InputTextResponse) {}
16
+
17
+ rpc eraseAllText(EraseAllTextRequest) returns (EraseAllTextResponse) {}
18
+
19
+ rpc setLocation(SetLocationRequest) returns (SetLocationResponse) {}
20
+
21
+ rpc isWindowUpdating(CheckWindowUpdatingRequest) returns (CheckWindowUpdatingResponse) {}
22
+
23
+ rpc launchApp(LaunchAppRequest) returns (LaunchAppResponse) {}
24
+
25
+ rpc addMedia(stream AddMediaRequest) returns (AddMediaResponse) {}
26
+
27
+ rpc enableMockLocationProviders(EmptyRequest) returns (EmptyResponse) {}
28
+
29
+ rpc disableLocationUpdates(EmptyRequest) returns (EmptyResponse) {}
30
+ }
31
+
32
+ message EmptyRequest {}
33
+ message EmptyResponse {}
34
+
35
+ message LaunchAppRequest {
36
+
37
+ string packageName = 1;
38
+ repeated ArgumentValue arguments = 2;
39
+ }
40
+
41
+ message ArgumentValue {
42
+ string key = 1;
43
+ string value = 2;
44
+ string type = 3;
45
+ }
46
+
47
+ message LaunchAppResponse {}
48
+
49
+ // Device info
50
+ message DeviceInfoRequest {}
51
+
52
+ message DeviceInfo {
53
+ uint32 widthPixels = 1;
54
+ uint32 heightPixels = 2;
55
+ }
56
+
57
+ message ScreenshotRequest {}
58
+
59
+ message ScreenshotResponse {
60
+ bytes bytes = 1;
61
+ }
62
+
63
+ // View hierarchy
64
+ message ViewHierarchyRequest {}
65
+
66
+ message ViewHierarchyResponse {
67
+ string hierarchy = 1;
68
+ }
69
+
70
+ // Interactions
71
+
72
+ message TapRequest {
73
+ uint32 x = 1;
74
+ uint32 y = 2;
75
+ }
76
+
77
+ message TapResponse {}
78
+
79
+ message InputTextRequest {
80
+ string text = 1;
81
+ }
82
+ message InputTextResponse {}
83
+
84
+
85
+ message EraseAllTextRequest {
86
+ uint32 charactersToErase = 1;
87
+ }
88
+
89
+ message EraseAllTextResponse {}
90
+
91
+ message SetLocationRequest {
92
+ double latitude = 1;
93
+ double longitude = 2;
94
+ }
95
+
96
+ message SetLocationResponse {}
97
+
98
+ message CheckWindowUpdatingRequest {
99
+ string appId = 1;
100
+ }
101
+
102
+ message CheckWindowUpdatingResponse {
103
+ bool isWindowUpdating = 1;
104
+ }
105
+
106
+ message AddMediaRequest {
107
+ Payload payload = 1;
108
+ string media_name = 2;
109
+ string media_ext = 3;
110
+ }
111
+
112
+ message AddMediaResponse { }
113
+
114
+ message Payload {
115
+ bytes data = 1;
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/core",
3
- "version": "1.32.0-beta.0",
3
+ "version": "1.32.0-beta.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,16 +44,20 @@
44
44
  "test:types": "tsd"
45
45
  },
46
46
  "dependencies": {
47
- "@percy/client": "1.32.0-beta.0",
48
- "@percy/config": "1.32.0-beta.0",
49
- "@percy/dom": "1.32.0-beta.0",
50
- "@percy/logger": "1.32.0-beta.0",
51
- "@percy/monitoring": "1.32.0-beta.0",
52
- "@percy/webdriver-utils": "1.32.0-beta.0",
47
+ "@grpc/grpc-js": "^1.14.3",
48
+ "@grpc/proto-loader": "^0.8.0",
49
+ "@percy/client": "1.32.0-beta.1",
50
+ "@percy/config": "1.32.0-beta.1",
51
+ "@percy/dom": "1.32.0-beta.1",
52
+ "@percy/logger": "1.32.0-beta.1",
53
+ "@percy/monitoring": "1.32.0-beta.1",
54
+ "@percy/webdriver-utils": "1.32.0-beta.1",
55
+ "busboy": "^1.6.0",
53
56
  "content-disposition": "^0.5.4",
54
57
  "cross-spawn": "^7.0.3",
55
58
  "extract-zip": "^2.0.1",
56
59
  "fast-glob": "^3.2.11",
60
+ "fast-xml-parser": "^4.4.1",
57
61
  "micromatch": "^4.0.8",
58
62
  "mime-types": "^2.1.34",
59
63
  "pako": "^2.1.0",
@@ -63,7 +67,7 @@
63
67
  "yaml": "^2.4.1"
64
68
  },
65
69
  "optionalDependencies": {
66
- "@percy/cli-doctor": "1.32.0-beta.0"
70
+ "@percy/cli-doctor": "1.32.0-beta.1"
67
71
  },
68
- "gitHead": "36c0d4a8f23b5e3ab6bdf5db5e181c1febd4b767"
72
+ "gitHead": "899783cc84e5fd1b68c701c251cd8cabcbe35fd1"
69
73
  }