@tamagui/native-ci 2.7.7 → 3.0.0-beta.637.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/android-utils.mjs +62 -0
- package/dist/android-utils.mjs.map +1 -0
- package/dist/cache.mjs +54 -70
- package/dist/cache.mjs.map +1 -1
- package/dist/cli.mjs +378 -403
- package/dist/cli.mjs.map +1 -1
- package/dist/constants.mjs +3 -2
- package/dist/constants.mjs.map +1 -1
- package/dist/deps.mjs +52 -64
- package/dist/deps.mjs.map +1 -1
- package/dist/detox.mjs +220 -202
- package/dist/detox.mjs.map +1 -1
- package/dist/fingerprint.mjs +35 -33
- package/dist/fingerprint.mjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.mjs +10 -10
- package/dist/ios-utils.mjs +318 -0
- package/dist/ios-utils.mjs.map +1 -0
- package/dist/metro.mjs +137 -139
- package/dist/metro.mjs.map +1 -1
- package/dist/runner.mjs +62 -65
- package/dist/runner.mjs.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +3 -3
- package/src/constants.ts +1 -1
- package/src/detox.ts +1 -0
- package/src/index.ts +2 -2
- package/src/metro.ts +2 -2
- package/src/run-detox-android.ts +1 -1
- package/src/run-detox-ios.ts +1 -1
- package/types/{android.d.ts → android-utils.d.ts} +1 -1
- package/types/android-utils.d.ts.map +1 -0
- package/types/cli.d.ts +1 -1
- package/types/constants.d.ts +2 -2
- package/types/detox.d.ts.map +1 -1
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/{ios.d.ts → ios-utils.d.ts} +1 -1
- package/types/ios-utils.d.ts.map +1 -0
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/types/android.d.ts.map +0 -1
- package/types/ios.d.ts.map +0 -1
- /package/src/{android.ts → android-utils.ts} +0 -0
- /package/src/{ios.ts → ios-utils.ts} +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { $ } from "bun";
|
|
4
|
+
import { DETOX_SERVER_PORT, METRO_PORT } from "./constants.mjs";
|
|
5
|
+
|
|
6
|
+
async function waitForDevice() {
|
|
7
|
+
console.info("\n--- Waiting for device ---");
|
|
8
|
+
try {
|
|
9
|
+
const result = await $`adb devices`.quiet();
|
|
10
|
+
const lines = result.stdout.toString().split("\n").filter((line) => line.includes(" device"));
|
|
11
|
+
if (lines.length === 0) {
|
|
12
|
+
throw new Error("No Android device/emulator connected. Please start an emulator first.");
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const err = error;
|
|
16
|
+
throw new Error(`No Android device available: ${err.message}`);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await $`timeout 30 adb wait-for-device`.quiet();
|
|
20
|
+
await $`timeout 60 adb shell 'while [ -z "$(getprop sys.boot_completed)" ]; do sleep 1; done'`.quiet();
|
|
21
|
+
console.info("Device is ready!");
|
|
22
|
+
} catch (error) {
|
|
23
|
+
const err = error;
|
|
24
|
+
throw new Error(`Failed to wait for Android device: ${err.message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function setupAdbReverse() {
|
|
28
|
+
console.info("\n--- Setting up ADB reverse ---");
|
|
29
|
+
try {
|
|
30
|
+
await $`adb reverse tcp:${METRO_PORT} tcp:${METRO_PORT}`;
|
|
31
|
+
console.info(`Reversed port ${METRO_PORT} (Metro)`);
|
|
32
|
+
await $`adb reverse tcp:${DETOX_SERVER_PORT} tcp:${DETOX_SERVER_PORT}`;
|
|
33
|
+
console.info(`Reversed port ${DETOX_SERVER_PORT} (Detox)`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const err = error;
|
|
36
|
+
throw new Error(`Failed to setup ADB reverse ports: ${err.message}
|
|
37
|
+
Make sure the Android emulator is running and adb is available.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function setupAndroidDevice() {
|
|
41
|
+
await waitForDevice();
|
|
42
|
+
await setupAdbReverse();
|
|
43
|
+
}
|
|
44
|
+
async function ensureAndroidFolder() {
|
|
45
|
+
const buildGradlePath = join(process.cwd(), "android", "build.gradle");
|
|
46
|
+
if (existsSync(buildGradlePath)) {
|
|
47
|
+
console.info("Android folder already exists (build.gradle found)");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.info("\n--- Generating android/ folder (for Metro) ---");
|
|
51
|
+
console.info("Note: android/build.gradle not found, running expo prebuild");
|
|
52
|
+
try {
|
|
53
|
+
await $`npx expo prebuild --platform android`;
|
|
54
|
+
console.info("Android folder generated!");
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const err = error;
|
|
57
|
+
throw new Error(`Failed to generate android folder: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { ensureAndroidFolder, setupAdbReverse, setupAndroidDevice, waitForDevice };
|
|
62
|
+
//# sourceMappingURL=android-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-utils.js","names":[],"sources":["android-utils.js"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { $ } from \"bun\";\nimport { METRO_PORT, DETOX_SERVER_PORT } from \"./constants\";\nasync function waitForDevice() {\n console.info(\"\\n--- Waiting for device ---\");\n try {\n const result = await $`adb devices`.quiet();\n const lines = result.stdout.toString().split(\"\\n\").filter((line) => line.includes(\"\tdevice\"));\n if (lines.length === 0) {\n throw new Error(\n \"No Android device/emulator connected. Please start an emulator first.\"\n );\n }\n } catch (error) {\n const err = error;\n throw new Error(`No Android device available: ${err.message}`);\n }\n try {\n await $`timeout 30 adb wait-for-device`.quiet();\n await $`timeout 60 adb shell 'while [ -z \"$(getprop sys.boot_completed)\" ]; do sleep 1; done'`.quiet();\n console.info(\"Device is ready!\");\n } catch (error) {\n const err = error;\n throw new Error(`Failed to wait for Android device: ${err.message}`);\n }\n}\nasync function setupAdbReverse() {\n console.info(\"\\n--- Setting up ADB reverse ---\");\n try {\n await $`adb reverse tcp:${METRO_PORT} tcp:${METRO_PORT}`;\n console.info(`Reversed port ${METRO_PORT} (Metro)`);\n await $`adb reverse tcp:${DETOX_SERVER_PORT} tcp:${DETOX_SERVER_PORT}`;\n console.info(`Reversed port ${DETOX_SERVER_PORT} (Detox)`);\n } catch (error) {\n const err = error;\n throw new Error(\n `Failed to setup ADB reverse ports: ${err.message}\nMake sure the Android emulator is running and adb is available.`\n );\n }\n}\nasync function setupAndroidDevice() {\n await waitForDevice();\n await setupAdbReverse();\n}\nasync function ensureAndroidFolder() {\n const buildGradlePath = join(process.cwd(), \"android\", \"build.gradle\");\n if (existsSync(buildGradlePath)) {\n console.info(\"Android folder already exists (build.gradle found)\");\n return;\n }\n console.info(\"\\n--- Generating android/ folder (for Metro) ---\");\n console.info(\"Note: android/build.gradle not found, running expo prebuild\");\n try {\n await $`npx expo prebuild --platform android`;\n console.info(\"Android folder generated!\");\n } catch (error) {\n const err = error;\n throw new Error(`Failed to generate android folder: ${err.message}`);\n }\n}\nexport {\n ensureAndroidFolder,\n setupAdbReverse,\n setupAndroidDevice,\n waitForDevice\n};\n//# sourceMappingURL=android-utils.js.map\n"],"mappings":";;;;;;AAIA,eAAe,gBAAgB;CAC7B,QAAQ,KAAK,8BAA8B;CAC3C,IAAI;EACF,MAAM,SAAS,MAAM,CAAC,cAAc,MAAM;EAC1C,MAAM,QAAQ,OAAO,OAAO,SAAS,EAAE,MAAM,IAAI,EAAE,QAAQ,SAAS,KAAK,SAAS,SAAS,CAAC;EAC5F,IAAI,MAAM,WAAW,GAAG;GACtB,MAAM,IAAI,MACR,uEACF;EACF;CACF,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,MAAM,IAAI,MAAM,gCAAgC,IAAI,SAAS;CAC/D;CACA,IAAI;EACF,MAAM,CAAC,iCAAiC,MAAM;EAC9C,MAAM,CAAC,wFAAwF,MAAM;EACrG,QAAQ,KAAK,kBAAkB;CACjC,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,MAAM,IAAI,MAAM,sCAAsC,IAAI,SAAS;CACrE;AACF;AACA,eAAe,kBAAkB;CAC/B,QAAQ,KAAK,kCAAkC;CAC/C,IAAI;EACF,MAAM,CAAC,mBAAmB,WAAW,OAAO;EAC5C,QAAQ,KAAK,iBAAiB,WAAW,SAAS;EAClD,MAAM,CAAC,mBAAmB,kBAAkB,OAAO;EACnD,QAAQ,KAAK,iBAAiB,kBAAkB,SAAS;CAC3D,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,MAAM,IAAI,MACR,sCAAsC,IAAI,QAAQ;gEAEpD;CACF;AACF;AACA,eAAe,qBAAqB;CAClC,MAAM,cAAc;CACpB,MAAM,gBAAgB;AACxB;AACA,eAAe,sBAAsB;CACnC,MAAM,kBAAkB,KAAK,QAAQ,IAAI,GAAG,WAAW,cAAc;CACrE,IAAI,WAAW,eAAe,GAAG;EAC/B,QAAQ,KAAK,oDAAoD;EACjE;CACF;CACA,QAAQ,KAAK,kDAAkD;CAC/D,QAAQ,KAAK,6DAA6D;CAC1E,IAAI;EACF,MAAM,CAAC;EACP,QAAQ,KAAK,2BAA2B;CAC1C,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,MAAM,IAAI,MAAM,sCAAsC,IAAI,SAAS;CACrE;AACF"}
|
package/dist/cache.mjs
CHANGED
|
@@ -1,86 +1,70 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { DEFAULT_KV_TTL_SECONDS } from "./constants.mjs";
|
|
4
|
+
|
|
4
5
|
function createCacheKey(options) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
fingerprint,
|
|
8
|
-
prefix = "native-build"
|
|
9
|
-
} = options;
|
|
10
|
-
return `${prefix}-${platform}-${fingerprint}`;
|
|
6
|
+
const { platform, fingerprint, prefix = "native-build" } = options;
|
|
7
|
+
return `${prefix}-${platform}-${fingerprint}`;
|
|
11
8
|
}
|
|
12
9
|
async function saveFingerprintToKV(kv, key, fingerprint, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
throw new Error(`Failed to save fingerprint to KV: ${error.message}`);
|
|
28
|
-
}
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(`${kv.url}/SETEX/${key}/${ttlSeconds}/${fingerprint}`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: { Authorization: `Bearer ${kv.token}` }
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error instanceof TypeError) {
|
|
20
|
+
throw new Error(`Network error connecting to KV store: ${error.message}`);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Failed to save fingerprint to KV: ${error.message}`);
|
|
23
|
+
}
|
|
29
24
|
}
|
|
30
25
|
async function getFingerprintFromKV(kv, key) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
throw new Error(`Network error connecting to KV store: ${error.message}`);
|
|
45
|
-
}
|
|
46
|
-
throw new Error(`Failed to get fingerprint from KV: ${error.message}`);
|
|
47
|
-
}
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(`${kv.url}/get/${key}`, { headers: { Authorization: `Bearer ${kv.token}` } });
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
30
|
+
}
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
return data.result === "null" ? null : data.result;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error instanceof TypeError) {
|
|
35
|
+
throw new Error(`Network error connecting to KV store: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Failed to get fingerprint from KV: ${error.message}`);
|
|
38
|
+
}
|
|
48
39
|
}
|
|
49
40
|
async function extendKVTTL(kv, key, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
console.warn(`Failed to extend KV TTL: ${error.message}`);
|
|
59
|
-
}
|
|
41
|
+
try {
|
|
42
|
+
await fetch(`${kv.url}/EXPIRE/${key}/${ttlSeconds}`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { Authorization: `Bearer ${kv.token}` }
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.warn(`Failed to extend KV TTL: ${error.message}`);
|
|
48
|
+
}
|
|
60
49
|
}
|
|
61
50
|
function saveCache(filePath, data, options = {}) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
mkdirSync(dirname(cachePath), {
|
|
67
|
-
recursive: true
|
|
68
|
-
});
|
|
69
|
-
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
51
|
+
const { cacheDir } = options;
|
|
52
|
+
const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
|
|
53
|
+
mkdirSync(dirname(cachePath), { recursive: true });
|
|
54
|
+
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
70
55
|
}
|
|
71
56
|
function loadCache(filePath, options = {}) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
57
|
+
const { cacheDir } = options;
|
|
58
|
+
const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
|
|
59
|
+
if (!existsSync(cachePath)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
84
67
|
}
|
|
68
|
+
|
|
85
69
|
export { createCacheKey, extendKVTTL, getFingerprintFromKV, loadCache, saveCache, saveFingerprintToKV };
|
|
86
70
|
//# sourceMappingURL=cache.mjs.map
|
package/dist/cache.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"file":"cache.js","names":[],"sources":["cache.js"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { DEFAULT_KV_TTL_SECONDS } from \"./constants\";\nfunction createCacheKey(options) {\n const { platform, fingerprint, prefix = \"native-build\" } = options;\n return `${prefix}-${platform}-${fingerprint}`;\n}\nasync function saveFingerprintToKV(kv, key, fingerprint, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {\n try {\n const response = await fetch(`${kv.url}/SETEX/${key}/${ttlSeconds}/${fingerprint}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${kv.token}`\n }\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error(`Network error connecting to KV store: ${error.message}`);\n }\n throw new Error(`Failed to save fingerprint to KV: ${error.message}`);\n }\n}\nasync function getFingerprintFromKV(kv, key) {\n try {\n const response = await fetch(`${kv.url}/get/${key}`, {\n headers: {\n Authorization: `Bearer ${kv.token}`\n }\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const data = await response.json();\n return data.result === \"null\" ? null : data.result;\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error(`Network error connecting to KV store: ${error.message}`);\n }\n throw new Error(`Failed to get fingerprint from KV: ${error.message}`);\n }\n}\nasync function extendKVTTL(kv, key, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {\n try {\n await fetch(`${kv.url}/EXPIRE/${key}/${ttlSeconds}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${kv.token}`\n }\n });\n } catch (error) {\n console.warn(`Failed to extend KV TTL: ${error.message}`);\n }\n}\nfunction saveCache(filePath, data, options = {}) {\n const { cacheDir } = options;\n const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);\n mkdirSync(dirname(cachePath), { recursive: true });\n writeFileSync(cachePath, JSON.stringify(data, null, 2));\n}\nfunction loadCache(filePath, options = {}) {\n const { cacheDir } = options;\n const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);\n if (!existsSync(cachePath)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(cachePath, \"utf-8\"));\n } catch {\n return null;\n }\n}\nexport {\n createCacheKey,\n extendKVTTL,\n getFingerprintFromKV,\n loadCache,\n saveCache,\n saveFingerprintToKV\n};\n//# sourceMappingURL=cache.js.map\n"],"mappings":";;;;;AAGA,SAAS,eAAe,SAAS;CAC/B,MAAM,EAAE,UAAU,aAAa,SAAS,mBAAmB;CAC3D,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG;AAClC;AACA,eAAe,oBAAoB,IAAI,KAAK,aAAa,aAAa,wBAAwB;CAC5F,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,GAAG,GAAG,IAAI,SAAS,IAAI,GAAG,WAAW,GAAG,eAAe;GAClF,QAAQ;GACR,SAAS,EACP,eAAe,UAAU,GAAG,QAC9B;EACF,CAAC;EACD,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,IAAI,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,YAAY;EACnE;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,WAAW;GAC9B,MAAM,IAAI,MAAM,yCAAyC,MAAM,SAAS;EAC1E;EACA,MAAM,IAAI,MAAM,qCAAqC,MAAM,SAAS;CACtE;AACF;AACA,eAAe,qBAAqB,IAAI,KAAK;CAC3C,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,GAAG,GAAG,IAAI,OAAO,OAAO,EACnD,SAAS,EACP,eAAe,UAAU,GAAG,QAC9B,EACF,CAAC;EACD,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,IAAI,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,YAAY;EACnE;EACA,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,OAAO,KAAK,WAAW,SAAS,OAAO,KAAK;CAC9C,SAAS,OAAO;EACd,IAAI,iBAAiB,WAAW;GAC9B,MAAM,IAAI,MAAM,yCAAyC,MAAM,SAAS;EAC1E;EACA,MAAM,IAAI,MAAM,sCAAsC,MAAM,SAAS;CACvE;AACF;AACA,eAAe,YAAY,IAAI,KAAK,aAAa,wBAAwB;CACvE,IAAI;EACF,MAAM,MAAM,GAAG,GAAG,IAAI,UAAU,IAAI,GAAG,cAAc;GACnD,QAAQ;GACR,SAAS,EACP,eAAe,UAAU,GAAG,QAC9B;EACF,CAAC;CACH,SAAS,OAAO;EACd,QAAQ,KAAK,4BAA4B,MAAM,SAAS;CAC1D;AACF;AACA,SAAS,UAAU,UAAU,MAAM,UAAU,CAAC,GAAG;CAC/C,MAAM,EAAE,aAAa;CACrB,MAAM,YAAY,WAAW,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG,QAAQ;CACnG,UAAU,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,cAAc,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACxD;AACA,SAAS,UAAU,UAAU,UAAU,CAAC,GAAG;CACzC,MAAM,EAAE,aAAa;CACrB,MAAM,YAAY,WAAW,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG,QAAQ;CACnG,IAAI,CAAC,WAAW,SAAS,GAAG;EAC1B,OAAO;CACT;CACA,IAAI;EACF,OAAO,KAAK,MAAM,aAAa,WAAW,OAAO,CAAC;CACpD,QAAQ;EACN,OAAO;CACT;AACF"}
|