@infracraft/pulumi 1.11.0 → 1.12.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.
- package/dist/hash.cjs +17 -7
- package/dist/hash.cjs.map +1 -1
- package/dist/hash.d.cts +26 -2
- package/dist/hash.d.cts.map +1 -1
- package/dist/hash.d.mts +26 -2
- package/dist/hash.d.mts.map +1 -1
- package/dist/hash.mjs +16 -7
- package/dist/hash.mjs.map +1 -1
- package/package.json +1 -1
package/dist/hash.cjs
CHANGED
|
@@ -4,6 +4,8 @@ let node_fs = require("node:fs");
|
|
|
4
4
|
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
7
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
8
|
+
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
7
9
|
let node_crypto = require("node:crypto");
|
|
8
10
|
node_crypto = require_chunk.__toESM(node_crypto, 1);
|
|
9
11
|
|
|
@@ -16,9 +18,17 @@ const DEFAULT_IGNORE = new Set([
|
|
|
16
18
|
".git",
|
|
17
19
|
".vercel"
|
|
18
20
|
]);
|
|
19
|
-
function
|
|
21
|
+
function hash(input, options) {
|
|
22
|
+
if (typeof input !== "string") {
|
|
23
|
+
const keys = Object.keys(input).sort();
|
|
24
|
+
return _pulumi_pulumi.unsecret(_pulumi_pulumi.all(keys.map((key) => input[key])).apply((values) => {
|
|
25
|
+
const digest = node_crypto.createHash("sha256");
|
|
26
|
+
for (const [index, key] of keys.entries()) digest.update(`${key}=${values[index]}\0`);
|
|
27
|
+
return digest.digest("hex");
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
20
30
|
const ignore = options?.ignore ?? DEFAULT_IGNORE;
|
|
21
|
-
const
|
|
31
|
+
const digest = node_crypto.createHash("sha256");
|
|
22
32
|
function walk(currentPath) {
|
|
23
33
|
const entries = node_fs.readdirSync(currentPath, { withFileTypes: true });
|
|
24
34
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
@@ -26,15 +36,15 @@ function hashDirectory(dirPath, options) {
|
|
|
26
36
|
const fullPath = node_path.join(currentPath, entry.name);
|
|
27
37
|
if (entry.isDirectory()) walk(fullPath);
|
|
28
38
|
else if (entry.isFile()) {
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
digest.update(entry.name);
|
|
40
|
+
digest.update(node_fs.readFileSync(fullPath));
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
|
-
walk(
|
|
35
|
-
return
|
|
44
|
+
walk(input);
|
|
45
|
+
return digest.digest("hex");
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
//#endregion
|
|
39
|
-
exports.
|
|
49
|
+
exports.hash = hash;
|
|
40
50
|
//# sourceMappingURL=hash.cjs.map
|
package/dist/hash.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.cjs","names":["crypto","fs","path"],"sources":["../src/hash.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst DEFAULT_IGNORE = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".turbo\",\n\t\".next\",\n\t\".git\",\n\t\".vercel\",\n]);\n\ninterface HashOptions {\n\tignore?: Set<string>;\n}\n\nexport function
|
|
1
|
+
{"version":3,"file":"hash.cjs","names":["pulumi","crypto","fs","path"],"sources":["../src/hash.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nconst DEFAULT_IGNORE = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".turbo\",\n\t\".next\",\n\t\".git\",\n\t\".vercel\",\n]);\n\ninterface HashOptions {\n\tignore?: Set<string>;\n}\n\n/**\n * Produces a stable SHA-256 hex digest for use as a resource/deploy trigger,\n * from either a source directory or an environment map.\n *\n * - **Directory** (`string`): recursively hashes file names + contents (build\n * and VCS directories skipped). Synchronous — returns a plain `string`.\n * - **Env map** (`Record<string, Input<string>>`): resolves the values, sorts\n * by key, and hashes them into a single non-secret `Output<string>`. Passing\n * secret `Output`s straight into a dynamic resource's inputs intermittently\n * races Pulumi's gRPC secret serialization (`Unexpected struct type`, issue\n * #16041) — the reason such deploys otherwise need `--parallel 1`. Collapsing\n * the env to one `unsecret` digest keeps the trigger moving on any change\n * while carrying no secret, so deploys are safe to (re)create at full\n * parallelism. Exposing the digest is safe: it is a one-way hash of\n * high-entropy secrets.\n *\n * @param input A source directory path, or a map of env var name to value.\n * @param options Directory mode only: `ignore` overrides the default skip set.\n * @returns `string` for a directory, `Output<string>` for an env map.\n * @example\n * triggers: [hash(appDir), hash(env)]\n */\nexport function hash(directory: string, options?: HashOptions): string;\nexport function hash(\n\tenv: Record<string, pulumi.Input<string>>,\n): pulumi.Output<string>;\nexport function hash(\n\tinput: string | Record<string, pulumi.Input<string>>,\n\toptions?: HashOptions,\n): string | pulumi.Output<string> {\n\tif (typeof input !== \"string\") {\n\t\tconst keys = Object.keys(input).sort();\n\n\t\treturn pulumi.unsecret(\n\t\t\tpulumi.all(keys.map((key) => input[key])).apply((values) => {\n\t\t\t\tconst digest = crypto.createHash(\"sha256\");\n\n\t\t\t\tfor (const [index, key] of keys.entries()) {\n\t\t\t\t\tdigest.update(`${key}=${values[index]}\\0`);\n\t\t\t\t}\n\n\t\t\t\treturn digest.digest(\"hex\");\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst ignore = options?.ignore ?? DEFAULT_IGNORE;\n\tconst digest = crypto.createHash(\"sha256\");\n\n\tfunction walk(currentPath: string) {\n\t\tconst entries = fs.readdirSync(currentPath, { withFileTypes: true });\n\n\t\tfor (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n\t\t\tif (ignore.has(entry.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = path.join(currentPath, entry.name);\n\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\twalk(fullPath);\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\tdigest.update(entry.name);\n\t\t\t\tdigest.update(fs.readFileSync(fullPath));\n\t\t\t}\n\t\t}\n\t}\n\n\twalk(input);\n\n\treturn digest.digest(\"hex\");\n}\n"],"mappings":";;;;;;;;;;;;AAKA,MAAM,iBAAiB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAgCD,SAAgB,KACf,OACA,SACiC;CACjC,IAAI,OAAO,UAAU,UAAU;EAC9B,MAAM,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK;EAErC,OAAOA,eAAO,SACbA,eAAO,IAAI,KAAK,KAAK,QAAQ,MAAM,IAAI,CAAC,EAAE,OAAO,WAAW;GAC3D,MAAM,SAASC,YAAO,WAAW,QAAQ;GAEzC,KAAK,MAAM,CAAC,OAAO,QAAQ,KAAK,QAAQ,GACvC,OAAO,OAAO,GAAG,IAAI,GAAG,OAAO,OAAO,GAAG;GAG1C,OAAO,OAAO,OAAO,KAAK;EAC3B,CAAC,CACF;CACD;CAEA,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,SAASA,YAAO,WAAW,QAAQ;CAEzC,SAAS,KAAK,aAAqB;EAClC,MAAM,UAAUC,QAAG,YAAY,aAAa,EAAE,eAAe,KAAK,CAAC;EAEnE,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;GACzE,IAAI,OAAO,IAAI,MAAM,IAAI,GACxB;GAGD,MAAM,WAAWC,UAAK,KAAK,aAAa,MAAM,IAAI;GAElD,IAAI,MAAM,YAAY,GACrB,KAAK,QAAQ;QACP,IAAI,MAAM,OAAO,GAAG;IAC1B,OAAO,OAAO,MAAM,IAAI;IACxB,OAAO,OAAOD,QAAG,aAAa,QAAQ,CAAC;GACxC;EACD;CACD;CAEA,KAAK,KAAK;CAEV,OAAO,OAAO,OAAO,KAAK;AAC3B"}
|
package/dist/hash.d.cts
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-OPjESj5l.cjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
2
3
|
|
|
3
4
|
//#region src/hash.d.ts
|
|
4
5
|
interface HashOptions {
|
|
5
6
|
ignore?: Set<string>;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Produces a stable SHA-256 hex digest for use as a resource/deploy trigger,
|
|
10
|
+
* from either a source directory or an environment map.
|
|
11
|
+
*
|
|
12
|
+
* - **Directory** (`string`): recursively hashes file names + contents (build
|
|
13
|
+
* and VCS directories skipped). Synchronous — returns a plain `string`.
|
|
14
|
+
* - **Env map** (`Record<string, Input<string>>`): resolves the values, sorts
|
|
15
|
+
* by key, and hashes them into a single non-secret `Output<string>`. Passing
|
|
16
|
+
* secret `Output`s straight into a dynamic resource's inputs intermittently
|
|
17
|
+
* races Pulumi's gRPC secret serialization (`Unexpected struct type`, issue
|
|
18
|
+
* #16041) — the reason such deploys otherwise need `--parallel 1`. Collapsing
|
|
19
|
+
* the env to one `unsecret` digest keeps the trigger moving on any change
|
|
20
|
+
* while carrying no secret, so deploys are safe to (re)create at full
|
|
21
|
+
* parallelism. Exposing the digest is safe: it is a one-way hash of
|
|
22
|
+
* high-entropy secrets.
|
|
23
|
+
*
|
|
24
|
+
* @param input A source directory path, or a map of env var name to value.
|
|
25
|
+
* @param options Directory mode only: `ignore` overrides the default skip set.
|
|
26
|
+
* @returns `string` for a directory, `Output<string>` for an env map.
|
|
27
|
+
* @example
|
|
28
|
+
* triggers: [hash(appDir), hash(env)]
|
|
29
|
+
*/
|
|
30
|
+
declare function hash(directory: string, options?: HashOptions): string;
|
|
31
|
+
declare function hash(env: Record<string, pulumi.Input<string>>): pulumi.Output<string>;
|
|
8
32
|
//#endregion
|
|
9
|
-
export {
|
|
33
|
+
export { hash };
|
|
10
34
|
//# sourceMappingURL=hash.d.cts.map
|
package/dist/hash.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.d.cts","names":[],"sources":["../src/hash.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"hash.d.cts","names":[],"sources":["../src/hash.ts"],"mappings":";;;;UAcU,WAAA;EACT,MAAA,GAAS,GAAG;AAAA;AAZ4B;;;;AAY5B;AAyBb;;;;;;;;AAA6D;AAC7D;;;;;;;;AAtCyC,iBAqCzB,IAAA,CAAK,SAAA,UAAmB,OAAA,GAAU,WAAW;AAAA,iBAC7C,IAAA,CACf,GAAA,EAAK,MAAA,SAAe,MAAA,CAAO,KAAA,YACzB,MAAA,CAAO,MAAA"}
|
package/dist/hash.d.mts
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-OPjESj5l.mjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
2
3
|
|
|
3
4
|
//#region src/hash.d.ts
|
|
4
5
|
interface HashOptions {
|
|
5
6
|
ignore?: Set<string>;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Produces a stable SHA-256 hex digest for use as a resource/deploy trigger,
|
|
10
|
+
* from either a source directory or an environment map.
|
|
11
|
+
*
|
|
12
|
+
* - **Directory** (`string`): recursively hashes file names + contents (build
|
|
13
|
+
* and VCS directories skipped). Synchronous — returns a plain `string`.
|
|
14
|
+
* - **Env map** (`Record<string, Input<string>>`): resolves the values, sorts
|
|
15
|
+
* by key, and hashes them into a single non-secret `Output<string>`. Passing
|
|
16
|
+
* secret `Output`s straight into a dynamic resource's inputs intermittently
|
|
17
|
+
* races Pulumi's gRPC secret serialization (`Unexpected struct type`, issue
|
|
18
|
+
* #16041) — the reason such deploys otherwise need `--parallel 1`. Collapsing
|
|
19
|
+
* the env to one `unsecret` digest keeps the trigger moving on any change
|
|
20
|
+
* while carrying no secret, so deploys are safe to (re)create at full
|
|
21
|
+
* parallelism. Exposing the digest is safe: it is a one-way hash of
|
|
22
|
+
* high-entropy secrets.
|
|
23
|
+
*
|
|
24
|
+
* @param input A source directory path, or a map of env var name to value.
|
|
25
|
+
* @param options Directory mode only: `ignore` overrides the default skip set.
|
|
26
|
+
* @returns `string` for a directory, `Output<string>` for an env map.
|
|
27
|
+
* @example
|
|
28
|
+
* triggers: [hash(appDir), hash(env)]
|
|
29
|
+
*/
|
|
30
|
+
declare function hash(directory: string, options?: HashOptions): string;
|
|
31
|
+
declare function hash(env: Record<string, pulumi.Input<string>>): pulumi.Output<string>;
|
|
8
32
|
//#endregion
|
|
9
|
-
export {
|
|
33
|
+
export { hash };
|
|
10
34
|
//# sourceMappingURL=hash.d.mts.map
|
package/dist/hash.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.d.mts","names":[],"sources":["../src/hash.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"hash.d.mts","names":[],"sources":["../src/hash.ts"],"mappings":";;;;UAcU,WAAA;EACT,MAAA,GAAS,GAAG;AAAA;AAZ4B;;;;AAY5B;AAyBb;;;;;;;;AAA6D;AAC7D;;;;;;;;AAtCyC,iBAqCzB,IAAA,CAAK,SAAA,UAAmB,OAAA,GAAU,WAAW;AAAA,iBAC7C,IAAA,CACf,GAAA,EAAK,MAAA,SAAe,MAAA,CAAO,KAAA,YACzB,MAAA,CAAO,MAAA"}
|
package/dist/hash.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-OPjESj5l.mjs";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
4
5
|
import * as crypto from "node:crypto";
|
|
5
6
|
|
|
6
7
|
//#region src/hash.ts
|
|
@@ -12,9 +13,17 @@ const DEFAULT_IGNORE = new Set([
|
|
|
12
13
|
".git",
|
|
13
14
|
".vercel"
|
|
14
15
|
]);
|
|
15
|
-
function
|
|
16
|
+
function hash(input, options) {
|
|
17
|
+
if (typeof input !== "string") {
|
|
18
|
+
const keys = Object.keys(input).sort();
|
|
19
|
+
return pulumi.unsecret(pulumi.all(keys.map((key) => input[key])).apply((values) => {
|
|
20
|
+
const digest = crypto.createHash("sha256");
|
|
21
|
+
for (const [index, key] of keys.entries()) digest.update(`${key}=${values[index]}\0`);
|
|
22
|
+
return digest.digest("hex");
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
16
25
|
const ignore = options?.ignore ?? DEFAULT_IGNORE;
|
|
17
|
-
const
|
|
26
|
+
const digest = crypto.createHash("sha256");
|
|
18
27
|
function walk(currentPath) {
|
|
19
28
|
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
20
29
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
@@ -22,15 +31,15 @@ function hashDirectory(dirPath, options) {
|
|
|
22
31
|
const fullPath = path.join(currentPath, entry.name);
|
|
23
32
|
if (entry.isDirectory()) walk(fullPath);
|
|
24
33
|
else if (entry.isFile()) {
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
digest.update(entry.name);
|
|
35
|
+
digest.update(fs.readFileSync(fullPath));
|
|
27
36
|
}
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
|
-
walk(
|
|
31
|
-
return
|
|
39
|
+
walk(input);
|
|
40
|
+
return digest.digest("hex");
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
//#endregion
|
|
35
|
-
export {
|
|
44
|
+
export { hash };
|
|
36
45
|
//# sourceMappingURL=hash.mjs.map
|
package/dist/hash.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.mjs","names":[],"sources":["../src/hash.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst DEFAULT_IGNORE = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".turbo\",\n\t\".next\",\n\t\".git\",\n\t\".vercel\",\n]);\n\ninterface HashOptions {\n\tignore?: Set<string>;\n}\n\nexport function
|
|
1
|
+
{"version":3,"file":"hash.mjs","names":[],"sources":["../src/hash.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nconst DEFAULT_IGNORE = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".turbo\",\n\t\".next\",\n\t\".git\",\n\t\".vercel\",\n]);\n\ninterface HashOptions {\n\tignore?: Set<string>;\n}\n\n/**\n * Produces a stable SHA-256 hex digest for use as a resource/deploy trigger,\n * from either a source directory or an environment map.\n *\n * - **Directory** (`string`): recursively hashes file names + contents (build\n * and VCS directories skipped). Synchronous — returns a plain `string`.\n * - **Env map** (`Record<string, Input<string>>`): resolves the values, sorts\n * by key, and hashes them into a single non-secret `Output<string>`. Passing\n * secret `Output`s straight into a dynamic resource's inputs intermittently\n * races Pulumi's gRPC secret serialization (`Unexpected struct type`, issue\n * #16041) — the reason such deploys otherwise need `--parallel 1`. Collapsing\n * the env to one `unsecret` digest keeps the trigger moving on any change\n * while carrying no secret, so deploys are safe to (re)create at full\n * parallelism. Exposing the digest is safe: it is a one-way hash of\n * high-entropy secrets.\n *\n * @param input A source directory path, or a map of env var name to value.\n * @param options Directory mode only: `ignore` overrides the default skip set.\n * @returns `string` for a directory, `Output<string>` for an env map.\n * @example\n * triggers: [hash(appDir), hash(env)]\n */\nexport function hash(directory: string, options?: HashOptions): string;\nexport function hash(\n\tenv: Record<string, pulumi.Input<string>>,\n): pulumi.Output<string>;\nexport function hash(\n\tinput: string | Record<string, pulumi.Input<string>>,\n\toptions?: HashOptions,\n): string | pulumi.Output<string> {\n\tif (typeof input !== \"string\") {\n\t\tconst keys = Object.keys(input).sort();\n\n\t\treturn pulumi.unsecret(\n\t\t\tpulumi.all(keys.map((key) => input[key])).apply((values) => {\n\t\t\t\tconst digest = crypto.createHash(\"sha256\");\n\n\t\t\t\tfor (const [index, key] of keys.entries()) {\n\t\t\t\t\tdigest.update(`${key}=${values[index]}\\0`);\n\t\t\t\t}\n\n\t\t\t\treturn digest.digest(\"hex\");\n\t\t\t}),\n\t\t);\n\t}\n\n\tconst ignore = options?.ignore ?? DEFAULT_IGNORE;\n\tconst digest = crypto.createHash(\"sha256\");\n\n\tfunction walk(currentPath: string) {\n\t\tconst entries = fs.readdirSync(currentPath, { withFileTypes: true });\n\n\t\tfor (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n\t\t\tif (ignore.has(entry.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = path.join(currentPath, entry.name);\n\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\twalk(fullPath);\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\tdigest.update(entry.name);\n\t\t\t\tdigest.update(fs.readFileSync(fullPath));\n\t\t\t}\n\t\t}\n\t}\n\n\twalk(input);\n\n\treturn digest.digest(\"hex\");\n}\n"],"mappings":";;;;;;;AAKA,MAAM,iBAAiB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAgCD,SAAgB,KACf,OACA,SACiC;CACjC,IAAI,OAAO,UAAU,UAAU;EAC9B,MAAM,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK;EAErC,OAAO,OAAO,SACb,OAAO,IAAI,KAAK,KAAK,QAAQ,MAAM,IAAI,CAAC,EAAE,OAAO,WAAW;GAC3D,MAAM,SAAS,OAAO,WAAW,QAAQ;GAEzC,KAAK,MAAM,CAAC,OAAO,QAAQ,KAAK,QAAQ,GACvC,OAAO,OAAO,GAAG,IAAI,GAAG,OAAO,OAAO,GAAG;GAG1C,OAAO,OAAO,OAAO,KAAK;EAC3B,CAAC,CACF;CACD;CAEA,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,SAAS,OAAO,WAAW,QAAQ;CAEzC,SAAS,KAAK,aAAqB;EAClC,MAAM,UAAU,GAAG,YAAY,aAAa,EAAE,eAAe,KAAK,CAAC;EAEnE,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;GACzE,IAAI,OAAO,IAAI,MAAM,IAAI,GACxB;GAGD,MAAM,WAAW,KAAK,KAAK,aAAa,MAAM,IAAI;GAElD,IAAI,MAAM,YAAY,GACrB,KAAK,QAAQ;QACP,IAAI,MAAM,OAAO,GAAG;IAC1B,OAAO,OAAO,MAAM,IAAI;IACxB,OAAO,OAAO,GAAG,aAAa,QAAQ,CAAC;GACxC;EACD;CACD;CAEA,KAAK,KAAK;CAEV,OAAO,OAAO,OAAO,KAAK;AAC3B"}
|