@invarn/cibuild 2.8.0 → 2.8.2

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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts","sourceRoot":"","sources":["../../../src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,354 @@
1
+ /**
2
+ * An empty properties file fixes "file missing", not "field missing".
3
+ *
4
+ * The Secrets Gradle plugin turns each key of the properties file it loads
5
+ * into a `BuildConfig` constant and a manifest placeholder. A checkout without
6
+ * that file gets neither, so handing the build an empty stand-in clears the
7
+ * plugin's own throw and then fails one step later, inside Gradle, at the first
8
+ * reference to a constant that was never generated.
9
+ *
10
+ * `missingPropertyKeys` is the list a stand-in has to declare for that
11
+ * reference to resolve: what the build reads, read off the build files and the
12
+ * source under the modules that apply the plugin.
13
+ */
14
+ import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { tmpdir } from "node:os";
17
+ import { scanAndroidProject } from "./android-scanner.js";
18
+ let root;
19
+ beforeEach(() => {
20
+ root = mkdtempSync(join(tmpdir(), "cibuild-props-"));
21
+ });
22
+ afterEach(() => {
23
+ rmSync(root, { recursive: true, force: true });
24
+ });
25
+ function write(relative, content) {
26
+ const path = join(root, relative);
27
+ mkdirSync(join(path, ".."), { recursive: true });
28
+ writeFileSync(path, content);
29
+ }
30
+ async function keys() {
31
+ return (await scanAndroidProject(root)).missingPropertyKeys;
32
+ }
33
+ /**
34
+ * The shape the whole thing is for: one module applying the plugin, no
35
+ * properties file anywhere, and the root declaring the plugin without
36
+ * applying it — which is where a version catalog alias is nearly always named.
37
+ */
38
+ function projectApplyingTheSecretsPlugin(appBuildFileExtra = "") {
39
+ write("settings.gradle.kts", 'include(":app")\n');
40
+ write("build.gradle.kts", "plugins {\n alias(libs.plugins.secrets) apply false\n}\n");
41
+ write("app/build.gradle.kts", `plugins {\n id("com.android.application")\n alias(libs.plugins.secrets)\n}\n\nandroid {\n namespace = "com.example.app"\n${appBuildFileExtra}}\n`);
42
+ write(".gitignore", "local.properties\n");
43
+ }
44
+ describe("the keys a generated properties file has to declare", () => {
45
+ test("names a BuildConfig field referenced from Kotlin source", async () => {
46
+ projectApplyingTheSecretsPlugin();
47
+ write("app/src/main/java/com/example/app/Maps.kt", "package com.example.app\n\nval key: String = BuildConfig.MAPS_API_KEY\n");
48
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
49
+ });
50
+ test("names one referenced from Java source", async () => {
51
+ projectApplyingTheSecretsPlugin();
52
+ write("app/src/main/java/com/example/app/Maps.java", "package com.example.app;\n\nclass Maps { String k = BuildConfig.MAPS_API_KEY; }\n");
53
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
54
+ });
55
+ test("names a manifestPlaceholders key — Kotlin DSL, indexed", async () => {
56
+ projectApplyingTheSecretsPlugin(' defaultConfig {\n manifestPlaceholders["mapsApiKey"] = "unused"\n }\n');
57
+ expect(await keys()).toEqual(["mapsApiKey"]);
58
+ });
59
+ test("names a manifestPlaceholders key — Kotlin DSL, whole map", async () => {
60
+ projectApplyingTheSecretsPlugin(' defaultConfig {\n manifestPlaceholders += mapOf("mapsApiKey" to "u", "adsAppId" to "u")\n }\n');
61
+ expect(await keys()).toEqual(["adsAppId", "mapsApiKey"]);
62
+ });
63
+ test("names a manifestPlaceholders key — Groovy map literal", async () => {
64
+ write("settings.gradle", "include ':app'\n");
65
+ write("build.gradle", "// root\n");
66
+ write("app/build.gradle", "plugins {\n id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'\n}\n" +
67
+ "android {\n defaultConfig {\n" +
68
+ " manifestPlaceholders = [mapsApiKey: \"unused\", 'adsAppId': \"unused\"]\n" +
69
+ " }\n}\n");
70
+ expect(await keys()).toEqual(["adsAppId", "mapsApiKey"]);
71
+ });
72
+ test("names a manifestPlaceholders key — Groovy put", async () => {
73
+ write("settings.gradle", "include ':app'\n");
74
+ write("build.gradle", "// root\n");
75
+ write("app/build.gradle", "plugins {\n id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'\n}\n" +
76
+ "android {\n defaultConfig {\n" +
77
+ " manifestPlaceholders.put('mapsApiKey', 'unused')\n" +
78
+ " }\n}\n");
79
+ expect(await keys()).toEqual(["mapsApiKey"]);
80
+ });
81
+ test("merges both sources, sorted and deduplicated", async () => {
82
+ projectApplyingTheSecretsPlugin(' defaultConfig {\n manifestPlaceholders["MAPS_API_KEY"] = "u"\n }\n');
83
+ write("app/src/main/java/com/example/app/Keys.kt", "val a = BuildConfig.MAPS_API_KEY\nval b = BuildConfig.ADS_APP_ID\nval c = BuildConfig.MAPS_API_KEY\n");
84
+ expect(await keys()).toEqual(["ADS_APP_ID", "MAPS_API_KEY"]);
85
+ });
86
+ });
87
+ describe("what it deliberately leaves out", () => {
88
+ /**
89
+ * `BuildConfig.DEBUG` is the most-referenced constant in Android source, and
90
+ * AGP generates it whether or not a properties file exists. Declaring it
91
+ * would make the Secrets plugin generate a `String DEBUG` beside AGP's
92
+ * `boolean DEBUG` — the file meant to rescue the build breaking it instead.
93
+ */
94
+ test("the fields AGP generates by itself", async () => {
95
+ projectApplyingTheSecretsPlugin();
96
+ write("app/src/main/java/com/example/app/Env.kt", [
97
+ "val d = BuildConfig.DEBUG",
98
+ "val a = BuildConfig.APPLICATION_ID",
99
+ "val t = BuildConfig.BUILD_TYPE",
100
+ "val f = BuildConfig.FLAVOR",
101
+ "val fd = BuildConfig.FLAVOR_environment",
102
+ "val vc = BuildConfig.VERSION_CODE",
103
+ "val vn = BuildConfig.VERSION_NAME",
104
+ "val lp = BuildConfig.LIBRARY_PACKAGE_NAME",
105
+ "val mine = BuildConfig.MAPS_API_KEY",
106
+ ].join("\n"));
107
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
108
+ });
109
+ test("a project that applies no Secrets plugin", async () => {
110
+ write("settings.gradle.kts", 'include(":app")\n');
111
+ write("build.gradle.kts", "// root\n");
112
+ write("app/build.gradle.kts", 'plugins {\n id("com.android.application")\n}\n');
113
+ write("app/src/main/java/com/example/app/Maps.kt", "val k = BuildConfig.MAPS_API_KEY\n");
114
+ expect(await keys()).toEqual([]);
115
+ });
116
+ /**
117
+ * `apply false` names the plugin's version for the modules that will apply
118
+ * it; the root project loads no properties file of its own. Reading it as an
119
+ * application would aim the source walk at the whole repository — every
120
+ * module's source, on the strength of a line that applies nothing.
121
+ */
122
+ test("a root that declares the plugin with apply false", async () => {
123
+ write("settings.gradle.kts", 'include(":app", ":other")\n');
124
+ write("build.gradle.kts", "plugins {\n alias(libs.plugins.secrets) apply false\n}\n");
125
+ write("app/build.gradle.kts", 'plugins {\n id("com.android.application")\n}\n');
126
+ write("other/build.gradle.kts", "// nothing\n");
127
+ write("other/src/main/java/Other.kt", "val k = BuildConfig.ELSEWHERE\n");
128
+ expect(await keys()).toEqual([]);
129
+ });
130
+ test("generated sources under build/", async () => {
131
+ projectApplyingTheSecretsPlugin();
132
+ write("app/build/generated/source/buildConfig/debug/com/example/app/BuildConfig.java", "public final class BuildConfig { public static final String STALE = BuildConfig.STALE; }\n");
133
+ write("app/src/main/java/com/example/app/Maps.kt", "val k = BuildConfig.MAPS_API_KEY\n");
134
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
135
+ });
136
+ });
137
+ describe("the module boundary", () => {
138
+ test("reads source of the module that applies the plugin, not its siblings", async () => {
139
+ write("settings.gradle.kts", 'include(":app", ":other")\n');
140
+ write("build.gradle.kts", "// root\n");
141
+ write("app/build.gradle.kts", 'plugins {\n alias(libs.plugins.secrets)\n}\n');
142
+ write("app/src/main/java/App.kt", "val k = BuildConfig.MAPS_API_KEY\n");
143
+ write("other/build.gradle.kts", "// nothing\n");
144
+ write("other/src/main/java/Other.kt", "val k = BuildConfig.ELSEWHERE\n");
145
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
146
+ });
147
+ test("a root module that really applies the plugin covers the whole tree", async () => {
148
+ write("settings.gradle.kts", 'include(":app")\n');
149
+ write("build.gradle.kts", 'plugins {\n id("com.android.application")\n alias(libs.plugins.secrets)\n}\n');
150
+ write("app/build.gradle.kts", "// nothing\n");
151
+ write("app/src/main/java/App.kt", "val k = BuildConfig.MAPS_API_KEY\n");
152
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
153
+ });
154
+ });
155
+ /**
156
+ * The warnings a customer already sees. This scan adds a field and reads more
157
+ * files; it must not add, drop or reword a single warning, so the two paths
158
+ * that emit a `secrets-plugin` warning are asserted whole.
159
+ */
160
+ describe("the Secrets-plugin warning path is unchanged", () => {
161
+ test("with no companion defaults file, the one file-absent warning", async () => {
162
+ projectApplyingTheSecretsPlugin();
163
+ write("app/src/main/java/com/example/app/Maps.kt", "val k = BuildConfig.MAPS_API_KEY\n");
164
+ const result = await scanAndroidProject(root);
165
+ expect(result.warnings.filter((w) => w.category === "secrets-plugin")).toEqual([
166
+ {
167
+ category: "secrets-plugin",
168
+ severity: "warning",
169
+ message: "Google Secrets Gradle Plugin reads 'local.properties' which is absent in CI",
170
+ hint: "Inject 'local.properties' as a file step using a secret variable",
171
+ // The root build file, which is where the version-catalog alias is
172
+ // named with `apply false`. The warning path counts that as an
173
+ // application and always has; `missingPropertyKeys` does not, which is
174
+ // why the two ask the question through different functions.
175
+ location: "build.gradle.kts",
176
+ },
177
+ ]);
178
+ // And the new field is populated in the same scan, from the same files.
179
+ expect(result.missingPropertyKeys).toEqual(["MAPS_API_KEY"]);
180
+ });
181
+ test("with a companion defaults file, one warning per expected key", async () => {
182
+ projectApplyingTheSecretsPlugin();
183
+ write("secrets.defaults.properties", "MAPS_API_KEY=\nADS_APP_ID=\n");
184
+ write("app/src/main/java/com/example/app/Maps.kt", "val k = BuildConfig.MAPS_API_KEY\n");
185
+ const result = await scanAndroidProject(root);
186
+ expect(result.warnings.filter((w) => w.category === "secrets-plugin")).toEqual([
187
+ {
188
+ category: "secrets-plugin",
189
+ severity: "warning",
190
+ message: "Secrets plugin key: MAPS_API_KEY",
191
+ hint: "Add via: ci secrets add MAPS_API_KEY",
192
+ location: "local.properties",
193
+ },
194
+ {
195
+ category: "secrets-plugin",
196
+ severity: "warning",
197
+ message: "Secrets plugin key: ADS_APP_ID",
198
+ hint: "Add via: ci secrets add ADS_APP_ID",
199
+ location: "local.properties",
200
+ },
201
+ ]);
202
+ });
203
+ test("a properties file that is present but not gitignored warns about neither", async () => {
204
+ write("settings.gradle.kts", 'include(":app")\n');
205
+ write("build.gradle.kts", "// root\n");
206
+ write("app/build.gradle.kts", "plugins {\n alias(libs.plugins.secrets)\n}\n");
207
+ write("local.properties", "sdk.dir=/opt/android\n");
208
+ write("app/src/main/java/App.kt", "val k = BuildConfig.MAPS_API_KEY\n");
209
+ const result = await scanAndroidProject(root);
210
+ expect(result.warnings.filter((w) => w.category === "secrets-plugin")).toEqual([]);
211
+ // The keys are still reported. `invarn`-style callers run this scan on a
212
+ // developer's own checkout, where the file is nearly always present and
213
+ // holds nothing but `sdk.dir` — gating the keys on its absence would
214
+ // return nothing in exactly the case they are needed for.
215
+ expect(result.missingPropertyKeys).toEqual(["MAPS_API_KEY"]);
216
+ });
217
+ });
218
+ /**
219
+ * The commonest shape of all, and the one the first three sources missed.
220
+ *
221
+ * The Secrets plugin's own documentation, and every Google Maps setup guide,
222
+ * puts the key in the manifest and nowhere else. There is no `BuildConfig`
223
+ * reference and no `manifestPlaceholders` block — the plugin substitutes the
224
+ * value during manifest merging. A scan reading only build files and source
225
+ * returns nothing for such a repository, which is the majority of them.
226
+ */
227
+ describe("a key referenced only in AndroidManifest.xml", () => {
228
+ test("names a placeholder the plugin fills", async () => {
229
+ projectApplyingTheSecretsPlugin();
230
+ write("app/src/main/AndroidManifest.xml", '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' +
231
+ " <application>\n" +
232
+ ' <meta-data android:name="com.google.android.geo.API_KEY"\n' +
233
+ ' android:value="${MAPS_API_KEY}" />\n' +
234
+ " </application>\n</manifest>\n");
235
+ expect(await keys()).toEqual(["MAPS_API_KEY"]);
236
+ });
237
+ // AGP supplies this one from the variant, so it is not a value anybody owes.
238
+ test("ignores ${applicationId}", async () => {
239
+ projectApplyingTheSecretsPlugin();
240
+ write("app/src/main/AndroidManifest.xml", '<manifest>\n <provider android:authorities="${applicationId}.provider" />\n</manifest>\n');
241
+ expect(await keys()).toEqual([]);
242
+ });
243
+ test("finds them in flavor source sets too", async () => {
244
+ projectApplyingTheSecretsPlugin();
245
+ write("app/src/main/AndroidManifest.xml", '<manifest a="${BASE_KEY}" />\n');
246
+ write("app/src/prod/AndroidManifest.xml", '<manifest a="${PROD_KEY}" />\n');
247
+ expect(await keys()).toEqual(["BASE_KEY", "PROD_KEY"]);
248
+ });
249
+ test("skips a merged manifest under build/", async () => {
250
+ projectApplyingTheSecretsPlugin();
251
+ write("app/src/main/AndroidManifest.xml", '<manifest a="${REAL_KEY}" />\n');
252
+ write("app/build/intermediates/merged_manifest/AndroidManifest.xml", '<manifest a="${STALE}" />\n');
253
+ expect(await keys()).toEqual(["REAL_KEY"]);
254
+ });
255
+ });
256
+ /**
257
+ * A build that loads the properties file itself, with no Secrets plugin
258
+ * anywhere. The file existing is not the fix for these — only the file
259
+ * existing *with the key in it* is, because `getProperty` on an absent key
260
+ * returns null whether or not the file is there.
261
+ */
262
+ describe("a build that reads the properties file directly", () => {
263
+ test("names a getProperty key, with no plugin applied", async () => {
264
+ write("settings.gradle.kts", 'include(":app")\n');
265
+ write("build.gradle.kts", "// root\n");
266
+ write("app/build.gradle.kts", 'import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties\n' +
267
+ 'val url: String = gradleLocalProperties(rootDir).getProperty("api.url.release")\n');
268
+ expect(await keys()).toEqual(["api.url.release"]);
269
+ });
270
+ test("keeps dotted keys, which a direct reader may use freely", async () => {
271
+ write("settings.gradle", "include ':app'\n");
272
+ write("build.gradle", "// root\n");
273
+ write("app/build.gradle", "def props = new Properties()\n" +
274
+ "props.load(rootProject.file('local.properties').newDataInputStream())\n" +
275
+ "def a = props.getProperty('api.url.debug')\n" +
276
+ "def b = props.getProperty('tv.isDebug')\n");
277
+ expect(await keys()).toEqual(["api.url.debug", "tv.isDebug"]);
278
+ });
279
+ test("honours a default argument without treating it as a key", async () => {
280
+ write("settings.gradle.kts", 'include(":app")\n');
281
+ write("build.gradle.kts", "// root\n");
282
+ write("app/build.gradle.kts", 'val p = java.util.Properties()\n' +
283
+ 'p.load(rootProject.file("local.properties").reader())\n' +
284
+ 'val u = p.getProperty("api.url.release", "")\n');
285
+ expect(await keys()).toEqual(["api.url.release"]);
286
+ });
287
+ /**
288
+ * The idiom that hides every key behind a name the repository chose. Without
289
+ * following it, a repository like this reads as having no keys at all.
290
+ */
291
+ test("follows a hand-rolled accessor to its call sites", async () => {
292
+ write("settings.gradle", "include ':app'\n");
293
+ write("build.gradle", "// root\n");
294
+ write("app/build.gradle", "def getProps(String propName) {\n" +
295
+ " def propsFile = rootProject.file('local.properties')\n" +
296
+ " if (propsFile.exists()) {\n" +
297
+ " def props = new Properties()\n" +
298
+ " props.load(new FileInputStream(propsFile))\n" +
299
+ " return props[propName]\n" +
300
+ " } else {\n" +
301
+ " return \"\";\n" +
302
+ " }\n" +
303
+ "}\n" +
304
+ "\n" +
305
+ "String signFilePath = getProps(\"sign.file\")\n" +
306
+ "Boolean noObfuscate = getProps(\"app.dontobfuscate\")?.toBoolean() ?: false\n");
307
+ expect(await keys()).toEqual(["app.dontobfuscate", "sign.file"]);
308
+ });
309
+ test("does not follow a function that reads something else", async () => {
310
+ write("settings.gradle", "include ':app'\n");
311
+ write("build.gradle", "// root\n");
312
+ write("app/build.gradle", "def getProps(String n) {\n" +
313
+ " def p = new Properties()\n" +
314
+ " p.load(rootProject.file('local.properties').newDataInputStream())\n" +
315
+ " return p[n]\n" +
316
+ "}\n" +
317
+ "def version(String n) {\n" +
318
+ " def p = new Properties()\n" +
319
+ " p.load(rootProject.file('version.properties').newDataInputStream())\n" +
320
+ " return p[n]\n" +
321
+ "}\n" +
322
+ "def a = getProps('wanted')\n" +
323
+ "def b = version('unwanted')\n");
324
+ expect(await keys()).toEqual(["wanted"]);
325
+ });
326
+ /**
327
+ * `properties["x"]` is the Gradle *project* property channel —
328
+ * `gradle.properties` and `-P` — which the scan already reports separately.
329
+ * Declaring those in a properties file would be the wrong file entirely.
330
+ */
331
+ test("does not confuse Gradle project properties for properties-file keys", async () => {
332
+ write("settings.gradle.kts", 'include(":app")\n');
333
+ write("build.gradle.kts", "// root\n");
334
+ write("app/build.gradle.kts", 'val p = java.util.Properties()\n' +
335
+ 'p.load(rootProject.file("local.properties").reader())\n' +
336
+ 'val a = p.getProperty("wanted")\n' +
337
+ 'val b = properties["projectScoped"]\n' +
338
+ 'val c = findProperty("alsoProjectScoped")\n');
339
+ expect(await keys()).toEqual(["wanted"]);
340
+ });
341
+ /** A developer's own SDK path is not a value anybody owes the build. */
342
+ test("never names a machine-local key", async () => {
343
+ write("settings.gradle.kts", 'include(":app")\n');
344
+ write("build.gradle.kts", "// root\n");
345
+ write("app/build.gradle.kts", 'val p = java.util.Properties()\n' +
346
+ 'p.load(rootProject.file("local.properties").reader())\n' +
347
+ 'val sdk = p.getProperty("sdk.dir")\n' +
348
+ 'val ndk = p.getProperty("ndk.dir")\n' +
349
+ 'val cmake = p.getProperty("cmake.dir")\n' +
350
+ 'val real = p.getProperty("api.key")\n');
351
+ expect(await keys()).toEqual(["api.key"]);
352
+ });
353
+ });
354
+ //# sourceMappingURL=a-properties-stand-in-declares-the-keys-the-build-reads.test.js.map
@@ -25,6 +25,28 @@ export interface ScanResult {
25
25
  detectedGradleVersion?: string;
26
26
  /** Build types, product flavors, and all variant combinations detected in Gradle files. */
27
27
  buildVariants: BuildVariants;
28
+ /**
29
+ * The property names the build reads out of the properties file the Secrets
30
+ * Gradle plugin loads — sorted, deduplicated, empty when no module applies
31
+ * the plugin.
32
+ *
33
+ * **An empty properties file fixes "file missing", not "field missing".**
34
+ * The plugin turns each key of that file into a `BuildConfig` constant and a
35
+ * manifest placeholder, so a checkout without the file compiles neither.
36
+ * Supplying an empty stand-in gets the build past the plugin's own throw and
37
+ * then fails one step later, inside Gradle, at the first reference to a field
38
+ * that was never generated. These are those names: what a stand-in has to
39
+ * declare for the reference to resolve.
40
+ *
41
+ * Data, not prose — deliberately not more `warnings`. A warning is a sentence
42
+ * for a human to read; this is a list for a generator to consume, and sharing
43
+ * one channel between them means the generator has to parse English.
44
+ *
45
+ * Coarse on purpose. A name here that some other plugin turns out to
46
+ * generate costs one unused line in a properties file; a name missing from
47
+ * here costs the build.
48
+ */
49
+ missingPropertyKeys: string[];
28
50
  }
29
51
  export interface BuildVariants {
30
52
  /** Build types found, e.g. ["debug", "release", "staging"]. Always includes debug + release. */
@@ -1 +1 @@
1
- {"version":3,"file":"android-scanner.d.ts","sourceRoot":"","sources":["../../../src/commands/android-scanner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"android-scanner.d.ts","sourceRoot":"","sources":["../../../src/commands/android-scanner.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjD,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,SAAS,GACT,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,GACd,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,uFAAuF;IACvF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,2FAA2F;IAC3F,aAAa,EAAE,aAAa,CAAC;IAC7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,gGAAgG;IAChG,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,wFAAwF;IACxF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAikBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAcnE;AAyKD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAO7D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA8CjD;AA2BD;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG7E;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAsBvF;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIrF;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uBAAuB,UAAsB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,QAAuC,CAAC;AAElF,yEAAyE;AACzE,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,WAAW,GACX,mBAAmB,GACnB,eAAe,GACf,uBAAuB,CAAC;AAE5B,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,eAAe,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,iBAAiB,CAC/B,mBAAmB,EAAE,MAAM,GAAG,SAAS,EACvC,aAAa,CAAC,EAAE,MAAM,GACrB,iBAAiB,CAkCnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,MAAM,GAAG,SAAS,EACvC,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,mBAAmB,EAAE,MAAM,GAAG,SAAS,EACvC,aAAa,CAAC,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,CAwBpB;AAwLD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,aAAa,CAsCxE;AAMD,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA8RjF;AAgBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAqE3D"}