runnix 0.4.0-rc.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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # runnix
2
+
3
+ Command-line tool for [Runnix](https://runnix.run), the PostgreSQL-native
4
+ workflow orchestration platform: deploy runsets with their worker images,
5
+ verify worker builds, inspect deploys, promote rollbacks.
6
+
7
+ ```bash
8
+ npm install -g runnix # or: npx runnix <command>
9
+ ```
10
+
11
+ Node 22+. The CLI loads `runnix.config.ts` and your TypeScript function
12
+ modules directly; your project needs no build step.
13
+
14
+ ## Commands
15
+
16
+ | Command | What it does |
17
+ | --- | --- |
18
+ | `runnix deploy` | Discover functions, resolve runsets and post the manifests. `--image <ref>` (or `image` in `runnix.config.ts`) names the worker image; the digest is resolved from your own `docker` after the push. Records git and CI provenance on the deploy. |
19
+ | `runnix verify` | Build/startup guard: checks a worker build carries exactly one runset's complete, current function set. `--offline` skips the control-plane comparison. |
20
+ | `runnix deployments` | Current deployment per runset. `--history [n]` lists recent deploys with every runset each shipped. |
21
+ | `runnix promote --to <deploymentId>` | Roll a runset back (or forward) by replaying a previous deployment's snapshot as a new one. |
22
+ | `runnix init` | Scaffold a project. |
23
+ | `runnix functions list`, `runnix test <id>` | Local discovery and execution without a control plane. |
24
+
25
+ Remote commands read `RUNNIX_URL` and `RUNNIX_API_KEY` (a deploy-scoped key)
26
+ from the environment, or take `--url`.
27
+
28
+ ## CI order
29
+
30
+ ```bash
31
+ runnix verify --offline
32
+ docker build -t ghcr.io/acme/billing:$SHA . && docker push ghcr.io/acme/billing:$SHA
33
+ runnix deploy --image ghcr.io/acme/billing:$SHA --version $SHA
34
+ runnix verify
35
+ ```
36
+
37
+ Production-type environments refuse an image with no resolvable digest unless
38
+ you pass `--allow-unpinned`.
39
+
40
+ ## Documentation
41
+
42
+ - Runsets and deploys: https://runnix.run/docs/runsets
43
+ - Self-hosted worker pools: https://runnix.run/docs/worker-pool
44
+
45
+ ## License
46
+
47
+ AGPL-3.0-or-later. The SDK you import into your functions (`@runnix/sdk`) is
48
+ Apache-2.0.
package/bin/runnix.mjs ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ // Runnix CLI entry. Registers tsx's ESM loader before loading the compiled
3
+ // CLI so `runnix.config.ts` and the project's TypeScript function modules can
4
+ // be imported directly - the user's project needs no build step and no
5
+ // `--experimental-strip-types` (which cannot resolve extensionless imports).
6
+ import { register } from 'tsx/esm/api'
7
+
8
+ register()
9
+ await import('../dist/main.js')
@@ -0,0 +1,92 @@
1
+ /** Local check: every discovered function must resolve to `runset`. */
2
+ export function checkSingleRunset(functions, runset) {
3
+ return functions
4
+ .filter((fn) => fn.runset !== runset)
5
+ .map((fn) => ({
6
+ kind: 'mixed-runset',
7
+ identifier: fn.definition.identifier,
8
+ detail: `resolves to runset "${fn.runset}" but this build is verified as "${runset}" (${fn.modulePath})`,
9
+ }));
10
+ }
11
+ /** Remote check: the build must match the runset's deployed manifest exactly. */
12
+ export function compareAgainstManifest(local, deployed) {
13
+ const problems = [];
14
+ const localByIdentifier = new Map(local.map((fn) => [fn.identifier, fn.definitionHash]));
15
+ const deployedByIdentifier = new Map(deployed.map((fn) => [fn.identifier, fn.definitionHash]));
16
+ for (const [identifier, hash] of deployedByIdentifier) {
17
+ const localHash = localByIdentifier.get(identifier);
18
+ if (localHash === undefined) {
19
+ problems.push({
20
+ kind: 'missing',
21
+ identifier,
22
+ detail: 'deployed in this runset but absent from the build (partial image)',
23
+ });
24
+ }
25
+ else if (localHash !== hash) {
26
+ problems.push({
27
+ kind: 'hash-mismatch',
28
+ identifier,
29
+ detail: `build carries ${localHash.slice(0, 12)}, deployment expects ${hash.slice(0, 12)} (stale or unreleased build)`,
30
+ });
31
+ }
32
+ }
33
+ for (const identifier of localByIdentifier.keys()) {
34
+ if (!deployedByIdentifier.has(identifier)) {
35
+ problems.push({
36
+ kind: 'undeployed',
37
+ identifier,
38
+ detail: 'present in the build but not in the deployed manifest (deploy before shipping the image)',
39
+ });
40
+ }
41
+ }
42
+ return problems.sort((a, b) => a.identifier.localeCompare(b.identifier));
43
+ }
44
+ export function partitionByRunset(functions, owned) {
45
+ const byIdentifier = new Map();
46
+ const duplicates = new Map();
47
+ for (const fn of functions) {
48
+ const id = fn.definition.identifier;
49
+ const existing = byIdentifier.get(id);
50
+ if (!existing) {
51
+ byIdentifier.set(id, fn);
52
+ continue;
53
+ }
54
+ // Re-exports of the same build are harmless; different hashes are a bug.
55
+ if (existing.definitionHash !== fn.definitionHash || existing.runset !== fn.runset) {
56
+ const set = duplicates.get(id) ?? new Set([existing.modulePath]);
57
+ set.add(fn.modulePath);
58
+ duplicates.set(id, set);
59
+ }
60
+ }
61
+ const byRunset = new Map();
62
+ for (const fn of byIdentifier.values()) {
63
+ const list = byRunset.get(fn.runset) ?? [];
64
+ list.push(fn);
65
+ byRunset.set(fn.runset, list);
66
+ }
67
+ const body = {
68
+ runsets: [...byRunset.entries()]
69
+ .sort(([a], [b]) => a.localeCompare(b))
70
+ .map(([runset, fns]) => ({
71
+ runset,
72
+ functions: fns
73
+ .sort((a, b) => a.definition.identifier.localeCompare(b.definition.identifier))
74
+ .map((fn) => ({
75
+ definition: fn.definition,
76
+ definitionHash: fn.definitionHash,
77
+ region: fn.region,
78
+ requiredEnv: fn.requiredEnv,
79
+ })),
80
+ })),
81
+ };
82
+ if (owned && owned.length > 0)
83
+ body.owned = owned;
84
+ return {
85
+ body,
86
+ duplicates: [...duplicates.entries()].map(([identifier, modules]) => ({
87
+ identifier,
88
+ modules: [...modules].sort(),
89
+ })),
90
+ };
91
+ }
92
+ //# sourceMappingURL=deploy-logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-logic.js","sourceRoot":"","sources":["../src/deploy-logic.ts"],"names":[],"mappings":"AAoDA,uEAAuE;AACvE,MAAM,UAAU,iBAAiB,CAAC,SAA+B,EAAE,MAAc;IAC/E,OAAO,SAAS;SACb,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC;SACpC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACZ,IAAI,EAAE,cAAuB;QAC7B,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU;QACpC,MAAM,EAAE,uBAAuB,EAAE,CAAC,MAAM,oCAAoC,MAAM,MAAM,EAAE,CAAC,UAAU,GAAG;KACzG,CAAC,CAAC,CAAA;AACP,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CACpC,KAAuD,EACvD,QAA0D;IAE1D,MAAM,QAAQ,GAAoB,EAAE,CAAA;IACpC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;IACxF,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;IAE9F,KAAK,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,oBAAoB,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACnD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,SAAS;gBACf,UAAU;gBACV,MAAM,EAAE,mEAAmE;aAC5E,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,eAAe;gBACrB,UAAU;gBACV,MAAM,EAAE,iBAAiB,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,wBAAwB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,8BAA8B;aACvH,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC;QAClD,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,YAAY;gBAClB,UAAU;gBACV,MAAM,EAAE,0FAA0F;aACnG,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAA+B,EAAE,KAAgB;IACjF,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAA;IAC1D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAA;IACjD,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,CAAA;QACnC,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;YACxB,SAAQ;QACV,CAAC;QACD,yEAAyE;QACzE,IAAI,QAAQ,CAAC,cAAc,KAAK,EAAE,CAAC,cAAc,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;YACnF,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAA;YAChE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,CAAA;YACtB,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAA;IACxD,KAAK,MAAM,EAAE,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;QAC1C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACb,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED,MAAM,IAAI,GAAsB;QAC9B,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;aAC7B,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;aACtC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,MAAM;YACN,SAAS,EAAE,GAAG;iBACX,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;iBAC9E,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACZ,UAAU,EAAE,EAAE,CAAC,UAAU;gBACzB,cAAc,EAAE,EAAE,CAAC,cAAc;gBACjC,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,WAAW,EAAE,EAAE,CAAC,WAAW;aAC5B,CAAC,CAAC;SACN,CAAC,CAAC;KACN,CAAA;IACD,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IAEjD,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YACpE,UAAU;YACV,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE;SAC7B,CAAC,CAAC;KACJ,CAAA;AACH,CAAC"}
@@ -0,0 +1,142 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { parseImageReference, sameRepository } from '@runnix/protocol';
4
+ const execFileAsync = promisify(execFile);
5
+ /** Image cascade (Decision #24): --image > runsets[x].image > config.image > none. */
6
+ export function resolveImage(input) {
7
+ return input.flag ?? input.config.runsets?.[input.runset]?.image ?? input.config.image;
8
+ }
9
+ /**
10
+ * From `docker image inspect --format '{{json .RepoDigests}}'`: the digest
11
+ * for THIS repository - an image tagged into several repositories carries
12
+ * one RepoDigest per repository, and only the matching one is the pinned
13
+ * generation the pool will pull.
14
+ */
15
+ export function pickRepoDigest(repoDigests, reference) {
16
+ for (const entry of repoDigests) {
17
+ const at = entry.indexOf('@');
18
+ if (at === -1)
19
+ continue;
20
+ if (sameRepository(entry.slice(0, at), reference))
21
+ return entry.slice(at + 1);
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Resolve the pushed digest for a reference using the developer's local
27
+ * docker (their login - the control plane never touches a registry). First
28
+ * hit wins: an explicit digest, a digest in the reference, the local
29
+ * image's RepoDigests, then the registry via buildx imagetools. Undefined
30
+ * when nothing resolves (not built/pushed yet, or no docker at all).
31
+ */
32
+ export async function resolveDigest(reference, explicit) {
33
+ if (explicit)
34
+ return explicit;
35
+ const parsed = parseImageReference(reference);
36
+ if (parsed?.digest)
37
+ return parsed.digest;
38
+ try {
39
+ const { stdout } = await execFileAsync('docker', [
40
+ 'image',
41
+ 'inspect',
42
+ '--format',
43
+ '{{json .RepoDigests}}',
44
+ reference,
45
+ ]);
46
+ const digest = pickRepoDigest(JSON.parse(stdout.trim()), reference);
47
+ if (digest)
48
+ return digest;
49
+ }
50
+ catch {
51
+ // Not local (or no docker): try the registry.
52
+ }
53
+ try {
54
+ const { stdout } = await execFileAsync('docker', [
55
+ 'buildx',
56
+ 'imagetools',
57
+ 'inspect',
58
+ reference,
59
+ '--format',
60
+ '{{.Manifest.Digest}}',
61
+ ]);
62
+ const digest = stdout.trim();
63
+ if (/^sha256:[a-f0-9]{64}$/.test(digest))
64
+ return digest;
65
+ }
66
+ catch {
67
+ // Unreachable registry, or not pushed.
68
+ }
69
+ return undefined;
70
+ }
71
+ /** Short git SHA as the default deployment version label; undefined outside a repo. */
72
+ export async function gitShortSha() {
73
+ try {
74
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--short=12', 'HEAD']);
75
+ return stdout.trim() || undefined;
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ }
81
+ /** Strip embedded credentials from a remote URL (`https://user:token@host/...` is common in CI). */
82
+ export function redactRemote(url) {
83
+ return url.replace(/^(\w+:\/\/)[^@/]+@/, '$1');
84
+ }
85
+ /**
86
+ * Best-effort git provenance for the deploy record (Decision #25): sha,
87
+ * branch, dirty flag, remote, subject line. Undefined outside a repository;
88
+ * every field beyond the sha is optional so a shallow CI checkout still
89
+ * yields a record.
90
+ */
91
+ export async function gitMetadata(cwd = process.cwd()) {
92
+ const run = async (args) => (await execFileAsync('git', args, { cwd })).stdout.trim();
93
+ let sha;
94
+ try {
95
+ sha = await run(['rev-parse', 'HEAD']);
96
+ }
97
+ catch {
98
+ return undefined;
99
+ }
100
+ if (!/^[0-9a-f]{7,64}$/.test(sha))
101
+ return undefined;
102
+ const out = { sha };
103
+ const ignore = () => { };
104
+ await Promise.all([
105
+ run(['rev-parse', '--abbrev-ref', 'HEAD']).then((branch) => {
106
+ if (branch && branch !== 'HEAD')
107
+ out.branch = branch.slice(0, 200);
108
+ }, ignore),
109
+ run(['status', '--porcelain']).then((status) => {
110
+ out.dirty = status.length > 0;
111
+ }, ignore),
112
+ run(['remote', 'get-url', 'origin']).then((remote) => {
113
+ if (remote)
114
+ out.remote = redactRemote(remote).slice(0, 300);
115
+ }, ignore),
116
+ run(['log', '-1', '--pretty=%s']).then((message) => {
117
+ if (message)
118
+ out.message = message.slice(0, 200);
119
+ }, ignore),
120
+ ]);
121
+ return out;
122
+ }
123
+ /** CI run provenance from the well-known environment variables; undefined outside CI. */
124
+ export function ciMetadata(env = process.env) {
125
+ const withUrl = (provider, runUrl) => runUrl ? { provider, runUrl: runUrl.slice(0, 500) } : { provider };
126
+ if (env.GITHUB_ACTIONS === 'true') {
127
+ const runUrl = env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID
128
+ ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`
129
+ : undefined;
130
+ return withUrl('github', runUrl);
131
+ }
132
+ if (env.GITLAB_CI === 'true')
133
+ return withUrl('gitlab', env.CI_JOB_URL);
134
+ if (env.CIRCLECI === 'true')
135
+ return withUrl('circleci', env.CIRCLE_BUILD_URL);
136
+ if (env.BUILDKITE === 'true')
137
+ return withUrl('buildkite', env.BUILDKITE_BUILD_URL);
138
+ if (env.CI)
139
+ return { provider: 'ci' };
140
+ return undefined;
141
+ }
142
+ //# sourceMappingURL=image-digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-digest.js","sourceRoot":"","sources":["../src/image-digest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGtE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;AAEzC,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,KAA8D;IACzF,OAAO,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAA;AACxF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,WAAqB,EAAE,SAAiB;IACrE,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAQ;QACvB,IAAI,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;IAC/E,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,QAAiB;IACtE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAA;IAC7C,IAAI,MAAM,EAAE,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;YAC/C,OAAO;YACP,SAAS;YACT,UAAU;YACV,uBAAuB;YACvB,SAAS;SACV,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAa,EAAE,SAAS,CAAC,CAAA;QAC/E,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;YAC/C,QAAQ;YACR,YAAY;YACZ,SAAS;YACT,SAAS;YACT,UAAU;YACV,sBAAsB;SACvB,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;QAC5B,IAAI,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAA;QAClF,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,SAAS,CAAA;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAUD,oGAAoG;AACpG,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAA;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,KAAK,EAAE,IAAc,EAAE,EAAE,CAAC,CAAC,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAC/F,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IACnD,MAAM,GAAG,GAAgB,EAAE,GAAG,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;IACvB,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,GAAG,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACzD,IAAI,MAAM,IAAI,MAAM,KAAK,MAAM;gBAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACpE,CAAC,EAAE,MAAM,CAAC;QACV,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YAC7C,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QAC/B,CAAC,EAAE,MAAM,CAAC;QACV,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACnD,IAAI,MAAM;gBAAE,GAAG,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QAC7D,CAAC,EAAE,MAAM,CAAC;QACV,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,IAAI,OAAO;gBAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QAClD,CAAC,EAAE,MAAM,CAAC;KACX,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAE,MAAe,EAAE,EAAE,CACpD,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAA;IACpE,IAAI,GAAG,CAAC,cAAc,KAAK,MAAM,EAAE,CAAC;QAClC,MAAM,MAAM,GACV,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,aAAa;YACjE,CAAC,CAAC,GAAG,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,iBAAiB,iBAAiB,GAAG,CAAC,aAAa,EAAE;YACvF,CAAC,CAAC,SAAS,CAAA;QACf,OAAO,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAClC,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAA;IACtE,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAC7E,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAA;IAClF,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IACrC,OAAO,SAAS,CAAA;AAClB,CAAC"}