jumpy-lion 1.0.0-beta.4 → 1.0.0-beta.5

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,168 @@
1
+ /**
2
+ * The Google-only request-header integrity inputs.
3
+ *
4
+ * Real Chrome sends four headers to Google properties, gated by
5
+ * `google_util::IsGoogleAssociatedDomainUrl`:
6
+ *
7
+ * X-Browser-Channel: stable
8
+ * X-Browser-Year: 2026
9
+ * X-Browser-Copyright: Copyright 2026 Google LLC. All Rights Reserved.
10
+ * X-Browser-Validation: base64(sha1(kIntegritySeed + apiKey + userAgent))
11
+ *
12
+ * Only the last one needs data we do not have by default, and it needs two things
13
+ * an unbranded build gets wrong:
14
+ *
15
+ * - `kIntegritySeed` lives in a Google-internal DEPS submodule, so ours is "".
16
+ * It also ROTATES EVERY CHROME RELEASE, so it cannot be a compiled constant -
17
+ * it has to be supplied per session, matched to the version the session claims.
18
+ * - `google_apis::GetAPIKey()` returns OUR build's key, not the key Google bakes
19
+ * into a macOS Chrome. Per ADR 0004 the digest must use the key of the CLAIMED
20
+ * platform, so it is injected too.
21
+ *
22
+ * The user agent needs nothing: `embedder_support::GetUserAgent()` honours
23
+ * `--user-agent`, so the C++ side hashes exactly the string we advertise.
24
+ *
25
+ * Seeds are harvested from a real Chrome by `scripts/harvest-chrome-integrity.py`
26
+ * (see the harvest-chrome-integrity skill). A seed only enters this table once it
27
+ * has reproduced a real captured header byte for byte.
28
+ */
29
+ /** Platform-specific Google API keys, as compiled into real branded Chrome. */
30
+ export const CHROME_API_KEYS = {
31
+ macos: 'AIzaSyDr2UxVnv_U85AbhhY8XSHSIavUW0DC-sY',
32
+ windows: 'AIzaSyA2KlwBX3mkFo30om9LUFYQhpqLoa_BNhE',
33
+ linux: 'AIzaSyBqJZh-7pA44blAaAkH6490hUFOwX0KCYM',
34
+ };
35
+ /**
36
+ * Verified seeds, keyed by the full Chrome version they came from.
37
+ *
38
+ * ONLY ADD A ROW THAT HAS BEEN VERIFIED. The harvester reproduces a real captured
39
+ * (user agent, digest) pair before reporting a seed as confirmed, and an unverified
40
+ * value must never land here: a missing header set leaves us indistinguishable from
41
+ * every Chromium build in the world, while a header carrying a wrong digest is
42
+ * impossible-valued and unique.
43
+ *
44
+ * 151.0.7922.108 is verified:
45
+ * base64(sha1('59d2629a8246b372' + macosKey + reducedUA)) = YphvkVZHC4/QbFYqn/v4wI6lmx0=
46
+ * which is exactly the header captured from a real Chrome 151.0.7922.108 / macOS 26.5.2.
47
+ *
48
+ * The other two were extracted by the same anchored method but have no capture yet,
49
+ * so they are recorded as unverified and are NOT eligible for use.
50
+ */
51
+ export const VERIFIED_INTEGRITY_PROFILES = {
52
+ '151.0.7922.108': {
53
+ version: '151.0.7922.108',
54
+ seed: '59d2629a8246b372',
55
+ year: '2026',
56
+ copyright: 'Copyright 2026 Google LLC. All Rights Reserved.',
57
+ },
58
+ };
59
+ /** Extracted but NOT verified against a real capture. Do not ship these. */
60
+ export const UNVERIFIED_SEEDS = {
61
+ '151.0.7922.110': '05d8c431b39bdc9f',
62
+ '151.0.7922.138': '669f97eb3c38549b',
63
+ };
64
+ /**
65
+ * The version we advertise when integrity headers are enabled.
66
+ *
67
+ * This is the crux of the coupling. The REDUCED user agent carries no patch level
68
+ * (`Chrome/151.0.0.0`), so it is identical across every 151.x build and the seed is
69
+ * the only version-specific input to the digest. But `sec-ch-ua-full-version` DOES
70
+ * carry the patch level, and Google can recompute the digest using the seed for
71
+ * whichever version we claim there.
72
+ *
73
+ * So the claimed full version and the seed must come from the same build. Our binary
74
+ * is 151.0.7922.72 and no seed exists for it (that build is neither installed
75
+ * locally nor in Google's deb pool), which is why enabling this feature requires
76
+ * advertising a version we hold a seed for. Nothing on the wire reveals the true
77
+ * binary version.
78
+ */
79
+ export const INTEGRITY_CLAIMED_VERSION = '151.0.7922.108';
80
+ /**
81
+ * Build the launch switches, or return an empty list when we cannot do it correctly.
82
+ *
83
+ * Returning nothing is a deliberate, safe outcome rather than a failure: the patched
84
+ * C++ side emits no headers at all when the switches are absent, which leaves us in
85
+ * the large crowd of Chromium builds that send none. Emitting a digest built from a
86
+ * mismatched seed, or the wrong platform's key, would be unique and therefore worse.
87
+ */
88
+ export function buildIntegrityHeaderArgs(options) {
89
+ const profile = VERIFIED_INTEGRITY_PROFILES[options.claimedVersion];
90
+ if (!profile) {
91
+ const unverified = UNVERIFIED_SEEDS[options.claimedVersion];
92
+ return {
93
+ args: [],
94
+ skipped: unverified
95
+ ? `a seed for ${options.claimedVersion} was extracted but never verified against a real capture`
96
+ : `no verified integrity seed for claimed version ${options.claimedVersion}`,
97
+ };
98
+ }
99
+ const apiKey = CHROME_API_KEYS[options.claimedPlatform];
100
+ if (!apiKey) {
101
+ return { args: [], skipped: `no Chrome API key known for claimed platform ${options.claimedPlatform}` };
102
+ }
103
+ return {
104
+ args: [
105
+ `--integrity-seed=${profile.seed}`,
106
+ `--integrity-api-key=${apiKey}`,
107
+ `--integrity-year=${profile.year}`,
108
+ `--integrity-copyright=${profile.copyright}`,
109
+ ],
110
+ };
111
+ }
112
+ /**
113
+ * Build the `integrity` block for the session's JSON config, and align the claimed
114
+ * full version to the seed we actually hold.
115
+ *
116
+ * THE COUPLING THIS RESOLVES. The reduced user agent carries no patch level
117
+ * (`Chrome/151.0.0.0`), so it is identical across every 151.x build. But
118
+ * `sec-ch-ua-full-version` and `fullVersionList` DO carry it, and Google can
119
+ * recompute the digest with the seed belonging to whichever version we advertise
120
+ * there. So the advertised full version and the seed must come from the same build.
121
+ *
122
+ * Our binary is 151.0.7922.72 and no seed exists for it - that build is neither
123
+ * installed locally nor still in Google's deb pool - so alignFingerprintConfigWithBinary
124
+ * leaves the config advertising a version we cannot produce a digest for. This
125
+ * rewrites the advertised version to the one the seed belongs to. Nothing on the
126
+ * wire reveals the binary's true version, so claiming .108 while running .72 is
127
+ * consistent to every observer; claiming .72 while sending .108's digest is not.
128
+ *
129
+ * Mutates the config in place and returns whether it did anything, so the caller can
130
+ * log the decision rather than silently shipping a mismatch.
131
+ */
132
+ export function applyIntegrityToConfig(config, claimedPlatform, version = INTEGRITY_CLAIMED_VERSION) {
133
+ const profile = VERIFIED_INTEGRITY_PROFILES[version];
134
+ if (!profile) {
135
+ return {
136
+ applied: false,
137
+ reason: UNVERIFIED_SEEDS[version]
138
+ ? `seed for ${version} is extracted but unverified, so it must not ship`
139
+ : `no verified integrity seed for ${version}`,
140
+ };
141
+ }
142
+ const apiKey = CHROME_API_KEYS[claimedPlatform];
143
+ if (!apiKey) {
144
+ return { applied: false, reason: `no Chrome API key for claimed platform ${claimedPlatform}` };
145
+ }
146
+ config.integrity = {
147
+ seed: profile.seed,
148
+ apiKey,
149
+ year: profile.year,
150
+ copyright: profile.copyright,
151
+ };
152
+ // Align every place the patch level is advertised, not just uaFullVersion:
153
+ // fullVersionList is sent as sec-ch-ua-full-version-list and would otherwise
154
+ // disagree with it, which is its own contradiction.
155
+ const uaData = config.navigator?.userAgentData;
156
+ if (uaData) {
157
+ uaData.uaFullVersion = profile.version;
158
+ if (Array.isArray(uaData.fullVersionList)) {
159
+ const major = profile.version.split('.')[0];
160
+ uaData.fullVersionList = uaData.fullVersionList.map((entry) => ({
161
+ ...entry,
162
+ version: /^\d+\.0\.0\.0$/.test(entry.version) ? `${major}.0.0.0` : profile.version,
163
+ }));
164
+ }
165
+ }
166
+ return { applied: true };
167
+ }
168
+ //# sourceMappingURL=integrity-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrity-headers.js","sourceRoot":"","sources":["../../src/browser-process/integrity-headers.ts"],"names":[],"mappings":"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"}
@@ -54,6 +54,52 @@ export declare function resolvePlatformVersion(platform: string | undefined, see
54
54
  */
55
55
  export declare function deriveRealisticDeviceMemory(seed: number, rawFromFingerprint: number | undefined): number;
56
56
  export declare function deriveStorageQuota(seed: number, deviceMemoryGB: number): number;
57
+ /**
58
+ * The frame clock a session claims: refresh rate, the panel's own deviation
59
+ * from its nominal rate, and how much each vsync wanders around the grid.
60
+ *
61
+ * All three exist because a container has no panel. Chromium then falls back to
62
+ * `BeginFrameArgs::DefaultInterval()`, which is `base::Seconds(1) / 60` and
63
+ * truncates on the microsecond field to 16666 us - 60.0024 Hz, delivered by a
64
+ * delay-based timer that snaps every tick back onto the same grid. Measured in
65
+ * the Apify Linux container: fitted period 16666 us exactly, and a median
66
+ * absolute deviation of the inter-frame deltas of exactly 0, stable across
67
+ * seven repetitions. The same probe on a real Mac reads a period of 8336 us
68
+ * (nominal ProMotion is 8333.33) with an MAD of 100 us.
69
+ *
70
+ * Zero deviation is the part that cannot be explained away. A slow container is
71
+ * innocent; a session claiming macOS whose display produces a mathematically
72
+ * perfect grid is claiming a panel that has never been manufactured. See #164.
73
+ *
74
+ * - `refreshRateHz` - 60 for every desktop profile. Deliberately NOT the 120 Hz
75
+ * of a ProMotion MacBook Pro even though the reference Mac has one: the
76
+ * compositor has to actually deliver the frames it claims, and a container
77
+ * that advertises 120 Hz while dropping half of them is more detectable than
78
+ * one that advertises the 60 Hz an Air, an iMac or any external display runs
79
+ * at. Left as a field so a host that can sustain it can say so.
80
+ * - `intervalSkewPpm` - the panel's constant error against its nominal rate.
81
+ * The reference Mac's 8336 us against a nominal 8333.33 is +320 ppm. Without
82
+ * this the fitted period lands on exactly 1e6/refreshRateHz, which is its own
83
+ * signature: a real crystal is never exact.
84
+ * - `vsyncJitterUs` - standard deviation of the per-frame wander, in
85
+ * microseconds. The binary applies it to the tick TARGET, so the frame is
86
+ * both scheduled and timestamped late-or-early together; jittering only the
87
+ * reported timestamp would leave `performance.now()` inside the callback on
88
+ * the honest grid and simply move the contradiction.
89
+ */
90
+ export interface DisplayTimingProfile {
91
+ refreshRateHz: number;
92
+ intervalSkewPpm: number;
93
+ vsyncJitterUs: number;
94
+ }
95
+ /**
96
+ * The frame clock for a session, deterministic in the seed so the same session
97
+ * replays the same panel on every host.
98
+ *
99
+ * Independent digits of the seed drive the two values so a profile cannot have
100
+ * its skew predict its jitter.
101
+ */
102
+ export declare function deriveDisplayTiming(seed: number): DisplayTimingProfile;
57
103
  /**
58
104
  * Stable non-negative hash of a session key, for paths that hold a seed STRING
59
105
  * (the JS injector's proxy session key) rather than the numeric seed the native
@@ -1 +1 @@
1
- {"version":3,"file":"hardware-realism.d.ts","sourceRoot":"","sources":["../../src/fingerprinting/hardware-realism.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA0BH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAInF,CAAC;AAKF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CASxG;AAwBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,GACb,MAAM,CAOR"}
1
+ {"version":3,"file":"hardware-realism.d.ts","sourceRoot":"","sources":["../../src/fingerprinting/hardware-realism.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA0BH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAInF,CAAC;AAKF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CASxG;AAwBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACzB;AAiCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAiBtE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,GACb,MAAM,CAOR"}
@@ -122,6 +122,57 @@ export function deriveStorageQuota(seed, deviceMemoryGB) {
122
122
  void deviceMemoryGB;
123
123
  return CHROME_STORAGE_QUOTA_BYTES;
124
124
  }
125
+ /**
126
+ * Refresh rate claimed by every desktop profile. See DisplayTimingProfile for
127
+ * why this is 60 and not the reference Mac's 120.
128
+ */
129
+ const DESKTOP_REFRESH_RATE_HZ = 60;
130
+ /**
131
+ * Bounds on the per-session panel error, in ppm. The magnitude is at least
132
+ * MIN so the fitted period never lands back on the nominal value, and at most
133
+ * MAX because a display that is 0.1% off its rated rate would be a warranty
134
+ * case. +320 ppm was measured on the reference Mac.
135
+ */
136
+ const SKEW_PPM_MIN = 120;
137
+ const SKEW_PPM_MAX = 520;
138
+ /**
139
+ * Bounds on the per-vsync wander, as a standard deviation in microseconds.
140
+ *
141
+ * The upper bound matters for correctness, not only for realism: the binary
142
+ * caps the applied offset at 3 sigma, and Chromium's own double-tick guard in
143
+ * `DelayBasedTimeSource::PostNextTickTask` tolerates a tick arriving up to
144
+ * 500 us early. 3 x 160 = 480 us stays inside that, so an early frame is still
145
+ * recognised as the frame it is instead of being pushed a whole interval out.
146
+ *
147
+ * The range is also where the reference Mac sits: an MAD of 100 us, which is
148
+ * one quantum of the 100 us clamp `performance.now()` and the rAF timestamp are
149
+ * both rounded to, so the underlying deviation is of that order.
150
+ */
151
+ const VSYNC_JITTER_US_MIN = 110;
152
+ const VSYNC_JITTER_US_MAX = 160;
153
+ /**
154
+ * The frame clock for a session, deterministic in the seed so the same session
155
+ * replays the same panel on every host.
156
+ *
157
+ * Independent digits of the seed drive the two values so a profile cannot have
158
+ * its skew predict its jitter.
159
+ */
160
+ export function deriveDisplayTiming(seed) {
161
+ const base = Math.abs(Math.trunc(Number.isFinite(seed) ? seed : 0));
162
+ const skewPick = base % 1000;
163
+ const jitterPick = Math.trunc(base / 1000) % 1000;
164
+ const skewMagnitude = SKEW_PPM_MIN
165
+ + Math.round((SKEW_PPM_MAX - SKEW_PPM_MIN) * (skewPick % 500) / 499);
166
+ // Panels run fast or slow with equal frequency; the sign is a separate draw
167
+ // so the magnitude distribution is not folded onto one side of nominal.
168
+ const sign = skewPick < 500 ? 1 : -1;
169
+ return {
170
+ refreshRateHz: DESKTOP_REFRESH_RATE_HZ,
171
+ intervalSkewPpm: sign * skewMagnitude,
172
+ vsyncJitterUs: VSYNC_JITTER_US_MIN
173
+ + Math.round((VSYNC_JITTER_US_MAX - VSYNC_JITTER_US_MIN) * jitterPick / 999),
174
+ };
175
+ }
125
176
  /**
126
177
  * Stable non-negative hash of a session key, for paths that hold a seed STRING
127
178
  * (the JS injector's proxy session key) rather than the numeric seed the native
@@ -1 +1 @@
1
- {"version":3,"file":"hardware-realism.js","sourceRoot":"","sources":["../../src/fingerprinting/hardware-realism.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hardware-realism.js","sourceRoot":"","sources":["../../src/fingerprinting/hardware-realism.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,mBAAmB,GAAgC;IACrD,CAAC,CAAC,EAAE,EAAE,CAAC;IACP,CAAC,EAAE,EAAE,EAAE,CAAC;IACR,CAAC,EAAE,EAAE,EAAE,CAAC;IACR,CAAC,EAAE,EAAE,EAAE,CAAC;IACR,CAAC,EAAE,EAAE,EAAE,CAAC;IACR,CAAC,EAAE,EAAE,EAAE,CAAC;IACR,CAAC,GAAG,EAAE,GAAG,CAAC;CACb,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAgD;IACpF,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;IACjD,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAClF,cAAc,EAAE,CAAC,EAAE,CAAC;CACvB,CAAC;AAEF,kGAAkG;AAClG,MAAM,6BAA6B,GAAG,OAAO,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA4B,EAAE,IAAY;IAC7E,MAAM,UAAU,GAAG,2BAA2B,CAAC,QAAQ,IAAI,EAAE,CAAC;WACvD,2BAA2B,CAAC,6BAA6B,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC;IACzF,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAY,EAAE,kBAAsC;IAC5F,IAAI,OAAO,kBAAkB,KAAK,QAAQ,IAAI,oBAAoB,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACzF,OAAO,kBAAkB,CAAC;IAC9B,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IAC1E,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,mBAAmB,EAAE,CAAC;QACjD,IAAI,IAAI,GAAG,UAAU;YAAE,OAAO,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,0BAA0B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,cAAsB;IACnE,KAAK,IAAI,CAAC;IACV,KAAK,cAAc,CAAC;IACpB,OAAO,0BAA0B,CAAC;AACtC,CAAC;AAyCD;;;GAGG;AACH,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB;;;;;;;;;;;;GAYG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAElD,MAAM,aAAa,GAAG,YAAY;UAC5B,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IACzE,4EAA4E;IAC5E,wEAAwE;IACxE,MAAM,IAAI,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAErC,OAAO;QACH,aAAa,EAAE,uBAAuB;QACtC,eAAe,EAAE,IAAI,GAAG,aAAa;QACrC,aAAa,EAAE,mBAAmB;cAC5B,IAAI,CAAC,KAAK,CAAC,CAAC,mBAAmB,GAAG,mBAAmB,CAAC,GAAG,UAAU,GAAG,GAAG,CAAC;KACnF,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC5C,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACpC,QAA4B,EAC5B,YAAgC,EAChC,IAAY;IAEZ,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;QAC1C,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,sBAAsB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}