@hsuite/smart-engines-cli 1.2.1 → 1.4.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.
- package/README.md +272 -58
- package/dist/_lib/attestation.d.ts +45 -0
- package/dist/_lib/attestation.d.ts.map +1 -0
- package/dist/_lib/attestation.js +76 -0
- package/dist/_lib/attestation.js.map +1 -0
- package/dist/_lib/baas.d.ts +147 -0
- package/dist/_lib/baas.d.ts.map +1 -0
- package/dist/_lib/baas.js +260 -0
- package/dist/_lib/baas.js.map +1 -0
- package/dist/_lib/cluster.d.ts +6 -6
- package/dist/_lib/cluster.js +6 -6
- package/dist/_lib/deploy-manifest.d.ts +69 -0
- package/dist/_lib/deploy-manifest.d.ts.map +1 -0
- package/dist/_lib/deploy-manifest.js +87 -0
- package/dist/_lib/deploy-manifest.js.map +1 -0
- package/dist/_lib/docker.d.ts +82 -0
- package/dist/_lib/docker.d.ts.map +1 -0
- package/dist/_lib/docker.js +164 -0
- package/dist/_lib/docker.js.map +1 -0
- package/dist/_lib/faucet-client.d.ts +64 -0
- package/dist/_lib/faucet-client.d.ts.map +1 -0
- package/dist/_lib/faucet-client.js +92 -0
- package/dist/_lib/faucet-client.js.map +1 -0
- package/dist/_lib/framework.d.ts +59 -0
- package/dist/_lib/framework.d.ts.map +1 -0
- package/dist/_lib/framework.js +149 -0
- package/dist/_lib/framework.js.map +1 -0
- package/dist/_lib/frontend-guard.d.ts +13 -0
- package/dist/_lib/frontend-guard.d.ts.map +1 -0
- package/dist/_lib/frontend-guard.js +41 -0
- package/dist/_lib/frontend-guard.js.map +1 -0
- package/dist/_lib/registry-fanout.d.ts +178 -0
- package/dist/_lib/registry-fanout.d.ts.map +1 -0
- package/dist/_lib/registry-fanout.js +545 -0
- package/dist/_lib/registry-fanout.js.map +1 -0
- package/dist/_lib/runtime-env.d.ts +126 -0
- package/dist/_lib/runtime-env.d.ts.map +1 -0
- package/dist/_lib/runtime-env.js +172 -0
- package/dist/_lib/runtime-env.js.map +1 -0
- package/dist/_lib/subscription-client.d.ts +129 -18
- package/dist/_lib/subscription-client.d.ts.map +1 -1
- package/dist/_lib/subscription-client.js +268 -40
- package/dist/_lib/subscription-client.js.map +1 -1
- package/dist/_lib/subscription-env.d.ts +35 -0
- package/dist/_lib/subscription-env.d.ts.map +1 -1
- package/dist/_lib/subscription-env.js +46 -2
- package/dist/_lib/subscription-env.js.map +1 -1
- package/dist/commands/app-lifecycle.d.ts +36 -0
- package/dist/commands/app-lifecycle.d.ts.map +1 -0
- package/dist/commands/app-lifecycle.js +145 -0
- package/dist/commands/app-lifecycle.js.map +1 -0
- package/dist/commands/ci.d.ts +45 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +174 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/deploy-manifest.d.ts +11 -0
- package/dist/commands/deploy-manifest.d.ts.map +1 -0
- package/dist/commands/deploy-manifest.js +17 -0
- package/dist/commands/deploy-manifest.js.map +1 -0
- package/dist/commands/deploy.d.ts +70 -11
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +273 -150
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/downgrade.js +1 -1
- package/dist/commands/downgrade.js.map +1 -1
- package/dist/commands/faucet.d.ts +78 -0
- package/dist/commands/faucet.d.ts.map +1 -0
- package/dist/commands/faucet.js +184 -0
- package/dist/commands/faucet.js.map +1 -0
- package/dist/commands/governance.js +1 -1
- package/dist/commands/governance.js.map +1 -1
- package/dist/commands/hist-balance.d.ts +1 -1
- package/dist/commands/hist-balance.js +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/list.d.ts +22 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +81 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/personhood.js +2 -2
- package/dist/commands/personhood.js.map +1 -1
- package/dist/commands/redeploy.d.ts +62 -0
- package/dist/commands/redeploy.d.ts.map +1 -0
- package/dist/commands/redeploy.js +322 -0
- package/dist/commands/redeploy.js.map +1 -0
- package/dist/commands/status.d.ts +51 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/subscribe.d.ts +94 -17
- package/dist/commands/subscribe.d.ts.map +1 -1
- package/dist/commands/subscribe.js +487 -81
- package/dist/commands/subscribe.js.map +1 -1
- package/dist/commands/transactions.d.ts +60 -0
- package/dist/commands/transactions.d.ts.map +1 -0
- package/dist/commands/transactions.js +101 -0
- package/dist/commands/transactions.js.map +1 -0
- package/dist/commands/update.d.ts +40 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +114 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +10 -19
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +14 -23
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -3
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseAuthChallenge = parseAuthChallenge;
|
|
37
|
+
exports.resolveClusterEndpoints = resolveClusterEndpoints;
|
|
38
|
+
exports.defaultTransport = defaultTransport;
|
|
39
|
+
exports.ensureImageOnClusters = ensureImageOnClusters;
|
|
40
|
+
exports.resolveRegistryDigest = resolveRegistryDigest;
|
|
41
|
+
exports.resolveAttestationDigest = resolveAttestationDigest;
|
|
42
|
+
exports.fanOutPushedImage = fanOutPushedImage;
|
|
43
|
+
/**
|
|
44
|
+
* Synchronous Registry-v2 fan-out: copy one already-pushed image to EVERY
|
|
45
|
+
* federated Harbor cluster, then prove it landed on all of them.
|
|
46
|
+
*
|
|
47
|
+
* ## Why this exists
|
|
48
|
+
*
|
|
49
|
+
* `harbor.testnet.hsuite.network` is public-DNS round-robin over the 3
|
|
50
|
+
* cluster-ingress IPs, and each IP fronts its OWN independent Harbor instance.
|
|
51
|
+
* A single `docker push` (via {@link import('./baas').buildAndPush}) therefore
|
|
52
|
+
* lands the image on exactly ONE cluster — whichever IP the OS resolver picked
|
|
53
|
+
* that second. The Harbor cross-cluster replication mesh is unreliable, so we do
|
|
54
|
+
* the fan-out ourselves, at the Registry-v2 API level, and BLOCK the deploy
|
|
55
|
+
* until every cluster serves the exact pushed digest.
|
|
56
|
+
*
|
|
57
|
+
* ## How the fan-out works
|
|
58
|
+
*
|
|
59
|
+
* We talk to each cluster by its IP while pinning TLS SNI + the HTTP `Host`
|
|
60
|
+
* header to the federated hostname ({@link defaultTransport}) — the cert and the
|
|
61
|
+
* Harbor vhost are keyed on that name, the IP is just the dial target. The
|
|
62
|
+
* push-robot credential is BLS-deterministic and identical on all 3 Harbors, so
|
|
63
|
+
* one credential authenticates against every IP.
|
|
64
|
+
*
|
|
65
|
+
* 1. HEAD the tag on each IP; an IP whose `Docker-Content-Digest` equals the
|
|
66
|
+
* pushed digest is already `present`.
|
|
67
|
+
* 2. At least one IP MUST be present (the federated push landed somewhere); pick
|
|
68
|
+
* it as the copy SOURCE.
|
|
69
|
+
* 3. To each non-present IP, copy: pull the source manifest (exact bytes +
|
|
70
|
+
* content-type), and for the config blob + every layer blob, HEAD it on the
|
|
71
|
+
* dest and — only if absent — monolithically PUT it (start an upload session,
|
|
72
|
+
* fetch the blob from source, PUT to the session `Location` with `?digest=`).
|
|
73
|
+
* Then PUT the manifest with the source's exact bytes + content-type.
|
|
74
|
+
* 4. RE-VERIFY by HEADing the tag on EVERY IP. If any IP's served digest still
|
|
75
|
+
* mismatches, throw an aggregated error naming the failed IPs — a deploy must
|
|
76
|
+
* never proceed with a partially-distributed image.
|
|
77
|
+
*
|
|
78
|
+
* Single-arch only: the CLI builds `linux/amd64 --provenance=false --sbom=false`,
|
|
79
|
+
* so the artifact is always a single image manifest, never a manifest list /
|
|
80
|
+
* image index. A manifest-list mediaType is treated as a hard error rather than
|
|
81
|
+
* silently fanning out a list whose per-arch children we never copied.
|
|
82
|
+
*
|
|
83
|
+
* The transport is the ONLY injectable seam ({@link RegistryTransport}); specs
|
|
84
|
+
* pass an in-memory Harbor simulation so no real network is touched. The robot
|
|
85
|
+
* password rides only inside the Basic-auth header of the token request and is
|
|
86
|
+
* never logged.
|
|
87
|
+
*/
|
|
88
|
+
const https = __importStar(require("https"));
|
|
89
|
+
const buffer_1 = require("buffer");
|
|
90
|
+
const dns_1 = require("dns");
|
|
91
|
+
/** Media types that identify a multi-arch manifest list / image index (unsupported). */
|
|
92
|
+
const MANIFEST_LIST_TYPES = [
|
|
93
|
+
'application/vnd.docker.distribution.manifest.list.v2+json',
|
|
94
|
+
'application/vnd.oci.image.index.v1+json',
|
|
95
|
+
];
|
|
96
|
+
/** Accept header offered on manifest HEAD/GET — single-arch image manifests, either flavour. */
|
|
97
|
+
const MANIFEST_ACCEPT = [
|
|
98
|
+
'application/vnd.docker.distribution.manifest.v2+json',
|
|
99
|
+
'application/vnd.oci.image.manifest.v1+json',
|
|
100
|
+
// Also accept the list types so the registry hands us the list (we then reject
|
|
101
|
+
// it with a clear error) rather than 406-ing into an opaque failure.
|
|
102
|
+
...MANIFEST_LIST_TYPES,
|
|
103
|
+
].join(', ');
|
|
104
|
+
/**
|
|
105
|
+
* Accept header offered when resolving the digest to ATTEST (#1589) — BYTE-FOR-BYTE
|
|
106
|
+
* and IN THE SAME ORDER as the deployer's `CustomerImageDigestResolverService`
|
|
107
|
+
* (`oci.index → oci.manifest → docker.v2 → docker.list.v2`). Harbor picks the
|
|
108
|
+
* served manifest (and thus the `Docker-Content-Digest`) by the Accept order, so
|
|
109
|
+
* the CLI MUST offer the identical preference list — otherwise content negotiation
|
|
110
|
+
* hands the CLI a different canonical digest than the deployer pins (e.g. the
|
|
111
|
+
* schema2 manifest digest vs the OCI-index digest) and the #1445 gate rejects a
|
|
112
|
+
* legitimate deploy. Do not reorder without changing the deployer in lockstep.
|
|
113
|
+
*/
|
|
114
|
+
const DEPLOYER_PARITY_MANIFEST_ACCEPT = [
|
|
115
|
+
'application/vnd.oci.image.index.v1+json',
|
|
116
|
+
'application/vnd.oci.image.manifest.v1+json',
|
|
117
|
+
'application/vnd.docker.distribution.manifest.v2+json',
|
|
118
|
+
'application/vnd.docker.distribution.manifest.list.v2+json',
|
|
119
|
+
].join(', ');
|
|
120
|
+
/**
|
|
121
|
+
* Parse a `Bearer realm="...",service="...",scope="..."` challenge. Pure.
|
|
122
|
+
*
|
|
123
|
+
* Returns `null` for a non-Bearer scheme or a Bearer challenge missing the
|
|
124
|
+
* mandatory `realm` (without a realm there is nowhere to fetch a token from).
|
|
125
|
+
*/
|
|
126
|
+
function parseAuthChallenge(wwwAuthenticate) {
|
|
127
|
+
const trimmed = wwwAuthenticate.trim();
|
|
128
|
+
if (!/^Bearer\s/i.test(trimmed))
|
|
129
|
+
return null;
|
|
130
|
+
const params = trimmed.replace(/^Bearer\s+/i, '');
|
|
131
|
+
const out = {};
|
|
132
|
+
// Match `key="value"` pairs; values are quoted in every real registry challenge.
|
|
133
|
+
const re = /([a-zA-Z0-9_]+)="([^"]*)"/g;
|
|
134
|
+
let m;
|
|
135
|
+
while ((m = re.exec(params)) !== null) {
|
|
136
|
+
out[m[1]] = m[2];
|
|
137
|
+
}
|
|
138
|
+
if (!out.realm)
|
|
139
|
+
return null;
|
|
140
|
+
const result = { realm: out.realm };
|
|
141
|
+
if (out.service !== undefined)
|
|
142
|
+
result.service = out.service;
|
|
143
|
+
if (out.scope !== undefined)
|
|
144
|
+
result.scope = out.scope;
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* DNS-resolve the federated host's A-records into the de-duplicated, sorted list
|
|
149
|
+
* of cluster-ingress IPs to fan out across. Injectable `resolve4` for tests;
|
|
150
|
+
* defaults to `dns.promises.resolve4`.
|
|
151
|
+
*/
|
|
152
|
+
async function resolveClusterEndpoints(host, resolve4 = (h) => dns_1.promises.resolve4(h)) {
|
|
153
|
+
const ips = await resolve4(host);
|
|
154
|
+
return Array.from(new Set(ips)).sort();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Real {@link RegistryTransport}: HTTPS to `req.ip:443` with TLS SNI + HTTP
|
|
158
|
+
* `Host` pinned to `host`, verification ON. All registry paths are `/v2/...`;
|
|
159
|
+
* absolute token-realm URLs are handled by {@link fetchAbsolute}.
|
|
160
|
+
*/
|
|
161
|
+
function defaultTransport(host) {
|
|
162
|
+
return (req) => new Promise((resolvePromise, reject) => {
|
|
163
|
+
const r = https.request({
|
|
164
|
+
host: req.ip,
|
|
165
|
+
servername: host, // TLS SNI → the federated cert
|
|
166
|
+
port: 443,
|
|
167
|
+
method: req.method,
|
|
168
|
+
path: req.path,
|
|
169
|
+
rejectUnauthorized: true,
|
|
170
|
+
headers: { ...(req.headers ?? {}), Host: host },
|
|
171
|
+
}, (res) => {
|
|
172
|
+
const chunks = [];
|
|
173
|
+
res.on('data', (d) => chunks.push(d));
|
|
174
|
+
res.on('end', () => resolvePromise({
|
|
175
|
+
status: res.statusCode ?? 0,
|
|
176
|
+
headers: lowerCaseHeaders(res.headers),
|
|
177
|
+
body: buffer_1.Buffer.concat(chunks),
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
r.on('error', reject);
|
|
181
|
+
if (req.body)
|
|
182
|
+
r.write(req.body);
|
|
183
|
+
r.end();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Lower-case header keys; collapse array values to the first entry. */
|
|
187
|
+
function lowerCaseHeaders(h) {
|
|
188
|
+
const out = {};
|
|
189
|
+
for (const [k, v] of Object.entries(h)) {
|
|
190
|
+
if (v === undefined)
|
|
191
|
+
continue;
|
|
192
|
+
out[k.toLowerCase()] = Array.isArray(v) ? v[0] : v;
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/** Case-insensitive header read. */
|
|
197
|
+
function header(res, name) {
|
|
198
|
+
const lower = name.toLowerCase();
|
|
199
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
200
|
+
if (k.toLowerCase() === lower)
|
|
201
|
+
return v;
|
|
202
|
+
}
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
/** Resolve a possibly-relative `Location` against the `/v2/...` request space. */
|
|
206
|
+
function locationPath(location) {
|
|
207
|
+
if (location.startsWith('http://') || location.startsWith('https://')) {
|
|
208
|
+
const u = new URL(location);
|
|
209
|
+
return u.pathname + u.search;
|
|
210
|
+
}
|
|
211
|
+
return location;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* An authenticating Registry client over the injected transport. Transparently
|
|
215
|
+
* handles the 401 + Bearer-challenge dance: on a 401 it parses the challenge,
|
|
216
|
+
* fetches a token from the realm with HTTP Basic over the robot creds (caching
|
|
217
|
+
* it per `(ip, scope)`), then retries the original request with the Bearer.
|
|
218
|
+
*/
|
|
219
|
+
function makeClient(transport, username, password) {
|
|
220
|
+
const tokens = new Map();
|
|
221
|
+
const basic = 'Basic ' + buffer_1.Buffer.from(`${username}:${password}`).toString('base64');
|
|
222
|
+
async function fetchToken(ip, realm, service, scope) {
|
|
223
|
+
const key = `${ip}|${scope ?? ''}`;
|
|
224
|
+
const cached = tokens.get(key);
|
|
225
|
+
if (cached)
|
|
226
|
+
return cached;
|
|
227
|
+
const u = new URL(realm);
|
|
228
|
+
if (service)
|
|
229
|
+
u.searchParams.set('service', service);
|
|
230
|
+
if (scope)
|
|
231
|
+
u.searchParams.set('scope', scope);
|
|
232
|
+
const res = await transport({
|
|
233
|
+
ip,
|
|
234
|
+
method: 'GET',
|
|
235
|
+
path: u.pathname + u.search,
|
|
236
|
+
headers: { Authorization: basic },
|
|
237
|
+
});
|
|
238
|
+
if (res.status !== 200) {
|
|
239
|
+
throw new Error(`token request to ${ip} (${realm}) failed: HTTP ${res.status}`);
|
|
240
|
+
}
|
|
241
|
+
const parsed = JSON.parse(res.body.toString('utf8'));
|
|
242
|
+
const token = parsed.token ?? parsed.access_token;
|
|
243
|
+
if (!token)
|
|
244
|
+
throw new Error(`token request to ${ip} returned no token`);
|
|
245
|
+
tokens.set(key, token);
|
|
246
|
+
return token;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Send `req`, transparently authenticating on a 401. `scopeHint` is the scope
|
|
250
|
+
* we proactively request (`repository:<repo>:pull,push`) when the challenge
|
|
251
|
+
* itself omits one.
|
|
252
|
+
*/
|
|
253
|
+
async function send(req, scopeHint) {
|
|
254
|
+
const res = await transport(req);
|
|
255
|
+
if (res.status !== 401)
|
|
256
|
+
return res;
|
|
257
|
+
const wwwAuth = header(res, 'www-authenticate');
|
|
258
|
+
const challenge = wwwAuth ? parseAuthChallenge(wwwAuth) : null;
|
|
259
|
+
if (!challenge)
|
|
260
|
+
return res; // not a bearer challenge we can satisfy
|
|
261
|
+
const token = await fetchToken(req.ip, challenge.realm, challenge.service, challenge.scope ?? scopeHint);
|
|
262
|
+
return transport({
|
|
263
|
+
...req,
|
|
264
|
+
headers: { ...(req.headers ?? {}), Authorization: `Bearer ${token}` },
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return { send };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Parse a single-arch image manifest into its config + layer descriptors.
|
|
271
|
+
* Throws on a manifest-list / image-index mediaType (single-arch deploy only).
|
|
272
|
+
*/
|
|
273
|
+
function parseManifest(bytes, contentType) {
|
|
274
|
+
const doc = JSON.parse(bytes.toString('utf8'));
|
|
275
|
+
const mediaType = contentType ?? doc.mediaType;
|
|
276
|
+
if (mediaType && MANIFEST_LIST_TYPES.includes(mediaType.split(';')[0].trim())) {
|
|
277
|
+
throw new Error(`manifest list not supported (single-arch deploy only): got mediaType "${mediaType}". ` +
|
|
278
|
+
'The CLI builds linux/amd64 --provenance=false --sbom=false, which must yield a single image manifest.');
|
|
279
|
+
}
|
|
280
|
+
if (!doc.config?.digest || !Array.isArray(doc.layers)) {
|
|
281
|
+
throw new Error('source manifest is not a single-arch image manifest (no config/layers)');
|
|
282
|
+
}
|
|
283
|
+
return { config: doc.config, layers: doc.layers };
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Fan out an already-pushed image to every cluster Harbor and prove it landed
|
|
287
|
+
* on all of them. See the module header for the full algorithm. Throws if no
|
|
288
|
+
* endpoint serves the digest to copy from, or if re-verification finds any
|
|
289
|
+
* endpoint not serving the pushed digest (the error names the failed IPs).
|
|
290
|
+
*/
|
|
291
|
+
async function ensureImageOnClusters(opts) {
|
|
292
|
+
const transport = opts.transport ?? defaultTransport(opts.host);
|
|
293
|
+
const client = makeClient(transport, opts.username, opts.password);
|
|
294
|
+
const pullPushScope = `repository:${opts.repository}:pull,push`;
|
|
295
|
+
const manifestPath = `/v2/${opts.repository}/manifests/${opts.tag}`;
|
|
296
|
+
// 1. Which endpoints already serve the pushed digest?
|
|
297
|
+
const presence = await Promise.all(opts.endpoints.map(async (ip) => {
|
|
298
|
+
const res = await client.send({ ip, method: 'HEAD', path: manifestPath, headers: { Accept: MANIFEST_ACCEPT } }, pullPushScope);
|
|
299
|
+
const served = res.status === 200 ? header(res, 'docker-content-digest') : undefined;
|
|
300
|
+
return { ip, present: served === opts.digest };
|
|
301
|
+
}));
|
|
302
|
+
const source = presence.find((p) => p.present);
|
|
303
|
+
if (!source) {
|
|
304
|
+
throw new Error(`no cluster endpoint serves ${opts.repository}:${opts.tag}@${opts.digest} — ` +
|
|
305
|
+
'nothing to copy from (the federated push must land on at least one cluster first).');
|
|
306
|
+
}
|
|
307
|
+
// Fast path: every endpoint already serves the digest (e.g. a single-cluster
|
|
308
|
+
// deployment, or a re-run). Nothing to copy — skip the source manifest GET.
|
|
309
|
+
if (presence.every((p) => p.present)) {
|
|
310
|
+
return { targets: presence.map((p) => ({ ip: p.ip, outcome: 'present' })) };
|
|
311
|
+
}
|
|
312
|
+
// 2. Pull the source manifest once (exact bytes + content-type).
|
|
313
|
+
const srcManifestRes = await client.send({ ip: source.ip, method: 'GET', path: manifestPath, headers: { Accept: MANIFEST_ACCEPT } }, pullPushScope);
|
|
314
|
+
if (srcManifestRes.status !== 200) {
|
|
315
|
+
throw new Error(`failed to read source manifest from ${source.ip}: HTTP ${srcManifestRes.status}`);
|
|
316
|
+
}
|
|
317
|
+
const manifestContentType = header(srcManifestRes, 'content-type');
|
|
318
|
+
const parsed = parseManifest(srcManifestRes.body, manifestContentType); // throws on manifest list
|
|
319
|
+
const blobDigests = [parsed.config.digest, ...parsed.layers.map((l) => l.digest)];
|
|
320
|
+
// 3. Copy to every non-present endpoint.
|
|
321
|
+
const report = { targets: [] };
|
|
322
|
+
for (const p of presence) {
|
|
323
|
+
if (p.present) {
|
|
324
|
+
report.targets.push({ ip: p.ip, outcome: 'present' });
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
await copyToDest({
|
|
328
|
+
client,
|
|
329
|
+
source: source.ip,
|
|
330
|
+
dest: p.ip,
|
|
331
|
+
repository: opts.repository,
|
|
332
|
+
tag: opts.tag,
|
|
333
|
+
pullPushScope,
|
|
334
|
+
blobDigests,
|
|
335
|
+
manifestBytes: srcManifestRes.body,
|
|
336
|
+
manifestContentType,
|
|
337
|
+
manifestPath,
|
|
338
|
+
});
|
|
339
|
+
report.targets.push({ ip: p.ip, outcome: 'copied' });
|
|
340
|
+
}
|
|
341
|
+
// 4. Re-verify the tag on EVERY endpoint; any mismatch is fatal.
|
|
342
|
+
const failures = [];
|
|
343
|
+
await Promise.all(opts.endpoints.map(async (ip) => {
|
|
344
|
+
const res = await client.send({ ip, method: 'HEAD', path: manifestPath, headers: { Accept: MANIFEST_ACCEPT } }, pullPushScope);
|
|
345
|
+
const served = res.status === 200 ? header(res, 'docker-content-digest') : undefined;
|
|
346
|
+
if (served !== opts.digest) {
|
|
347
|
+
failures.push(ip);
|
|
348
|
+
const t = report.targets.find((x) => x.ip === ip);
|
|
349
|
+
if (t) {
|
|
350
|
+
t.outcome = 'failed';
|
|
351
|
+
t.error = `re-verify: served digest ${served ?? '<none>'} !== ${opts.digest}`;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}));
|
|
355
|
+
if (failures.length > 0) {
|
|
356
|
+
throw new Error(`image fan-out incomplete: ${failures.sort().join(', ')} do not serve ` +
|
|
357
|
+
`${opts.repository}:${opts.tag}@${opts.digest} after copy — deploy aborted.`);
|
|
358
|
+
}
|
|
359
|
+
return report;
|
|
360
|
+
}
|
|
361
|
+
/** Copy the config + layer blobs (skipping ones the dest already has) and PUT the manifest. */
|
|
362
|
+
async function copyToDest(args) {
|
|
363
|
+
const { client, source, dest, repository, pullPushScope } = args;
|
|
364
|
+
for (const digest of args.blobDigests) {
|
|
365
|
+
// HEAD on dest — skip the upload entirely if the blob already exists.
|
|
366
|
+
const head = await client.send({ ip: dest, method: 'HEAD', path: `/v2/${repository}/blobs/${digest}` }, pullPushScope);
|
|
367
|
+
if (head.status === 200)
|
|
368
|
+
continue;
|
|
369
|
+
// Fetch the blob bytes from the source.
|
|
370
|
+
const blob = await client.send({ ip: source, method: 'GET', path: `/v2/${repository}/blobs/${digest}` }, pullPushScope);
|
|
371
|
+
if (blob.status !== 200) {
|
|
372
|
+
throw new Error(`failed to fetch blob ${digest} from source ${source}: HTTP ${blob.status}`);
|
|
373
|
+
}
|
|
374
|
+
// Start an upload session, then monolithic-PUT to its Location with ?digest=.
|
|
375
|
+
const start = await client.send({ ip: dest, method: 'POST', path: `/v2/${repository}/blobs/uploads/` }, pullPushScope);
|
|
376
|
+
if (start.status !== 202) {
|
|
377
|
+
throw new Error(`failed to start blob upload on ${dest}: HTTP ${start.status}`);
|
|
378
|
+
}
|
|
379
|
+
const location = header(start, 'location');
|
|
380
|
+
if (!location)
|
|
381
|
+
throw new Error(`blob upload on ${dest} returned no Location`);
|
|
382
|
+
const putPath = appendDigestQuery(locationPath(location), digest);
|
|
383
|
+
const put = await client.send({
|
|
384
|
+
ip: dest,
|
|
385
|
+
method: 'PUT',
|
|
386
|
+
path: putPath,
|
|
387
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
388
|
+
body: blob.body,
|
|
389
|
+
}, pullPushScope);
|
|
390
|
+
if (put.status !== 201) {
|
|
391
|
+
throw new Error(`failed to upload blob ${digest} to ${dest}: HTTP ${put.status}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// All blobs present on dest → PUT the manifest with the EXACT source bytes + content-type.
|
|
395
|
+
const headers = {};
|
|
396
|
+
if (args.manifestContentType)
|
|
397
|
+
headers['Content-Type'] = args.manifestContentType;
|
|
398
|
+
const put = await client.send({ ip: dest, method: 'PUT', path: args.manifestPath, headers, body: args.manifestBytes }, pullPushScope);
|
|
399
|
+
if (put.status !== 201 && put.status !== 200) {
|
|
400
|
+
throw new Error(`failed to PUT manifest to ${dest}: HTTP ${put.status}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/** Append `digest=<d>` to a `Location`, respecting any query string already present. */
|
|
404
|
+
function appendDigestQuery(path, digest) {
|
|
405
|
+
const sep = path.includes('?') ? '&' : '?';
|
|
406
|
+
return `${path}${sep}digest=${encodeURIComponent(digest)}`;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Resolve the CANONICAL manifest digest the registry serves for `<repo>:<tag>`
|
|
410
|
+
* — the value returned in the `Docker-Content-Digest` response header on a
|
|
411
|
+
* `HEAD /v2/<repo>/manifests/<tag>`.
|
|
412
|
+
*
|
|
413
|
+
* ## Why this is the digest to ATTEST (issue #1589)
|
|
414
|
+
*
|
|
415
|
+
* The `docker push` summary reports the digest of the manifest the *local*
|
|
416
|
+
* client holds. With a containerd image store (Colima / buildkit snapshotter),
|
|
417
|
+
* that is an OCI image manifest, but Harbor stores + re-serves the tag under a
|
|
418
|
+
* DIFFERENT canonical digest (a re-serialized Docker schema2 manifest) — the
|
|
419
|
+
* push-summary digest and the tag's served digest disagree. The deployer pins +
|
|
420
|
+
* the fan-out re-verify BOTH key off the tag's served `Docker-Content-Digest`
|
|
421
|
+
* ({@link CustomerImageDigestResolverService} does the identical HEAD). So the
|
|
422
|
+
* CLI must attest THIS registry-truth digest, not the push-summary one, and feed
|
|
423
|
+
* the SAME value to the fan-out — then attested == fanned-out == deployer-resolved
|
|
424
|
+
* by construction.
|
|
425
|
+
*
|
|
426
|
+
* This is the exact resolution the deployer performs, so it is authoritative for
|
|
427
|
+
* the strict-equality attestation gate. Returns the `sha256:<64hex>` served
|
|
428
|
+
* digest, or `null` when the registry can't be probed (caller decides whether to
|
|
429
|
+
* fall back to the push-summary digest — a fail-soft, not a fail-open: the
|
|
430
|
+
* deployer still re-verifies).
|
|
431
|
+
*/
|
|
432
|
+
async function resolveRegistryDigest(opts) {
|
|
433
|
+
const transport = opts.transport ?? defaultTransport(opts.host);
|
|
434
|
+
const endpoints = await resolveClusterEndpoints(opts.host, opts.resolve4).catch(() => [opts.host]);
|
|
435
|
+
// Any single endpoint is authoritative for the tag→digest mapping (the
|
|
436
|
+
// fan-out proves every cluster ends up serving that same digest). Try each
|
|
437
|
+
// until one answers, so a single-cluster transient miss doesn't fail-soft.
|
|
438
|
+
const client = makeClient(transport, opts.username, opts.password);
|
|
439
|
+
const pullScope = `repository:${opts.repository}:pull`;
|
|
440
|
+
const manifestPath = `/v2/${opts.repository}/manifests/${opts.tag}`;
|
|
441
|
+
for (const ip of endpoints.length > 0 ? endpoints : [opts.host]) {
|
|
442
|
+
for (const method of ['HEAD', 'GET']) {
|
|
443
|
+
let res;
|
|
444
|
+
try {
|
|
445
|
+
res = await client.send({ ip, method, path: manifestPath, headers: { Accept: DEPLOYER_PARITY_MANIFEST_ACCEPT } }, pullScope);
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (res.status !== 200)
|
|
451
|
+
continue;
|
|
452
|
+
const served = header(res, 'docker-content-digest');
|
|
453
|
+
if (served && /^sha256:[0-9a-f]{64}$/.test(served))
|
|
454
|
+
return served;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Decide the digest to ATTEST (and to feed the fan-out): the digest Harbor
|
|
461
|
+
* SERVES for `<repo>:<tag>`, resolved the same way the deployer does. Falls back
|
|
462
|
+
* to the local `docker push` summary digest ONLY if the registry can't be probed
|
|
463
|
+
* (offline / dev registry / transient) — a fail-SOFT, never a fail-open, because
|
|
464
|
+
* the deployer independently re-verifies the served digest against the attested
|
|
465
|
+
* one before it pins the pod.
|
|
466
|
+
*
|
|
467
|
+
* See {@link resolveRegistryDigest} for why the served digest — not the push
|
|
468
|
+
* summary — is authoritative for the #1445 strict-equality gate (issue #1589).
|
|
469
|
+
*/
|
|
470
|
+
async function resolveAttestationDigest(opts) {
|
|
471
|
+
const log = opts.log ?? (() => undefined);
|
|
472
|
+
const host = opts.registry.server.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
|
|
473
|
+
let served = null;
|
|
474
|
+
try {
|
|
475
|
+
served = await resolveRegistryDigest({
|
|
476
|
+
host,
|
|
477
|
+
repository: opts.registry.repository,
|
|
478
|
+
tag: opts.tag,
|
|
479
|
+
username: opts.registry.username,
|
|
480
|
+
password: opts.registry.password,
|
|
481
|
+
transport: opts.transport,
|
|
482
|
+
resolve4: opts.resolve4,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
log(` attest: registry digest probe threw (${err.message}); ` +
|
|
487
|
+
'attesting the local push digest');
|
|
488
|
+
}
|
|
489
|
+
if (!served) {
|
|
490
|
+
log(` attest: could not resolve the registry-served digest for ${opts.registry.repository}:${opts.tag}; ` +
|
|
491
|
+
'attesting the local push digest (the deployer re-verifies)');
|
|
492
|
+
return opts.pushDigest;
|
|
493
|
+
}
|
|
494
|
+
if (served !== opts.pushDigest) {
|
|
495
|
+
log(` attest: registry serves ${served} for ${opts.registry.repository}:${opts.tag} ` +
|
|
496
|
+
`(local push reported ${opts.pushDigest}); attesting the registry-served digest`);
|
|
497
|
+
}
|
|
498
|
+
return served;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* The deploy-time entrypoint: take an image just pushed to the federated host
|
|
502
|
+
* (landed on ONE cluster) and guarantee it is present on EVERY cluster Harbor.
|
|
503
|
+
*
|
|
504
|
+
* Resolves the registry host's A-records into the cluster-ingress set, then:
|
|
505
|
+
* - 0/1 endpoint (a non-federated or local/dev registry, or DNS that doesn't
|
|
506
|
+
* fan out) → nothing to distribute; returns `null`.
|
|
507
|
+
* - ≥2 endpoints → {@link ensureImageOnClusters}, which copies + re-verifies
|
|
508
|
+
* and THROWS if any cluster ends up not serving the pushed digest.
|
|
509
|
+
*
|
|
510
|
+
* A DNS resolution failure is treated as "single-target" (logged, returns
|
|
511
|
+
* `null`) rather than fatal: a real federated push has already proven the host
|
|
512
|
+
* resolves, so a failure here means a dev/local registry where fan-out is moot.
|
|
513
|
+
*/
|
|
514
|
+
async function fanOutPushedImage(opts) {
|
|
515
|
+
const log = opts.log ?? (() => undefined);
|
|
516
|
+
const host = opts.registry.server.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
|
|
517
|
+
let endpoints;
|
|
518
|
+
try {
|
|
519
|
+
endpoints = await resolveClusterEndpoints(host, opts.resolve4);
|
|
520
|
+
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
log(` distribute: ${host} did not resolve (${err.message}); single-target, skipping fan-out`);
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
if (endpoints.length <= 1) {
|
|
526
|
+
log(` distribute: ${host} resolves to ${endpoints.length} endpoint(s); single-target, skipping fan-out`);
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
log(` distribute: fanning ${opts.registry.repository}:${opts.tag} to ${endpoints.length} clusters (${endpoints.join(', ')})`);
|
|
530
|
+
const report = await ensureImageOnClusters({
|
|
531
|
+
host,
|
|
532
|
+
repository: opts.registry.repository,
|
|
533
|
+
tag: opts.tag,
|
|
534
|
+
digest: opts.digest,
|
|
535
|
+
username: opts.registry.username,
|
|
536
|
+
password: opts.registry.password,
|
|
537
|
+
endpoints,
|
|
538
|
+
transport: opts.transport,
|
|
539
|
+
});
|
|
540
|
+
for (const t of report.targets) {
|
|
541
|
+
log(` distribute: ${t.ip} → ${t.outcome}${t.error ? ` (${t.error})` : ''}`);
|
|
542
|
+
}
|
|
543
|
+
return report;
|
|
544
|
+
}
|
|
545
|
+
//# sourceMappingURL=registry-fanout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-fanout.js","sourceRoot":"","sources":["../../src/_lib/registry-fanout.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwIA,gDAgBC;AAOD,0DAMC;AAOD,4CA6BC;AA8HD,sDA8FC;AAsHD,sDA8BC;AAkCD,4DAoCC;AA6BD,8CAiCC;AA7rBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,6CAA+B;AAC/B,mCAAgC;AAChC,6BAAsC;AAEtC,wFAAwF;AACxF,MAAM,mBAAmB,GAAG;IAC1B,2DAA2D;IAC3D,yCAAyC;CAC1C,CAAC;AAEF,gGAAgG;AAChG,MAAM,eAAe,GAAG;IACtB,sDAAsD;IACtD,4CAA4C;IAC5C,+EAA+E;IAC/E,qEAAqE;IACrE,GAAG,mBAAmB;CACvB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;;;;GASG;AACH,MAAM,+BAA+B,GAAG;IACtC,yCAAyC;IACzC,4CAA4C;IAC5C,sDAAsD;IACtD,2DAA2D;CAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAmDb;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,eAAuB;IACxD,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,iFAAiF;IACjF,MAAM,EAAE,GAAG,4BAA4B,CAAC;IACxC,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACtC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,MAAM,GAAkB,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;IACnD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;QAAE,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC5D,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACtD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,uBAAuB,CAC3C,IAAY,EACZ,WAA6C,CAAC,CAAC,EAAE,EAAE,CAAC,cAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEnE,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,OAAO,CAAC,GAAG,EAAE,EAAE,CACb,IAAI,OAAO,CAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE;QACvD,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CACrB;YACE,IAAI,EAAE,GAAG,CAAC,EAAE;YACZ,UAAU,EAAE,IAAI,EAAE,+BAA+B;YACjD,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,kBAAkB,EAAE,IAAI;YACxB,OAAO,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE;SAChD,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CACjB,cAAc,CAAC;gBACb,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC;gBAC3B,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,OAAwD,CAAC;gBACvF,IAAI,EAAE,eAAM,CAAC,MAAM,CAAC,MAAM,CAAC;aAC5B,CAAC,CACH,CAAC;QACJ,CAAC,CACF,CAAC;QACF,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtB,IAAI,GAAG,CAAC,IAAI;YAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,CAAC,GAAG,EAAE,CAAC;IACV,CAAC,CAAC,CAAC;AACP,CAAC;AAED,wEAAwE;AACxE,SAAS,gBAAgB,CAAC,CAAgD;IACxE,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS;QAC9B,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oCAAoC;AACpC,SAAS,MAAM,CAAC,GAAwC,EAAE,IAAY;IACpE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACjD,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAKD;;;;;GAKG;AACH,SAAS,UAAU,CACjB,SAA4B,EAC5B,QAAgB,EAChB,QAAgB;IAEhB,MAAM,MAAM,GAAe,IAAI,GAAG,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,QAAQ,GAAG,eAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEnF,KAAK,UAAU,UAAU,CAAC,EAAU,EAAE,KAAa,EAAE,OAAgB,EAAE,KAAc;QACnF,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,OAAO;YAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACpD,IAAI,KAAK;YAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC;YAC1B,EAAE;YACF,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM;YAC3B,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE;SAClC,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,EAAE,KAAK,KAAK,kBAAkB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA8C,CAAC;QAClG,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,YAAY,CAAC;QAClD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,KAAK,UAAU,IAAI,CAAC,GAAoB,EAAE,SAAkB;QAC1D,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,GAAG,CAAC;QACnC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,IAAI,CAAC,SAAS;YAAE,OAAO,GAAG,CAAC,CAAC,wCAAwC;QACpE,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;QACzG,OAAO,SAAS,CAAC;YACf,GAAG,GAAG;YACN,OAAO,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SACtE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAQD;;;GAGG;AACH,SAAS,aAAa,CAAC,KAAa,EAAE,WAAoB;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAI5C,CAAC;IACF,MAAM,SAAS,GAAG,WAAW,IAAI,GAAG,CAAC,SAAS,CAAC;IAC/C,IAAI,SAAS,IAAI,mBAAmB,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,KAAK,CACb,yEAAyE,SAAS,KAAK;YACrF,uGAAuG,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,qBAAqB,CAAC,IAAqB;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,cAAc,IAAI,CAAC,UAAU,YAAY,CAAC;IAChE,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC;IAEpE,sDAAsD;IACtD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAChF,aAAa,CACd,CAAC;QACF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;IACjD,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,8BAA8B,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK;YAC3E,oFAAoF,CACvF,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,4EAA4E;IAC5E,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,SAAkB,EAAE,CAAC,CAAC,EAAE,CAAC;IACvF,CAAC;IAED,iEAAiE;IACjE,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CACtC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAC1F,aAAa,CACd,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,MAAM,CAAC,EAAE,UAAU,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,MAAM,mBAAmB,GAAG,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,0BAA0B;IAClG,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAElF,yCAAyC;IACzC,MAAM,MAAM,GAAiB,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;YACtD,SAAS;QACX,CAAC;QACD,MAAM,UAAU,CAAC;YACf,MAAM;YACN,MAAM,EAAE,MAAM,CAAC,EAAE;YACjB,IAAI,EAAE,CAAC,CAAC,EAAE;YACV,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,aAAa;YACb,WAAW;YACX,aAAa,EAAE,cAAc,CAAC,IAAI;YAClC,mBAAmB;YACnB,YAAY;SACb,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,iEAAiE;IACjE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAChF,aAAa,CACd,CAAC;QACF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,IAAI,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE,CAAC;gBACN,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC;gBACrB,CAAC,CAAC,KAAK,GAAG,4BAA4B,MAAM,IAAI,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YAChF,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB;YACrE,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,+BAA+B,CAC/E,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+FAA+F;AAC/F,KAAK,UAAU,UAAU,CAAC,IAWzB;IACC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAEjE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,sEAAsE;QACtE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,UAAU,UAAU,MAAM,EAAE,EAAE,EACvE,aAAa,CACd,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG;YAAE,SAAS;QAElC,wCAAwC;QACxC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAC5B,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,UAAU,UAAU,MAAM,EAAE,EAAE,EACxE,aAAa,CACd,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,gBAAgB,MAAM,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,8EAA8E;QAC9E,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,UAAU,iBAAiB,EAAE,EACtE,aAAa,CACd,CAAC;QACF,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,UAAU,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,uBAAuB,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B;YACE,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;YACvD,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,EACD,aAAa,CACd,CAAC;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,OAAO,IAAI,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,2FAA2F;IAC3F,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,IAAI,CAAC,mBAAmB;QAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC;IACjF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,EACvF,aAAa,CACd,CAAC;IACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,IAAY,EAAE,MAAc;IACrD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3C,OAAO,GAAG,IAAI,GAAG,GAAG,UAAU,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7D,CAAC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACI,KAAK,UAAU,qBAAqB,CACzC,IAA+B;IAE/B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC7E,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAClB,CAAC;IACF,uEAAuE;IACvE,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,cAAc,IAAI,CAAC,UAAU,OAAO,CAAC;IACvD,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC;IACpE,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAU,EAAE,CAAC;YAC9C,IAAI,GAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CACrB,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,+BAA+B,EAAE,EAAE,EACxF,SAAS,CACV,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,SAAS;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;YACpD,IAAI,MAAM,IAAI,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAuBD;;;;;;;;;;GAUG;AACI,KAAK,UAAU,wBAAwB,CAC5C,IAAkC;IAElC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACnF,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,qBAAqB,CAAC;YACnC,IAAI;YACJ,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;YACpC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAChC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CACD,0CAA2C,GAAa,CAAC,OAAO,KAAK;YACnE,iCAAiC,CACpC,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CACD,8DAA8D,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,IAAI;YACpG,4DAA4D,CAC/D,CAAC;QACF,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;QAC/B,GAAG,CACD,6BAA6B,MAAM,QAAQ,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,GAAG;YAChF,wBAAwB,IAAI,CAAC,UAAU,yCAAyC,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAeD;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,iBAAiB,CACrC,IAA2B;IAE3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAEnF,IAAI,SAAmB,CAAC;IACxB,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,iBAAiB,IAAI,qBAAsB,GAAa,CAAC,OAAO,oCAAoC,CAAC,CAAC;QAC1G,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,iBAAiB,IAAI,gBAAgB,SAAS,CAAC,MAAM,+CAA+C,CAAC,CAAC;QAC1G,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,yBAAyB,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,OAAO,SAAS,CAAC,MAAM,cAAc,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/H,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;QACzC,IAAI;QACJ,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;QACpC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ;QAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ;QAChC,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;IACH,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,GAAG,CAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|