@nevermined-io/cli 1.4.0 → 1.5.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.
Files changed (43) hide show
  1. package/dist/base-command.d.ts +11 -1
  2. package/dist/base-command.d.ts.map +1 -1
  3. package/dist/base-command.js +14 -0
  4. package/dist/base-command.js.map +1 -1
  5. package/dist/commands/cards/delegate.d.ts +27 -0
  6. package/dist/commands/cards/delegate.d.ts.map +1 -0
  7. package/dist/commands/cards/delegate.js +89 -0
  8. package/dist/commands/cards/delegate.js.map +1 -0
  9. package/dist/commands/cards/enroll.d.ts +26 -0
  10. package/dist/commands/cards/enroll.d.ts.map +1 -0
  11. package/dist/commands/cards/enroll.js +90 -0
  12. package/dist/commands/cards/enroll.js.map +1 -0
  13. package/dist/commands/cards/setup.d.ts +29 -0
  14. package/dist/commands/cards/setup.d.ts.map +1 -0
  15. package/dist/commands/cards/setup.js +115 -0
  16. package/dist/commands/cards/setup.js.map +1 -0
  17. package/dist/commands/login.js +1 -26
  18. package/dist/commands/login.js.map +1 -1
  19. package/dist/commands/organizations/get-my-memberships.d.ts +15 -0
  20. package/dist/commands/organizations/get-my-memberships.d.ts.map +1 -0
  21. package/dist/commands/organizations/get-my-memberships.js +25 -0
  22. package/dist/commands/organizations/get-my-memberships.js.map +1 -0
  23. package/dist/commands/organizations/get-organization-activity.d.ts +18 -0
  24. package/dist/commands/organizations/get-organization-activity.d.ts.map +1 -0
  25. package/dist/commands/organizations/get-organization-activity.js +32 -0
  26. package/dist/commands/organizations/get-organization-activity.js.map +1 -0
  27. package/dist/generator/command-generator.d.ts.map +1 -1
  28. package/dist/generator/command-generator.js +3 -1
  29. package/dist/generator/command-generator.js.map +1 -1
  30. package/dist/utils/browser.d.ts +14 -0
  31. package/dist/utils/browser.d.ts.map +1 -0
  32. package/dist/utils/browser.js +41 -0
  33. package/dist/utils/browser.js.map +1 -0
  34. package/dist/utils/orgs.d.ts +36 -0
  35. package/dist/utils/orgs.d.ts.map +1 -0
  36. package/dist/utils/orgs.js +65 -0
  37. package/dist/utils/orgs.js.map +1 -0
  38. package/dist/utils/widget-redirect-flow.d.ts +118 -0
  39. package/dist/utils/widget-redirect-flow.d.ts.map +1 -0
  40. package/dist/utils/widget-redirect-flow.js +296 -0
  41. package/dist/utils/widget-redirect-flow.js.map +1 -0
  42. package/oclif.manifest.json +430 -66
  43. package/package.json +1 -1
@@ -0,0 +1,296 @@
1
+ import { createServer } from 'http';
2
+ import { randomBytes, timingSafeEqual } from 'crypto';
3
+ import { openBrowser } from './browser.js';
4
+ /**
5
+ * Map a CLI environment name to the embed app's `network` value. The
6
+ * `embed` origin is shared across the sandbox/live pair within a tier
7
+ * (`embed.nevermined.app` for both `sandbox` and `live`), differentiated
8
+ * only by the backend the session is validated against — so the embed
9
+ * app cannot infer the network from the origin and we must pass it.
10
+ *
11
+ * `custom` has no fixed tier, so we sniff `NVM_BACKEND_URL`: a backend
12
+ * host containing `live` selects `live`, otherwise we fall back to
13
+ * `sandbox` (matching the embed app's own default).
14
+ *
15
+ * NOTE: `live` is only matched as a dot/slash-bounded segment (the
16
+ * `api.live.<host>` convention), so a hyphenated host like
17
+ * `https://api-live.example.com` would fall through to `sandbox`. That's
18
+ * intentional given the naming convention; a `custom` deployment that
19
+ * doesn't follow it should set `NVM_BACKEND_URL` to a conforming host.
20
+ */
21
+ export function resolveEmbedNetwork(environment) {
22
+ switch (environment) {
23
+ case 'live':
24
+ case 'staging_live':
25
+ return 'live';
26
+ case 'sandbox':
27
+ case 'staging_sandbox':
28
+ return 'sandbox';
29
+ case 'custom':
30
+ return /(^|[.\/])live([.\/]|$)/.test((process.env.NVM_BACKEND_URL || '').toLowerCase())
31
+ ? 'live'
32
+ : 'sandbox';
33
+ }
34
+ }
35
+ const SELF_MINT_FETCH_TIMEOUT_MS = 15_000;
36
+ /**
37
+ * Default timeout for a redirect-mode CLI flow. Mirrors the existing
38
+ * `nvm login` callback timeout — 5 minutes is enough for the user to
39
+ * tab into the browser, complete a card enrolment + delegation, and
40
+ * land back at the CLI.
41
+ */
42
+ const REDIRECT_TIMEOUT_MS = 5 * 60 * 1000;
43
+ /**
44
+ * Constant-time string equality for the CSRF `state` nonce. The state we
45
+ * issue is a 32-char hex string (`randomBytes(16).toString('hex')`), so
46
+ * we ALWAYS allocate fixed 16-byte buffers from `hex` regardless of the
47
+ * caller-supplied value's encoding. Computing buffer length from string
48
+ * length would diverge on non-ASCII input (`a.length` is UTF-16 code
49
+ * units; `Buffer.from(a, 'utf8').length` is byte count), and a crafted
50
+ * non-hex `received` could otherwise throw inside `timingSafeEqual`.
51
+ *
52
+ * Inputs must already have been verified to be 32 hex chars by the
53
+ * caller (or we reject up front).
54
+ */
55
+ function safeEqualHexState(received, expected) {
56
+ if (!/^[0-9a-f]{32}$/i.test(received) || !/^[0-9a-f]{32}$/i.test(expected))
57
+ return false;
58
+ return timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'));
59
+ }
60
+ /**
61
+ * Shared redirect-mode handshake for any CLI command that hands the user
62
+ * off to a `/cards/*` page on the standalone embed app (`embed.<tier>`)
63
+ * and waits for a localhost callback.
64
+ *
65
+ * Flow:
66
+ * 1. Bind a one-shot HTTP server on `127.0.0.1:0` (the OS picks a free port).
67
+ * 2. Compute `returnUrl = http://127.0.0.1:<port>/callback` and hand it
68
+ * to `opts.mintSession`. The caller mints a widget session bound to
69
+ * that exact returnUrl (which the backend can validate against the
70
+ * session-specific allow-list at creation time). We use the literal
71
+ * `127.0.0.1` rather than `localhost` because the server binds to
72
+ * `127.0.0.1` and Node 17+ resolves `localhost` to `::1` first on
73
+ * modern hosts — the browser would stall on the IPv6 attempt before
74
+ * falling back to IPv4.
75
+ * 3. Open the browser at `{embed}/<path>?sessionToken=…&returnUrl=…&state=<rand>`.
76
+ * 4. Resolve when the embed page redirects to `/callback?…&state=<echo>`.
77
+ * `state` is compared in constant time.
78
+ *
79
+ * Rejects on bind failure, mint failure, 5-minute timeout, or
80
+ * state-mismatched callback (the bad request gets a styled error page
81
+ * and the server stays alive — the legitimate callback can still land).
82
+ */
83
+ export async function runWidgetRedirectFlow(opts) {
84
+ const state = randomBytes(16).toString('hex');
85
+ return new Promise((resolve, reject) => {
86
+ let resolved = false;
87
+ let timeout;
88
+ const finalize = (err, value) => {
89
+ if (resolved)
90
+ return;
91
+ resolved = true;
92
+ if (timeout)
93
+ clearTimeout(timeout);
94
+ server.close();
95
+ if (err)
96
+ reject(err);
97
+ else if (value)
98
+ resolve(value);
99
+ };
100
+ const server = createServer((req, res) => {
101
+ const url = new URL(req.url || '/', 'http://localhost');
102
+ if (url.pathname !== '/callback') {
103
+ res.writeHead(404).end('Not found');
104
+ return;
105
+ }
106
+ const receivedState = url.searchParams.get('state');
107
+ if (!receivedState || !safeEqualHexState(receivedState, state)) {
108
+ // INTENTIONALLY do NOT close the server here. The state nonce
109
+ // is 128 bits of randomness, so brute-forcing one bad callback
110
+ // per request is infeasible. Closing on first 400 would let an
111
+ // attacker (or a misconfigured redirect) DoS the legitimate
112
+ // browser callback that's about to arrive. The 5-minute timer
113
+ // is the upper bound on how long we'll wait either way.
114
+ res.writeHead(400, { 'Content-Type': 'text/html' }).end(errorHtml('Callback rejected', 'State mismatch. This callback did not match the request that started the flow — close this tab and re-run the command.'));
115
+ return;
116
+ }
117
+ // Convert URLSearchParams → plain object, dropping the bookkeeping
118
+ // `state` field (the caller doesn't need to re-validate it).
119
+ const query = {};
120
+ for (const [key, value] of url.searchParams.entries()) {
121
+ if (key === 'state')
122
+ continue;
123
+ query[key] = value;
124
+ }
125
+ res.writeHead(200, { 'Content-Type': 'text/html' }).end(successHtml(opts.successPageTitle ?? 'All done', 'You can close this tab and return to your terminal.'));
126
+ finalize(undefined, { state, query });
127
+ });
128
+ timeout = setTimeout(() => {
129
+ finalize(new Error(opts.timeoutMessage ?? 'Browser flow timed out after 5 minutes. Please try again.'));
130
+ }, REDIRECT_TIMEOUT_MS);
131
+ server.on('error', (err) => {
132
+ finalize(new Error(`Failed to start local callback server: ${err.message}`));
133
+ });
134
+ server.listen(0, '127.0.0.1', () => {
135
+ const addr = server.address();
136
+ if (!addr || typeof addr === 'string') {
137
+ finalize(new Error('Failed to obtain local callback port'));
138
+ return;
139
+ }
140
+ // Symmetric with the server bind above (`127.0.0.1` only). Using
141
+ // the `localhost` alias here would cause an IPv6 stall on modern
142
+ // Linux/macOS where Node's `dns.lookup` prefers `::1`. The backend
143
+ // returnUrl allow-list accepts both forms.
144
+ const returnUrl = `http://127.0.0.1:${addr.port}/callback`;
145
+ // Mint the session now that returnUrl is known. The backend's
146
+ // allow-list check at session creation can validate the URL the
147
+ // browser will actually be redirected to.
148
+ void (async () => {
149
+ let sessionToken;
150
+ try {
151
+ const minted = await opts.mintSession({ returnUrl });
152
+ sessionToken = minted.sessionToken;
153
+ }
154
+ catch (mintErr) {
155
+ finalize(mintErr instanceof Error ? mintErr : new Error(String(mintErr)));
156
+ return;
157
+ }
158
+ const browserUrl = buildEmbedUrl({
159
+ embedUrl: opts.embedUrl,
160
+ embedPath: opts.embedPath,
161
+ sessionToken,
162
+ returnUrl,
163
+ state,
164
+ network: opts.network,
165
+ extra: opts.extraSearchParams,
166
+ });
167
+ if (opts.noBrowser) {
168
+ opts.log('Open this URL in your browser to continue:');
169
+ opts.log('');
170
+ opts.log(browserUrl);
171
+ opts.log('');
172
+ opts.log('Waiting for completion...');
173
+ }
174
+ else {
175
+ opts.log('Opening browser...');
176
+ openBrowser(browserUrl).catch(() => {
177
+ opts.log('Could not open the browser automatically. Open this URL manually:');
178
+ opts.log('');
179
+ opts.log(browserUrl);
180
+ });
181
+ opts.log('Waiting for completion...');
182
+ }
183
+ })();
184
+ });
185
+ });
186
+ }
187
+ function buildEmbedUrl(opts) {
188
+ const url = new URL(opts.embedPath, opts.embedUrl);
189
+ url.searchParams.set('sessionToken', opts.sessionToken);
190
+ url.searchParams.set('returnUrl', opts.returnUrl);
191
+ url.searchParams.set('state', opts.state);
192
+ if (opts.extra) {
193
+ for (const [k, v] of Object.entries(opts.extra)) {
194
+ url.searchParams.set(k, v);
195
+ }
196
+ }
197
+ // Set AFTER `extra` so a caller can never accidentally clobber the
198
+ // network the embed app keys its backend selection off of —
199
+ // `URLSearchParams.set` overwrites, so writing it last makes it win.
200
+ url.searchParams.set('network', opts.network);
201
+ return url.toString();
202
+ }
203
+ function successHtml(title, message) {
204
+ // Plain HTML, no third-party assets, no scripts. The page is shown
205
+ // once and the user closes the tab; keep it self-contained so a
206
+ // captive-portal-style network doesn't break the success state.
207
+ return buildHtmlPage(title, message, '#f8f9fa', '#0f5132', '#d1e7dd');
208
+ }
209
+ function errorHtml(title, message) {
210
+ // Same shell as the success page (so the layout looks consistent if
211
+ // the user sees both back-to-back), but tinted red so a state mismatch
212
+ // or other 4xx response is visually distinct from "we're done".
213
+ return buildHtmlPage(title, message, '#f8f9fa', '#842029', '#f8d7da');
214
+ }
215
+ function buildHtmlPage(title, message, pageBg, fgColor, panelBg) {
216
+ return `<!doctype html>
217
+ <html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title></head>
218
+ <body style="font-family: system-ui, sans-serif; display:flex; align-items:center; justify-content:center; min-height:100vh; margin:0; background:${pageBg};">
219
+ <div style="text-align:center; max-width: 480px; padding: 24px; background:${panelBg}; color:${fgColor}; border-radius:8px;">
220
+ <h2 style="margin: 0 0 12px;">${escapeHtml(title)}</h2>
221
+ <p style="margin: 0;">${escapeHtml(message)}</p>
222
+ </div>
223
+ </body></html>`;
224
+ }
225
+ function escapeHtml(value) {
226
+ return value
227
+ .replace(/&/g, '&amp;')
228
+ .replace(/</g, '&lt;')
229
+ .replace(/>/g, '&gt;')
230
+ .replace(/"/g, '&quot;')
231
+ .replace(/'/g, '&#39;');
232
+ }
233
+ /**
234
+ * Hosts a request to `mintSelfWidgetSession` may be sent over plaintext
235
+ * without warning. Loopback addresses are always safe; every other host
236
+ * must use `https:` because we're about to send the user's NVM API key
237
+ * in the `Authorization` header. The `custom` environment variable
238
+ * (`NVM_BACKEND_URL`) is the only realistic way a user could accidentally
239
+ * point this at a plaintext non-localhost URL.
240
+ */
241
+ const LOOPBACK_HOSTNAMES = new Set([
242
+ 'localhost',
243
+ '127.0.0.1',
244
+ '::1',
245
+ '[::1]',
246
+ ]);
247
+ export async function mintSelfWidgetSession(args) {
248
+ const url = new URL('/api/v1/widgets/session/self', args.backendUrl);
249
+ // Refuse to send the API key over plaintext to a non-loopback host.
250
+ // All four built-in environments are `https://`, so this only bites
251
+ // when a user has set `NVM_BACKEND_URL` to a custom plaintext
252
+ // endpoint — exactly the case where they'd otherwise leak the key
253
+ // without realising.
254
+ if (url.protocol === 'http:' &&
255
+ !LOOPBACK_HOSTNAMES.has(url.hostname.toLowerCase())) {
256
+ throw new Error(`Refusing to send the NVM API key over plaintext to ${url.host}. Set NVM_BACKEND_URL to an https:// endpoint, or use a loopback host (localhost / 127.0.0.1 / [::1]).`);
257
+ }
258
+ // 15s ceiling for the request — without it, an unreachable backend
259
+ // (DNS failure, TLS handshake hang, network partition) leaves the CLI
260
+ // frozen with no output. `AbortSignal.timeout` (Node 17.3+) is the
261
+ // idiomatic replacement for a hand-rolled `AbortController` + setTimeout.
262
+ let response;
263
+ try {
264
+ response = await fetch(url, {
265
+ method: 'POST',
266
+ headers: {
267
+ 'Content-Type': 'application/json',
268
+ Authorization: `Bearer ${args.nvmApiKey}`,
269
+ },
270
+ body: JSON.stringify({
271
+ orgId: args.orgId,
272
+ ...(args.returnUrl ? { returnUrl: args.returnUrl } : {}),
273
+ }),
274
+ signal: AbortSignal.timeout(SELF_MINT_FETCH_TIMEOUT_MS),
275
+ });
276
+ }
277
+ catch (cause) {
278
+ if (cause instanceof Error && (cause.name === 'TimeoutError' || cause.name === 'AbortError')) {
279
+ throw new Error(`Self-mint widget session request timed out after ${SELF_MINT_FETCH_TIMEOUT_MS / 1000}s — check that ${args.backendUrl} is reachable.`);
280
+ }
281
+ throw cause;
282
+ }
283
+ if (!response.ok) {
284
+ const detail = await response
285
+ .json()
286
+ .catch(() => ({ message: response.statusText }));
287
+ const message = detail.message ??
288
+ `Self-mint widget session failed (HTTP ${response.status})`;
289
+ const err = new Error(message);
290
+ err.status = response.status;
291
+ err.apiCode = detail.code;
292
+ throw err;
293
+ }
294
+ return (await response.json());
295
+ }
296
+ //# sourceMappingURL=widget-redirect-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget-redirect-flow.js","sourceRoot":"","sources":["../../src/utils/widget-redirect-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmC,MAAM,MAAM,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAW1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAA4B;IAC9D,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC;QACZ,KAAK,cAAc;YACjB,OAAO,MAAM,CAAA;QACf,KAAK,SAAS,CAAC;QACf,KAAK,iBAAiB;YACpB,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ;YACX,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;gBACrF,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,SAAS,CAAA;IACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,GAAG,MAAM,CAAA;AAEzC;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAEzC;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;IAC3D,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAA;IACxF,OAAO,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;AACpF,CAAC;AA6CD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAA+B;IAE/B,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE7C,OAAO,IAAI,OAAO,CAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC/D,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,OAAkD,CAAA;QAEtD,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,KAAgC,EAAQ,EAAE;YACvE,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,OAAO;gBAAE,YAAY,CAAC,OAAO,CAAC,CAAA;YAClC,MAAM,CAAC,KAAK,EAAE,CAAA;YACd,IAAI,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAA;iBACf,IAAI,KAAK;gBAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC,CAAA;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAoB,EAAE,GAAmB,EAAE,EAAE;YACxE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAA;YACvD,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;gBACnC,OAAM;YACR,CAAC;YAED,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACnD,IAAI,CAAC,aAAa,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC/D,8DAA8D;gBAC9D,+DAA+D;gBAC/D,+DAA+D;gBAC/D,4DAA4D;gBAC5D,8DAA8D;gBAC9D,wDAAwD;gBACxD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CACrD,SAAS,CACP,mBAAmB,EACnB,wHAAwH,CACzH,CACF,CAAA;gBACD,OAAM;YACR,CAAC;YAED,mEAAmE;YACnE,6DAA6D;YAC7D,MAAM,KAAK,GAA2B,EAAE,CAAA;YACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;gBACtD,IAAI,GAAG,KAAK,OAAO;oBAAE,SAAQ;gBAC7B,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;YACpB,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CACrD,WAAW,CACT,IAAI,CAAC,gBAAgB,IAAI,UAAU,EACnC,qDAAqD,CACtD,CACF,CAAA;YACD,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACvC,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,2DAA2D,CAAC,CAAC,CAAA;QACzG,CAAC,EAAE,mBAAmB,CAAC,CAAA;QAEvB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,QAAQ,CAAC,IAAI,KAAK,CAAC,0CAA0C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QAC9E,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;YAC7B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtC,QAAQ,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,iEAAiE;YACjE,iEAAiE;YACjE,mEAAmE;YACnE,2CAA2C;YAC3C,MAAM,SAAS,GAAG,oBAAoB,IAAI,CAAC,IAAI,WAAW,CAAA;YAE1D,8DAA8D;YAC9D,gEAAgE;YAChE,0CAA0C;YAC1C,KAAK,CAAC,KAAK,IAAI,EAAE;gBACf,IAAI,YAAoB,CAAA;gBACxB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;oBACpD,YAAY,GAAG,MAAM,CAAC,YAAY,CAAA;gBACpC,CAAC;gBAAC,OAAO,OAAO,EAAE,CAAC;oBACjB,QAAQ,CAAC,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;oBACzE,OAAM;gBACR,CAAC;gBAED,MAAM,UAAU,GAAG,aAAa,CAAC;oBAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,YAAY;oBACZ,SAAS;oBACT,KAAK;oBACL,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,IAAI,CAAC,iBAAiB;iBAC9B,CAAC,CAAA;gBAEF,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnB,IAAI,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;oBACtD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;oBACZ,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;oBACpB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;oBACZ,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;gBACvC,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;oBAC9B,WAAW,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;wBACjC,IAAI,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAA;wBAC7E,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;wBACZ,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;oBACtB,CAAC,CAAC,CAAA;oBACF,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;gBACvC,CAAC;YACH,CAAC,CAAC,EAAE,CAAA;QACN,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAYD,SAAS,aAAa,CAAC,IAA0B;IAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IAClD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACvD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACzC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IACD,mEAAmE;IACnE,4DAA4D;IAC5D,qEAAqE;IACrE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7C,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,OAAe;IACjD,mEAAmE;IACnE,gEAAgE;IAChE,gEAAgE;IAChE,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;AACvE,CAAC;AAED,SAAS,SAAS,CAAC,KAAa,EAAE,OAAe;IAC/C,oEAAoE;IACpE,uEAAuE;IACvE,gEAAgE;IAChE,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;AACvE,CAAC;AAED,SAAS,aAAa,CACpB,KAAa,EACb,OAAe,EACf,MAAc,EACd,OAAe,EACf,OAAe;IAEf,OAAO;2CACkC,UAAU,CAAC,KAAK,CAAC;oJACwF,MAAM;+EAC3E,OAAO,WAAW,OAAO;oCACpE,UAAU,CAAC,KAAK,CAAC;4BACzB,UAAU,CAAC,OAAO,CAAC;;eAEhC,CAAA;AACf,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC3B,CAAC;AAkBD;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC;IACtD,WAAW;IACX,WAAW;IACX,KAAK;IACL,OAAO;CACR,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAK3C;IACC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAEpE,oEAAoE;IACpE,oEAAoE;IACpE,8DAA8D;IAC9D,kEAAkE;IAClE,qBAAqB;IACrB,IACE,GAAG,CAAC,QAAQ,KAAK,OAAO;QACxB,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,EACnD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,sDAAsD,GAAG,CAAC,IAAI,wGAAwG,CACvK,CAAA;IACH,CAAC;IAED,mEAAmE;IACnE,sEAAsE;IACtE,mEAAmE;IACnE,0EAA0E;IAC1E,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC1B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,SAAS,EAAE;aAC1C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzD,CAAC;YACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,0BAA0B,CAAC;SACxD,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;YAC7F,MAAM,IAAI,KAAK,CACb,oDAAoD,0BAA0B,GAAG,IAAI,kBAAkB,IAAI,CAAC,UAAU,gBAAgB,CACvI,CAAA;QACH,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ;aAC1B,IAAI,EAAE;aACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAA;QAClD,MAAM,OAAO,GACV,MAA+B,CAAC,OAAO;YACxC,yCAAyC,QAAQ,CAAC,MAAM,GAAG,CAAA;QAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,OAAO,CAAkD,CAAA;QAC/E,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;QAC5B,GAAG,CAAC,OAAO,GAAI,MAA4B,CAAC,IAAI,CAAA;QAChD,MAAM,GAAG,CAAA;IACX,CAAC;IACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAA;AAC3D,CAAC"}