home-hosted 0.2.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/AGENTS.md +128 -0
- package/LICENSE +21 -0
- package/README.md +433 -0
- package/UI_CREATION.md +148 -0
- package/bin/home-hosted.mjs +34 -0
- package/dist/cli.js +7238 -0
- package/dist/cli.js.map +1 -0
- package/package.json +112 -0
- package/uis/stock/dist/assets/index-CmkrtNU0.css +2 -0
- package/uis/stock/dist/assets/index-DqcdQi-W.js +47 -0
- package/uis/stock/dist/assets/jetbrains-mono-latin-B9CIFXIH.woff2 +0 -0
- package/uis/stock/dist/assets/jetbrains-mono-latin-ext-DBQx-q_a.woff2 +0 -0
- package/uis/stock/dist/assets/plex-sans-latin-IvpUvPa2.woff2 +0 -0
- package/uis/stock/dist/assets/plex-sans-latin-ext-CIII54If.woff2 +0 -0
- package/uis/stock/dist/index.html +25 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/helpers/cookies.ts","../src/helpers/factory.ts","../src/shared/contracts.ts","../src/helpers/openapi.ts","../src/helpers/validator.ts","../src/middleware/loopback.ts","../src/helpers/atomic.ts","../src/config/secrets.ts","../src/services/auth.ts","../src/middleware/auth.ts","../src/helpers/bind.ts","../src/services/exposure.ts","../src/api/auth/$.routes.ts","../src/helpers/logger.ts","../src/helpers/validate.ts","../src/helpers/paths.ts","../src/services/state.ts","../src/api/backups.ts","../src/helpers/deferred.ts","../src/api/control.ts","../src/api/events.ts","../src/api/health.ts","../src/api/logs.ts","../src/api/metrics.ts","../src/providers/telegram.ts","../src/api/notifications.ts","../src/config/schema.ts","../src/config/seed.ts","../src/config/store.ts","../src/api/servers/$.routes.ts","../src/api/settings.ts","../src/api/state.ts","../src/api/static.ts","../src/api/tls.ts","../src/helpers/error.ts","../src/openapi.ts","../src/app.ts","../src/helpers/daemon.ts","../src/helpers/open.ts","../src/helpers/template.ts","../src/providers/port.ts","../src/helpers/env-file.ts","../src/providers/archive.ts","../src/helpers/backoff.ts","../src/providers/health-check.ts","../src/providers/proc.ts","../src/providers/process.ts","../src/services/dependencies.ts","../src/services/log-buffer.ts","../src/services/supervisor.ts","../src/services/backups.ts","../src/services/control-server.ts","../src/services/events.ts","../src/services/history.ts","../src/providers/host.ts","../src/services/host-monitor.ts","../src/services/log-files.ts","../src/services/notifications.ts","../src/services/tls.ts","../src/services/ui.ts","../src/index.ts","../src/cli.ts"],"sourcesContent":["/** Minimal cookie helpers, enough for one httpOnly session cookie. */\nexport interface CookieOptions {\n maxAgeMs?: number\n httpOnly?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n secure?: boolean\n path?: string\n}\n\nexport function parseCookies(header: string | null | undefined): Record<string, string> {\n const cookies: Record<string, string> = {}\n if (!header)\n return cookies\n for (const part of header.split(';')) {\n const separator = part.indexOf('=')\n if (separator < 0)\n continue\n const name = part.slice(0, separator).trim()\n if (name.length === 0)\n continue\n const value = part.slice(separator + 1).trim()\n try {\n cookies[name] = decodeURIComponent(value)\n }\n catch {\n cookies[name] = value\n }\n }\n return cookies\n}\n\nexport function serializeCookie(name: string, value: string, options: CookieOptions = {}): string {\n const parts = [`${name}=${encodeURIComponent(value)}`]\n parts.push(`Path=${options.path ?? '/'}`)\n if (options.maxAgeMs !== undefined)\n parts.push(`Max-Age=${Math.max(0, Math.floor(options.maxAgeMs / 1000))}`)\n if (options.httpOnly !== false)\n parts.push('HttpOnly')\n parts.push(`SameSite=${options.sameSite ?? 'Strict'}`)\n if (options.secure)\n parts.push('Secure')\n return parts.join('; ')\n}\n","import { createFactory } from 'hono/factory'\n\n/**\n * Every route is built from this factory and chained (`app.get(...).post(...)`)\n * so Hono keeps the full route map in its type — which is what `AppType` exports\n * for `hc<AppType>` clients and for the generated OpenAPI document.\n */\nexport const appFactory = createFactory()\n","import { type } from 'arktype'\n\n/**\n * Schemas shared by the control plane and the SPA: the server entry shape, the\n * control panel's own settings, API DTOs and SSE frames. Nothing here knows\n * about a particular server — an entry carries its own command, args, env and\n * bootstrap.\n */\n\n/** `local` -> 127.0.0.1, `lan` -> 0.0.0.0, or an explicit IPv4 to bind. */\nexport const bindSchema = type('\"local\" | \"lan\" | /^\\\\d{1,3}(?:\\\\.\\\\d{1,3}){3}$/')\nexport type Bind = typeof bindSchema.infer\n\n/** Parses a bind value (`local` | `lan` | ipv4); null when it is not one. */\nexport function parseBind(value: string): Bind | null {\n const parsed = bindSchema(value)\n return parsed instanceof type.errors ? null : parsed\n}\n\n/** `null` means \"no port\": no readiness probe, no health supervision, no preflight. */\nexport const portSchema = type('1 <= number.integer <= 65535 | null')\n\nexport const restartSchema = type({\n enabled: 'boolean = true',\n maxRetries: 'number.integer >= 0 = 3',\n baseDelayMs: 'number >= 0 = 1000',\n factor: 'number >= 1 = 2',\n maxDelayMs: 'number >= 0 = 30000',\n /** A process alive this long is considered healthy again and the retry counter resets. */\n resetAfterMs: 'number >= 0 = 60000',\n}).onUndeclaredKey('reject')\n\nexport type RestartConfig = typeof restartSchema.infer\n\nexport const httpCheckSchema = type({\n /** Path on the server's own port, e.g. `/healthz`. */\n path: 'string = \"/\"',\n method: '\"GET\" | \"HEAD\" = \"GET\"',\n /** Exact status to accept; `null` (or omitted) means any status below `expectStatusBelow`. */\n expectStatus: 'number.integer | null?',\n expectStatusBelow: 'number.integer = 400',\n /** Substring that must appear in the response body. */\n expectBody: 'string = \"\"',\n}).onUndeclaredKey('reject')\nexport type HttpCheckConfig = typeof httpCheckSchema.infer\n\n/** Restart guards for the process tree. */\nexport const resourcesSchema = type({\n /** Restart when the tree's RSS exceeds this; 0 disables. */\n maxRssBytes: 'number.integer >= 0 = 0',\n}).onUndeclaredKey('reject')\nexport type ResourcesConfig = typeof resourcesSchema.infer\n\nexport const healthSchema = type({\n enabled: 'boolean = true',\n /** `port` = TCP connect only; `http` = fetch `http.path` and assert the response. */\n mode: '\"port\" | \"http\" = \"port\"',\n http: httpCheckSchema.default(() => ({})),\n intervalMs: 'number >= 500 = 5000',\n timeoutMs: 'number >= 100 = 1500',\n /** Consecutive failed probes before the warning state is shown. */\n unhealthyThreshold: 'number.integer >= 1 = 3',\n /** 0 disables it; otherwise a port stuck unhealthy this long forces a restart. */\n forceRestartAfterMs: 'number >= 0 = 0',\n /** How long to wait for the port to accept connections after spawn. */\n startTimeoutMs: 'number >= 0 = 20000',\n}).onUndeclaredKey('reject')\n\nexport type HealthConfig = typeof healthSchema.infer\n\nexport const stopSchema = type({\n signal: '\"SIGTERM\" | \"SIGINT\" | \"SIGKILL\" = \"SIGTERM\"',\n killGroup: 'boolean = true',\n graceMs: 'number >= 0 = 5000',\n /** Last resort for wrappers that detach their real server. */\n killPortHolders: 'boolean = false',\n}).onUndeclaredKey('reject')\n\nexport type StopConfig = typeof stopSchema.infer\n\nexport const bootstrapSchema = type({\n command: 'string',\n args: type('string[]').default(() => []),\n env: type('Record<string, string>').default(() => ({})),\n timeoutMs: 'number >= 1000 = 120000',\n /** Run once per `up` session; whatever it installs persists on disk. */\n runOnce: 'boolean = true',\n}).onUndeclaredKey('reject')\nexport type BootstrapConfig = typeof bootstrapSchema.infer\nexport const bootstrapOrNullSchema = bootstrapSchema.or(type('null'))\n\nexport const logBufferLinesSchema = type('50 <= number.integer <= 100000')\n\nexport const serverSchema = type({\n id: '/^[a-z0-9][a-z0-9_-]*$/',\n label: 'string?',\n enabled: 'boolean = true',\n autostart: 'boolean = false',\n command: 'string >= 1',\n args: type('string[]').default(() => []),\n cwd: 'string = \".\"',\n env: type('Record<string, string>').default(() => ({})),\n /**\n * `ENV=path` pairs: exported to the process (overriding `env`) *and* the path\n * is backed up automatically — one declaration for data directories.\n */\n dataEnvs: type('Record<string, string>').default(() => ({})),\n bootstrap: bootstrapOrNullSchema.optional(),\n port: portSchema.optional(),\n bind: bindSchema.default(() => 'local' as const),\n onPortConflict: '\"block\" | \"warn\" = \"block\"',\n restart: restartSchema.default(() => ({})),\n health: healthSchema.default(() => ({})),\n stop: stopSchema.default(() => ({})),\n logBufferLines: logBufferLinesSchema.default(() => 500),\n /** Ids this server needs running first (and healthy); stopped in reverse order. */\n dependsOn: type('string[]').default(() => []),\n /** Optional KEY=value file loaded at spawn; its values override `env`. */\n envFile: 'string = \"\"',\n resources: resourcesSchema.default(() => ({})),\n /** Paths included in backups for this server (templates allowed). */\n backupPaths: type('string[]').default(() => []),\n}).onUndeclaredKey('reject')\nexport type ServerConfig = Omit<typeof serverSchema.infer, 'port'> & { port: number | null }\n\n/**\n * Authentication for the control panel itself. The password never lives here —\n * only the policy does; its scrypt hash sits in a git-ignored secrets file.\n */\nexport const authSchema = type({\n enabled: 'boolean = true',\n sessionTtlMs: 'number >= 60000 = 604800000',\n /** `auto` adds `Secure` when the request arrived over https (proxy-aware). */\n cookieSecure: '\"auto\" | \"always\" | \"never\" = \"auto\"',\n /** Trust `x-forwarded-*` from a reverse proxy; also drives the client IP. */\n trustProxy: 'boolean = false',\n maxLoginAttempts: 'number.integer >= 1 = 5',\n lockoutMs: 'number >= 1000 = 60000',\n}).onUndeclaredKey('reject')\nexport type AuthConfig = typeof authSchema.infer\n\n/** Outbound crash/health notifications. The bot token lives in the secrets file. */\nexport const telegramSchema = type({\n enabled: 'boolean = false',\n chatId: 'string = \"\"',\n onCrash: 'boolean = true',\n onUnhealthy: 'boolean = true',\n onForcedRestart: 'boolean = true',\n onRecovered: 'boolean = false',\n /** Host vitals breaches (disk, memory, swap, load, temperature). */\n onHost: 'boolean = true',\n /** Per server *and* reason, so a flapping server cannot spam the chat. */\n cooldownMs: 'number >= 0 = 120000',\n}).onUndeclaredKey('reject')\nexport type TelegramConfig = typeof telegramSchema.infer\n\nexport const notificationsSchema = type({\n telegram: telegramSchema.default(() => ({})),\n}).onUndeclaredKey('reject')\nexport type NotificationsConfig = typeof notificationsSchema.infer\n\n/** On-disk log retention for the Logs page. */\nexport const logsSchema = type({\n persist: 'boolean = true',\n /** Per server, before rotating to `.1`, `.2`, ... */\n maxBytes: '10000 <= number <= 100000000 = 2000000',\n keep: '1 <= number.integer <= 10 = 3',\n}).onUndeclaredKey('reject')\nexport type LogsConfig = typeof logsSchema.infer\n\nexport const tlsSchema = type({\n enabled: 'boolean = false',\n}).onUndeclaredKey('reject')\nexport type TlsConfig = typeof tlsSchema.infer\n\n/** Host-level vitals and their alert thresholds. */\nexport const hostSchema = type({\n enabled: 'boolean = true',\n intervalMs: 'number >= 5000 = 15000',\n /** Filesystems reported and alerted on; templates and `~` are expanded. */\n diskPaths: type('string[]').default(() => ['.']),\n /** 0 disables an individual alert. */\n diskUsedPercent: 'number >= 0 = 90',\n memoryUsedPercent: 'number >= 0 = 90',\n swapUsedPercent: 'number >= 0 = 50',\n loadPerCpu: 'number >= 0 = 2',\n tempCelsius: 'number >= 0 = 85',\n}).onUndeclaredKey('reject')\nexport type HostConfig = typeof hostSchema.infer\n\n/** Tar archives of config, secrets, TLS and declared data paths. */\nexport const backupsSchema = type({\n enabled: 'boolean = true',\n dir: 'string = \".backups\"',\n keep: 'number.integer >= 1 = 5',\n /** Extra paths in every backup, in addition to each server's `backupPaths`. */\n includePaths: type('string[]').default(() => []),\n}).onUndeclaredKey('reject')\nexport type BackupsConfig = typeof backupsSchema.infer\n\nexport const controlSchema = type({\n /** What the panel calls itself; the stock UI shows it in the sidebar. */\n label: '1 <= string <= 60 = \"Stock UI\"',\n port: '1 <= number.integer <= 65535 = 3999',\n /** Where the control panel itself listens; keep it `local` unless you mean it. */\n host: bindSchema.default(() => 'local' as const),\n openBrowser: 'boolean = false',\n auth: authSchema.default(() => ({})),\n tls: tlsSchema.default(() => ({})),\n}).onUndeclaredKey('reject')\nexport type ControlConfig = typeof controlSchema.infer\n\n/** Applied to every server entry; whatever an entry sets wins. */\nexport const defaultsSchema = type({\n enabled: 'boolean = true',\n autostart: 'boolean = false',\n bind: bindSchema.default(() => 'local' as const),\n onPortConflict: '\"block\" | \"warn\" = \"block\"',\n restart: restartSchema.default(() => ({})),\n health: healthSchema.default(() => ({})),\n stop: stopSchema.default(() => ({})),\n logBufferLines: logBufferLinesSchema.default(() => 500),\n}).onUndeclaredKey('reject')\nexport type ServerDefaults = typeof defaultsSchema.infer\n\n// Patch variants stay default-free: an API client sends only what it changes, so\n// a partial nested group must not silently pull in the code defaults.\nconst restartPatchSchema = type({\n enabled: 'boolean?',\n maxRetries: 'number.integer >= 0?',\n baseDelayMs: 'number >= 0?',\n factor: 'number >= 1?',\n maxDelayMs: 'number >= 0?',\n resetAfterMs: 'number >= 0?',\n}).onUndeclaredKey('reject')\n\nconst httpCheckPatchSchema = type({\n path: 'string?',\n method: '\"GET\" | \"HEAD\"?',\n expectStatus: 'number.integer | null?',\n expectStatusBelow: 'number.integer?',\n expectBody: 'string?',\n}).onUndeclaredKey('reject')\n\nconst resourcesPatchSchema = type({\n maxRssBytes: 'number.integer >= 0?',\n}).onUndeclaredKey('reject')\n\nconst healthPatchSchema = type({\n enabled: 'boolean?',\n mode: '\"port\" | \"http\"?',\n http: httpCheckPatchSchema.optional(),\n intervalMs: 'number >= 500?',\n timeoutMs: 'number >= 100?',\n unhealthyThreshold: 'number.integer >= 1?',\n forceRestartAfterMs: 'number >= 0?',\n startTimeoutMs: 'number >= 0?',\n}).onUndeclaredKey('reject')\n\nconst stopPatchSchema = type({\n signal: '\"SIGTERM\" | \"SIGINT\" | \"SIGKILL\"?',\n killGroup: 'boolean?',\n graceMs: 'number >= 0?',\n killPortHolders: 'boolean?',\n}).onUndeclaredKey('reject')\n\nconst authPatchSchema = type({\n enabled: 'boolean?',\n sessionTtlMs: 'number >= 60000?',\n cookieSecure: '\"auto\" | \"always\" | \"never\"?',\n trustProxy: 'boolean?',\n maxLoginAttempts: 'number.integer >= 1?',\n lockoutMs: 'number >= 1000?',\n}).onUndeclaredKey('reject')\n\nconst telegramPatchSchema = type({\n enabled: 'boolean?',\n chatId: 'string?',\n onCrash: 'boolean?',\n onUnhealthy: 'boolean?',\n onForcedRestart: 'boolean?',\n onRecovered: 'boolean?',\n onHost: 'boolean?',\n cooldownMs: 'number >= 0?',\n}).onUndeclaredKey('reject')\n\nconst notificationsPatchSchema = type({\n telegram: telegramPatchSchema.optional(),\n}).onUndeclaredKey('reject')\n\nconst hostPatchSchema = type({\n enabled: 'boolean?',\n intervalMs: 'number >= 5000?',\n diskPaths: type('string[]').optional(),\n diskUsedPercent: 'number >= 0?',\n memoryUsedPercent: 'number >= 0?',\n swapUsedPercent: 'number >= 0?',\n loadPerCpu: 'number >= 0?',\n tempCelsius: 'number >= 0?',\n}).onUndeclaredKey('reject')\n\nconst backupsPatchSchema = type({\n enabled: 'boolean?',\n dir: 'string?',\n keep: 'number.integer >= 1?',\n includePaths: type('string[]').optional(),\n}).onUndeclaredKey('reject')\n\nconst logsPatchSchema = type({\n persist: 'boolean?',\n maxBytes: '10000 <= number <= 100000000?',\n keep: '1 <= number.integer <= 10?',\n}).onUndeclaredKey('reject')\n\nconst tlsPatchSchema = type({\n enabled: 'boolean?',\n}).onUndeclaredKey('reject')\n\nconst controlPatchSchema = type({\n label: '1 <= string <= 60?',\n port: '1 <= number.integer <= 65535?',\n host: bindSchema.optional(),\n openBrowser: 'boolean?',\n auth: authPatchSchema.optional(),\n tls: tlsPatchSchema.optional(),\n}).onUndeclaredKey('reject')\n\nconst defaultsPatchSchema = type({\n enabled: 'boolean?',\n autostart: 'boolean?',\n bind: bindSchema.optional(),\n onPortConflict: '\"block\" | \"warn\"?',\n restart: restartPatchSchema.optional(),\n health: healthPatchSchema.optional(),\n stop: stopPatchSchema.optional(),\n logBufferLines: logBufferLinesSchema.optional(),\n}).onUndeclaredKey('reject')\n\nconst editableFields = {\n label: 'string?',\n enabled: 'boolean?',\n autostart: 'boolean?',\n command: 'string?',\n args: 'string[]?',\n cwd: 'string?',\n env: 'Record<string, string>?',\n dataEnvs: 'Record<string, string>?',\n bootstrap: bootstrapOrNullSchema.optional(),\n port: portSchema.optional(),\n bind: bindSchema.optional(),\n onPortConflict: '\"block\" | \"warn\"?',\n restart: restartPatchSchema.optional(),\n health: healthPatchSchema.optional(),\n stop: stopPatchSchema.optional(),\n logBufferLines: logBufferLinesSchema.optional(),\n dependsOn: type('string[]').optional(),\n envFile: 'string?',\n resources: resourcesPatchSchema.optional(),\n backupPaths: type('string[]').optional(),\n} as const\n\nexport const serverPatchSchema = type(editableFields).onUndeclaredKey('reject')\nexport type ServerPatch = typeof serverPatchSchema.infer\n\nexport const serverCreateSchema = type({\n id: '/^[a-z0-9][a-z0-9_-]*$/',\n ...editableFields,\n command: 'string',\n}).onUndeclaredKey('reject')\nexport type ServerCreate = typeof serverCreateSchema.infer\n\n/** Edits to the panel's own control block and to the global server defaults. */\nexport const settingsPatchSchema = type({\n control: controlPatchSchema.optional(),\n defaults: defaultsPatchSchema.optional(),\n logs: logsPatchSchema.optional(),\n notifications: notificationsPatchSchema.optional(),\n host: hostPatchSchema.optional(),\n backups: backupsPatchSchema.optional(),\n}).onUndeclaredKey('reject')\nexport type SettingsPatch = typeof settingsPatchSchema.infer\n\nexport const authStatusSchema = type({\n enabled: 'boolean',\n passwordSet: 'boolean',\n passwordUpdatedAt: 'number | null',\n /** Still the boot-time default; the login page says so and exposure stays blocked. */\n usingDefaultPassword: 'boolean',\n /** The panel currently listens beyond loopback. */\n exposed: 'boolean',\n /** Non-null when that exposure is not backed by a password. */\n blockedReason: 'string | null',\n sessionTtlMs: 'number',\n cookieSecure: 'string',\n trustProxy: 'boolean',\n maxLoginAttempts: 'number',\n lockoutMs: 'number',\n})\nexport type AuthStatus = typeof authStatusSchema.infer\n\nexport const tlsStatusSchema = type({\n enabled: 'boolean',\n certPresent: 'boolean',\n subject: 'string | null',\n issuer: 'string | null',\n validFrom: 'string | null',\n validTo: 'string | null',\n daysRemaining: 'number | null',\n fingerprint: 'string | null',\n keyMatches: 'boolean | null',\n error: 'string | null',\n})\nexport type TlsStatus = typeof tlsStatusSchema.infer\n\nexport const telegramStatusSchema = type({\n enabled: 'boolean',\n tokenSet: 'boolean',\n chatId: 'string',\n onCrash: 'boolean',\n onUnhealthy: 'boolean',\n onForcedRestart: 'boolean',\n onRecovered: 'boolean',\n /** Host vitals breaches (disk, memory, swap, load, temperature). */\n onHost: 'boolean',\n cooldownMs: 'number',\n /** Last delivery outcome, for the settings page. */\n lastResult: 'string | null',\n lastResultAt: 'number | null',\n})\nexport type TelegramStatus = typeof telegramStatusSchema.infer\n\nexport const notificationViewSchema = type({\n telegram: telegramStatusSchema,\n})\nexport type NotificationView = typeof notificationViewSchema.infer\n\nexport const sessionViewSchema = type({\n authenticated: 'boolean',\n authRequired: 'boolean',\n passwordSet: 'boolean',\n usingDefaultPassword: 'boolean',\n /** The boot-time password, exposed only while it is still in use. */\n defaultPassword: 'string | null',\n sessionTtlMs: 'number',\n})\nexport type SessionView = typeof sessionViewSchema.infer\n\nexport const loginSchema = type({ password: 'string' }).onUndeclaredKey('reject')\nexport type LoginRequest = typeof loginSchema.infer\n\n/** Any non-empty password is allowed; only a sanity cap on the length. */\nexport const passwordValueSchema = type('1 <= string <= 512')\n\nexport const passwordSchema = type({\n currentPassword: 'string?',\n newPassword: passwordValueSchema,\n}).onUndeclaredKey('reject')\nexport type PasswordRequest = typeof passwordSchema.infer\n\nexport const serverStatusSchema = type('\"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"backoff\" | \"crashed\" | \"conflict\"')\nexport type ServerStatus = typeof serverStatusSchema.infer\n\nexport const healthStateSchema = type('\"disabled\" | \"unknown\" | \"healthy\" | \"unhealthy\"')\nexport type HealthState = typeof healthStateSchema.infer\n\nexport const portStateSchema = type('\"unknown\" | \"free\" | \"in-use\"')\nexport type PortState = typeof portStateSchema.infer\n\nexport const logStreamSchema = type('\"stdout\" | \"stderr\" | \"system\"')\nexport type LogStream = typeof logStreamSchema.infer\n\nexport const logLineSchema = type({\n ts: 'number',\n stream: logStreamSchema,\n text: 'string',\n})\nexport type LogLine = typeof logLineSchema.infer\n\nexport const historyEventSchema = type({\n serverId: 'string',\n ts: 'number',\n type: '\"start\" | \"exit\" | \"crash\" | \"forced-restart\" | \"unhealthy\" | \"recovered\"',\n detail: 'string',\n /** How long the process had been up, recorded on exit and crash. */\n runtimeMs: 'number?',\n})\nexport type HistoryEvent = typeof historyEventSchema.infer\n\n/** Rolling window stats derived from the persisted event log. */\nexport const serverHistorySchema = type({\n windowMs: 'number',\n /** Share of the window the process was up (null when nothing is known yet). */\n uptimeRatio: 'number | null',\n restarts: 'number',\n crashes: 'number',\n forcedRestarts: 'number',\n lastCrashAt: 'number | null',\n lastExitAt: 'number | null',\n lastRuntimeMs: 'number | null',\n events: historyEventSchema.array(),\n})\nexport type ServerHistory = typeof serverHistorySchema.infer\n\nexport const processResourcesSchema = type({\n cpuPercent: 'number | null',\n /** RSS of the process and its descendants. */\n rssBytes: 'number | null',\n processes: 'number',\n sampledAt: 'number',\n})\nexport type ProcessResources = typeof processResourcesSchema.infer\n\nexport const hostDiskSchema = type({\n path: 'string',\n totalBytes: 'number',\n freeBytes: 'number',\n usedPercent: 'number',\n})\n\nexport const hostViewSchema = type({\n enabled: 'boolean',\n cpus: 'number',\n loadAvg: type('number[]'),\n uptimeMs: 'number',\n memoryUsedPercent: 'number',\n swapUsedPercent: 'number',\n tempCelsius: 'number | null',\n disks: hostDiskSchema.array(),\n /** Human readable threshold breaches, for the banner and notifications. */\n alerts: type('string[]'),\n sampledAt: 'number | null',\n})\nexport type HostView = typeof hostViewSchema.infer\n\nexport const backupFileSchema = type({\n name: 'string',\n sizeBytes: 'number',\n createdAt: 'number',\n /** The archive carries a password-protected payload. */\n encrypted: 'boolean',\n})\n\nexport type BackupFile = typeof backupFileSchema.infer\n\n/** One declared data path, with the reason it will (or will not) be captured. */\nexport const backupPathSchema = type({\n path: 'string',\n /** Who declared it: `global`, `<serverId>:backupPaths` or `<serverId>:<ENV>`. */\n origin: 'string',\n /** false when a parent path already covers it, or it would swallow the archive dir. */\n included: 'boolean',\n note: 'string | null',\n})\nexport type BackupPath = typeof backupPathSchema.infer\n\nexport const backupsViewSchema = type({\n enabled: 'boolean',\n dir: 'string',\n keep: 'number',\n /** Extra paths from the config, in addition to each server's own. */\n includePaths: type('string[]'),\n /** Every declared path that will be picked up, for the UI to show. */\n paths: backupPathSchema.array(),\n files: backupFileSchema.array(),\n})\nexport type BackupsView = typeof backupsViewSchema.infer\n\n/** One restorable slice of an archive: the panel's own state or a data path. */\nexport const restoreItemSchema = type({\n /** `config` | `secrets` | `tls`, or the data path itself. */\n id: 'string',\n label: 'string',\n kind: '\"config\" | \"secrets\" | \"tls\" | \"data\"',\n /** false when the current config does not declare it, or it is not in the archive. */\n restorable: 'boolean',\n /** Echo of the request's selection, so the checkboxes round-trip. */\n selected: 'boolean',\n note: 'string | null',\n})\nexport type RestoreItem = typeof restoreItemSchema.infer\n\nexport const restorePlanSchema = type({\n dryRun: 'boolean',\n encrypted: 'boolean',\n /** The archive needs a password (none or a wrong one was supplied). */\n needsPassword: 'boolean',\n items: restoreItemSchema.array(),\n applied: type('string[]'),\n skipped: type('string[]'),\n /** Only the panel's own listener needs a restart; its servers are re-read live. */\n restartRequired: 'boolean',\n /** The panel re-read the restored config within this same restore. */\n reloaded: 'boolean',\n error: 'string?',\n})\nexport type RestorePlan = typeof restorePlanSchema.infer\n\nexport const backupCreateSchema = type({\n /** Optional: encrypts the archive. Never stored. */\n password: passwordValueSchema.optional(),\n}).onUndeclaredKey('reject')\nexport type BackupCreate = typeof backupCreateSchema.infer\n\nexport const restoreRequestSchema = type({\n name: 'string?',\n password: passwordValueSchema.optional(),\n /** Item ids to restore; omitted means every restorable item. */\n include: type('string[]').optional(),\n}).onUndeclaredKey('reject')\nexport type RestoreRequest = typeof restoreRequestSchema.infer\n\n/** Runtime view of a server: its effective config plus everything observed. */\nexport const serverViewSchema = type({\n id: 'string',\n config: serverSchema,\n bindHost: 'string',\n url: 'string | null',\n status: serverStatusSchema,\n health: healthStateSchema,\n portState: portStateSchema,\n pid: 'number | null',\n startedAt: 'number | null',\n exitCode: 'number | null',\n exitSignal: 'string | null',\n restarts: 'number',\n maxRetries: 'number',\n lastError: 'string | null',\n nextRetryAt: 'number | null',\n unhealthySince: 'number | null',\n bufferedLines: 'number',\n history: serverHistorySchema,\n /** Last health probe latency (TCP connect or HTTP request). */\n responseMs: 'number | null',\n resources: processResourcesSchema.or(type('null')),\n})\n// `config` is emitted normalized (port is always `number | null`, never absent),\n// while the schema accepts both forms so a hand-written payload still validates.\nexport type ServerView = Omit<typeof serverViewSchema.infer, 'config'> & { config: ServerConfig }\n\nexport const controlViewSchema = type({\n /** The configured panel name, for the shell to render. */\n label: 'string',\n port: 'number',\n /** The configured bind value (`local` | `lan` | ipv4). */\n host: 'string',\n /** The address actually bound. */\n bindHost: 'string',\n url: 'string',\n openBrowser: 'boolean',\n /** The live listener differs from the configured host/port. */\n restartRequired: 'boolean',\n protocol: 'string',\n auth: authStatusSchema,\n tls: tlsStatusSchema,\n})\nexport type ControlView = typeof controlViewSchema.infer\n\nexport const appStateSchema = type({\n control: controlViewSchema,\n defaults: defaultsSchema,\n logs: logsSchema,\n notifications: notificationViewSchema,\n host: hostViewSchema,\n backups: backupsViewSchema,\n configPath: 'string',\n configError: 'string | null',\n /** The directory the panel was started from; relative entry paths use it. */\n projectDir: 'string',\n /** `HHOSTED_HOME`: every file home-hosted owns lives under here. */\n dataRoot: 'string',\n logsDir: 'string',\n servers: serverViewSchema.array(),\n})\nexport type AppState = Omit<typeof appStateSchema.infer, 'servers'> & { servers: ServerView[] }\n\nexport const sseMessageSchema = type({\n type: '\"hello\" | \"state\" | \"log\" | \"server\"',\n ts: 'number',\n serverId: 'string?',\n state: appStateSchema.optional(),\n server: serverViewSchema.optional(),\n lines: logLineSchema.array().optional(),\n})\nexport type SseMessage = typeof sseMessageSchema.infer\n\nexport const logQuerySchema = type({\n limit: 'string?',\n})\n\nexport const logHistoryQuerySchema = type({\n tail: 'string?',\n /** Case-insensitive substring filter over the tail window. */\n search: 'string?',\n stream: '\"stdout\" | \"stderr\" | \"system\"?',\n})\n\nexport const logFileInfoSchema = type({\n name: 'string',\n sizeBytes: 'number',\n})\n\nexport const logServerViewSchema = type({\n serverId: 'string',\n label: 'string',\n status: serverStatusSchema,\n enabled: 'boolean',\n sizeBytes: 'number',\n files: logFileInfoSchema.array(),\n})\nexport type LogServerView = typeof logServerViewSchema.infer\n\nexport const logServersViewSchema = type({\n servers: logServerViewSchema.array(),\n})\n\nexport const logHistoryViewSchema = type({\n serverId: 'string',\n enabled: 'boolean',\n sizeBytes: 'number',\n files: type('string[]'),\n /** How many lines the search looked at, or null when not searching. */\n searched: 'number | null',\n lines: logLineSchema.array(),\n})\nexport type LogHistoryView = typeof logHistoryViewSchema.infer\n\nexport type TelegramToken = typeof telegramTokenSchema.infer\n\nexport const notificationActionSchema = type({\n /** Optional override, so the token can be tested before it is saved. */\n botToken: 'string?',\n chatId: 'string?',\n}).onUndeclaredKey('reject')\nexport type NotificationAction = typeof notificationActionSchema.infer\n\nexport const telegramTokenSchema = type({\n botToken: 'string >= 1',\n}).onUndeclaredKey('reject')\n\n/** The single error envelope every route answers failures with. */\nexport const apiErrorSchema = type({\n message: 'string',\n /** Stable, machine-readable; `AUTH_REQUIRED` also drives the login redirect. */\n code: 'string',\n detail: 'unknown',\n}).onUndeclaredKey('reject')\nexport type ApiError = typeof apiErrorSchema.infer\n\n/** A user-supplied UI, as the settings page shows it. */\nexport const uiMetaSchema = type({\n name: 'string',\n version: 'string | null',\n uploadedAt: 'number',\n files: 'number.integer >= 1',\n})\nexport type UiMeta = typeof uiMetaSchema.infer\n\nexport const uiStatusSchema = type({\n /** A user-supplied UI is being served instead of the stock one. */\n custom: 'boolean',\n /** Where that UI lives, whether or not it exists yet. */\n dir: 'string',\n meta: uiMetaSchema.or(type('null')),\n})\nexport type UiStatus = typeof uiStatusSchema.infer\n\nexport const tlsUploadSchema = type({\n certificate: 'string >= 1',\n privateKey: 'string >= 1',\n}).onUndeclaredKey('reject')\nexport type TlsUpload = typeof tlsUploadSchema.infer\n","import type { StandardSchemaV1 } from '@standard-schema/spec'\nimport { resolver } from 'hono-openapi'\nimport { apiErrorSchema } from '#src/shared/contracts'\n\n/**\n * A JSON response body for `describeRoute`. The resolver needs a real Standard\n * Schema, so only ArkType schemas go in here — never a hand-written JSON schema.\n */\nexport function jsonBody(schema: StandardSchemaV1) {\n return { 'application/json': { schema: resolver(schema as never) } }\n}\n\n/** The envelope every failing request gets (see `src/helpers/error.ts`). */\nexport const ERROR_RESPONSES = {\n 400: { description: 'The request was rejected', content: jsonBody(apiErrorSchema) },\n 401: { description: 'No valid session', content: jsonBody(apiErrorSchema) },\n 404: { description: 'Unknown id', content: jsonBody(apiErrorSchema) },\n} as const\n","import type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { ValidationTargets } from 'hono'\nimport { DetailedError } from '@namesmt/utils'\nimport { validator as standardValidator } from 'hono-openapi'\n\n/**\n * ArkType-backed request validation. On success Hono stores the *parsed* value,\n * so `c.req.valid('json')` is fully typed and already normalized; on failure it\n * becomes a `DetailedError`, which the global error handler turns into the one\n * error envelope this API speaks.\n */\nexport function validate<Target extends keyof ValidationTargets, Schema extends StandardSchemaV1>(target: Target, schema: Schema) {\n return standardValidator(target, schema, (result) => {\n if (result.success === false)\n throw new DetailedError('validation failed', { statusCode: 400, detail: normalizeIssues(result.error) })\n })\n}\n\n/** ArkType issues serialize poorly, so only the fields a client can act on survive. */\nfunction normalizeIssues(error: StandardSchemaV1.FailureResult['issues']): Array<{ path: string, message: string }> {\n return error.map((issue) => {\n const path = (issue.path ?? [])\n .map(segment => (typeof segment === 'object' && segment !== null && 'key' in segment ? String(segment.key) : String(segment)))\n .join('.')\n return { path, message: issue.message }\n })\n}\n","/** Client address helpers shared by the auth guard and the setup routes. */\n\nexport function requestIp(c: { req: { raw: unknown } }): string | null {\n // srvx resolves this hop-aware from `trustProxy` + x-forwarded-for.\n const raw = c.req.raw as { ip?: string } | undefined\n return raw?.ip ?? null\n}\n\nexport function isLoopback(address: string | null): boolean {\n if (!address)\n return false\n return address === '127.0.0.1' || address === '::1' || address === '::ffff:127.0.0.1'\n}\n\nexport function isLoopbackRequest(c: { req: { raw: unknown } }): boolean {\n return isLoopback(requestIp(c))\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nexport interface WriteFileOptions {\n /** Applied to the temp file before the rename, so secrets never exist world-readable. */\n mode?: number\n}\n\n/**\n * Write via a temp file in the same directory, then rename: readers never see a\n * half-written file, and a crash cannot truncate the previous config.\n */\nexport function writeFileAtomic(file: string, content: string, options: WriteFileOptions = {}): void {\n fs.mkdirSync(path.dirname(file), { recursive: true })\n const tmp = `${file}.${process.pid}.tmp`\n fs.writeFileSync(tmp, content, options.mode === undefined ? undefined : { mode: options.mode })\n if (options.mode !== undefined)\n fs.chmodSync(tmp, options.mode)\n fs.renameSync(tmp, file)\n}\n","import { Buffer } from 'node:buffer'\nimport crypto from 'node:crypto'\nimport fs from 'node:fs'\nimport { writeFileAtomic } from '#src/helpers/atomic'\n\n/** Node's scrypt defaults, pinned so a hash stays verifiable across versions. */\nconst COST = { N: 16384, r: 8, p: 1 } as const\nconst KEYLEN = 64\nconst SALT_BYTES = 16\n\nexport interface PasswordRecord {\n algo: 'scrypt'\n salt: string\n hash: string\n keylen: number\n cost: { N: number, r: number, p: number }\n updatedAt: number\n /** Set for the boot-time default, so the UI can say so and exposure stays blocked. */\n isDefault?: boolean\n}\n\ninterface SecretsFile {\n version: 2\n password: PasswordRecord | null\n telegram: { botToken: string } | null\n}\n\nexport interface ScryptCost { N: number, r: number, p: number }\n\nexport function deriveKey(password: string, salt: Buffer, cost: ScryptCost, keylen: number): Buffer {\n return crypto.scryptSync(password.normalize('NFKC'), salt, keylen, { ...cost })\n}\n\nexport function hashPassword(password: string, options: { now?: number, isDefault?: boolean } = {}): PasswordRecord {\n const salt = crypto.randomBytes(SALT_BYTES)\n return {\n algo: 'scrypt',\n salt: salt.toString('base64'),\n hash: deriveKey(password, salt, COST, KEYLEN).toString('base64'),\n keylen: KEYLEN,\n cost: { ...COST },\n updatedAt: options.now ?? Date.now(),\n ...(options.isDefault === true ? { isDefault: true } : {}),\n }\n}\n\nexport function verifyPassword(password: string, record: PasswordRecord): boolean {\n const expected = Buffer.from(record.hash, 'base64')\n let actual: Buffer\n try {\n actual = deriveKey(password, Buffer.from(record.salt, 'base64'), record.cost, record.keylen)\n }\n catch {\n return false\n }\n if (actual.length !== expected.length)\n return false\n return crypto.timingSafeEqual(actual, expected)\n}\n\n/**\n * The password hash is a secret, so it lives outside `servers.config.json`\n * (which is tracked) in a 0600 file that is git-ignored.\n */\nexport class SecretsStore {\n private cache: SecretsFile | null = null\n private cacheKey = ''\n\n constructor(private readonly file: string) {}\n\n get path(): string {\n return this.file\n }\n\n /**\n * Re-reads whenever the file changes on disk, so a password set by\n * `pnpm run set-password` (or another process) takes effect without\n * restarting `up`.\n */\n load(): SecretsFile {\n const key = this.statKey()\n if (this.cache !== null && key === this.cacheKey)\n return this.cache\n this.cache = this.read()\n this.cacheKey = key\n return this.cache\n }\n\n private statKey(): string {\n try {\n const stats = fs.statSync(this.file)\n return `${stats.mtimeMs}:${stats.size}`\n }\n catch {\n return 'missing'\n }\n }\n\n get password(): PasswordRecord | null {\n return this.load().password\n }\n\n get passwordUpdatedAt(): number | null {\n return this.password?.updatedAt ?? null\n }\n\n get passwordSet(): boolean {\n return this.password !== null\n }\n\n get usingDefaultPassword(): boolean {\n return this.password?.isDefault === true\n }\n\n get telegramToken(): string | null {\n return this.load().telegram?.botToken ?? null\n }\n\n get telegramTokenSet(): boolean {\n return (this.telegramToken ?? '').length > 0\n }\n\n setPassword(password: string, options: { isDefault?: boolean } = {}): PasswordRecord {\n const record = hashPassword(password, options)\n this.save({ ...this.load(), password: record })\n return record\n }\n\n /** Creates the default password only when none exists yet. */\n ensureDefaultPassword(password: string): PasswordRecord | null {\n if (this.passwordSet)\n return null\n return this.setPassword(password, { isDefault: true })\n }\n\n clearPassword(): void {\n this.save({ ...this.load(), password: null })\n }\n\n setTelegramToken(token: string | null): void {\n const trimmed = token?.trim() ?? ''\n this.save({ ...this.load(), telegram: trimmed.length > 0 ? { botToken: trimmed } : null })\n }\n\n private read(): SecretsFile {\n if (!fs.existsSync(this.file))\n return { version: 2, password: null, telegram: null }\n try {\n const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8')) as Partial<SecretsFile>\n return {\n version: 2,\n password: parsed?.password ?? null,\n telegram: parsed?.telegram?.botToken ? { botToken: parsed.telegram.botToken } : null,\n }\n }\n catch {\n // A corrupt secrets file must not silently authenticate anyone.\n return { version: 2, password: null, telegram: null }\n }\n }\n\n private save(contents: SecretsFile): void {\n writeFileAtomic(this.file, `${JSON.stringify(contents, null, 2)}\\n`, { mode: 0o600 })\n this.cache = contents\n }\n}\n","import type { SecretsStore } from '#src/config/secrets'\nimport type { AuthConfig, SessionView } from '#src/shared/contracts'\nimport crypto from 'node:crypto'\nimport { verifyPassword } from '#src/config/secrets'\nimport { parseCookies } from '#src/helpers/cookies'\n\nexport const SESSION_COOKIE = 'hh2_session'\n\n/** Created on first boot when no password exists; exposure stays blocked until it changes. */\nexport const DEFAULT_PASSWORD = 'hh'\n\nconst MAX_SESSIONS = 100\nconst MAX_LOCKOUT_MS = 15 * 60_000\n/** Bounds on the per-IP bookkeeping, which an attacker can otherwise grow. */\nconst MAX_ATTEMPT_RECORDS = 10_000\nconst ATTEMPT_RECORD_TTL_MS = 60 * 60_000\n\nexport interface SessionRecord {\n token: string\n createdAt: number\n expiresAt: number\n lastSeenAt: number\n ip: string | null\n}\n\ninterface AttemptRecord {\n failures: number\n blockedUntil: number\n blocks: number\n /** For expiring idle records: with `trustProxy` the key is client-chosen. */\n lastAttemptAt: number\n}\n\nexport type LoginOutcome\n = | { ok: true, status: 200, token: string, maxAgeMs: number }\n | { ok: false, status: 401 | 409 | 429, error: string, retryAfterMs?: number }\n\n/**\n * Single-password authentication for the control panel.\n *\n * The password hash lives in the git-ignored secrets file; sessions live only in\n * memory, so restarting `up` invalidates every session.\n */\nexport class AuthService {\n private readonly sessions = new Map<string, SessionRecord>()\n private readonly attempts = new Map<string, AttemptRecord>()\n private readonly timer: NodeJS.Timeout\n\n constructor(\n private readonly secrets: SecretsStore,\n private readonly getConfig: () => AuthConfig,\n ) {\n this.timer = setInterval(() => this.cleanup(), 60_000)\n this.timer.unref()\n }\n\n get passwordSet(): boolean {\n return this.secrets.passwordSet\n }\n\n get passwordUpdatedAt(): number | null {\n return this.secrets.passwordUpdatedAt\n }\n\n /** Still the boot-time default: the login page says so, exposure stays blocked. */\n get usingDefaultPassword(): boolean {\n return this.secrets.usingDefaultPassword\n }\n\n /** The feature is on. */\n isEnabled(): boolean {\n return this.getConfig().enabled\n }\n\n /** On *and* usable: there is a password to check against. */\n isArmed(): boolean {\n return this.getConfig().enabled && this.secrets.passwordSet\n }\n\n /** Kept as the \"should the guard demand a session\" predicate. */\n isRequired(): boolean {\n return this.isArmed()\n }\n\n sessionView(token: string | null): SessionView {\n return {\n authenticated: this.validate(token) !== null,\n authRequired: this.isRequired(),\n passwordSet: this.secrets.passwordSet,\n usingDefaultPassword: this.secrets.usingDefaultPassword,\n defaultPassword: this.secrets.usingDefaultPassword ? DEFAULT_PASSWORD : null,\n sessionTtlMs: this.getConfig().sessionTtlMs,\n }\n }\n\n tokenFromCookie(cookieHeader: string | null | undefined): string | null {\n return parseCookies(cookieHeader)[SESSION_COOKIE] ?? null\n }\n\n /** Sliding expiry: an active panel stays logged in, an idle one does not. */\n validate(token: string | null): SessionRecord | null {\n if (!token)\n return null\n const session = this.sessions.get(token)\n if (!session)\n return null\n\n const now = Date.now()\n if (session.expiresAt <= now) {\n this.sessions.delete(token)\n return null\n }\n\n session.lastSeenAt = now\n session.expiresAt = now + this.getConfig().sessionTtlMs\n return session\n }\n\n verifyCurrentPassword(password: string): boolean {\n const record = this.secrets.password\n if (record === null)\n return false\n return verifyPassword(password, record)\n }\n\n login(password: string, ip: string | null): LoginOutcome {\n const config = this.getConfig()\n const key = ip ?? 'unknown'\n const now = Date.now()\n const attempt = this.attempts.get(key)\n\n if (attempt && attempt.blockedUntil > now) {\n const retryAfterMs = attempt.blockedUntil - now\n return {\n ok: false,\n status: 429,\n error: `too many failed attempts, retry in ${Math.ceil(retryAfterMs / 1000)}s`,\n retryAfterMs,\n }\n }\n\n const record = this.secrets.password\n if (record === null) {\n return { ok: false, status: 409, error: 'no password is set yet' }\n }\n\n if (!verifyPassword(password, record)) {\n const failures = (attempt?.failures ?? 0) + 1\n if (failures >= config.maxLoginAttempts) {\n const blocks = (attempt?.blocks ?? 0) + 1\n const blockedUntil = Date.now() + Math.min(config.lockoutMs * 2 ** (blocks - 1), MAX_LOCKOUT_MS)\n this.attempts.set(key, { failures: 0, blockedUntil, blocks, lastAttemptAt: Date.now() })\n }\n else {\n this.attempts.set(key, { failures, blockedUntil: 0, blocks: attempt?.blocks ?? 0, lastAttemptAt: Date.now() })\n }\n return { ok: false, status: 401, error: 'invalid password' }\n }\n\n this.attempts.delete(key)\n if (this.sessions.size >= MAX_SESSIONS) {\n const oldest = [...this.sessions.values()].sort((a, b) => a.lastSeenAt - b.lastSeenAt)[0]\n if (oldest)\n this.sessions.delete(oldest.token)\n }\n\n const token = crypto.randomBytes(32).toString('base64url')\n this.sessions.set(token, {\n token,\n createdAt: now,\n expiresAt: now + config.sessionTtlMs,\n lastSeenAt: now,\n ip,\n })\n\n return { ok: true, status: 200, token, maxAgeMs: config.sessionTtlMs }\n }\n\n logout(token: string | null): void {\n if (token)\n this.sessions.delete(token)\n }\n\n logoutAll(): void {\n this.sessions.clear()\n }\n\n /**\n * Changing the password must not leave old sessions valid. The session that\n * made the change is kept — being signed out of the page you just used is not\n * a security requirement, and it makes a successful change look like a failure.\n */\n setPassword(password: string, options: { isDefault?: boolean, keepToken?: string | null } = {}): void {\n this.secrets.setPassword(password, options)\n this.logoutOthers(options.keepToken ?? null)\n }\n\n /** Drops every session except one, which is how a password change stays signed in. */\n private logoutOthers(keep: string | null): void {\n if (keep === null) {\n this.sessions.clear()\n return\n }\n for (const token of [...this.sessions.keys()]) {\n if (token !== keep)\n this.sessions.delete(token)\n }\n }\n\n /** Creates the boot-time default only when nothing is set yet. */\n ensureDefaultPassword(password: string): boolean {\n const created = this.secrets.ensureDefaultPassword(password) !== null\n if (created)\n this.logoutAll()\n return created\n }\n\n clearPassword(): void {\n this.secrets.clearPassword()\n this.logoutAll()\n }\n\n activeSessions(): number {\n return this.sessions.size\n }\n\n dispose(): void {\n clearInterval(this.timer)\n this.sessions.clear()\n }\n\n private cleanup(): void {\n const now = Date.now()\n for (const [token, session] of this.sessions) {\n if (session.expiresAt <= now)\n this.sessions.delete(token)\n }\n for (const [key, attempt] of this.attempts) {\n // Idle records go, whether or not they were ever blocked — a failed login\n // from an address that never comes back must not be remembered forever.\n if (now - attempt.lastAttemptAt >= ATTEMPT_RECORD_TTL_MS)\n this.attempts.delete(key)\n }\n\n if (this.attempts.size > MAX_ATTEMPT_RECORDS) {\n const oldest = [...this.attempts.entries()]\n .sort((a, b) => a[1].lastAttemptAt - b[1].lastAttemptAt)\n .slice(0, this.attempts.size - MAX_ATTEMPT_RECORDS)\n for (const [key] of oldest) this.attempts.delete(key)\n }\n }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport type { AuthService } from '#src/services/auth'\nimport { DetailedError } from '@namesmt/utils'\nimport { isLoopbackRequest } from '#src/middleware/loopback'\nimport { SESSION_COOKIE } from '#src/services/auth'\n\n/** Endpoints the SPA needs before it can show a login form. */\nconst PUBLIC_PATHS = new Set(['/api/auth/login', '/api/auth/session'])\n\n/** 401s from the guard carry this code so the SPA can route to the login view. */\nexport const AUTH_REQUIRED_CODE = 'AUTH_REQUIRED'\n\nexport interface AuthGuardDeps {\n auth: AuthService\n /** Test hook: bind the guard to an explicit cookie header / ip. */\n now?: () => number\n}\n\n/**\n * Guards every `/api/*` route. The SPA shell stays public (it holds no data),\n * so the browser can load the app and show the login screen.\n *\n * State-changing requests that carry an `Origin` must come from this same host:\n * with `SameSite=Strict` cookies that closes the cross-site CSRF path.\n */\nexport function createAuthGuard(deps: AuthGuardDeps): MiddlewareHandler {\n return async (c, next) => {\n const path = c.req.path\n\n if (path.startsWith('/api')) {\n const method = c.req.method\n if (method !== 'GET' && method !== 'HEAD') {\n const origin = c.req.header('origin')\n if (origin !== undefined) {\n // `Host` is required by HTTP/1.1 but not guaranteed to be set by every\n // client, so fall back to the authority the server itself resolved.\n const requestHost = c.req.header('host') ?? new URL(c.req.url).host\n let originHost: string | null = null\n try {\n originHost = new URL(origin).host\n }\n catch {\n originHost = null\n }\n if (originHost === null || originHost !== requestHost)\n throw new DetailedError('cross-origin request rejected', { statusCode: 403, code: 'CROSS_ORIGIN' })\n }\n }\n\n if (!PUBLIC_PATHS.has(path)) {\n if (deps.auth.isArmed()) {\n const token = deps.auth.tokenFromCookie(c.req.header('cookie'))\n if (deps.auth.validate(token) === null) {\n throw new DetailedError('authentication required', { statusCode: 401, code: AUTH_REQUIRED_CODE })\n }\n }\n else if (deps.auth.isEnabled()) {\n // Enabled but not armed: nothing could authenticate, so only this\n // machine may look. A proxied request must set `trustProxy` to be seen\n // as remote, otherwise it is indistinguishable from a local one.\n if (!isLoopbackRequest(c)) {\n throw new DetailedError('authentication is enabled but no password is set — set one from the machine running the panel', {\n statusCode: 401,\n code: 'AUTH_UNARMED',\n })\n }\n }\n }\n }\n\n await next()\n }\n}\n\nexport { SESSION_COOKIE }\n\nexport { isLoopbackRequest, requestIp } from '#src/middleware/loopback'\n","import os from 'node:os'\n\nexport function lanAddress(): string | null {\n for (const entries of Object.values(os.networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal)\n return entry.address\n }\n }\n return null\n}\n\nexport function bindHost(bind: string): string {\n if (bind === 'local')\n return '127.0.0.1'\n if (bind === 'lan')\n return '0.0.0.0'\n return bind\n}\n\n/** Address a human should open, which is never `0.0.0.0`. */\nexport function displayHost(bind: string): string {\n if (bind === 'local')\n return '127.0.0.1'\n if (bind === 'lan')\n return lanAddress() ?? '127.0.0.1'\n return bind\n}\n\n/** True when the bind value makes the port reachable from outside this machine. */\nexport function isExposed(bind: string): boolean {\n return bindHost(bind) !== '127.0.0.1'\n}\n","import type { ControlConfig } from '#src/shared/contracts'\nimport { isExposed } from '#src/helpers/bind'\n\nexport interface ExposureState {\n /** The control panel listens beyond loopback. */\n exposed: boolean\n /** Non-null when that exposure is not backed by a password. */\n blockedReason: string | null\n}\n\n/**\n * Exposing the panel beyond loopback is only allowed with authentication fully\n * configured — this is checked at startup, on every settings write, and shown in\n * the UI, so the three can never disagree.\n */\nexport function checkExposure(control: ControlConfig, passwordSet: boolean, usingDefaultPassword = false): ExposureState {\n const exposed = isExposed(control.host)\n if (!exposed)\n return { exposed, blockedReason: null }\n\n if (!control.auth.enabled && !passwordSet) {\n return {\n exposed,\n blockedReason: `the control panel is bound to ${control.host} but authentication is disabled and no password is set`,\n }\n }\n if (!control.auth.enabled) {\n return { exposed, blockedReason: `the control panel is bound to ${control.host} but authentication is disabled` }\n }\n if (!passwordSet) {\n return {\n exposed,\n blockedReason: `the control panel is bound to ${control.host} but no password is set (run \\`pnpm run auth:set-password\\`)`,\n }\n }\n if (usingDefaultPassword) {\n return {\n exposed,\n blockedReason: `the control panel is bound to ${control.host} but still uses the default password — change it first`,\n }\n }\n return { exposed, blockedReason: null }\n}\n","import type { Context } from 'hono'\nimport type { AppDeps } from '#src/app'\nimport type { LoginRequest, PasswordRequest } from '#src/shared/contracts'\nimport { DetailedError } from '@namesmt/utils'\nimport { describeRoute } from 'hono-openapi'\nimport { serializeCookie } from '#src/helpers/cookies'\nimport { appFactory } from '#src/helpers/factory'\nimport { ERROR_RESPONSES, jsonBody } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { AUTH_REQUIRED_CODE, SESSION_COOKIE } from '#src/middleware/auth'\nimport { isLoopbackRequest, requestIp } from '#src/middleware/loopback'\nimport { checkExposure } from '#src/services/exposure'\nimport { loginSchema, passwordSchema, sessionViewSchema } from '#src/shared/contracts'\n\n/** `Secure` only helps over TLS, and would break plain http on a LAN. */\nfunction secureCookie(c: Context, deps: AppDeps): boolean {\n const mode = deps.store.config.control.auth.cookieSecure\n if (mode === 'always')\n return true\n if (mode === 'never')\n return false\n return new URL(c.req.url).protocol === 'https:'\n}\n\nexport function createAuthRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/auth/session',\n describeRoute({\n tags: ['auth'],\n summary: 'Who this request is, and how the panel is protected',\n responses: { 200: { description: 'Session', content: jsonBody(sessionViewSchema) } },\n }),\n c => c.json(deps.auth.sessionView(deps.auth.tokenFromCookie(c.req.header('cookie')))),\n )\n\n .post(\n '/auth/login',\n describeRoute({\n tags: ['auth'],\n summary: 'Exchange the panel password for a session cookie',\n responses: {\n 200: { description: 'Signed in', content: jsonBody(sessionViewSchema) },\n 400: ERROR_RESPONSES[400],\n 401: { description: 'Wrong password, or locked out (see `Retry-After`)' },\n },\n }),\n validate('json', loginSchema),\n (c) => {\n const body: LoginRequest = c.req.valid('json')\n const outcome = deps.auth.login(body.password, requestIp(c))\n\n if (!outcome.ok) {\n if (outcome.retryAfterMs !== undefined)\n c.header('Retry-After', String(Math.ceil(outcome.retryAfterMs / 1000)))\n throw new DetailedError(outcome.error, { statusCode: outcome.status, code: 'LOGIN_FAILED' })\n }\n\n c.header('Set-Cookie', serializeCookie(SESSION_COOKIE, outcome.token, {\n maxAgeMs: outcome.maxAgeMs,\n secure: secureCookie(c, deps),\n sameSite: 'Strict',\n httpOnly: true,\n }))\n\n return c.json(deps.auth.sessionView(outcome.token))\n },\n )\n\n .post(\n '/auth/logout',\n describeRoute({\n tags: ['auth'],\n summary: 'Drop the session cookie',\n responses: { 200: { description: 'Signed out' } },\n }),\n (c) => {\n deps.auth.logout(deps.auth.tokenFromCookie(c.req.header('cookie')))\n c.header('Set-Cookie', serializeCookie(SESSION_COOKIE, '', {\n maxAgeMs: 0,\n secure: secureCookie(c, deps),\n sameSite: 'Strict',\n httpOnly: true,\n }))\n return c.json({ ok: true })\n },\n )\n\n /**\n * First-time setup is allowed from loopback without a session (there is\n * nothing to authenticate against yet); every later change needs the session\n * and* the current password.\n */\n .post(\n '/auth/password',\n describeRoute({\n tags: ['auth'],\n summary: 'Set, change or enable the panel password',\n responses: { 200: { description: 'Updated' }, 400: ERROR_RESPONSES[400], 401: ERROR_RESPONSES[401] },\n }),\n validate('json', passwordSchema),\n (c) => {\n const body: PasswordRequest = c.req.valid('json')\n const hadPassword = deps.auth.passwordSet\n const authenticated = deps.auth.validate(deps.auth.tokenFromCookie(c.req.header('cookie'))) !== null\n const firstSetup = !hadPassword && isLoopbackRequest(c)\n\n if (!authenticated && !firstSetup)\n throw new DetailedError('authentication required', { statusCode: 401, code: AUTH_REQUIRED_CODE })\n\n if (authenticated && hadPassword) {\n if (body.currentPassword === undefined)\n throw new DetailedError('currentPassword is required to change an existing password', { statusCode: 400, code: 'CURRENT_PASSWORD_REQUIRED' })\n if (!deps.auth.verifyCurrentPassword(body.currentPassword))\n throw new DetailedError('current password is incorrect', { statusCode: 401, code: 'CURRENT_PASSWORD_WRONG' })\n }\n\n // Keep the caller signed in: every *other* session is dropped.\n deps.auth.setPassword(body.newPassword, { keepToken: deps.auth.tokenFromCookie(c.req.header('cookie')) })\n\n // A password that is not enforced protects nothing, so the first setup enables it.\n let enabled = deps.store.config.control.auth.enabled\n if (!enabled) {\n deps.store.updateControl({ auth: { enabled: true } })\n enabled = true\n }\n\n return c.json({ ok: true, enabled, sessionsInvalidated: true })\n },\n )\n\n .delete(\n '/auth/password',\n describeRoute({\n tags: ['auth'],\n summary: 'Clear the password and turn authentication off',\n responses: { 200: { description: 'Cleared' }, 400: ERROR_RESPONSES[400], 401: ERROR_RESPONSES[401] },\n }),\n (c) => {\n if (deps.auth.validate(deps.auth.tokenFromCookie(c.req.header('cookie'))) === null)\n throw new DetailedError('authentication required', { statusCode: 401, code: AUTH_REQUIRED_CODE })\n\n const exposure = checkExposure(deps.store.config.control, false)\n if (exposure.exposed) {\n throw new DetailedError('refusing to clear the password while the control panel is bound beyond loopback — set the bind back to local first', {\n statusCode: 400,\n code: 'EXPOSED_WITHOUT_PASSWORD',\n })\n }\n\n deps.auth.clearPassword()\n deps.store.updateControl({ auth: { enabled: false } })\n return c.json({ ok: true })\n },\n )\n}\n","import type { ConsolaInstance } from 'consola'\nimport { createConsola, LogLevels } from 'consola'\nimport { isDevelopment } from 'std-env'\n\n/**\n * Note: this logger will log the `debug` level logs in development mode.\n *\n * For actual debug logs with `NODE_DEBUG`, it is recommended to use the `debug` package.\n */\nexport const logger: ConsolaInstance = createConsola(\n {\n level: isDevelopment ? LogLevels.debug : undefined,\n },\n)\n","import type { StandardSchemaV1 } from '@standard-schema/spec'\nimport { DetailedError } from '@namesmt/utils'\nimport { type } from 'arktype'\n\n/**\n * Validates a value that is not a request target (a query string, a URL\n * parameter, a payload read by hand) and fails with the standard error envelope.\n * Request bodies and queries that a route reads once go through the `validate()`\n * middleware instead, which also carries the type into the handler.\n */\nexport function parseOrThrow<T>(schema: (input: unknown) => unknown, input: unknown, label: string): T {\n const result = schema(input)\n if (result instanceof type.errors) {\n throw new DetailedError(`${label}: ${result.summary}`, {\n statusCode: 400,\n code: 'INVALID_INPUT',\n detail: result.issues.map(issue => ({ path: issue.path.join('.'), message: issue.message })),\n })\n }\n return result as T\n}\n\n/** ArkType is a Standard Schema, so the middleware accepts it as-is. */\nexport type ValidatorSchema = StandardSchemaV1\n","import os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\n\n/**\n * Where home-hosted keeps everything it owns: the servers config, the secrets\n * file, logs, TLS material, backups and the runtime file. `HHOSTED_HOME`\n * overrides it — which is how a project repo keeps its own state directory\n * while the package itself ships no configuration at all.\n */\nexport function resolveDataRoot(): string {\n const override = process.env.HHOSTED_HOME\n if (override !== undefined && override.length > 0)\n return path.resolve(override)\n return path.join(os.homedir(), '.home-hosted')\n}\n\nexport const dataRoot = resolveDataRoot()\n\n/**\n * The directory home-hosted was started from. Relative entry paths (`cwd`,\n * declared data directories) resolve against it, so the project's own launcher\n * decides the base instead of wherever the package happens to be installed.\n * `HHOSTED_PROJECT` pins it explicitly.\n */\nexport function resolveProjectDir(): string {\n const override = process.env.HHOSTED_PROJECT\n if (override !== undefined && override.length > 0)\n return path.resolve(override)\n return process.cwd()\n}\n\nexport const projectDir = resolveProjectDir()\n\nexport const defaultConfigPath = path.join(dataRoot, 'servers.config.json')\n/** Regenerated for editor autocomplete; kept beside the config it describes. */\nexport const configSchemaPath = path.join(dataRoot, 'servers.config.schema.json')\n/** Password hash + bot token; written with mode 0600. */\nexport const defaultSecretsPath = path.join(dataRoot, '.control-secrets.json')\n/** Rotated per-server JSONL logs. */\nexport const defaultLogsDir = path.join(dataRoot, '.logs')\n/** Persisted restart/crash history. */\nexport const defaultHistoryPath = path.join(dataRoot, '.logs', 'history.json')\n/** Uploaded TLS PEM pair (the key is written 0600). */\nexport const defaultTlsDir = path.join(dataRoot, '.tls')\n/** `run.json` records the live control plane; the log captures its console. */\nexport const runtimePath = path.join(dataRoot, 'run.json')\nexport const daemonLogPath = path.join(dataRoot, '.logs', 'home-hosted.log')\n\n/** Expands `~` and resolves relative paths against `base`, for config-declared paths. */\nexport function resolveUserPath(target: string, base = projectDir): string {\n let value = target\n if (value === '~')\n value = os.homedir()\n else if (value.startsWith('~/'))\n value = path.join(os.homedir(), value.slice(2))\n return path.isAbsolute(value) ? value : path.resolve(base, value)\n}\n","import type { ConfigStore } from '#src/config/store'\nimport type { AuthService } from '#src/services/auth'\nimport type { BackupService } from '#src/services/backups'\nimport type { ControlEndpoint } from '#src/services/control-server'\nimport type { HostMonitor } from '#src/services/host-monitor'\nimport type { NotificationService } from '#src/services/notifications'\nimport type { TlsStore } from '#src/services/tls'\nimport type { AppState, BackupsView, ControlConfig, ControlView, HostView, ServerDefaults, ServerView } from '#src/shared/contracts'\nimport { dataRoot, projectDir } from '#src/helpers/paths'\nimport { checkExposure } from '#src/services/exposure'\n\nexport interface BuildStateDeps {\n store: ConfigStore\n auth: AuthService\n control: ControlEndpoint\n tls: TlsStore\n notifications: NotificationService\n hostMonitor: HostMonitor\n backups: BackupService\n logsDir: string\n views: ServerView[]\n}\n\n/**\n * The panel's own settings are derived here — configured values from the store,\n * live values from the listener, security state from the auth service and the\n * certificate pair from disk — so the UI never has to reason about the\n * differences.\n */\nexport function buildControlView(\n store: ConfigStore,\n auth: AuthService,\n control: ControlEndpoint,\n tls: TlsStore,\n): ControlView {\n const config: ControlConfig = store.config.control\n const exposure = checkExposure(config, auth.passwordSet, auth.usingDefaultPassword)\n\n return {\n label: config.label,\n port: config.port,\n host: config.host,\n bindHost: control.bindHost,\n url: control.url,\n protocol: control.protocol,\n openBrowser: config.openBrowser,\n restartRequired: control.port !== config.port || control.host !== config.host,\n auth: {\n enabled: config.auth.enabled,\n passwordSet: auth.passwordSet,\n passwordUpdatedAt: auth.passwordUpdatedAt,\n usingDefaultPassword: auth.usingDefaultPassword,\n exposed: exposure.exposed,\n blockedReason: exposure.blockedReason,\n sessionTtlMs: config.auth.sessionTtlMs,\n cookieSecure: config.auth.cookieSecure,\n trustProxy: config.auth.trustProxy,\n maxLoginAttempts: config.auth.maxLoginAttempts,\n lockoutMs: config.auth.lockoutMs,\n },\n tls: tls.status(config.tls.enabled),\n }\n}\n\nexport function buildDefaults(store: ConfigStore): ServerDefaults {\n return store.defaults\n}\n\nexport function buildBackupsView(store: ConfigStore, backups: BackupService): BackupsView {\n return {\n enabled: store.config.backups.enabled,\n dir: backups.directory,\n keep: store.config.backups.keep,\n includePaths: store.config.backups.includePaths,\n paths: backups.paths,\n files: backups.list(),\n }\n}\n\nexport function buildHostView(hostMonitor: HostMonitor): HostView {\n return hostMonitor.view\n}\n\nexport function buildAppState(deps: BuildStateDeps): AppState {\n return {\n control: buildControlView(deps.store, deps.auth, deps.control, deps.tls),\n defaults: buildDefaults(deps.store),\n logs: deps.store.config.logs,\n notifications: { telegram: deps.notifications.status() },\n host: buildHostView(deps.hostMonitor),\n backups: buildBackupsView(deps.store, deps.backups),\n configPath: deps.store.path,\n configError: deps.store.configError,\n projectDir,\n dataRoot,\n logsDir: deps.logsDir,\n servers: deps.views,\n }\n}\n","import type { AppDeps } from '#src/app'\nimport type { BackupCreate, RestoreRequest } from '#src/shared/contracts'\nimport { Buffer } from 'node:buffer'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { DetailedError } from '@namesmt/utils'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\nimport { logger } from '#src/helpers/logger'\nimport { ERROR_RESPONSES, jsonBody } from '#src/helpers/openapi'\nimport { parseOrThrow } from '#src/helpers/validate'\nimport { validate } from '#src/helpers/validator'\nimport { buildBackupsView } from '#src/services/state'\nimport { backupCreateSchema, backupsViewSchema, restorePlanSchema, restoreRequestSchema } from '#src/shared/contracts'\n\n/** Uploads are buffered in memory by `parseBody`, so they get a hard ceiling. */\nconst MAX_UPLOAD_BYTES = 256 * 1024 * 1024\n\n/** A backup that failed to be created is a bad request, not a server fault. */\nfunction backupFailed(error: string | undefined): DetailedError {\n return new DetailedError(error ?? 'the backup failed', { statusCode: 400, code: 'BACKUP_FAILED' })\n}\n\nexport function createBackupsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/backups',\n describeRoute({\n tags: ['backups'],\n summary: 'Archives on disk, and the paths a backup would capture',\n responses: { 200: { description: 'Backups', content: jsonBody(backupsViewSchema) } },\n }),\n c => c.json(buildBackupsView(deps.store, deps.backups)),\n )\n\n .post(\n '/backups',\n describeRoute({\n tags: ['backups'],\n summary: 'Create a backup (optionally password-protected)',\n responses: { 200: { description: 'Created' }, 400: ERROR_RESPONSES[400] },\n }),\n validate('json', backupCreateSchema),\n async (c) => {\n const body: BackupCreate = c.req.valid('json')\n const result = await deps.backups.create({ password: body.password })\n if (!result.ok)\n throw backupFailed(result.error)\n return c.json({ file: result.file, files: deps.backups.list() })\n },\n )\n\n .get(\n '/backups/:name/download',\n describeRoute({\n tags: ['backups'],\n summary: 'Download one archive',\n responses: { 200: { description: 'the archive (application/zip)' }, 404: ERROR_RESPONSES[404] },\n }),\n (c) => {\n const file = deps.backups.resolve(c.req.param('name'))\n if (file === null)\n throw new DetailedError('unknown backup', { statusCode: 404, code: 'UNKNOWN_BACKUP' })\n\n const stats = fs.statSync(file)\n return c.body(fs.readFileSync(file), 200, {\n 'Content-Type': 'application/zip',\n 'Content-Length': String(stats.size),\n 'Content-Disposition': `attachment; filename=\"${path.basename(file)}\"`,\n })\n },\n )\n\n .delete(\n '/backups/:name',\n describeRoute({\n tags: ['backups'],\n summary: 'Delete one archive',\n responses: { 200: { description: 'Removed' }, 404: ERROR_RESPONSES[404] },\n }),\n (c) => {\n if (!deps.backups.remove(c.req.param('name')))\n throw new DetailedError('unknown backup', { statusCode: 404, code: 'UNKNOWN_BACKUP' })\n return c.json({ ok: true, files: deps.backups.list() })\n },\n )\n\n /**\n * Restore from a stored backup (`{ \"name\": \"...\" }`) or an uploaded one.\n * Without `confirm` it answers with the plan and changes nothing; `password`\n * unlocks a protected archive and `include` selects the items to apply.\n */\n .post('/backups/restore', describeRoute({\n tags: ['backups'],\n summary: 'Plan or apply a restore from a stored or uploaded archive',\n responses: { 200: { description: 'The plan', content: jsonBody(restorePlanSchema) }, 400: ERROR_RESPONSES[400], 404: ERROR_RESPONSES[404] },\n }), async (c) => {\n const confirm = c.req.query('confirm') === 'true'\n const contentType = c.req.header('content-type') ?? ''\n\n let archive: string | null = null\n let uploadedTo: string | null = null\n let request: RestoreRequest\n\n try {\n if (contentType.includes('multipart/form-data')) {\n const declared = Number.parseInt(c.req.header('content-length') ?? '0', 10)\n if (Number.isFinite(declared) && declared > MAX_UPLOAD_BYTES)\n throw new DetailedError(`the upload is larger than ${Math.round(MAX_UPLOAD_BYTES / 1024 / 1024)}MB`, { statusCode: 413, code: 'UPLOAD_TOO_LARGE' })\n\n const body = await c.req.parseBody()\n const file = body.file\n if (!(file instanceof File))\n throw new DetailedError('expected a `file` field with the archive', { statusCode: 400, code: 'MISSING_FILE' })\n if (file.size > MAX_UPLOAD_BYTES)\n throw new DetailedError(`the upload is larger than ${Math.round(MAX_UPLOAD_BYTES / 1024 / 1024)}MB`, { statusCode: 413, code: 'UPLOAD_TOO_LARGE' })\n\n const uploads = path.join(deps.backups.directory, 'uploads')\n fs.mkdirSync(uploads, { recursive: true })\n uploadedTo = path.join(uploads, `upload-${Date.now()}.zip`)\n fs.writeFileSync(uploadedTo, Buffer.from(await file.arrayBuffer()))\n archive = uploadedTo\n // A multipart body can only carry strings, so the selection is JSON.\n const rawInclude = typeof body.include === 'string' && body.include.length > 0 ? body.include : null\n let include: unknown\n if (rawInclude !== null) {\n try {\n include = JSON.parse(rawInclude)\n }\n catch {\n throw new DetailedError('`include` must be a JSON array of item ids', { statusCode: 400, code: 'INVALID_INCLUDE' })\n }\n }\n request = parseOrThrow<RestoreRequest>(restoreRequestSchema, {\n ...(typeof body.password === 'string' ? { password: body.password } : {}),\n ...(rawInclude === null ? {} : { include }),\n }, 'body')\n }\n else {\n request = parseOrThrow<RestoreRequest>(restoreRequestSchema, await c.req.json().catch(() => ({})), 'body')\n if (request.name === undefined)\n throw new DetailedError('expected a backup name or a file upload', { statusCode: 400, code: 'MISSING_ARCHIVE' })\n archive = deps.backups.resolve(request.name)\n if (archive === null)\n throw new DetailedError('unknown backup', { statusCode: 404, code: 'UNKNOWN_BACKUP' })\n }\n\n const plan = await deps.backups.restore(archive, {\n confirm,\n password: request.password,\n include: request.include,\n })\n // A wrong or missing password is a prompt, not a failure.\n if (plan.needsPassword)\n return c.json(plan)\n if (plan.error !== undefined)\n throw new DetailedError(plan.error, { statusCode: 400, code: 'RESTORE_FAILED', detail: { items: plan.items, applied: plan.applied, skipped: plan.skipped } })\n if (confirm)\n logger.info(`restored from ${path.basename(archive)}: ${plan.applied.join(', ')}`)\n\n return c.json(plan)\n }\n finally {\n // An uploaded archive is only needed for this request.\n if (uploadedTo !== null)\n fs.rmSync(uploadedTo, { force: true })\n }\n })\n}\n","/**\n * Runs a task after the current response has been written.\n *\n * Moving the control listener closes the connection serving the request that\n * asked for the move, so those steps must happen once the response is out.\n */\nexport function afterResponse(task: () => Promise<void>, onError?: (error: unknown) => void): void {\n setImmediate(() => {\n void task().catch((error: unknown) => {\n onError?.(error)\n })\n })\n}\n","import type { AppDeps } from '#src/app'\nimport { DetailedError } from '@namesmt/utils'\nimport { describeRoute } from 'hono-openapi'\nimport { afterResponse } from '#src/helpers/deferred'\nimport { appFactory } from '#src/helpers/factory'\nimport { logger } from '#src/helpers/logger'\nimport { isLoopbackRequest } from '#src/middleware/loopback'\n\n/**\n * The local control channel used by `home-hosted down`.\n *\n * It is deliberately outside `/api` (so no session is needed) and guarded by a\n * token that only the owner of `run.json` can read, plus a loopback check: a\n * different local user, or anyone on the network, gets nothing.\n */\nexport function createControlRoute(deps: AppDeps) {\n return appFactory.createApp()\n .post(\n '/shutdown',\n describeRoute({\n tags: ['panel'],\n summary: 'Stop the panel and everything it supervises (local token required)',\n responses: { 200: { description: 'Stopping' }, 403: { description: 'Bad token, or not a local caller' } },\n }),\n (c) => {\n const token = c.req.header('x-home-hosted-token')\n if (token === undefined || token !== deps.runtimeToken)\n throw new DetailedError('invalid token', { statusCode: 403, code: 'INVALID_TOKEN' })\n if (!isLoopbackRequest(c))\n throw new DetailedError('only this machine may stop the control panel', { statusCode: 403, code: 'NOT_LOOPBACK' })\n\n // Deferred: the answer has to reach `down` before the process goes away.\n afterResponse(deps.onShutdown, error => logger.error('shutdown failed', error))\n\n return c.json({ ok: true })\n },\n )\n}\n","import type { AppDeps } from '#src/app'\nimport type { SseMessage } from '#src/shared/contracts'\nimport { type } from 'arktype'\nimport { describeRoute } from 'hono-openapi'\nimport { streamSSE } from 'hono/streaming'\nimport { appFactory } from '#src/helpers/factory'\nimport { validate } from '#src/helpers/validator'\n\nconst MAX_PENDING_WRITES = 200\nconst PING_INTERVAL_MS = 15000\n\nconst eventsQuery = type({\n /** Only this server's frames. */\n 'serverId?': 'string',\n /** `logs=0` drops log frames; the first frame is always the full state. */\n 'logs?': 'string',\n})\n\n/**\n * One SSE stream per subscriber. Pass `?serverId=<id>` to receive only that\n * server's messages; the first frame always carries the full state snapshot.\n */\nexport function createEventsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/events',\n describeRoute({\n tags: ['panel'],\n summary: 'Panel state and log frames as server-sent events',\n responses: { 200: { description: 'text/event-stream' } },\n }),\n validate('query', eventsQuery),\n (c) => {\n const query = c.req.valid('query')\n const serverId = query.serverId ?? null\n const logOnly = query.logs !== '0'\n\n return streamSSE(c, async (stream) => {\n let pending = 0\n let queue: Promise<void> = Promise.resolve()\n let closed = false\n\n const send = (message: SseMessage): Promise<void> => {\n if (closed)\n return queue\n // A chatty child must not grow the queue without bound; state frames are\n // always kept, log frames are dropped once the client falls behind.\n if (message.type === 'log' && pending > MAX_PENDING_WRITES)\n return queue\n pending += 1\n queue = queue\n .then(() => stream.writeSSE({ event: message.type, data: JSON.stringify(message) }))\n .catch(() => {\n closed = true\n })\n .finally(() => {\n pending -= 1\n })\n return queue\n }\n\n const unsubscribe = deps.hub.subscribe(serverId, (message) => {\n if (!logOnly && message.type === 'log')\n return\n void send(message)\n })\n\n stream.onAbort(() => {\n closed = true\n unsubscribe()\n })\n\n await send({ type: 'hello', ts: Date.now(), state: deps.supervisor.getState() })\n\n while (true) {\n await stream.sleep(PING_INTERVAL_MS)\n if (closed)\n break\n await stream.writeSSE({ event: 'ping', data: String(Date.now()) })\n }\n })\n },\n )\n}\n","import type { AppDeps } from '#src/app'\nimport process from 'node:process'\nimport { type } from 'arktype'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\nimport { jsonBody } from '#src/helpers/openapi'\n\n/**\n * Liveness for external monitors. Mounted outside `/api`, so it answers without a\n * session: it reports whether the panel itself is serving, and 503 when an\n * autostart server has crashed. Server details are only included for an\n * authenticated caller.\n */\nexport function createHealthRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/healthz',\n describeRoute({\n tags: ['panel'],\n summary: 'Liveness for monitors — no session required',\n responses: {\n 200: {\n description: 'Serving',\n content: jsonBody(type({\n 'status': '\"ok\" | \"degraded\"',\n 'uptimeMs': 'number',\n 'servers?': type({ total: 'number', running: 'number', crashed: 'number', unhealthy: 'number' }),\n 'hostAlerts?': 'string[]',\n })),\n },\n 503: { description: 'An autostart server has crashed' },\n },\n }),\n (c) => {\n const state = deps.supervisor.getState()\n const broken = state.servers.filter(server => server.config.autostart && server.status === 'crashed')\n const authenticated = deps.auth.validate(deps.auth.tokenFromCookie(c.req.header('cookie'))) !== null\n\n return c.json({\n status: broken.length > 0 ? 'degraded' : 'ok',\n uptimeMs: Math.round(process.uptime() * 1000),\n ...(authenticated\n ? {\n servers: {\n total: state.servers.length,\n running: state.servers.filter(server => server.status === 'running').length,\n crashed: state.servers.filter(server => server.status === 'crashed').length,\n unhealthy: state.servers.filter(server => server.health === 'unhealthy').length,\n },\n hostAlerts: state.host.alerts,\n }\n : {}),\n }, broken.length > 0 ? 503 : 200)\n },\n )\n}\n","import type { AppDeps } from '#src/app'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { DetailedError } from '@namesmt/utils'\nimport { type } from 'arktype'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\nimport { ERROR_RESPONSES, jsonBody } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { logHistoryQuerySchema, logServersViewSchema } from '#src/shared/contracts'\n\n/** Bounds for the tail query, so a bad client cannot ask for the whole file. */\nconst MIN_TAIL = 50\nconst MAX_TAIL = 5000\nconst DEFAULT_TAIL = 500\n\nconst idParam = type({ id: 'string >= 1' })\nconst downloadQuery = type({ 'file?': 'string' })\n\nfunction unknownServer(id: string): DetailedError {\n return new DetailedError(`unknown server \"${id}\"`, { statusCode: 404, code: 'UNKNOWN_SERVER' })\n}\n\nexport function createLogsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/logs',\n describeRoute({\n tags: ['logs'],\n summary: 'Every server with its on-disk log files',\n responses: { 200: { description: 'Log sources', content: jsonBody(logServersViewSchema) } },\n }),\n c => c.json({\n servers: deps.supervisor.views().map(server => ({\n serverId: server.id,\n label: server.config.label ?? server.id,\n status: server.status,\n ...deps.logFiles.info(server.id),\n })),\n }),\n )\n\n .get(\n '/logs/:id',\n describeRoute({\n tags: ['logs'],\n summary: 'Persisted log lines, with search and a stream filter',\n responses: { 200: { description: 'Lines' }, 404: ERROR_RESPONSES[404] },\n }),\n validate('param', idParam),\n validate('query', logHistoryQuerySchema),\n (c) => {\n const { id } = c.req.valid('param')\n if (!deps.store.getServer(id))\n throw unknownServer(id)\n\n const query = c.req.valid('query')\n const requested = query.tail === undefined ? DEFAULT_TAIL : Number.parseInt(query.tail, 10)\n const tail = Number.isNaN(requested) ? DEFAULT_TAIL : Math.min(Math.max(requested, MIN_TAIL), MAX_TAIL)\n\n const info = deps.logFiles.info(id)\n // Search reads a wider window than the display tail, otherwise a match older\n // than the last N lines would look like \"no results\".\n const search = query.search?.trim() ?? ''\n const window = search.length > 0 ? Math.max(tail, MAX_TAIL) : tail\n\n let lines = deps.logFiles.readTail(id, window)\n if (query.stream !== undefined && query.stream.length > 0)\n lines = lines.filter(line => line.stream === query.stream)\n if (search.length > 0) {\n const needle = search.toLowerCase()\n lines = lines.filter(line => line.text.toLowerCase().includes(needle))\n }\n\n return c.json({\n serverId: id,\n enabled: info.enabled,\n sizeBytes: info.sizeBytes,\n files: info.files.map(file => file.name),\n searched: search.length > 0 ? window : null,\n lines: lines.slice(-tail),\n })\n },\n )\n\n /** Raw file download; the name is checked against the rotation allowlist. */\n .get(\n '/logs/:id/download',\n describeRoute({\n tags: ['logs'],\n summary: 'Download one rotated log file',\n responses: { 200: { description: 'application/x-ndjson' }, 404: ERROR_RESPONSES[404] },\n }),\n validate('param', idParam),\n validate('query', downloadQuery),\n (c) => {\n const { id } = c.req.valid('param')\n if (!deps.store.getServer(id))\n throw unknownServer(id)\n\n const requested = c.req.valid('query').file ?? `${id}.log`\n const known = deps.logFiles.info(id).files.map(file => file.name)\n if (!known.includes(requested))\n throw new DetailedError('unknown log file', { statusCode: 404, code: 'UNKNOWN_LOG_FILE' })\n\n const file = path.join(deps.logFiles.directory, requested)\n const body = fs.readFileSync(file)\n return c.body(body, 200, {\n 'Content-Type': 'application/x-ndjson; charset=utf-8',\n 'Content-Length': String(body.byteLength),\n 'Content-Disposition': `attachment; filename=\"${requested}\"`,\n })\n },\n )\n\n .delete(\n '/logs/:id',\n describeRoute({\n tags: ['logs'],\n summary: 'Delete the persisted logs of one server',\n responses: { 200: { description: 'Cleared' }, 404: ERROR_RESPONSES[404] },\n }),\n validate('param', idParam),\n (c) => {\n const { id } = c.req.valid('param')\n if (!deps.store.getServer(id))\n throw unknownServer(id)\n deps.logFiles.clear(id)\n return c.json({ ok: true })\n },\n )\n}\n","import type { AppDeps } from '#src/app'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\n\n/**\n * Prometheus text for whatever wants to scrape the panel (Beszel, Grafana,\n * `curl`). Lives under `/api`, so it needs a session like every other route.\n */\nexport function createMetricsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get('/metrics', describeRoute({\n tags: ['panel'],\n summary: 'Prometheus text for whatever scrapes the panel',\n responses: { 200: { description: 'text/plain; version=0.0.4' } },\n }), (c) => {\n const state = deps.supervisor.getState()\n const lines: string[] = []\n\n const metric = (name: string, help: string, samples: string[]): void => {\n if (samples.length === 0)\n return\n lines.push(`# HELP ${name} ${help}`, `# TYPE ${name} gauge`, ...samples)\n }\n\n metric('hh2_control_up', 'Control plane is serving', ['hh2_control_up 1'])\n metric('hh2_servers_total', 'Configured servers', [`hh2_servers_total ${state.servers.length}`])\n\n const up = state.servers.map(server => `hh2_server_up{server=\"${server.id}\"} ${server.status === 'running' ? 1 : 0}`)\n metric('hh2_server_up', 'Server process is running', up)\n\n const restarts = state.servers.map(server => `hh2_server_restarts_total{server=\"${server.id}\"} ${server.restarts}`)\n metric('hh2_server_restarts_total', 'Restarts since the control plane started', restarts)\n\n const crashes = state.servers.map(server => `hh2_server_crashes_24h{server=\"${server.id}\"} ${server.history.crashes}`)\n metric('hh2_server_crashes_24h', 'Crashes in the last 24 hours', crashes)\n\n const uptime = state.servers\n .filter(server => server.history.uptimeRatio !== null)\n .map(server => `hh2_server_uptime_ratio_24h{server=\"${server.id}\"} ${server.history.uptimeRatio!.toFixed(4)}`)\n metric('hh2_server_uptime_ratio_24h', 'Share of the last 24 hours the server was up', uptime)\n\n const response = state.servers\n .filter(server => server.responseMs !== null)\n .map(server => `hh2_server_response_ms{server=\"${server.id}\"} ${server.responseMs}`)\n metric('hh2_server_response_ms', 'Last health probe latency in milliseconds', response)\n\n const rss = state.servers\n .filter(server => server.resources?.rssBytes != null)\n .map(server => `hh2_server_rss_bytes{server=\"${server.id}\"} ${server.resources!.rssBytes}`)\n metric('hh2_server_rss_bytes', 'RSS of the server process tree', rss)\n\n const cpu = state.servers\n .filter(server => server.resources?.cpuPercent != null)\n .map(server => `hh2_server_cpu_percent{server=\"${server.id}\"} ${server.resources!.cpuPercent}`)\n metric('hh2_server_cpu_percent', 'CPU percent of the server process tree', cpu)\n\n const disks = state.host.disks.map(disk => `hh2_host_disk_used_percent{mount=\"${disk.path}\"} ${disk.usedPercent.toFixed(2)}`)\n metric('hh2_host_disk_used_percent', 'Disk usage percent per configured path', disks)\n\n metric('hh2_host_memory_used_percent', 'Memory usage percent', [`hh2_host_memory_used_percent ${state.host.memoryUsedPercent.toFixed(2)}`])\n metric('hh2_host_swap_used_percent', 'Swap usage percent', [`hh2_host_swap_used_percent ${state.host.swapUsedPercent.toFixed(2)}`])\n metric('hh2_host_load1_per_cpu', '1 minute load average per cpu', [\n `hh2_host_load1_per_cpu ${((state.host.loadAvg[0] ?? 0) / Math.max(1, state.host.cpus)).toFixed(3)}`,\n ])\n\n return c.text(`${lines.join('\\n')}\\n`, 200, { 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8' })\n })\n}\n","import type { Bot } from 'grammy'\nimport { autoRetry } from '@grammyjs/auto-retry'\nimport { Bot as GrammyBot } from 'grammy'\n\n/**\n * Telegram Bot API access, built on grammY.\n *\n * grammY is used for what it is good at — a typed, retrying, extensible Bot API\n * client — while the bot itself stays outbound-only for now. If inbound commands\n * or a webhook are ever wanted, the same instance can host handlers without\n * touching the notification code.\n *\n * `autoRetry` handles Telegram's 429 `retry_after` (and other transient failures)\n * so callers get either a result or a real error.\n */\n\nconst bots = new Map<string, Bot>()\n\nexport function getBot(token: string): Bot {\n const cached = bots.get(token)\n if (cached)\n return cached\n\n const bot = new GrammyBot(token, { client: { timeoutSeconds: 10 } })\n bot.api.config.use(autoRetry({ maxRetryAttempts: 3, maxDelaySeconds: 20 }))\n bots.set(token, bot)\n return bot\n}\n\n/** Drops cached clients; used when the token changes or the service shuts down. */\nexport function forgetBots(): void {\n bots.clear()\n}\n\nexport function escapeHtml(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')\n}\n\nexport function formatTelegramMessage(title: string, lines: string[]): string {\n const body = lines.filter(line => line.length > 0).map(line => `• ${escapeHtml(line)}`).join('\\n')\n return `<b>${escapeHtml(title)}</b>${body.length > 0 ? `\\n${body}` : ''}`\n}\n\nexport interface TelegramOutcome {\n ok: boolean\n error?: string\n}\n\n/** Turns a grammY error into something worth showing in the settings page. */\nexport function describeTelegramError(error: unknown): string {\n if (typeof error === 'object' && error !== null) {\n const candidate = error as { error_code?: number, description?: string, message?: string, parameters?: { retry_after?: number } }\n const description = candidate.description ?? candidate.message\n if (description) {\n const code = candidate.error_code === undefined ? '' : ` (${candidate.error_code})`\n const retry = candidate.parameters?.retry_after === undefined ? '' : `, retry in ${candidate.parameters.retry_after}s`\n return `${description}${code}${retry}`\n }\n }\n return error instanceof Error ? error.message : String(error)\n}\n\nexport async function sendTelegramMessage(token: string, chatId: string, html: string): Promise<TelegramOutcome> {\n try {\n await getBot(token).api.sendMessage(chatId, html, {\n parse_mode: 'HTML',\n link_preview_options: { is_disabled: true },\n })\n return { ok: true }\n }\n catch (error) {\n return { ok: false, error: describeTelegramError(error) }\n }\n}\n\nexport async function verifyTelegramToken(token: string): Promise<{ ok: boolean, username?: string, error?: string }> {\n try {\n const me = await getBot(token).api.getMe()\n return { ok: true, username: me.username }\n }\n catch (error) {\n return { ok: false, error: describeTelegramError(error) }\n }\n}\n\nexport interface TelegramChat {\n id: number | string\n title: string\n}\n\n/**\n * Recent chats that talked to the bot, so a chat id can be picked instead of\n * hunted down by hand. Telegram only reports chats with pending updates, so the\n * caller is told to message the bot first.\n */\nexport async function listTelegramChats(token: string): Promise<{ ok: boolean, chats: TelegramChat[], error?: string }> {\n try {\n const updates = await getBot(token).api.getUpdates({\n limit: 100,\n allowed_updates: ['message', 'channel_post', 'edited_message'],\n })\n\n const chats = new Map<string, TelegramChat>()\n for (const update of updates) {\n const chat = update.message?.chat ?? update.channel_post?.chat ?? update.edited_message?.chat\n if (!chat)\n continue\n const title = 'title' in chat && chat.title\n ? chat.title\n : 'username' in chat && chat.username\n ? `@${chat.username}`\n : 'first_name' in chat && chat.first_name\n ? chat.first_name\n : 'private chat'\n chats.set(String(chat.id), { id: chat.id, title })\n }\n\n return { ok: true, chats: [...chats.values()] }\n }\n catch (error) {\n return { ok: false, chats: [], error: describeTelegramError(error) }\n }\n}\n","import type { AppDeps } from '#src/app'\nimport { DetailedError } from '@namesmt/utils'\nimport { type } from 'arktype'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\nimport { ERROR_RESPONSES, jsonBody } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { forgetBots } from '#src/providers/telegram'\nimport { notificationActionSchema, telegramTokenSchema } from '#src/shared/contracts'\n\n/**\n * The bot token is written straight to the secrets file and never into\n * `servers.config.json`, so notification *policy* and the *credential* stay\n * separate.\n */\nexport function createNotificationsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .put(\n '/notifications/token',\n describeRoute({\n tags: ['notifications'],\n summary: 'Store the Telegram bot token (verified first)',\n responses: { 200: { description: 'Stored' }, 400: ERROR_RESPONSES[400] },\n }),\n validate('json', telegramTokenSchema),\n async (c) => {\n const { botToken } = c.req.valid('json')\n const verified = await deps.notifications.verifyToken(botToken)\n if (!verified.ok)\n throw new DetailedError(`telegram rejected the token: ${verified.error ?? 'unknown error'}`, { statusCode: 400, code: 'TELEGRAM_TOKEN_REJECTED' })\n\n deps.secrets.setTelegramToken(botToken)\n forgetBots()\n return c.json({ ok: true, username: verified.username ?? null })\n },\n )\n\n .delete(\n '/notifications/token',\n describeRoute({\n tags: ['notifications'],\n summary: 'Forget the Telegram bot token',\n responses: { 200: { description: 'Removed' } },\n }),\n (c) => {\n deps.secrets.setTelegramToken(null)\n forgetBots()\n return c.json({ ok: true })\n },\n )\n\n .post(\n '/notifications/test',\n describeRoute({\n tags: ['notifications'],\n summary: 'Send a test message',\n responses: { 200: { description: 'Sent or refused' }, 400: ERROR_RESPONSES[400] },\n }),\n validate('json', notificationActionSchema),\n async (c) => {\n const result = await deps.notifications.sendTest(c.req.valid('json'))\n if (!result.ok)\n throw new DetailedError(result.error ?? 'the test message failed', { statusCode: 400, code: 'TELEGRAM_SEND_FAILED' })\n return c.json(result)\n },\n )\n\n .post(\n '/notifications/detect-chats',\n describeRoute({\n tags: ['notifications'],\n summary: 'List the chats the bot can see',\n responses: {\n 200: { description: 'Chats', content: jsonBody(type({ chats: type({ id: 'string | number', title: 'string' }).array() })) },\n 400: ERROR_RESPONSES[400],\n },\n }),\n validate('json', notificationActionSchema),\n async (c) => {\n const result = await deps.notifications.detectChats(c.req.valid('json'))\n if (!result.ok)\n throw new DetailedError(result.error ?? 'could not list chats', { statusCode: 400, code: 'TELEGRAM_LIST_FAILED' })\n return c.json({ chats: result.chats })\n },\n )\n}\n","import type { ServerConfig } from '#src/shared/contracts'\nimport { type } from 'arktype'\nimport {\n backupsSchema,\n controlSchema,\n defaultsSchema,\n hostSchema,\n logsSchema,\n notificationsSchema,\n serverSchema,\n} from '#src/shared/contracts'\n\nexport { backupsSchema, controlSchema, defaultsSchema, hostSchema, logsSchema, notificationsSchema, serverSchema }\nexport type { ServerConfig } from '#src/shared/contracts'\n\n/** The shape of `servers.config.json`: control panel settings, defaults, servers. */\nexport const configSchema = type({\n $schema: 'string?',\n control: controlSchema.default(() => ({})),\n defaults: defaultsSchema.default(() => ({})),\n logs: logsSchema.default(() => ({})),\n notifications: notificationsSchema.default(() => ({})),\n host: hostSchema.default(() => ({})),\n backups: backupsSchema.default(() => ({})),\n servers: serverSchema.array().default(() => []),\n}).onUndeclaredKey('reject')\n\n/** Same shape as the schema output, with each server `port` normalized to `null` when unset. */\nexport type ResolvedConfig = Omit<typeof configSchema.infer, 'servers'> & { servers: ServerConfig[] }\n\n/** The on-disk shape: everything optional except `servers`, defaults applied per entry. */\nexport interface RawConfig {\n $schema?: string\n control?: Record<string, unknown>\n defaults?: Record<string, unknown>\n logs?: Record<string, unknown>\n notifications?: Record<string, unknown>\n host?: Record<string, unknown>\n backups?: Record<string, unknown>\n servers?: Record<string, unknown>[]\n}\n","import type { RawConfig } from '#src/config/schema'\n\n/**\n * Written when a data directory has no config yet (`$HHOSTED_HOME/servers.config.json`).\n *\n * It stays empty on purpose: home-hosted ships no servers of its own, so what a\n * user supervises is theirs to declare. The rest of the file is default policy,\n * which the settings page can change.\n */\nexport const SEED_CONFIG: RawConfig = {\n $schema: './servers.config.schema.json',\n control: {\n port: 3999,\n host: 'local',\n openBrowser: false,\n },\n defaults: {\n enabled: true,\n autostart: false,\n bind: 'local',\n onPortConflict: 'block',\n },\n servers: [],\n}\n","import type { RawConfig, ResolvedConfig, ServerConfig } from '#src/config/schema'\nimport type {\n BackupsConfig,\n ControlConfig,\n HostConfig,\n LogsConfig,\n NotificationsConfig,\n ServerDefaults,\n ServerPatch,\n SettingsPatch,\n} from '#src/shared/contracts'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { type } from 'arktype'\nimport {\n backupsSchema,\n configSchema,\n controlSchema,\n defaultsSchema,\n hostSchema,\n logsSchema,\n notificationsSchema,\n serverSchema,\n} from '#src/config/schema'\nimport { SEED_CONFIG } from '#src/config/seed'\nimport { writeFileAtomic } from '#src/helpers/atomic'\nimport { configSchemaPath } from '#src/helpers/paths'\n\n/** Nested groups a patch merges into instead of replacing. */\nconst SERVER_MERGE_KEYS = new Set(['restart', 'health', 'stop'])\nconst CONTROL_MERGE_KEYS = new Set(['auth', 'tls'])\nconst NOTIFICATION_MERGE_KEYS = new Set(['telegram'])\nconst EMPTY_MERGE_KEYS = new Set<string>()\n\nexport class ConfigError extends Error {\n override name = 'ConfigError'\n}\n\nfunction formatErrors(errors: type.errors): string {\n return errors.summary\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Dangling dependencies and cycles are reported, not fatal: supervision still runs. */\nfunction validateDependencies(servers: ServerConfig[]): string[] {\n const ids = new Set(servers.map(server => server.id))\n const errors: string[] = []\n\n for (const server of servers) {\n for (const dependency of server.dependsOn) {\n if (dependency === server.id)\n errors.push(`\"${server.id}\" depends on itself`)\n else if (!ids.has(dependency))\n errors.push(`\"${server.id}\" depends on unknown server \"${dependency}\"`)\n }\n }\n\n const visiting = new Set<string>()\n const done = new Set<string>()\n const byId = new Map(servers.map(server => [server.id, server]))\n\n const walk = (id: string, trail: string[]): void => {\n if (done.has(id))\n return\n if (visiting.has(id)) {\n errors.push(`dependency cycle: ${[...trail, id].join(' -> ')}`)\n return\n }\n visiting.add(id)\n for (const dependency of byId.get(id)?.dependsOn ?? []) {\n if (ids.has(dependency) && dependency !== id)\n walk(dependency, [...trail, id])\n }\n visiting.delete(id)\n done.add(id)\n }\n\n for (const server of servers) walk(server.id, [])\n return errors\n}\n\n/** Nested groups merge so a partial edit never drops a sibling field. */\nfunction applyPatch(target: Record<string, unknown>, patch: Record<string, unknown>, mergeKeys: Set<string>): void {\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined)\n continue\n if (mergeKeys.has(key) && isRecord(value) && isRecord(target[key])) {\n target[key] = mergeGroup(target[key], value)\n continue\n }\n target[key] = value\n }\n}\n\n/**\n * Merges one nested group recursively — `health.http` is a group of its own, and\n * replacing it wholesale would silently reset the siblings a partial patch never\n * mentioned. An explicit `null` removes a key, which is how a schema-optional\n * field is cleared.\n */\nfunction mergeGroup(target: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {\n const merged = { ...target }\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined)\n continue\n if (value === null) {\n delete merged[key]\n continue\n }\n if (isRecord(value) && isRecord(merged[key])) {\n merged[key] = mergeGroup(merged[key] as Record<string, unknown>, value)\n continue\n }\n merged[key] = value\n }\n return merged\n}\n\nexport class ConfigStore {\n private raw: RawConfig = {}\n private resolvedConfig!: ResolvedConfig\n private error: string | null = null\n private readonly listeners = new Set<() => void>()\n\n constructor(private readonly file: string, private readonly seed: RawConfig = SEED_CONFIG) {}\n\n get path(): string {\n return this.file\n }\n\n get config(): ResolvedConfig {\n return this.resolvedConfig\n }\n\n get configError(): string | null {\n return this.error\n }\n\n get servers(): ServerConfig[] {\n return this.resolvedConfig.servers\n }\n\n get defaults(): ResolvedConfig['defaults'] {\n return this.resolvedConfig.defaults\n }\n\n get rawConfig(): RawConfig {\n return structuredClone(this.raw)\n }\n\n onChange(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n getServer(id: string): ServerConfig | undefined {\n return this.servers.find(server => server.id === id)\n }\n\n /**\n * Reads the file and tells the listeners, so whatever wrote it — the settings\n * page, or a restored backup landing on disk — becomes the live config.\n */\n load(): void {\n this.read()\n this.notify()\n }\n\n private read(): void {\n if (!fs.existsSync(this.file)) {\n // A missing file gets the seed, written out for the user to edit.\n const seed = structuredClone(this.seed)\n writeFileAtomic(this.file, `${JSON.stringify(seed, null, 2)}\\n`)\n this.raw = seed\n this.apply(seed)\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(fs.readFileSync(this.file, 'utf8'))\n }\n catch (error) {\n this.error = `cannot parse ${path.basename(this.file)}: ${(error as Error).message}`\n this.raw = {}\n this.resolvedConfig = this.resolveFallback()\n return\n }\n\n if (!isRecord(parsed)) {\n this.error = `${path.basename(this.file)} must contain a JSON object`\n this.raw = {}\n this.resolvedConfig = this.resolveFallback()\n return\n }\n\n this.apply(parsed as RawConfig)\n }\n\n private notify(): void {\n for (const listener of this.listeners) listener()\n }\n\n updateServer(id: string, patch: ServerPatch): ServerConfig {\n const index = this.raw.servers?.findIndex(entry => entry.id === id) ?? -1\n if (index < 0)\n throw new ConfigError(`unknown server \"${id}\"`)\n\n const draft = structuredClone(this.raw)\n const entry = draft.servers![index]!\n\n applyPatch(entry, patch as Record<string, unknown>, SERVER_MERGE_KEYS)\n\n const validated = this.validateServer(entry, `servers[${index}]`)\n this.commit(draft)\n return validated\n }\n\n updateControl(patch: NonNullable<SettingsPatch['control']>): ControlConfig {\n const draft = structuredClone(this.raw)\n draft.control = { ...(draft.control ?? {}) }\n applyPatch(draft.control, patch as Record<string, unknown>, CONTROL_MERGE_KEYS)\n\n const control = controlSchema(draft.control)\n if (control instanceof type.errors)\n throw new ConfigError(`control: ${formatErrors(control)}`)\n\n this.commit(draft)\n return control\n }\n\n updateLogs(patch: NonNullable<SettingsPatch['logs']>): LogsConfig {\n const draft = structuredClone(this.raw)\n draft.logs = { ...(draft.logs ?? {}) }\n applyPatch(draft.logs, patch as Record<string, unknown>, EMPTY_MERGE_KEYS)\n\n const logs = logsSchema(draft.logs)\n if (logs instanceof type.errors)\n throw new ConfigError(`logs: ${formatErrors(logs)}`)\n\n this.commit(draft)\n return logs\n }\n\n updateNotifications(patch: NonNullable<SettingsPatch['notifications']>): NotificationsConfig {\n const draft = structuredClone(this.raw)\n draft.notifications = { ...(draft.notifications ?? {}) }\n applyPatch(draft.notifications, patch as Record<string, unknown>, NOTIFICATION_MERGE_KEYS)\n\n const notifications = notificationsSchema(draft.notifications)\n if (notifications instanceof type.errors)\n throw new ConfigError(`notifications: ${formatErrors(notifications)}`)\n\n this.commit(draft)\n return notifications\n }\n\n updateHost(patch: NonNullable<SettingsPatch['host']>): HostConfig {\n const draft = structuredClone(this.raw)\n draft.host = { ...(draft.host ?? {}) }\n applyPatch(draft.host, patch as Record<string, unknown>, EMPTY_MERGE_KEYS)\n\n const host = hostSchema(draft.host)\n if (host instanceof type.errors)\n throw new ConfigError(`host: ${formatErrors(host)}`)\n\n this.commit(draft)\n return host\n }\n\n updateBackups(patch: NonNullable<SettingsPatch['backups']>): BackupsConfig {\n const draft = structuredClone(this.raw)\n draft.backups = { ...(draft.backups ?? {}) }\n applyPatch(draft.backups, patch as Record<string, unknown>, EMPTY_MERGE_KEYS)\n\n const backups = backupsSchema(draft.backups)\n if (backups instanceof type.errors)\n throw new ConfigError(`backups: ${formatErrors(backups)}`)\n\n this.commit(draft)\n return backups\n }\n\n updateDefaults(patch: NonNullable<SettingsPatch['defaults']>): ServerDefaults {\n const draft = structuredClone(this.raw)\n draft.defaults = { ...(draft.defaults ?? {}) }\n applyPatch(draft.defaults, patch as Record<string, unknown>, SERVER_MERGE_KEYS)\n\n const defaults = defaultsSchema(draft.defaults)\n if (defaults instanceof type.errors)\n throw new ConfigError(`defaults: ${formatErrors(defaults)}`)\n\n this.commit(draft)\n return defaults\n }\n\n addServer(input: Record<string, unknown>): ServerConfig {\n const draft = structuredClone(this.raw)\n draft.servers ??= []\n if (draft.servers.some(entry => entry.id === input.id)) {\n throw new ConfigError(`server \"${String(input.id)}\" already exists`)\n }\n\n const index = draft.servers.length\n draft.servers.push(structuredClone(input))\n const validated = this.validateServer(draft.servers[index]!, `servers[${index}]`)\n this.commit(draft)\n return validated\n }\n\n removeServer(id: string): void {\n const draft = structuredClone(this.raw)\n const before = draft.servers?.length ?? 0\n draft.servers = (draft.servers ?? []).filter(entry => entry.id !== id)\n if (draft.servers.length === before)\n throw new ConfigError(`unknown server \"${id}\"`)\n this.commit(draft)\n }\n\n /** Regenerates `servers.config.schema.json` for editor autocomplete. */\n writeJsonSchema(): void {\n const schema = JSON.stringify(configSchema.toJsonSchema(), null, 2)\n const current = fs.existsSync(configSchemaPath) ? fs.readFileSync(configSchemaPath, 'utf8') : null\n if (current !== schema)\n writeFileAtomic(configSchemaPath, schema)\n }\n\n private validateServer(entry: Record<string, unknown>, label: string): ServerConfig {\n const parsed = serverSchema({ ...this.defaults, ...entry })\n if (parsed instanceof type.errors)\n throw new ConfigError(`${label}: ${formatErrors(parsed)}`)\n return { ...parsed, port: parsed.port ?? null }\n }\n\n private commit(draft: RawConfig): void {\n writeFileAtomic(this.file, `${JSON.stringify(draft, null, 2)}\\n`)\n this.raw = draft\n this.apply(draft)\n this.notify()\n }\n\n private resolveFallback(): ResolvedConfig {\n const control = controlSchema({})\n const defaults = defaultsSchema({})\n if (control instanceof type.errors || defaults instanceof type.errors) {\n throw new ConfigError('internal: default config failed validation')\n }\n const logs = logsSchema({})\n const notifications = notificationsSchema({})\n const host = hostSchema({})\n const backups = backupsSchema({})\n if (logs instanceof type.errors || notifications instanceof type.errors || host instanceof type.errors || backups instanceof type.errors) {\n throw new ConfigError('internal: default settings failed validation')\n }\n return { control, defaults, logs, notifications, host, backups, servers: [] }\n }\n\n private apply(raw: RawConfig): void {\n this.raw = raw\n const errors: string[] = []\n\n const control = controlSchema(raw.control ?? {})\n const defaults = defaultsSchema(raw.defaults ?? {})\n const logs = logsSchema(raw.logs ?? {})\n const notifications = notificationsSchema(raw.notifications ?? {})\n const host = hostSchema(raw.host ?? {})\n const backups = backupsSchema(raw.backups ?? {})\n if (control instanceof type.errors)\n errors.push(`control: ${formatErrors(control)}`)\n if (defaults instanceof type.errors)\n errors.push(`defaults: ${formatErrors(defaults)}`)\n if (logs instanceof type.errors)\n errors.push(`logs: ${formatErrors(logs)}`)\n if (notifications instanceof type.errors)\n errors.push(`notifications: ${formatErrors(notifications)}`)\n if (host instanceof type.errors)\n errors.push(`host: ${formatErrors(host)}`)\n if (backups instanceof type.errors)\n errors.push(`backups: ${formatErrors(backups)}`)\n\n const resolvedDefaults = defaults instanceof type.errors ? defaultsSchema({}) as ResolvedConfig['defaults'] : defaults\n const servers: ServerConfig[] = []\n const seen = new Set<string>()\n const rawServers = Array.isArray(raw.servers) ? raw.servers : []\n\n rawServers.forEach((entry, index) => {\n const parsed = serverSchema({ ...resolvedDefaults, ...entry })\n if (parsed instanceof type.errors) {\n errors.push(`servers[${index}] (${(entry as { id?: string })?.id ?? 'no id'}): ${formatErrors(parsed)}`)\n return\n }\n if (seen.has(parsed.id)) {\n errors.push(`servers[${index}]: duplicate id \"${parsed.id}\"`)\n return\n }\n seen.add(parsed.id)\n servers.push({ ...parsed, port: parsed.port ?? null })\n })\n\n errors.push(...validateDependencies(servers))\n\n this.error = errors.length > 0 ? errors.join('; ') : null\n this.resolvedConfig = {\n $schema: raw.$schema,\n control: control instanceof type.errors ? controlSchema({}) as ResolvedConfig['control'] : control,\n defaults: resolvedDefaults,\n logs: logs instanceof type.errors ? logsSchema({}) as ResolvedConfig['logs'] : logs,\n notifications: notifications instanceof type.errors\n ? notificationsSchema({}) as ResolvedConfig['notifications']\n : notifications,\n host: host instanceof type.errors ? hostSchema({}) as ResolvedConfig['host'] : host,\n backups: backups instanceof type.errors ? backupsSchema({}) as ResolvedConfig['backups'] : backups,\n servers,\n }\n }\n}\n","import type { AppDeps } from '#src/app'\nimport { DetailedError } from '@namesmt/utils'\nimport { type } from 'arktype'\nimport { describeRoute } from 'hono-openapi'\nimport { streamSSE } from 'hono/streaming'\nimport { ConfigError } from '#src/config/store'\nimport { appFactory } from '#src/helpers/factory'\nimport { ERROR_RESPONSES, jsonBody } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { logQuerySchema, serverCreateSchema, serverPatchSchema, serverViewSchema } from '#src/shared/contracts'\n\nconst idParam = type({ id: 'string >= 1' })\nconst serverResponse = type({ server: serverViewSchema })\nconst serversResponse = type({ servers: serverViewSchema.array() })\nconst okResponse = type({ ok: 'boolean' })\n\n/** Unknown ids are 404; a server that exists but cannot start is a 409. */\nfunction statusFor(result: { ok: boolean, error?: string }): 200 | 404 | 409 {\n if (result.ok)\n return 200\n return result.error?.startsWith('unknown server') ? 404 : 409\n}\n\nfunction unknownServer(id: string): DetailedError {\n return new DetailedError(`unknown server \"${id}\"`, { statusCode: 404, code: 'UNKNOWN_SERVER' })\n}\n\nexport function createServersRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/',\n describeRoute({\n tags: ['servers'],\n summary: 'Every supervised server, with its live state',\n responses: { 200: { description: 'The servers', content: jsonBody(serversResponse) } },\n }),\n c => c.json({ servers: deps.supervisor.views() }),\n )\n\n .post(\n '/',\n describeRoute({\n tags: ['servers'],\n summary: 'Add a server',\n responses: {\n 201: { description: 'Created', content: jsonBody(serverResponse) },\n 400: ERROR_RESPONSES[400],\n },\n }),\n validate('json', serverCreateSchema),\n (c) => {\n const body = c.req.valid('json')\n try {\n return c.json({ server: deps.store.addServer(body) }, 201)\n }\n catch (error) {\n if (error instanceof ConfigError)\n throw new DetailedError(error.message, { statusCode: 400, code: 'INVALID_SERVER' })\n throw error\n }\n },\n )\n\n // Registered before `/:id` so the literal segments always win.\n .post(\n '/start-all',\n describeRoute({ tags: ['servers'], summary: 'Start every enabled server', responses: { 200: { description: 'The servers', content: jsonBody(serversResponse) } } }),\n async (c) => {\n await deps.supervisor.startAll()\n return c.json({ servers: deps.supervisor.views() })\n },\n )\n\n .post(\n '/stop-all',\n describeRoute({ tags: ['servers'], summary: 'Stop every server', responses: { 200: { description: 'The servers', content: jsonBody(serversResponse) } } }),\n async (c) => {\n await deps.supervisor.stopAll()\n return c.json({ servers: deps.supervisor.views() })\n },\n )\n\n .get(\n '/:id',\n describeRoute({ tags: ['servers'], summary: 'One server', responses: { 200: { description: 'The server', content: jsonBody(serverResponse) }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n (c) => {\n const { id } = c.req.valid('param')\n const server = deps.supervisor.views().find(entry => entry.id === id)\n if (!server)\n throw unknownServer(id)\n return c.json({ server })\n },\n )\n\n .get(\n '/:id/logs',\n describeRoute({ tags: ['servers'], summary: 'Buffered log lines from memory', responses: { 200: { description: 'Lines' }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n validate('query', logQuerySchema),\n (c) => {\n const { id } = c.req.valid('param')\n if (!deps.store.getServer(id))\n throw unknownServer(id)\n\n const { limit } = c.req.valid('query')\n const parsed = limit === undefined ? Number.NaN : Number.parseInt(limit, 10)\n // Clamped: a negative or huge value must not slice from the wrong end.\n const bounded = Number.isNaN(parsed) ? undefined : Math.min(Math.max(parsed, 1), 100_000)\n return c.json({ lines: deps.supervisor.logLines(id, bounded) })\n },\n )\n\n .get(\n '/:id/stream',\n describeRoute({ tags: ['servers'], summary: 'Server state and logs as server-sent events', responses: { 200: { description: 'text/event-stream' }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n (c) => {\n const { id } = c.req.valid('param')\n if (!deps.store.getServer(id))\n throw unknownServer(id)\n\n return streamSSE(c, async (stream) => {\n let closed = false\n let queue: Promise<void> = Promise.resolve()\n const send = (data: string, event: string): void => {\n if (closed)\n return\n queue = queue.then(() => stream.writeSSE({ event, data })).catch(() => {\n closed = true\n })\n }\n\n const unsubscribe = deps.hub.subscribe(id, (message) => {\n send(JSON.stringify(message), message.type)\n })\n stream.onAbort(() => {\n closed = true\n unsubscribe()\n })\n\n const server = deps.supervisor.views().find(entry => entry.id === id)\n send(JSON.stringify({ type: 'server', ts: Date.now(), serverId: id, server }), 'server')\n send(JSON.stringify({\n type: 'log',\n ts: Date.now(),\n serverId: id,\n lines: deps.supervisor.logLines(id, 200),\n }), 'log')\n\n while (true) {\n await stream.sleep(15000)\n if (closed)\n break\n await stream.writeSSE({ event: 'ping', data: String(Date.now()) })\n }\n })\n },\n )\n\n .post(\n '/:id/start',\n describeRoute({ tags: ['servers'], summary: 'Start a server', responses: { 200: { description: 'Result' }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n async (c) => {\n const result = await deps.supervisor.start(c.req.valid('param').id)\n return c.json(result, statusFor(result))\n },\n )\n\n .post(\n '/:id/stop',\n describeRoute({ tags: ['servers'], summary: 'Stop a server', responses: { 200: { description: 'Result' }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n async (c) => {\n const result = await deps.supervisor.stop(c.req.valid('param').id)\n return c.json(result, result.ok ? 200 : 404)\n },\n )\n\n .post(\n '/:id/restart',\n describeRoute({ tags: ['servers'], summary: 'Restart a server', responses: { 200: { description: 'Result' }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n async (c) => {\n const result = await deps.supervisor.restart(c.req.valid('param').id)\n return c.json(result, statusFor(result))\n },\n )\n\n .post(\n '/:id/clear-logs',\n describeRoute({ tags: ['servers'], summary: 'Forget the buffered log lines', responses: { 200: { description: 'Cleared', content: jsonBody(okResponse) } } }),\n validate('param', idParam),\n (c) => {\n deps.supervisor.clearLogs(c.req.valid('param').id)\n return c.json({ ok: true })\n },\n )\n\n .patch(\n '/:id',\n describeRoute({ tags: ['servers'], summary: 'Edit a server', responses: { 200: { description: 'The server', content: jsonBody(serverResponse) }, 400: ERROR_RESPONSES[400], 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n validate('json', serverPatchSchema),\n (c) => {\n try {\n return c.json({ server: deps.store.updateServer(c.req.valid('param').id, c.req.valid('json')) })\n }\n catch (error) {\n if (error instanceof ConfigError) {\n const status = error.message.startsWith('unknown server') ? 404 : 400\n throw new DetailedError(error.message, { statusCode: status, code: status === 404 ? 'UNKNOWN_SERVER' : 'INVALID_SERVER' })\n }\n throw error\n }\n },\n )\n\n .delete(\n '/:id',\n describeRoute({ tags: ['servers'], summary: 'Stop and remove a server', responses: { 200: { description: 'Removed', content: jsonBody(okResponse) }, 404: ERROR_RESPONSES[404] } }),\n validate('param', idParam),\n async (c) => {\n const { id } = c.req.valid('param')\n try {\n await deps.supervisor.stop(id)\n deps.store.removeServer(id)\n return c.json({ ok: true })\n }\n catch (error) {\n if (error instanceof ConfigError)\n throw unknownServer(id)\n throw error\n }\n },\n )\n}\n","import type { AppDeps } from '#src/app'\nimport type { SettingsPatch } from '#src/shared/contracts'\nimport { Buffer } from 'node:buffer'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { DetailedError } from '@namesmt/utils'\nimport { describeRoute } from 'hono-openapi'\nimport { ConfigError } from '#src/config/store'\nimport { displayHost } from '#src/helpers/bind'\nimport { afterResponse } from '#src/helpers/deferred'\nimport { appFactory } from '#src/helpers/factory'\nimport { logger } from '#src/helpers/logger'\nimport { ERROR_RESPONSES } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { checkExposure } from '#src/services/exposure'\nimport { buildBackupsView, buildControlView } from '#src/services/state'\nimport { settingsPatchSchema } from '#src/shared/contracts'\n\n/** Uploads are buffered in memory by `parseBody`, so they get a hard ceiling. */\nconst MAX_UI_UPLOAD_BYTES = 128 * 1024 * 1024\n\n/** The archive's name is only a label, so it never reaches the filesystem. */\nfunction sanitizeName(name: string): string {\n const cleaned = name.replace(/\\.zip$/i, '').replace(/[^\\w.-]+/g, '-').replace(/^-+|-+$/g, '')\n return cleaned.length > 0 ? cleaned.slice(0, 60) : 'custom-ui'\n}\n\nexport function createSettingsRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get('/settings', c => c.json({\n control: buildControlView(deps.store, deps.auth, deps.controlServer.endpoint, deps.tls),\n defaults: deps.store.defaults,\n logs: deps.store.config.logs,\n notifications: { telegram: deps.notifications.status() },\n host: deps.store.config.host,\n backups: buildBackupsView(deps.store, deps.backups),\n ui: deps.ui.status(),\n }))\n\n .patch('/settings', describeRoute({\n tags: ['panel'],\n summary: 'Edit the panel, server defaults, logs, notifications, host and backups',\n responses: { 200: { description: 'Saved; the listener may be moving' }, 400: ERROR_RESPONSES[400] },\n }), validate('json', settingsPatchSchema), async (c) => {\n const patch: SettingsPatch = c.req.valid('json')\n const current = deps.store.config.control\n\n // Refuse an exposure that is not backed by a password before writing anything.\n const exposure = checkExposure(\n {\n ...current,\n host: patch.control?.host ?? current.host,\n auth: { ...current.auth, enabled: patch.control?.auth?.enabled ?? current.auth.enabled },\n },\n deps.auth.passwordSet,\n deps.auth.usingDefaultPassword,\n )\n if (exposure.blockedReason !== null)\n throw new DetailedError(exposure.blockedReason, { statusCode: 400, code: 'EXPOSURE_BLOCKED' })\n\n const previous = { trustProxy: current.auth.trustProxy, tlsEnabled: current.tls.enabled }\n try {\n if (patch.defaults !== undefined)\n deps.store.updateDefaults(patch.defaults)\n if (patch.logs !== undefined)\n deps.store.updateLogs(patch.logs)\n if (patch.notifications !== undefined)\n deps.store.updateNotifications(patch.notifications)\n if (patch.host !== undefined)\n deps.store.updateHost(patch.host)\n if (patch.backups !== undefined)\n deps.store.updateBackups(patch.backups)\n if (patch.control !== undefined)\n deps.store.updateControl(patch.control)\n }\n catch (error) {\n if (error instanceof ConfigError)\n throw new DetailedError(error.message, { statusCode: 400, code: 'INVALID_SETTINGS' })\n throw error\n }\n\n const next = deps.store.config.control\n const endpointChanged = next.host !== deps.controlServer.endpoint.host || next.port !== deps.controlServer.endpoint.port\n const proxyChanged = next.auth.trustProxy !== previous.trustProxy\n const tlsChanged = next.tls.enabled !== previous.tlsEnabled\n let targetUrl: string | null = null\n\n if (endpointChanged || proxyChanged || tlsChanged) {\n // Moving the listener kills the connection serving this very response, so\n // it happens after the response is written. A failure reverts the config\n // and shows up as `restartRequired` in the next state frame.\n const nextProtocol = tlsChanged ? (next.tls.enabled ? 'https' : 'http') : deps.controlServer.endpoint.protocol\n targetUrl = `${nextProtocol}://${displayHost(next.host)}:${next.port}`\n\n afterResponse(async () => {\n const result = endpointChanged\n ? await deps.controlServer.rebind({ host: next.host, port: next.port })\n : await deps.controlServer.restart()\n\n if (result.ok) {\n logger.info(`control panel listening on ${deps.controlServer.endpoint.url}`)\n return\n }\n\n logger.error(`could not move the control panel: ${result.error ?? 'unknown error'}`)\n deps.store.updateControl({\n host: deps.controlServer.endpoint.host,\n port: deps.controlServer.endpoint.port,\n ...(proxyChanged ? { auth: { trustProxy: previous.trustProxy } } : {}),\n ...(tlsChanged ? { tls: { enabled: previous.tlsEnabled } } : {}),\n })\n }, error => logger.error('control panel move failed', error))\n }\n\n return c.json({\n // `control` describes the listener that is live *right now*; `targetUrl`\n // is where it is about to be, which is what the client should open.\n control: buildControlView(deps.store, deps.auth, deps.controlServer.endpoint, deps.tls),\n defaults: deps.store.defaults,\n logs: deps.store.config.logs,\n notifications: { telegram: deps.notifications.status() },\n host: deps.store.config.host,\n backups: buildBackupsView(deps.store, deps.backups),\n ui: deps.ui.status(),\n rebinding: endpointChanged || proxyChanged || tlsChanged,\n targetUrl,\n })\n })\n\n /**\n * Replace the panel's UI with an uploaded static build. The archive is validated\n * and staged before it is swapped in, so a bad upload changes nothing.\n */\n .post('/settings/ui', describeRoute({\n tags: ['panel'],\n summary: 'Replace the panel UI with an uploaded static build',\n responses: { 200: { description: 'Installed' }, 400: ERROR_RESPONSES[400], 413: { description: 'Too large' } },\n }), async (c) => {\n const declared = Number.parseInt(c.req.header('content-length') ?? '0', 10)\n if (Number.isFinite(declared) && declared > MAX_UI_UPLOAD_BYTES)\n throw new DetailedError(`the upload is larger than ${Math.round(MAX_UI_UPLOAD_BYTES / 1024 / 1024)}MB`, { statusCode: 413, code: 'UPLOAD_TOO_LARGE' })\n\n const body = await c.req.parseBody()\n const file = body.file\n if (!(file instanceof File))\n throw new DetailedError('expected a `file` field with the UI archive', { statusCode: 400, code: 'MISSING_FILE' })\n if (file.size > MAX_UI_UPLOAD_BYTES)\n throw new DetailedError(`the upload is larger than ${Math.round(MAX_UI_UPLOAD_BYTES / 1024 / 1024)}MB`, { statusCode: 413, code: 'UPLOAD_TOO_LARGE' })\n\n const staging = path.join(path.dirname(deps.ui.directory), `.ui-upload-${Date.now()}.zip`)\n try {\n await fs.promises.writeFile(staging, Buffer.from(await file.arrayBuffer()))\n const result = await deps.ui.install(staging, sanitizeName(file.name))\n if (!result.ok)\n throw new DetailedError(result.error, { statusCode: 400, code: 'INVALID_UI' })\n\n logger.info(`UI replaced with ${result.meta.name} (${result.meta.files} files)`)\n return c.json({ ok: true, meta: result.meta, ui: deps.ui.status() })\n }\n finally {\n fs.rmSync(staging, { force: true })\n }\n })\n\n /** Back to the stock UI. */\n .delete('/settings/ui', describeRoute({\n tags: ['panel'],\n summary: 'Go back to the stock UI',\n responses: { 200: { description: 'Reverted' } },\n }), (c) => {\n const removed = deps.ui.revert()\n logger.info(removed ? 'custom UI removed — the stock panel is back' : 'no custom UI was installed')\n return c.json({ ok: true, removed, ui: deps.ui.status() })\n })\n}\n","import type { AppDeps } from '#src/app'\nimport { describeRoute } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\nimport { jsonBody } from '#src/helpers/openapi'\nimport { appStateSchema } from '#src/shared/contracts'\n\n/** The whole panel in one payload: config, live server state and host vitals. */\nexport function createStateRoute(deps: AppDeps) {\n return appFactory.createApp()\n .get(\n '/state',\n describeRoute({\n tags: ['panel'],\n summary: 'Full snapshot of the panel',\n responses: { 200: { description: 'The snapshot', content: jsonBody(appStateSchema) } },\n }),\n c => c.json(deps.supervisor.getState()),\n )\n}\n","import type { Context } from 'hono'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { Hono } from 'hono'\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain; charset=utf-8',\n}\n\nexport interface StaticRouteOptions {\n /** Resolved per request, so a UI installed at runtime takes effect on refresh. */\n dir: string | (() => string)\n entry?: string\n}\n\n/**\n * Serves the built control UI and falls back to `index.html` for client routes,\n * so a deep URL like `/servers/static` works after a refresh.\n */\nexport function createStaticRoute(options: StaticRouteOptions): Hono {\n const route = new Hono()\n const entry = options.entry ?? 'index.html'\n const currentRoot = (): string => path.resolve(typeof options.dir === 'function' ? options.dir() : options.dir)\n\n route.get('*', async (c) => {\n const root = currentRoot()\n const pathname = safeDecode(new URL(c.req.url).pathname)\n if (pathname === null)\n return c.text('bad path', 400)\n\n const file = resolveWithin(root, pathname)\n if (file !== null) {\n const response = await serveFile(c, file, pathname)\n if (response !== null)\n return response\n }\n\n const indexFile = path.join(root, entry)\n if (fs.existsSync(indexFile)) {\n const response = await serveFile(c, indexFile, '/')\n if (response !== null)\n return response\n }\n\n return c.text('no UI is installed — build one and upload it under Settings → Interface', 503)\n })\n\n return route\n}\n\nfunction safeDecode(value: string): string | null {\n try {\n return decodeURIComponent(value)\n }\n catch {\n return null\n }\n}\n\nfunction resolveWithin(root: string, pathname: string): string | null {\n const resolved = path.resolve(root, `.${pathname}`)\n if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`))\n return null\n return resolved\n}\n\nasync function serveFile(c: Context, file: string, pathname: string): Promise<Response | null> {\n let stats: fs.Stats\n try {\n stats = await fs.promises.stat(file)\n }\n catch {\n return null\n }\n if (!stats.isFile())\n return null\n\n const body = await fs.promises.readFile(file)\n const ext = path.extname(file).toLowerCase()\n const immutable = pathname.startsWith('/assets/')\n const payload = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer\n\n return c.body(payload, 200, {\n 'Content-Type': CONTENT_TYPES[ext] ?? 'application/octet-stream',\n 'Content-Length': String(stats.size),\n 'Cache-Control': immutable ? 'public, max-age=31536000, immutable' : 'no-cache',\n })\n}\n","import type { AppDeps } from '#src/app'\nimport { DetailedError } from '@namesmt/utils'\nimport { describeRoute } from 'hono-openapi'\nimport { afterResponse } from '#src/helpers/deferred'\nimport { appFactory } from '#src/helpers/factory'\nimport { logger } from '#src/helpers/logger'\nimport { ERROR_RESPONSES } from '#src/helpers/openapi'\nimport { validate } from '#src/helpers/validator'\nimport { buildControlView } from '#src/services/state'\nimport { tlsUploadSchema } from '#src/shared/contracts'\n\n/**\n * Uploads the PEM pair used for https on the control panel.\n *\n * When TLS is already enabled the listener has to be rebuilt with the new pair,\n * which kills the connection serving this request — so the swap is deferred, and\n * the response tells the client where the panel will be.\n */\nexport function createTlsRoute(deps: AppDeps) {\n const review = () => ({\n control: buildControlView(deps.store, deps.auth, deps.controlServer.endpoint, deps.tls),\n rebinding: deps.store.config.control.tls.enabled,\n targetUrl: deps.controlServer.endpoint.url,\n })\n\n return appFactory.createApp()\n .post(\n '/settings/tls',\n describeRoute({\n tags: ['tls'],\n summary: 'Upload the certificate and key the panel should serve',\n responses: { 200: { description: 'Stored; the panel may be moving to https' }, 400: ERROR_RESPONSES[400] },\n }),\n validate('json', tlsUploadSchema),\n (c) => {\n const body = c.req.valid('json')\n const saved = deps.tls.save(body.certificate, body.privateKey)\n if (!saved.ok)\n throw new DetailedError(saved.error ?? 'the certificate pair was rejected', { statusCode: 400, code: 'INVALID_CERTIFICATE' })\n\n if (deps.store.config.control.tls.enabled) {\n afterResponse(async () => {\n const result = await deps.controlServer.restart()\n if (!result.ok)\n logger.error(`could not reload TLS: ${result.error ?? 'unknown error'}`)\n else logger.info(`control panel listening on ${deps.controlServer.endpoint.url} (https)`)\n }, error => logger.error('tls reload failed', error))\n }\n\n return c.json(review())\n },\n )\n\n .delete(\n '/settings/tls',\n describeRoute({\n tags: ['tls'],\n summary: 'Remove the certificate pair',\n responses: { 200: { description: 'Removed' } },\n }),\n (c) => {\n deps.tls.clear()\n if (deps.store.config.control.tls.enabled) {\n afterResponse(async () => {\n await deps.controlServer.restart()\n }, error => logger.error('tls reload failed', error))\n }\n return c.json(review())\n },\n )\n}\n","import type { DetailedError } from '@namesmt/utils'\nimport type { ErrorHandler as HonoErrorHandler } from 'hono'\nimport type { ContentfulStatusCode } from 'hono/utils/http-status'\nimport { HTTPException } from 'hono/http-exception'\nimport { logger } from '#src/helpers/logger'\n\n/**\n * The one error envelope this API speaks:\n *\n * ```json\n * { \"message\": \"human readable\", \"code\": \"MACHINE_READABLE\", \"detail\": … }\n * ```\n *\n * `@namesmt/utils`' `DetailedError` is the preferred way to fail — it carries the\n * status, a stable code and structured detail — so a client (and the OpenAPI\n * schema) can rely on the shape.\n */\nexport interface ApiErrorBody {\n message: string\n code: string\n detail?: unknown\n}\n\nexport const errorHandler: HonoErrorHandler = (error, c) => {\n const body = toErrorBody(error)\n const status = statusOf(error)\n\n if (status >= 500)\n logger.error(`${c.req.method} ${new URL(c.req.url).pathname} failed:`, error)\n else\n logger.debug(`${c.req.method} ${new URL(c.req.url).pathname} → ${status} ${body.message}`)\n\n return c.json(body, status)\n}\n\nfunction toErrorBody(error: unknown): ApiErrorBody {\n if (error instanceof HTTPException)\n return { message: error.message, code: 'HTTP_EXCEPTION' }\n\n // `DetailedError` can come from this code or from Hono's own parsing helpers,\n // so a name check is safer than `instanceof` across module instances.\n if (isDetailedError(error)) {\n return {\n message: error.message,\n code: error.code ?? 'DETAILED_ERROR',\n ...(error.detail === undefined ? {} : { detail: error.detail }),\n }\n }\n\n if (error instanceof Error)\n return { message: error.message, code: error.name === 'Error' ? 'INTERNAL_ERROR' : error.name.toUpperCase() }\n\n return { message: String(error), code: 'INTERNAL_ERROR' }\n}\n\nfunction isDetailedError(error: unknown): error is DetailedError {\n return error instanceof Error && error.name === 'DetailedError' && 'statusCode' in error\n}\n\nfunction statusOf(error: unknown): ContentfulStatusCode {\n const candidate = (error as { statusCode?: unknown, status?: unknown })?.statusCode ?? (error as { status?: unknown })?.status\n const status = typeof candidate === 'number' ? candidate : 500\n return status >= 400 && status <= 599 ? (status as ContentfulStatusCode) : 500\n}\n","import fs from 'node:fs'\nimport { Scalar } from '@scalar/hono-api-reference'\nimport { openAPIRouteHandler } from 'hono-openapi'\nimport { appFactory } from '#src/helpers/factory'\n\nconst PREFIX = '/openapi'\n\n/**\n * The machine-readable contract, generated from the same ArkType schemas the\n * routes validate with — one source of truth, no second set of DTOs to drift.\n * `/openapi/ui` is a browsable reference (Scalar) and needs no session, since a\n * UI author has to be able to read it before they can log in.\n */\n/** The version the package was built with, so the spec never drifts from it. */\nfunction packageVersion(): string {\n try {\n const manifest = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { version?: string }\n return manifest.version ?? '0.0.0'\n }\n catch {\n return '0.0.0'\n }\n}\n\nexport function setupOpenAPI(app: Parameters<typeof openAPIRouteHandler>[0]) {\n return appFactory.createApp()\n .get(\n `${PREFIX}/spec.json`,\n openAPIRouteHandler(app, {\n documentation: {\n info: {\n title: 'home-hosted',\n version: packageVersion(),\n description: 'Control plane for the processes you host at home: servers, logs, vitals, backups and settings.',\n },\n tags: [\n { name: 'panel', description: 'Snapshot, health and the local shutdown channel' },\n { name: 'servers', description: 'The processes being supervised' },\n { name: 'logs', description: 'Live and persisted logs' },\n { name: 'backups', description: 'Archives of config, secrets, TLS and data paths' },\n { name: 'auth', description: 'Sessions and the panel password' },\n { name: 'notifications', description: 'Telegram delivery' },\n { name: 'tls', description: 'The panel certificate' },\n ],\n },\n }),\n )\n .get(\n `${PREFIX}/ui`,\n Scalar({ theme: 'deepSpace', url: `${PREFIX}/spec.json` }),\n )\n}\n","import type { SecretsStore } from '#src/config/secrets'\nimport type { ConfigStore } from '#src/config/store'\nimport type { AuthService } from '#src/services/auth'\nimport type { BackupService } from '#src/services/backups'\nimport type { ControlServer } from '#src/services/control-server'\nimport type { EventHub } from '#src/services/events'\nimport type { LogFiles } from '#src/services/log-files'\nimport type { NotificationService } from '#src/services/notifications'\nimport type { Supervisor } from '#src/services/supervisor'\nimport type { TlsStore } from '#src/services/tls'\nimport type { UiService } from '#src/services/ui'\nimport { createAuthRoute } from '#src/api/auth/$.routes'\nimport { createBackupsRoute } from '#src/api/backups'\nimport { createControlRoute } from '#src/api/control'\nimport { createEventsRoute } from '#src/api/events'\nimport { createHealthRoute } from '#src/api/health'\nimport { createLogsRoute } from '#src/api/logs'\nimport { createMetricsRoute } from '#src/api/metrics'\nimport { createNotificationsRoute } from '#src/api/notifications'\nimport { createServersRoute } from '#src/api/servers/$.routes'\nimport { createSettingsRoute } from '#src/api/settings'\nimport { createStateRoute } from '#src/api/state'\nimport { createStaticRoute } from '#src/api/static'\nimport { createTlsRoute } from '#src/api/tls'\nimport { errorHandler } from '#src/helpers/error'\nimport { appFactory } from '#src/helpers/factory'\nimport { logger } from '#src/helpers/logger'\nimport { createAuthGuard } from '#src/middleware/auth'\nimport { setupOpenAPI } from '#src/openapi'\n\nexport interface AppDeps {\n store: ConfigStore\n supervisor: Supervisor\n hub: EventHub\n auth: AuthService\n secrets: SecretsStore\n controlServer: ControlServer\n tls: TlsStore\n logFiles: LogFiles\n notifications: NotificationService\n backups: BackupService\n ui: UiService\n /** Token for the local `down` command, and the graceful stop it asks for. */\n runtimeToken: string\n onShutdown: () => Promise<void>\n}\n\n/**\n * The root app: middleware and sub-routes only, never a handler of its own.\n *\n * The whole thing is one chained expression on purpose — that is what keeps the\n * route map in the type, which `AppType` hands to `hc<AppType>` clients and to\n * `setupOpenAPI`.\n */\nexport function createRootApp(deps: AppDeps) {\n const app = appFactory.createApp()\n .use('*', async (c, next) => {\n const started = Date.now()\n await next()\n logger.debug(`${c.req.method} ${new URL(c.req.url).pathname} ${c.res.status} ${Date.now() - started}ms`)\n })\n\n .onError(errorHandler)\n\n // Outside `/api`, so a local `down` needs no session — but it needs the token\n // from `run.json` and a loopback peer. Registered before the guard by design.\n .route('/_hh', createControlRoute(deps))\n\n .use('/api/*', createAuthGuard({ auth: deps.auth }))\n\n .route('/api', createAuthRoute(deps))\n .route('/api', createStateRoute(deps))\n .route('/api', createEventsRoute(deps))\n .route('/api', createSettingsRoute(deps))\n .route('/api', createTlsRoute(deps))\n .route('/api', createLogsRoute(deps))\n .route('/api', createNotificationsRoute(deps))\n .route('/api', createMetricsRoute(deps))\n .route('/api', createBackupsRoute(deps))\n .route('/api/servers', createServersRoute(deps))\n\n .route('/', createHealthRoute(deps))\n\n // The spec is generated from the finished route table (and must be routed\n // *before* the static catch-all, which would otherwise swallow it).\n const documented = app.route('/', setupOpenAPI(app))\n return documented.route('/', createStaticRoute({ dir: () => deps.ui.resolveDir() }))\n}\n\n/** What a typed client (`hc<AppType>`) and the OpenAPI document are built from. */\nexport type AppType = ReturnType<typeof createRootApp>\n","import { randomBytes } from 'node:crypto'\nimport fs from 'node:fs'\nimport http from 'node:http'\nimport https from 'node:https'\nimport process from 'node:process'\nimport { type } from 'arktype'\nimport { writeFileAtomic } from '#src/helpers/atomic'\nimport { runtimePath } from '#src/helpers/paths'\n\n/**\n * `run.json` is how `status`/`down` find the live control plane and how they are\n * allowed to stop it without a password: the file is mode 0600, and the token in\n * it is what `POST /_hh/shutdown` checks.\n */\nexport const runtimeSchema = type({\n version: 'string',\n pid: 'number.integer >= 1',\n /** The address that was actually bound, for humans. */\n url: 'string',\n /** Always a reachable loopback address, for probes (`lan` binds to 0.0.0.0). */\n probeUrl: 'string',\n protocol: 'string',\n port: '1 <= number.integer <= 65535',\n bindHost: 'string',\n startedAt: 'number',\n projectDir: 'string',\n dataRoot: 'string',\n configPath: 'string',\n logFile: 'string',\n token: 'string >= 1',\n}).onUndeclaredKey('reject')\nexport type Runtime = typeof runtimeSchema.infer\n\nexport function readRuntime(): Runtime | null {\n try {\n const parsed = runtimeSchema(JSON.parse(fs.readFileSync(runtimePath, 'utf8')))\n return parsed instanceof type.errors ? null : parsed\n }\n catch {\n return null\n }\n}\n\nexport function writeRuntime(runtime: Runtime): void {\n writeFileAtomic(runtimePath, `${JSON.stringify(runtime, null, 2)}\\n`, { mode: 0o600 })\n}\n\nexport function clearRuntime(): void {\n fs.rmSync(runtimePath, { force: true })\n}\n\nexport function newToken(): string {\n return randomBytes(32).toString('base64url')\n}\n\n/** Signal 0 only probes the pid; `EPERM` still means the process is there. */\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n }\n catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\nexport interface RuntimeProbe {\n /** The panel answered on its own port — stronger than \"the pid exists\". */\n reachable: boolean\n /** It answered, but reports a crashed autostart server (`/healthz` is 503). */\n degraded: boolean\n}\n\n/** A degraded panel is still answering: a 503 must not read as \"not running\". */\nexport async function probeRuntime(runtime: Runtime, timeoutMs = 2500): Promise<RuntimeProbe> {\n const status = await localRequest(runtime, '/healthz', 'GET', undefined, timeoutMs)\n return { reachable: status !== null, degraded: status === 503 }\n}\n\n/**\n * Asks the daemon to stop through its own endpoint, so supervised servers are\n * shut down cleanly on every platform (a bare signal is not graceful on Windows).\n */\nexport async function requestShutdown(runtime: Runtime, timeoutMs = 4000): Promise<boolean> {\n const status = await localRequest(runtime, '/_hh/shutdown', 'POST', runtime.token, timeoutMs)\n return status !== null && status >= 200 && status < 300\n}\n\n/**\n * Talks to the panel over loopback. Node's `fetch` cannot be told to accept the\n * self-signed certificate an uploaded TLS pair usually is, which would break\n * `status` and the graceful `down` — so this speaks http/https directly.\n */\nfunction localRequest(runtime: Runtime, path: string, method: 'GET' | 'POST', token: string | undefined, timeoutMs: number): Promise<number | null> {\n return new Promise((resolve) => {\n const url = new URL(`${runtime.probeUrl}${path}`)\n const secure = url.protocol === 'https:'\n const request = (secure ? https : http).request({\n hostname: url.hostname,\n port: url.port,\n path: url.pathname,\n method,\n // Only ever pointed at our own listener on this machine.\n ...(secure ? { rejectUnauthorized: false } : {}),\n headers: token === undefined ? {} : { 'x-home-hosted-token': token },\n timeout: timeoutMs,\n }, (response) => {\n response.resume()\n response.once('end', () => resolve(response.statusCode ?? null))\n })\n\n request.once('error', () => resolve(null))\n request.once('timeout', () => {\n request.destroy()\n resolve(null)\n })\n request.end()\n })\n}\n","import { exec } from 'node:child_process'\nimport process from 'node:process'\n\n/** Best-effort browser launch; a headless host simply logs instead. */\nexport function openBrowser(url: string): void {\n const command = process.platform === 'darwin'\n ? `open \"${url}\"`\n : process.platform === 'win32'\n ? `start \"\" \"${url}\"`\n : `xdg-open \"${url}\"`\n\n exec(command, { windowsHide: true }, () => {\n // No display / no handler: the URL is already printed, so this is not an error.\n })\n}\n","export type TemplateVars = Record<string, string | number>\n\n/**\n * Replaces `{name}` placeholders. Unknown placeholders are left untouched so a\n * typo surfaces in the child's args instead of silently becoming an empty string.\n */\nexport function resolveTemplate(value: string, vars: TemplateVars): string {\n return value.replace(/(?<!\\$)\\{([a-z][\\w-]*)\\}/gi, (match, name: string) => {\n const replacement = vars[name]\n return replacement === undefined ? match : String(replacement)\n })\n}\n\nexport function resolveTemplates<T extends string | string[]>(value: T, vars: TemplateVars): T {\n if (Array.isArray(value))\n return value.map(entry => resolveTemplate(entry, vars)) as T\n return resolveTemplate(value as string, vars) as T\n}\n\nexport function resolveRecord(record: Record<string, string>, vars: TemplateVars): Record<string, string> {\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [key, resolveTemplate(value, vars)]),\n )\n}\n","import { execFile } from 'node:child_process'\nimport net from 'node:net'\nimport process from 'node:process'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\n/** True when something accepts TCP connections on host:port. */\nexport function probePort(host: string, port: number, timeoutMs = 1500): Promise<boolean> {\n return new Promise((resolve) => {\n const socket = net.connect({ host, port })\n const done = (result: boolean): void => {\n socket.removeAllListeners()\n socket.destroy()\n resolve(result)\n }\n socket.setTimeout(timeoutMs)\n socket.once('connect', () => done(true))\n socket.once('timeout', () => done(false))\n socket.once('error', () => done(false))\n })\n}\n\n/** A port is free when nothing is listening on it (loopback is enough to detect conflicts). */\nexport async function isPortFree(port: number, host = '127.0.0.1', timeoutMs = 1000): Promise<boolean> {\n return !(await probePort(host, port, timeoutMs))\n}\n\n/**\n * Kills whatever holds the port. Only used as a last resort for wrappers that\n * spawn their real server detached, where a process-group signal cannot reach it.\n */\nexport async function killPortHolders(port: number): Promise<number[]> {\n const pids = await listPortHolders(port)\n for (const pid of pids) {\n try {\n process.kill(pid, 'SIGKILL')\n }\n catch {\n // already gone\n }\n }\n return pids\n}\n\n/** `netstat -ano` lines: ` TCP 127.0.0.1:4010 0.0.0.0:0 LISTENING 1234` */\nexport function parseNetstatListeners(output: string, port: number): number[] {\n const pids = new Set<number>()\n\n for (const line of output.split(/\\r?\\n/)) {\n const cells = line.trim().split(/\\s+/)\n if (cells.length < 5)\n continue\n const local = cells[1] ?? ''\n const state = cells[3] ?? ''\n const pid = Number.parseInt(cells[4] ?? '', 10)\n const localPort = Number.parseInt(local.slice(local.lastIndexOf(':') + 1), 10)\n if (state.toUpperCase() !== 'LISTENING' || localPort !== port)\n continue\n if (Number.isInteger(pid) && pid > 0 && pid !== process.pid)\n pids.add(pid)\n }\n\n return [...pids]\n}\n\nexport async function listPortHolders(port: number): Promise<number[]> {\n if (process.platform === 'win32') {\n try {\n const { stdout } = await execFileAsync('netstat', ['-ano', '-p', 'tcp'], { timeout: 5000 })\n return parseNetstatListeners(stdout, port)\n }\n catch {\n return []\n }\n }\n\n try {\n const { stdout } = await execFileAsync('lsof', ['-ti', `tcp:${port}`, '-sTCP:LISTEN'], { timeout: 3000 })\n return parsePids(stdout)\n }\n catch {\n // lsof missing or nothing listening\n }\n\n try {\n const { stdout } = await execFileAsync('fuser', [`${port}/tcp`], { timeout: 3000 })\n return parsePids(stdout)\n }\n catch {\n return []\n }\n}\n\nfunction parsePids(stdout: string): number[] {\n return [...new Set(\n stdout.split(/\\s+/)\n .map(entry => Number.parseInt(entry, 10))\n .filter(pid => Number.isInteger(pid) && pid > 0 && pid !== process.pid),\n )]\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\n/** `KEY=value` files, with optional `export `, `#` comments and quoted values. */\nexport function parseEnvFile(text: string): Record<string, string> {\n const env: Record<string, string> = {}\n\n for (const raw of text.split('\\n')) {\n const line = raw.trim()\n if (line.length === 0 || line.startsWith('#'))\n continue\n\n const assignment = line.startsWith('export ') ? line.slice(7) : line\n const separator = assignment.indexOf('=')\n if (separator <= 0)\n continue\n\n const key = assignment.slice(0, separator).trim()\n let value = assignment.slice(separator + 1).trim()\n if (value.length > 1 && ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith('\\'') && value.endsWith('\\'')))) {\n value = value.slice(1, -1)\n }\n env[key] = value\n }\n\n return env\n}\n\n/** Missing files are not an error: an env file is an optional override layer. */\nexport function loadEnvFile(file: string): { env: Record<string, string>, path: string, error: string | null } {\n try {\n return { env: parseEnvFile(fs.readFileSync(file, 'utf8')), path: file, error: null }\n }\n catch (error) {\n const code = (error as NodeJS.ErrnoException).code\n if (code === 'ENOENT')\n return { env: {}, path: file, error: null }\n return { env: {}, path: file, error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nconst VARIABLE = /\\$\\{([A-Z_]\\w*)\\}/gi\n\n/** Expands `${VAR}` from the given vars; unknown references are left visible. */\nexport function expandEnv(value: string, vars: Record<string, string | undefined>): string {\n return value.replace(VARIABLE, (match, name: string) => vars[name] ?? match)\n}\n\nexport function expandEnvRecord(record: Record<string, string>, vars: Record<string, string | undefined>): Record<string, string> {\n return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, expandEnv(value, vars)]))\n}\n\nexport function expandEnvList(values: string[], vars: Record<string, string | undefined>): string[] {\n return values.map(value => expandEnv(value, vars))\n}\n\nexport function resolveEnvFilePath(file: string, cwd: string): string {\n if (path.isAbsolute(file))\n return file\n return path.resolve(cwd, file)\n}\n","import type { FileEntry } from '@zip.js/zip.js'\nimport { Buffer } from 'node:buffer'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { Readable, Writable } from 'node:stream'\nimport { finished } from 'node:stream/promises'\nimport { BlobReader, configure, ZipReader, ZipWriter } from '@zip.js/zip.js'\n\n/**\n * `Readable.toWeb` is typed against `node:stream/web`, while zip.js declares the\n * global `ReadableStream` — the same objects under two declarations, and which\n * one wins depends on the tsconfig (the SPA's adds lib.dom). These aliases take\n * the type zip.js expects, whichever it is where this file is compiled.\n */\ntype ZipInput = Parameters<ZipWriter<unknown>['add']>[1]\ntype ZipOutput = ConstructorParameters<typeof ZipWriter>[0] & { abort: (reason?: unknown) => Promise<void> }\ntype ZipDataOutput = Parameters<FileEntry['getData']>[0]\n\n/**\n * Backups are ordinary zip files: the same container whether or not they are\n * password-protected, openable by any archive manager (including the one built\n * into Windows and macOS), and produced without a native binary — zip.js is pure\n * JavaScript.\n *\n * A password means WinZip AES-256 (`encryptionStrength: 3`, AE-2): strong, and\n * still standard, unlike the legacy ZipCrypto encryption.\n */\n\n// Deterministic, in-process codecs: a bundled CLI has no worker file to load.\nconfigure({ useWebWorkers: false })\n\nconst ZIPS = [\n [0x50, 0x4B, 0x03, 0x04],\n [0x50, 0x4B, 0x05, 0x06],\n [0x50, 0x4B, 0x07, 0x08],\n]\n\n/** Recognised by content, never by the file's name. */\nexport function isZipArchive(file: string): boolean {\n let fd: number | null = null\n try {\n fd = fs.openSync(file, 'r')\n const head = Buffer.alloc(4)\n const read = fs.readSync(fd, head, 0, 4, 0)\n return read === 4 && ZIPS.some(magic => magic.every((byte, index) => head[index] === byte))\n }\n catch {\n return false\n }\n finally {\n if (fd !== null)\n fs.closeSync(fd)\n }\n}\n\nexport interface ArchiveEntry {\n /** Forward-slash path inside the archive; directories end with `/`. */\n name: string\n directory: boolean\n encrypted: boolean\n symlink: boolean\n /** Uncompressed size, for callers that cap what they will extract. */\n size: number\n}\n\n/** The central directory is not encrypted, so this works without a password. */\nexport async function listZip(file: string): Promise<ArchiveEntry[]> {\n const reader = await open(file)\n try {\n const entries = await reader.getEntries()\n return entries.map(entry => ({\n name: entry.filename,\n directory: entry.directory === true,\n encrypted: entry.encrypted === true,\n symlink: entry.symlink === true,\n size: entry.uncompressedSize ?? 0,\n }))\n }\n finally {\n await reader.close()\n }\n}\n\n/** True when the archive rejected the password we used. */\nexport function isInvalidPassword(error: unknown): boolean {\n return error instanceof Error && /password/i.test(error.message)\n}\n\n/**\n * Writes the contents of `sourceDir` into `destination`, preserving the tree.\n * Symlinks are followed, so a link to a directory is captured as a directory and\n * a link loop cannot recurse forever. Streams from disk to disk: nothing is\n * buffered whole.\n */\nexport async function createZip(sourceDir: string, destination: string, options: { password?: string } = {}): Promise<void> {\n const output = fs.createWriteStream(destination, { mode: 0o600 })\n // Attached up front: the fd closes as part of the web stream ending, so a\n // listener added afterwards would wait forever.\n const flushed = finished(output)\n const writer = Writable.toWeb(output) as unknown as ZipOutput\n const zip = new ZipWriter(writer, {\n ...(options.password === undefined ? {} : { password: options.password, encryptionStrength: 3 as const }),\n level: 6,\n keepOrder: true,\n })\n\n try {\n for (const item of walk(sourceDir)) {\n if (item.directory)\n await zip.add(item.name, null, { directory: true })\n else if (item.size === 0)\n // An empty file carries no content to protect, and leaving it as a plain\n // AE-2 entry makes older tools (p7zip 16.02) report a CRC failure on it.\n await zip.add(item.name, null, { directory: false })\n else\n await zip.add(item.name, Readable.toWeb(fs.createReadStream(item.absolute)) as unknown as ZipInput)\n }\n await zip.close()\n await flushed\n }\n catch (error) {\n await writer.abort(error).catch(() => {})\n // The file stream also fails here (a full disk, a directory in the way), and\n // an unobserved rejection would take the whole control plane down.\n await flushed.catch(() => {})\n throw error\n }\n}\n\n/**\n * Extracts the given entries (already validated by the caller) into\n * `destination`. Symbolic links are never recreated — an archive is not allowed\n * to make the filesystem point somewhere else.\n */\nexport async function extractZip(\n file: string,\n destination: string,\n options: { names: string[], password?: string },\n): Promise<{ skipped: string[] }> {\n const reader = await open(file, options.password)\n const skipped: string[] = []\n\n try {\n const entries = new Map((await reader.getEntries()).map(entry => [entry.filename, entry]))\n\n for (const name of options.names) {\n const entry = entries.get(name)\n if (entry === undefined) {\n skipped.push(name)\n continue\n }\n\n const target = path.join(destination, name)\n if (entry.directory) {\n fs.mkdirSync(target, { recursive: true })\n continue\n }\n if (entry.symlink) {\n skipped.push(name)\n continue\n }\n\n fs.mkdirSync(path.dirname(target), { recursive: true })\n await entry.getData(Writable.toWeb(fs.createWriteStream(target)) as unknown as ZipDataOutput, writeOptions(entry, options.password))\n }\n }\n finally {\n await reader.close()\n }\n\n return { skipped }\n}\n\nfunction writeOptions(entry: FileEntry, password: string | undefined): { password?: string } {\n return entry.encrypted && password !== undefined ? { password } : {}\n}\n\nasync function open(file: string, password?: string): Promise<ZipReader<unknown>> {\n // A lazily-read Blob keeps a multi-gigabyte archive out of memory: zip.js only\n // pulls the byte ranges it needs.\n const blob = await fs.openAsBlob(file, { type: 'application/zip' })\n return password === undefined\n ? new ZipReader(new BlobReader(blob))\n : new ZipReader(new BlobReader(blob), { password })\n}\n\ninterface WalkedFile {\n name: string\n absolute: string\n directory: boolean\n size: number\n}\n\n/** Sorted, deterministic walk with symlinks resolved and directory loops broken. */\nfunction walk(root: string): WalkedFile[] {\n const files: WalkedFile[] = []\n const seen = new Set<string>()\n\n const visit = (absolute: string, name: string): void => {\n let stats: fs.Stats\n try {\n stats = fs.statSync(absolute)\n }\n catch {\n return\n }\n\n if (stats.isDirectory()) {\n const real = fs.realpathSync(absolute)\n if (seen.has(real))\n return\n seen.add(real)\n files.push({ name: `${name}/`, absolute, directory: true, size: 0 })\n for (const child of fs.readdirSync(absolute).sort())\n visit(path.join(absolute, child), `${name}/${child}`)\n return\n }\n\n if (stats.isFile())\n files.push({ name, absolute, directory: false, size: stats.size })\n }\n\n for (const child of fs.readdirSync(root).sort())\n visit(path.join(root, child), child)\n\n return files\n}\n","export interface BackoffOptions {\n baseDelayMs: number\n factor: number\n maxDelayMs: number\n}\n\n/** Exponential backoff: base * factor^(attempt - 1), capped at maxDelayMs. */\nexport function computeBackoff(attempt: number, options: BackoffOptions): number {\n const normalized = Math.max(1, Math.floor(attempt))\n const raw = options.baseDelayMs * options.factor ** (normalized - 1)\n if (!Number.isFinite(raw))\n return options.maxDelayMs\n return Math.min(Math.max(0, raw), options.maxDelayMs)\n}\n","import type { HttpCheckConfig } from '#src/shared/contracts'\nimport { probePort } from '#src/providers/port'\n\nexport interface HealthProbeResult {\n healthy: boolean\n ms: number\n detail: string\n}\n\n/** TCP connect timing, used for the default `port` mode and readiness. */\nexport async function probeTcp(host: string, port: number, timeoutMs: number): Promise<HealthProbeResult> {\n const started = Date.now()\n const accepting = await probePort(host, port, timeoutMs)\n const ms = Date.now() - started\n return { healthy: accepting, ms, detail: accepting ? 'port accepted a connection' : 'port did not accept a connection' }\n}\n\nexport interface HttpProbeOptions extends Pick<HttpCheckConfig, 'path' | 'method' | 'expectBody' | 'expectStatusBelow'> {\n /** `null` counts as \"not set\", so a value saved by the UI can be cleared again. */\n expectStatus?: number | null\n timeoutMs: number\n}\n\n/**\n * Fetches the configured path and asserts the response, so \"listening\" is not\n * mistaken for \"working\".\n */\nexport async function probeHttp(host: string, port: number, options: HttpProbeOptions): Promise<HealthProbeResult> {\n const url = `http://${host}:${port}${options.path.startsWith('/') ? options.path : `/${options.path}`}`\n const started = Date.now()\n\n try {\n const response = await fetch(url, {\n method: options.method,\n redirect: 'manual',\n signal: AbortSignal.timeout(options.timeoutMs),\n })\n const ms = Date.now() - started\n\n const expected = options.expectStatus ?? null\n if (expected !== null && response.status !== expected) {\n return { healthy: false, ms, detail: `expected status ${expected}, got ${response.status}` }\n }\n if (expected === null && response.status >= options.expectStatusBelow) {\n return { healthy: false, ms, detail: `status ${response.status} is >= ${options.expectStatusBelow}` }\n }\n\n if (options.expectBody.length > 0 && options.method !== 'HEAD') {\n const body = await response.text()\n if (!body.includes(options.expectBody)) {\n return { healthy: false, ms, detail: `body does not contain ${JSON.stringify(options.expectBody)}` }\n }\n }\n\n return { healthy: true, ms, detail: `HTTP ${response.status}` }\n }\n catch (error) {\n const ms = Date.now() - started\n const reason = error instanceof Error ? error.message : String(error)\n return { healthy: false, ms, detail: `request failed: ${reason}` }\n }\n}\n\nexport async function probeHealth(options: {\n mode: 'port' | 'http'\n hosts: string[]\n port: number\n timeoutMs: number\n http: Pick<HttpCheckConfig, 'path' | 'method' | 'expectBody' | 'expectStatusBelow'> & { expectStatus?: number | null }\n}): Promise<HealthProbeResult> {\n let last: HealthProbeResult = { healthy: false, ms: 0, detail: 'not probed' }\n\n // Try each candidate host in order (loopback first), so a server bound to one\n // specific address is still probed somewhere it actually listens.\n for (const host of options.hosts) {\n const result = options.mode === 'http'\n ? await probeHttp(host, options.port, { ...options.http, timeoutMs: options.timeoutMs })\n : await probeTcp(host, options.port, options.timeoutMs)\n if (result.healthy)\n return result\n last = result\n }\n\n return last\n}\n","import type { ProcessResources } from '#src/shared/contracts'\nimport { execFile } from 'node:child_process'\nimport fs from 'node:fs'\nimport process from 'node:process'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\ninterface ProcRow {\n pid: number\n ppid: number\n rssKb: number\n /** Cumulative CPU seconds, when the platform reports it (Linux, Windows). */\n cpuSeconds?: number\n /** Instantaneous/decaying CPU percent, when the platform reports it (macOS). */\n cpuPercent?: number\n}\n\nexport function parsePsOutput(text: string): ProcRow[] {\n const rows: ProcRow[] = []\n for (const line of text.split('\\n')) {\n const parts = line.trim().split(/\\s+/)\n if (parts.length < 4)\n continue\n const [pid, ppid, rss, cpu] = parts.map(entry => Number.parseFloat(entry))\n if (pid === undefined || ppid === undefined || rss === undefined || !Number.isFinite(pid))\n continue\n rows.push({ pid, ppid, rssKb: rss, cpuPercent: Number.isFinite(cpu) ? cpu : undefined })\n }\n return rows\n}\n\n/** CSV from `Get-CimInstance ... | ConvertTo-Csv`, or wmic's `/format:csv`. */\nexport function parseWindowsCsv(text: string): ProcRow[] {\n const rows: ProcRow[] = []\n const lines = text.split(/\\r?\\n/).filter(line => line.trim().length > 0)\n const header = lines.shift()\n if (header === undefined)\n return rows\n\n const columns = header.split(',').map(entry => entry.replace(/\"/g, '').trim().toLowerCase())\n const index = (name: string): number => columns.indexOf(name.toLowerCase())\n const pidAt = index('ProcessId')\n const ppidAt = index('ParentProcessId')\n const rssAt = index('WorkingSetSize')\n const kernelAt = index('KernelModeTime')\n const userAt = index('UserModeTime')\n\n for (const line of lines) {\n const cells = line.split(',').map(entry => entry.replace(/\"/g, '').trim())\n const pid = Number.parseInt(cells[pidAt] ?? '', 10)\n if (!Number.isFinite(pid))\n continue\n\n // A missing time column must read as \"unknown\", never as zero CPU.\n const kernel = kernelAt >= 0 ? Number.parseInt(cells[kernelAt] ?? '', 10) : Number.NaN\n const user = userAt >= 0 ? Number.parseInt(cells[userAt] ?? '', 10) : Number.NaN\n const hasTimes = Number.isFinite(kernel) && Number.isFinite(user)\n\n rows.push({\n pid,\n ppid: Number.parseInt(cells[ppidAt] ?? '', 10) || 0,\n // WorkingSetSize is bytes on Windows; the sampler sums kilobytes.\n rssKb: (Number.parseInt(cells[rssAt] ?? '', 10) || 0) / 1024,\n cpuSeconds: hasTimes ? (kernel + user) / 1e7 /* 100ns units */ : undefined,\n })\n }\n\n return rows\n}\n\nlet clockTicks: number | null = null\n\n/** Linux jiffies per second; `getconf` is POSIX, with the usual default behind it. */\nasync function getClockTicks(): Promise<number> {\n if (clockTicks !== null)\n return clockTicks\n try {\n const { stdout } = await execFileAsync('getconf', ['CLK_TCK'], { timeout: 2000 })\n const parsed = Number.parseInt(stdout.trim(), 10)\n clockTicks = Number.isFinite(parsed) && parsed > 0 ? parsed : 100\n }\n catch {\n clockTicks = 100\n }\n return clockTicks\n}\n\n/**\n * `/proc/<pid>/stat` needs care: the comm field is parenthesised and may itself\n * contain spaces or parentheses, so parsing starts after the last `)`.\n */\nfunction parseStat(pid: number, content: string): ProcRow | null {\n const close = content.lastIndexOf(')')\n if (close < 0)\n return null\n const fields = content.slice(close + 2).split(' ')\n const ppid = Number.parseInt(fields[1] ?? '', 10)\n const utime = Number.parseInt(fields[11] ?? '', 10)\n const stime = Number.parseInt(fields[12] ?? '', 10)\n const rssPages = Number.parseInt(fields[21] ?? '', 10)\n\n if (!Number.isFinite(ppid) || !Number.isFinite(utime) || !Number.isFinite(stime))\n return null\n return { pid, ppid, rssKb: Number.isFinite(rssPages) ? rssPages * 4 : 0, cpuSeconds: utime + stime }\n}\n\nasync function readLinux(): Promise<ProcRow[]> {\n const rows: ProcRow[] = []\n let names: string[] = []\n try {\n names = fs.readdirSync('/proc')\n }\n catch {\n return rows\n }\n\n for (const name of names) {\n if (!/^\\d+$/.test(name))\n continue\n const pid = Number.parseInt(name, 10)\n try {\n const row = parseStat(pid, fs.readFileSync(`/proc/${pid}/stat`, 'utf8'))\n if (row === null)\n continue\n // VmRSS is exact; the stat page count assumes a 4K page.\n try {\n const vmRss = /^VmRSS:\\s+(\\d+)\\s+kB/m.exec(fs.readFileSync(`/proc/${pid}/status`, 'utf8'))?.[1]\n if (vmRss !== undefined)\n row.rssKb = Number.parseInt(vmRss, 10)\n }\n catch {\n // Fall back to the page count.\n }\n rows.push(row)\n }\n catch {\n // Exited between listing and reading.\n }\n }\n\n return rows\n}\n\nasync function readPosix(): Promise<ProcRow[]> {\n const { stdout } = await execFileAsync('ps', ['-Ao', 'pid=,ppid=,rss=,%cpu='], { timeout: 5000, maxBuffer: 16 * 1024 * 1024 })\n return parsePsOutput(stdout)\n}\n\nasync function readWindows(): Promise<ProcRow[]> {\n const script = 'Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,WorkingSetSize,KernelModeTime,UserModeTime | ConvertTo-Csv -NoTypeInformation'\n try {\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {\n timeout: 8000,\n maxBuffer: 16 * 1024 * 1024,\n })\n return parseWindowsCsv(stdout)\n }\n catch {\n try {\n const { stdout } = await execFileAsync('wmic', [\n 'process',\n 'get',\n 'ProcessId,ParentProcessId,WorkingSetSize,KernelModeTime,UserModeTime',\n '/format:csv',\n ], { timeout: 8000, maxBuffer: 16 * 1024 * 1024 })\n return parseWindowsCsv(stdout)\n }\n catch {\n // Neither tool is available; resource sampling degrades to \"unknown\".\n return []\n }\n }\n}\n\nasync function readProcesses(): Promise<ProcRow[]> {\n if (process.platform === 'linux')\n return readLinux()\n if (process.platform === 'win32')\n return readWindows()\n return readPosix()\n}\n\nfunction collectTree(rootPid: number, children: Map<number, number[]>): number[] {\n const pids: number[] = []\n const stack = [rootPid]\n const seen = new Set<number>()\n\n while (stack.length > 0) {\n const pid = stack.pop()!\n if (seen.has(pid))\n continue\n seen.add(pid)\n pids.push(pid)\n for (const child of children.get(pid) ?? []) stack.push(child)\n }\n\n return pids\n}\n\n/**\n * Samples CPU and RSS for a process *and its descendants*.\n *\n * Descendants matter: a wrapper that spawns the real server detached (the\n * 9router CLI does) owns the tree, and only the tree's RSS means anything.\n *\n * Backends: `/proc` on Linux, `ps` on macOS/other POSIX, and Win32_Process via\n * PowerShell (wmic as a fallback) on Windows. When a backend cannot run, samples\n * are null rather than wrong.\n */\nexport class ProcessSampler {\n private readonly previous = new Map<number, { cpuSeconds: number, at: number }>()\n\n async sample(rootPid: number, now = Date.now()): Promise<ProcessResources | null> {\n const samples = await this.sampleMany([rootPid], now)\n return samples.get(rootPid) ?? null\n }\n\n async sampleMany(rootPids: number[], now = Date.now()): Promise<Map<number, ProcessResources | null>> {\n const results = new Map<number, ProcessResources | null>()\n if (rootPids.length === 0)\n return results\n\n let rows: ProcRow[] = []\n try {\n rows = await readProcesses()\n }\n catch {\n rows = []\n }\n\n const byPid = new Map(rows.map(row => [row.pid, row]))\n const children = new Map<number, number[]>()\n for (const row of rows) {\n const siblings = children.get(row.ppid) ?? []\n siblings.push(row.pid)\n children.set(row.ppid, siblings)\n }\n\n for (const rootPid of rootPids) {\n if (!byPid.has(rootPid)) {\n this.previous.delete(rootPid)\n results.set(rootPid, null)\n continue\n }\n\n const pids = collectTree(rootPid, children)\n let rssKb = 0\n let cpuSeconds: number | null = 0\n let percentAverage: number | null = null\n\n for (const pid of pids) {\n const row = byPid.get(pid)\n if (!row)\n continue\n rssKb += row.rssKb\n if (row.cpuSeconds === undefined)\n cpuSeconds = null\n else if (cpuSeconds !== null)\n cpuSeconds += row.cpuSeconds\n if (row.cpuPercent !== undefined)\n percentAverage = (percentAverage ?? 0) + row.cpuPercent\n }\n\n let cpuPercent: number | null = percentAverage\n if (cpuPercent === null && cpuSeconds !== null) {\n if (process.platform === 'linux') {\n const ticks = await getClockTicks()\n cpuSeconds /= ticks\n }\n\n const before = this.previous.get(rootPid)\n if (before !== undefined && now > before.at) {\n const elapsedSeconds = (now - before.at) / 1000\n const usedSeconds = cpuSeconds - before.cpuSeconds\n if (elapsedSeconds > 0 && usedSeconds >= 0)\n cpuPercent = (usedSeconds / elapsedSeconds) * 100\n }\n this.previous.set(rootPid, { cpuSeconds, at: now })\n }\n else {\n this.previous.delete(rootPid)\n }\n\n results.set(rootPid, {\n cpuPercent: cpuPercent === null ? null : Math.round(cpuPercent * 10) / 10,\n rssBytes: Math.round(rssKb * 1024),\n processes: pids.length,\n sampledAt: now,\n })\n }\n\n return results\n }\n\n forget(rootPid: number): void {\n this.previous.delete(rootPid)\n }\n}\n","import type { ChildProcess } from 'node:child_process'\nimport { execFile, spawn } from 'node:child_process'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { promisify } from 'node:util'\nimport { projectDir } from '#src/helpers/paths'\n\nconst execFileAsync = promisify(execFile)\n\nexport interface SpawnSpec {\n command: string\n args: string[]\n cwd: string\n env: Record<string, string>\n}\n\n/**\n * Resolves a bare command through the entry's own directory and the project's\n * `node_modules/.bin` first, so a server installed as a project dependency is\n * found even when the launcher's PATH has no pnpm-injected bin dir.\n */\nexport function resolveCommand(command: string, ...searchDirs: string[]): string {\n if (command.includes('/') || command.includes('\\\\'))\n return command\n\n for (const dir of searchDirs) {\n const local = path.join(dir, 'node_modules', '.bin', command)\n if (fs.existsSync(local))\n return local\n }\n\n return command\n}\n\n/** Relative entry paths belong to the project that launched the panel. */\nexport function resolveCwd(cwd: string, base: string = projectDir): string {\n return path.resolve(base, cwd)\n}\n\nexport function spawnManaged(spec: SpawnSpec): ChildProcess {\n return spawn(spec.command, spec.args, {\n cwd: spec.cwd,\n env: { ...process.env, ...spec.env },\n // Own process group: a stop can signal the whole tree with one kill(-pid).\n detached: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nexport interface TerminateOptions {\n signal: NodeJS.Signals\n killGroup: boolean\n graceMs: number\n}\n\n/** SIGTERM (default) to the child, escalating to SIGKILL after the grace period. */\nexport async function terminate(child: ChildProcess, options: TerminateOptions): Promise<'exited' | 'force-killed'> {\n if (child.exitCode !== null || child.signalCode !== null)\n return 'exited'\n\n const exited = waitForExit(child, options.graceMs)\n signalChild(child, options.signal, options.killGroup)\n\n if (await exited)\n return 'exited'\n\n signalChild(child, 'SIGKILL', options.killGroup)\n await waitForExit(child, 2000)\n return 'force-killed'\n}\n\n/**\n * Windows has no process groups and no SIGTERM: `taskkill /T` walks the tree and\n * `/F` is the only reliable way to stop a console process.\n */\nexport async function killTreeWindows(pid: number): Promise<void> {\n try {\n await execFileAsync('taskkill', ['/pid', String(pid), '/T', '/F'], { timeout: 5000 })\n }\n catch {\n // Already gone, or taskkill is unavailable.\n }\n}\n\nfunction signalChild(child: ChildProcess, signal: NodeJS.Signals, killGroup: boolean): void {\n const pid = child.pid\n if (pid === undefined)\n return\n\n if (process.platform === 'win32') {\n if (killGroup) {\n void killTreeWindows(pid)\n }\n else {\n try {\n process.kill(pid, signal)\n }\n catch {\n // already exited\n }\n }\n return\n }\n\n if (killGroup) {\n try {\n process.kill(-pid, signal)\n return\n }\n catch {\n // group already gone, fall through to the single pid\n }\n }\n\n try {\n process.kill(pid, signal)\n }\n catch {\n // already exited\n }\n}\n\nfunction waitForExit(child: ChildProcess, timeoutMs: number): Promise<boolean> {\n if (child.exitCode !== null || child.signalCode !== null)\n return Promise.resolve(true)\n if (timeoutMs <= 0)\n return Promise.resolve(false)\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n child.removeListener('exit', onExit)\n resolve(false)\n }, timeoutMs)\n\n function onExit(): void {\n clearTimeout(timer)\n resolve(true)\n }\n\n child.once('exit', onExit)\n })\n}\n","import type { ServerConfig } from '#src/shared/contracts'\n\n/**\n * Orders servers so every dependency comes before its dependents.\n *\n * Cycles and unknown ids are ignored here — the config store reports them as\n * config errors — so this can never throw and stall supervision.\n */\nexport function orderByDependencies(servers: ServerConfig[]): ServerConfig[] {\n const byId = new Map(servers.map(server => [server.id, server]))\n const ordered: ServerConfig[] = []\n const visited = new Set<string>()\n\n const visit = (server: ServerConfig): void => {\n if (visited.has(server.id))\n return\n visited.add(server.id)\n for (const dependency of server.dependsOn) {\n const target = byId.get(dependency)\n if (target && target.id !== server.id)\n visit(target)\n }\n ordered.push(server)\n }\n\n for (const server of servers) visit(server)\n return ordered\n}\n\n/** The transitive dependencies of a server, nearest first. */\nexport function dependenciesOf(server: ServerConfig, servers: ServerConfig[]): ServerConfig[] {\n const byId = new Map(servers.map(entry => [entry.id, entry]))\n const found: ServerConfig[] = []\n const seen = new Set<string>()\n\n const walk = (current: ServerConfig): void => {\n for (const dependency of current.dependsOn) {\n if (seen.has(dependency))\n continue\n seen.add(dependency)\n const target = byId.get(dependency)\n if (!target)\n continue\n found.push(target)\n walk(target)\n }\n }\n\n walk(server)\n return found\n}\n\n/** Dependents that must stop before this server does. */\nexport function dependentsOf(server: ServerConfig, servers: ServerConfig[]): ServerConfig[] {\n return servers.filter(entry => entry.id !== server.id && dependenciesOf(entry, servers).some(d => d.id === server.id))\n}\n","import type { Buffer } from 'node:buffer'\nimport type { LogLine, LogStream } from '#src/shared/contracts'\n\n/** Fixed-capacity line buffer; oldest lines are dropped first. */\nexport class LogBuffer {\n private lines: LogLine[] = []\n\n constructor(private capacity: number) {}\n\n push(line: LogLine): void {\n this.lines.push(line)\n if (this.lines.length > this.capacity)\n this.lines.splice(0, this.lines.length - this.capacity)\n }\n\n extend(lines: LogLine[]): void {\n for (const line of lines) this.push(line)\n }\n\n list(limit?: number): LogLine[] {\n if (limit === undefined || limit >= this.lines.length)\n return [...this.lines]\n return this.lines.slice(-limit)\n }\n\n clear(): void {\n this.lines = []\n }\n\n get size(): number {\n return this.lines.length\n }\n}\n\n/**\n * Splits a chunk into complete lines, keeping a trailing partial line buffered:\n * a child writing \"hel\" then \"lo\\n\" must surface one line, not two.\n */\nexport class LineSplitter {\n private pending = ''\n\n constructor(private readonly emit: (stream: LogStream, text: string) => void) {}\n\n push(stream: LogStream, chunk: string | Buffer): void {\n this.pending += chunk.toString()\n const parts = this.pending.split('\\n')\n this.pending = parts.pop() ?? ''\n for (const part of parts) this.emit(stream, part.replace(/\\r$/, ''))\n }\n\n flush(stream: LogStream): void {\n if (this.pending.length === 0)\n return\n this.emit(stream, this.pending)\n this.pending = ''\n }\n}\n","import type { ChildProcess } from 'node:child_process'\nimport type { ServerConfig } from '#src/config/schema'\nimport type { ConfigStore } from '#src/config/store'\nimport type { TemplateVars } from '#src/helpers/template'\nimport type { ControlEndpoint } from '#src/services/control-server'\nimport type { EventHub } from '#src/services/events'\nimport type { HistoryStore } from '#src/services/history'\nimport type { HostMonitor } from '#src/services/host-monitor'\nimport type { LogFiles } from '#src/services/log-files'\nimport type { NotificationReason, NotificationService } from '#src/services/notifications'\nimport type {\n AppState,\n HealthState,\n LogLine,\n LogStream,\n PortState,\n ProcessResources,\n ServerStatus,\n ServerView,\n} from '#src/shared/contracts'\nimport { spawn } from 'node:child_process'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { computeBackoff } from '#src/helpers/backoff'\nimport { bindHost, displayHost, lanAddress } from '#src/helpers/bind'\nimport { expandEnvList, expandEnvRecord, loadEnvFile, resolveEnvFilePath } from '#src/helpers/env-file'\nimport { logger } from '#src/helpers/logger'\nimport { dataRoot, projectDir } from '#src/helpers/paths'\nimport { resolveRecord, resolveTemplates } from '#src/helpers/template'\nimport { probeHealth } from '#src/providers/health-check'\nimport { isPortFree, killPortHolders, listPortHolders, probePort } from '#src/providers/port'\nimport { ProcessSampler } from '#src/providers/proc'\nimport { resolveCommand, resolveCwd, spawnManaged, terminate } from '#src/providers/process'\nimport { dependenciesOf, orderByDependencies } from '#src/services/dependencies'\nimport { LineSplitter, LogBuffer } from '#src/services/log-buffer'\n\nexport interface SupervisorOptions {\n configPath: string\n /** Live listener info, mutated by the control server when it rebinds. */\n control: ControlEndpoint\n /** Injected so SSE state frames carry the same view the API serves. */\n buildState: (views: ServerView[]) => AppState\n history: HistoryStore\n logFiles: LogFiles\n notifications: NotificationService\n hostMonitor: HostMonitor\n}\n\n/** Uptime/crash counters are reported over this window. */\nconst HISTORY_WINDOW_MS = 24 * 60 * 60 * 1000\nconst HISTORY_CACHE_MS = 5000\n\nexport interface StartResult {\n ok: boolean\n error?: string\n}\n\ninterface Entry {\n config: ServerConfig\n status: ServerStatus\n health: HealthState\n portState: PortState\n child: ChildProcess | null\n pid: number | null\n startedAt: number | null\n exitCode: number | null\n exitSignal: string | null\n restarts: number\n lastError: string | null\n nextRetryAt: number | null\n retryTimer: NodeJS.Timeout | null\n healthFailures: number\n unhealthySince: number | null\n lastProbeAt: number\n lastOccupancyProbeAt: number\n probing: boolean\n /** A start is in flight (set synchronously, unlike `status`). */\n starting: boolean\n stopping: boolean\n bootstrapDone: boolean\n logs: LogBuffer\n responseMs: number | null\n resources: ProcessResources | null\n resourcesSampledAt: number\n historyCache: { revision: number, at: number, summary: ServerView['history'] } | null\n}\n\nconst TICK_INTERVAL_MS = 1000\nconst PORT_STATE_INTERVAL_MS = 10000\nconst PORT_RELEASE_RECHECK_MS = 300\nconst RESOURCE_SAMPLE_INTERVAL_MS = 5000\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/** The placeholder set every server entry can use; shared with path resolvers. */\nexport function serverTemplateVars(config: ServerConfig): TemplateVars {\n return {\n id: config.id,\n label: config.label ?? config.id,\n port: config.port ?? '',\n host: bindHost(config.bind),\n displayHost: displayHost(config.bind),\n bind: config.bind,\n lanIp: lanAddress() ?? '127.0.0.1',\n cwd: resolveCwd(config.cwd),\n projectDir,\n dataRoot,\n home: os.homedir(),\n }\n}\n\nexport class Supervisor {\n private readonly sampler = new ProcessSampler()\n private readonly entries = new Map<string, Entry>()\n private readonly tickTimer: NodeJS.Timeout\n private disposed = false\n private lastStateSignature = ''\n\n constructor(\n private readonly store: ConfigStore,\n private readonly hub: EventHub,\n private readonly options: SupervisorOptions,\n ) {\n this.sync()\n this.store.onChange(() => this.sync())\n this.tickTimer = setInterval(() => void this.tick(), TICK_INTERVAL_MS)\n this.tickTimer.unref()\n }\n\n getState(): AppState {\n return this.options.buildState(this.views())\n }\n\n views(): ServerView[] {\n return [...this.entries.values()].map(entry => this.view(entry))\n }\n\n logLines(id: string, limit?: number): LogLine[] {\n return this.entries.get(id)?.logs.list(limit) ?? []\n }\n\n async startAll(options: { autostartOnly?: boolean } = {}): Promise<void> {\n const targets = [...this.entries.values()]\n .filter(entry => !options.autostartOnly || entry.config.autostart)\n .map(entry => entry.config)\n // Dependencies first; levels are still started concurrently.\n for (const config of orderByDependencies(targets)) {\n await this.start(config.id)\n }\n }\n\n async stopAll(): Promise<void> {\n const targets = orderByDependencies([...this.entries.values()].map(entry => entry.config)).reverse()\n for (const config of targets) {\n await this.stop(config.id)\n }\n }\n\n async start(id: string, options: { retry?: boolean } = {}): Promise<StartResult> {\n const entry = this.entries.get(id)\n if (!entry)\n return { ok: false, error: `unknown server \"${id}\"` }\n if (!entry.config.enabled)\n return { ok: false, error: `server \"${id}\" is disabled` }\n if (entry.status === 'running' || entry.status === 'starting' || entry.starting)\n return { ok: true }\n if (entry.stopping)\n return { ok: false, error: `server \"${id}\" is stopping` }\n\n // Set before the first await: two overlapping `start()` calls (a click and a\n // retry timer, say) would otherwise both reach `spawnEntry` and orphan one.\n entry.starting = true\n this.clearRetry(entry)\n if (!options.retry) {\n entry.restarts = 0\n entry.healthFailures = 0\n entry.unhealthySince = null\n }\n\n try {\n await this.startDependencies(entry)\n // A stop that arrived while we were waiting must win: the process must not\n // start and then be reported as stopped.\n if (entry.stopping || this.disposed)\n return { ok: false, error: `server \"${id}\" is stopping` }\n\n entry.lastError = null\n entry.status = 'starting'\n entry.health = entry.config.health.enabled ? 'unknown' : 'disabled'\n this.publishServer(entry)\n\n await this.runBootstrap(entry)\n if (entry.stopping)\n return { ok: false, error: `server \"${id}\" is stopping` }\n if (this.disposed)\n return { ok: false, error: 'supervisor is shutting down' }\n\n const conflict = await this.preflight(entry)\n if (conflict !== null)\n return { ok: false, error: conflict }\n\n return this.spawnEntry(entry)\n }\n finally {\n entry.starting = false\n }\n }\n\n async stop(id: string): Promise<StartResult> {\n const entry = this.entries.get(id)\n if (!entry)\n return { ok: false, error: `unknown server \"${id}\"` }\n return this.stopEntry(entry)\n }\n\n async restart(id: string): Promise<StartResult> {\n await this.stop(id)\n return this.start(id)\n }\n\n clearLogs(id: string): void {\n const entry = this.entries.get(id)\n if (!entry)\n return\n entry.logs.clear()\n this.publishServer(entry)\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n clearInterval(this.tickTimer)\n for (const entry of this.entries.values()) this.clearRetry(entry)\n await Promise.all([...this.entries.values()].map(entry => this.stopEntry(entry)))\n }\n\n /**\n * Starts whatever this server depends on and waits for it to accept\n * connections. A dependency that refuses to come up is logged and skipped\n * rather than blocking the dependent forever.\n */\n private async startDependencies(entry: Entry): Promise<void> {\n if (entry.config.dependsOn.length === 0)\n return\n\n for (const dependency of dependenciesOf(entry.config, this.store.servers)) {\n const target = this.entries.get(dependency.id)\n if (!target || !dependency.enabled)\n continue\n if (target.status === 'running' || target.child !== null)\n continue\n\n this.log(entry, 'system', `starting dependency \"${dependency.id}\" first`)\n await this.start(dependency.id)\n\n const deadline = Date.now() + dependency.health.startTimeoutMs\n const isReady = (): boolean => {\n const status = this.statusOf(target)\n if (status !== 'running')\n return false\n // A dependency that is listening but failing its check is not ready.\n return target.health !== 'unhealthy'\n }\n\n while (!isReady() && Date.now() < deadline) await delay(250)\n\n if (!isReady()) {\n const status = this.statusOf(target)\n this.log(entry, 'system', `dependency \"${dependency.id}\" is ${status}/${target.health} — starting anyway`)\n }\n }\n }\n\n /** Read through a method so TypeScript does not carry a stale narrowing across awaits. */\n private statusOf(entry: Entry): ServerStatus {\n return entry.status\n }\n\n /**\n * Summaries are rebuilt when history changes or every few seconds, because\n * `view()` runs on every state frame and the window math is O(events).\n */\n private summarizeHistory(entry: Entry): ServerView['history'] {\n const now = Date.now()\n const revision = this.options.history.revision\n const cached = entry.historyCache\n if (cached !== null && cached.revision === revision && now - cached.at < HISTORY_CACHE_MS)\n return cached.summary\n\n const runningSince = entry.child !== null && entry.startedAt !== null ? entry.startedAt : null\n const summary = this.options.history.summarize(entry.config.id, HISTORY_WINDOW_MS, now, runningSince)\n entry.historyCache = { revision, at: now, summary }\n return summary\n }\n\n private notify(entry: Entry, reason: NotificationReason, detail: string): void {\n this.options.notifications.notify({\n serverId: entry.config.id,\n label: entry.config.label ?? entry.config.id,\n reason,\n detail,\n })\n }\n\n private async stopEntry(entry: Entry): Promise<StartResult> {\n this.clearRetry(entry)\n entry.nextRetryAt = null\n\n // Set first: a start that is still bootstrapping (no child yet) checks this\n // after every await and aborts, instead of spawning behind our back.\n entry.stopping = true\n\n if (entry.child === null) {\n entry.status = 'stopped'\n entry.pid = null\n this.publishServer(entry)\n return { ok: true }\n }\n\n entry.status = 'stopping'\n this.publishServer(entry)\n\n const outcome = await terminate(entry.child, entry.config.stop)\n if (outcome === 'force-killed')\n this.log(entry, 'system', 'force-killed after grace period')\n\n const { port, stop } = entry.config\n if (stop.killPortHolders && port !== null) {\n const leftover = await listPortHolders(port)\n if (leftover.length > 0) {\n this.log(entry, 'system', `port ${port} still held by pid ${leftover.join(', ')} — killing`)\n await killPortHolders(port)\n }\n }\n\n entry.stopping = false\n entry.child = null\n entry.pid = null\n entry.status = 'stopped'\n this.log(entry, 'system', 'stopped')\n this.publishServer(entry)\n return { ok: true }\n }\n\n private createEntry(config: ServerConfig): Entry {\n return {\n config,\n status: 'stopped',\n health: config.health.enabled ? 'unknown' : 'disabled',\n portState: 'unknown',\n child: null,\n pid: null,\n startedAt: null,\n exitCode: null,\n exitSignal: null,\n restarts: 0,\n lastError: null,\n nextRetryAt: null,\n retryTimer: null,\n healthFailures: 0,\n unhealthySince: null,\n lastProbeAt: 0,\n lastOccupancyProbeAt: 0,\n probing: false,\n starting: false,\n stopping: false,\n bootstrapDone: !config.bootstrap,\n logs: new LogBuffer(config.logBufferLines),\n responseMs: null,\n resources: null,\n resourcesSampledAt: 0,\n historyCache: null,\n }\n }\n\n private sync(): void {\n const wanted = new Map(this.store.servers.map(server => [server.id, server]))\n\n for (const [id, entry] of [...this.entries]) {\n const config = wanted.get(id)\n if (!config) {\n this.entries.delete(id)\n void this.stopEntry(entry)\n continue\n }\n const bufferChanged = entry.config.logBufferLines !== config.logBufferLines\n entry.config = config\n if (bufferChanged) {\n const kept = entry.logs.list(config.logBufferLines)\n entry.logs = new LogBuffer(config.logBufferLines)\n entry.logs.extend(kept)\n }\n if (!config.enabled && this.isActive(entry))\n void this.stopEntry(entry)\n }\n\n for (const [id, config] of wanted) {\n if (!this.entries.has(id))\n this.entries.set(id, this.createEntry(config))\n }\n\n this.publishState()\n }\n\n private isActive(entry: Entry): boolean {\n return entry.child !== null || entry.status === 'backoff'\n }\n\n /** Loopback first, then the configured address, so a custom bind is still probed. */\n private probeHosts(entry: Entry): string[] {\n const primary = '127.0.0.1'\n const configured = displayHost(entry.config.bind)\n return configured === primary ? [primary] : [primary, configured]\n }\n\n /**\n * Where a port can actually be observed for this entry. A server bound to a\n * specific address is not reachable on loopback, and a `lan` bind is reachable\n * there *and* on this machine's LAN address.\n */\n private occupancyHosts(entry: Entry): string[] {\n const configured = bindHost(entry.config.bind)\n const candidates = configured === '0.0.0.0'\n ? ['127.0.0.1', lanAddress() ?? '127.0.0.1']\n : [configured, '127.0.0.1']\n return [...new Set(candidates)]\n }\n\n /** True when the port accepts a connection on any of the entry's addresses. */\n private async portAccepts(entry: Entry, port: number, timeoutMs: number): Promise<boolean> {\n const results = await Promise.all(this.occupancyHosts(entry).map(host => probePort(host, port, timeoutMs)))\n return results.some(Boolean)\n }\n\n private async preflight(entry: Entry): Promise<string | null> {\n const port = entry.config.port\n if (port === null)\n return null\n\n // A listener that was just closed can still complete a handshake for a few\n // milliseconds, which is exactly the window a fast restart lands in — so a\n // busy-looking port gets a second look before it is treated as a conflict.\n // The configured address first: a server bound to a LAN ip is not \"free\" just\n // because nothing holds it on loopback.\n const hosts = this.occupancyHosts(entry)\n const freeOnAll = async (): Promise<boolean> => {\n const results = await Promise.all(hosts.map(host => isPortFree(port, host)))\n return results.every(Boolean)\n }\n\n let free = await freeOnAll()\n if (!free) {\n await delay(PORT_RELEASE_RECHECK_MS)\n free = await freeOnAll()\n }\n\n entry.portState = free ? 'free' : 'in-use'\n if (free)\n return null\n\n const holders = await listPortHolders(port)\n const suffix = holders.length > 0 ? ` (pid ${holders.join(', ')})` : ''\n\n if (entry.config.onPortConflict === 'block') {\n entry.status = 'conflict'\n entry.lastError = `port ${port} is already in use${suffix}`\n this.log(entry, 'system', `${entry.lastError} — not starting (onPortConflict: block)`)\n this.publishServer(entry)\n return entry.lastError\n }\n\n this.log(entry, 'system', `warning: port ${port} is already in use${suffix} — starting anyway`)\n return null\n }\n\n private async runBootstrap(entry: Entry): Promise<void> {\n const spec = entry.config.bootstrap\n if (!spec || (spec.runOnce && entry.bootstrapDone))\n return\n\n const vars = this.buildVars(entry)\n const cwd = resolveCwd(entry.config.cwd)\n const args = resolveTemplates(spec.args, vars)\n this.log(entry, 'system', `bootstrap: ${spec.command} ${args.join(' ')}`)\n\n const splitter = new LineSplitter((_stream, text) => {\n if (text.trim().length > 0)\n this.log(entry, 'system', `[bootstrap] ${text}`)\n })\n\n const child = spawn(resolveCommand(spec.command, cwd, projectDir), args, {\n cwd,\n env: { ...process.env, ...resolveRecord(spec.env, vars) },\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n })\n child.stdout?.on('data', chunk => splitter.push('stdout', chunk))\n child.stderr?.on('data', chunk => splitter.push('stderr', chunk))\n\n const code = await new Promise<number | null>((resolve) => {\n const timer = setTimeout(() => {\n this.log(entry, 'system', `bootstrap timed out after ${spec.timeoutMs}ms`)\n try {\n child.kill('SIGKILL')\n }\n catch {\n // already gone\n }\n }, spec.timeoutMs)\n child.once('exit', (exitCode) => {\n clearTimeout(timer)\n resolve(exitCode)\n })\n child.once('error', (error) => {\n clearTimeout(timer)\n this.log(entry, 'system', `bootstrap failed: ${(error as Error).message}`)\n resolve(null)\n })\n })\n\n entry.bootstrapDone = true\n if (code === 0)\n this.log(entry, 'system', 'bootstrap finished')\n else if (code !== null)\n this.log(entry, 'system', `bootstrap exited with code ${code} — continuing anyway`)\n }\n\n private spawnEntry(entry: Entry): StartResult {\n const vars = this.buildVars(entry)\n const cwd = resolveCwd(entry.config.cwd)\n const command = resolveCommand(entry.config.command, cwd, projectDir)\n\n // A machine-local env file is layered over the tracked config and also feeds\n // `${VAR}` in args/env, so secrets stay out of servers.config.json.\n let fileEnv: Record<string, string> = {}\n if (entry.config.envFile.length > 0) {\n const file = resolveEnvFilePath(entry.config.envFile, cwd)\n const loaded = loadEnvFile(file)\n if (loaded.error !== null)\n this.log(entry, 'system', `env file ${file} could not be read: ${loaded.error}`)\n else if (Object.keys(loaded.env).length > 0)\n this.log(entry, 'system', `env file ${file} (${Object.keys(loaded.env).length} vars)`)\n fileEnv = loaded.env\n }\n\n const expansionVars: Record<string, string | undefined> = { ...process.env, ...fileEnv }\n const args = expandEnvList(resolveTemplates(entry.config.args, vars), expansionVars)\n const env = {\n // `envFile` is the machine-local layer, so it overrides the tracked `env`.\n ...expandEnvRecord(resolveRecord(entry.config.env, vars), expansionVars),\n ...fileEnv,\n // Data envs win over `env`: their value is the directory that gets backed\n // up, so the process has to be pointed at exactly that path.\n ...expandEnvRecord(resolveRecord(entry.config.dataEnvs, vars), expansionVars),\n HHOSTED_SERVER_ID: entry.config.id,\n HHOSTED_CONTROL_PORT: String(this.options.control.port),\n }\n\n // Logged *before* `${VAR}` expansion: an argument like `${API_TOKEN}` must not\n // land in the ring buffer, the rotated files, SSE or Telegram.\n this.log(entry, 'system', `start: ${command} ${resolveTemplates(entry.config.args, vars).join(' ')}`)\n\n let child: ChildProcess\n try {\n child = spawnManaged({ command, args, cwd, env })\n }\n catch (error) {\n entry.status = 'crashed'\n entry.lastError = (error as Error).message\n this.log(entry, 'system', `spawn failed: ${entry.lastError}`)\n this.publishServer(entry)\n return { ok: false, error: entry.lastError }\n }\n\n entry.child = child\n entry.pid = child.pid ?? null\n entry.startedAt = Date.now()\n this.options.history.record(entry.config.id, {\n type: 'start',\n detail: `${command} ${args.join(' ')}`.trim(),\n })\n entry.exitCode = null\n entry.exitSignal = null\n entry.lastProbeAt = 0\n entry.healthFailures = 0\n entry.unhealthySince = null\n this.publishServer(entry)\n\n const stdout = new LineSplitter((stream, text) => this.log(entry, stream, text))\n const stderr = new LineSplitter((stream, text) => this.log(entry, stream, text))\n child.stdout?.on('data', chunk => stdout.push('stdout', chunk))\n child.stderr?.on('data', chunk => stderr.push('stderr', chunk))\n\n child.once('error', (error) => {\n entry.lastError = (error as Error).message\n this.log(entry, 'system', `process error: ${entry.lastError}`)\n stdout.flush('stdout')\n stderr.flush('stderr')\n this.handleExit(entry, child, null, null)\n })\n\n child.once('exit', (code, signal) => {\n stdout.flush('stdout')\n stderr.flush('stderr')\n this.handleExit(entry, child, code, signal)\n })\n\n void this.awaitReadiness(entry, child)\n return { ok: true }\n }\n\n /** One probe using the configured mode (TCP or HTTP), with timing. */\n private async probeEntryHealth(entry: Entry): Promise<{ healthy: boolean, ms: number, detail: string }> {\n const { port, health } = entry.config\n if (port === null)\n return { healthy: true, ms: 0, detail: 'no port configured' }\n\n return probeHealth({\n mode: health.mode,\n hosts: this.probeHosts(entry),\n port,\n timeoutMs: health.timeoutMs,\n http: health.http,\n })\n }\n\n private async awaitReadiness(entry: Entry, child: ChildProcess): Promise<void> {\n const { port, health } = entry.config\n\n if (port === null) {\n if (entry.child !== child || entry.status !== 'starting')\n return\n entry.status = 'running'\n entry.health = health.enabled ? 'unknown' : 'disabled'\n this.log(entry, 'system', 'running (no port configured; readiness assumed on spawn)')\n this.publishServer(entry)\n return\n }\n\n const deadline = Date.now() + health.startTimeoutMs\n while (Date.now() < deadline) {\n if (entry.child !== child || entry.status !== 'starting' || this.disposed)\n return\n if (await this.portAccepts(entry, port, Math.min(health.timeoutMs, 1000))) {\n entry.portState = 'in-use'\n entry.health = health.enabled ? 'healthy' : 'disabled'\n entry.status = 'running'\n entry.lastProbeAt = Date.now()\n this.log(entry, 'system', `accepting connections on port ${port}`)\n this.publishServer(entry)\n return\n }\n await delay(300)\n }\n\n if (entry.child === child && entry.status === 'starting') {\n entry.status = 'running'\n entry.health = 'unhealthy'\n entry.unhealthySince = Date.now()\n this.log(entry, 'system', `no connection on port ${port} after ${health.startTimeoutMs}ms — supervising anyway`)\n this.publishServer(entry)\n }\n }\n\n private handleExit(entry: Entry, child: ChildProcess, code: number | null, signal: NodeJS.Signals | null): void {\n if (entry.child !== child)\n return\n if (entry.pid !== null)\n this.sampler.forget(entry.pid)\n entry.child = null\n entry.pid = null\n entry.resources = null\n entry.responseMs = null\n entry.exitCode = code\n entry.exitSignal = signal\n\n // Both null means the process never got off the ground — a missing command,\n // for instance — so its error is more useful than \"code null\".\n const neverStarted = code === null && signal === null && entry.lastError !== null\n const detail = neverStarted\n ? entry.lastError!\n : signal !== null ? `signal ${signal}` : `code ${code}`\n const ranForMs = entry.startedAt === null ? 0 : Date.now() - entry.startedAt\n const ranFor = `${Math.max(1, Math.round(ranForMs / 1000))}s`\n\n // Recorded for *every* exit, not only the ones that end in `crashed`: the\n // rolling window (crashes, uptime, last exit) is built from these events.\n this.options.history.record(entry.config.id, {\n type: 'exit',\n detail,\n runtimeMs: ranForMs,\n })\n\n if (entry.stopping) {\n entry.status = 'stopped'\n this.publishServer(entry)\n return\n }\n\n const restart = entry.config.restart\n if (ranForMs >= restart.resetAfterMs)\n entry.restarts = 0\n\n this.log(entry, 'system', neverStarted ? `did not start: ${detail}` : `exited with ${detail} after ${ranFor}`)\n entry.lastError = neverStarted ? detail : `exited with ${detail}`\n\n if (restart.enabled && entry.restarts < restart.maxRetries) {\n entry.restarts += 1\n const backoffMs = computeBackoff(entry.restarts, restart)\n entry.status = 'backoff'\n entry.nextRetryAt = Date.now() + backoffMs\n this.log(entry, 'system', `restart ${entry.restarts}/${restart.maxRetries} in ${backoffMs}ms`)\n entry.retryTimer = setTimeout(() => {\n entry.retryTimer = null\n void this.start(entry.config.id, { retry: true })\n }, backoffMs)\n entry.retryTimer.unref()\n }\n else {\n entry.status = 'crashed'\n entry.nextRetryAt = null\n entry.lastError = restart.enabled\n ? `gave up after ${restart.maxRetries} retries (${detail})`\n : `${detail} (automatic restart disabled)`\n this.log(entry, 'system', entry.lastError)\n this.options.history.record(entry.config.id, {\n type: 'crash',\n detail: entry.lastError,\n runtimeMs: ranForMs,\n })\n this.notify(entry, 'crash', entry.lastError)\n }\n\n this.publishServer(entry)\n }\n\n private async tick(): Promise<void> {\n if (this.disposed)\n return\n const now = Date.now()\n\n await this.options.hostMonitor.tick(now)\n await this.sampleResources(now)\n\n // Probes run concurrently: one slow server must not delay the others' health.\n await Promise.allSettled([...this.entries.values()].map(entry => this.probeEntry(entry, now)))\n\n for (const entry of this.entries.values()) {\n if (await this.enforceMemoryLimit(entry))\n continue\n if (this.shouldForceRestart(entry, now)) {\n await this.restart(entry.config.id)\n continue\n }\n if (entry.status === 'backoff' && entry.nextRetryAt !== null && now >= entry.nextRetryAt && entry.retryTimer === null)\n void this.start(entry.config.id, { retry: true })\n }\n\n this.publishState()\n }\n\n /** One scan of /proc covers every server; only live processes are sampled. */\n private async sampleResources(now: number): Promise<void> {\n const due = [...this.entries.values()].filter(entry =>\n entry.pid !== null\n && entry.child !== null\n && now - entry.resourcesSampledAt >= RESOURCE_SAMPLE_INTERVAL_MS)\n if (due.length === 0)\n return\n\n for (const entry of due) entry.resourcesSampledAt = now\n try {\n const samples = await this.sampler.sampleMany(due.map(entry => entry.pid!))\n for (const entry of due) entry.resources = samples.get(entry.pid!) ?? null\n }\n catch {\n // Sampling is best effort; a missing /proc must not break supervision.\n }\n }\n\n private async probeEntry(entry: Entry, now: number): Promise<void> {\n const { port, health } = entry.config\n if (port === null || entry.probing)\n return\n\n entry.probing = true\n try {\n // Occupancy is shown even while stopped, so it keeps its own slower cadence\n // instead of sharing (and being skipped by) the health probe's timer.\n if (now - entry.lastOccupancyProbeAt >= PORT_STATE_INTERVAL_MS) {\n entry.lastOccupancyProbeAt = now\n const accepting = await this.portAccepts(entry, port, health.timeoutMs)\n entry.portState = accepting ? 'in-use' : 'free'\n }\n\n if (entry.status !== 'running' || !health.enabled)\n return\n if (now - entry.lastProbeAt < health.intervalMs)\n return\n\n const probe = await this.probeEntryHealth(entry)\n entry.lastProbeAt = now\n entry.responseMs = probe.ms\n entry.portState = probe.healthy ? 'in-use' : entry.portState\n\n if (probe.healthy) {\n if (entry.health === 'unhealthy') {\n this.log(entry, 'system', `${probe.detail} — healthy again (${probe.ms}ms)`)\n this.options.history.record(entry.config.id, { type: 'recovered', detail: probe.detail })\n this.notify(entry, 'recovered', probe.detail)\n }\n entry.health = 'healthy'\n entry.healthFailures = 0\n entry.unhealthySince = null\n return\n }\n\n entry.healthFailures += 1\n if (entry.healthFailures >= health.unhealthyThreshold) {\n if (entry.unhealthySince === null) {\n entry.unhealthySince = now\n this.log(entry, 'system', `unhealthy: ${probe.detail} (${entry.healthFailures} failed probes) — warning only`)\n this.options.history.record(entry.config.id, { type: 'unhealthy', detail: probe.detail })\n this.notify(entry, 'unhealthy', probe.detail)\n }\n entry.health = 'unhealthy'\n }\n }\n finally {\n entry.probing = false\n }\n }\n\n private async enforceMemoryLimit(entry: Entry): Promise<boolean> {\n const limit = entry.config.resources.maxRssBytes\n const rss = entry.resources?.rssBytes ?? null\n if (limit <= 0 || rss === null || entry.child === null || entry.status !== 'running' || rss <= limit)\n return false\n\n const detail = `process tree uses ${Math.round(rss / 1024 / 1024)}MB, over the ${Math.round(limit / 1024 / 1024)}MB limit`\n this.log(entry, 'system', `${detail} — restarting`)\n this.options.history.record(entry.config.id, { type: 'forced-restart', detail })\n this.notify(entry, 'rss', detail)\n await this.restart(entry.config.id)\n return true\n }\n\n private shouldForceRestart(entry: Entry, now: number): boolean {\n const { port, health } = entry.config\n if (port === null || entry.status !== 'running' || entry.health !== 'unhealthy')\n return false\n if (entry.unhealthySince === null || health.forceRestartAfterMs <= 0)\n return false\n if (now - entry.unhealthySince < health.forceRestartAfterMs)\n return false\n\n this.log(entry, 'system', `unhealthy for ${health.forceRestartAfterMs}ms — forcing a restart`)\n this.options.history.record(entry.config.id, { type: 'forced-restart', detail: `health check stayed unhealthy` })\n this.notify(entry, 'forced-restart', `unhealthy for ${Math.round(health.forceRestartAfterMs / 1000)}s`)\n return true\n }\n\n private clearRetry(entry: Entry): void {\n if (entry.retryTimer !== null) {\n clearTimeout(entry.retryTimer)\n entry.retryTimer = null\n }\n }\n\n private buildVars(entry: Entry): TemplateVars {\n return serverTemplateVars(entry.config)\n }\n\n private view(entry: Entry): ServerView {\n const config = entry.config\n const host = displayHost(config.bind)\n return {\n id: config.id,\n config,\n bindHost: bindHost(config.bind),\n url: config.port === undefined || config.port === null ? null : `http://${host}:${config.port}`,\n status: entry.status,\n health: entry.health,\n portState: entry.portState,\n pid: entry.pid,\n startedAt: entry.startedAt,\n exitCode: entry.exitCode,\n exitSignal: entry.exitSignal,\n restarts: entry.restarts,\n maxRetries: config.restart.maxRetries,\n lastError: entry.lastError,\n nextRetryAt: entry.nextRetryAt,\n unhealthySince: entry.unhealthySince,\n bufferedLines: entry.logs.size,\n history: this.summarizeHistory(entry),\n responseMs: entry.responseMs,\n resources: entry.resources,\n }\n }\n\n private log(entry: Entry, stream: LogStream, text: string): void {\n const line: LogLine = { ts: Date.now(), stream, text }\n entry.logs.push(line)\n this.options.logFiles.append(entry.config.id, line)\n this.hub.publish({ type: 'log', ts: line.ts, serverId: entry.config.id, lines: [line] })\n if (stream === 'system')\n logger.debug(`[${entry.config.id}] ${text}`)\n }\n\n private publishServer(entry: Entry): void {\n if (!this.entries.has(entry.config.id))\n return\n this.hub.publish({\n type: 'server',\n ts: Date.now(),\n serverId: entry.config.id,\n server: this.view(entry),\n })\n }\n\n private publishState(): void {\n const state = this.getState()\n const signature = [\n state.configError ?? '',\n ...state.servers.map(server => [\n server.id,\n server.status,\n server.health,\n server.portState,\n server.pid,\n server.restarts,\n server.nextRetryAt,\n server.lastError,\n server.bufferedLines,\n ].join(':')),\n ].join('|')\n\n if (signature === this.lastStateSignature)\n return\n this.lastStateSignature = signature\n this.hub.publish({ type: 'state', ts: Date.now(), state })\n }\n}\n","import type { BackupFile, BackupPath, BackupsConfig, ServerConfig } from '#src/shared/contracts'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { type } from 'arktype'\nimport { configSchema } from '#src/config/schema'\nimport { writeFileAtomic } from '#src/helpers/atomic'\nimport { expandEnv } from '#src/helpers/env-file'\nimport { dataRoot, projectDir, resolveUserPath } from '#src/helpers/paths'\nimport { resolveTemplate } from '#src/helpers/template'\nimport { createZip, extractZip, isInvalidPassword, isZipArchive, listZip } from '#src/providers/archive'\nimport { serverTemplateVars } from '#src/services/supervisor'\n\nconst MANIFEST = 'manifest.json'\nconst ALLOWED_ROOTS = new Set(['config', 'secrets', 'tls', 'data'])\n/** Every archive this service writes is a zip, encrypted or not. */\nconst SUFFIX = '.zip'\n\n/**\n * Structural allowlist for archive entries. A regex alone is not enough: `..`\n * is made of allowed characters, so segments are checked explicitly and any\n * entry that could resolve outside the staging directory aborts the restore.\n */\nexport function isSafeArchiveEntry(entry: string): boolean {\n if (entry.startsWith('/') || entry.includes('\\0'))\n return false\n\n const cleaned = entry.replace(/^\\.\\//, '').replace(/\\/+$/, '')\n if (cleaned.length === 0)\n return true\n if (cleaned === 'manifest.json')\n return true\n\n const segments = cleaned.split('/')\n if (segments.some(segment => segment.length === 0 || segment === '.' || segment === '..'))\n return false\n if (!ALLOWED_ROOTS.has(segments[0]!))\n return false\n return segments.every(segment => /^[\\w.-]+$/.test(segment))\n}\n\n/** True when `child` is `parent` itself or lives underneath it. */\nexport function isInside(parent: string, child: string): boolean {\n if (parent === child)\n return true\n // `path.relative` is case-insensitive on Windows, and empty for a case-only\n // difference — which is still the same directory.\n const relative = path.relative(parent, child)\n return !path.isAbsolute(relative) && (relative.length === 0 || !relative.startsWith('..'))\n}\n\ninterface DeclaredPath {\n path: string\n origin: string\n depth: number\n order: number\n}\n\n/**\n * Every path a backup should capture: the global list, then each server's\n * `backupPaths` and the values of its `dataEnvs`. A path already covered by a\n * declared parent is reported but not captured, so an entry only has to name\n * the shallowest directory it cares about.\n */\nexport function resolveBackupPaths(servers: ServerConfig[], includePaths: string[] = []): BackupPath[] {\n const declared: DeclaredPath[] = []\n const globalVars = { projectDir, dataRoot, home: os.homedir() }\n\n const add = (value: string, origin: string, vars: Record<string, string | number>): void => {\n if (value.trim().length === 0)\n return\n // Normalized, so a trailing slash or a doubled one cannot defeat the\n // parent/child comparison below.\n const resolved = path.normalize(resolveUserPath(expandEnv(resolveTemplate(value, vars), process.env)))\n declared.push({\n path: resolved,\n origin,\n depth: path.normalize(resolved).split(path.sep).filter(Boolean).length,\n order: declared.length,\n })\n }\n\n for (const value of includePaths) add(value, 'global', globalVars)\n\n for (const config of servers) {\n const vars = serverTemplateVars(config)\n for (const [name, value] of Object.entries(config.dataEnvs)) add(value, `${config.id}:${name}`, vars)\n for (const value of config.backupPaths) add(value, `${config.id}:backupPaths`, vars)\n }\n\n // Shallowest first, so a parent always absorbs its descendants whatever order\n // the config declared them in; ties keep declaration order.\n const sorted = [...declared].sort((a, b) => a.depth - b.depth || a.order - b.order)\n\n return sorted.map((entry, index) => {\n const parent = sorted.slice(0, index).find(candidate => isInside(candidate.path, entry.path))\n if (parent === undefined)\n return { path: entry.path, origin: entry.origin, included: true, note: null }\n const note = parent.path === entry.path\n ? `already declared by ${parent.origin}`\n : `covered by ${parent.path}`\n return { path: entry.path, origin: entry.origin, included: false, note }\n })\n}\n\nexport interface BackupSources {\n configPath: string\n secretsPath: string\n tlsDir: string\n /** Declared paths, resolved, with their origin and inclusion verdict. */\n paths: BackupPath[]\n}\n\nexport interface BackupManifest {\n version: 1\n createdAt: number\n hostname: string\n /** `origin` is what lets a restore land under *this* machine's paths. */\n data: Array<{ slug: string, path: string, origin?: string }>\n}\n\nexport interface RestoreOptions {\n confirm: boolean\n /** Required for, and ignored by, archives that are not password-protected. */\n password?: string\n /** Item ids to restore; omitted means every restorable item. */\n include?: string[]\n}\n\nexport interface RestorePlan {\n dryRun: boolean\n encrypted: boolean\n needsPassword: boolean\n items: Array<{\n id: string\n label: string\n kind: 'config' | 'secrets' | 'tls' | 'data'\n restorable: boolean\n selected: boolean\n note: string | null\n }>\n applied: string[]\n skipped: string[]\n restartRequired: boolean\n /** The panel re-read the restored config in this same run. */\n reloaded: boolean\n error?: string\n}\n\n/** One place a restore may write a data path to, and what declared it. */\ninterface DeclaredTarget {\n path: string\n origin: string\n}\n\n/** The panel's own listener is the only thing a restart is needed for. */\nfunction controlBlock(configText: string | null): unknown {\n try {\n return (JSON.parse(configText ?? '{}') as { control?: unknown }).control ?? null\n }\n catch {\n return null\n }\n}\n\n/** A restored config is only accepted when the full schema can read it. */\nfunction isUsableConfig(text: string | null): boolean {\n if (text === null)\n return false\n try {\n return !(configSchema(JSON.parse(text)) instanceof type.errors)\n }\n catch {\n return false\n }\n}\n\n/**\n * The data paths the archive's own config declares, resolved against *this*\n * machine — so a backup made with `{home}` templates restores under this user's\n * paths, and one restored onto a blank instance brings its servers with it.\n */\nfunction archiveTargets(configText: string | null): DeclaredTarget[] {\n if (configText === null)\n return []\n try {\n const parsed = configSchema(JSON.parse(configText))\n if (parsed instanceof type.errors)\n return []\n const servers: ServerConfig[] = parsed.servers.map(server => ({ ...server, port: server.port ?? null }))\n return resolveBackupPaths(servers, parsed.backups.includePaths)\n .filter(entry => entry.included)\n .map(entry => ({ path: entry.path, origin: entry.origin }))\n }\n catch {\n return []\n }\n}\n\n/**\n * The manifest is written by us, but an uploaded archive's copy is attacker\n * controlled: never join an unvalidated slug into a path.\n */\nfunction safeSlug(slug: unknown): string | null {\n if (typeof slug !== 'string' || slug.length === 0 || slug.length > 80)\n return null\n if (!/^[\\w.-]+$/.test(slug) || slug === '.' || slug === '..')\n return null\n return slug\n}\n\n/** A flag is valid for one exact file revision, not for the name alone. */\nfunction cacheKey(file: { sizeBytes: number, createdAt: number }): string {\n return `${file.sizeBytes}:${file.createdAt}`\n}\n\n/** Stable, filesystem-safe name for a data path inside the archive. */\nexport function slugifyPath(target: string): string {\n const cleaned = target.replace(/[^A-Z0-9]+/gi, '-').replace(/^-+|-+$/g, '')\n return cleaned.length > 0 ? cleaned.slice(-80) : 'path'\n}\n\n/**\n * Archives of the control plane's own state plus whatever paths the config\n * declares. Two rules keep restore safe: the archive layout is an allowlist, and\n * a data path is only written back when the *current* config still declares it —\n * an uploaded archive can never choose where to write.\n *\n * A backup is always a zip; a password makes it a WinZip-AES one, so the same\n * file opens in any archive manager either way.\n */\nexport class BackupService {\n /**\n * Whether an archive is encrypted is only knowable by reading its central\n * directory, which is async while `list()` is not. The flags are cached here\n * and refreshed in the background, so a state frame stays cheap.\n */\n private readonly flags = new Map<string, { key: string, encrypted: boolean }>()\n private refreshing: Promise<void> | null = null\n\n constructor(\n private readonly options: {\n /** Relative `backups.dir` values resolve against it. */\n dataRoot: string\n getConfig: () => BackupsConfig\n getSources: () => BackupSources\n /** Called after this instance's own config was overwritten by a restore. */\n onConfigRestored?: () => void\n },\n ) {}\n\n /** Reads every archive once, so the first `list()` is already accurate. */\n async warm(): Promise<void> {\n await this.refresh()\n }\n\n get directory(): string {\n return this.resolveDir()\n }\n\n /** Declared paths with their verdict, as the UI shows them. */\n get paths(): BackupPath[] {\n const dir = path.resolve(this.resolveDir())\n return this.options.getSources().paths.map((entry) => {\n // Capturing a directory that contains the archive directory would make the\n // archive contain itself.\n if (isInside(entry.path, dir))\n return { ...entry, included: false, note: 'contains the backup directory' }\n return entry\n })\n }\n\n /** Only what actually goes into a backup. */\n get dataPaths(): string[] {\n return this.paths.filter(entry => entry.included).map(entry => entry.path)\n }\n\n list(): BackupFile[] {\n const files = this.scan()\n for (const file of files) {\n const cached = this.flags.get(file.name)\n if (cached === undefined || cached.key !== cacheKey(file))\n void this.scheduleRefresh()\n }\n\n return files.map((file) => {\n const cached = this.flags.get(file.name)\n return {\n ...file,\n encrypted: cached !== undefined && cached.key === cacheKey(file) ? cached.encrypted : false,\n }\n })\n }\n\n /** Validated absolute path for a download, or null when the name is not a backup. */\n resolve(name: string): string | null {\n if (!/^[A-Z0-9][\\w.-]*$/i.test(name) || name.includes('..'))\n return null\n const file = path.join(this.resolveDir(), name)\n return fs.existsSync(file) ? file : null\n }\n\n /** `password` encrypts the archive; it is never stored anywhere. */\n async create(options: { password?: string } = {}): Promise<{ ok: boolean, file?: BackupFile, error?: string }> {\n const config = this.options.getConfig()\n if (!config.enabled)\n return { ok: false, error: 'backups are disabled' }\n\n const password = options.password !== undefined && options.password.length > 0 ? options.password : null\n\n const dir = this.resolveDir()\n const sources = this.options.getSources()\n const staging = path.join(dir, `.staging-${Date.now()}`)\n const createdAt = Date.now()\n // Milliseconds matter: two backups in the same second must not collide.\n const name = `backup-${new Date(createdAt).toISOString().replace(/[:T]/g, '-').replace(/\\.\\d+Z$/, '')}-${createdAt % 1000}${SUFFIX}`\n const destination = path.join(dir, name)\n\n try {\n fs.mkdirSync(staging, { recursive: true })\n this.copyInto(staging, 'config/servers.config.json', sources.configPath)\n this.copyInto(staging, 'secrets/control-secrets.json', sources.secretsPath)\n this.copyInto(staging, 'tls', sources.tlsDir)\n\n const data: BackupManifest['data'] = []\n for (const declared of this.paths) {\n if (!declared.included || !fs.existsSync(declared.path))\n continue\n const slug = slugifyPath(declared.path)\n if (data.some(entry => entry.slug === slug))\n continue\n this.copyInto(staging, path.join('data', slug), declared.path)\n data.push({ slug, path: declared.path, origin: declared.origin })\n }\n\n const manifest: BackupManifest = { version: 1, createdAt, hostname: os.hostname(), data }\n fs.writeFileSync(path.join(staging, MANIFEST), `${JSON.stringify(manifest, null, 2)}\\n`)\n\n await createZip(staging, destination, password === null ? {} : { password })\n\n fs.rmSync(staging, { recursive: true, force: true })\n this.prune()\n\n const stats = fs.statSync(destination)\n this.flags.set(name, { key: `${stats.size}:${Math.round(stats.mtimeMs)}`, encrypted: password !== null })\n return { ok: true, file: { name, sizeBytes: stats.size, createdAt, encrypted: password !== null } }\n }\n catch (error) {\n fs.rmSync(staging, { recursive: true, force: true })\n fs.rmSync(destination, { force: true })\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n\n remove(name: string): boolean {\n const file = this.resolve(name)\n if (file === null)\n return false\n fs.rmSync(file, { force: true })\n this.flags.delete(name)\n return true\n }\n\n /**\n * Validates an archive, then (unless `confirm` is false) applies whichever of\n * its items were selected. Data paths come from the *current* config, never\n * from the archive's manifest.\n */\n async restore(archivePath: string, options: RestoreOptions): Promise<RestorePlan> {\n const password = options.password !== undefined && options.password.length > 0 ? options.password : null\n const plan: RestorePlan = {\n dryRun: !options.confirm,\n encrypted: false,\n needsPassword: false,\n items: [],\n applied: [],\n skipped: [],\n restartRequired: false,\n reloaded: false,\n }\n\n if (!isZipArchive(archivePath))\n return { ...plan, error: 'the archive is not a home-hosted backup (a zip file was expected)' }\n\n const staging = path.join(this.resolveDir(), `.restore-${Date.now()}`)\n\n try {\n // The central directory is readable without a password, so a backup can be\n // listed and its selection offered before the password is ever entered.\n let entries\n try {\n entries = await listZip(archivePath)\n }\n catch (error) {\n return { ...plan, error: `the archive could not be read: ${error instanceof Error ? error.message : String(error)}` }\n }\n\n plan.encrypted = entries.some(entry => entry.encrypted)\n if (plan.encrypted && password === null)\n return { ...plan, needsPassword: true, error: 'this backup is password-protected' }\n\n if (entries.length === 0)\n return { ...plan, error: 'the archive is empty' }\n if (entries.length > 100_000)\n return { ...plan, error: 'the archive has too many entries' }\n\n const invalid = entries.filter(entry => !isSafeArchiveEntry(entry.name))\n if (invalid.length > 0) {\n return { ...plan, error: `the archive contains unexpected entries (e.g. ${invalid.slice(0, 3).map(entry => entry.name).join(', ')})` }\n }\n\n fs.mkdirSync(staging, { recursive: true })\n const extracted = await extractZip(archivePath, staging, {\n names: entries.map(entry => entry.name),\n ...(password === null ? {} : { password }),\n })\n for (const name of extracted.skipped)\n plan.skipped.push(`${name} (symbolic link, skipped)`)\n\n const manifestPath = path.join(staging, MANIFEST)\n if (!fs.existsSync(manifestPath))\n return { ...plan, error: 'the archive has no manifest' }\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as BackupManifest\n\n const sources = this.options.getSources()\n const configInArchive = path.join(staging, 'config', 'servers.config.json')\n const secretsInArchive = path.join(staging, 'secrets', 'control-secrets.json')\n const tlsInArchive = path.join(staging, 'tls')\n\n const selectedIds = options.include === undefined ? null : new Set(options.include)\n const actions = new Map<string, () => void>()\n\n const addItem = (item: RestorePlan['items'][number], apply: (() => void) | null): void => {\n if (apply === null) {\n plan.items.push({ ...item, selected: false })\n plan.skipped.push(`${item.label}${item.note === null ? '' : ` (${item.note})`}`)\n return\n }\n const selected = selectedIds === null || selectedIds.has(item.id)\n plan.items.push({ ...item, selected })\n if (selected) {\n actions.set(item.id, apply)\n }\n else {\n plan.skipped.push(`${item.label} (not selected)`)\n }\n }\n\n const archivedConfig = fs.existsSync(configInArchive) ? fs.readFileSync(configInArchive, 'utf8') : null\n // A config from an archive replaces the live one, so it has to validate\n // first — otherwise a malformed upload silently removes every server.\n const restoredConfig = isUsableConfig(archivedConfig) ? archivedConfig : null\n if (archivedConfig !== null && restoredConfig === null)\n plan.skipped.push('config/servers.config.json (the archive\\'s config is not valid)')\n if (restoredConfig !== null) {\n addItem({ id: 'config', label: 'config/servers.config.json', kind: 'config', restorable: true, selected: false, note: null }, () => {\n writeFileAtomic(sources.configPath, restoredConfig)\n })\n }\n if (fs.existsSync(secretsInArchive)) {\n const restored = fs.readFileSync(secretsInArchive, 'utf8')\n addItem({ id: 'secrets', label: 'secrets/control-secrets.json', kind: 'secrets', restorable: true, selected: false, note: null }, () => {\n writeFileAtomic(sources.secretsPath, restored, { mode: 0o600 })\n })\n }\n if (fs.existsSync(tlsInArchive)) {\n const files = fs.readdirSync(tlsInArchive).filter(file => fs.statSync(path.join(tlsInArchive, file)).isFile())\n addItem({ id: 'tls', label: 'tls/', kind: 'tls', restorable: true, selected: false, note: null }, () => {\n fs.mkdirSync(sources.tlsDir, { recursive: true })\n for (const file of files) {\n const from = path.join(tlsInArchive, file)\n const mode = file.endsWith('.key.pem') ? { mode: 0o600 } : {}\n writeFileAtomic(path.join(sources.tlsDir, file), fs.readFileSync(from, 'utf8'), mode)\n }\n })\n }\n\n // A data path is written to a path a *config* declares — this instance's, or\n // the one the archive brings. That second source is what makes a blank\n // instance restorable: the backup's own `servers.config.json` names its data\n // directories, so restoring the config restores the whole setup.\n const fromArchive = archiveTargets(restoredConfig)\n const candidates: DeclaredTarget[] = [\n // The restored config wins, because it is the one that will be live.\n ...(actions.has('config') ? fromArchive : []),\n ...this.paths.filter(entry => entry.included).map(entry => ({ path: entry.path, origin: entry.origin })),\n ]\n\n for (const entry of manifest.data ?? []) {\n const target = candidates.find(candidate => entry.origin !== undefined && candidate.origin === entry.origin)\n ?? candidates.find(candidate => candidate.path === entry.path)\n const from = path.join(staging, 'data', safeSlug(entry.slug) ?? slugifyPath(entry.path))\n const common = {\n id: `data:${entry.path}`,\n label: target?.path ?? entry.path,\n kind: 'data' as const,\n restorable: false,\n selected: false,\n note: null,\n }\n\n if (target === undefined) {\n const archiveOnly = fromArchive.some(candidate => candidate.origin === entry.origin)\n addItem({\n ...common,\n note: archiveOnly && !actions.has('config')\n ? 'declared by the backup\\'s config, which is not being restored'\n : 'not declared by this config, nor by the backup',\n }, null)\n continue\n }\n if (!fs.existsSync(from)) {\n addItem({ ...common, note: 'missing from the archive' }, null)\n continue\n }\n\n addItem(\n { ...common, restorable: true, note: target.path === entry.path ? null : `restored from ${entry.path}` },\n () => fs.cpSync(from, target.path, { recursive: true, force: true }),\n )\n }\n\n // The plan has to say whether a restart is needed even in a dry run: only\n // the panel's own listener does, the servers are re-read from the file.\n if (restoredConfig !== null && actions.has('config')) {\n const current = fs.existsSync(sources.configPath) ? fs.readFileSync(sources.configPath, 'utf8') : null\n plan.restartRequired = JSON.stringify(controlBlock(restoredConfig)) !== JSON.stringify(controlBlock(current))\n }\n\n if (!options.confirm) {\n // A dry run reports what *would* happen, so the UI can show the plan\n // and the selection before anything is written.\n plan.applied = [...actions.keys()].map(id => plan.items.find(item => item.id === id)!.label)\n return plan\n }\n\n for (const [id, apply] of actions) {\n apply()\n plan.applied.push(plan.items.find(item => item.id === id)!.label)\n }\n\n if (actions.has('config') && this.options.onConfigRestored !== undefined) {\n plan.reloaded = true\n // The panel re-reads the restored file here, so the servers it declares\n // exist immediately instead of after a restart.\n this.options.onConfigRestored()\n }\n\n return plan\n }\n catch (error) {\n // Extraction happens before anything is written, so a rejected password has\n // changed nothing at all.\n if (isInvalidPassword(error))\n return { ...plan, encrypted: true, needsPassword: true, error: 'the password is wrong' }\n // A restore is not transactional: say what already landed, so a failure\n // cannot look like nothing happened.\n const done = plan.applied.length > 0 ? ` — already applied: ${plan.applied.join(', ')}` : ''\n return { ...plan, error: `${error instanceof Error ? error.message : String(error)}${done}` }\n }\n finally {\n fs.rmSync(staging, { recursive: true, force: true })\n }\n }\n\n /** Newest-first listing, without the encryption flag, which needs a read. */\n private scan(): Array<Omit<BackupFile, 'encrypted'>> {\n const dir = this.resolveDir()\n let names: string[] = []\n try {\n names = fs.readdirSync(dir)\n }\n catch {\n return []\n }\n\n return names\n .filter(name => name.endsWith(SUFFIX))\n .flatMap((name) => {\n try {\n const stats = fs.statSync(path.join(dir, name))\n return [{ name, sizeBytes: stats.size, createdAt: Math.round(stats.mtimeMs) }]\n }\n catch {\n return []\n }\n })\n .sort((a, b) => b.createdAt - a.createdAt)\n }\n\n /** Single-flight: a state frame must never queue a pile of reads. */\n private scheduleRefresh(): Promise<void> {\n this.refreshing ??= this.refresh().finally(() => {\n this.refreshing = null\n })\n return this.refreshing\n }\n\n private async refresh(): Promise<void> {\n const dir = this.resolveDir()\n const listed = this.scan()\n\n for (const file of listed) {\n const key = cacheKey(file)\n if (this.flags.get(file.name)?.key === key)\n continue\n try {\n const entries = await listZip(path.join(dir, file.name))\n this.flags.set(file.name, { key, encrypted: entries.some(entry => entry.encrypted) })\n }\n catch {\n // Unreadable stays unmarked here; restoring it reports the real reason.\n this.flags.set(file.name, { key, encrypted: false })\n }\n }\n\n const present = new Set(listed.map(file => file.name))\n for (const name of [...this.flags.keys()]) {\n if (!present.has(name))\n this.flags.delete(name)\n }\n }\n\n private copyInto(staging: string, relative: string, source: string): void {\n if (!fs.existsSync(source))\n return\n const target = path.join(staging, relative)\n fs.mkdirSync(path.dirname(target), { recursive: true })\n // Never copy the archive directory into itself, however broad a declared\n // path is (`fs.cpSync` would walk it while writing into it).\n const archiveDir = path.resolve(this.resolveDir())\n fs.cpSync(source, target, {\n recursive: true,\n force: true,\n filter: from => !isInside(archiveDir, path.resolve(from)),\n })\n }\n\n private prune(): void {\n const { keep } = this.options.getConfig()\n for (const file of this.list().slice(keep)) this.remove(file.name)\n }\n\n private resolveDir(): string {\n const configured = this.options.getConfig().dir\n return path.isAbsolute(configured) ? configured : path.resolve(this.options.dataRoot, configured)\n }\n}\n","import type { Server } from 'srvx'\nimport type { Bind } from '#src/shared/contracts'\nimport { serve } from 'srvx'\nimport { bindHost, displayHost } from '#src/helpers/bind'\nimport { isPortFree } from '#src/providers/port'\n\nexport interface ControlEndpoint {\n /** Configured bind value: `local` | `lan` | ipv4. */\n host: Bind\n port: number\n /** Address actually bound. */\n bindHost: string\n url: string\n protocol: 'http' | 'https'\n}\n\nexport interface ControlServerOptions {\n /** A thunk, so the app can be built after this server exists. */\n fetch: (request: Request) => Response | Promise<Response>\n /** Read per (re)bind, so a settings change applies without a restart. */\n trustProxy: () => boolean\n /** The uploaded PEM pair, or null for plain http. Read per (re)bind. */\n tls: () => { cert: string, key: string } | null\n}\n\nexport interface RebindResult {\n ok: boolean\n error?: string\n}\n\n/**\n * Owns the control panel's own listener, so the settings page can move it to a\n * new host/port without stopping the supervised servers.\n */\nexport class ControlServer {\n readonly endpoint: ControlEndpoint\n private server: Server | null = null\n\n constructor(\n private readonly options: ControlServerOptions,\n initial: { host: Bind, port: number, tls?: boolean },\n ) {\n this.endpoint = {\n host: initial.host,\n port: initial.port,\n bindHost: bindHost(initial.host),\n url: `${initial.tls ? 'https' : 'http'}://${displayHost(initial.host)}:${initial.port}`,\n protocol: initial.tls ? 'https' : 'http',\n }\n }\n\n get liveHost(): string {\n return this.endpoint.host\n }\n\n get livePort(): number {\n return this.endpoint.port\n }\n\n async start(): Promise<void> {\n await this.listenWithRetry(this.endpoint.host, this.endpoint.port)\n }\n\n /** Re-listens on the same endpoint, e.g. after `trustProxy` changed. */\n async restart(): Promise<RebindResult> {\n const { host, port } = this.endpoint\n await this.close()\n try {\n await this.listenWithRetry(host, port)\n return { ok: true }\n }\n catch (error) {\n return { ok: false, error: `restart failed: ${error instanceof Error ? error.message : String(error)}` }\n }\n }\n\n /**\n * Moves the listener. Preflights the new port, and restores the previous\n * endpoint if the new one refuses to bind — otherwise the panel would become\n * unreachable and need a manual restart.\n */\n async rebind(next: { host: Bind, port: number }): Promise<RebindResult> {\n if (next.host === this.endpoint.host && next.port === this.endpoint.port)\n return { ok: true }\n\n const previous = { host: this.endpoint.host, port: this.endpoint.port }\n if (next.port !== previous.port && !(await isPortFree(next.port))) {\n return { ok: false, error: `port ${next.port} is already in use` }\n }\n\n await this.close()\n try {\n await this.listenWithRetry(next.host, next.port)\n return { ok: true }\n }\n catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n try {\n await this.listenWithRetry(previous.host, previous.port)\n }\n catch {\n // Nothing left to fall back to; the caller surfaces the original error.\n }\n return { ok: false, error: `rebind failed: ${message}` }\n }\n }\n\n async close(force = true): Promise<void> {\n const server = this.server\n this.server = null\n if (!server)\n return\n try {\n await server.close(force)\n }\n catch {\n // Already gone.\n }\n }\n\n /** A just-closed listener can refuse a rebind for a moment, so retry briefly. */\n private async listenWithRetry(host: Bind, port: number, attempts = 3): Promise<void> {\n let lastError: unknown\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n await this.listen(host, port)\n return\n }\n catch (error) {\n lastError = error\n if (attempt < attempts)\n await new Promise(resolve => setTimeout(resolve, 200))\n }\n }\n throw lastError\n }\n\n private async listen(host: Bind, port: number): Promise<void> {\n const tls = this.options.tls()\n const server = serve({\n fetch: this.options.fetch,\n port,\n hostname: bindHost(host),\n trustProxy: this.options.trustProxy(),\n ...(tls === null ? {} : { tls: { cert: tls.cert, key: tls.key } }),\n })\n\n // Without a listener, a failed bind would surface as an unhandled 'error' event.\n const nodeServer = server.node?.server\n const failure = new Promise<Error>((resolve) => {\n nodeServer?.once('error', error => resolve(error as Error))\n })\n\n const outcome = await Promise.race([\n server.ready().then(() => null).catch((error: unknown) => error as Error),\n failure,\n ])\n if (outcome !== null)\n throw outcome\n\n this.server = server\n this.endpoint.host = host\n this.endpoint.port = port\n this.endpoint.bindHost = bindHost(host)\n this.endpoint.protocol = tls === null ? 'http' : 'https'\n this.endpoint.url = `${this.endpoint.protocol}://${displayHost(host)}:${port}`\n }\n}\n","import type { SseMessage } from '#src/shared/contracts'\n\nexport type EventListener = (message: SseMessage) => void\n\nconst ALL = '*'\n\n/** Fan-out for SSE subscribers, optionally scoped to a single server. */\nexport class EventHub {\n private readonly listeners = new Map<string, Set<EventListener>>()\n\n subscribe(serverId: string | null, listener: EventListener): () => void {\n const key = serverId ?? ALL\n const bucket = this.listeners.get(key) ?? new Set<EventListener>()\n bucket.add(listener)\n this.listeners.set(key, bucket)\n\n return () => {\n bucket.delete(listener)\n if (bucket.size === 0)\n this.listeners.delete(key)\n }\n }\n\n publish(message: SseMessage): void {\n this.dispatch(ALL, message)\n if (message.serverId)\n this.dispatch(message.serverId, message)\n }\n\n get subscriberCount(): number {\n let total = 0\n for (const bucket of this.listeners.values()) total += bucket.size\n return total\n }\n\n private dispatch(key: string, message: SseMessage): void {\n const bucket = this.listeners.get(key)\n if (!bucket)\n return\n for (const listener of [...bucket]) {\n try {\n listener(message)\n }\n catch {\n bucket.delete(listener)\n }\n }\n }\n}\n","import type { HistoryEvent, ServerHistory } from '#src/shared/contracts'\nimport fs from 'node:fs'\nimport { writeFileAtomic } from '#src/helpers/atomic'\n\nconst MAX_EVENTS = 5000\nconst SAVE_DEBOUNCE_MS = 2000\nconst EVENTS_IN_VIEW = 8\n\nexport type HistoryEventType = HistoryEvent['type']\n\n/**\n * Bounded event log per server, persisted as JSON so uptime and crash counts\n * survive a restart of the control plane.\n *\n * Uptime is derived from recorded runtimes (each exit stores how long the process\n * was up) rather than from sampling, so it stays accurate without a background\n * poller.\n */\nexport class HistoryStore {\n private events: HistoryEvent[] = []\n private saveTimer: NodeJS.Timeout | null = null\n private loaded = false\n /** Bumped on every record, so readers can cache their summaries. */\n private version = 0\n\n constructor(private readonly file: string) {}\n\n get revision(): number {\n return this.version\n }\n\n load(): void {\n if (this.loaded)\n return\n this.loaded = true\n try {\n const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8')) as { events?: HistoryEvent[] }\n this.events = Array.isArray(parsed.events) ? parsed.events.slice(-MAX_EVENTS) : []\n }\n catch {\n this.events = []\n }\n }\n\n record(serverId: string, event: Omit<HistoryEvent, 'serverId' | 'ts'>, ts = Date.now()): void {\n this.load()\n this.events.push({ serverId, ts, ...event })\n this.version += 1\n if (this.events.length > MAX_EVENTS)\n this.events.splice(0, this.events.length - MAX_EVENTS)\n this.scheduleSave()\n }\n\n all(): HistoryEvent[] {\n this.load()\n return [...this.events]\n }\n\n /** `runningSince` adds the in-flight up-interval so a long-running server shows its real ratio. */\n summarize(serverId: string, windowMs: number, now = Date.now(), runningSince: number | null = null): ServerHistory {\n this.load()\n const since = now - windowMs\n const mine = this.events.filter(event => event.serverId === serverId)\n const recent = mine.filter(event => event.ts >= since)\n\n let upMs = 0\n for (const event of recent) {\n if (event.runtimeMs !== undefined)\n upMs += Math.min(event.runtimeMs, windowMs)\n }\n if (runningSince !== null)\n upMs += Math.max(0, now - Math.max(runningSince, since))\n\n const lastCrash = [...mine].reverse().find(event => event.type === 'crash')\n const lastExit = [...mine].reverse().find(event => event.type === 'exit' || event.type === 'crash')\n\n return {\n windowMs,\n uptimeRatio: mine.length === 0 ? null : Math.max(0, Math.min(1, upMs / windowMs)),\n restarts: recent.filter(event => event.type === 'start').length,\n crashes: recent.filter(event => event.type === 'crash').length,\n forcedRestarts: recent.filter(event => event.type === 'forced-restart').length,\n lastCrashAt: lastCrash?.ts ?? null,\n lastExitAt: lastExit?.ts ?? null,\n lastRuntimeMs: lastExit?.runtimeMs ?? null,\n events: mine.slice(-EVENTS_IN_VIEW),\n }\n }\n\n dispose(): void {\n if (this.saveTimer !== null)\n clearTimeout(this.saveTimer)\n this.saveTimer = null\n this.save()\n }\n\n private scheduleSave(): void {\n if (this.saveTimer !== null)\n return\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null\n this.save()\n }, SAVE_DEBOUNCE_MS)\n this.saveTimer.unref()\n }\n\n private save(): void {\n try {\n writeFileAtomic(this.file, `${JSON.stringify({ version: 1, events: this.events })}\\n`)\n }\n catch {\n // History is best-effort; never let it break supervision.\n }\n }\n}\n","import type { HostConfig, HostView } from '#src/shared/contracts'\nimport { execFile } from 'node:child_process'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\n/** Swap usage per platform: /proc on Linux, sysctl on macOS, CIM on Windows. */\nasync function swapUsedPercent(): Promise<number> {\n if (process.platform === 'linux')\n return 0 // filled by memoryInfo below\n\n if (process.platform === 'darwin') {\n try {\n const { stdout } = await execFileAsync('sysctl', ['-n', 'vm.swapusage'], { timeout: 3000 })\n const total = /total\\s*=\\s*([\\d.]+)M/.exec(stdout)?.[1]\n const used = /used\\s*=\\s*([\\d.]+)M/.exec(stdout)?.[1]\n const totalMb = Number.parseFloat(total ?? '0')\n const usedMb = Number.parseFloat(used ?? '0')\n return totalMb > 0 ? (usedMb / totalMb) * 100 : 0\n }\n catch {\n return 0\n }\n }\n\n if (process.platform === 'win32') {\n try {\n const script = 'Get-CimInstance Win32_PageFileUsage | Select-Object AllocatedBaseSize,CurrentUsage | ConvertTo-Csv -NoTypeInformation'\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { timeout: 5000 })\n const line = stdout.split(/\\r?\\n/).slice(1).find(entry => entry.trim().length > 0)\n const cells = (line ?? '').split(',').map(entry => entry.replace(/\"/g, '').trim())\n const totalMb = Number.parseFloat(cells[0] ?? '0')\n const usedMb = Number.parseFloat(cells[1] ?? '0')\n return totalMb > 0 ? (usedMb / totalMb) * 100 : 0\n }\n catch {\n return 0\n }\n }\n\n return 0\n}\n\n/** `/proc/meminfo` counts cache as available, which `os.freemem()` does not. */\nexport function memoryInfo(): { memoryUsedPercent: number, swapUsedPercent: number } {\n try {\n const info = fs.readFileSync('/proc/meminfo', 'utf8')\n const read = (key: string): number => Number.parseInt(new RegExp(`^${key}:\\\\s+(\\\\d+)`, 'm').exec(info)?.[1] ?? '0', 10)\n const total = read('MemTotal')\n const available = read('MemAvailable')\n const swapTotal = read('SwapTotal')\n const swapFree = read('SwapFree')\n\n return {\n memoryUsedPercent: total > 0 ? ((total - available) / total) * 100 : 0,\n swapUsedPercent: swapTotal > 0 ? ((swapTotal - swapFree) / swapTotal) * 100 : 0,\n }\n }\n catch {\n const total = os.totalmem()\n const free = os.freemem()\n return { memoryUsedPercent: total > 0 ? ((total - free) / total) * 100 : 0, swapUsedPercent: 0 }\n }\n}\n\n/**\n * Best-effort CPU temperature from Linux thermal zones / hwmon. macOS and\n * Windows expose no unprivileged sensor, so those platforms report null and the\n * UI simply hides the reading.\n */\nexport function cpuTemperature(): number | null {\n const readings: number[] = []\n\n const inspect = (file: string): void => {\n try {\n const raw = Number.parseInt(fs.readFileSync(file, 'utf8').trim(), 10)\n if (!Number.isFinite(raw))\n return\n const celsius = raw / 1000 // both interfaces report millidegrees\n if (celsius > 0 && celsius < 150)\n readings.push(celsius)\n }\n catch {\n // Absent on this machine.\n }\n }\n\n try {\n for (const zone of fs.readdirSync('/sys/class/thermal')) {\n if (zone.startsWith('thermal_zone'))\n inspect(path.join('/sys/class/thermal', zone, 'temp'))\n }\n }\n catch {\n // No thermal class.\n }\n\n try {\n for (const hwmon of fs.readdirSync('/sys/class/hwmon')) {\n const dir = path.join('/sys/class/hwmon', hwmon)\n for (const entry of fs.readdirSync(dir)) {\n if (/^temp\\d+_input$/.test(entry))\n inspect(path.join(dir, entry))\n }\n }\n }\n catch {\n // No hwmon.\n }\n\n return readings.length > 0 ? Math.max(...readings) : null\n}\n\nasync function diskUsage(target: string): Promise<HostView['disks'][number] | null> {\n try {\n const stats = await fs.promises.statfs(target)\n const totalBytes = stats.blocks * stats.bsize\n const freeBytes = stats.bavail * stats.bsize\n return {\n path: target,\n totalBytes,\n freeBytes,\n usedPercent: totalBytes > 0 ? ((totalBytes - freeBytes) / totalBytes) * 100 : 0,\n }\n }\n catch {\n return null\n }\n}\n\n/**\n * Samples the machine itself: the failures a home server actually dies from are\n * a full disk, exhausted memory or a runaway load — none of which a port probe\n * can see.\n */\nexport async function sampleHost(config: HostConfig, resolvePath: (target: string) => string): Promise<HostView> {\n const cpus = os.cpus().length || 1\n const loadAvg = os.loadavg()\n const memory = memoryInfo()\n // `os.loadavg()` is always zero on Windows, so per-cpu load would alert forever.\n if (process.platform === 'win32')\n loadAvg.fill(0)\n if (memory.swapUsedPercent === 0 && process.platform !== 'linux') {\n memory.swapUsedPercent = await swapUsedPercent()\n }\n const tempCelsius = cpuTemperature()\n\n const disks = (await Promise.all(config.diskPaths.map(entry => diskUsage(resolvePath(entry))))).filter(\n (disk): disk is HostView['disks'][number] => disk !== null,\n )\n\n const alerts: string[] = []\n for (const disk of disks) {\n if (config.diskUsedPercent > 0 && disk.usedPercent >= config.diskUsedPercent) {\n alerts.push(`disk ${disk.path} is ${disk.usedPercent.toFixed(1)}% full`)\n }\n }\n if (config.memoryUsedPercent > 0 && memory.memoryUsedPercent >= config.memoryUsedPercent) {\n alerts.push(`memory is ${memory.memoryUsedPercent.toFixed(1)}% used`)\n }\n if (config.swapUsedPercent > 0 && memory.swapUsedPercent >= config.swapUsedPercent) {\n alerts.push(`swap is ${memory.swapUsedPercent.toFixed(1)}% used`)\n }\n const loadPerCpu = Number(loadAvg[0] ?? 0) / cpus\n if (config.loadPerCpu > 0 && loadPerCpu >= config.loadPerCpu) {\n alerts.push(`load ${loadPerCpu.toFixed(2)}/cpu exceeds ${config.loadPerCpu}`)\n }\n if (tempCelsius !== null && config.tempCelsius > 0 && tempCelsius >= config.tempCelsius) {\n alerts.push(`cpu temperature is ${tempCelsius.toFixed(0)}°C`)\n }\n\n return {\n enabled: config.enabled,\n cpus,\n loadAvg: [...loadAvg],\n uptimeMs: os.uptime() * 1000,\n memoryUsedPercent: memory.memoryUsedPercent,\n swapUsedPercent: memory.swapUsedPercent,\n tempCelsius,\n disks,\n alerts,\n sampledAt: Date.now(),\n }\n}\n\nexport function emptyHostView(config: HostConfig): HostView {\n return {\n enabled: config.enabled,\n cpus: os.cpus().length || 1,\n loadAvg: [0, 0, 0],\n uptimeMs: os.uptime() * 1000,\n memoryUsedPercent: 0,\n swapUsedPercent: 0,\n tempCelsius: null,\n disks: [],\n alerts: [],\n sampledAt: null,\n }\n}\n","import type { NotificationService } from '#src/services/notifications'\nimport type { HostConfig, HostView } from '#src/shared/contracts'\nimport { emptyHostView, sampleHost } from '#src/providers/host'\n\n/**\n * Samples host vitals on their own (slower) interval and turns threshold\n * breaches into one notification per transition, not one per sample.\n */\nexport class HostMonitor {\n private current: HostView\n private lastSampleAt = 0\n private alerting = false\n\n constructor(\n private readonly getConfig: () => HostConfig,\n private readonly resolvePath: (target: string) => string,\n private readonly notifications: NotificationService,\n ) {\n this.current = emptyHostView(getConfig())\n }\n\n get view(): HostView {\n return this.current\n }\n\n /** Cheap when the interval has not elapsed; safe to call every tick. */\n async tick(now = Date.now()): Promise<void> {\n const config = this.getConfig()\n if (!config.enabled) {\n if (this.current.enabled)\n this.current = { ...this.current, enabled: false }\n return\n }\n if (now - this.lastSampleAt < config.intervalMs)\n return\n\n this.lastSampleAt = now\n this.current = await sampleHost(config, this.resolvePath)\n\n if (this.current.alerts.length > 0) {\n if (!this.alerting) {\n this.alerting = true\n this.notifications.notify({\n serverId: 'host',\n label: 'Host',\n reason: 'host',\n detail: this.current.alerts.join('; '),\n })\n }\n return\n }\n\n if (this.alerting) {\n this.alerting = false\n this.notifications.notify({\n serverId: 'host',\n label: 'Host',\n reason: 'host-recovered',\n detail: 'every host threshold is back to normal',\n })\n }\n }\n}\n","import type { LogLine, LogsConfig } from '#src/shared/contracts'\nimport { Buffer } from 'node:buffer'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * Append-only JSONL per server, one line per log entry, rotated by size.\n *\n * JSONL keeps the tail readable without parsing a stream, and appending needs no\n * rewrite of the existing file. Writes are batched on a short timer so a chatty\n * child cannot turn into a syscall per line.\n */\nconst FLUSH_INTERVAL_MS = 250\nconst MAX_PENDING_LINES = 500\nconst READ_CHUNK_BYTES = 256 * 1024\n\nexport interface LogFileInfo {\n name: string\n sizeBytes: number\n}\n\nexport class LogFiles {\n private readonly pending = new Map<string, LogLine[]>()\n private timer: NodeJS.Timeout | null = null\n private closed = false\n\n constructor(\n private readonly dir: string,\n private readonly getConfig: () => LogsConfig,\n ) {}\n\n get directory(): string {\n return this.dir\n }\n\n append(serverId: string, line: LogLine): void {\n if (this.closed || !this.getConfig().persist)\n return\n\n const bucket = this.pending.get(serverId) ?? []\n bucket.push(line)\n this.pending.set(serverId, bucket)\n\n if (bucket.length >= MAX_PENDING_LINES) {\n this.flush()\n return\n }\n this.timer ??= setTimeout(() => {\n this.timer = null\n this.flush()\n }, FLUSH_INTERVAL_MS)\n this.timer.unref()\n }\n\n flush(): void {\n if (this.pending.size === 0)\n return\n\n const batches = [...this.pending.entries()]\n this.pending.clear()\n\n for (const [serverId, lines] of batches) {\n try {\n this.write(serverId, lines)\n }\n catch {\n // Logging must never take the control plane down.\n }\n }\n }\n\n info(serverId: string): { enabled: boolean, sizeBytes: number, files: LogFileInfo[] } {\n const config = this.getConfig()\n const files: LogFileInfo[] = []\n let sizeBytes = 0\n\n for (const file of this.rotateTargets(serverId)) {\n try {\n const stats = fs.statSync(file)\n files.push({ name: path.basename(file), sizeBytes: stats.size })\n if (file === this.currentPath(serverId))\n sizeBytes = stats.size\n }\n catch {\n // Not rotated there yet.\n }\n }\n\n return { enabled: config.persist, sizeBytes, files }\n }\n\n /** Reads the last `tail` lines, newest file first, padding from one rotation back. */\n readTail(serverId: string, tail: number): LogLine[] {\n const sources = [this.currentPath(serverId), this.rotatedPath(serverId, 1)]\n const lines: LogLine[] = []\n\n for (const file of sources) {\n if (lines.length >= tail)\n break\n const chunk = this.readTailChunk(file, tail - lines.length)\n lines.unshift(...chunk)\n }\n\n return lines.slice(-tail)\n }\n\n clear(serverId: string): void {\n for (const file of this.rotateTargets(serverId)) {\n try {\n fs.rmSync(file, { force: true })\n }\n catch {\n // Nothing to remove.\n }\n }\n }\n\n dispose(): void {\n this.closed = true\n if (this.timer !== null)\n clearTimeout(this.timer)\n this.timer = null\n this.flush()\n }\n\n private write(serverId: string, lines: LogLine[]): void {\n const { maxBytes } = this.getConfig()\n const file = this.currentPath(serverId)\n fs.mkdirSync(this.dir, { recursive: true })\n\n // A batch is split at the size limit: writing it whole would sail past\n // maxBytes long before the next rotation check runs.\n let encoded: string[] = []\n let bytes = 0\n\n const commit = (): void => {\n if (encoded.length === 0)\n return\n const payload = `${encoded.join('\\n')}\\n`\n const currentSize = fs.existsSync(file) ? fs.statSync(file).size : 0\n if (currentSize + Buffer.byteLength(payload) > maxBytes)\n this.rotate(serverId)\n fs.appendFileSync(this.currentPath(serverId), payload)\n encoded = []\n bytes = 0\n }\n\n for (const line of lines) {\n const json = JSON.stringify(line)\n const size = Buffer.byteLength(json) + 1\n if (bytes > 0 && bytes + size > maxBytes)\n commit()\n encoded.push(json)\n bytes += size\n }\n\n commit()\n }\n\n private rotate(serverId: string): void {\n const { keep } = this.getConfig()\n for (let index = keep - 1; index >= 1; index--) {\n const from = this.rotatedPath(serverId, index)\n if (!fs.existsSync(from))\n continue\n fs.renameSync(from, this.rotatedPath(serverId, index + 1))\n }\n if (fs.existsSync(this.currentPath(serverId))) {\n fs.renameSync(this.currentPath(serverId), this.rotatedPath(serverId, 1))\n }\n }\n\n private readTailChunk(file: string, tail: number): LogLine[] {\n let handle: number\n try {\n handle = fs.openSync(file, 'r')\n }\n catch {\n return []\n }\n\n try {\n const size = fs.fstatSync(handle).size\n const length = Math.min(size, READ_CHUNK_BYTES)\n const buffer = Buffer.alloc(length)\n fs.readSync(handle, buffer, 0, length, size - length)\n\n const text = buffer.toString('utf8')\n // A mid-file cut can leave a partial first line, which is dropped.\n const raw = text.split('\\n').filter(entry => entry.trim().length > 0)\n const parsed: LogLine[] = []\n for (const entry of raw.slice(size > length ? 1 : 0)) {\n try {\n parsed.push(JSON.parse(entry) as LogLine)\n }\n catch {\n // Partial line from a rotation boundary.\n }\n }\n return parsed.slice(-tail)\n }\n finally {\n fs.closeSync(handle)\n }\n }\n\n private currentPath(serverId: string): string {\n return path.join(this.dir, `${serverId}.log`)\n }\n\n private rotatedPath(serverId: string, index: number): string {\n return path.join(this.dir, `${serverId}.log.${index}`)\n }\n\n private rotateTargets(serverId: string): string[] {\n const { keep } = this.getConfig()\n const targets = [this.currentPath(serverId)]\n for (let index = 1; index <= keep; index++) targets.push(this.rotatedPath(serverId, index))\n return targets\n }\n}\n","import type { SecretsStore } from '#src/config/secrets'\nimport type { LogsConfig, NotificationsConfig, TelegramStatus } from '#src/shared/contracts'\nimport { logger } from '#src/helpers/logger'\nimport { formatTelegramMessage, listTelegramChats, sendTelegramMessage, verifyTelegramToken } from '#src/providers/telegram'\n\nexport type NotificationReason = 'crash' | 'unhealthy' | 'forced-restart' | 'recovered' | 'rss' | 'host' | 'host-recovered'\n\nexport interface NotificationEvent {\n serverId: string\n label: string\n reason: NotificationReason\n detail: string\n}\n\nconst REASON_LABEL: Record<NotificationReason, string> = {\n 'crash': 'gave up restarting',\n 'unhealthy': 'health check failing',\n 'forced-restart': 'force restarted',\n 'recovered': 'recovered',\n 'rss': 'exceeded its memory limit',\n 'host': 'host thresholds breached',\n 'host-recovered': 'host thresholds recovered',\n}\n\nconst TITLE: Record<NotificationReason, string> = {\n 'crash': '🔴 server down',\n 'unhealthy': '🟠 server unhealthy',\n 'forced-restart': '🔁 server force restarted',\n 'recovered': '🟢 server recovered',\n 'rss': '🔴 server over its memory limit',\n 'host': '🟠 host warning',\n 'host-recovered': '🟢 host recovered',\n}\n\n/**\n * Fans supervision events out to notification transports.\n *\n * Telegram is the only transport so far. The bot token never leaves the secrets\n * file, and every send is rate-limited per server *and* reason so a flapping\n * server cannot flood the chat.\n */\nexport class NotificationService {\n private readonly cooldowns = new Map<string, number>()\n private lastResult: string | null = null\n private lastResultAt: number | null = null\n\n constructor(\n private readonly secrets: SecretsStore,\n private readonly getConfig: () => NotificationsConfig,\n private readonly getLogsConfig: () => LogsConfig,\n ) {}\n\n get telegramTokenSet(): boolean {\n return this.secrets.telegramTokenSet\n }\n\n status(): TelegramStatus {\n const telegram = this.getConfig().telegram\n return {\n enabled: telegram.enabled,\n tokenSet: this.secrets.telegramTokenSet,\n chatId: telegram.chatId,\n onCrash: telegram.onCrash,\n onUnhealthy: telegram.onUnhealthy,\n onForcedRestart: telegram.onForcedRestart,\n onRecovered: telegram.onRecovered,\n onHost: telegram.onHost,\n cooldownMs: telegram.cooldownMs,\n lastResult: this.lastResult,\n lastResultAt: this.lastResultAt,\n }\n }\n\n /** Enabled for this reason *and* outside its cooldown window. */\n shouldNotify(event: NotificationEvent, now = Date.now()): boolean {\n const telegram = this.getConfig().telegram\n if (!telegram.enabled)\n return false\n\n const reasonEnabled = {\n 'crash': telegram.onCrash,\n 'unhealthy': telegram.onUnhealthy,\n 'forced-restart': telegram.onForcedRestart,\n 'recovered': telegram.onRecovered,\n 'rss': telegram.onCrash,\n 'host': telegram.onHost,\n 'host-recovered': telegram.onHost,\n }[event.reason]\n if (!reasonEnabled)\n return false\n\n const until = this.cooldowns.get(`${event.serverId}:${event.reason}`) ?? 0\n return !(telegram.cooldownMs > 0 && until > now)\n }\n\n /** Starts the cooldown window for this event, so a flapping server stays quiet. */\n markSent(event: NotificationEvent, now = Date.now()): void {\n this.cooldowns.set(`${event.serverId}:${event.reason}`, now + this.getConfig().telegram.cooldownMs)\n }\n\n /** Fire-and-forget by design: supervision must never wait on a chat API. */\n notify(event: NotificationEvent): void {\n void this.dispatch(event).catch((error: unknown) => {\n logger.warn(`notification failed: ${error instanceof Error ? error.message : String(error)}`)\n })\n }\n\n async dispatch(event: NotificationEvent): Promise<boolean> {\n // `shouldNotify` owns the toggles and the cooldown, so the policy lives once.\n if (!this.shouldNotify(event))\n return false\n this.markSent(event)\n\n return this.sendTelegram(\n formatTelegramMessage(TITLE[event.reason], [\n `${event.label} (${event.serverId}) ${REASON_LABEL[event.reason]}`,\n event.detail,\n ]),\n )\n }\n\n /** Used by the \"send test\" button in settings. */\n async sendTest(overrides: { botToken?: string, chatId?: string } = {}): Promise<{ ok: boolean, error?: string }> {\n const chatId = overrides.chatId ?? this.getConfig().telegram.chatId\n if (chatId.length === 0)\n return { ok: false, error: 'no chat id configured' }\n\n const token = this.resolveToken(overrides.botToken)\n if (token === null)\n return { ok: false, error: 'no bot token configured' }\n\n const result = await sendTelegramMessage(\n token,\n chatId,\n formatTelegramMessage('✅ home-hosted-2 test', ['notifications are wired up correctly']),\n )\n this.remember(result.ok ? 'test message sent' : result.error ?? 'test failed')\n return result\n }\n\n async detectChats(overrides: { botToken?: string } = {}): Promise<{ ok: boolean, chats: Array<{ id: number | string, title: string }>, error?: string }> {\n const token = this.resolveToken(overrides.botToken)\n if (token === null)\n return { ok: false, chats: [], error: 'no bot token configured' }\n\n const result = await listTelegramChats(token)\n this.remember(result.ok ? `${result.chats.length} chat(s) found` : result.error ?? 'detect failed')\n return result\n }\n\n /** Verifies a token without sending anything. */\n async verifyToken(token: string): Promise<{ ok: boolean, username?: string, error?: string }> {\n return verifyTelegramToken(token)\n }\n\n private resolveToken(tokenOverride?: string): string | null {\n const token = tokenOverride?.trim() ?? this.secrets.telegramToken ?? ''\n return token.length > 0 ? token : null\n }\n\n private async sendTelegram(html: string): Promise<boolean> {\n const telegram = this.getConfig().telegram\n if (telegram.chatId.length === 0) {\n this.remember('no chat id configured')\n return false\n }\n\n const token = this.resolveToken()\n if (token === null) {\n this.remember('no bot token configured')\n return false\n }\n\n const result = await sendTelegramMessage(token, telegram.chatId, html)\n this.remember(result.ok ? 'sent' : result.error ?? 'send failed')\n return result.ok\n }\n\n private remember(message: string): void {\n this.lastResult = message\n this.lastResultAt = Date.now()\n }\n}\n","import type { TlsStatus } from '#src/shared/contracts'\nimport { Buffer } from 'node:buffer'\nimport { createPrivateKey, createPublicKey, X509Certificate } from 'node:crypto'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { writeFileAtomic } from '#src/helpers/atomic'\n\n/**\n * Stores an uploaded PEM pair and reports what it contains.\n *\n * The key is written 0600 and both files stay out of git. Nothing here binds a\n * socket — the control server reads the pair and hands it to srvx.\n */\nexport class TlsStore {\n private cached: { cert: string, key: string } | null = null\n private cachedMtime = ''\n\n constructor(private readonly dir: string) {}\n\n get directory(): string {\n return this.dir\n }\n\n get certPath(): string {\n return path.join(this.dir, 'control.crt.pem')\n }\n\n get keyPath(): string {\n return path.join(this.dir, 'control.key.pem')\n }\n\n get present(): boolean {\n return fs.existsSync(this.certPath) && fs.existsSync(this.keyPath)\n }\n\n /** Returns the PEM pair, re-read when the files change on disk. */\n load(): { cert: string, key: string } | null {\n if (!this.present) {\n this.cached = null\n return null\n }\n\n const key = [this.certPath, this.keyPath].map((file) => {\n try {\n return `${fs.statSync(file).mtimeMs}`\n }\n catch {\n return 'x'\n }\n }).join(':')\n\n if (this.cached !== null && key === this.cachedMtime)\n return this.cached\n\n try {\n this.cached = {\n cert: fs.readFileSync(this.certPath, 'utf8'),\n key: fs.readFileSync(this.keyPath, 'utf8'),\n }\n this.cachedMtime = key\n return this.cached\n }\n catch {\n this.cached = null\n return null\n }\n }\n\n save(certificate: string, privateKey: string): { ok: boolean, error?: string } {\n const validation = validatePair(certificate, privateKey)\n if (!validation.ok)\n return { ok: false, error: validation.error }\n\n fs.mkdirSync(this.dir, { recursive: true })\n writeFileAtomic(this.certPath, `${certificate.trimEnd()}\\n`)\n writeFileAtomic(this.keyPath, `${privateKey.trimEnd()}\\n`, { mode: 0o600 })\n this.cached = null\n this.cachedMtime = ''\n return { ok: true }\n }\n\n clear(): void {\n for (const file of [this.certPath, this.keyPath]) {\n try {\n fs.rmSync(file, { force: true })\n }\n catch {\n // Nothing to remove.\n }\n }\n this.cached = null\n }\n\n status(enabled: boolean): TlsStatus {\n const base: TlsStatus = {\n enabled,\n certPresent: this.present,\n subject: null,\n issuer: null,\n validFrom: null,\n validTo: null,\n daysRemaining: null,\n fingerprint: null,\n keyMatches: null,\n error: null,\n }\n\n if (!this.present) {\n return enabled ? { ...base, error: 'TLS is enabled but no certificate has been uploaded' } : base\n }\n\n const pair = this.load()\n if (pair === null)\n return { ...base, error: 'the stored certificate could not be read' }\n\n try {\n const x509 = new X509Certificate(pair.cert)\n const validTo = new Date(x509.validTo)\n const daysRemaining = Math.floor((validTo.getTime() - Date.now()) / 86_400_000)\n return {\n ...base,\n subject: x509.subject.replace(/\\n/g, ', '),\n issuer: x509.issuer.replace(/\\n/g, ', '),\n validFrom: new Date(x509.validFrom).toISOString(),\n validTo: validTo.toISOString(),\n daysRemaining,\n fingerprint: x509.fingerprint256,\n keyMatches: validatePair(pair.cert, pair.key).ok,\n error: daysRemaining < 0 ? 'the certificate has expired' : null,\n }\n }\n catch (error) {\n return { ...base, error: `invalid certificate: ${error instanceof Error ? error.message : String(error)}` }\n }\n }\n}\n\n/** Checks the certificate parses, is time-valid, and matches the private key. */\nexport function validatePair(certificate: string, privateKey: string): { ok: boolean, error?: string } {\n let x509: X509Certificate\n try {\n x509 = new X509Certificate(certificate)\n }\n catch (error) {\n return { ok: false, error: `certificate is not a valid PEM: ${error instanceof Error ? error.message : String(error)}` }\n }\n\n try {\n const key = createPrivateKey(privateKey)\n const fromKey = createPublicKey(key).export({ type: 'spki', format: 'der' })\n const fromCert = x509.publicKey.export({ type: 'spki', format: 'der' })\n if (!Buffer.from(fromKey).equals(Buffer.from(fromCert))) {\n return { ok: false, error: 'the private key does not match the certificate' }\n }\n }\n catch (error) {\n return { ok: false, error: `private key is not a valid PEM: ${error instanceof Error ? error.message : String(error)}` }\n }\n\n if (new Date(x509.validTo).getTime() < Date.now()) {\n return { ok: false, error: `the certificate expired on ${x509.validTo}` }\n }\n\n return { ok: true }\n}\n","import type { ArchiveEntry } from '#src/providers/archive'\nimport type { UiMeta, UiStatus } from '#src/shared/contracts'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { type } from 'arktype'\nimport { writeFileAtomic } from '#src/helpers/atomic'\nimport { extractZip, isZipArchive, listZip } from '#src/providers/archive'\nimport { uiMetaSchema } from '#src/shared/contracts'\n\n/**\n * The panel's UI is replaceable: the stock SPA ships in the package, and a user\n * can put their own build in `$HHOSTED_HOME/.ui` — by hand, or by uploading a zip\n * in the settings page. Everything that serves files asks `resolveDir()` per\n * request, so an install (or `home-hosted ui-revert`) applies on the next refresh.\n */\n\nconst META = 'ui.json'\n/** A UI is static files; these caps keep a hostile or accidental archive harmless. */\nconst MAX_ENTRIES = 20_000\nconst MAX_BYTES = 512 * 1024 * 1024\nconst MAX_NAME = 120\n\n/** What a UI author may declare in a root `ui.json` inside their archive. */\nconst manifestSchema = type({\n 'name?': 'string',\n 'version?': 'string',\n})\n\n/**\n * A UI archive is a static site: relative paths only, no traversal, no absolute\n * paths, no drive letters, and an `index.html` to serve. Symlinks are dropped by\n * the extractor.\n */\nexport function isSafeUiEntry(entry: string): boolean {\n if (entry.length === 0 || entry.length > MAX_NAME)\n return false\n if (entry.startsWith('/') || entry.includes('\\\\') || entry.includes('\\0'))\n return false\n\n const cleaned = entry.replace(/^\\.\\//, '').replace(/\\/+$/, '')\n if (cleaned.length === 0)\n return false\n return cleaned.split('/').every(segment => segment !== '' && segment !== '.' && segment !== '..' && !segment.includes(':'))\n}\n\nexport type UiInstallResult\n = | { ok: true, meta: UiMeta }\n | { ok: false, error: string }\n\nexport class UiService {\n constructor(private readonly options: { dataRoot: string, stockDir?: string }) {}\n\n /** `$HHOSTED_HOME/.ui` — the only place a user UI is ever read from. */\n get directory(): string {\n return path.join(this.options.dataRoot, '.ui')\n }\n\n /** True when a user UI is installed and complete. */\n get custom(): boolean {\n return fs.existsSync(path.join(this.directory, 'index.html'))\n }\n\n /** What the panel should serve right now. */\n resolveDir(): string {\n if (this.custom)\n return this.directory\n return this.options.stockDir ?? this.directory\n }\n\n status(): UiStatus {\n return { custom: this.custom, dir: this.directory, meta: this.readMeta() }\n }\n\n readMeta(): UiMeta | null {\n try {\n const parsed = uiMetaSchema(JSON.parse(fs.readFileSync(path.join(this.directory, META), 'utf8')))\n return parsed instanceof type.errors ? null : parsed\n }\n catch {\n return null\n }\n }\n\n /**\n * Installs a UI from a zip. The archive is extracted beside `.ui` and only then\n * swapped in, so a failed upload leaves the previous UI (or the stock one)\n * serving.\n */\n async install(archivePath: string, fallbackName = 'custom-ui'): Promise<UiInstallResult> {\n if (!isZipArchive(archivePath))\n return { ok: false, error: 'the upload is not a zip archive' }\n\n const staging = path.join(this.options.dataRoot, `.ui-staging-${Date.now()}`)\n\n try {\n return await this.stage(archivePath, staging, fallbackName)\n }\n catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n finally {\n fs.rmSync(staging, { recursive: true, force: true })\n }\n }\n\n /** Removes the user UI, putting the stock panel back. */\n revert(): boolean {\n const existed = fs.existsSync(this.directory)\n fs.rmSync(this.directory, { recursive: true, force: true })\n return existed\n }\n\n private async stage(archivePath: string, staging: string, fallbackName: string): Promise<UiInstallResult> {\n let entries: ArchiveEntry[]\n try {\n entries = await listZip(archivePath)\n }\n catch (error) {\n return { ok: false, error: `the archive could not be read: ${error instanceof Error ? error.message : String(error)}` }\n }\n\n if (entries.length === 0)\n return { ok: false, error: 'the archive is empty' }\n if (entries.length > MAX_ENTRIES)\n return { ok: false, error: `the archive has more than ${MAX_ENTRIES} entries` }\n\n const bytes = entries.reduce((total, entry) => total + entry.size, 0)\n if (bytes > MAX_BYTES)\n return { ok: false, error: `the archive is larger than ${Math.round(MAX_BYTES / 1024 / 1024)}MB uncompressed` }\n\n const unusable = entries.find(entry => !isSafeUiEntry(entry.name))\n if (unusable !== undefined)\n return { ok: false, error: `the archive contains an unusable path: ${unusable.name}` }\n\n fs.mkdirSync(staging, { recursive: true })\n await extractZip(archivePath, staging, { names: entries.map(entry => entry.name) })\n\n const root = resolveRoot(staging)\n if (root === null)\n return { ok: false, error: 'the archive has no index.html at its root' }\n\n const manifest = readManifest(root)\n const meta: UiMeta = {\n name: manifest?.name ?? fallbackName,\n version: manifest?.version ?? null,\n uploadedAt: Date.now(),\n files: countFiles(root),\n }\n\n // The old UI moves aside first: renaming onto an existing directory fails.\n const previous = `${this.directory}.previous`\n fs.rmSync(previous, { recursive: true, force: true })\n if (fs.existsSync(this.directory))\n fs.renameSync(this.directory, previous)\n\n try {\n fs.renameSync(root, this.directory)\n writeFileAtomic(path.join(this.directory, META), `${JSON.stringify(meta, null, 2)}\\n`)\n }\n catch (error) {\n fs.rmSync(this.directory, { recursive: true, force: true })\n if (fs.existsSync(previous))\n fs.renameSync(previous, this.directory)\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n finally {\n fs.rmSync(previous, { recursive: true, force: true })\n }\n\n return { ok: true, meta }\n }\n}\n\n/**\n * Where the site actually starts: the archive root, or a single wrapper directory\n * (`zip -r ui.zip dist` is a common way to build one).\n */\nfunction resolveRoot(staging: string): string | null {\n if (fs.existsSync(path.join(staging, 'index.html')))\n return staging\n\n const directories = fs.readdirSync(staging, { withFileTypes: true }).filter(entry => entry.isDirectory())\n if (directories.length !== 1)\n return null\n\n const inner = path.join(staging, directories[0]!.name)\n return fs.existsSync(path.join(inner, 'index.html')) ? inner : null\n}\n\nfunction readManifest(root: string): { name?: string, version?: string } | null {\n try {\n const parsed = manifestSchema(JSON.parse(fs.readFileSync(path.join(root, META), 'utf8')))\n return parsed instanceof type.errors ? null : parsed\n }\n catch {\n return null\n }\n}\n\nfunction countFiles(root: string): number {\n let total = 0\n for (const entry of fs.readdirSync(root, { withFileTypes: true, recursive: true })) {\n if (entry.isFile() && entry.name !== META)\n total += 1\n }\n return Math.max(1, total)\n}\n","import type { AppType } from '#src/app'\nimport type { Runtime } from '#src/helpers/daemon'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { fileURLToPath } from 'node:url'\nimport { createRootApp } from '#src/app'\nimport { SecretsStore } from '#src/config/secrets'\nimport { SEED_CONFIG } from '#src/config/seed'\nimport { ConfigStore } from '#src/config/store'\nimport { clearRuntime, isProcessAlive, newToken, readRuntime, writeRuntime } from '#src/helpers/daemon'\nimport { logger } from '#src/helpers/logger'\nimport { openBrowser } from '#src/helpers/open'\nimport {\n daemonLogPath,\n dataRoot,\n defaultConfigPath,\n defaultHistoryPath,\n defaultLogsDir,\n defaultSecretsPath,\n defaultTlsDir,\n projectDir,\n resolveUserPath,\n} from '#src/helpers/paths'\nimport { resolveTemplate } from '#src/helpers/template'\nimport { isPortFree } from '#src/providers/port'\nimport { AuthService, DEFAULT_PASSWORD } from '#src/services/auth'\nimport { BackupService, resolveBackupPaths } from '#src/services/backups'\nimport { ControlServer } from '#src/services/control-server'\nimport { EventHub } from '#src/services/events'\nimport { checkExposure } from '#src/services/exposure'\nimport { HistoryStore } from '#src/services/history'\nimport { HostMonitor } from '#src/services/host-monitor'\nimport { LogFiles } from '#src/services/log-files'\nimport { NotificationService } from '#src/services/notifications'\nimport { buildAppState } from '#src/services/state'\nimport { Supervisor } from '#src/services/supervisor'\nimport { TlsStore } from '#src/services/tls'\nimport { UiService } from '#src/services/ui'\nimport { parseBind } from '#src/shared/contracts'\n\n/** The package root: one level above this file, whether it is `src/` or `dist/`. */\nexport const packageRoot = path.resolve(fileURLToPath(new URL('..', import.meta.url)))\n\nfunction packageVersion(): string {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8')) as { version?: string }\n return manifest.version ?? '0.0.0'\n }\n catch {\n return '0.0.0'\n }\n}\n\nexport interface ControlPlaneOptions {\n /** `--config`, or `$HHOSTED_HOME/servers.config.json`. */\n configPath?: string\n /** One-off overrides, persisted only after every guard below passes. */\n port?: number\n host?: string\n autostart: boolean\n open: boolean\n /** Print the effective config and exit without starting anything. */\n printConfig: boolean\n}\n\n/** A probe target for a `lan` bind, which is not connectable as `0.0.0.0`. */\nfunction probeHostFor(bindHost: string): string {\n return bindHost === '0.0.0.0' || bindHost === '::' ? '127.0.0.1' : bindHost\n}\n\n/**\n * Runs the control plane in *this* process until it is stopped. `home-hosted up`\n * detaches a child that calls this; `--foreground` (systemd, docker) calls it\n * directly.\n */\nexport async function runControlPlane(options: ControlPlaneOptions): Promise<void> {\n const existing = readRuntime()\n if (existing !== null && existing.pid !== process.pid && isProcessAlive(existing.pid)) {\n logger.error(`already running (pid ${existing.pid}) at ${existing.url} — run \\`home-hosted down\\` first`)\n process.exit(1)\n }\n if (existing !== null)\n clearRuntime()\n\n const configPath = options.configPath ?? defaultConfigPath\n const store = new ConfigStore(configPath, SEED_CONFIG)\n store.load()\n store.writeJsonSchema()\n\n const secrets = new SecretsStore(defaultSecretsPath)\n const auth = new AuthService(secrets, () => store.config.control.auth)\n const tls = new TlsStore(defaultTlsDir)\n const logFiles = new LogFiles(defaultLogsDir, () => store.config.logs)\n const history = new HistoryStore(defaultHistoryPath)\n const notifications = new NotificationService(\n secrets,\n () => store.config.notifications,\n () => store.config.logs,\n )\n const hostMonitor = new HostMonitor(\n () => store.config.host,\n target => resolveUserPath(resolveTemplate(target, { projectDir, dataRoot, home: os.homedir() })),\n notifications,\n )\n // Restoring a backup replaces the config file, which no store write covers: the\n // hook below re-reads it and brings the restored autostart entries up, so a\n // blank instance ends up running the setup the archive carried.\n let onConfigRestored: (() => void) | undefined\n const backups = new BackupService({\n dataRoot,\n getConfig: () => store.config.backups,\n getSources: () => ({\n configPath: store.path,\n secretsPath: secrets.path,\n tlsDir: tls.directory,\n paths: resolveBackupPaths(store.servers, store.config.backups.includePaths),\n }),\n onConfigRestored: () => onConfigRestored?.(),\n })\n\n // Auth is on by default, so a first boot needs *a* password; the default is\n // deliberately weak and flagged, which keeps LAN/exposure binding blocked (and\n // is announced on the login page) until it is changed.\n if (!auth.passwordSet) {\n auth.ensureDefaultPassword(DEFAULT_PASSWORD)\n logger.warn(`no password was set — created the default \"${DEFAULT_PASSWORD}\"; change it in Settings → Password`)\n }\n\n const configured = store.config.control\n const intendedHost = options.host === undefined ? configured.host : parseBind(options.host)\n if (intendedHost === null) {\n logger.error(`invalid control host: ${String(options.host)} (expected local, lan or an ipv4 address)`)\n process.exit(1)\n }\n\n const intended = { host: intendedHost, port: options.port ?? configured.port }\n if (!Number.isInteger(intended.port) || intended.port <= 0 || intended.port > 65535) {\n logger.error(`invalid control port: ${String(options.port)}`)\n process.exit(1)\n }\n\n if (options.printConfig) {\n process.stdout.write(`${JSON.stringify(store.config, null, 2)}\\n`)\n return\n }\n\n // Never serve the panel beyond loopback without a password behind it.\n const exposure = checkExposure({ ...configured, host: intended.host }, auth.passwordSet, auth.usingDefaultPassword)\n if (exposure.blockedReason !== null) {\n logger.error(`refusing to start: ${exposure.blockedReason}`)\n logger.info('bind the panel back to `local`, or set a password with `home-hosted set-password` and enable auth in the settings page')\n process.exit(1)\n }\n\n if (!(await isPortFree(intended.port))) {\n logger.error(`control port ${intended.port} is already in use — is another home-hosted running?`)\n process.exit(1)\n }\n\n if (intended.host !== configured.host || intended.port !== configured.port)\n store.updateControl({ host: intended.host, port: intended.port })\n\n const ui = new UiService({ dataRoot, stockDir: path.join(packageRoot, 'uis', 'stock', 'dist') })\n const hub = new EventHub()\n let app: AppType | undefined\n const token = newToken()\n\n const controlServer = new ControlServer(\n {\n fetch: (request) => {\n if (!app)\n throw new Error('the control app is not ready yet')\n return app.fetch(request)\n },\n trustProxy: () => store.config.control.auth.trustProxy,\n tls: () => (store.config.control.tls.enabled ? tls.load() : null),\n },\n { host: intended.host, port: intended.port, tls: store.config.control.tls.enabled },\n )\n\n const supervisor = new Supervisor(store, hub, {\n configPath: store.path,\n control: controlServer.endpoint,\n buildState: views => buildAppState({\n store,\n auth,\n control: controlServer.endpoint,\n tls,\n notifications,\n hostMonitor,\n backups,\n logsDir: logFiles.directory,\n views,\n }),\n history,\n logFiles,\n notifications,\n hostMonitor,\n })\n\n let shuttingDown = false\n const shutdown = async (reason: string): Promise<void> => {\n if (shuttingDown)\n return\n shuttingDown = true\n logger.info(`${reason} — stopping ${supervisor.views().length} server(s)`)\n clearRuntime()\n auth.dispose()\n await supervisor.dispose()\n logFiles.dispose()\n history.dispose()\n await controlServer.close(true)\n process.exit(0)\n }\n\n app = createRootApp({\n store,\n supervisor,\n hub,\n auth,\n secrets,\n controlServer,\n tls,\n logFiles,\n notifications,\n backups,\n ui,\n runtimeToken: token,\n onShutdown: () => shutdown('shutdown requested locally'),\n })\n\n await controlServer.start()\n // Reads every existing archive once, so the first state frame already shows\n // which backups are password-protected.\n await backups.warm()\n\n const endpoint = controlServer.endpoint\n const runtime: Runtime = {\n version: packageVersion(),\n pid: process.pid,\n url: endpoint.url,\n probeUrl: `${endpoint.protocol}://${probeHostFor(endpoint.bindHost)}:${endpoint.port}`,\n protocol: endpoint.protocol,\n port: endpoint.port,\n bindHost: endpoint.bindHost,\n startedAt: Date.now(),\n projectDir,\n dataRoot,\n configPath: store.path,\n logFile: daemonLogPath,\n token,\n }\n writeRuntime(runtime)\n\n logger.box(`home-hosted ${runtime.version}\\n${endpoint.url}`)\n logger.info(`config: ${store.path}`)\n logger.info(`secrets: ${secrets.path}${auth.passwordSet ? '' : ' (no password set)'}`)\n logger.info(`auth: ${auth.isRequired() ? 'required' : 'disabled'}${auth.usingDefaultPassword ? ' (default password)' : ''}${exposure.exposed ? ' · exposed beyond loopback' : ''}`)\n logger.info(`logs: ${store.config.logs.persist ? `${logFiles.directory} (max ${store.config.logs.maxBytes} B x ${store.config.logs.keep})` : 'memory only'}`)\n logger.info(`project: ${projectDir}`)\n if (ui.custom) {\n const meta = ui.status().meta\n logger.warn(`custom UI in use${meta === null ? '' : ` (${meta.name}${meta.version === null ? '' : ` ${meta.version}`})`} — if it breaks, run \\`home-hosted ui-revert\\``)\n }\n if (store.configError !== null)\n logger.warn(`config problem(s): ${store.configError}`)\n for (const entry of supervisor.views())\n logger.info(` ${entry.id.padEnd(12)} ${entry.config.command} ${entry.config.args.join(' ')}`.trimEnd())\n\n if (configured.openBrowser || options.open)\n openBrowser(endpoint.url)\n\n if (options.autostart) {\n void supervisor.startAll({ autostartOnly: true }).catch((error: unknown) => {\n logger.error('autostart failed', error)\n })\n }\n\n onConfigRestored = () => {\n store.load()\n\n // The restored config is a config write like any other, so the exposure rule\n // applies: a backup taken from a local instance must not open a LAN panel.\n const exposure = checkExposure(store.config.control, auth.passwordSet, auth.usingDefaultPassword)\n if (exposure.blockedReason !== null) {\n logger.warn(`the restored config would expose the panel (${exposure.blockedReason}) — forcing authentication on`)\n store.updateControl({ auth: { enabled: true } })\n }\n\n logger.info(`config restored — ${store.servers.length} server(s) reloaded`)\n // `--no-autostart` means \"do not start anything on your own\", restores included.\n if (!options.autostart)\n return\n void supervisor.startAll({ autostartOnly: true }).catch((error: unknown) => {\n logger.error('could not start the restored servers', error)\n })\n }\n\n process.on('SIGINT', () => void shutdown('SIGINT'))\n process.on('SIGTERM', () => void shutdown('SIGTERM'))\n}\n","import type { ChildProcess } from 'node:child_process'\nimport { spawn, spawnSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport readline from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { parseArgs } from 'node:util'\n\n/**\n * The command line. Everything here works before the state directories are known\n * (so `--home`/`--project` can point them somewhere), which is why the modules\n * that read those paths are imported dynamically instead of at the top.\n */\n\nconst CLI_ENTRY = fileURLToPath(import.meta.url)\nconst DEFAULT_PORT = 3999\nconst LOG_ROTATE_BYTES = 5 * 1024 * 1024\n\nconst USAGE = `home-hosted — a control panel for the processes on your home server\n\nUsage\n home-hosted up [options] start it in the background (detached)\n home-hosted down stop it, and everything it supervises\n home-hosted restart [options] down, then up\n home-hosted status [--json] is it running, where, and how to reach it\n home-hosted set-password set the panel password without the API\n home-hosted ui-revert go back to the stock control panel UI\n\nOptions for up/restart\n -c, --config <file> servers config (default: <state>/servers.config.json)\n -p, --port <port> control panel port (default: ${DEFAULT_PORT})\n --host <bind> local | lan | an ipv4 address (default: local)\n --open open the panel in a browser once it is up\n --no-autostart do not start the entries marked autostart\n --foreground run in this process instead of detaching (systemd/docker)\n --print-config print the effective config and exit\n\nEverywhere\n --home <dir> state directory (default: $HHOSTED_HOME or ~/.home-hosted)\n --project <dir> base for relative entry paths (default: the current directory)\n -h, --help this text\n -v, --version the version\n\nEnvironment\n HHOSTED_HOME where config, secrets, logs, TLS and backups live\n HHOSTED_PROJECT base for relative entry paths\n`\n\nconst isTty = (): boolean => process.stdout.isTTY === true\nconst paint = (code: string, text: string): string => (isTty() ? `\\x1B[${code}m${text}\\x1B[0m` : text)\nconst dim = (text: string): string => paint('2', text)\nconst bold = (text: string): string => paint('1', text)\nconst green = (text: string): string => paint('32', text)\n\nfunction fail(message: string): never {\n process.stderr.write(`${paint('31', 'error')} ${message}\\n`)\n process.exit(1)\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\ninterface DirFlags {\n project?: string\n home?: string\n}\n\n/** `--home`/`--project` are handled for every command, so they are peeled off first. */\nfunction extractDirFlags(argv: string[]): { rest: string[] } & DirFlags {\n const rest: string[] = []\n let project: string | undefined\n let home: string | undefined\n\n for (let index = 0; index < argv.length; index++) {\n const arg = argv[index]!\n const equals = arg.indexOf('=')\n const name = equals === -1 ? arg : arg.slice(0, equals)\n if (name !== '--project' && name !== '--home') {\n rest.push(arg)\n continue\n }\n const value = equals === -1 ? argv[++index] : arg.slice(equals + 1)\n if (value === undefined || value.length === 0)\n fail(`${name} needs a directory`)\n if (name === '--project')\n project = value\n else\n home = value\n }\n\n return { rest, project, home }\n}\n\n/** Set before any state module is imported, so it decides where state lives. */\nfunction applyDirFlags(flags: DirFlags): void {\n if (flags.project !== undefined)\n process.env.HHOSTED_PROJECT = path.resolve(flags.project)\n if (flags.home !== undefined)\n process.env.HHOSTED_HOME = path.resolve(flags.home)\n}\n\ninterface UpFlags {\n config?: string\n port?: number\n host?: string\n autostart: boolean\n open: boolean\n foreground: boolean\n printConfig: boolean\n}\n\nfunction parseUpFlags(argv: string[]): UpFlags {\n const { values } = parseArgs({\n args: argv,\n options: {\n 'config': { type: 'string', short: 'c' },\n 'port': { type: 'string', short: 'p' },\n 'host': { type: 'string' },\n 'no-autostart': { type: 'boolean' },\n 'open': { type: 'boolean' },\n 'foreground': { type: 'boolean' },\n 'print-config': { type: 'boolean' },\n },\n allowPositionals: false,\n })\n\n let port: number | undefined\n if (values.port !== undefined) {\n port = Number.parseInt(values.port, 10)\n if (!Number.isInteger(port) || port <= 0 || port > 65535)\n fail(`invalid port: ${values.port}`)\n }\n\n return {\n config: values.config,\n port,\n host: values.host,\n autostart: values['no-autostart'] !== true,\n open: values.open === true,\n foreground: values.foreground === true,\n printConfig: values['print-config'] === true,\n }\n}\n\n/** The daemon gets the same instructions, but never `--foreground`. */\nfunction daemonFlags(flags: UpFlags): string[] {\n const args: string[] = []\n if (flags.config !== undefined)\n args.push('--config', flags.config)\n if (flags.port !== undefined)\n args.push('--port', String(flags.port))\n if (flags.host !== undefined)\n args.push('--host', flags.host)\n if (!flags.autostart)\n args.push('--no-autostart')\n if (flags.open)\n args.push('--open')\n return args\n}\n\n/**\n * How to run this CLI again in the same runtime. Under tsx that means passing the\n * resolved loader too, because the daemon's working directory is the project's,\n * not the package's.\n */\nfunction runtimeArgs(): string[] {\n let resolved: string | null = null\n const resolveTsx = (): string => resolved ??= import.meta.resolve('tsx')\n\n return process.execArgv.map((arg) => {\n if (arg === 'tsx')\n return resolveTsx()\n if (arg.startsWith('--import=') && arg.slice('--import='.length) === 'tsx')\n return `--import=${resolveTsx()}`\n return arg\n })\n}\n\n/** One rotation is enough for a console log. */\nfunction rotateLog(file: string): void {\n try {\n if (fs.statSync(file).size < LOG_ROTATE_BYTES)\n return\n fs.rmSync(`${file}.1`, { force: true })\n fs.renameSync(file, `${file}.1`)\n }\n catch {\n // no log yet\n }\n}\n\nfunction tailLog(file: string, lines = 15): string {\n try {\n return fs.readFileSync(file, 'utf8').split('\\n').slice(-lines).join('\\n').trimEnd()\n }\n catch {\n return ''\n }\n}\n\nasync function up(argv: string[]): Promise<void> {\n const flags = parseUpFlags(argv)\n const { runControlPlane } = await import('#src/index')\n\n if (flags.foreground) {\n await runControlPlane({\n configPath: flags.config,\n port: flags.port,\n host: flags.host,\n autostart: flags.autostart,\n open: flags.open,\n printConfig: flags.printConfig,\n })\n return\n }\n\n const { clearRuntime, isProcessAlive, readRuntime } = await import('#src/helpers/daemon')\n const { daemonLogPath, dataRoot, projectDir } = await import('#src/helpers/paths')\n\n const existing = readRuntime()\n if (existing !== null && isProcessAlive(existing.pid)) {\n process.stdout.write(`${green('already running')} (pid ${existing.pid}) at ${existing.url}\\n`)\n process.stdout.write(`${dim('stop it with `home-hosted down`')}\\n`)\n return\n }\n if (existing !== null)\n clearRuntime()\n\n fs.mkdirSync(path.dirname(daemonLogPath), { recursive: true })\n rotateLog(daemonLogPath)\n const log = fs.openSync(daemonLogPath, 'a')\n\n const child = spawn(process.execPath, [...runtimeArgs(), CLI_ENTRY, 'up', '--foreground', ...daemonFlags(flags)], {\n detached: true,\n cwd: projectDir,\n env: { ...process.env, HHOSTED_HOME: dataRoot, HHOSTED_PROJECT: projectDir },\n stdio: ['ignore', log, log],\n windowsHide: true,\n })\n child.unref()\n fs.closeSync(log)\n\n const runtime = await waitForStartup(child)\n if (runtime === null) {\n const output = tailLog(daemonLogPath)\n process.stderr.write(`${paint('31', 'error')} the control panel did not start\\n`)\n if (output.length > 0)\n process.stderr.write(`${dim(`${daemonLogPath}:`)}\\n${output}\\n`)\n process.exit(1)\n }\n\n process.stdout.write(`${green('home-hosted is up')} (pid ${runtime.pid})\\n`)\n process.stdout.write(` ${bold(runtime.url)}\\n`)\n process.stdout.write(` ${dim(`project ${runtime.projectDir}`)}\\n`)\n process.stdout.write(` ${dim(`state ${runtime.dataRoot}`)}\\n`)\n process.stdout.write(` ${dim(`log ${runtime.logFile}`)}\\n`)\n}\n\nasync function waitForStartup(child: ChildProcess, timeoutMs = 20000) {\n const { readRuntime } = await import('#src/helpers/daemon')\n const deadline = Date.now() + timeoutMs\n\n for (;;) {\n if (child.exitCode !== null || child.signalCode !== null)\n return null\n\n const runtime = readRuntime()\n if (runtime !== null && runtime.pid === child.pid)\n return runtime\n\n if (Date.now() > deadline)\n return null\n await delay(150)\n }\n}\n\nasync function down(): Promise<void> {\n const { clearRuntime, isProcessAlive, readRuntime, requestShutdown } = await import('#src/helpers/daemon')\n\n const runtime = readRuntime()\n if (runtime === null) {\n process.stdout.write('home-hosted is not running\\n')\n return\n }\n if (!isProcessAlive(runtime.pid)) {\n clearRuntime()\n process.stdout.write('home-hosted is not running (removed a stale run.json)\\n')\n return\n }\n\n process.stdout.write(`stopping pid ${runtime.pid}…\\n`)\n // The panel's own endpoint stops supervised servers cleanly on every platform;\n // a signal is the fallback for a wedged or unreachable process.\n if (!(await requestShutdown(runtime)))\n signal(runtime.pid, 'SIGTERM')\n\n if (await waitForExit(runtime.pid, 20000)) {\n clearRuntime()\n process.stdout.write(`${green('stopped')}\\n`)\n return\n }\n\n process.stdout.write(`${dim('it did not stop in time — forcing')}\\n`)\n forceStop(runtime.pid)\n await waitForExit(runtime.pid, 5000)\n clearRuntime()\n process.stdout.write(`${green('stopped')} (forced)\\n`)\n}\n\nasync function waitForExit(pid: number, timeoutMs: number): Promise<boolean> {\n const { isProcessAlive } = await import('#src/helpers/daemon')\n const deadline = Date.now() + timeoutMs\n\n while (Date.now() < deadline) {\n if (!isProcessAlive(pid))\n return true\n await delay(200)\n }\n return !isProcessAlive(pid)\n}\n\nfunction signal(pid: number, name: NodeJS.Signals): void {\n try {\n process.kill(pid, name)\n }\n catch {\n // already gone\n }\n}\n\n/** Windows cannot deliver a graceful signal, so the whole tree is killed. */\nfunction forceStop(pid: number): void {\n if (process.platform === 'win32') {\n spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { windowsHide: true })\n return\n }\n signal(pid, 'SIGKILL')\n}\n\nasync function restart(argv: string[]): Promise<void> {\n await down()\n await up(argv)\n}\n\nasync function status(argv: string[]): Promise<void> {\n const { values } = parseArgs({\n args: argv,\n options: { json: { type: 'boolean' } },\n allowPositionals: false,\n })\n const { isProcessAlive, probeRuntime, readRuntime } = await import('#src/helpers/daemon')\n const { UiService } = await import('#src/services/ui')\n const { dataRoot } = await import('#src/helpers/paths')\n const runtime = readRuntime()\n\n if (runtime === null) {\n if (values.json)\n process.stdout.write(`${JSON.stringify({ running: false }, null, 2)}\\n`)\n else\n process.stdout.write('home-hosted is not running\\n')\n process.exitCode = 1\n return\n }\n\n const running = isProcessAlive(runtime.pid)\n const probe = running ? await probeRuntime(runtime) : { reachable: false, degraded: false }\n\n if (values.json) {\n // The token is what authorises a local shutdown; a script only needs the rest.\n const { token: _token, ...safe } = runtime\n process.stdout.write(`${JSON.stringify({ running, answering: probe.reachable, degraded: probe.degraded, ...safe }, null, 2)}\\n`)\n if (!running)\n process.exitCode = 1\n return\n }\n\n const uptime = formatDuration(Date.now() - runtime.startedAt)\n const state = !running\n ? paint('31', 'stale (the process is gone)')\n : probe.degraded\n ? paint('33', 'running — a server needs attention')\n : probe.reachable ? green('running') : paint('33', 'running, but not answering')\n\n const ui = new UiService({ dataRoot })\n const rows: Array<[string, string]> = [\n ['status', state],\n ['pid', running ? `${runtime.pid} · up ${uptime}` : String(runtime.pid)],\n ['url', `${runtime.url} ${dim(`(${runtime.protocol})`)}`],\n ['version', runtime.version],\n ['project', runtime.projectDir],\n ['state', runtime.dataRoot],\n ['config', runtime.configPath],\n ['log', runtime.logFile],\n ['ui', ui.custom ? `custom — ${ui.status().meta?.name ?? 'installed'} (revert with \\`home-hosted ui-revert\\`)` : 'stock'],\n ]\n\n process.stdout.write(`${bold(`home-hosted ${runtime.version}`)}\\n`)\n for (const [label, value] of rows)\n process.stdout.write(` ${dim(label.padEnd(8))} ${value}\\n`)\n if (!running)\n process.exitCode = 1\n}\n\nfunction formatDuration(ms: number): string {\n const seconds = Math.max(0, Math.round(ms / 1000))\n if (seconds < 60)\n return `${seconds}s`\n const minutes = Math.floor(seconds / 60)\n if (minutes < 60)\n return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n if (hours < 24)\n return `${hours}h ${minutes % 60}m`\n return `${Math.floor(hours / 24)}d ${hours % 24}h`\n}\n\n/** Reads a line with echo suppressed, so the password never lands in scrollback. */\nfunction promptHidden(question: string): Promise<string> {\n return new Promise((resolve) => {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true })\n const onData = (): void => {\n readline.clearLine(process.stdout, 0)\n readline.cursorTo(process.stdout, 0)\n process.stdout.write(question)\n }\n\n process.stdin.on('data', onData)\n rl.question(question, (answer) => {\n process.stdin.off('data', onData)\n rl.close()\n process.stdout.write('\\n')\n resolve(answer)\n })\n })\n}\n\n/** Drops a user-installed UI so the stock panel serves again. */\nasync function uiRevert(): Promise<void> {\n const { UiService } = await import('#src/services/ui')\n const { dataRoot } = await import('#src/helpers/paths')\n const ui = new UiService({ dataRoot })\n\n if (!ui.custom) {\n process.stdout.write('no custom UI is installed — the stock panel is already in use\\n')\n return\n }\n ui.revert()\n process.stdout.write(`${green('custom UI removed')} — the stock panel is back; refresh the browser\\n`)\n}\n\nasync function setPassword(argv: string[]): Promise<void> {\n const { values } = parseArgs({\n args: argv,\n options: { clear: { type: 'boolean' } },\n allowPositionals: false,\n })\n const { defaultSecretsPath } = await import('#src/helpers/paths')\n const { SecretsStore } = await import('#src/config/secrets')\n const store = new SecretsStore(defaultSecretsPath)\n\n if (values.clear === true) {\n store.clearPassword()\n process.stdout.write(`cleared the control panel password in ${defaultSecretsPath}\\n`)\n process.stdout.write(`${dim('authentication stays disabled until you enable it again in the settings page')}\\n`)\n return\n }\n\n const interactive = process.stdin.isTTY === true\n let password = process.env.HHOSTED_PASSWORD\n\n if (password === undefined && interactive) {\n password = await promptHidden('New control panel password: ')\n const again = await promptHidden('Repeat it: ')\n if (password !== again)\n fail('the passwords do not match')\n }\n\n if (password === undefined || password.length === 0) {\n fail('no password given: run interactively, or set HHOSTED_PASSWORD for a non-interactive run')\n }\n\n store.setPassword(password)\n process.stdout.write(`${green('password stored')} in ${defaultSecretsPath} (mode 0600)\\n`)\n if (password.length < 8)\n process.stdout.write(`${dim(`\"${password}\" is short — easy to guess if the panel is reachable beyond loopback`)}\\n`)\n process.stdout.write(`${dim('restart the panel for it to take effect: home-hosted restart')}\\n`)\n}\n\nfunction version(): void {\n const manifest = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { version?: string }\n process.stdout.write(`${manifest.version ?? '0.0.0'}\\n`)\n}\n\nasync function main(): Promise<void> {\n const { rest, ...dirFlags } = extractDirFlags(process.argv.slice(2))\n const [command = '', ...args] = rest\n\n if (command === '' && rest.length === 0) {\n applyDirFlags(dirFlags)\n await up([])\n return\n }\n\n switch (command) {\n case 'up':\n applyDirFlags(dirFlags)\n await up(args)\n return\n case 'down':\n applyDirFlags(dirFlags)\n await down()\n return\n case 'restart':\n applyDirFlags(dirFlags)\n await restart(args)\n return\n case 'status':\n applyDirFlags(dirFlags)\n await status(args)\n return\n case 'set-password':\n applyDirFlags(dirFlags)\n await setPassword(args)\n return\n case 'ui-revert':\n applyDirFlags(dirFlags)\n await uiRevert()\n return\n case 'help':\n case '--help':\n case '-h':\n process.stdout.write(USAGE)\n return\n case 'version':\n case '--version':\n case '-v':\n version()\n return\n default:\n if (command.startsWith('-')) {\n // `home-hosted -p 4000` reads as `up`, the way a one-shot CLI should.\n applyDirFlags(dirFlags)\n await up(rest)\n return\n }\n process.stderr.write(`unknown command: ${command}\\n\\n${USAGE}`)\n process.exit(1)\n }\n}\n\nvoid main().catch((error: unknown) => {\n fail(error instanceof Error ? error.message : String(error))\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,SAAgB,aAAa,QAA2D;CACtF,MAAM,UAAkC,CAAC;CACzC,IAAI,CAAC,QACH,OAAO;CACT,KAAK,MAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;EACpC,MAAM,YAAY,KAAK,QAAQ,GAAG;EAClC,IAAI,YAAY,GACd;EACF,MAAM,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;EAC3C,IAAI,KAAK,WAAW,GAClB;EACF,MAAM,QAAQ,KAAK,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;EAC7C,IAAI;GACF,QAAQ,QAAQ,mBAAmB,KAAK;EAC1C,QACM;GACJ,QAAQ,QAAQ;EAClB;CACF;CACA,OAAO;AACT;AAEA,SAAgB,gBAAgB,MAAc,OAAe,UAAyB,CAAC,GAAW;CAChG,MAAM,QAAQ,CAAC,GAAG,KAAK,GAAG,mBAAmB,KAAK,GAAG;CACrD,MAAM,KAAK,QAAQ,QAAQ,QAAQ,KAAK;CACxC,IAAI,QAAQ,aAAa,KAAA,GACvB,MAAM,KAAK,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,WAAW,GAAI,CAAC,GAAG;CAC1E,IAAI,QAAQ,aAAa,OACvB,MAAM,KAAK,UAAU;CACvB,MAAM,KAAK,YAAY,QAAQ,YAAY,UAAU;CACrD,IAAI,QAAQ,QACV,MAAM,KAAK,QAAQ;CACrB,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;CCnCa,aAAa,cAAc;;;;;ACOxC,SAAgB,UAAU,OAA4B;CACpD,MAAM,SAAS,WAAW,KAAK;CAC/B,OAAO,kBAAkB,KAAK,SAAS,OAAO;AAChD;;;CAPa,aAAa,KAAK,sDAAkD;CAUpE,aAAa,KAAK,qCAAqC;CAEvD,gBAAgB,KAAK;EAChC,SAAS;EACT,YAAY;EACZ,aAAa;EACb,QAAQ;EACR,YAAY;;EAEZ,cAAc;CAChB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,kBAAkB,KAAK;;EAElC,MAAM;EACN,QAAQ;;EAER,cAAc;EACd,mBAAmB;;EAEnB,YAAY;CACd,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,kBAAkB,KAAK;;AAElC,aAAa,0BACf,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,eAAe,KAAK;EAC/B,SAAS;;EAET,MAAM;EACN,MAAM,gBAAgB,eAAe,CAAC,EAAE;EACxC,YAAY;EACZ,WAAW;;EAEX,oBAAoB;;EAEpB,qBAAqB;;EAErB,gBAAgB;CAClB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,aAAa,KAAK;EAC7B,QAAQ;EACR,WAAW;EACX,SAAS;;EAET,iBAAiB;CACnB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,kBAAkB,KAAK;EAClC,SAAS;EACT,MAAM,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;EACvC,KAAK,KAAK,wBAAwB,CAAC,CAAC,eAAe,CAAC,EAAE;EACtD,WAAW;;EAEX,SAAS;CACX,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAEd,wBAAwB,gBAAgB,GAAG,KAAK,MAAM,CAAC;CAEvD,uBAAuB,KAAK,gCAAgC;CAE5D,eAAe,KAAK;EAC/B,IAAI;EACJ,OAAO;EACP,SAAS;EACT,WAAW;EACX,SAAS;EACT,MAAM,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;EACvC,KAAK;EACL,KAAK,KAAK,wBAAwB,CAAC,CAAC,eAAe,CAAC,EAAE;;;;;EAKtD,UAAU,KAAK,wBAAwB,CAAC,CAAC,eAAe,CAAC,EAAE;EAC3D,WAAW,sBAAsB,SAAS;EAC1C,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,cAAc,OAAgB;EAC/C,gBAAgB;EAChB,SAAS,cAAc,eAAe,CAAC,EAAE;EACzC,QAAQ,aAAa,eAAe,CAAC,EAAE;EACvC,MAAM,WAAW,eAAe,CAAC,EAAE;EACnC,gBAAgB,qBAAqB,cAAc,GAAG;;EAEtD,WAAW,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;;EAE5C,SAAS;EACT,WAAW,gBAAgB,eAAe,CAAC,EAAE;;EAE7C,aAAa,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;CAChD,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAOd,aAAa,KAAK;EAC7B,SAAS;EACT,cAAc;;EAEd,cAAc;;EAEd,YAAY;EACZ,kBAAkB;EAClB,WAAW;CACb,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,iBAAiB,KAAK;EACjC,SAAS;EACT,QAAQ;EACR,SAAS;EACT,aAAa;EACb,iBAAiB;EACjB,aAAa;;EAEb,QAAQ;;EAER,YAAY;CACd,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,sBAAsB,KAAK,EACtC,UAAU,eAAe,eAAe,CAAC,EAAE,EAC7C,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,aAAa,KAAK;EAC7B,SAAS;;EAET,UAAU;EACV,MAAM;CACR,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,YAAY,KAAK,EAC5B,SAAS,kBACX,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,aAAa,KAAK;EAC7B,SAAS;EACT,YAAY;;EAEZ,WAAW,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC;;EAE/C,iBAAiB;EACjB,mBAAmB;EACnB,iBAAiB;EACjB,YAAY;EACZ,aAAa;CACf,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,gBAAgB,KAAK;EAChC,SAAS;EACT,KAAK;EACL,MAAM;;EAEN,cAAc,KAAK,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;CACjD,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,gBAAgB,KAAK;;EAEhC,OAAO;EACP,MAAM;;EAEN,MAAM,WAAW,cAAc,OAAgB;EAC/C,aAAa;EACb,MAAM,WAAW,eAAe,CAAC,EAAE;EACnC,KAAK,UAAU,eAAe,CAAC,EAAE;CACnC,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,iBAAiB,KAAK;EACjC,SAAS;EACT,WAAW;EACX,MAAM,WAAW,cAAc,OAAgB;EAC/C,gBAAgB;EAChB,SAAS,cAAc,eAAe,CAAC,EAAE;EACzC,QAAQ,aAAa,eAAe,CAAC,EAAE;EACvC,MAAM,WAAW,eAAe,CAAC,EAAE;EACnC,gBAAgB,qBAAqB,cAAc,GAAG;CACxD,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAKrB,qBAAqB,KAAK;EAC9B,SAAS;EACT,YAAY;EACZ,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,cAAc;CAChB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,uBAAuB,KAAK;EAChC,MAAM;EACN,QAAQ;EACR,cAAc;EACd,mBAAmB;EACnB,YAAY;CACd,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,uBAAuB,KAAK,EAChC,aAAa,uBACf,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,oBAAoB,KAAK;EAC7B,SAAS;EACT,MAAM;EACN,MAAM,qBAAqB,SAAS;EACpC,YAAY;EACZ,WAAW;EACX,oBAAoB;EACpB,qBAAqB;EACrB,gBAAgB;CAClB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,kBAAkB,KAAK;EAC3B,QAAQ;EACR,WAAW;EACX,SAAS;EACT,iBAAiB;CACnB,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,kBAAkB,KAAK;EAC3B,SAAS;EACT,cAAc;EACd,cAAc;EACd,YAAY;EACZ,kBAAkB;EAClB,WAAW;CACb,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,sBAAsB,KAAK;EAC/B,SAAS;EACT,QAAQ;EACR,SAAS;EACT,aAAa;EACb,iBAAiB;EACjB,aAAa;EACb,QAAQ;EACR,YAAY;CACd,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,2BAA2B,KAAK,EACpC,UAAU,oBAAoB,SAAS,EACzC,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,kBAAkB,KAAK;EAC3B,SAAS;EACT,YAAY;EACZ,WAAW,KAAK,UAAU,CAAC,CAAC,SAAS;EACrC,iBAAiB;EACjB,mBAAmB;EACnB,iBAAiB;EACjB,YAAY;EACZ,aAAa;CACf,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,qBAAqB,KAAK;EAC9B,SAAS;EACT,KAAK;EACL,MAAM;EACN,cAAc,KAAK,UAAU,CAAC,CAAC,SAAS;CAC1C,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,kBAAkB,KAAK;EAC3B,SAAS;EACT,UAAU;EACV,MAAM;CACR,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,iBAAiB,KAAK,EAC1B,SAAS,WACX,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,qBAAqB,KAAK;EAC9B,OAAO;EACP,MAAM;EACN,MAAM,WAAW,SAAS;EAC1B,aAAa;EACb,MAAM,gBAAgB,SAAS;EAC/B,KAAK,eAAe,SAAS;CAC/B,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,sBAAsB,KAAK;EAC/B,SAAS;EACT,WAAW;EACX,MAAM,WAAW,SAAS;EAC1B,gBAAgB;EAChB,SAAS,mBAAmB,SAAS;EACrC,QAAQ,kBAAkB,SAAS;EACnC,MAAM,gBAAgB,SAAS;EAC/B,gBAAgB,qBAAqB,SAAS;CAChD,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAErB,iBAAiB;EACrB,OAAO;EACP,SAAS;EACT,WAAW;EACX,SAAS;EACT,MAAM;EACN,KAAK;EACL,KAAK;EACL,UAAU;EACV,WAAW,sBAAsB,SAAS;EAC1C,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,SAAS;EAC1B,gBAAgB;EAChB,SAAS,mBAAmB,SAAS;EACrC,QAAQ,kBAAkB,SAAS;EACnC,MAAM,gBAAgB,SAAS;EAC/B,gBAAgB,qBAAqB,SAAS;EAC9C,WAAW,KAAK,UAAU,CAAC,CAAC,SAAS;EACrC,SAAS;EACT,WAAW,qBAAqB,SAAS;EACzC,aAAa,KAAK,UAAU,CAAC,CAAC,SAAS;CACzC;CAEa,oBAAoB,KAAK,cAAc,CAAC,CAAC,gBAAgB,QAAQ;CAGjE,qBAAqB,KAAK;EACrC,IAAI;EACJ,GAAG;EACH,SAAS;CACX,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,sBAAsB,KAAK;EACtC,SAAS,mBAAmB,SAAS;EACrC,UAAU,oBAAoB,SAAS;EACvC,MAAM,gBAAgB,SAAS;EAC/B,eAAe,yBAAyB,SAAS;EACjD,MAAM,gBAAgB,SAAS;EAC/B,SAAS,mBAAmB,SAAS;CACvC,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,mBAAmB,KAAK;EACnC,SAAS;EACT,aAAa;EACb,mBAAmB;;EAEnB,sBAAsB;;EAEtB,SAAS;;EAET,eAAe;EACf,cAAc;EACd,cAAc;EACd,YAAY;EACZ,kBAAkB;EAClB,WAAW;CACb,CAAC;CAGY,kBAAkB,KAAK;EAClC,SAAS;EACT,aAAa;EACb,SAAS;EACT,QAAQ;EACR,WAAW;EACX,SAAS;EACT,eAAe;EACf,aAAa;EACb,YAAY;EACZ,OAAO;CACT,CAAC;CAGY,uBAAuB,KAAK;EACvC,SAAS;EACT,UAAU;EACV,QAAQ;EACR,SAAS;EACT,aAAa;EACb,iBAAiB;EACjB,aAAa;;EAEb,QAAQ;EACR,YAAY;;EAEZ,YAAY;EACZ,cAAc;CAChB,CAAC;CAGY,yBAAyB,KAAK,EACzC,UAAU,qBACZ,CAAC;CAGY,oBAAoB,KAAK;EACpC,eAAe;EACf,cAAc;EACd,aAAa;EACb,sBAAsB;;EAEtB,iBAAiB;EACjB,cAAc;CAChB,CAAC;CAGY,cAAc,KAAK,EAAE,UAAU,SAAS,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAInE,sBAAsB,KAAK,oBAAoB;CAE/C,iBAAiB,KAAK;EACjC,iBAAiB;EACjB,aAAa;CACf,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,qBAAqB,KAAK,oGAAsF;CAGhH,oBAAoB,KAAK,0DAAkD;CAG3E,kBAAkB,KAAK,qCAA+B;CAGtD,kBAAkB,KAAK,sCAAgC;CAGvD,gBAAgB,KAAK;EAChC,IAAI;EACJ,QAAQ;EACR,MAAM;CACR,CAAC;CAGY,qBAAqB,KAAK;EACrC,UAAU;EACV,IAAI;EACJ,MAAM;EACN,QAAQ;;EAER,WAAW;CACb,CAAC;CAIY,sBAAsB,KAAK;EACtC,UAAU;;EAEV,aAAa;EACb,UAAU;EACV,SAAS;EACT,gBAAgB;EAChB,aAAa;EACb,YAAY;EACZ,eAAe;EACf,QAAQ,mBAAmB,MAAM;CACnC,CAAC;CAGY,yBAAyB,KAAK;EACzC,YAAY;;EAEZ,UAAU;EACV,WAAW;EACX,WAAW;CACb,CAAC;CAGY,iBAAiB,KAAK;EACjC,MAAM;EACN,YAAY;EACZ,WAAW;EACX,aAAa;CACf,CAAC;CAEY,iBAAiB,KAAK;EACjC,SAAS;EACT,MAAM;EACN,SAAS,KAAK,UAAU;EACxB,UAAU;EACV,mBAAmB;EACnB,iBAAiB;EACjB,aAAa;EACb,OAAO,eAAe,MAAM;;EAE5B,QAAQ,KAAK,UAAU;EACvB,WAAW;CACb,CAAC;CAGY,mBAAmB,KAAK;EACnC,MAAM;EACN,WAAW;EACX,WAAW;;EAEX,WAAW;CACb,CAAC;CAKY,mBAAmB,KAAK;EACnC,MAAM;;EAEN,QAAQ;;EAER,UAAU;EACV,MAAM;CACR,CAAC;CAGY,oBAAoB,KAAK;EACpC,SAAS;EACT,KAAK;EACL,MAAM;;EAEN,cAAc,KAAK,UAAU;;EAE7B,OAAO,iBAAiB,MAAM;EAC9B,OAAO,iBAAiB,MAAM;CAChC,CAAC;CAIY,oBAAoB,KAAK;;EAEpC,IAAI;EACJ,OAAO;EACP,MAAM;;EAEN,YAAY;;EAEZ,UAAU;EACV,MAAM;CACR,CAAC;CAGY,oBAAoB,KAAK;EACpC,QAAQ;EACR,WAAW;;EAEX,eAAe;EACf,OAAO,kBAAkB,MAAM;EAC/B,SAAS,KAAK,UAAU;EACxB,SAAS,KAAK,UAAU;;EAExB,iBAAiB;;EAEjB,UAAU;EACV,OAAO;CACT,CAAC;CAGY,qBAAqB,KAAK;;AAErC,UAAU,oBAAoB,SAAS,EACzC,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,uBAAuB,KAAK;EACvC,MAAM;EACN,UAAU,oBAAoB,SAAS;;EAEvC,SAAS,KAAK,UAAU,CAAC,CAAC,SAAS;CACrC,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,mBAAmB,KAAK;EACnC,IAAI;EACJ,QAAQ;EACR,UAAU;EACV,KAAK;EACL,QAAQ;EACR,QAAQ;EACR,WAAW;EACX,KAAK;EACL,WAAW;EACX,UAAU;EACV,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,eAAe;EACf,SAAS;;EAET,YAAY;EACZ,WAAW,uBAAuB,GAAG,KAAK,MAAM,CAAC;CACnD,CAAC;CAKY,oBAAoB,KAAK;;EAEpC,OAAO;EACP,MAAM;;EAEN,MAAM;;EAEN,UAAU;EACV,KAAK;EACL,aAAa;;EAEb,iBAAiB;EACjB,UAAU;EACV,MAAM;EACN,KAAK;CACP,CAAC;CAGY,iBAAiB,KAAK;EACjC,SAAS;EACT,UAAU;EACV,MAAM;EACN,eAAe;EACf,MAAM;EACN,SAAS;EACT,YAAY;EACZ,aAAa;;EAEb,YAAY;;EAEZ,UAAU;EACV,SAAS;EACT,SAAS,iBAAiB,MAAM;CAClC,CAAC;CAGY,AAAmB,KAAK;EACnC,MAAM;EACN,IAAI;EACJ,UAAU;EACV,OAAO,eAAe,SAAS;EAC/B,QAAQ,iBAAiB,SAAS;EAClC,OAAO,cAAc,MAAM,CAAC,CAAC,SAAS;CACxC,CAAC;CAGY,iBAAiB,KAAK,EACjC,OAAO,UACT,CAAC;CAEY,wBAAwB,KAAK;EACxC,MAAM;;EAEN,QAAQ;EACR,QAAQ;CACV,CAAC;CAEY,oBAAoB,KAAK;EACpC,MAAM;EACN,WAAW;CACb,CAAC;CAEY,sBAAsB,KAAK;EACtC,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,OAAO,kBAAkB,MAAM;CACjC,CAAC;CAGY,uBAAuB,KAAK,EACvC,SAAS,oBAAoB,MAAM,EACrC,CAAC;CAEY,AAAuB,KAAK;EACvC,UAAU;EACV,SAAS;EACT,WAAW;EACX,OAAO,KAAK,UAAU;;EAEtB,UAAU;EACV,OAAO,cAAc,MAAM;CAC7B,CAAC;CAKY,2BAA2B,KAAK;;EAE3C,UAAU;EACV,QAAQ;CACV,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,sBAAsB,KAAK,EACtC,UAAU,cACZ,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAGd,iBAAiB,KAAK;EACjC,SAAS;;EAET,MAAM;EACN,QAAQ;CACV,CAAC,CAAC,CAAC,gBAAgB,QAAQ;CAId,eAAe,KAAK;EAC/B,MAAM;EACN,SAAS;EACT,YAAY;EACZ,OAAO;CACT,CAAC;CAGY,AAAiB,KAAK;;EAEjC,QAAQ;;EAER,KAAK;EACL,MAAM,aAAa,GAAG,KAAK,MAAM,CAAC;CACpC,CAAC;CAGY,kBAAkB,KAAK;EAClC,aAAa;EACb,YAAY;CACd,CAAC,CAAC,CAAC,gBAAgB,QAAQ;;;;;;;;ACzvB3B,SAAgB,SAAS,QAA0B;CACjD,OAAO,EAAE,oBAAoB,EAAE,QAAQ,SAAS,MAAe,EAAE,EAAE;AACrE;;;CAR+B,eAAA;CAWlB,kBAAkB;EAC7B,KAAK;GAAE,aAAa;GAA4B,SAAS,SAAS,cAAc;EAAE;EAClF,KAAK;GAAE,aAAa;GAAoB,SAAS,SAAS,cAAc;EAAE;EAC1E,KAAK;GAAE,aAAa;GAAc,SAAS,SAAS,cAAc;EAAE;CACtE;;;;;;;;;;ACNA,SAAgB,SAAkF,QAAgB,QAAgB;CAChI,OAAO,UAAkB,QAAQ,SAAS,WAAW;EACnD,IAAI,OAAO,YAAY,OACrB,MAAM,IAAI,cAAc,qBAAqB;GAAE,YAAY;GAAK,QAAQ,gBAAgB,OAAO,KAAK;EAAE,CAAC;CAC3G,CAAC;AACH;;AAGA,SAAS,gBAAgB,OAA2F;CAClH,OAAO,MAAM,KAAK,UAAU;EAI1B,OAAO;GAAE,OAHK,MAAM,QAAQ,CAAC,EAAA,CAC1B,KAAI,YAAY,OAAO,YAAY,YAAY,YAAY,QAAQ,SAAS,UAAU,OAAO,QAAQ,GAAG,IAAI,OAAO,OAAO,CAAE,CAAC,CAC7H,KAAK,GACC;GAAM,SAAS,MAAM;EAAQ;CACxC,CAAC;AACH;;;;;ACxBA,SAAgB,UAAU,GAA6C;CAGrE,OADY,EAAE,IAAI,KACN,MAAM;AACpB;AAEA,SAAgB,WAAW,SAAiC;CAC1D,IAAI,CAAC,SACH,OAAO;CACT,OAAO,YAAY,eAAe,YAAY,SAAS,YAAY;AACrE;AAEA,SAAgB,kBAAkB,GAAuC;CACvE,OAAO,WAAW,UAAU,CAAC,CAAC;AAChC;;;;;;;;ACHA,SAAgB,gBAAgB,MAAc,SAAiB,UAA4B,CAAC,GAAS;CACnG,GAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CACpD,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,IAAI;CACnC,GAAG,cAAc,KAAK,SAAS,QAAQ,SAAS,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,QAAQ,KAAK,CAAC;CAC9F,IAAI,QAAQ,SAAS,KAAA,GACnB,GAAG,UAAU,KAAK,QAAQ,IAAI;CAChC,GAAG,WAAW,KAAK,IAAI;AACzB;;;;;;;;;;ACSA,SAAgB,UAAU,UAAkB,MAAc,MAAkB,QAAwB;CAClG,OAAO,OAAO,WAAW,SAAS,UAAU,MAAM,GAAG,MAAM,QAAQ,EAAE,GAAG,KAAK,CAAC;AAChF;AAEA,SAAgB,aAAa,UAAkB,UAAiD,CAAC,GAAmB;CAClH,MAAM,OAAO,OAAO,YAAY,UAAU;CAC1C,OAAO;EACL,MAAM;EACN,MAAM,KAAK,SAAS,QAAQ;EAC5B,MAAM,UAAU,UAAU,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,QAAQ;EAC/D,QAAQ;EACR,MAAM,EAAE,GAAG,KAAK;EAChB,WAAW,QAAQ,OAAO,KAAK,IAAI;EACnC,GAAI,QAAQ,cAAc,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC1D;AACF;AAEA,SAAgB,eAAe,UAAkB,QAAiC;CAChF,MAAM,WAAW,SAAO,KAAK,OAAO,MAAM,QAAQ;CAClD,IAAI;CACJ,IAAI;EACF,SAAS,UAAU,UAAU,SAAO,KAAK,OAAO,MAAM,QAAQ,GAAG,OAAO,MAAM,OAAO,MAAM;CAC7F,QACM;EACJ,OAAO;CACT;CACA,IAAI,OAAO,WAAW,SAAS,QAC7B,OAAO;CACT,OAAO,OAAO,gBAAgB,QAAQ,QAAQ;AAChD;;;CAvDgC,YAAA;CAG1B,OAAO;EAAE,GAAG;EAAO,GAAG;EAAG,GAAG;CAAE;CAC9B,SAAS;CACT,aAAa;CAwDN,eAAb,MAA0B;EAIK;EAH7B,QAAoC;EACpC,WAAmB;EAEnB,YAAY,MAA+B;GAAd,KAAA,OAAA;EAAe;EAE5C,IAAI,OAAe;GACjB,OAAO,KAAK;EACd;;;;;;EAOA,OAAoB;GAClB,MAAM,MAAM,KAAK,QAAQ;GACzB,IAAI,KAAK,UAAU,QAAQ,QAAQ,KAAK,UACtC,OAAO,KAAK;GACd,KAAK,QAAQ,KAAK,KAAK;GACvB,KAAK,WAAW;GAChB,OAAO,KAAK;EACd;EAEA,UAA0B;GACxB,IAAI;IACF,MAAM,QAAQ,GAAG,SAAS,KAAK,IAAI;IACnC,OAAO,GAAG,MAAM,QAAQ,GAAG,MAAM;GACnC,QACM;IACJ,OAAO;GACT;EACF;EAEA,IAAI,WAAkC;GACpC,OAAO,KAAK,KAAK,CAAC,CAAC;EACrB;EAEA,IAAI,oBAAmC;GACrC,OAAO,KAAK,UAAU,aAAa;EACrC;EAEA,IAAI,cAAuB;GACzB,OAAO,KAAK,aAAa;EAC3B;EAEA,IAAI,uBAAgC;GAClC,OAAO,KAAK,UAAU,cAAc;EACtC;EAEA,IAAI,gBAA+B;GACjC,OAAO,KAAK,KAAK,CAAC,CAAC,UAAU,YAAY;EAC3C;EAEA,IAAI,mBAA4B;GAC9B,QAAQ,KAAK,iBAAiB,GAAA,CAAI,SAAS;EAC7C;EAEA,YAAY,UAAkB,UAAmC,CAAC,GAAmB;GACnF,MAAM,SAAS,aAAa,UAAU,OAAO;GAC7C,KAAK,KAAK;IAAE,GAAG,KAAK,KAAK;IAAG,UAAU;GAAO,CAAC;GAC9C,OAAO;EACT;;EAGA,sBAAsB,UAAyC;GAC7D,IAAI,KAAK,aACP,OAAO;GACT,OAAO,KAAK,YAAY,UAAU,EAAE,WAAW,KAAK,CAAC;EACvD;EAEA,gBAAsB;GACpB,KAAK,KAAK;IAAE,GAAG,KAAK,KAAK;IAAG,UAAU;GAAK,CAAC;EAC9C;EAEA,iBAAiB,OAA4B;GAC3C,MAAM,UAAU,OAAO,KAAK,KAAK;GACjC,KAAK,KAAK;IAAE,GAAG,KAAK,KAAK;IAAG,UAAU,QAAQ,SAAS,IAAI,EAAE,UAAU,QAAQ,IAAI;GAAK,CAAC;EAC3F;EAEA,OAA4B;GAC1B,IAAI,CAAC,GAAG,WAAW,KAAK,IAAI,GAC1B,OAAO;IAAE,SAAS;IAAG,UAAU;IAAM,UAAU;GAAK;GACtD,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,KAAK,MAAM,MAAM,CAAC;IAC5D,OAAO;KACL,SAAS;KACT,UAAU,QAAQ,YAAY;KAC9B,UAAU,QAAQ,UAAU,WAAW,EAAE,UAAU,OAAO,SAAS,SAAS,IAAI;IAClF;GACF,QACM;IAEJ,OAAO;KAAE,SAAS;KAAG,UAAU;KAAM,UAAU;IAAK;GACtD;EACF;EAEA,KAAa,UAA6B;GACxC,gBAAgB,KAAK,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;GACpF,KAAK,QAAQ;EACf;CACF;;;CClK+B,aAAA;CACF,aAAA;CAEhB,iBAAiB;CAKxB,eAAe;CACf,iBAAiB;CAEjB,sBAAsB;CACtB,wBAAwB;CA4BjB,cAAb,MAAyB;EAMJ;EACA;EANnB,2BAA4B,IAAI,IAA2B;EAC3D,2BAA4B,IAAI,IAA2B;EAC3D;EAEA,YACE,SACA,WACA;GAFiB,KAAA,UAAA;GACA,KAAA,YAAA;GAEjB,KAAK,QAAQ,kBAAkB,KAAK,QAAQ,GAAG,GAAM;GACrD,KAAK,MAAM,MAAM;EACnB;EAEA,IAAI,cAAuB;GACzB,OAAO,KAAK,QAAQ;EACtB;EAEA,IAAI,oBAAmC;GACrC,OAAO,KAAK,QAAQ;EACtB;;EAGA,IAAI,uBAAgC;GAClC,OAAO,KAAK,QAAQ;EACtB;;EAGA,YAAqB;GACnB,OAAO,KAAK,UAAU,CAAC,CAAC;EAC1B;;EAGA,UAAmB;GACjB,OAAO,KAAK,UAAU,CAAC,CAAC,WAAW,KAAK,QAAQ;EAClD;;EAGA,aAAsB;GACpB,OAAO,KAAK,QAAQ;EACtB;EAEA,YAAY,OAAmC;GAC7C,OAAO;IACL,eAAe,KAAK,SAAS,KAAK,MAAM;IACxC,cAAc,KAAK,WAAW;IAC9B,aAAa,KAAK,QAAQ;IAC1B,sBAAsB,KAAK,QAAQ;IACnC,iBAAiB,KAAK,QAAQ,uBAAA,OAA0C;IACxE,cAAc,KAAK,UAAU,CAAC,CAAC;GACjC;EACF;EAEA,gBAAgB,cAAwD;GACtE,OAAO,aAAa,YAAY,CAAC,CAAA,kBAAoB;EACvD;;EAGA,SAAS,OAA4C;GACnD,IAAI,CAAC,OACH,OAAO;GACT,MAAM,UAAU,KAAK,SAAS,IAAI,KAAK;GACvC,IAAI,CAAC,SACH,OAAO;GAET,MAAM,MAAM,KAAK,IAAI;GACrB,IAAI,QAAQ,aAAa,KAAK;IAC5B,KAAK,SAAS,OAAO,KAAK;IAC1B,OAAO;GACT;GAEA,QAAQ,aAAa;GACrB,QAAQ,YAAY,MAAM,KAAK,UAAU,CAAC,CAAC;GAC3C,OAAO;EACT;EAEA,sBAAsB,UAA2B;GAC/C,MAAM,SAAS,KAAK,QAAQ;GAC5B,IAAI,WAAW,MACb,OAAO;GACT,OAAO,eAAe,UAAU,MAAM;EACxC;EAEA,MAAM,UAAkB,IAAiC;GACvD,MAAM,SAAS,KAAK,UAAU;GAC9B,MAAM,MAAM,MAAM;GAClB,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,UAAU,KAAK,SAAS,IAAI,GAAG;GAErC,IAAI,WAAW,QAAQ,eAAe,KAAK;IACzC,MAAM,eAAe,QAAQ,eAAe;IAC5C,OAAO;KACL,IAAI;KACJ,QAAQ;KACR,OAAO,sCAAsC,KAAK,KAAK,eAAe,GAAI,EAAE;KAC5E;IACF;GACF;GAEA,MAAM,SAAS,KAAK,QAAQ;GAC5B,IAAI,WAAW,MACb,OAAO;IAAE,IAAI;IAAO,QAAQ;IAAK,OAAO;GAAyB;GAGnE,IAAI,CAAC,eAAe,UAAU,MAAM,GAAG;IACrC,MAAM,YAAY,SAAS,YAAY,KAAK;IAC5C,IAAI,YAAY,OAAO,kBAAkB;KACvC,MAAM,UAAU,SAAS,UAAU,KAAK;KACxC,MAAM,eAAe,KAAK,IAAI,IAAI,KAAK,IAAI,OAAO,YAAY,MAAM,SAAS,IAAI,cAAc;KAC/F,KAAK,SAAS,IAAI,KAAK;MAAE,UAAU;MAAG;MAAc;MAAQ,eAAe,KAAK,IAAI;KAAE,CAAC;IACzF,OAEE,KAAK,SAAS,IAAI,KAAK;KAAE;KAAU,cAAc;KAAG,QAAQ,SAAS,UAAU;KAAG,eAAe,KAAK,IAAI;IAAE,CAAC;IAE/G,OAAO;KAAE,IAAI;KAAO,QAAQ;KAAK,OAAO;IAAmB;GAC7D;GAEA,KAAK,SAAS,OAAO,GAAG;GACxB,IAAI,KAAK,SAAS,QAAQ,cAAc;IACtC,MAAM,SAAS,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IACvF,IAAI,QACF,KAAK,SAAS,OAAO,OAAO,KAAK;GACrC;GAEA,MAAM,QAAQ,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;GACzD,KAAK,SAAS,IAAI,OAAO;IACvB;IACA,WAAW;IACX,WAAW,MAAM,OAAO;IACxB,YAAY;IACZ;GACF,CAAC;GAED,OAAO;IAAE,IAAI;IAAM,QAAQ;IAAK;IAAO,UAAU,OAAO;GAAa;EACvE;EAEA,OAAO,OAA4B;GACjC,IAAI,OACF,KAAK,SAAS,OAAO,KAAK;EAC9B;EAEA,YAAkB;GAChB,KAAK,SAAS,MAAM;EACtB;;;;;;EAOA,YAAY,UAAkB,UAA8D,CAAC,GAAS;GACpG,KAAK,QAAQ,YAAY,UAAU,OAAO;GAC1C,KAAK,aAAa,QAAQ,aAAa,IAAI;EAC7C;;EAGA,aAAqB,MAA2B;GAC9C,IAAI,SAAS,MAAM;IACjB,KAAK,SAAS,MAAM;IACpB;GACF;GACA,KAAK,MAAM,SAAS,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,GAC1C,IAAI,UAAU,MACZ,KAAK,SAAS,OAAO,KAAK;EAEhC;;EAGA,sBAAsB,UAA2B;GAC/C,MAAM,UAAU,KAAK,QAAQ,sBAAsB,QAAQ,MAAM;GACjE,IAAI,SACF,KAAK,UAAU;GACjB,OAAO;EACT;EAEA,gBAAsB;GACpB,KAAK,QAAQ,cAAc;GAC3B,KAAK,UAAU;EACjB;EAEA,iBAAyB;GACvB,OAAO,KAAK,SAAS;EACvB;EAEA,UAAgB;GACd,cAAc,KAAK,KAAK;GACxB,KAAK,SAAS,MAAM;EACtB;EAEA,UAAwB;GACtB,MAAM,MAAM,KAAK,IAAI;GACrB,KAAK,MAAM,CAAC,OAAO,YAAY,KAAK,UAClC,IAAI,QAAQ,aAAa,KACvB,KAAK,SAAS,OAAO,KAAK;GAE9B,KAAK,MAAM,CAAC,KAAK,YAAY,KAAK,UAGhC,IAAI,MAAM,QAAQ,iBAAiB,uBACjC,KAAK,SAAS,OAAO,GAAG;GAG5B,IAAI,KAAK,SAAS,OAAO,qBAAqB;IAC5C,MAAM,SAAS,CAAC,GAAG,KAAK,SAAS,QAAQ,CAAC,CAAC,CACxC,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC,gBAAgB,EAAE,EAAE,CAAC,aAAa,CAAC,CACvD,MAAM,GAAG,KAAK,SAAS,OAAO,mBAAmB;IACpD,KAAK,MAAM,CAAC,QAAQ,QAAQ,KAAK,SAAS,OAAO,GAAG;GACtD;EACF;CACF;;;;;;;;;;;AClOA,SAAgB,gBAAgB,MAAwC;CACtE,OAAO,OAAO,GAAG,SAAS;EACxB,MAAM,OAAO,EAAE,IAAI;EAEnB,IAAI,KAAK,WAAW,MAAM,GAAG;GAC3B,MAAM,SAAS,EAAE,IAAI;GACrB,IAAI,WAAW,SAAS,WAAW,QAAQ;IACzC,MAAM,SAAS,EAAE,IAAI,OAAO,QAAQ;IACpC,IAAI,WAAW,KAAA,GAAW;KAGxB,MAAM,cAAc,EAAE,IAAI,OAAO,MAAM,KAAK,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC;KAC/D,IAAI,aAA4B;KAChC,IAAI;MACF,aAAa,IAAI,IAAI,MAAM,CAAC,CAAC;KAC/B,QACM;MACJ,aAAa;KACf;KACA,IAAI,eAAe,QAAQ,eAAe,aACxC,MAAM,IAAI,cAAc,iCAAiC;MAAE,YAAY;MAAK,MAAM;KAAe,CAAC;IACtG;GACF;GAEA,IAAI,CAAC,aAAa,IAAI,IAAI,GAAG;IAC3B,IAAI,KAAK,KAAK,QAAQ,GAAG;KACvB,MAAM,QAAQ,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC;KAC9D,IAAI,KAAK,KAAK,SAAS,KAAK,MAAM,MAChC,MAAM,IAAI,cAAc,2BAA2B;MAAE,YAAY;MAAK,MAAM;KAAmB,CAAC;IAEpG,OACK,IAAI,KAAK,KAAK,UAAU,GAIvB;SAAA,CAAC,kBAAkB,CAAC,GACtB,MAAM,IAAI,cAAc,iGAAiG;MACvH,YAAY;MACZ,MAAM;KACR,CAAC;IAAA;GAGP;EACF;EAEA,MAAM,KAAK;CACb;AACF;;;CArEkC,cAAA;CACH,YAAA;CAGzB,+BAAe,IAAI,IAAI,CAAC,mBAAmB,mBAAmB,CAAC;CAGxD,qBAAqB;;;;ACRlC,SAAgB,aAA4B;CAC1C,KAAK,MAAM,WAAW,OAAO,OAAO,GAAG,kBAAkB,CAAC,GACxD,KAAK,MAAM,SAAS,WAAW,CAAC,GAC9B,IAAI,MAAM,WAAW,UAAU,CAAC,MAAM,UACpC,OAAO,MAAM;CAGnB,OAAO;AACT;AAEA,SAAgB,SAAS,MAAsB;CAC7C,IAAI,SAAS,SACX,OAAO;CACT,IAAI,SAAS,OACX,OAAO;CACT,OAAO;AACT;;AAGA,SAAgB,YAAY,MAAsB;CAChD,IAAI,SAAS,SACX,OAAO;CACT,IAAI,SAAS,OACX,OAAO,WAAW,KAAK;CACzB,OAAO;AACT;;AAGA,SAAgB,UAAU,MAAuB;CAC/C,OAAO,SAAS,IAAI,MAAM;AAC5B;;;;;;;;;ACjBA,SAAgB,cAAc,SAAwB,aAAsB,uBAAuB,OAAsB;CACvH,MAAM,UAAU,UAAU,QAAQ,IAAI;CACtC,IAAI,CAAC,SACH,OAAO;EAAE;EAAS,eAAe;CAAK;CAExC,IAAI,CAAC,QAAQ,KAAK,WAAW,CAAC,aAC5B,OAAO;EACL;EACA,eAAe,iCAAiC,QAAQ,KAAK;CAC/D;CAEF,IAAI,CAAC,QAAQ,KAAK,SAChB,OAAO;EAAE;EAAS,eAAe,iCAAiC,QAAQ,KAAK;CAAiC;CAElH,IAAI,CAAC,aACH,OAAO;EACL;EACA,eAAe,iCAAiC,QAAQ,KAAK;CAC/D;CAEF,IAAI,sBACF,OAAO;EACL;EACA,eAAe,iCAAiC,QAAQ,KAAK;CAC/D;CAEF,OAAO;EAAE;EAAS,eAAe;CAAK;AACxC;;CAzC0B,UAAA;;;;;ACc1B,SAAS,aAAa,GAAY,MAAwB;CACxD,MAAM,OAAO,KAAK,MAAM,OAAO,QAAQ,KAAK;CAC5C,IAAI,SAAS,UACX,OAAO;CACT,IAAI,SAAS,SACX,OAAO;CACT,OAAO,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,aAAa;AACzC;AAEA,SAAgB,gBAAgB,MAAe;CAC7C,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,iBACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW,EAAE,KAAK;GAAE,aAAa;GAAW,SAAS,SAAS,iBAAiB;EAAE,EAAE;CACrF,CAAC,IACD,MAAK,EAAE,KAAK,KAAK,KAAK,YAAY,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,CAAC,CAAC,CACtF,CAAC,CAEA,KACC,eACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GACT,KAAK;IAAE,aAAa;IAAa,SAAS,SAAS,iBAAiB;GAAE;GACtE,KAAK,gBAAgB;GACrB,KAAK,EAAE,aAAa,oDAAoD;EAC1E;CACF,CAAC,GACD,SAAS,QAAQ,WAAW,IAC3B,MAAM;EACL,MAAM,OAAqB,EAAE,IAAI,MAAM,MAAM;EAC7C,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,UAAU,CAAC,CAAC;EAE3D,IAAI,CAAC,QAAQ,IAAI;GACf,IAAI,QAAQ,iBAAiB,KAAA,GAC3B,EAAE,OAAO,eAAe,OAAO,KAAK,KAAK,QAAQ,eAAe,GAAI,CAAC,CAAC;GACxE,MAAM,IAAI,cAAc,QAAQ,OAAO;IAAE,YAAY,QAAQ;IAAQ,MAAM;GAAe,CAAC;EAC7F;EAEA,EAAE,OAAO,cAAc,gBAAgB,gBAAgB,QAAQ,OAAO;GACpE,UAAU,QAAQ;GAClB,QAAQ,aAAa,GAAG,IAAI;GAC5B,UAAU;GACV,UAAU;EACZ,CAAC,CAAC;EAEF,OAAO,EAAE,KAAK,KAAK,KAAK,YAAY,QAAQ,KAAK,CAAC;CACpD,CACF,CAAC,CAEA,KACC,gBACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,aAAa,EAAE;CAClD,CAAC,IACA,MAAM;EACL,KAAK,KAAK,OAAO,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,CAAC;EAClE,EAAE,OAAO,cAAc,gBAAgB,gBAAgB,IAAI;GACzD,UAAU;GACV,QAAQ,aAAa,GAAG,IAAI;GAC5B,UAAU;GACV,UAAU;EACZ,CAAC,CAAC;EACF,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF,CAAC,CAOA,KACC,kBACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,UAAU;GAAG,KAAK,gBAAgB;GAAM,KAAK,gBAAgB;EAAK;CACrG,CAAC,GACD,SAAS,QAAQ,cAAc,IAC9B,MAAM;EACL,MAAM,OAAwB,EAAE,IAAI,MAAM,MAAM;EAChD,MAAM,cAAc,KAAK,KAAK;EAC9B,MAAM,gBAAgB,KAAK,KAAK,SAAS,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,CAAC,MAAM;EAChG,MAAM,aAAa,CAAC,eAAe,kBAAkB,CAAC;EAEtD,IAAI,CAAC,iBAAiB,CAAC,YACrB,MAAM,IAAI,cAAc,2BAA2B;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAElG,IAAI,iBAAiB,aAAa;GAChC,IAAI,KAAK,oBAAoB,KAAA,GAC3B,MAAM,IAAI,cAAc,8DAA8D;IAAE,YAAY;IAAK,MAAM;GAA4B,CAAC;GAC9I,IAAI,CAAC,KAAK,KAAK,sBAAsB,KAAK,eAAe,GACvD,MAAM,IAAI,cAAc,iCAAiC;IAAE,YAAY;IAAK,MAAM;GAAyB,CAAC;EAChH;EAGA,KAAK,KAAK,YAAY,KAAK,aAAa,EAAE,WAAW,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,EAAE,CAAC;EAGxG,IAAI,UAAU,KAAK,MAAM,OAAO,QAAQ,KAAK;EAC7C,IAAI,CAAC,SAAS;GACZ,KAAK,MAAM,cAAc,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;GACpD,UAAU;EACZ;EAEA,OAAO,EAAE,KAAK;GAAE,IAAI;GAAM;GAAS,qBAAqB;EAAK,CAAC;CAChE,CACF,CAAC,CAEA,OACC,kBACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,UAAU;GAAG,KAAK,gBAAgB;GAAM,KAAK,gBAAgB;EAAK;CACrG,CAAC,IACA,MAAM;EACL,IAAI,KAAK,KAAK,SAAS,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,CAAC,MAAM,MAC5E,MAAM,IAAI,cAAc,2BAA2B;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAGlG,IADiB,cAAc,KAAK,MAAM,OAAO,SAAS,KACtD,CAAA,CAAS,SACX,MAAM,IAAI,cAAc,sHAAsH;GAC5I,YAAY;GACZ,MAAM;EACR,CAAC;EAGH,KAAK,KAAK,cAAc;EACxB,KAAK,MAAM,cAAc,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;EACrD,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF;AACJ;;CAtJgC,aAAA;CACL,aAAA;CACe,eAAA;CACjB,eAAA;CAC0B,UAAA;CACN,cAAA;CACf,cAAA;CACiC,eAAA;;;;;;CCHlD,SAA0B,cACrC,EACE,OAAO,gBAAgB,UAAU,QAAQ,KAAA,EAC3C,CACF;;;;;;;;;;ACHA,SAAgB,aAAgB,QAAqC,OAAgB,OAAkB;CACrG,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,kBAAkB,KAAK,QACzB,MAAM,IAAI,cAAc,GAAG,MAAM,IAAI,OAAO,WAAW;EACrD,YAAY;EACZ,MAAM;EACN,QAAQ,OAAO,OAAO,KAAI,WAAU;GAAE,MAAM,MAAM,KAAK,KAAK,GAAG;GAAG,SAAS,MAAM;EAAQ,EAAE;CAC7F,CAAC;CAEH,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;ACVA,SAAgB,kBAA0B;CACxC,MAAM,WAAW,QAAQ,IAAI;CAC7B,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,GAC9C,OAAO,KAAK,QAAQ,QAAQ;CAC9B,OAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,cAAc;AAC/C;;;;;;;AAUA,SAAgB,oBAA4B;CAC1C,MAAM,WAAW,QAAQ,IAAI;CAC7B,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,GAC9C,OAAO,KAAK,QAAQ,QAAQ;CAC9B,OAAO,QAAQ,IAAI;AACrB;;AAoBA,SAAgB,gBAAgB,QAAgB,OAAO,YAAoB;CACzE,IAAI,QAAQ;CACZ,IAAI,UAAU,KACZ,QAAQ,GAAG,QAAQ;MAChB,IAAI,MAAM,WAAW,IAAI,GAC5B,QAAQ,KAAK,KAAK,GAAG,QAAQ,GAAG,MAAM,MAAM,CAAC,CAAC;CAChD,OAAO,KAAK,WAAW,KAAK,IAAI,QAAQ,KAAK,QAAQ,MAAM,KAAK;AAClE;;;CAxCa,WAAW,gBAAgB;CAe3B,aAAa,kBAAkB;CAE/B,oBAAoB,KAAK,KAAK,UAAU,qBAAqB;CAE7D,mBAAmB,KAAK,KAAK,UAAU,4BAA4B;CAEnE,qBAAqB,KAAK,KAAK,UAAU,uBAAuB;CAEhE,iBAAiB,KAAK,KAAK,UAAU,OAAO;CAE5C,qBAAqB,KAAK,KAAK,UAAU,SAAS,cAAc;CAEhE,gBAAgB,KAAK,KAAK,UAAU,MAAM;CAE1C,cAAc,KAAK,KAAK,UAAU,UAAU;CAC5C,gBAAgB,KAAK,KAAK,UAAU,SAAS,iBAAiB;;;;;;;;;;AClB3E,SAAgB,iBACd,OACA,MACA,SACA,KACa;CACb,MAAM,SAAwB,MAAM,OAAO;CAC3C,MAAM,WAAW,cAAc,QAAQ,KAAK,aAAa,KAAK,oBAAoB;CAElF,OAAO;EACL,OAAO,OAAO;EACd,MAAM,OAAO;EACb,MAAM,OAAO;EACb,UAAU,QAAQ;EAClB,KAAK,QAAQ;EACb,UAAU,QAAQ;EAClB,aAAa,OAAO;EACpB,iBAAiB,QAAQ,SAAS,OAAO,QAAQ,QAAQ,SAAS,OAAO;EACzE,MAAM;GACJ,SAAS,OAAO,KAAK;GACrB,aAAa,KAAK;GAClB,mBAAmB,KAAK;GACxB,sBAAsB,KAAK;GAC3B,SAAS,SAAS;GAClB,eAAe,SAAS;GACxB,cAAc,OAAO,KAAK;GAC1B,cAAc,OAAO,KAAK;GAC1B,YAAY,OAAO,KAAK;GACxB,kBAAkB,OAAO,KAAK;GAC9B,WAAW,OAAO,KAAK;EACzB;EACA,KAAK,IAAI,OAAO,OAAO,IAAI,OAAO;CACpC;AACF;AAEA,SAAgB,cAAc,OAAoC;CAChE,OAAO,MAAM;AACf;AAEA,SAAgB,iBAAiB,OAAoB,SAAqC;CACxF,OAAO;EACL,SAAS,MAAM,OAAO,QAAQ;EAC9B,KAAK,QAAQ;EACb,MAAM,MAAM,OAAO,QAAQ;EAC3B,cAAc,MAAM,OAAO,QAAQ;EACnC,OAAO,QAAQ;EACf,OAAO,QAAQ,KAAK;CACtB;AACF;AAEA,SAAgB,cAAc,aAAoC;CAChE,OAAO,YAAY;AACrB;AAEA,SAAgB,cAAc,MAAgC;CAC5D,OAAO;EACL,SAAS,iBAAiB,KAAK,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG;EACvE,UAAU,cAAc,KAAK,KAAK;EAClC,MAAM,KAAK,MAAM,OAAO;EACxB,eAAe,EAAE,UAAU,KAAK,cAAc,OAAO,EAAE;EACvD,MAAM,cAAc,KAAK,WAAW;EACpC,SAAS,iBAAiB,KAAK,OAAO,KAAK,OAAO;EAClD,YAAY,KAAK,MAAM;EACvB,aAAa,KAAK,MAAM;EACxB;EACA;EACA,SAAS,KAAK;EACd,SAAS,KAAK;CAChB;AACF;;CA1FqC,WAAA;CACP,cAAA;;;;;ACU9B,SAAS,aAAa,OAA0C;CAC9D,OAAO,IAAI,cAAc,SAAS,qBAAqB;EAAE,YAAY;EAAK,MAAM;CAAgB,CAAC;AACnG;AAEA,SAAgB,mBAAmB,MAAe;CAChD,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,YACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW,EAAE,KAAK;GAAE,aAAa;GAAW,SAAS,SAAS,iBAAiB;EAAE,EAAE;CACrF,CAAC,IACD,MAAK,EAAE,KAAK,iBAAiB,KAAK,OAAO,KAAK,OAAO,CAAC,CACxD,CAAC,CAEA,KACC,YACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,UAAU;GAAG,KAAK,gBAAgB;EAAK;CAC1E,CAAC,GACD,SAAS,QAAQ,kBAAkB,GACnC,OAAO,MAAM;EACX,MAAM,OAAqB,EAAE,IAAI,MAAM,MAAM;EAC7C,MAAM,SAAS,MAAM,KAAK,QAAQ,OAAO,EAAE,UAAU,KAAK,SAAS,CAAC;EACpE,IAAI,CAAC,OAAO,IACV,MAAM,aAAa,OAAO,KAAK;EACjC,OAAO,EAAE,KAAK;GAAE,MAAM,OAAO;GAAM,OAAO,KAAK,QAAQ,KAAK;EAAE,CAAC;CACjE,CACF,CAAC,CAEA,IACC,2BACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,gCAAgC;GAAG,KAAK,gBAAgB;EAAK;CAChG,CAAC,IACA,MAAM;EACL,MAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE,IAAI,MAAM,MAAM,CAAC;EACrD,IAAI,SAAS,MACX,MAAM,IAAI,cAAc,kBAAkB;GAAE,YAAY;GAAK,MAAM;EAAiB,CAAC;EAEvF,MAAM,QAAQ,GAAG,SAAS,IAAI;EAC9B,OAAO,EAAE,KAAK,GAAG,aAAa,IAAI,GAAG,KAAK;GACxC,gBAAgB;GAChB,kBAAkB,OAAO,MAAM,IAAI;GACnC,uBAAuB,yBAAyB,KAAK,SAAS,IAAI,EAAE;EACtE,CAAC;CACH,CACF,CAAC,CAEA,OACC,kBACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,UAAU;GAAG,KAAK,gBAAgB;EAAK;CAC1E,CAAC,IACA,MAAM;EACL,IAAI,CAAC,KAAK,QAAQ,OAAO,EAAE,IAAI,MAAM,MAAM,CAAC,GAC1C,MAAM,IAAI,cAAc,kBAAkB;GAAE,YAAY;GAAK,MAAM;EAAiB,CAAC;EACvF,OAAO,EAAE,KAAK;GAAE,IAAI;GAAM,OAAO,KAAK,QAAQ,KAAK;EAAE,CAAC;CACxD,CACF,CAAC,CAOA,KAAK,oBAAoB,cAAc;EACtC,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW;GAAE,KAAK;IAAE,aAAa;IAAY,SAAS,SAAS,iBAAiB;GAAE;GAAG,KAAK,gBAAgB;GAAM,KAAK,gBAAgB;EAAK;CAC5I,CAAC,GAAG,OAAO,MAAM;EACf,MAAM,UAAU,EAAE,IAAI,MAAM,SAAS,MAAM;EAC3C,MAAM,cAAc,EAAE,IAAI,OAAO,cAAc,KAAK;EAEpD,IAAI,UAAyB;EAC7B,IAAI,aAA4B;EAChC,IAAI;EAEJ,IAAI;GACF,IAAI,YAAY,SAAS,qBAAqB,GAAG;IAC/C,MAAM,WAAW,OAAO,SAAS,EAAE,IAAI,OAAO,gBAAgB,KAAK,KAAK,EAAE;IAC1E,IAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,kBAC1C,MAAM,IAAI,cAAc,6BAA6B,KAAK,MAAM,mBAAmB,OAAO,IAAI,EAAE,KAAK;KAAE,YAAY;KAAK,MAAM;IAAmB,CAAC;IAEpJ,MAAM,OAAO,MAAM,EAAE,IAAI,UAAU;IACnC,MAAM,OAAO,KAAK;IAClB,IAAI,EAAE,gBAAgB,OACpB,MAAM,IAAI,cAAc,4CAA4C;KAAE,YAAY;KAAK,MAAM;IAAe,CAAC;IAC/G,IAAI,KAAK,OAAO,kBACd,MAAM,IAAI,cAAc,6BAA6B,KAAK,MAAM,mBAAmB,OAAO,IAAI,EAAE,KAAK;KAAE,YAAY;KAAK,MAAM;IAAmB,CAAC;IAEpJ,MAAM,UAAU,KAAK,KAAK,KAAK,QAAQ,WAAW,SAAS;IAC3D,GAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;IACzC,aAAa,KAAK,KAAK,SAAS,UAAU,KAAK,IAAI,EAAE,KAAK;IAC1D,GAAG,cAAc,YAAY,SAAO,KAAK,MAAM,KAAK,YAAY,CAAC,CAAC;IAClE,UAAU;IAEV,MAAM,aAAa,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,IAAI,KAAK,UAAU;IAChG,IAAI;IACJ,IAAI,eAAe,MACjB,IAAI;KACF,UAAU,KAAK,MAAM,UAAU;IACjC,QACM;KACJ,MAAM,IAAI,cAAc,8CAA8C;MAAE,YAAY;MAAK,MAAM;KAAkB,CAAC;IACpH;IAEF,UAAU,aAA6B,sBAAsB;KAC3D,GAAI,OAAO,KAAK,aAAa,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;KACvE,GAAI,eAAe,OAAO,CAAC,IAAI,EAAE,QAAQ;IAC3C,GAAG,MAAM;GACX,OACK;IACH,UAAU,aAA6B,sBAAsB,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,GAAG,MAAM;IACzG,IAAI,QAAQ,SAAS,KAAA,GACnB,MAAM,IAAI,cAAc,2CAA2C;KAAE,YAAY;KAAK,MAAM;IAAkB,CAAC;IACjH,UAAU,KAAK,QAAQ,QAAQ,QAAQ,IAAI;IAC3C,IAAI,YAAY,MACd,MAAM,IAAI,cAAc,kBAAkB;KAAE,YAAY;KAAK,MAAM;IAAiB,CAAC;GACzF;GAEA,MAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,SAAS;IAC/C;IACA,UAAU,QAAQ;IAClB,SAAS,QAAQ;GACnB,CAAC;GAED,IAAI,KAAK,eACP,OAAO,EAAE,KAAK,IAAI;GACpB,IAAI,KAAK,UAAU,KAAA,GACjB,MAAM,IAAI,cAAc,KAAK,OAAO;IAAE,YAAY;IAAK,MAAM;IAAkB,QAAQ;KAAE,OAAO,KAAK;KAAO,SAAS,KAAK;KAAS,SAAS,KAAK;IAAQ;GAAE,CAAC;GAC9J,IAAI,SACF,OAAO,KAAK,iBAAiB,KAAK,SAAS,OAAO,EAAE,IAAI,KAAK,QAAQ,KAAK,IAAI,GAAG;GAEnF,OAAO,EAAE,KAAK,IAAI;EACpB,UACQ;GAEN,IAAI,eAAe,MACjB,GAAG,OAAO,YAAY,EAAE,OAAO,KAAK,CAAC;EACzC;CACF,CAAC;AACL;;;CAjK2B,aAAA;CACJ,YAAA;CACmB,eAAA;CACb,cAAA;CACJ,eAAA;CACQ,aAAA;CAC8D,eAAA;CAGzF,mBAAmB;;;;;;;;;;ACVzB,SAAgB,cAAc,MAA2B,SAA0C;CACjG,mBAAmB;EACjB,KAAU,CAAC,CAAC,OAAO,UAAmB;GACpC,UAAU,KAAK;EACjB,CAAC;CACH,CAAC;AACH;;;;;;;;;;;ACGA,SAAgB,mBAAmB,MAAe;CAChD,OAAO,WAAW,UAAU,CAAC,CAC1B,KACC,aACA,cAAc;EACZ,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,WAAW;GAAG,KAAK,EAAE,aAAa,mCAAmC;EAAE;CAC1G,CAAC,IACA,MAAM;EACL,MAAM,QAAQ,EAAE,IAAI,OAAO,qBAAqB;EAChD,IAAI,UAAU,KAAA,KAAa,UAAU,KAAK,cACxC,MAAM,IAAI,cAAc,iBAAiB;GAAE,YAAY;GAAK,MAAM;EAAgB,CAAC;EACrF,IAAI,CAAC,kBAAkB,CAAC,GACtB,MAAM,IAAI,cAAc,gDAAgD;GAAE,YAAY;GAAK,MAAM;EAAe,CAAC;EAGnH,cAAc,KAAK,aAAY,UAAS,OAAO,MAAM,mBAAmB,KAAK,CAAC;EAE9E,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF;AACJ;;CAlC8B,cAAA;CACH,aAAA;CACJ,YAAA;CACW,cAAA;;;;;;;;ACgBlC,SAAgB,kBAAkB,MAAe;CAC/C,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,WACA,cAAc;EACZ,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,oBAAoB,EAAE;CACzD,CAAC,GACD,SAAS,SAAS,WAAW,IAC5B,MAAM;EACL,MAAM,QAAQ,EAAE,IAAI,MAAM,OAAO;EACjC,MAAM,WAAW,MAAM,YAAY;EACnC,MAAM,UAAU,MAAM,SAAS;EAE/B,OAAO,UAAU,GAAG,OAAO,WAAW;GACpC,IAAI,UAAU;GACd,IAAI,QAAuB,QAAQ,QAAQ;GAC3C,IAAI,SAAS;GAEb,MAAM,QAAQ,YAAuC;IACnD,IAAI,QACF,OAAO;IAGT,IAAI,QAAQ,SAAS,SAAS,UAAU,oBACtC,OAAO;IACT,WAAW;IACX,QAAQ,MACL,WAAW,OAAO,SAAS;KAAE,OAAO,QAAQ;KAAM,MAAM,KAAK,UAAU,OAAO;IAAE,CAAC,CAAC,CAAC,CACnF,YAAY;KACX,SAAS;IACX,CAAC,CAAC,CACD,cAAc;KACb,WAAW;IACb,CAAC;IACH,OAAO;GACT;GAEA,MAAM,cAAc,KAAK,IAAI,UAAU,WAAW,YAAY;IAC5D,IAAI,CAAC,WAAW,QAAQ,SAAS,OAC/B;IACF,KAAU,OAAO;GACnB,CAAC;GAED,OAAO,cAAc;IACnB,SAAS;IACT,YAAY;GACd,CAAC;GAED,MAAM,KAAK;IAAE,MAAM;IAAS,IAAI,KAAK,IAAI;IAAG,OAAO,KAAK,WAAW,SAAS;GAAE,CAAC;GAE/E,OAAO,MAAM;IACX,MAAM,OAAO,MAAM,gBAAgB;IACnC,IAAI,QACF;IACF,MAAM,OAAO,SAAS;KAAE,OAAO;KAAQ,MAAM,OAAO,KAAK,IAAI,CAAC;IAAE,CAAC;GACnE;EACF,CAAC;CACH,CACF;AACJ;;;CA9E2B,aAAA;CACF,eAAA;CAEnB,qBAAqB;CACrB,mBAAmB;CAEnB,cAAc,KAAK;;EAEvB,aAAa;;EAEb,SAAS;CACX,CAAC;;;;;;;;;;ACHD,SAAgB,kBAAkB,MAAe;CAC/C,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,YACA,cAAc;EACZ,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW;GACT,KAAK;IACH,aAAa;IACb,SAAS,SAAS,KAAK;KACrB,UAAU;KACV,YAAY;KACZ,YAAY,KAAK;MAAE,OAAO;MAAU,SAAS;MAAU,SAAS;MAAU,WAAW;KAAS,CAAC;KAC/F,eAAe;IACjB,CAAC,CAAC;GACJ;GACA,KAAK,EAAE,aAAa,kCAAkC;EACxD;CACF,CAAC,IACA,MAAM;EACL,MAAM,QAAQ,KAAK,WAAW,SAAS;EACvC,MAAM,SAAS,MAAM,QAAQ,QAAO,WAAU,OAAO,OAAO,aAAa,OAAO,WAAW,SAAS;EACpG,MAAM,gBAAgB,KAAK,KAAK,SAAS,KAAK,KAAK,gBAAgB,EAAE,IAAI,OAAO,QAAQ,CAAC,CAAC,MAAM;EAEhG,OAAO,EAAE,KAAK;GACZ,QAAQ,OAAO,SAAS,IAAI,aAAa;GACzC,UAAU,KAAK,MAAM,QAAQ,OAAO,IAAI,GAAI;GAC5C,GAAI,gBACA;IACE,SAAS;KACP,OAAO,MAAM,QAAQ;KACrB,SAAS,MAAM,QAAQ,QAAO,WAAU,OAAO,WAAW,SAAS,CAAC,CAAC;KACrE,SAAS,MAAM,QAAQ,QAAO,WAAU,OAAO,WAAW,SAAS,CAAC,CAAC;KACrE,WAAW,MAAM,QAAQ,QAAO,WAAU,OAAO,WAAW,WAAW,CAAC,CAAC;IAC3E;IACA,YAAY,MAAM,KAAK;GACzB,IACA,CAAC;EACP,GAAG,OAAO,SAAS,IAAI,MAAM,GAAG;CAClC,CACF;AACJ;;CAnD2B,aAAA;CACF,eAAA;;;;ACczB,SAAS,gBAAc,IAA2B;CAChD,OAAO,IAAI,cAAc,mBAAmB,GAAG,IAAI;EAAE,YAAY;EAAK,MAAM;CAAiB,CAAC;AAChG;AAEA,SAAgB,gBAAgB,MAAe;CAC7C,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,SACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW,EAAE,KAAK;GAAE,aAAa;GAAe,SAAS,SAAS,oBAAoB;EAAE,EAAE;CAC5F,CAAC,IACD,MAAK,EAAE,KAAK,EACV,SAAS,KAAK,WAAW,MAAM,CAAC,CAAC,KAAI,YAAW;EAC9C,UAAU,OAAO;EACjB,OAAO,OAAO,OAAO,SAAS,OAAO;EACrC,QAAQ,OAAO;EACf,GAAG,KAAK,SAAS,KAAK,OAAO,EAAE;CACjC,EAAE,EACJ,CAAC,CACH,CAAC,CAEA,IACC,aACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,QAAQ;GAAG,KAAK,gBAAgB;EAAK;CACxE,CAAC,GACD,SAAS,SAAS,SAAO,GACzB,SAAS,SAAS,qBAAqB,IACtC,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI,CAAC,KAAK,MAAM,UAAU,EAAE,GAC1B,MAAM,gBAAc,EAAE;EAExB,MAAM,QAAQ,EAAE,IAAI,MAAM,OAAO;EACjC,MAAM,YAAY,MAAM,SAAS,KAAA,IAAY,eAAe,OAAO,SAAS,MAAM,MAAM,EAAE;EAC1F,MAAM,OAAO,OAAO,MAAM,SAAS,IAAI,eAAe,KAAK,IAAI,KAAK,IAAI,WAAW,QAAQ,GAAG,QAAQ;EAEtG,MAAM,OAAO,KAAK,SAAS,KAAK,EAAE;EAGlC,MAAM,SAAS,MAAM,QAAQ,KAAK,KAAK;EACvC,MAAM,SAAS,OAAO,SAAS,IAAI,KAAK,IAAI,MAAM,QAAQ,IAAI;EAE9D,IAAI,QAAQ,KAAK,SAAS,SAAS,IAAI,MAAM;EAC7C,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,OAAO,SAAS,GACtD,QAAQ,MAAM,QAAO,SAAQ,KAAK,WAAW,MAAM,MAAM;EAC3D,IAAI,OAAO,SAAS,GAAG;GACrB,MAAM,SAAS,OAAO,YAAY;GAClC,QAAQ,MAAM,QAAO,SAAQ,KAAK,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC;EACvE;EAEA,OAAO,EAAE,KAAK;GACZ,UAAU;GACV,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,OAAO,KAAK,MAAM,KAAI,SAAQ,KAAK,IAAI;GACvC,UAAU,OAAO,SAAS,IAAI,SAAS;GACvC,OAAO,MAAM,MAAM,CAAC,IAAI;EAC1B,CAAC;CACH,CACF,CAAC,CAGA,IACC,sBACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,uBAAuB;GAAG,KAAK,gBAAgB;EAAK;CACvF,CAAC,GACD,SAAS,SAAS,SAAO,GACzB,SAAS,SAAS,aAAa,IAC9B,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI,CAAC,KAAK,MAAM,UAAU,EAAE,GAC1B,MAAM,gBAAc,EAAE;EAExB,MAAM,YAAY,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,QAAQ,GAAG,GAAG;EAErD,IAAI,CADU,KAAK,SAAS,KAAK,EAAE,CAAC,CAAC,MAAM,KAAI,SAAQ,KAAK,IACvD,CAAA,CAAM,SAAS,SAAS,GAC3B,MAAM,IAAI,cAAc,oBAAoB;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAE3F,MAAM,OAAO,KAAK,KAAK,KAAK,SAAS,WAAW,SAAS;EACzD,MAAM,OAAO,GAAG,aAAa,IAAI;EACjC,OAAO,EAAE,KAAK,MAAM,KAAK;GACvB,gBAAgB;GAChB,kBAAkB,OAAO,KAAK,UAAU;GACxC,uBAAuB,yBAAyB,UAAU;EAC5D,CAAC;CACH,CACF,CAAC,CAEA,OACC,aACA,cAAc;EACZ,MAAM,CAAC,MAAM;EACb,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,UAAU;GAAG,KAAK,gBAAgB;EAAK;CAC1E,CAAC,GACD,SAAS,SAAS,SAAO,IACxB,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI,CAAC,KAAK,MAAM,UAAU,EAAE,GAC1B,MAAM,gBAAc,EAAE;EACxB,KAAK,SAAS,MAAM,EAAE;EACtB,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF;AACJ;;;CA7H2B,aAAA;CACe,eAAA;CACjB,eAAA;CACmC,eAAA;CAGtD,WAAW;CACX,WAAW;CACX,eAAe;CAEf,YAAU,KAAK,EAAE,IAAI,cAAc,CAAC;CACpC,gBAAgB,KAAK,EAAE,SAAS,SAAS,CAAC;;;;;;;;ACThD,SAAgB,mBAAmB,MAAe;CAChD,OAAO,WAAW,UAAU,CAAC,CAC1B,IAAI,YAAY,cAAc;EAC7B,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,4BAA4B,EAAE;CACjE,CAAC,IAAI,MAAM;EACT,MAAM,QAAQ,KAAK,WAAW,SAAS;EACvC,MAAM,QAAkB,CAAC;EAEzB,MAAM,UAAU,MAAc,MAAc,YAA4B;GACtE,IAAI,QAAQ,WAAW,GACrB;GACF,MAAM,KAAK,UAAU,KAAK,GAAG,QAAQ,UAAU,KAAK,SAAS,GAAG,OAAO;EACzE;EAEA,OAAO,kBAAkB,4BAA4B,CAAC,kBAAkB,CAAC;EACzE,OAAO,qBAAqB,sBAAsB,CAAC,qBAAqB,MAAM,QAAQ,QAAQ,CAAC;EAG/F,OAAO,iBAAiB,6BADb,MAAM,QAAQ,KAAI,WAAU,yBAAyB,OAAO,GAAG,KAAK,OAAO,WAAW,YAAY,IAAI,GAC5D,CAAE;EAGvD,OAAO,6BAA6B,4CADnB,MAAM,QAAQ,KAAI,WAAU,qCAAqC,OAAO,GAAG,KAAK,OAAO,UACxB,CAAQ;EAGxF,OAAO,0BAA0B,gCADjB,MAAM,QAAQ,KAAI,WAAU,kCAAkC,OAAO,GAAG,KAAK,OAAO,QAAQ,SAC3C,CAAO;EAKxE,OAAO,+BAA+B,gDAHvB,MAAM,QAClB,QAAO,WAAU,OAAO,QAAQ,gBAAgB,IAAI,CAAC,CACrD,KAAI,WAAU,uCAAuC,OAAO,GAAG,KAAK,OAAO,QAAQ,YAAa,QAAQ,CAAC,GACtB,CAAM;EAK5F,OAAO,0BAA0B,6CAHhB,MAAM,QACpB,QAAO,WAAU,OAAO,eAAe,IAAI,CAAC,CAC5C,KAAI,WAAU,kCAAkC,OAAO,GAAG,KAAK,OAAO,YACK,CAAQ;EAKtF,OAAO,wBAAwB,kCAHnB,MAAM,QACf,QAAO,WAAU,OAAO,WAAW,YAAY,IAAI,CAAC,CACpD,KAAI,WAAU,gCAAgC,OAAO,GAAG,KAAK,OAAO,UAAW,UACjB,CAAG;EAKpE,OAAO,0BAA0B,0CAHrB,MAAM,QACf,QAAO,WAAU,OAAO,WAAW,cAAc,IAAI,CAAC,CACtD,KAAI,WAAU,kCAAkC,OAAO,GAAG,KAAK,OAAO,UAAW,YACT,CAAG;EAG9E,OAAO,8BAA8B,0CADvB,MAAM,KAAK,MAAM,KAAI,SAAQ,qCAAqC,KAAK,KAAK,KAAK,KAAK,YAAY,QAAQ,CAAC,GAC1C,CAAK;EAEpF,OAAO,gCAAgC,wBAAwB,CAAC,gCAAgC,MAAM,KAAK,kBAAkB,QAAQ,CAAC,GAAG,CAAC;EAC1I,OAAO,8BAA8B,sBAAsB,CAAC,8BAA8B,MAAM,KAAK,gBAAgB,QAAQ,CAAC,GAAG,CAAC;EAClI,OAAO,0BAA0B,iCAAiC,CAChE,4BAA4B,MAAM,KAAK,QAAQ,MAAM,KAAK,KAAK,IAAI,GAAG,MAAM,KAAK,IAAI,EAAA,CAAG,QAAQ,CAAC,GACnG,CAAC;EAED,OAAO,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,gBAAgB,2CAA2C,CAAC;CAC5G,CAAC;AACL;;CAjE2B,aAAA;;;;ACgB3B,SAAgB,OAAO,OAAoB;CACzC,MAAM,SAAS,KAAK,IAAI,KAAK;CAC7B,IAAI,QACF,OAAO;CAET,MAAM,MAAM,IAAI,IAAU,OAAO,EAAE,QAAQ,EAAE,gBAAgB,GAAG,EAAE,CAAC;CACnE,IAAI,IAAI,OAAO,IAAI,UAAU;EAAE,kBAAkB;EAAG,iBAAiB;CAAG,CAAC,CAAC;CAC1E,KAAK,IAAI,OAAO,GAAG;CACnB,OAAO;AACT;;AAGA,SAAgB,aAAmB;CACjC,KAAK,MAAM;AACb;AAEA,SAAgB,WAAW,OAAuB;CAChD,OAAO,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM;AAChF;AAEA,SAAgB,sBAAsB,OAAe,OAAyB;CAC5E,MAAM,OAAO,MAAM,QAAO,SAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAI,SAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI;CACjG,OAAO,MAAM,WAAW,KAAK,EAAE,MAAM,KAAK,SAAS,IAAI,KAAK,SAAS;AACvE;;AAQA,SAAgB,sBAAsB,OAAwB;CAC5D,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,YAAY;EAClB,MAAM,cAAc,UAAU,eAAe,UAAU;EACvD,IAAI,aAGF,OAAO,GAAG,cAFG,UAAU,eAAe,KAAA,IAAY,KAAK,KAAK,UAAU,WAAW,KACnE,UAAU,YAAY,gBAAgB,KAAA,IAAY,KAAK,cAAc,UAAU,WAAW,YAAY;CAGxH;CACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,eAAsB,oBAAoB,OAAe,QAAgB,MAAwC;CAC/G,IAAI;EACF,MAAM,OAAO,KAAK,CAAC,CAAC,IAAI,YAAY,QAAQ,MAAM;GAChD,YAAY;GACZ,sBAAsB,EAAE,aAAa,KAAK;EAC5C,CAAC;EACD,OAAO,EAAE,IAAI,KAAK;CACpB,SACO,OAAO;EACZ,OAAO;GAAE,IAAI;GAAO,OAAO,sBAAsB,KAAK;EAAE;CAC1D;AACF;AAEA,eAAsB,oBAAoB,OAA4E;CACpH,IAAI;EAEF,OAAO;GAAE,IAAI;GAAM,WAAU,MADZ,OAAO,KAAK,CAAC,CAAC,IAAI,MAAM,EAAA,CACT;EAAS;CAC3C,SACO,OAAO;EACZ,OAAO;GAAE,IAAI;GAAO,OAAO,sBAAsB,KAAK;EAAE;CAC1D;AACF;;;;;;AAYA,eAAsB,kBAAkB,OAAgF;CACtH,IAAI;EACF,MAAM,UAAU,MAAM,OAAO,KAAK,CAAC,CAAC,IAAI,WAAW;GACjD,OAAO;GACP,iBAAiB;IAAC;IAAW;IAAgB;GAAgB;EAC/D,CAAC;EAED,MAAM,wBAAQ,IAAI,IAA0B;EAC5C,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,OAAO,OAAO,SAAS,QAAQ,OAAO,cAAc,QAAQ,OAAO,gBAAgB;GACzF,IAAI,CAAC,MACH;GACF,MAAM,QAAQ,WAAW,QAAQ,KAAK,QAClC,KAAK,QACL,cAAc,QAAQ,KAAK,WACzB,IAAI,KAAK,aACT,gBAAgB,QAAQ,KAAK,aAC3B,KAAK,aACL;GACR,MAAM,IAAI,OAAO,KAAK,EAAE,GAAG;IAAE,IAAI,KAAK;IAAI;GAAM,CAAC;EACnD;EAEA,OAAO;GAAE,IAAI;GAAM,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;EAAE;CAChD,SACO,OAAO;EACZ,OAAO;GAAE,IAAI;GAAO,OAAO,CAAC;GAAG,OAAO,sBAAsB,KAAK;EAAE;CACrE;AACF;;;CA1GM,uBAAO,IAAI,IAAiB;;;;;;;;;ACDlC,SAAgB,yBAAyB,MAAe;CACtD,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,wBACA,cAAc;EACZ,MAAM,CAAC,eAAe;EACtB,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,SAAS;GAAG,KAAK,gBAAgB;EAAK;CACzE,CAAC,GACD,SAAS,QAAQ,mBAAmB,GACpC,OAAO,MAAM;EACX,MAAM,EAAE,aAAa,EAAE,IAAI,MAAM,MAAM;EACvC,MAAM,WAAW,MAAM,KAAK,cAAc,YAAY,QAAQ;EAC9D,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,cAAc,gCAAgC,SAAS,SAAS,mBAAmB;GAAE,YAAY;GAAK,MAAM;EAA0B,CAAC;EAEnJ,KAAK,QAAQ,iBAAiB,QAAQ;EACtC,WAAW;EACX,OAAO,EAAE,KAAK;GAAE,IAAI;GAAM,UAAU,SAAS,YAAY;EAAK,CAAC;CACjE,CACF,CAAC,CAEA,OACC,wBACA,cAAc;EACZ,MAAM,CAAC,eAAe;EACtB,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,UAAU,EAAE;CAC/C,CAAC,IACA,MAAM;EACL,KAAK,QAAQ,iBAAiB,IAAI;EAClC,WAAW;EACX,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF,CAAC,CAEA,KACC,uBACA,cAAc;EACZ,MAAM,CAAC,eAAe;EACtB,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,kBAAkB;GAAG,KAAK,gBAAgB;EAAK;CAClF,CAAC,GACD,SAAS,QAAQ,wBAAwB,GACzC,OAAO,MAAM;EACX,MAAM,SAAS,MAAM,KAAK,cAAc,SAAS,EAAE,IAAI,MAAM,MAAM,CAAC;EACpE,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,cAAc,OAAO,SAAS,2BAA2B;GAAE,YAAY;GAAK,MAAM;EAAuB,CAAC;EACtH,OAAO,EAAE,KAAK,MAAM;CACtB,CACF,CAAC,CAEA,KACC,+BACA,cAAc;EACZ,MAAM,CAAC,eAAe;EACtB,SAAS;EACT,WAAW;GACT,KAAK;IAAE,aAAa;IAAS,SAAS,SAAS,KAAK,EAAE,OAAO,KAAK;KAAE,IAAI;KAAmB,OAAO;IAAS,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;GAAE;GAC1H,KAAK,gBAAgB;EACvB;CACF,CAAC,GACD,SAAS,QAAQ,wBAAwB,GACzC,OAAO,MAAM;EACX,MAAM,SAAS,MAAM,KAAK,cAAc,YAAY,EAAE,IAAI,MAAM,MAAM,CAAC;EACvE,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,cAAc,OAAO,SAAS,wBAAwB;GAAE,YAAY;GAAK,MAAM;EAAuB,CAAC;EACnH,OAAO,EAAE,KAAK,EAAE,OAAO,OAAO,MAAM,CAAC;CACvC,CACF;AACJ;;CAjF2B,aAAA;CACe,eAAA;CACjB,eAAA;CACE,cAAA;CACmC,eAAA;;;;;;CCEvD,eAAA;CAMM,eAAe,KAAK;EAC/B,SAAS;EACT,SAAS,cAAc,eAAe,CAAC,EAAE;EACzC,UAAU,eAAe,eAAe,CAAC,EAAE;EAC3C,MAAM,WAAW,eAAe,CAAC,EAAE;EACnC,eAAe,oBAAoB,eAAe,CAAC,EAAE;EACrD,MAAM,WAAW,eAAe,CAAC,EAAE;EACnC,SAAS,cAAc,eAAe,CAAC,EAAE;EACzC,SAAS,aAAa,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;CAChD,CAAC,CAAC,CAAC,gBAAgB,QAAQ;;;;;;CChBd,cAAyB;EACpC,SAAS;EACT,SAAS;GACP,MAAM;GACN,MAAM;GACN,aAAa;EACf;EACA,UAAU;GACR,SAAS;GACT,WAAW;GACX,MAAM;GACN,gBAAgB;EAClB;EACA,SAAS,CAAC;CACZ;;;;ACeA,SAAS,aAAa,QAA6B;CACjD,OAAO,OAAO;AAChB;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;AAGA,SAAS,qBAAqB,SAAmC;CAC/D,MAAM,MAAM,IAAI,IAAI,QAAQ,KAAI,WAAU,OAAO,EAAE,CAAC;CACpD,MAAM,SAAmB,CAAC;CAE1B,KAAK,MAAM,UAAU,SACnB,KAAK,MAAM,cAAc,OAAO,WAC9B,IAAI,eAAe,OAAO,IACxB,OAAO,KAAK,IAAI,OAAO,GAAG,oBAAoB;MAC3C,IAAI,CAAC,IAAI,IAAI,UAAU,GAC1B,OAAO,KAAK,IAAI,OAAO,GAAG,+BAA+B,WAAW,EAAE;CAI5E,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,OAAO,IAAI,IAAI,QAAQ,KAAI,WAAU,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;CAE/D,MAAM,QAAQ,IAAY,UAA0B;EAClD,IAAI,KAAK,IAAI,EAAE,GACb;EACF,IAAI,SAAS,IAAI,EAAE,GAAG;GACpB,OAAO,KAAK,qBAAqB,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,KAAK,MAAM,GAAG;GAC9D;EACF;EACA,SAAS,IAAI,EAAE;EACf,KAAK,MAAM,cAAc,KAAK,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC,GACnD,IAAI,IAAI,IAAI,UAAU,KAAK,eAAe,IACxC,KAAK,YAAY,CAAC,GAAG,OAAO,EAAE,CAAC;EAEnC,SAAS,OAAO,EAAE;EAClB,KAAK,IAAI,EAAE;CACb;CAEA,KAAK,MAAM,UAAU,SAAS,KAAK,OAAO,IAAI,CAAC,CAAC;CAChD,OAAO;AACT;;AAGA,SAAS,WAAW,QAAiC,OAAgC,WAA8B;CACjH,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,IAAI,UAAU,KAAA,GACZ;EACF,IAAI,UAAU,IAAI,GAAG,KAAK,SAAS,KAAK,KAAK,SAAS,OAAO,IAAI,GAAG;GAClE,OAAO,OAAO,WAAW,OAAO,MAAM,KAAK;GAC3C;EACF;EACA,OAAO,OAAO;CAChB;AACF;;;;;;;AAQA,SAAS,WAAW,QAAiC,OAAyD;CAC5G,MAAM,SAAS,EAAE,GAAG,OAAO;CAC3B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,IAAI,UAAU,KAAA,GACZ;EACF,IAAI,UAAU,MAAM;GAClB,OAAO,OAAO;GACd;EACF;EACA,IAAI,SAAS,KAAK,KAAK,SAAS,OAAO,IAAI,GAAG;GAC5C,OAAO,OAAO,WAAW,OAAO,MAAiC,KAAK;GACtE;EACF;EACA,OAAO,OAAO;CAChB;CACA,OAAO;AACT;;;CAhGO,YAAA;CACqB,UAAA;CACI,YAAA;CACC,WAAA;CAG3B,oCAAoB,IAAI,IAAI;EAAC;EAAW;EAAU;CAAM,CAAC;CACzD,qCAAqB,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;CAC5C,0CAA0B,IAAI,IAAI,CAAC,UAAU,CAAC;CAC9C,mCAAmB,IAAI,IAAY;CAE5B,cAAb,cAAiC,MAAM;EACrC,OAAgB;CAClB;CAqFa,cAAb,MAAyB;EAMM;EAA+B;EAL5D,MAAyB,CAAC;EAC1B;EACA,QAA+B;EAC/B,4BAA6B,IAAI,IAAgB;EAEjD,YAAY,MAA+B,OAAmC,aAAa;GAA9D,KAAA,OAAA;GAA+B,KAAA,OAAA;EAAgC;EAE5F,IAAI,OAAe;GACjB,OAAO,KAAK;EACd;EAEA,IAAI,SAAyB;GAC3B,OAAO,KAAK;EACd;EAEA,IAAI,cAA6B;GAC/B,OAAO,KAAK;EACd;EAEA,IAAI,UAA0B;GAC5B,OAAO,KAAK,eAAe;EAC7B;EAEA,IAAI,WAAuC;GACzC,OAAO,KAAK,eAAe;EAC7B;EAEA,IAAI,YAAuB;GACzB,OAAO,gBAAgB,KAAK,GAAG;EACjC;EAEA,SAAS,UAAkC;GACzC,KAAK,UAAU,IAAI,QAAQ;GAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;EAC7C;EAEA,UAAU,IAAsC;GAC9C,OAAO,KAAK,QAAQ,MAAK,WAAU,OAAO,OAAO,EAAE;EACrD;;;;;EAMA,OAAa;GACX,KAAK,KAAK;GACV,KAAK,OAAO;EACd;EAEA,OAAqB;GACnB,IAAI,CAAC,GAAG,WAAW,KAAK,IAAI,GAAG;IAE7B,MAAM,OAAO,gBAAgB,KAAK,IAAI;IACtC,gBAAgB,KAAK,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,GAAG;IAC/D,KAAK,MAAM;IACX,KAAK,MAAM,IAAI;IACf;GACF;GAEA,IAAI;GACJ,IAAI;IACF,SAAS,KAAK,MAAM,GAAG,aAAa,KAAK,MAAM,MAAM,CAAC;GACxD,SACO,OAAO;IACZ,KAAK,QAAQ,gBAAgB,KAAK,SAAS,KAAK,IAAI,EAAE,IAAK,MAAgB;IAC3E,KAAK,MAAM,CAAC;IACZ,KAAK,iBAAiB,KAAK,gBAAgB;IAC3C;GACF;GAEA,IAAI,CAAC,SAAS,MAAM,GAAG;IACrB,KAAK,QAAQ,GAAG,KAAK,SAAS,KAAK,IAAI,EAAE;IACzC,KAAK,MAAM,CAAC;IACZ,KAAK,iBAAiB,KAAK,gBAAgB;IAC3C;GACF;GAEA,KAAK,MAAM,MAAmB;EAChC;EAEA,SAAuB;GACrB,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS;EAClD;EAEA,aAAa,IAAY,OAAkC;GACzD,MAAM,QAAQ,KAAK,IAAI,SAAS,WAAU,UAAS,MAAM,OAAO,EAAE,KAAK;GACvE,IAAI,QAAQ,GACV,MAAM,IAAI,YAAY,mBAAmB,GAAG,EAAE;GAEhD,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,QAAQ,MAAM,QAAS;GAE7B,WAAW,OAAO,OAAkC,iBAAiB;GAErE,MAAM,YAAY,KAAK,eAAe,OAAO,WAAW,MAAM,EAAE;GAChE,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,cAAc,OAA6D;GACzE,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,UAAU,EAAE,GAAI,MAAM,WAAW,CAAC,EAAG;GAC3C,WAAW,MAAM,SAAS,OAAkC,kBAAkB;GAE9E,MAAM,UAAU,cAAc,MAAM,OAAO;GAC3C,IAAI,mBAAmB,KAAK,QAC1B,MAAM,IAAI,YAAY,YAAY,aAAa,OAAO,GAAG;GAE3D,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,WAAW,OAAuD;GAChE,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,OAAO,EAAE,GAAI,MAAM,QAAQ,CAAC,EAAG;GACrC,WAAW,MAAM,MAAM,OAAkC,gBAAgB;GAEzE,MAAM,OAAO,WAAW,MAAM,IAAI;GAClC,IAAI,gBAAgB,KAAK,QACvB,MAAM,IAAI,YAAY,SAAS,aAAa,IAAI,GAAG;GAErD,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,oBAAoB,OAAyE;GAC3F,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,gBAAgB,EAAE,GAAI,MAAM,iBAAiB,CAAC,EAAG;GACvD,WAAW,MAAM,eAAe,OAAkC,uBAAuB;GAEzF,MAAM,gBAAgB,oBAAoB,MAAM,aAAa;GAC7D,IAAI,yBAAyB,KAAK,QAChC,MAAM,IAAI,YAAY,kBAAkB,aAAa,aAAa,GAAG;GAEvE,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,WAAW,OAAuD;GAChE,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,OAAO,EAAE,GAAI,MAAM,QAAQ,CAAC,EAAG;GACrC,WAAW,MAAM,MAAM,OAAkC,gBAAgB;GAEzE,MAAM,OAAO,WAAW,MAAM,IAAI;GAClC,IAAI,gBAAgB,KAAK,QACvB,MAAM,IAAI,YAAY,SAAS,aAAa,IAAI,GAAG;GAErD,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,cAAc,OAA6D;GACzE,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,UAAU,EAAE,GAAI,MAAM,WAAW,CAAC,EAAG;GAC3C,WAAW,MAAM,SAAS,OAAkC,gBAAgB;GAE5E,MAAM,UAAU,cAAc,MAAM,OAAO;GAC3C,IAAI,mBAAmB,KAAK,QAC1B,MAAM,IAAI,YAAY,YAAY,aAAa,OAAO,GAAG;GAE3D,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,eAAe,OAA+D;GAC5E,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,WAAW,EAAE,GAAI,MAAM,YAAY,CAAC,EAAG;GAC7C,WAAW,MAAM,UAAU,OAAkC,iBAAiB;GAE9E,MAAM,WAAW,eAAe,MAAM,QAAQ;GAC9C,IAAI,oBAAoB,KAAK,QAC3B,MAAM,IAAI,YAAY,aAAa,aAAa,QAAQ,GAAG;GAE7D,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,UAAU,OAA8C;GACtD,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,YAAY,CAAC;GACnB,IAAI,MAAM,QAAQ,MAAK,UAAS,MAAM,OAAO,MAAM,EAAE,GACnD,MAAM,IAAI,YAAY,WAAW,OAAO,MAAM,EAAE,EAAE,iBAAiB;GAGrE,MAAM,QAAQ,MAAM,QAAQ;GAC5B,MAAM,QAAQ,KAAK,gBAAgB,KAAK,CAAC;GACzC,MAAM,YAAY,KAAK,eAAe,MAAM,QAAQ,QAAS,WAAW,MAAM,EAAE;GAChF,KAAK,OAAO,KAAK;GACjB,OAAO;EACT;EAEA,aAAa,IAAkB;GAC7B,MAAM,QAAQ,gBAAgB,KAAK,GAAG;GACtC,MAAM,SAAS,MAAM,SAAS,UAAU;GACxC,MAAM,WAAW,MAAM,WAAW,CAAC,EAAA,CAAG,QAAO,UAAS,MAAM,OAAO,EAAE;GACrE,IAAI,MAAM,QAAQ,WAAW,QAC3B,MAAM,IAAI,YAAY,mBAAmB,GAAG,EAAE;GAChD,KAAK,OAAO,KAAK;EACnB;;EAGA,kBAAwB;GACtB,MAAM,SAAS,KAAK,UAAU,aAAa,aAAa,GAAG,MAAM,CAAC;GAElE,KADgB,GAAG,WAAW,gBAAgB,IAAI,GAAG,aAAa,kBAAkB,MAAM,IAAI,UAC9E,QACd,gBAAgB,kBAAkB,MAAM;EAC5C;EAEA,eAAuB,OAAgC,OAA6B;GAClF,MAAM,SAAS,aAAa;IAAE,GAAG,KAAK;IAAU,GAAG;GAAM,CAAC;GAC1D,IAAI,kBAAkB,KAAK,QACzB,MAAM,IAAI,YAAY,GAAG,MAAM,IAAI,aAAa,MAAM,GAAG;GAC3D,OAAO;IAAE,GAAG;IAAQ,MAAM,OAAO,QAAQ;GAAK;EAChD;EAEA,OAAe,OAAwB;GACrC,gBAAgB,KAAK,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;GAChE,KAAK,MAAM;GACX,KAAK,MAAM,KAAK;GAChB,KAAK,OAAO;EACd;EAEA,kBAA0C;GACxC,MAAM,UAAU,cAAc,CAAC,CAAC;GAChC,MAAM,WAAW,eAAe,CAAC,CAAC;GAClC,IAAI,mBAAmB,KAAK,UAAU,oBAAoB,KAAK,QAC7D,MAAM,IAAI,YAAY,4CAA4C;GAEpE,MAAM,OAAO,WAAW,CAAC,CAAC;GAC1B,MAAM,gBAAgB,oBAAoB,CAAC,CAAC;GAC5C,MAAM,OAAO,WAAW,CAAC,CAAC;GAC1B,MAAM,UAAU,cAAc,CAAC,CAAC;GAChC,IAAI,gBAAgB,KAAK,UAAU,yBAAyB,KAAK,UAAU,gBAAgB,KAAK,UAAU,mBAAmB,KAAK,QAChI,MAAM,IAAI,YAAY,8CAA8C;GAEtE,OAAO;IAAE;IAAS;IAAU;IAAM;IAAe;IAAM;IAAS,SAAS,CAAC;GAAE;EAC9E;EAEA,MAAc,KAAsB;GAClC,KAAK,MAAM;GACX,MAAM,SAAmB,CAAC;GAE1B,MAAM,UAAU,cAAc,IAAI,WAAW,CAAC,CAAC;GAC/C,MAAM,WAAW,eAAe,IAAI,YAAY,CAAC,CAAC;GAClD,MAAM,OAAO,WAAW,IAAI,QAAQ,CAAC,CAAC;GACtC,MAAM,gBAAgB,oBAAoB,IAAI,iBAAiB,CAAC,CAAC;GACjE,MAAM,OAAO,WAAW,IAAI,QAAQ,CAAC,CAAC;GACtC,MAAM,UAAU,cAAc,IAAI,WAAW,CAAC,CAAC;GAC/C,IAAI,mBAAmB,KAAK,QAC1B,OAAO,KAAK,YAAY,aAAa,OAAO,GAAG;GACjD,IAAI,oBAAoB,KAAK,QAC3B,OAAO,KAAK,aAAa,aAAa,QAAQ,GAAG;GACnD,IAAI,gBAAgB,KAAK,QACvB,OAAO,KAAK,SAAS,aAAa,IAAI,GAAG;GAC3C,IAAI,yBAAyB,KAAK,QAChC,OAAO,KAAK,kBAAkB,aAAa,aAAa,GAAG;GAC7D,IAAI,gBAAgB,KAAK,QACvB,OAAO,KAAK,SAAS,aAAa,IAAI,GAAG;GAC3C,IAAI,mBAAmB,KAAK,QAC1B,OAAO,KAAK,YAAY,aAAa,OAAO,GAAG;GAEjD,MAAM,mBAAmB,oBAAoB,KAAK,SAAS,eAAe,CAAC,CAAC,IAAkC;GAC9G,MAAM,UAA0B,CAAC;GACjC,MAAM,uBAAO,IAAI,IAAY;GAG7B,CAFmB,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,CAAC,EAAA,CAEpD,SAAS,OAAO,UAAU;IACnC,MAAM,SAAS,aAAa;KAAE,GAAG;KAAkB,GAAG;IAAM,CAAC;IAC7D,IAAI,kBAAkB,KAAK,QAAQ;KACjC,OAAO,KAAK,WAAW,MAAM,KAAM,OAA2B,MAAM,QAAQ,KAAK,aAAa,MAAM,GAAG;KACvG;IACF;IACA,IAAI,KAAK,IAAI,OAAO,EAAE,GAAG;KACvB,OAAO,KAAK,WAAW,MAAM,mBAAmB,OAAO,GAAG,EAAE;KAC5D;IACF;IACA,KAAK,IAAI,OAAO,EAAE;IAClB,QAAQ,KAAK;KAAE,GAAG;KAAQ,MAAM,OAAO,QAAQ;IAAK,CAAC;GACvD,CAAC;GAED,OAAO,KAAK,GAAG,qBAAqB,OAAO,CAAC;GAE5C,KAAK,QAAQ,OAAO,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI;GACrD,KAAK,iBAAiB;IACpB,SAAS,IAAI;IACb,SAAS,mBAAmB,KAAK,SAAS,cAAc,CAAC,CAAC,IAAiC;IAC3F,UAAU;IACV,MAAM,gBAAgB,KAAK,SAAS,WAAW,CAAC,CAAC,IAA8B;IAC/E,eAAe,yBAAyB,KAAK,SACzC,oBAAoB,CAAC,CAAC,IACtB;IACJ,MAAM,gBAAgB,KAAK,SAAS,WAAW,CAAC,CAAC,IAA8B;IAC/E,SAAS,mBAAmB,KAAK,SAAS,cAAc,CAAC,CAAC,IAAiC;IAC3F;GACF;EACF;CACF;;;;;ACjZA,SAAS,UAAU,QAA0D;CAC3E,IAAI,OAAO,IACT,OAAO;CACT,OAAO,OAAO,OAAO,WAAW,gBAAgB,IAAI,MAAM;AAC5D;AAEA,SAAS,cAAc,IAA2B;CAChD,OAAO,IAAI,cAAc,mBAAmB,GAAG,IAAI;EAAE,YAAY;EAAK,MAAM;CAAiB,CAAC;AAChG;AAEA,SAAgB,mBAAmB,MAAe;CAChD,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,KACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW,EAAE,KAAK;GAAE,aAAa;GAAe,SAAS,SAAS,eAAe;EAAE,EAAE;CACvF,CAAC,IACD,MAAK,EAAE,KAAK,EAAE,SAAS,KAAK,WAAW,MAAM,EAAE,CAAC,CAClD,CAAC,CAEA,KACC,KACA,cAAc;EACZ,MAAM,CAAC,SAAS;EAChB,SAAS;EACT,WAAW;GACT,KAAK;IAAE,aAAa;IAAW,SAAS,SAAS,cAAc;GAAE;GACjE,KAAK,gBAAgB;EACvB;CACF,CAAC,GACD,SAAS,QAAQ,kBAAkB,IAClC,MAAM;EACL,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,IAAI;GACF,OAAO,EAAE,KAAK,EAAE,QAAQ,KAAK,MAAM,UAAU,IAAI,EAAE,GAAG,GAAG;EAC3D,SACO,OAAO;GACZ,IAAI,iBAAiB,aACnB,MAAM,IAAI,cAAc,MAAM,SAAS;IAAE,YAAY;IAAK,MAAM;GAAiB,CAAC;GACpF,MAAM;EACR;CACF,CACF,CAAC,CAGA,KACC,cACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAA8B,WAAW,EAAE,KAAK;GAAE,aAAa;GAAe,SAAS,SAAS,eAAe;EAAE,EAAE;CAAE,CAAC,GAClK,OAAO,MAAM;EACX,MAAM,KAAK,WAAW,SAAS;EAC/B,OAAO,EAAE,KAAK,EAAE,SAAS,KAAK,WAAW,MAAM,EAAE,CAAC;CACpD,CACF,CAAC,CAEA,KACC,aACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAqB,WAAW,EAAE,KAAK;GAAE,aAAa;GAAe,SAAS,SAAS,eAAe;EAAE,EAAE;CAAE,CAAC,GACzJ,OAAO,MAAM;EACX,MAAM,KAAK,WAAW,QAAQ;EAC9B,OAAO,EAAE,KAAK,EAAE,SAAS,KAAK,WAAW,MAAM,EAAE,CAAC;CACpD,CACF,CAAC,CAEA,IACC,QACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAc,WAAW;GAAE,KAAK;IAAE,aAAa;IAAc,SAAS,SAAS,cAAc;GAAE;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GAC3K,SAAS,SAAS,OAAO,IACxB,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,MAAM,SAAS,KAAK,WAAW,MAAM,CAAC,CAAC,MAAK,UAAS,MAAM,OAAO,EAAE;EACpE,IAAI,CAAC,QACH,MAAM,cAAc,EAAE;EACxB,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;CAC1B,CACF,CAAC,CAEA,IACC,aACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAkC,WAAW;GAAE,KAAK,EAAE,aAAa,QAAQ;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GACvJ,SAAS,SAAS,OAAO,GACzB,SAAS,SAAS,cAAc,IAC/B,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI,CAAC,KAAK,MAAM,UAAU,EAAE,GAC1B,MAAM,cAAc,EAAE;EAExB,MAAM,EAAE,UAAU,EAAE,IAAI,MAAM,OAAO;EACrC,MAAM,SAAS,UAAU,KAAA,IAAY,MAAa,OAAO,SAAS,OAAO,EAAE;EAE3E,MAAM,UAAU,OAAO,MAAM,MAAM,IAAI,KAAA,IAAY,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,GAAG,GAAO;EACxF,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,WAAW,SAAS,IAAI,OAAO,EAAE,CAAC;CAChE,CACF,CAAC,CAEA,IACC,eACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAA+C,WAAW;GAAE,KAAK,EAAE,aAAa,oBAAoB;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GAChL,SAAS,SAAS,OAAO,IACxB,MAAM;EACL,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI,CAAC,KAAK,MAAM,UAAU,EAAE,GAC1B,MAAM,cAAc,EAAE;EAExB,OAAO,UAAU,GAAG,OAAO,WAAW;GACpC,IAAI,SAAS;GACb,IAAI,QAAuB,QAAQ,QAAQ;GAC3C,MAAM,QAAQ,MAAc,UAAwB;IAClD,IAAI,QACF;IACF,QAAQ,MAAM,WAAW,OAAO,SAAS;KAAE;KAAO;IAAK,CAAC,CAAC,CAAC,CAAC,YAAY;KACrE,SAAS;IACX,CAAC;GACH;GAEA,MAAM,cAAc,KAAK,IAAI,UAAU,KAAK,YAAY;IACtD,KAAK,KAAK,UAAU,OAAO,GAAG,QAAQ,IAAI;GAC5C,CAAC;GACD,OAAO,cAAc;IACnB,SAAS;IACT,YAAY;GACd,CAAC;GAED,MAAM,SAAS,KAAK,WAAW,MAAM,CAAC,CAAC,MAAK,UAAS,MAAM,OAAO,EAAE;GACpE,KAAK,KAAK,UAAU;IAAE,MAAM;IAAU,IAAI,KAAK,IAAI;IAAG,UAAU;IAAI;GAAO,CAAC,GAAG,QAAQ;GACvF,KAAK,KAAK,UAAU;IAClB,MAAM;IACN,IAAI,KAAK,IAAI;IACb,UAAU;IACV,OAAO,KAAK,WAAW,SAAS,IAAI,GAAG;GACzC,CAAC,GAAG,KAAK;GAET,OAAO,MAAM;IACX,MAAM,OAAO,MAAM,IAAK;IACxB,IAAI,QACF;IACF,MAAM,OAAO,SAAS;KAAE,OAAO;KAAQ,MAAM,OAAO,KAAK,IAAI,CAAC;IAAE,CAAC;GACnE;EACF,CAAC;CACH,CACF,CAAC,CAEA,KACC,cACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAkB,WAAW;GAAE,KAAK,EAAE,aAAa,SAAS;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GACxI,SAAS,SAAS,OAAO,GACzB,OAAO,MAAM;EACX,MAAM,SAAS,MAAM,KAAK,WAAW,MAAM,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE;EAClE,OAAO,EAAE,KAAK,QAAQ,UAAU,MAAM,CAAC;CACzC,CACF,CAAC,CAEA,KACC,aACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAiB,WAAW;GAAE,KAAK,EAAE,aAAa,SAAS;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GACvI,SAAS,SAAS,OAAO,GACzB,OAAO,MAAM;EACX,MAAM,SAAS,MAAM,KAAK,WAAW,KAAK,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE;EACjE,OAAO,EAAE,KAAK,QAAQ,OAAO,KAAK,MAAM,GAAG;CAC7C,CACF,CAAC,CAEA,KACC,gBACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAoB,WAAW;GAAE,KAAK,EAAE,aAAa,SAAS;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GAC1I,SAAS,SAAS,OAAO,GACzB,OAAO,MAAM;EACX,MAAM,SAAS,MAAM,KAAK,WAAW,QAAQ,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE;EACpE,OAAO,EAAE,KAAK,QAAQ,UAAU,MAAM,CAAC;CACzC,CACF,CAAC,CAEA,KACC,mBACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAiC,WAAW,EAAE,KAAK;GAAE,aAAa;GAAW,SAAS,SAAS,UAAU;EAAE,EAAE;CAAE,CAAC,GAC5J,SAAS,SAAS,OAAO,IACxB,MAAM;EACL,KAAK,WAAW,UAAU,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE;EACjD,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;CAC5B,CACF,CAAC,CAEA,MACC,QACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAAiB,WAAW;GAAE,KAAK;IAAE,aAAa;IAAc,SAAS,SAAS,cAAc;GAAE;GAAG,KAAK,gBAAgB;GAAM,KAAK,gBAAgB;EAAK;CAAE,CAAC,GACzM,SAAS,SAAS,OAAO,GACzB,SAAS,QAAQ,iBAAiB,IACjC,MAAM;EACL,IAAI;GACF,OAAO,EAAE,KAAK,EAAE,QAAQ,KAAK,MAAM,aAAa,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,MAAM,MAAM,CAAC,EAAE,CAAC;EACjG,SACO,OAAO;GACZ,IAAI,iBAAiB,aAAa;IAChC,MAAM,SAAS,MAAM,QAAQ,WAAW,gBAAgB,IAAI,MAAM;IAClE,MAAM,IAAI,cAAc,MAAM,SAAS;KAAE,YAAY;KAAQ,MAAM,WAAW,MAAM,mBAAmB;IAAiB,CAAC;GAC3H;GACA,MAAM;EACR;CACF,CACF,CAAC,CAEA,OACC,QACA,cAAc;EAAE,MAAM,CAAC,SAAS;EAAG,SAAS;EAA4B,WAAW;GAAE,KAAK;IAAE,aAAa;IAAW,SAAS,SAAS,UAAU;GAAE;GAAG,KAAK,gBAAgB;EAAK;CAAE,CAAC,GAClL,SAAS,SAAS,OAAO,GACzB,OAAO,MAAM;EACX,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,OAAO;EAClC,IAAI;GACF,MAAM,KAAK,WAAW,KAAK,EAAE;GAC7B,KAAK,MAAM,aAAa,EAAE;GAC1B,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;EAC5B,SACO,OAAO;GACZ,IAAI,iBAAiB,aACnB,MAAM,cAAc,EAAE;GACxB,MAAM;EACR;CACF,CACF;AACJ;;;CAxO4B,WAAA;CACD,aAAA;CACe,eAAA;CACjB,eAAA;CAC+D,eAAA;CAElF,UAAU,KAAK,EAAE,IAAI,cAAc,CAAC;CACpC,iBAAiB,KAAK,EAAE,QAAQ,iBAAiB,CAAC;CAClD,kBAAkB,KAAK,EAAE,SAAS,iBAAiB,MAAM,EAAE,CAAC;CAC5D,aAAa,KAAK,EAAE,IAAI,UAAU,CAAC;;;;;ACQzC,SAAS,aAAa,MAAsB;CAC1C,MAAM,UAAU,KAAK,QAAQ,WAAW,EAAE,CAAC,CAAC,QAAQ,aAAa,GAAG,CAAC,CAAC,QAAQ,YAAY,EAAE;CAC5F,OAAO,QAAQ,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AACrD;AAEA,SAAgB,oBAAoB,MAAe;CACjD,OAAO,WAAW,UAAU,CAAC,CAC1B,IAAI,cAAa,MAAK,EAAE,KAAK;EAC5B,SAAS,iBAAiB,KAAK,OAAO,KAAK,MAAM,KAAK,cAAc,UAAU,KAAK,GAAG;EACtF,UAAU,KAAK,MAAM;EACrB,MAAM,KAAK,MAAM,OAAO;EACxB,eAAe,EAAE,UAAU,KAAK,cAAc,OAAO,EAAE;EACvD,MAAM,KAAK,MAAM,OAAO;EACxB,SAAS,iBAAiB,KAAK,OAAO,KAAK,OAAO;EAClD,IAAI,KAAK,GAAG,OAAO;CACrB,CAAC,CAAC,CAAC,CAEF,MAAM,aAAa,cAAc;EAChC,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,oCAAoC;GAAG,KAAK,gBAAgB;EAAK;CACpG,CAAC,GAAG,SAAS,QAAQ,mBAAmB,GAAG,OAAO,MAAM;EACtD,MAAM,QAAuB,EAAE,IAAI,MAAM,MAAM;EAC/C,MAAM,UAAU,KAAK,MAAM,OAAO;EAGlC,MAAM,WAAW,cACf;GACE,GAAG;GACH,MAAM,MAAM,SAAS,QAAQ,QAAQ;GACrC,MAAM;IAAE,GAAG,QAAQ;IAAM,SAAS,MAAM,SAAS,MAAM,WAAW,QAAQ,KAAK;GAAQ;EACzF,GACA,KAAK,KAAK,aACV,KAAK,KAAK,oBACZ;EACA,IAAI,SAAS,kBAAkB,MAC7B,MAAM,IAAI,cAAc,SAAS,eAAe;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAE/F,MAAM,WAAW;GAAE,YAAY,QAAQ,KAAK;GAAY,YAAY,QAAQ,IAAI;EAAQ;EACxF,IAAI;GACF,IAAI,MAAM,aAAa,KAAA,GACrB,KAAK,MAAM,eAAe,MAAM,QAAQ;GAC1C,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,WAAW,MAAM,IAAI;GAClC,IAAI,MAAM,kBAAkB,KAAA,GAC1B,KAAK,MAAM,oBAAoB,MAAM,aAAa;GACpD,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,WAAW,MAAM,IAAI;GAClC,IAAI,MAAM,YAAY,KAAA,GACpB,KAAK,MAAM,cAAc,MAAM,OAAO;GACxC,IAAI,MAAM,YAAY,KAAA,GACpB,KAAK,MAAM,cAAc,MAAM,OAAO;EAC1C,SACO,OAAO;GACZ,IAAI,iBAAiB,aACnB,MAAM,IAAI,cAAc,MAAM,SAAS;IAAE,YAAY;IAAK,MAAM;GAAmB,CAAC;GACtF,MAAM;EACR;EAEA,MAAM,OAAO,KAAK,MAAM,OAAO;EAC/B,MAAM,kBAAkB,KAAK,SAAS,KAAK,cAAc,SAAS,QAAQ,KAAK,SAAS,KAAK,cAAc,SAAS;EACpH,MAAM,eAAe,KAAK,KAAK,eAAe,SAAS;EACvD,MAAM,aAAa,KAAK,IAAI,YAAY,SAAS;EACjD,IAAI,YAA2B;EAE/B,IAAI,mBAAmB,gBAAgB,YAAY;GAKjD,YAAY,GADS,aAAc,KAAK,IAAI,UAAU,UAAU,SAAU,KAAK,cAAc,SAAS,SAC1E,KAAK,YAAY,KAAK,IAAI,EAAE,GAAG,KAAK;GAEhE,cAAc,YAAY;IACxB,MAAM,SAAS,kBACX,MAAM,KAAK,cAAc,OAAO;KAAE,MAAM,KAAK;KAAM,MAAM,KAAK;IAAK,CAAC,IACpE,MAAM,KAAK,cAAc,QAAQ;IAErC,IAAI,OAAO,IAAI;KACb,OAAO,KAAK,8BAA8B,KAAK,cAAc,SAAS,KAAK;KAC3E;IACF;IAEA,OAAO,MAAM,qCAAqC,OAAO,SAAS,iBAAiB;IACnF,KAAK,MAAM,cAAc;KACvB,MAAM,KAAK,cAAc,SAAS;KAClC,MAAM,KAAK,cAAc,SAAS;KAClC,GAAI,eAAe,EAAE,MAAM,EAAE,YAAY,SAAS,WAAW,EAAE,IAAI,CAAC;KACpE,GAAI,aAAa,EAAE,KAAK,EAAE,SAAS,SAAS,WAAW,EAAE,IAAI,CAAC;IAChE,CAAC;GACH,IAAG,UAAS,OAAO,MAAM,6BAA6B,KAAK,CAAC;EAC9D;EAEA,OAAO,EAAE,KAAK;GAGZ,SAAS,iBAAiB,KAAK,OAAO,KAAK,MAAM,KAAK,cAAc,UAAU,KAAK,GAAG;GACtF,UAAU,KAAK,MAAM;GACrB,MAAM,KAAK,MAAM,OAAO;GACxB,eAAe,EAAE,UAAU,KAAK,cAAc,OAAO,EAAE;GACvD,MAAM,KAAK,MAAM,OAAO;GACxB,SAAS,iBAAiB,KAAK,OAAO,KAAK,OAAO;GAClD,IAAI,KAAK,GAAG,OAAO;GACnB,WAAW,mBAAmB,gBAAgB;GAC9C;EACF,CAAC;CACH,CAAC,CAAC,CAMD,KAAK,gBAAgB,cAAc;EAClC,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,YAAY;GAAG,KAAK,gBAAgB;GAAM,KAAK,EAAE,aAAa,YAAY;EAAE;CAC/G,CAAC,GAAG,OAAO,MAAM;EACf,MAAM,WAAW,OAAO,SAAS,EAAE,IAAI,OAAO,gBAAgB,KAAK,KAAK,EAAE;EAC1E,IAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,qBAC1C,MAAM,IAAI,cAAc,6BAA6B,KAAK,MAAM,sBAAsB,OAAO,IAAI,EAAE,KAAK;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAGvJ,MAAM,QAAO,MADM,EAAE,IAAI,UAAU,EAAA,CACjB;EAClB,IAAI,EAAE,gBAAgB,OACpB,MAAM,IAAI,cAAc,+CAA+C;GAAE,YAAY;GAAK,MAAM;EAAe,CAAC;EAClH,IAAI,KAAK,OAAO,qBACd,MAAM,IAAI,cAAc,6BAA6B,KAAK,MAAM,sBAAsB,OAAO,IAAI,EAAE,KAAK;GAAE,YAAY;GAAK,MAAM;EAAmB,CAAC;EAEvJ,MAAM,UAAU,KAAK,KAAK,KAAK,QAAQ,KAAK,GAAG,SAAS,GAAG,cAAc,KAAK,IAAI,EAAE,KAAK;EACzF,IAAI;GACF,MAAM,GAAG,SAAS,UAAU,SAAS,SAAO,KAAK,MAAM,KAAK,YAAY,CAAC,CAAC;GAC1E,MAAM,SAAS,MAAM,KAAK,GAAG,QAAQ,SAAS,aAAa,KAAK,IAAI,CAAC;GACrE,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,cAAc,OAAO,OAAO;IAAE,YAAY;IAAK,MAAM;GAAa,CAAC;GAE/E,OAAO,KAAK,oBAAoB,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,QAAQ;GAC/E,OAAO,EAAE,KAAK;IAAE,IAAI;IAAM,MAAM,OAAO;IAAM,IAAI,KAAK,GAAG,OAAO;GAAE,CAAC;EACrE,UACQ;GACN,GAAG,OAAO,SAAS,EAAE,OAAO,KAAK,CAAC;EACpC;CACF,CAAC,CAAC,CAGD,OAAO,gBAAgB,cAAc;EACpC,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,WAAW,EAAE;CAChD,CAAC,IAAI,MAAM;EACT,MAAM,UAAU,KAAK,GAAG,OAAO;EAC/B,OAAO,KAAK,UAAU,gDAAgD,4BAA4B;EAClG,OAAO,EAAE,KAAK;GAAE,IAAI;GAAM;GAAS,IAAI,KAAK,GAAG,OAAO;EAAE,CAAC;CAC3D,CAAC;AACL;;;CAvK4B,WAAA;CACA,UAAA;CACE,cAAA;CACH,aAAA;CACJ,YAAA;CACS,eAAA;CACP,eAAA;CACK,cAAA;CACqB,aAAA;CACf,eAAA;CAG9B,sBAAsB;;;;;ACZ5B,SAAgB,iBAAiB,MAAe;CAC9C,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,UACA,cAAc;EACZ,MAAM,CAAC,OAAO;EACd,SAAS;EACT,WAAW,EAAE,KAAK;GAAE,aAAa;GAAgB,SAAS,SAAS,cAAc;EAAE,EAAE;CACvF,CAAC,IACD,MAAK,EAAE,KAAK,KAAK,WAAW,SAAS,CAAC,CACxC;AACJ;;CAhB2B,aAAA;CACF,eAAA;CACM,eAAA;;;;;;;;AC+B/B,SAAgB,kBAAkB,SAAmC;CACnE,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,oBAA4B,KAAK,QAAQ,OAAO,QAAQ,QAAQ,aAAa,QAAQ,IAAI,IAAI,QAAQ,GAAG;CAE9G,MAAM,IAAI,KAAK,OAAO,MAAM;EAC1B,MAAM,OAAO,YAAY;EACzB,MAAM,WAAW,WAAW,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ;EACvD,IAAI,aAAa,MACf,OAAO,EAAE,KAAK,YAAY,GAAG;EAE/B,MAAM,OAAO,cAAc,MAAM,QAAQ;EACzC,IAAI,SAAS,MAAM;GACjB,MAAM,WAAW,MAAM,UAAU,GAAG,MAAM,QAAQ;GAClD,IAAI,aAAa,MACf,OAAO;EACX;EAEA,MAAM,YAAY,KAAK,KAAK,MAAM,KAAK;EACvC,IAAI,GAAG,WAAW,SAAS,GAAG;GAC5B,MAAM,WAAW,MAAM,UAAU,GAAG,WAAW,GAAG;GAClD,IAAI,aAAa,MACf,OAAO;EACX;EAEA,OAAO,EAAE,KAAK,2EAA2E,GAAG;CAC9F,CAAC;CAED,OAAO;AACT;AAEA,SAAS,WAAW,OAA8B;CAChD,IAAI;EACF,OAAO,mBAAmB,KAAK;CACjC,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAS,cAAc,MAAc,UAAiC;CACpE,MAAM,WAAW,KAAK,QAAQ,MAAM,IAAI,UAAU;CAClD,IAAI,aAAa,QAAQ,CAAC,SAAS,WAAW,GAAG,OAAO,KAAK,KAAK,GAChE,OAAO;CACT,OAAO;AACT;AAEA,eAAe,UAAU,GAAY,MAAc,UAA4C;CAC7F,IAAI;CACJ,IAAI;EACF,QAAQ,MAAM,GAAG,SAAS,KAAK,IAAI;CACrC,QACM;EACJ,OAAO;CACT;CACA,IAAI,CAAC,MAAM,OAAO,GAChB,OAAO;CAET,MAAM,OAAO,MAAM,GAAG,SAAS,SAAS,IAAI;CAC5C,MAAM,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,YAAY;CAC3C,MAAM,YAAY,SAAS,WAAW,UAAU;CAChD,MAAM,UAAU,KAAK,OAAO,MAAM,KAAK,YAAY,KAAK,aAAa,KAAK,UAAU;CAEpF,OAAO,EAAE,KAAK,SAAS,KAAK;EAC1B,gBAAgB,cAAc,QAAQ;EACtC,kBAAkB,OAAO,MAAM,IAAI;EACnC,iBAAiB,YAAY,wCAAwC;CACvE,CAAC;AACH;;;CAlGM,gBAAwC;EAC5C,SAAS;EACT,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,SAAS;EACT,UAAU;EACV,QAAQ;EACR,QAAQ;CACV;;;;;;;;;;;ACLA,SAAgB,eAAe,MAAe;CAC5C,MAAM,gBAAgB;EACpB,SAAS,iBAAiB,KAAK,OAAO,KAAK,MAAM,KAAK,cAAc,UAAU,KAAK,GAAG;EACtF,WAAW,KAAK,MAAM,OAAO,QAAQ,IAAI;EACzC,WAAW,KAAK,cAAc,SAAS;CACzC;CAEA,OAAO,WAAW,UAAU,CAAC,CAC1B,KACC,iBACA,cAAc;EACZ,MAAM,CAAC,KAAK;EACZ,SAAS;EACT,WAAW;GAAE,KAAK,EAAE,aAAa,2CAA2C;GAAG,KAAK,gBAAgB;EAAK;CAC3G,CAAC,GACD,SAAS,QAAQ,eAAe,IAC/B,MAAM;EACL,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,aAAa,KAAK,UAAU;EAC7D,IAAI,CAAC,MAAM,IACT,MAAM,IAAI,cAAc,MAAM,SAAS,qCAAqC;GAAE,YAAY;GAAK,MAAM;EAAsB,CAAC;EAE9H,IAAI,KAAK,MAAM,OAAO,QAAQ,IAAI,SAChC,cAAc,YAAY;GACxB,MAAM,SAAS,MAAM,KAAK,cAAc,QAAQ;GAChD,IAAI,CAAC,OAAO,IACV,OAAO,MAAM,yBAAyB,OAAO,SAAS,iBAAiB;QACpE,OAAO,KAAK,8BAA8B,KAAK,cAAc,SAAS,IAAI,SAAS;EAC1F,IAAG,UAAS,OAAO,MAAM,qBAAqB,KAAK,CAAC;EAGtD,OAAO,EAAE,KAAK,OAAO,CAAC;CACxB,CACF,CAAC,CAEA,OACC,iBACA,cAAc;EACZ,MAAM,CAAC,KAAK;EACZ,SAAS;EACT,WAAW,EAAE,KAAK,EAAE,aAAa,UAAU,EAAE;CAC/C,CAAC,IACA,MAAM;EACL,KAAK,IAAI,MAAM;EACf,IAAI,KAAK,MAAM,OAAO,QAAQ,IAAI,SAChC,cAAc,YAAY;GACxB,MAAM,KAAK,cAAc,QAAQ;EACnC,IAAG,UAAS,OAAO,MAAM,qBAAqB,KAAK,CAAC;EAEtD,OAAO,EAAE,KAAK,OAAO,CAAC;CACxB,CACF;AACJ;;CAnE8B,cAAA;CACH,aAAA;CACJ,YAAA;CACS,eAAA;CACP,eAAA;CACQ,aAAA;CACD,eAAA;;;;AC0BhC,SAAS,YAAY,OAA8B;CACjD,IAAI,iBAAiB,eACnB,OAAO;EAAE,SAAS,MAAM;EAAS,MAAM;CAAiB;CAI1D,IAAI,gBAAgB,KAAK,GACvB,OAAO;EACL,SAAS,MAAM;EACf,MAAM,MAAM,QAAQ;EACpB,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;CAC/D;CAGF,IAAI,iBAAiB,OACnB,OAAO;EAAE,SAAS,MAAM;EAAS,MAAM,MAAM,SAAS,UAAU,mBAAmB,MAAM,KAAK,YAAY;CAAE;CAE9G,OAAO;EAAE,SAAS,OAAO,KAAK;EAAG,MAAM;CAAiB;AAC1D;AAEA,SAAS,gBAAgB,OAAwC;CAC/D,OAAO,iBAAiB,SAAS,MAAM,SAAS,mBAAmB,gBAAgB;AACrF;AAEA,SAAS,SAAS,OAAsC;CACtD,MAAM,YAAa,OAAsD,cAAe,OAAgC;CACxH,MAAM,SAAS,OAAO,cAAc,WAAW,YAAY;CAC3D,OAAO,UAAU,OAAO,UAAU,MAAO,SAAkC;AAC7E;;;CA3DuB,YAAA;CAmBV,gBAAkC,OAAO,MAAM;EAC1D,MAAM,OAAO,YAAY,KAAK;EAC9B,MAAM,SAAS,SAAS,KAAK;EAE7B,IAAI,UAAU,KACZ,OAAO,MAAM,GAAG,EAAE,IAAI,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,WAAW,KAAK;OAE5E,OAAO,MAAM,GAAG,EAAE,IAAI,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,KAAK,OAAO,GAAG,KAAK,SAAS;EAE3F,OAAO,EAAE,KAAK,MAAM,MAAM;CAC5B;;;;;;;;;;;ACnBA,SAAS,mBAAyB;CAChC,IAAI;EAEF,OADiB,KAAK,MAAM,GAAG,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CACxF,CAAA,CAAS,WAAW;CAC7B,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAgB,aAAa,KAAgD;CAC3E,OAAO,WAAW,UAAU,CAAC,CAC1B,IACC,GAAG,OAAO,aACV,oBAAoB,KAAK,EACvB,eAAe;EACb,MAAM;GACJ,OAAO;GACP,SAAS,iBAAe;GACxB,aAAa;EACf;EACA,MAAM;GACJ;IAAE,MAAM;IAAS,aAAa;GAAkD;GAChF;IAAE,MAAM;IAAW,aAAa;GAAiC;GACjE;IAAE,MAAM;IAAQ,aAAa;GAA0B;GACvD;IAAE,MAAM;IAAW,aAAa;GAAkD;GAClF;IAAE,MAAM;IAAQ,aAAa;GAAkC;GAC/D;IAAE,MAAM;IAAiB,aAAa;GAAoB;GAC1D;IAAE,MAAM;IAAO,aAAa;GAAwB;EACtD;CACF,EACF,CAAC,CACH,CAAC,CACA,IACC,GAAG,OAAO,MACV,OAAO;EAAE,OAAO;EAAa,KAAK,GAAG,OAAO;CAAY,CAAC,CAC3D;AACJ;;;CAhD2B,aAAA;CAErB,SAAS;;;;;;;;;;;ACiDf,SAAgB,cAAc,MAAe;CAC3C,MAAM,MAAM,WAAW,UAAU,CAAC,CAC/B,IAAI,KAAK,OAAO,GAAG,SAAS;EAC3B,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,KAAK;EACX,OAAO,MAAM,GAAG,EAAE,IAAI,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,IAAI,OAAO,GAAG,KAAK,IAAI,IAAI,QAAQ,GAAG;CACzG,CAAC,CAAC,CAED,QAAQ,YAAY,CAAC,CAIrB,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,CAEvC,IAAI,UAAU,gBAAgB,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAEnD,MAAM,QAAQ,gBAAgB,IAAI,CAAC,CAAC,CACpC,MAAM,QAAQ,iBAAiB,IAAI,CAAC,CAAC,CACrC,MAAM,QAAQ,kBAAkB,IAAI,CAAC,CAAC,CACtC,MAAM,QAAQ,oBAAoB,IAAI,CAAC,CAAC,CACxC,MAAM,QAAQ,eAAe,IAAI,CAAC,CAAC,CACnC,MAAM,QAAQ,gBAAgB,IAAI,CAAC,CAAC,CACpC,MAAM,QAAQ,yBAAyB,IAAI,CAAC,CAAC,CAC7C,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,CACvC,MAAM,QAAQ,mBAAmB,IAAI,CAAC,CAAC,CACvC,MAAM,gBAAgB,mBAAmB,IAAI,CAAC,CAAC,CAE/C,MAAM,KAAK,kBAAkB,IAAI,CAAC;CAKrC,OADmB,IAAI,MAAM,KAAK,aAAa,GAAG,CAC3C,CAAA,CAAW,MAAM,KAAK,kBAAkB,EAAE,WAAW,KAAK,GAAG,WAAW,EAAE,CAAC,CAAC;AACrF;;CA5EgC,gBAAA;CACG,eAAA;CACA,aAAA;CACD,cAAA;CACA,YAAA;CACF,UAAA;CACG,aAAA;CACM,qBAAA;CACN,cAAA;CACC,cAAA;CACH,WAAA;CACC,YAAA;CACH,WAAA;CACF,WAAA;CACF,aAAA;CACJ,YAAA;CACS,UAAA;CACH,aAAA;;;;;;;;;;;;;;ACK7B,SAAgB,cAA8B;CAC5C,IAAI;EACF,MAAM,SAAS,cAAc,KAAK,MAAM,GAAG,aAAa,aAAa,MAAM,CAAC,CAAC;EAC7E,OAAO,kBAAkB,KAAK,SAAS,OAAO;CAChD,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAgB,aAAa,SAAwB;CACnD,gBAAgB,aAAa,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;AACvF;AAEA,SAAgB,eAAqB;CACnC,GAAG,OAAO,aAAa,EAAE,OAAO,KAAK,CAAC;AACxC;AAEA,SAAgB,WAAmB;CACjC,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;AAC7C;;AAGA,SAAgB,eAAe,KAAsB;CACnD,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SACO,OAAO;EACZ,OAAQ,MAAgC,SAAS;CACnD;AACF;;AAUA,eAAsB,aAAa,SAAkB,YAAY,MAA6B;CAC5F,MAAM,SAAS,MAAM,aAAa,SAAS,YAAY,OAAO,KAAA,GAAW,SAAS;CAClF,OAAO;EAAE,WAAW,WAAW;EAAM,UAAU,WAAW;CAAI;AAChE;;;;;AAMA,eAAsB,gBAAgB,SAAkB,YAAY,KAAwB;CAC1F,MAAM,SAAS,MAAM,aAAa,SAAS,iBAAiB,QAAQ,QAAQ,OAAO,SAAS;CAC5F,OAAO,WAAW,QAAQ,UAAU,OAAO,SAAS;AACtD;;;;;;AAOA,SAAS,aAAa,SAAkB,MAAc,QAAwB,OAA2B,WAA2C;CAClJ,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,MAAM,IAAI,IAAI,GAAG,QAAQ,WAAW,MAAM;EAChD,MAAM,SAAS,IAAI,aAAa;EAChC,MAAM,WAAW,SAAS,QAAQ,KAAA,CAAM,QAAQ;GAC9C,UAAU,IAAI;GACd,MAAM,IAAI;GACV,MAAM,IAAI;GACV;GAEA,GAAI,SAAS,EAAE,oBAAoB,MAAM,IAAI,CAAC;GAC9C,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,uBAAuB,MAAM;GACnE,SAAS;EACX,IAAI,aAAa;GACf,SAAS,OAAO;GAChB,SAAS,KAAK,aAAa,QAAQ,SAAS,cAAc,IAAI,CAAC;EACjE,CAAC;EAED,QAAQ,KAAK,eAAe,QAAQ,IAAI,CAAC;EACzC,QAAQ,KAAK,iBAAiB;GAC5B,QAAQ,QAAQ;GAChB,QAAQ,IAAI;EACd,CAAC;EACD,QAAQ,IAAI;CACd,CAAC;AACH;;;CAhHgC,YAAA;CACJ,WAAA;CAOf,gBAAgB,KAAK;EAChC,SAAS;EACT,KAAK;;EAEL,KAAK;;EAEL,UAAU;EACV,UAAU;EACV,MAAM;EACN,UAAU;EACV,WAAW;EACX,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,SAAS;EACT,OAAO;CACT,CAAC,CAAC,CAAC,gBAAgB,QAAQ;;;;;AC1B3B,SAAgB,YAAY,KAAmB;CAC7C,MAAM,UAAU,QAAQ,aAAa,WACjC,SAAS,IAAI,KACb,QAAQ,aAAa,UACnB,aAAa,IAAI,KACjB,aAAa,IAAI;CAEvB,KAAK,SAAS,EAAE,aAAa,KAAK,SAAS,CAE3C,CAAC;AACH;;;;;;;;ACRA,SAAgB,gBAAgB,OAAe,MAA4B;CACzE,OAAO,MAAM,QAAQ,+BAA+B,OAAO,SAAiB;EAC1E,MAAM,cAAc,KAAK;EACzB,OAAO,gBAAgB,KAAA,IAAY,QAAQ,OAAO,WAAW;CAC/D,CAAC;AACH;AAEA,SAAgB,iBAA8C,OAAU,MAAuB;CAC7F,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,KAAI,UAAS,gBAAgB,OAAO,IAAI,CAAC;CACxD,OAAO,gBAAgB,OAAiB,IAAI;AAC9C;AAEA,SAAgB,cAAc,QAAgC,MAA4C;CACxG,OAAO,OAAO,YACZ,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,gBAAgB,OAAO,IAAI,CAAC,CAAC,CAClF;AACF;;;;;ACfA,SAAgB,UAAU,MAAc,MAAc,YAAY,MAAwB;CACxF,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,SAAS,IAAI,QAAQ;GAAE;GAAM;EAAK,CAAC;EACzC,MAAM,QAAQ,WAA0B;GACtC,OAAO,mBAAmB;GAC1B,OAAO,QAAQ;GACf,QAAQ,MAAM;EAChB;EACA,OAAO,WAAW,SAAS;EAC3B,OAAO,KAAK,iBAAiB,KAAK,IAAI,CAAC;EACvC,OAAO,KAAK,iBAAiB,KAAK,KAAK,CAAC;EACxC,OAAO,KAAK,eAAe,KAAK,KAAK,CAAC;CACxC,CAAC;AACH;;AAGA,eAAsB,WAAW,MAAc,OAAO,aAAa,YAAY,KAAwB;CACrG,OAAO,CAAE,MAAM,UAAU,MAAM,MAAM,SAAS;AAChD;;;;;AAMA,eAAsB,gBAAgB,MAAiC;CACrE,MAAM,OAAO,MAAM,gBAAgB,IAAI;CACvC,KAAK,MAAM,OAAO,MAChB,IAAI;EACF,QAAQ,KAAK,KAAK,SAAS;CAC7B,QACM,CAEN;CAEF,OAAO;AACT;;AAGA,SAAgB,sBAAsB,QAAgB,MAAwB;CAC5E,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,QAAQ,OAAO,MAAM,OAAO,GAAG;EACxC,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK;EACrC,IAAI,MAAM,SAAS,GACjB;EACF,MAAM,QAAQ,MAAM,MAAM;EAC1B,MAAM,QAAQ,MAAM,MAAM;EAC1B,MAAM,MAAM,OAAO,SAAS,MAAM,MAAM,IAAI,EAAE;EAC9C,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE;EAC7E,IAAI,MAAM,YAAY,MAAM,eAAe,cAAc,MACvD;EACF,IAAI,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,QAAQ,QAAQ,KACtD,KAAK,IAAI,GAAG;CAChB;CAEA,OAAO,CAAC,GAAG,IAAI;AACjB;AAEA,eAAsB,gBAAgB,MAAiC;CACrE,IAAI,QAAQ,aAAa,SACvB,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,gBAAc,WAAW;GAAC;GAAQ;GAAM;EAAK,GAAG,EAAE,SAAS,IAAK,CAAC;EAC1F,OAAO,sBAAsB,QAAQ,IAAI;CAC3C,QACM;EACJ,OAAO,CAAC;CACV;CAGF,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,gBAAc,QAAQ;GAAC;GAAO,OAAO;GAAQ;EAAc,GAAG,EAAE,SAAS,IAAK,CAAC;EACxG,OAAO,UAAU,MAAM;CACzB,QACM,CAEN;CAEA,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,gBAAc,SAAS,CAAC,GAAG,KAAK,KAAK,GAAG,EAAE,SAAS,IAAK,CAAC;EAClF,OAAO,UAAU,MAAM;CACzB,QACM;EACJ,OAAO,CAAC;CACV;AACF;AAEA,SAAS,UAAU,QAA0B;CAC3C,OAAO,CAAC,GAAG,IAAI,IACb,OAAO,MAAM,KAAK,CAAC,CAChB,KAAI,UAAS,OAAO,SAAS,OAAO,EAAE,CAAC,CAAC,CACxC,QAAO,QAAO,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,QAAQ,QAAQ,GAAG,CAC1E,CAAC;AACH;;;CA/FM,kBAAgB,UAAU,QAAQ;;;;;ACDxC,SAAgB,aAAa,MAAsC;CACjE,MAAM,MAA8B,CAAC;CAErC,KAAK,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;EAClC,MAAM,OAAO,IAAI,KAAK;EACtB,IAAI,KAAK,WAAW,KAAK,KAAK,WAAW,GAAG,GAC1C;EAEF,MAAM,aAAa,KAAK,WAAW,SAAS,IAAI,KAAK,MAAM,CAAC,IAAI;EAChE,MAAM,YAAY,WAAW,QAAQ,GAAG;EACxC,IAAI,aAAa,GACf;EAEF,MAAM,MAAM,WAAW,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;EAChD,IAAI,QAAQ,WAAW,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;EACjD,IAAI,MAAM,SAAS,MAAO,MAAM,WAAW,IAAG,KAAK,MAAM,SAAS,IAAG,KAAO,MAAM,WAAW,GAAI,KAAK,MAAM,SAAS,GAAI,IACvH,QAAQ,MAAM,MAAM,GAAG,EAAE;EAE3B,IAAI,OAAO;CACb;CAEA,OAAO;AACT;;AAGA,SAAgB,YAAY,MAAmF;CAC7G,IAAI;EACF,OAAO;GAAE,KAAK,aAAa,GAAG,aAAa,MAAM,MAAM,CAAC;GAAG,MAAM;GAAM,OAAO;EAAK;CACrF,SACO,OAAO;EAEZ,IADc,MAAgC,SACjC,UACX,OAAO;GAAE,KAAK,CAAC;GAAG,MAAM;GAAM,OAAO;EAAK;EAC5C,OAAO;GAAE,KAAK,CAAC;GAAG,MAAM;GAAM,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAAE;CAC9F;AACF;;AAKA,SAAgB,UAAU,OAAe,MAAkD;CACzF,OAAO,MAAM,QAAQ,WAAW,OAAO,SAAiB,KAAK,SAAS,KAAK;AAC7E;AAEA,SAAgB,gBAAgB,QAAgC,MAAkE;CAChI,OAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,UAAU,OAAO,IAAI,CAAC,CAAC,CAAC;AACvG;AAEA,SAAgB,cAAc,QAAkB,MAAoD;CAClG,OAAO,OAAO,KAAI,UAAS,UAAU,OAAO,IAAI,CAAC;AACnD;AAEA,SAAgB,mBAAmB,MAAc,KAAqB;CACpE,IAAI,KAAK,WAAW,IAAI,GACtB,OAAO;CACT,OAAO,KAAK,QAAQ,KAAK,IAAI;AAC/B;;;CAnBM,WAAW;;;;;ACHjB,SAAgB,aAAa,MAAuB;CAClD,IAAI,KAAoB;CACxB,IAAI;EACF,KAAK,GAAG,SAAS,MAAM,GAAG;EAC1B,MAAM,OAAO,SAAO,MAAM,CAAC;EAE3B,OADa,GAAG,SAAS,IAAI,MAAM,GAAG,GAAG,CAClC,MAAS,KAAK,KAAK,MAAK,UAAS,MAAM,OAAO,MAAM,UAAU,KAAK,WAAW,IAAI,CAAC;CAC5F,QACM;EACJ,OAAO;CACT,UACQ;EACN,IAAI,OAAO,MACT,GAAG,UAAU,EAAE;CACnB;AACF;;AAaA,eAAsB,QAAQ,MAAuC;CACnE,MAAM,SAAS,MAAM,KAAK,IAAI;CAC9B,IAAI;EAEF,QAAO,MADe,OAAO,WAAW,EAAA,CACzB,KAAI,WAAU;GAC3B,MAAM,MAAM;GACZ,WAAW,MAAM,cAAc;GAC/B,WAAW,MAAM,cAAc;GAC/B,SAAS,MAAM,YAAY;GAC3B,MAAM,MAAM,oBAAoB;EAClC,EAAE;CACJ,UACQ;EACN,MAAM,OAAO,MAAM;CACrB;AACF;;AAGA,SAAgB,kBAAkB,OAAyB;CACzD,OAAO,iBAAiB,SAAS,YAAY,KAAK,MAAM,OAAO;AACjE;;;;;;;AAQA,eAAsB,UAAU,WAAmB,aAAqB,UAAiC,CAAC,GAAkB;CAC1H,MAAM,SAAS,GAAG,kBAAkB,aAAa,EAAE,MAAM,IAAM,CAAC;CAGhE,MAAM,UAAU,SAAS,MAAM;CAC/B,MAAM,SAAS,SAAS,MAAM,MAAM;CACpC,MAAM,MAAM,IAAI,UAAU,QAAQ;EAChC,GAAI,QAAQ,aAAa,KAAA,IAAY,CAAC,IAAI;GAAE,UAAU,QAAQ;GAAU,oBAAoB;EAAW;EACvG,OAAO;EACP,WAAW;CACb,CAAC;CAED,IAAI;EACF,KAAK,MAAM,QAAQ,KAAK,SAAS,GAC/B,IAAI,KAAK,WACP,MAAM,IAAI,IAAI,KAAK,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;OAC/C,IAAI,KAAK,SAAS,GAGrB,MAAM,IAAI,IAAI,KAAK,MAAM,MAAM,EAAE,WAAW,MAAM,CAAC;OAEnD,MAAM,IAAI,IAAI,KAAK,MAAM,SAAS,MAAM,GAAG,iBAAiB,KAAK,QAAQ,CAAC,CAAwB;EAEtG,MAAM,IAAI,MAAM;EAChB,MAAM;CACR,SACO,OAAO;EACZ,MAAM,OAAO,MAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;EAGxC,MAAM,QAAQ,YAAY,CAAC,CAAC;EAC5B,MAAM;CACR;AACF;;;;;;AAOA,eAAsB,WACpB,MACA,aACA,SACgC;CAChC,MAAM,SAAS,MAAM,KAAK,MAAM,QAAQ,QAAQ;CAChD,MAAM,UAAoB,CAAC;CAE3B,IAAI;EACF,MAAM,UAAU,IAAI,KAAK,MAAM,OAAO,WAAW,EAAA,CAAG,KAAI,UAAS,CAAC,MAAM,UAAU,KAAK,CAAC,CAAC;EAEzF,KAAK,MAAM,QAAQ,QAAQ,OAAO;GAChC,MAAM,QAAQ,QAAQ,IAAI,IAAI;GAC9B,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,KAAK,IAAI;IACjB;GACF;GAEA,MAAM,SAAS,KAAK,KAAK,aAAa,IAAI;GAC1C,IAAI,MAAM,WAAW;IACnB,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;IACxC;GACF;GACA,IAAI,MAAM,SAAS;IACjB,QAAQ,KAAK,IAAI;IACjB;GACF;GAEA,GAAG,UAAU,KAAK,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;GACtD,MAAM,MAAM,QAAQ,SAAS,MAAM,GAAG,kBAAkB,MAAM,CAAC,GAA+B,aAAa,OAAO,QAAQ,QAAQ,CAAC;EACrI;CACF,UACQ;EACN,MAAM,OAAO,MAAM;CACrB;CAEA,OAAO,EAAE,QAAQ;AACnB;AAEA,SAAS,aAAa,OAAkB,UAAqD;CAC3F,OAAO,MAAM,aAAa,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;AACrE;AAEA,eAAe,KAAK,MAAc,UAAgD;CAGhF,MAAM,OAAO,MAAM,GAAG,WAAW,MAAM,EAAE,MAAM,kBAAkB,CAAC;CAClE,OAAO,aAAa,KAAA,IAChB,IAAI,UAAU,IAAI,WAAW,IAAI,CAAC,IAClC,IAAI,UAAU,IAAI,WAAW,IAAI,GAAG,EAAE,SAAS,CAAC;AACtD;;AAUA,SAAS,KAAK,MAA4B;CACxC,MAAM,QAAsB,CAAC;CAC7B,MAAM,uBAAO,IAAI,IAAY;CAE7B,MAAM,SAAS,UAAkB,SAAuB;EACtD,IAAI;EACJ,IAAI;GACF,QAAQ,GAAG,SAAS,QAAQ;EAC9B,QACM;GACJ;EACF;EAEA,IAAI,MAAM,YAAY,GAAG;GACvB,MAAM,OAAO,GAAG,aAAa,QAAQ;GACrC,IAAI,KAAK,IAAI,IAAI,GACf;GACF,KAAK,IAAI,IAAI;GACb,MAAM,KAAK;IAAE,MAAM,GAAG,KAAK;IAAI;IAAU,WAAW;IAAM,MAAM;GAAE,CAAC;GACnE,KAAK,MAAM,SAAS,GAAG,YAAY,QAAQ,CAAC,CAAC,KAAK,GAChD,MAAM,KAAK,KAAK,UAAU,KAAK,GAAG,GAAG,KAAK,GAAG,OAAO;GACtD;EACF;EAEA,IAAI,MAAM,OAAO,GACf,MAAM,KAAK;GAAE;GAAM;GAAU,WAAW;GAAO,MAAM,MAAM;EAAK,CAAC;CACrE;CAEA,KAAK,MAAM,SAAS,GAAG,YAAY,IAAI,CAAC,CAAC,KAAK,GAC5C,MAAM,KAAK,KAAK,MAAM,KAAK,GAAG,KAAK;CAErC,OAAO;AACT;;;;;;;;;;;;CArMA,UAAU,EAAE,eAAe,MAAM,CAAC;CAE5B,OAAO;EACX;GAAC;GAAM;GAAM;GAAM;EAAI;EACvB;GAAC;GAAM;GAAM;GAAM;EAAI;EACvB;GAAC;GAAM;GAAM;GAAM;EAAI;CACzB;;;;;AC5BA,SAAgB,eAAe,SAAiB,SAAiC;CAC/E,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;CAClD,MAAM,MAAM,QAAQ,cAAc,QAAQ,WAAW,aAAa;CAClE,IAAI,CAAC,OAAO,SAAS,GAAG,GACtB,OAAO,QAAQ;CACjB,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,GAAG,QAAQ,UAAU;AACtD;;;;;ACHA,eAAsB,SAAS,MAAc,MAAc,WAA+C;CACxG,MAAM,UAAU,KAAK,IAAI;CACzB,MAAM,YAAY,MAAM,UAAU,MAAM,MAAM,SAAS;CAEvD,OAAO;EAAE,SAAS;EAAW,IADlB,KAAK,IAAI,IAAI;EACS,QAAQ,YAAY,+BAA+B;CAAmC;AACzH;;;;;AAYA,eAAsB,UAAU,MAAc,MAAc,SAAuD;CACjH,MAAM,MAAM,UAAU,KAAK,GAAG,OAAO,QAAQ,KAAK,WAAW,GAAG,IAAI,QAAQ,OAAO,IAAI,QAAQ;CAC/F,MAAM,UAAU,KAAK,IAAI;CAEzB,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ,QAAQ;GAChB,UAAU;GACV,QAAQ,YAAY,QAAQ,QAAQ,SAAS;EAC/C,CAAC;EACD,MAAM,KAAK,KAAK,IAAI,IAAI;EAExB,MAAM,WAAW,QAAQ,gBAAgB;EACzC,IAAI,aAAa,QAAQ,SAAS,WAAW,UAC3C,OAAO;GAAE,SAAS;GAAO;GAAI,QAAQ,mBAAmB,SAAS,QAAQ,SAAS;EAAS;EAE7F,IAAI,aAAa,QAAQ,SAAS,UAAU,QAAQ,mBAClD,OAAO;GAAE,SAAS;GAAO;GAAI,QAAQ,UAAU,SAAS,OAAO,SAAS,QAAQ;EAAoB;EAGtG,IAAI,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,QAElD;OAAA,EAAC,MADc,SAAS,KAAK,EAAA,CACvB,SAAS,QAAQ,UAAU,GACnC,OAAO;IAAE,SAAS;IAAO;IAAI,QAAQ,yBAAyB,KAAK,UAAU,QAAQ,UAAU;GAAI;EAAA;EAIvG,OAAO;GAAE,SAAS;GAAM;GAAI,QAAQ,QAAQ,SAAS;EAAS;CAChE,SACO,OAAO;EAGZ,OAAO;GAAE,SAAS;GAAO,IAFd,KAAK,IAAI,IAAI;GAEK,QAAQ,mBADtB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACH;CACnE;AACF;AAEA,eAAsB,YAAY,SAMH;CAC7B,IAAI,OAA0B;EAAE,SAAS;EAAO,IAAI;EAAG,QAAQ;CAAa;CAI5E,KAAK,MAAM,QAAQ,QAAQ,OAAO;EAChC,MAAM,SAAS,QAAQ,SAAS,SAC5B,MAAM,UAAU,MAAM,QAAQ,MAAM;GAAE,GAAG,QAAQ;GAAM,WAAW,QAAQ;EAAU,CAAC,IACrF,MAAM,SAAS,MAAM,QAAQ,MAAM,QAAQ,SAAS;EACxD,IAAI,OAAO,SACT,OAAO;EACT,OAAO;CACT;CAEA,OAAO;AACT;;CAnF0B,UAAA;;;;ACiB1B,SAAgB,cAAc,MAAyB;CACrD,MAAM,OAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;EACnC,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK;EACrC,IAAI,MAAM,SAAS,GACjB;EACF,MAAM,CAAC,KAAK,MAAM,KAAK,OAAO,MAAM,KAAI,UAAS,OAAO,WAAW,KAAK,CAAC;EACzE,IAAI,QAAQ,KAAA,KAAa,SAAS,KAAA,KAAa,QAAQ,KAAA,KAAa,CAAC,OAAO,SAAS,GAAG,GACtF;EACF,KAAK,KAAK;GAAE;GAAK;GAAM,OAAO;GAAK,YAAY,OAAO,SAAS,GAAG,IAAI,MAAM,KAAA;EAAU,CAAC;CACzF;CACA,OAAO;AACT;;AAGA,SAAgB,gBAAgB,MAAyB;CACvD,MAAM,OAAkB,CAAC;CACzB,MAAM,QAAQ,KAAK,MAAM,OAAO,CAAC,CAAC,QAAO,SAAQ,KAAK,KAAK,CAAC,CAAC,SAAS,CAAC;CACvE,MAAM,SAAS,MAAM,MAAM;CAC3B,IAAI,WAAW,KAAA,GACb,OAAO;CAET,MAAM,UAAU,OAAO,MAAM,GAAG,CAAC,CAAC,KAAI,UAAS,MAAM,QAAQ,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC;CAC3F,MAAM,SAAS,SAAyB,QAAQ,QAAQ,KAAK,YAAY,CAAC;CAC1E,MAAM,QAAQ,MAAM,WAAW;CAC/B,MAAM,SAAS,MAAM,iBAAiB;CACtC,MAAM,QAAQ,MAAM,gBAAgB;CACpC,MAAM,WAAW,MAAM,gBAAgB;CACvC,MAAM,SAAS,MAAM,cAAc;CAEnC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC,KAAI,UAAS,MAAM,QAAQ,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;EACzE,MAAM,MAAM,OAAO,SAAS,MAAM,UAAU,IAAI,EAAE;EAClD,IAAI,CAAC,OAAO,SAAS,GAAG,GACtB;EAGF,MAAM,SAAS,YAAY,IAAI,OAAO,SAAS,MAAM,aAAa,IAAI,EAAE,IAAI;EAC5E,MAAM,OAAO,UAAU,IAAI,OAAO,SAAS,MAAM,WAAW,IAAI,EAAE,IAAI;EACtE,MAAM,WAAW,OAAO,SAAS,MAAM,KAAK,OAAO,SAAS,IAAI;EAEhE,KAAK,KAAK;GACR;GACA,MAAM,OAAO,SAAS,MAAM,WAAW,IAAI,EAAE,KAAK;GAElD,QAAQ,OAAO,SAAS,MAAM,UAAU,IAAI,EAAE,KAAK,KAAK;GACxD,YAAY,YAAY,SAAS,QAAQ,MAAwB,KAAA;EACnE,CAAC;CACH;CAEA,OAAO;AACT;;AAKA,eAAe,gBAAiC;CAC9C,IAAI,eAAe,MACjB,OAAO;CACT,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,gBAAc,WAAW,CAAC,SAAS,GAAG,EAAE,SAAS,IAAK,CAAC;EAChF,MAAM,SAAS,OAAO,SAAS,OAAO,KAAK,GAAG,EAAE;EAChD,aAAa,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;CAChE,QACM;EACJ,aAAa;CACf;CACA,OAAO;AACT;;;;;AAMA,SAAS,UAAU,KAAa,SAAiC;CAC/D,MAAM,QAAQ,QAAQ,YAAY,GAAG;CACrC,IAAI,QAAQ,GACV,OAAO;CACT,MAAM,SAAS,QAAQ,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG;CACjD,MAAM,OAAO,OAAO,SAAS,OAAO,MAAM,IAAI,EAAE;CAChD,MAAM,QAAQ,OAAO,SAAS,OAAO,OAAO,IAAI,EAAE;CAClD,MAAM,QAAQ,OAAO,SAAS,OAAO,OAAO,IAAI,EAAE;CAClD,MAAM,WAAW,OAAO,SAAS,OAAO,OAAO,IAAI,EAAE;CAErD,IAAI,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,KAAK,GAC7E,OAAO;CACT,OAAO;EAAE;EAAK;EAAM,OAAO,OAAO,SAAS,QAAQ,IAAI,WAAW,IAAI;EAAG,YAAY,QAAQ;CAAM;AACrG;AAEA,eAAe,YAAgC;CAC7C,MAAM,OAAkB,CAAC;CACzB,IAAI,QAAkB,CAAC;CACvB,IAAI;EACF,QAAQ,GAAG,YAAY,OAAO;CAChC,QACM;EACJ,OAAO;CACT;CAEA,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,QAAQ,KAAK,IAAI,GACpB;EACF,MAAM,MAAM,OAAO,SAAS,MAAM,EAAE;EACpC,IAAI;GACF,MAAM,MAAM,UAAU,KAAK,GAAG,aAAa,SAAS,IAAI,QAAQ,MAAM,CAAC;GACvE,IAAI,QAAQ,MACV;GAEF,IAAI;IACF,MAAM,QAAQ,wBAAwB,KAAK,GAAG,aAAa,SAAS,IAAI,UAAU,MAAM,CAAC,CAAC,GAAG;IAC7F,IAAI,UAAU,KAAA,GACZ,IAAI,QAAQ,OAAO,SAAS,OAAO,EAAE;GACzC,QACM,CAEN;GACA,KAAK,KAAK,GAAG;EACf,QACM,CAEN;CACF;CAEA,OAAO;AACT;AAEA,eAAe,YAAgC;CAC7C,MAAM,EAAE,WAAW,MAAM,gBAAc,MAAM,CAAC,OAAO,uBAAuB,GAAG;EAAE,SAAS;EAAM,WAAW;CAAiB,CAAC;CAC7H,OAAO,cAAc,MAAM;AAC7B;AAEA,eAAe,cAAkC;CAC/C,MAAM,SAAS;CACf,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,gBAAc,kBAAkB;GAAC;GAAc;GAAmB;GAAY;EAAM,GAAG;GAC9G,SAAS;GACT,WAAW;EACb,CAAC;EACD,OAAO,gBAAgB,MAAM;CAC/B,QACM;EACJ,IAAI;GACF,MAAM,EAAE,WAAW,MAAM,gBAAc,QAAQ;IAC7C;IACA;IACA;IACA;GACF,GAAG;IAAE,SAAS;IAAM,WAAW;GAAiB,CAAC;GACjD,OAAO,gBAAgB,MAAM;EAC/B,QACM;GAEJ,OAAO,CAAC;EACV;CACF;AACF;AAEA,eAAe,gBAAoC;CACjD,IAAI,QAAQ,aAAa,SACvB,OAAO,UAAU;CACnB,IAAI,QAAQ,aAAa,SACvB,OAAO,YAAY;CACrB,OAAO,UAAU;AACnB;AAEA,SAAS,YAAY,SAAiB,UAA2C;CAC/E,MAAM,OAAiB,CAAC;CACxB,MAAM,QAAQ,CAAC,OAAO;CACtB,MAAM,uBAAO,IAAI,IAAY;CAE7B,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,IAAI;EACtB,IAAI,KAAK,IAAI,GAAG,GACd;EACF,KAAK,IAAI,GAAG;EACZ,KAAK,KAAK,GAAG;EACb,KAAK,MAAM,SAAS,SAAS,IAAI,GAAG,KAAK,CAAC,GAAG,MAAM,KAAK,KAAK;CAC/D;CAEA,OAAO;AACT;;;CAhMM,kBAAgB,UAAU,QAAQ;CAiEpC,aAA4B;CA2InB,iBAAb,MAA4B;EAC1B,2BAA4B,IAAI,IAAgD;EAEhF,MAAM,OAAO,SAAiB,MAAM,KAAK,IAAI,GAAqC;GAEhF,QAAO,MADe,KAAK,WAAW,CAAC,OAAO,GAAG,GAAG,EAAA,CACrC,IAAI,OAAO,KAAK;EACjC;EAEA,MAAM,WAAW,UAAoB,MAAM,KAAK,IAAI,GAAkD;GACpG,MAAM,0BAAU,IAAI,IAAqC;GACzD,IAAI,SAAS,WAAW,GACtB,OAAO;GAET,IAAI,OAAkB,CAAC;GACvB,IAAI;IACF,OAAO,MAAM,cAAc;GAC7B,QACM;IACJ,OAAO,CAAC;GACV;GAEA,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAI,QAAO,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;GACrD,MAAM,2BAAW,IAAI,IAAsB;GAC3C,KAAK,MAAM,OAAO,MAAM;IACtB,MAAM,WAAW,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC;IAC5C,SAAS,KAAK,IAAI,GAAG;IACrB,SAAS,IAAI,IAAI,MAAM,QAAQ;GACjC;GAEA,KAAK,MAAM,WAAW,UAAU;IAC9B,IAAI,CAAC,MAAM,IAAI,OAAO,GAAG;KACvB,KAAK,SAAS,OAAO,OAAO;KAC5B,QAAQ,IAAI,SAAS,IAAI;KACzB;IACF;IAEA,MAAM,OAAO,YAAY,SAAS,QAAQ;IAC1C,IAAI,QAAQ;IACZ,IAAI,aAA4B;IAChC,IAAI,iBAAgC;IAEpC,KAAK,MAAM,OAAO,MAAM;KACtB,MAAM,MAAM,MAAM,IAAI,GAAG;KACzB,IAAI,CAAC,KACH;KACF,SAAS,IAAI;KACb,IAAI,IAAI,eAAe,KAAA,GACrB,aAAa;UACV,IAAI,eAAe,MACtB,cAAc,IAAI;KACpB,IAAI,IAAI,eAAe,KAAA,GACrB,kBAAkB,kBAAkB,KAAK,IAAI;IACjD;IAEA,IAAI,aAA4B;IAChC,IAAI,eAAe,QAAQ,eAAe,MAAM;KAC9C,IAAI,QAAQ,aAAa,SAAS;MAChC,MAAM,QAAQ,MAAM,cAAc;MAClC,cAAc;KAChB;KAEA,MAAM,SAAS,KAAK,SAAS,IAAI,OAAO;KACxC,IAAI,WAAW,KAAA,KAAa,MAAM,OAAO,IAAI;MAC3C,MAAM,kBAAkB,MAAM,OAAO,MAAM;MAC3C,MAAM,cAAc,aAAa,OAAO;MACxC,IAAI,iBAAiB,KAAK,eAAe,GACvC,aAAc,cAAc,iBAAkB;KAClD;KACA,KAAK,SAAS,IAAI,SAAS;MAAE;MAAY,IAAI;KAAI,CAAC;IACpD,OAEE,KAAK,SAAS,OAAO,OAAO;IAG9B,QAAQ,IAAI,SAAS;KACnB,YAAY,eAAe,OAAO,OAAO,KAAK,MAAM,aAAa,EAAE,IAAI;KACvE,UAAU,KAAK,MAAM,QAAQ,IAAI;KACjC,WAAW,KAAK;KAChB,WAAW;IACb,CAAC;GACH;GAEA,OAAO;EACT;EAEA,OAAO,SAAuB;GAC5B,KAAK,SAAS,OAAO,OAAO;EAC9B;CACF;;;;;;;;;ACpRA,SAAgB,eAAe,SAAiB,GAAG,YAA8B;CAC/E,IAAI,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,IAAI,GAChD,OAAO;CAET,KAAK,MAAM,OAAO,YAAY;EAC5B,MAAM,QAAQ,KAAK,KAAK,KAAK,gBAAgB,QAAQ,OAAO;EAC5D,IAAI,GAAG,WAAW,KAAK,GACrB,OAAO;CACX;CAEA,OAAO;AACT;;AAGA,SAAgB,WAAW,KAAa,OAAe,YAAoB;CACzE,OAAO,KAAK,QAAQ,MAAM,GAAG;AAC/B;AAEA,SAAgB,aAAa,MAA+B;CAC1D,OAAO,MAAM,KAAK,SAAS,KAAK,MAAM;EACpC,KAAK,KAAK;EACV,KAAK;GAAE,GAAG,QAAQ;GAAK,GAAG,KAAK;EAAI;EAEnC,UAAU;EACV,OAAO;GAAC;GAAU;GAAQ;EAAM;EAChC,aAAa;CACf,CAAC;AACH;;AASA,eAAsB,UAAU,OAAqB,SAA+D;CAClH,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAClD,OAAO;CAET,MAAM,SAAS,cAAY,OAAO,QAAQ,OAAO;CACjD,YAAY,OAAO,QAAQ,QAAQ,QAAQ,SAAS;CAEpD,IAAI,MAAM,QACR,OAAO;CAET,YAAY,OAAO,WAAW,QAAQ,SAAS;CAC/C,MAAM,cAAY,OAAO,GAAI;CAC7B,OAAO;AACT;;;;;AAMA,eAAsB,gBAAgB,KAA4B;CAChE,IAAI;EACF,MAAM,gBAAc,YAAY;GAAC;GAAQ,OAAO,GAAG;GAAG;GAAM;EAAI,GAAG,EAAE,SAAS,IAAK,CAAC;CACtF,QACM,CAEN;AACF;AAEA,SAAS,YAAY,OAAqB,QAAwB,WAA0B;CAC1F,MAAM,MAAM,MAAM;CAClB,IAAI,QAAQ,KAAA,GACV;CAEF,IAAI,QAAQ,aAAa,SAAS;EAChC,IAAI,WACF,gBAAqB,GAAG;OAGxB,IAAI;GACF,QAAQ,KAAK,KAAK,MAAM;EAC1B,QACM,CAEN;EAEF;CACF;CAEA,IAAI,WACF,IAAI;EACF,QAAQ,KAAK,CAAC,KAAK,MAAM;EACzB;CACF,QACM,CAEN;CAGF,IAAI;EACF,QAAQ,KAAK,KAAK,MAAM;CAC1B,QACM,CAEN;AACF;AAEA,SAAS,cAAY,OAAqB,WAAqC;CAC7E,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAClD,OAAO,QAAQ,QAAQ,IAAI;CAC7B,IAAI,aAAa,GACf,OAAO,QAAQ,QAAQ,KAAK;CAE9B,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,QAAQ,iBAAiB;GAC7B,MAAM,eAAe,QAAQ,MAAM;GACnC,QAAQ,KAAK;EACf,GAAG,SAAS;EAEZ,SAAS,SAAe;GACtB,aAAa,KAAK;GAClB,QAAQ,IAAI;EACd;EAEA,MAAM,KAAK,QAAQ,MAAM;CAC3B,CAAC;AACH;;;CAzI2B,WAAA;CAErB,kBAAgB,UAAU,QAAQ;;;;;;;;;;ACAxC,SAAgB,oBAAoB,SAAyC;CAC3E,MAAM,OAAO,IAAI,IAAI,QAAQ,KAAI,WAAU,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;CAC/D,MAAM,UAA0B,CAAC;CACjC,MAAM,0BAAU,IAAI,IAAY;CAEhC,MAAM,SAAS,WAA+B;EAC5C,IAAI,QAAQ,IAAI,OAAO,EAAE,GACvB;EACF,QAAQ,IAAI,OAAO,EAAE;EACrB,KAAK,MAAM,cAAc,OAAO,WAAW;GACzC,MAAM,SAAS,KAAK,IAAI,UAAU;GAClC,IAAI,UAAU,OAAO,OAAO,OAAO,IACjC,MAAM,MAAM;EAChB;EACA,QAAQ,KAAK,MAAM;CACrB;CAEA,KAAK,MAAM,UAAU,SAAS,MAAM,MAAM;CAC1C,OAAO;AACT;;AAGA,SAAgB,eAAe,QAAsB,SAAyC;CAC5F,MAAM,OAAO,IAAI,IAAI,QAAQ,KAAI,UAAS,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;CAC5D,MAAM,QAAwB,CAAC;CAC/B,MAAM,uBAAO,IAAI,IAAY;CAE7B,MAAM,QAAQ,YAAgC;EAC5C,KAAK,MAAM,cAAc,QAAQ,WAAW;GAC1C,IAAI,KAAK,IAAI,UAAU,GACrB;GACF,KAAK,IAAI,UAAU;GACnB,MAAM,SAAS,KAAK,IAAI,UAAU;GAClC,IAAI,CAAC,QACH;GACF,MAAM,KAAK,MAAM;GACjB,KAAK,MAAM;EACb;CACF;CAEA,KAAK,MAAM;CACX,OAAO;AACT;;;;;;CC9Ca,YAAb,MAAuB;EAGD;EAFpB,QAA2B,CAAC;EAE5B,YAAY,UAA0B;GAAlB,KAAA,WAAA;EAAmB;EAEvC,KAAK,MAAqB;GACxB,KAAK,MAAM,KAAK,IAAI;GACpB,IAAI,KAAK,MAAM,SAAS,KAAK,UAC3B,KAAK,MAAM,OAAO,GAAG,KAAK,MAAM,SAAS,KAAK,QAAQ;EAC1D;EAEA,OAAO,OAAwB;GAC7B,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,IAAI;EAC1C;EAEA,KAAK,OAA2B;GAC9B,IAAI,UAAU,KAAA,KAAa,SAAS,KAAK,MAAM,QAC7C,OAAO,CAAC,GAAG,KAAK,KAAK;GACvB,OAAO,KAAK,MAAM,MAAM,CAAC,KAAK;EAChC;EAEA,QAAc;GACZ,KAAK,QAAQ,CAAC;EAChB;EAEA,IAAI,OAAe;GACjB,OAAO,KAAK,MAAM;EACpB;CACF;CAMa,eAAb,MAA0B;EAGK;EAF7B,UAAkB;EAElB,YAAY,MAAkE;GAAjD,KAAA,OAAA;EAAkD;EAE/E,KAAK,QAAmB,OAA8B;GACpD,KAAK,WAAW,MAAM,SAAS;GAC/B,MAAM,QAAQ,KAAK,QAAQ,MAAM,IAAI;GACrC,KAAK,UAAU,MAAM,IAAI,KAAK;GAC9B,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,QAAQ,KAAK,QAAQ,OAAO,EAAE,CAAC;EACrE;EAEA,MAAM,QAAyB;GAC7B,IAAI,KAAK,QAAQ,WAAW,GAC1B;GACF,KAAK,KAAK,QAAQ,KAAK,OAAO;GAC9B,KAAK,UAAU;EACjB;CACF;;;;ACoCA,SAAS,QAAM,IAA2B;CACxC,OAAO,IAAI,SAAQ,YAAW,WAAW,SAAS,EAAE,CAAC;AACvD;;AAGA,SAAgB,mBAAmB,QAAoC;CACrE,OAAO;EACL,IAAI,OAAO;EACX,OAAO,OAAO,SAAS,OAAO;EAC9B,MAAM,OAAO,QAAQ;EACrB,MAAM,SAAS,OAAO,IAAI;EAC1B,aAAa,YAAY,OAAO,IAAI;EACpC,MAAM,OAAO;EACb,OAAO,WAAW,KAAK;EACvB,KAAK,WAAW,OAAO,GAAG;EAC1B;EACA;EACA,MAAM,GAAG,QAAQ;CACnB;AACF;;;CAxF+B,aAAA;CACmB,UAAA;CAC8B,cAAA;CACzD,YAAA;CACc,WAAA;CACW,cAAA;CACpB,kBAAA;CAC4C,UAAA;CACzC,UAAA;CACqC,aAAA;CAChB,kBAAA;CACZ,gBAAA;CAelC,oBAAoB;CACpB,mBAAmB;CAqCnB,mBAAmB;CACnB,yBAAyB;CACzB,0BAA0B;CAC1B,8BAA8B;CAuBvB,aAAb,MAAwB;EAQH;EACA;EACA;EATnB,UAA2B,IAAI,eAAe;EAC9C,0BAA2B,IAAI,IAAmB;EAClD;EACA,WAAmB;EACnB,qBAA6B;EAE7B,YACE,OACA,KACA,SACA;GAHiB,KAAA,QAAA;GACA,KAAA,MAAA;GACA,KAAA,UAAA;GAEjB,KAAK,KAAK;GACV,KAAK,MAAM,eAAe,KAAK,KAAK,CAAC;GACrC,KAAK,YAAY,kBAAkB,KAAK,KAAK,KAAK,GAAG,gBAAgB;GACrE,KAAK,UAAU,MAAM;EACvB;EAEA,WAAqB;GACnB,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM,CAAC;EAC7C;EAEA,QAAsB;GACpB,OAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAI,UAAS,KAAK,KAAK,KAAK,CAAC;EACjE;EAEA,SAAS,IAAY,OAA2B;GAC9C,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,KAAK,KAAK,KAAK,KAAK,CAAC;EACpD;EAEA,MAAM,SAAS,UAAuC,CAAC,GAAkB;GACvE,MAAM,UAAU,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CACvC,QAAO,UAAS,CAAC,QAAQ,iBAAiB,MAAM,OAAO,SAAS,CAAC,CACjE,KAAI,UAAS,MAAM,MAAM;GAE5B,KAAK,MAAM,UAAU,oBAAoB,OAAO,GAC9C,MAAM,KAAK,MAAM,OAAO,EAAE;EAE9B;EAEA,MAAM,UAAyB;GAC7B,MAAM,UAAU,oBAAoB,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAI,UAAS,MAAM,MAAM,CAAC,CAAC,CAAC,QAAQ;GACnG,KAAK,MAAM,UAAU,SACnB,MAAM,KAAK,KAAK,OAAO,EAAE;EAE7B;EAEA,MAAM,MAAM,IAAY,UAA+B,CAAC,GAAyB;GAC/E,MAAM,QAAQ,KAAK,QAAQ,IAAI,EAAE;GACjC,IAAI,CAAC,OACH,OAAO;IAAE,IAAI;IAAO,OAAO,mBAAmB,GAAG;GAAG;GACtD,IAAI,CAAC,MAAM,OAAO,SAChB,OAAO;IAAE,IAAI;IAAO,OAAO,WAAW,GAAG;GAAe;GAC1D,IAAI,MAAM,WAAW,aAAa,MAAM,WAAW,cAAc,MAAM,UACrE,OAAO,EAAE,IAAI,KAAK;GACpB,IAAI,MAAM,UACR,OAAO;IAAE,IAAI;IAAO,OAAO,WAAW,GAAG;GAAe;GAI1D,MAAM,WAAW;GACjB,KAAK,WAAW,KAAK;GACrB,IAAI,CAAC,QAAQ,OAAO;IAClB,MAAM,WAAW;IACjB,MAAM,iBAAiB;IACvB,MAAM,iBAAiB;GACzB;GAEA,IAAI;IACF,MAAM,KAAK,kBAAkB,KAAK;IAGlC,IAAI,MAAM,YAAY,KAAK,UACzB,OAAO;KAAE,IAAI;KAAO,OAAO,WAAW,GAAG;IAAe;IAE1D,MAAM,YAAY;IAClB,MAAM,SAAS;IACf,MAAM,SAAS,MAAM,OAAO,OAAO,UAAU,YAAY;IACzD,KAAK,cAAc,KAAK;IAExB,MAAM,KAAK,aAAa,KAAK;IAC7B,IAAI,MAAM,UACR,OAAO;KAAE,IAAI;KAAO,OAAO,WAAW,GAAG;IAAe;IAC1D,IAAI,KAAK,UACP,OAAO;KAAE,IAAI;KAAO,OAAO;IAA8B;IAE3D,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK;IAC3C,IAAI,aAAa,MACf,OAAO;KAAE,IAAI;KAAO,OAAO;IAAS;IAEtC,OAAO,KAAK,WAAW,KAAK;GAC9B,UACQ;IACN,MAAM,WAAW;GACnB;EACF;EAEA,MAAM,KAAK,IAAkC;GAC3C,MAAM,QAAQ,KAAK,QAAQ,IAAI,EAAE;GACjC,IAAI,CAAC,OACH,OAAO;IAAE,IAAI;IAAO,OAAO,mBAAmB,GAAG;GAAG;GACtD,OAAO,KAAK,UAAU,KAAK;EAC7B;EAEA,MAAM,QAAQ,IAAkC;GAC9C,MAAM,KAAK,KAAK,EAAE;GAClB,OAAO,KAAK,MAAM,EAAE;EACtB;EAEA,UAAU,IAAkB;GAC1B,MAAM,QAAQ,KAAK,QAAQ,IAAI,EAAE;GACjC,IAAI,CAAC,OACH;GACF,MAAM,KAAK,MAAM;GACjB,KAAK,cAAc,KAAK;EAC1B;EAEA,MAAM,UAAyB;GAC7B,KAAK,WAAW;GAChB,cAAc,KAAK,SAAS;GAC5B,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GAAG,KAAK,WAAW,KAAK;GAChE,MAAM,QAAQ,IAAI,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAI,UAAS,KAAK,UAAU,KAAK,CAAC,CAAC;EAClF;;;;;;EAOA,MAAc,kBAAkB,OAA6B;GAC3D,IAAI,MAAM,OAAO,UAAU,WAAW,GACpC;GAEF,KAAK,MAAM,cAAc,eAAe,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;IACzE,MAAM,SAAS,KAAK,QAAQ,IAAI,WAAW,EAAE;IAC7C,IAAI,CAAC,UAAU,CAAC,WAAW,SACzB;IACF,IAAI,OAAO,WAAW,aAAa,OAAO,UAAU,MAClD;IAEF,KAAK,IAAI,OAAO,UAAU,wBAAwB,WAAW,GAAG,QAAQ;IACxE,MAAM,KAAK,MAAM,WAAW,EAAE;IAE9B,MAAM,WAAW,KAAK,IAAI,IAAI,WAAW,OAAO;IAChD,MAAM,gBAAyB;KAE7B,IADe,KAAK,SAAS,MACzB,MAAW,WACb,OAAO;KAET,OAAO,OAAO,WAAW;IAC3B;IAEA,OAAO,CAAC,QAAQ,KAAK,KAAK,IAAI,IAAI,UAAU,MAAM,QAAM,GAAG;IAE3D,IAAI,CAAC,QAAQ,GAAG;KACd,MAAM,SAAS,KAAK,SAAS,MAAM;KACnC,KAAK,IAAI,OAAO,UAAU,eAAe,WAAW,GAAG,OAAO,OAAO,GAAG,OAAO,OAAO,mBAAmB;IAC3G;GACF;EACF;;EAGA,SAAiB,OAA4B;GAC3C,OAAO,MAAM;EACf;;;;;EAMA,iBAAyB,OAAqC;GAC5D,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,WAAW,KAAK,QAAQ,QAAQ;GACtC,MAAM,SAAS,MAAM;GACrB,IAAI,WAAW,QAAQ,OAAO,aAAa,YAAY,MAAM,OAAO,KAAK,kBACvE,OAAO,OAAO;GAEhB,MAAM,eAAe,MAAM,UAAU,QAAQ,MAAM,cAAc,OAAO,MAAM,YAAY;GAC1F,MAAM,UAAU,KAAK,QAAQ,QAAQ,UAAU,MAAM,OAAO,IAAI,mBAAmB,KAAK,YAAY;GACpG,MAAM,eAAe;IAAE;IAAU,IAAI;IAAK;GAAQ;GAClD,OAAO;EACT;EAEA,OAAe,OAAc,QAA4B,QAAsB;GAC7E,KAAK,QAAQ,cAAc,OAAO;IAChC,UAAU,MAAM,OAAO;IACvB,OAAO,MAAM,OAAO,SAAS,MAAM,OAAO;IAC1C;IACA;GACF,CAAC;EACH;EAEA,MAAc,UAAU,OAAoC;GAC1D,KAAK,WAAW,KAAK;GACrB,MAAM,cAAc;GAIpB,MAAM,WAAW;GAEjB,IAAI,MAAM,UAAU,MAAM;IACxB,MAAM,SAAS;IACf,MAAM,MAAM;IACZ,KAAK,cAAc,KAAK;IACxB,OAAO,EAAE,IAAI,KAAK;GACpB;GAEA,MAAM,SAAS;GACf,KAAK,cAAc,KAAK;GAGxB,IAAI,MADkB,UAAU,MAAM,OAAO,MAAM,OAAO,IAAI,MAC9C,gBACd,KAAK,IAAI,OAAO,UAAU,iCAAiC;GAE7D,MAAM,EAAE,MAAM,SAAS,MAAM;GAC7B,IAAI,KAAK,mBAAmB,SAAS,MAAM;IACzC,MAAM,WAAW,MAAM,gBAAgB,IAAI;IAC3C,IAAI,SAAS,SAAS,GAAG;KACvB,KAAK,IAAI,OAAO,UAAU,QAAQ,KAAK,qBAAqB,SAAS,KAAK,IAAI,EAAE,WAAW;KAC3F,MAAM,gBAAgB,IAAI;IAC5B;GACF;GAEA,MAAM,WAAW;GACjB,MAAM,QAAQ;GACd,MAAM,MAAM;GACZ,MAAM,SAAS;GACf,KAAK,IAAI,OAAO,UAAU,SAAS;GACnC,KAAK,cAAc,KAAK;GACxB,OAAO,EAAE,IAAI,KAAK;EACpB;EAEA,YAAoB,QAA6B;GAC/C,OAAO;IACL;IACA,QAAQ;IACR,QAAQ,OAAO,OAAO,UAAU,YAAY;IAC5C,WAAW;IACX,OAAO;IACP,KAAK;IACL,WAAW;IACX,UAAU;IACV,YAAY;IACZ,UAAU;IACV,WAAW;IACX,aAAa;IACb,YAAY;IACZ,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,sBAAsB;IACtB,SAAS;IACT,UAAU;IACV,UAAU;IACV,eAAe,CAAC,OAAO;IACvB,MAAM,IAAI,UAAU,OAAO,cAAc;IACzC,YAAY;IACZ,WAAW;IACX,oBAAoB;IACpB,cAAc;GAChB;EACF;EAEA,OAAqB;GACnB,MAAM,SAAS,IAAI,IAAI,KAAK,MAAM,QAAQ,KAAI,WAAU,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;GAE5E,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,KAAK,OAAO,GAAG;IAC3C,MAAM,SAAS,OAAO,IAAI,EAAE;IAC5B,IAAI,CAAC,QAAQ;KACX,KAAK,QAAQ,OAAO,EAAE;KACtB,KAAU,UAAU,KAAK;KACzB;IACF;IACA,MAAM,gBAAgB,MAAM,OAAO,mBAAmB,OAAO;IAC7D,MAAM,SAAS;IACf,IAAI,eAAe;KACjB,MAAM,OAAO,MAAM,KAAK,KAAK,OAAO,cAAc;KAClD,MAAM,OAAO,IAAI,UAAU,OAAO,cAAc;KAChD,MAAM,KAAK,OAAO,IAAI;IACxB;IACA,IAAI,CAAC,OAAO,WAAW,KAAK,SAAS,KAAK,GACxC,KAAU,UAAU,KAAK;GAC7B;GAEA,KAAK,MAAM,CAAC,IAAI,WAAW,QACzB,IAAI,CAAC,KAAK,QAAQ,IAAI,EAAE,GACtB,KAAK,QAAQ,IAAI,IAAI,KAAK,YAAY,MAAM,CAAC;GAGjD,KAAK,aAAa;EACpB;EAEA,SAAiB,OAAuB;GACtC,OAAO,MAAM,UAAU,QAAQ,MAAM,WAAW;EAClD;;EAGA,WAAmB,OAAwB;GACzC,MAAM,UAAU;GAChB,MAAM,aAAa,YAAY,MAAM,OAAO,IAAI;GAChD,OAAO,eAAe,UAAU,CAAC,OAAO,IAAI,CAAC,SAAS,UAAU;EAClE;;;;;;EAOA,eAAuB,OAAwB;GAC7C,MAAM,aAAa,SAAS,MAAM,OAAO,IAAI;GAC7C,MAAM,aAAa,eAAe,YAC9B,CAAC,aAAa,WAAW,KAAK,WAAW,IACzC,CAAC,YAAY,WAAW;GAC5B,OAAO,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC;EAChC;;EAGA,MAAc,YAAY,OAAc,MAAc,WAAqC;GAEzF,QAAO,MADe,QAAQ,IAAI,KAAK,eAAe,KAAK,CAAC,CAAC,KAAI,SAAQ,UAAU,MAAM,MAAM,SAAS,CAAC,CAAC,EAAA,CAC3F,KAAK,OAAO;EAC7B;EAEA,MAAc,UAAU,OAAsC;GAC5D,MAAM,OAAO,MAAM,OAAO;GAC1B,IAAI,SAAS,MACX,OAAO;GAOT,MAAM,QAAQ,KAAK,eAAe,KAAK;GACvC,MAAM,YAAY,YAA8B;IAE9C,QAAO,MADe,QAAQ,IAAI,MAAM,KAAI,SAAQ,WAAW,MAAM,IAAI,CAAC,CAAC,EAAA,CAC5D,MAAM,OAAO;GAC9B;GAEA,IAAI,OAAO,MAAM,UAAU;GAC3B,IAAI,CAAC,MAAM;IACT,MAAM,QAAM,uBAAuB;IACnC,OAAO,MAAM,UAAU;GACzB;GAEA,MAAM,YAAY,OAAO,SAAS;GAClC,IAAI,MACF,OAAO;GAET,MAAM,UAAU,MAAM,gBAAgB,IAAI;GAC1C,MAAM,SAAS,QAAQ,SAAS,IAAI,SAAS,QAAQ,KAAK,IAAI,EAAE,KAAK;GAErE,IAAI,MAAM,OAAO,mBAAmB,SAAS;IAC3C,MAAM,SAAS;IACf,MAAM,YAAY,QAAQ,KAAK,oBAAoB;IACnD,KAAK,IAAI,OAAO,UAAU,GAAG,MAAM,UAAU,wCAAwC;IACrF,KAAK,cAAc,KAAK;IACxB,OAAO,MAAM;GACf;GAEA,KAAK,IAAI,OAAO,UAAU,iBAAiB,KAAK,oBAAoB,OAAO,mBAAmB;GAC9F,OAAO;EACT;EAEA,MAAc,aAAa,OAA6B;GACtD,MAAM,OAAO,MAAM,OAAO;GAC1B,IAAI,CAAC,QAAS,KAAK,WAAW,MAAM,eAClC;GAEF,MAAM,OAAO,KAAK,UAAU,KAAK;GACjC,MAAM,MAAM,WAAW,MAAM,OAAO,GAAG;GACvC,MAAM,OAAO,iBAAiB,KAAK,MAAM,IAAI;GAC7C,KAAK,IAAI,OAAO,UAAU,cAAc,KAAK,QAAQ,GAAG,KAAK,KAAK,GAAG,GAAG;GAExE,MAAM,WAAW,IAAI,cAAc,SAAS,SAAS;IACnD,IAAI,KAAK,KAAK,CAAC,CAAC,SAAS,GACvB,KAAK,IAAI,OAAO,UAAU,eAAe,MAAM;GACnD,CAAC;GAED,MAAM,QAAQ,MAAM,eAAe,KAAK,SAAS,KAAK,UAAU,GAAG,MAAM;IACvE;IACA,KAAK;KAAE,GAAG,QAAQ;KAAK,GAAG,cAAc,KAAK,KAAK,IAAI;IAAE;IACxD,OAAO;KAAC;KAAU;KAAQ;IAAM;IAChC,aAAa;GACf,CAAC;GACD,MAAM,QAAQ,GAAG,SAAQ,UAAS,SAAS,KAAK,UAAU,KAAK,CAAC;GAChE,MAAM,QAAQ,GAAG,SAAQ,UAAS,SAAS,KAAK,UAAU,KAAK,CAAC;GAEhE,MAAM,OAAO,MAAM,IAAI,SAAwB,YAAY;IACzD,MAAM,QAAQ,iBAAiB;KAC7B,KAAK,IAAI,OAAO,UAAU,6BAA6B,KAAK,UAAU,GAAG;KACzE,IAAI;MACF,MAAM,KAAK,SAAS;KACtB,QACM,CAEN;IACF,GAAG,KAAK,SAAS;IACjB,MAAM,KAAK,SAAS,aAAa;KAC/B,aAAa,KAAK;KAClB,QAAQ,QAAQ;IAClB,CAAC;IACD,MAAM,KAAK,UAAU,UAAU;KAC7B,aAAa,KAAK;KAClB,KAAK,IAAI,OAAO,UAAU,qBAAsB,MAAgB,SAAS;KACzE,QAAQ,IAAI;IACd,CAAC;GACH,CAAC;GAED,MAAM,gBAAgB;GACtB,IAAI,SAAS,GACX,KAAK,IAAI,OAAO,UAAU,oBAAoB;QAC3C,IAAI,SAAS,MAChB,KAAK,IAAI,OAAO,UAAU,8BAA8B,KAAK,qBAAqB;EACtF;EAEA,WAAmB,OAA2B;GAC5C,MAAM,OAAO,KAAK,UAAU,KAAK;GACjC,MAAM,MAAM,WAAW,MAAM,OAAO,GAAG;GACvC,MAAM,UAAU,eAAe,MAAM,OAAO,SAAS,KAAK,UAAU;GAIpE,IAAI,UAAkC,CAAC;GACvC,IAAI,MAAM,OAAO,QAAQ,SAAS,GAAG;IACnC,MAAM,OAAO,mBAAmB,MAAM,OAAO,SAAS,GAAG;IACzD,MAAM,SAAS,YAAY,IAAI;IAC/B,IAAI,OAAO,UAAU,MACnB,KAAK,IAAI,OAAO,UAAU,YAAY,KAAK,sBAAsB,OAAO,OAAO;SAC5E,IAAI,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,SAAS,GACxC,KAAK,IAAI,OAAO,UAAU,YAAY,KAAK,IAAI,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,OAAO,OAAO;IACvF,UAAU,OAAO;GACnB;GAEA,MAAM,gBAAoD;IAAE,GAAG,QAAQ;IAAK,GAAG;GAAQ;GACvF,MAAM,OAAO,cAAc,iBAAiB,MAAM,OAAO,MAAM,IAAI,GAAG,aAAa;GACnF,MAAM,MAAM;IAEV,GAAG,gBAAgB,cAAc,MAAM,OAAO,KAAK,IAAI,GAAG,aAAa;IACvE,GAAG;IAGH,GAAG,gBAAgB,cAAc,MAAM,OAAO,UAAU,IAAI,GAAG,aAAa;IAC5E,mBAAmB,MAAM,OAAO;IAChC,sBAAsB,OAAO,KAAK,QAAQ,QAAQ,IAAI;GACxD;GAIA,KAAK,IAAI,OAAO,UAAU,UAAU,QAAQ,GAAG,iBAAiB,MAAM,OAAO,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,GAAG;GAEpG,IAAI;GACJ,IAAI;IACF,QAAQ,aAAa;KAAE;KAAS;KAAM;KAAK;IAAI,CAAC;GAClD,SACO,OAAO;IACZ,MAAM,SAAS;IACf,MAAM,YAAa,MAAgB;IACnC,KAAK,IAAI,OAAO,UAAU,iBAAiB,MAAM,WAAW;IAC5D,KAAK,cAAc,KAAK;IACxB,OAAO;KAAE,IAAI;KAAO,OAAO,MAAM;IAAU;GAC7C;GAEA,MAAM,QAAQ;GACd,MAAM,MAAM,MAAM,OAAO;GACzB,MAAM,YAAY,KAAK,IAAI;GAC3B,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;IAC3C,MAAM;IACN,QAAQ,GAAG,QAAQ,GAAG,KAAK,KAAK,GAAG,IAAI,KAAK;GAC9C,CAAC;GACD,MAAM,WAAW;GACjB,MAAM,aAAa;GACnB,MAAM,cAAc;GACpB,MAAM,iBAAiB;GACvB,MAAM,iBAAiB;GACvB,KAAK,cAAc,KAAK;GAExB,MAAM,SAAS,IAAI,cAAc,QAAQ,SAAS,KAAK,IAAI,OAAO,QAAQ,IAAI,CAAC;GAC/E,MAAM,SAAS,IAAI,cAAc,QAAQ,SAAS,KAAK,IAAI,OAAO,QAAQ,IAAI,CAAC;GAC/E,MAAM,QAAQ,GAAG,SAAQ,UAAS,OAAO,KAAK,UAAU,KAAK,CAAC;GAC9D,MAAM,QAAQ,GAAG,SAAQ,UAAS,OAAO,KAAK,UAAU,KAAK,CAAC;GAE9D,MAAM,KAAK,UAAU,UAAU;IAC7B,MAAM,YAAa,MAAgB;IACnC,KAAK,IAAI,OAAO,UAAU,kBAAkB,MAAM,WAAW;IAC7D,OAAO,MAAM,QAAQ;IACrB,OAAO,MAAM,QAAQ;IACrB,KAAK,WAAW,OAAO,OAAO,MAAM,IAAI;GAC1C,CAAC;GAED,MAAM,KAAK,SAAS,MAAM,WAAW;IACnC,OAAO,MAAM,QAAQ;IACrB,OAAO,MAAM,QAAQ;IACrB,KAAK,WAAW,OAAO,OAAO,MAAM,MAAM;GAC5C,CAAC;GAED,KAAU,eAAe,OAAO,KAAK;GACrC,OAAO,EAAE,IAAI,KAAK;EACpB;;EAGA,MAAc,iBAAiB,OAAyE;GACtG,MAAM,EAAE,MAAM,WAAW,MAAM;GAC/B,IAAI,SAAS,MACX,OAAO;IAAE,SAAS;IAAM,IAAI;IAAG,QAAQ;GAAqB;GAE9D,OAAO,YAAY;IACjB,MAAM,OAAO;IACb,OAAO,KAAK,WAAW,KAAK;IAC5B;IACA,WAAW,OAAO;IAClB,MAAM,OAAO;GACf,CAAC;EACH;EAEA,MAAc,eAAe,OAAc,OAAoC;GAC7E,MAAM,EAAE,MAAM,WAAW,MAAM;GAE/B,IAAI,SAAS,MAAM;IACjB,IAAI,MAAM,UAAU,SAAS,MAAM,WAAW,YAC5C;IACF,MAAM,SAAS;IACf,MAAM,SAAS,OAAO,UAAU,YAAY;IAC5C,KAAK,IAAI,OAAO,UAAU,0DAA0D;IACpF,KAAK,cAAc,KAAK;IACxB;GACF;GAEA,MAAM,WAAW,KAAK,IAAI,IAAI,OAAO;GACrC,OAAO,KAAK,IAAI,IAAI,UAAU;IAC5B,IAAI,MAAM,UAAU,SAAS,MAAM,WAAW,cAAc,KAAK,UAC/D;IACF,IAAI,MAAM,KAAK,YAAY,OAAO,MAAM,KAAK,IAAI,OAAO,WAAW,GAAI,CAAC,GAAG;KACzE,MAAM,YAAY;KAClB,MAAM,SAAS,OAAO,UAAU,YAAY;KAC5C,MAAM,SAAS;KACf,MAAM,cAAc,KAAK,IAAI;KAC7B,KAAK,IAAI,OAAO,UAAU,iCAAiC,MAAM;KACjE,KAAK,cAAc,KAAK;KACxB;IACF;IACA,MAAM,QAAM,GAAG;GACjB;GAEA,IAAI,MAAM,UAAU,SAAS,MAAM,WAAW,YAAY;IACxD,MAAM,SAAS;IACf,MAAM,SAAS;IACf,MAAM,iBAAiB,KAAK,IAAI;IAChC,KAAK,IAAI,OAAO,UAAU,yBAAyB,KAAK,SAAS,OAAO,eAAe,wBAAwB;IAC/G,KAAK,cAAc,KAAK;GAC1B;EACF;EAEA,WAAmB,OAAc,OAAqB,MAAqB,QAAqC;GAC9G,IAAI,MAAM,UAAU,OAClB;GACF,IAAI,MAAM,QAAQ,MAChB,KAAK,QAAQ,OAAO,MAAM,GAAG;GAC/B,MAAM,QAAQ;GACd,MAAM,MAAM;GACZ,MAAM,YAAY;GAClB,MAAM,aAAa;GACnB,MAAM,WAAW;GACjB,MAAM,aAAa;GAInB,MAAM,eAAe,SAAS,QAAQ,WAAW,QAAQ,MAAM,cAAc;GAC7E,MAAM,SAAS,eACX,MAAM,YACN,WAAW,OAAO,UAAU,WAAW,QAAQ;GACnD,MAAM,WAAW,MAAM,cAAc,OAAO,IAAI,KAAK,IAAI,IAAI,MAAM;GACnE,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,GAAI,CAAC,EAAE;GAI3D,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;IAC3C,MAAM;IACN;IACA,WAAW;GACb,CAAC;GAED,IAAI,MAAM,UAAU;IAClB,MAAM,SAAS;IACf,KAAK,cAAc,KAAK;IACxB;GACF;GAEA,MAAM,UAAU,MAAM,OAAO;GAC7B,IAAI,YAAY,QAAQ,cACtB,MAAM,WAAW;GAEnB,KAAK,IAAI,OAAO,UAAU,eAAe,kBAAkB,WAAW,eAAe,OAAO,SAAS,QAAQ;GAC7G,MAAM,YAAY,eAAe,SAAS,eAAe;GAEzD,IAAI,QAAQ,WAAW,MAAM,WAAW,QAAQ,YAAY;IAC1D,MAAM,YAAY;IAClB,MAAM,YAAY,eAAe,MAAM,UAAU,OAAO;IACxD,MAAM,SAAS;IACf,MAAM,cAAc,KAAK,IAAI,IAAI;IACjC,KAAK,IAAI,OAAO,UAAU,WAAW,MAAM,SAAS,GAAG,QAAQ,WAAW,MAAM,UAAU,GAAG;IAC7F,MAAM,aAAa,iBAAiB;KAClC,MAAM,aAAa;KACnB,KAAU,MAAM,MAAM,OAAO,IAAI,EAAE,OAAO,KAAK,CAAC;IAClD,GAAG,SAAS;IACZ,MAAM,WAAW,MAAM;GACzB,OACK;IACH,MAAM,SAAS;IACf,MAAM,cAAc;IACpB,MAAM,YAAY,QAAQ,UACtB,iBAAiB,QAAQ,WAAW,YAAY,OAAO,KACvD,GAAG,OAAO;IACd,KAAK,IAAI,OAAO,UAAU,MAAM,SAAS;IACzC,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;KAC3C,MAAM;KACN,QAAQ,MAAM;KACd,WAAW;IACb,CAAC;IACD,KAAK,OAAO,OAAO,SAAS,MAAM,SAAS;GAC7C;GAEA,KAAK,cAAc,KAAK;EAC1B;EAEA,MAAc,OAAsB;GAClC,IAAI,KAAK,UACP;GACF,MAAM,MAAM,KAAK,IAAI;GAErB,MAAM,KAAK,QAAQ,YAAY,KAAK,GAAG;GACvC,MAAM,KAAK,gBAAgB,GAAG;GAG9B,MAAM,QAAQ,WAAW,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAI,UAAS,KAAK,WAAW,OAAO,GAAG,CAAC,CAAC;GAE7F,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GAAG;IACzC,IAAI,MAAM,KAAK,mBAAmB,KAAK,GACrC;IACF,IAAI,KAAK,mBAAmB,OAAO,GAAG,GAAG;KACvC,MAAM,KAAK,QAAQ,MAAM,OAAO,EAAE;KAClC;IACF;IACA,IAAI,MAAM,WAAW,aAAa,MAAM,gBAAgB,QAAQ,OAAO,MAAM,eAAe,MAAM,eAAe,MAC/G,KAAU,MAAM,MAAM,OAAO,IAAI,EAAE,OAAO,KAAK,CAAC;GACpD;GAEA,KAAK,aAAa;EACpB;;EAGA,MAAc,gBAAgB,KAA4B;GACxD,MAAM,MAAM,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAO,UAC5C,MAAM,QAAQ,QACX,MAAM,UAAU,QAChB,MAAM,MAAM,sBAAsB,2BAA2B;GAClE,IAAI,IAAI,WAAW,GACjB;GAEF,KAAK,MAAM,SAAS,KAAK,MAAM,qBAAqB;GACpD,IAAI;IACF,MAAM,UAAU,MAAM,KAAK,QAAQ,WAAW,IAAI,KAAI,UAAS,MAAM,GAAI,CAAC;IAC1E,KAAK,MAAM,SAAS,KAAK,MAAM,YAAY,QAAQ,IAAI,MAAM,GAAI,KAAK;GACxE,QACM,CAEN;EACF;EAEA,MAAc,WAAW,OAAc,KAA4B;GACjE,MAAM,EAAE,MAAM,WAAW,MAAM;GAC/B,IAAI,SAAS,QAAQ,MAAM,SACzB;GAEF,MAAM,UAAU;GAChB,IAAI;IAGF,IAAI,MAAM,MAAM,wBAAwB,wBAAwB;KAC9D,MAAM,uBAAuB;KAE7B,MAAM,YAAY,MADM,KAAK,YAAY,OAAO,MAAM,OAAO,SAAS,IACxC,WAAW;IAC3C;IAEA,IAAI,MAAM,WAAW,aAAa,CAAC,OAAO,SACxC;IACF,IAAI,MAAM,MAAM,cAAc,OAAO,YACnC;IAEF,MAAM,QAAQ,MAAM,KAAK,iBAAiB,KAAK;IAC/C,MAAM,cAAc;IACpB,MAAM,aAAa,MAAM;IACzB,MAAM,YAAY,MAAM,UAAU,WAAW,MAAM;IAEnD,IAAI,MAAM,SAAS;KACjB,IAAI,MAAM,WAAW,aAAa;MAChC,KAAK,IAAI,OAAO,UAAU,GAAG,MAAM,OAAO,oBAAoB,MAAM,GAAG,IAAI;MAC3E,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;OAAE,MAAM;OAAa,QAAQ,MAAM;MAAO,CAAC;MACxF,KAAK,OAAO,OAAO,aAAa,MAAM,MAAM;KAC9C;KACA,MAAM,SAAS;KACf,MAAM,iBAAiB;KACvB,MAAM,iBAAiB;KACvB;IACF;IAEA,MAAM,kBAAkB;IACxB,IAAI,MAAM,kBAAkB,OAAO,oBAAoB;KACrD,IAAI,MAAM,mBAAmB,MAAM;MACjC,MAAM,iBAAiB;MACvB,KAAK,IAAI,OAAO,UAAU,cAAc,MAAM,OAAO,IAAI,MAAM,eAAe,+BAA+B;MAC7G,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;OAAE,MAAM;OAAa,QAAQ,MAAM;MAAO,CAAC;MACxF,KAAK,OAAO,OAAO,aAAa,MAAM,MAAM;KAC9C;KACA,MAAM,SAAS;IACjB;GACF,UACQ;IACN,MAAM,UAAU;GAClB;EACF;EAEA,MAAc,mBAAmB,OAAgC;GAC/D,MAAM,QAAQ,MAAM,OAAO,UAAU;GACrC,MAAM,MAAM,MAAM,WAAW,YAAY;GACzC,IAAI,SAAS,KAAK,QAAQ,QAAQ,MAAM,UAAU,QAAQ,MAAM,WAAW,aAAa,OAAO,OAC7F,OAAO;GAET,MAAM,SAAS,qBAAqB,KAAK,MAAM,MAAM,OAAO,IAAI,EAAE,eAAe,KAAK,MAAM,QAAQ,OAAO,IAAI,EAAE;GACjH,KAAK,IAAI,OAAO,UAAU,GAAG,OAAO,cAAc;GAClD,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;IAAE,MAAM;IAAkB;GAAO,CAAC;GAC/E,KAAK,OAAO,OAAO,OAAO,MAAM;GAChC,MAAM,KAAK,QAAQ,MAAM,OAAO,EAAE;GAClC,OAAO;EACT;EAEA,mBAA2B,OAAc,KAAsB;GAC7D,MAAM,EAAE,MAAM,WAAW,MAAM;GAC/B,IAAI,SAAS,QAAQ,MAAM,WAAW,aAAa,MAAM,WAAW,aAClE,OAAO;GACT,IAAI,MAAM,mBAAmB,QAAQ,OAAO,uBAAuB,GACjE,OAAO;GACT,IAAI,MAAM,MAAM,iBAAiB,OAAO,qBACtC,OAAO;GAET,KAAK,IAAI,OAAO,UAAU,iBAAiB,OAAO,oBAAoB,uBAAuB;GAC7F,KAAK,QAAQ,QAAQ,OAAO,MAAM,OAAO,IAAI;IAAE,MAAM;IAAkB,QAAQ;GAAgC,CAAC;GAChH,KAAK,OAAO,OAAO,kBAAkB,iBAAiB,KAAK,MAAM,OAAO,sBAAsB,GAAI,EAAE,EAAE;GACtG,OAAO;EACT;EAEA,WAAmB,OAAoB;GACrC,IAAI,MAAM,eAAe,MAAM;IAC7B,aAAa,MAAM,UAAU;IAC7B,MAAM,aAAa;GACrB;EACF;EAEA,UAAkB,OAA4B;GAC5C,OAAO,mBAAmB,MAAM,MAAM;EACxC;EAEA,KAAa,OAA0B;GACrC,MAAM,SAAS,MAAM;GACrB,MAAM,OAAO,YAAY,OAAO,IAAI;GACpC,OAAO;IACL,IAAI,OAAO;IACX;IACA,UAAU,SAAS,OAAO,IAAI;IAC9B,KAAK,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;IACzF,QAAQ,MAAM;IACd,QAAQ,MAAM;IACd,WAAW,MAAM;IACjB,KAAK,MAAM;IACX,WAAW,MAAM;IACjB,UAAU,MAAM;IAChB,YAAY,MAAM;IAClB,UAAU,MAAM;IAChB,YAAY,OAAO,QAAQ;IAC3B,WAAW,MAAM;IACjB,aAAa,MAAM;IACnB,gBAAgB,MAAM;IACtB,eAAe,MAAM,KAAK;IAC1B,SAAS,KAAK,iBAAiB,KAAK;IACpC,YAAY,MAAM;IAClB,WAAW,MAAM;GACnB;EACF;EAEA,IAAY,OAAc,QAAmB,MAAoB;GAC/D,MAAM,OAAgB;IAAE,IAAI,KAAK,IAAI;IAAG;IAAQ;GAAK;GACrD,MAAM,KAAK,KAAK,IAAI;GACpB,KAAK,QAAQ,SAAS,OAAO,MAAM,OAAO,IAAI,IAAI;GAClD,KAAK,IAAI,QAAQ;IAAE,MAAM;IAAO,IAAI,KAAK;IAAI,UAAU,MAAM,OAAO;IAAI,OAAO,CAAC,IAAI;GAAE,CAAC;GACvF,IAAI,WAAW,UACb,OAAO,MAAM,IAAI,MAAM,OAAO,GAAG,IAAI,MAAM;EAC/C;EAEA,cAAsB,OAAoB;GACxC,IAAI,CAAC,KAAK,QAAQ,IAAI,MAAM,OAAO,EAAE,GACnC;GACF,KAAK,IAAI,QAAQ;IACf,MAAM;IACN,IAAI,KAAK,IAAI;IACb,UAAU,MAAM,OAAO;IACvB,QAAQ,KAAK,KAAK,KAAK;GACzB,CAAC;EACH;EAEA,eAA6B;GAC3B,MAAM,QAAQ,KAAK,SAAS;GAC5B,MAAM,YAAY,CAChB,MAAM,eAAe,IACrB,GAAG,MAAM,QAAQ,KAAI,WAAU;IAC7B,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;GACT,CAAC,CAAC,KAAK,GAAG,CAAC,CACb,CAAC,CAAC,KAAK,GAAG;GAEV,IAAI,cAAc,KAAK,oBACrB;GACF,KAAK,qBAAqB;GAC1B,KAAK,IAAI,QAAQ;IAAE,MAAM;IAAS,IAAI,KAAK,IAAI;IAAG;GAAM,CAAC;EAC3D;CACF;;;;;;;;;ACv5BA,SAAgB,mBAAmB,OAAwB;CACzD,IAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,IAAI,GAC9C,OAAO;CAET,MAAM,UAAU,MAAM,QAAQ,SAAS,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC7D,IAAI,QAAQ,WAAW,GACrB,OAAO;CACT,IAAI,YAAY,iBACd,OAAO;CAET,MAAM,WAAW,QAAQ,MAAM,GAAG;CAClC,IAAI,SAAS,MAAK,YAAW,QAAQ,WAAW,KAAK,YAAY,OAAO,YAAY,IAAI,GACtF,OAAO;CACT,IAAI,CAAC,cAAc,IAAI,SAAS,EAAG,GACjC,OAAO;CACT,OAAO,SAAS,OAAM,YAAW,YAAY,KAAK,OAAO,CAAC;AAC5D;;AAGA,SAAgB,SAAS,QAAgB,OAAwB;CAC/D,IAAI,WAAW,OACb,OAAO;CAGT,MAAM,WAAW,KAAK,SAAS,QAAQ,KAAK;CAC5C,OAAO,CAAC,KAAK,WAAW,QAAQ,MAAM,SAAS,WAAW,KAAK,CAAC,SAAS,WAAW,IAAI;AAC1F;;;;;;;AAeA,SAAgB,mBAAmB,SAAyB,eAAyB,CAAC,GAAiB;CACrG,MAAM,WAA2B,CAAC;CAClC,MAAM,aAAa;EAAE;EAAY;EAAU,MAAM,GAAG,QAAQ;CAAE;CAE9D,MAAM,OAAO,OAAe,QAAgB,SAAgD;EAC1F,IAAI,MAAM,KAAK,CAAC,CAAC,WAAW,GAC1B;EAGF,MAAM,WAAW,KAAK,UAAU,gBAAgB,UAAU,gBAAgB,OAAO,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC;EACrG,SAAS,KAAK;GACZ,MAAM;GACN;GACA,OAAO,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC;GAChE,OAAO,SAAS;EAClB,CAAC;CACH;CAEA,KAAK,MAAM,SAAS,cAAc,IAAI,OAAO,UAAU,UAAU;CAEjE,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,mBAAmB,MAAM;EACtC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,QAAQ,GAAG,IAAI,OAAO,GAAG,OAAO,GAAG,GAAG,QAAQ,IAAI;EACpG,KAAK,MAAM,SAAS,OAAO,aAAa,IAAI,OAAO,GAAG,OAAO,GAAG,eAAe,IAAI;CACrF;CAIA,MAAM,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK;CAElF,OAAO,OAAO,KAAK,OAAO,UAAU;EAClC,MAAM,SAAS,OAAO,MAAM,GAAG,KAAK,CAAC,CAAC,MAAK,cAAa,SAAS,UAAU,MAAM,MAAM,IAAI,CAAC;EAC5F,IAAI,WAAW,KAAA,GACb,OAAO;GAAE,MAAM,MAAM;GAAM,QAAQ,MAAM;GAAQ,UAAU;GAAM,MAAM;EAAK;EAC9E,MAAM,OAAO,OAAO,SAAS,MAAM,OAC/B,uBAAuB,OAAO,WAC9B,cAAc,OAAO;EACzB,OAAO;GAAE,MAAM,MAAM;GAAM,QAAQ,MAAM;GAAQ,UAAU;GAAO;EAAK;CACzE,CAAC;AACH;;AAqDA,SAAS,aAAa,YAAoC;CACxD,IAAI;EACF,OAAQ,KAAK,MAAM,cAAc,IAAI,CAAC,CAA2B,WAAW;CAC9E,QACM;EACJ,OAAO;CACT;AACF;;AAGA,SAAS,eAAe,MAA8B;CACpD,IAAI,SAAS,MACX,OAAO;CACT,IAAI;EACF,OAAO,EAAE,aAAa,KAAK,MAAM,IAAI,CAAC,aAAa,KAAK;CAC1D,QACM;EACJ,OAAO;CACT;AACF;;;;;;AAOA,SAAS,eAAe,YAA6C;CACnE,IAAI,eAAe,MACjB,OAAO,CAAC;CACV,IAAI;EACF,MAAM,SAAS,aAAa,KAAK,MAAM,UAAU,CAAC;EAClD,IAAI,kBAAkB,KAAK,QACzB,OAAO,CAAC;EAEV,OAAO,mBADyB,OAAO,QAAQ,KAAI,YAAW;GAAE,GAAG;GAAQ,MAAM,OAAO,QAAQ;EAAK,EAC3E,GAAS,OAAO,QAAQ,YAAY,CAAC,CAC5D,QAAO,UAAS,MAAM,QAAQ,CAAC,CAC/B,KAAI,WAAU;GAAE,MAAM,MAAM;GAAM,QAAQ,MAAM;EAAO,EAAE;CAC9D,QACM;EACJ,OAAO,CAAC;CACV;AACF;;;;;AAMA,SAAS,SAAS,MAA8B;CAC9C,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,KAAK,KAAK,SAAS,IACjE,OAAO;CACT,IAAI,CAAC,YAAY,KAAK,IAAI,KAAK,SAAS,OAAO,SAAS,MACtD,OAAO;CACT,OAAO;AACT;;AAGA,SAAS,SAAS,MAAwD;CACxE,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK;AACnC;;AAGA,SAAgB,YAAY,QAAwB;CAClD,MAAM,UAAU,OAAO,QAAQ,gBAAgB,GAAG,CAAC,CAAC,QAAQ,YAAY,EAAE;CAC1E,OAAO,QAAQ,SAAS,IAAI,QAAQ,MAAM,GAAG,IAAI;AACnD;;;CAvN6B,YAAA;CACG,YAAA;CACN,cAAA;CAC4B,WAAA;CACtB,cAAA;CACgD,aAAA;CAC7C,gBAAA;CAE7B,WAAW;CACX,gCAAgB,IAAI,IAAI;EAAC;EAAU;EAAW;EAAO;CAAM,CAAC;CAE5D,SAAS;CAuNF,gBAAb,MAA2B;EAUN;;;;;;EAJnB,wBAAyB,IAAI,IAAiD;EAC9E,aAA2C;EAE3C,YACE,SAQA;GARiB,KAAA,UAAA;EAQhB;;EAGH,MAAM,OAAsB;GAC1B,MAAM,KAAK,QAAQ;EACrB;EAEA,IAAI,YAAoB;GACtB,OAAO,KAAK,WAAW;EACzB;;EAGA,IAAI,QAAsB;GACxB,MAAM,MAAM,KAAK,QAAQ,KAAK,WAAW,CAAC;GAC1C,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,MAAM,KAAK,UAAU;IAGpD,IAAI,SAAS,MAAM,MAAM,GAAG,GAC1B,OAAO;KAAE,GAAG;KAAO,UAAU;KAAO,MAAM;IAAgC;IAC5E,OAAO;GACT,CAAC;EACH;;EAGA,IAAI,YAAsB;GACxB,OAAO,KAAK,MAAM,QAAO,UAAS,MAAM,QAAQ,CAAC,CAAC,KAAI,UAAS,MAAM,IAAI;EAC3E;EAEA,OAAqB;GACnB,MAAM,QAAQ,KAAK,KAAK;GACxB,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,SAAS,KAAK,MAAM,IAAI,KAAK,IAAI;IACvC,IAAI,WAAW,KAAA,KAAa,OAAO,QAAQ,SAAS,IAAI,GACtD,KAAU,gBAAgB;GAC9B;GAEA,OAAO,MAAM,KAAK,SAAS;IACzB,MAAM,SAAS,KAAK,MAAM,IAAI,KAAK,IAAI;IACvC,OAAO;KACL,GAAG;KACH,WAAW,WAAW,KAAA,KAAa,OAAO,QAAQ,SAAS,IAAI,IAAI,OAAO,YAAY;IACxF;GACF,CAAC;EACH;;EAGA,QAAQ,MAA6B;GACnC,IAAI,CAAC,qBAAqB,KAAK,IAAI,KAAK,KAAK,SAAS,IAAI,GACxD,OAAO;GACT,MAAM,OAAO,KAAK,KAAK,KAAK,WAAW,GAAG,IAAI;GAC9C,OAAO,GAAG,WAAW,IAAI,IAAI,OAAO;EACtC;;EAGA,MAAM,OAAO,UAAiC,CAAC,GAAgE;GAE7G,IAAI,CADW,KAAK,QAAQ,UACvB,CAAA,CAAO,SACV,OAAO;IAAE,IAAI;IAAO,OAAO;GAAuB;GAEpD,MAAM,WAAW,QAAQ,aAAa,KAAA,KAAa,QAAQ,SAAS,SAAS,IAAI,QAAQ,WAAW;GAEpG,MAAM,MAAM,KAAK,WAAW;GAC5B,MAAM,UAAU,KAAK,QAAQ,WAAW;GACxC,MAAM,UAAU,KAAK,KAAK,KAAK,YAAY,KAAK,IAAI,GAAG;GACvD,MAAM,YAAY,KAAK,IAAI;GAE3B,MAAM,OAAO,UAAU,IAAI,KAAK,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,QAAQ,SAAS,GAAG,CAAC,CAAC,QAAQ,WAAW,EAAE,EAAE,GAAG,YAAY,MAAO;GAC5H,MAAM,cAAc,KAAK,KAAK,KAAK,IAAI;GAEvC,IAAI;IACF,GAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;IACzC,KAAK,SAAS,SAAS,8BAA8B,QAAQ,UAAU;IACvE,KAAK,SAAS,SAAS,gCAAgC,QAAQ,WAAW;IAC1E,KAAK,SAAS,SAAS,OAAO,QAAQ,MAAM;IAE5C,MAAM,OAA+B,CAAC;IACtC,KAAK,MAAM,YAAY,KAAK,OAAO;KACjC,IAAI,CAAC,SAAS,YAAY,CAAC,GAAG,WAAW,SAAS,IAAI,GACpD;KACF,MAAM,OAAO,YAAY,SAAS,IAAI;KACtC,IAAI,KAAK,MAAK,UAAS,MAAM,SAAS,IAAI,GACxC;KACF,KAAK,SAAS,SAAS,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS,IAAI;KAC7D,KAAK,KAAK;MAAE;MAAM,MAAM,SAAS;MAAM,QAAQ,SAAS;KAAO,CAAC;IAClE;IAEA,MAAM,WAA2B;KAAE,SAAS;KAAG;KAAW,UAAU,GAAG,SAAS;KAAG;IAAK;IACxF,GAAG,cAAc,KAAK,KAAK,SAAS,QAAQ,GAAG,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,GAAG;IAEvF,MAAM,UAAU,SAAS,aAAa,aAAa,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;IAE3E,GAAG,OAAO,SAAS;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;IACnD,KAAK,MAAM;IAEX,MAAM,QAAQ,GAAG,SAAS,WAAW;IACrC,KAAK,MAAM,IAAI,MAAM;KAAE,KAAK,GAAG,MAAM,KAAK,GAAG,KAAK,MAAM,MAAM,OAAO;KAAK,WAAW,aAAa;IAAK,CAAC;IACxG,OAAO;KAAE,IAAI;KAAM,MAAM;MAAE;MAAM,WAAW,MAAM;MAAM;MAAW,WAAW,aAAa;KAAK;IAAE;GACpG,SACO,OAAO;IACZ,GAAG,OAAO,SAAS;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;IACnD,GAAG,OAAO,aAAa,EAAE,OAAO,KAAK,CAAC;IACtC,OAAO;KAAE,IAAI;KAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAE;GACpF;EACF;EAEA,OAAO,MAAuB;GAC5B,MAAM,OAAO,KAAK,QAAQ,IAAI;GAC9B,IAAI,SAAS,MACX,OAAO;GACT,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;GAC/B,KAAK,MAAM,OAAO,IAAI;GACtB,OAAO;EACT;;;;;;EAOA,MAAM,QAAQ,aAAqB,SAA+C;GAChF,MAAM,WAAW,QAAQ,aAAa,KAAA,KAAa,QAAQ,SAAS,SAAS,IAAI,QAAQ,WAAW;GACpG,MAAM,OAAoB;IACxB,QAAQ,CAAC,QAAQ;IACjB,WAAW;IACX,eAAe;IACf,OAAO,CAAC;IACR,SAAS,CAAC;IACV,SAAS,CAAC;IACV,iBAAiB;IACjB,UAAU;GACZ;GAEA,IAAI,CAAC,aAAa,WAAW,GAC3B,OAAO;IAAE,GAAG;IAAM,OAAO;GAAoE;GAE/F,MAAM,UAAU,KAAK,KAAK,KAAK,WAAW,GAAG,YAAY,KAAK,IAAI,GAAG;GAErE,IAAI;IAGF,IAAI;IACJ,IAAI;KACF,UAAU,MAAM,QAAQ,WAAW;IACrC,SACO,OAAO;KACZ,OAAO;MAAE,GAAG;MAAM,OAAO,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAAI;IACtH;IAEA,KAAK,YAAY,QAAQ,MAAK,UAAS,MAAM,SAAS;IACtD,IAAI,KAAK,aAAa,aAAa,MACjC,OAAO;KAAE,GAAG;KAAM,eAAe;KAAM,OAAO;IAAoC;IAEpF,IAAI,QAAQ,WAAW,GACrB,OAAO;KAAE,GAAG;KAAM,OAAO;IAAuB;IAClD,IAAI,QAAQ,SAAS,KACnB,OAAO;KAAE,GAAG;KAAM,OAAO;IAAmC;IAE9D,MAAM,UAAU,QAAQ,QAAO,UAAS,CAAC,mBAAmB,MAAM,IAAI,CAAC;IACvE,IAAI,QAAQ,SAAS,GACnB,OAAO;KAAE,GAAG;KAAM,OAAO,iDAAiD,QAAQ,MAAM,GAAG,CAAC,CAAC,CAAC,KAAI,UAAS,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE;IAAG;IAGvI,GAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;IACzC,MAAM,YAAY,MAAM,WAAW,aAAa,SAAS;KACvD,OAAO,QAAQ,KAAI,UAAS,MAAM,IAAI;KACtC,GAAI,aAAa,OAAO,CAAC,IAAI,EAAE,SAAS;IAC1C,CAAC;IACD,KAAK,MAAM,QAAQ,UAAU,SAC3B,KAAK,QAAQ,KAAK,GAAG,KAAK,0BAA0B;IAEtD,MAAM,eAAe,KAAK,KAAK,SAAS,QAAQ;IAChD,IAAI,CAAC,GAAG,WAAW,YAAY,GAC7B,OAAO;KAAE,GAAG;KAAM,OAAO;IAA8B;IACzD,MAAM,WAAW,KAAK,MAAM,GAAG,aAAa,cAAc,MAAM,CAAC;IAEjE,MAAM,UAAU,KAAK,QAAQ,WAAW;IACxC,MAAM,kBAAkB,KAAK,KAAK,SAAS,UAAU,qBAAqB;IAC1E,MAAM,mBAAmB,KAAK,KAAK,SAAS,WAAW,sBAAsB;IAC7E,MAAM,eAAe,KAAK,KAAK,SAAS,KAAK;IAE7C,MAAM,cAAc,QAAQ,YAAY,KAAA,IAAY,OAAO,IAAI,IAAI,QAAQ,OAAO;IAClF,MAAM,0BAAU,IAAI,IAAwB;IAE5C,MAAM,WAAW,MAAoC,UAAqC;KACxF,IAAI,UAAU,MAAM;MAClB,KAAK,MAAM,KAAK;OAAE,GAAG;OAAM,UAAU;MAAM,CAAC;MAC5C,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,KAAK,SAAS,OAAO,KAAK,KAAK,KAAK,KAAK,IAAI;MAC/E;KACF;KACA,MAAM,WAAW,gBAAgB,QAAQ,YAAY,IAAI,KAAK,EAAE;KAChE,KAAK,MAAM,KAAK;MAAE,GAAG;MAAM;KAAS,CAAC;KACrC,IAAI,UACF,QAAQ,IAAI,KAAK,IAAI,KAAK;UAG1B,KAAK,QAAQ,KAAK,GAAG,KAAK,MAAM,gBAAgB;IAEpD;IAEA,MAAM,iBAAiB,GAAG,WAAW,eAAe,IAAI,GAAG,aAAa,iBAAiB,MAAM,IAAI;IAGnG,MAAM,iBAAiB,eAAe,cAAc,IAAI,iBAAiB;IACzE,IAAI,mBAAmB,QAAQ,mBAAmB,MAChD,KAAK,QAAQ,KAAK,gEAAiE;IACrF,IAAI,mBAAmB,MACrB,QAAQ;KAAE,IAAI;KAAU,OAAO;KAA8B,MAAM;KAAU,YAAY;KAAM,UAAU;KAAO,MAAM;IAAK,SAAS;KAClI,gBAAgB,QAAQ,YAAY,cAAc;IACpD,CAAC;IAEH,IAAI,GAAG,WAAW,gBAAgB,GAAG;KACnC,MAAM,WAAW,GAAG,aAAa,kBAAkB,MAAM;KACzD,QAAQ;MAAE,IAAI;MAAW,OAAO;MAAgC,MAAM;MAAW,YAAY;MAAM,UAAU;MAAO,MAAM;KAAK,SAAS;MACtI,gBAAgB,QAAQ,aAAa,UAAU,EAAE,MAAM,IAAM,CAAC;KAChE,CAAC;IACH;IACA,IAAI,GAAG,WAAW,YAAY,GAAG;KAC/B,MAAM,QAAQ,GAAG,YAAY,YAAY,CAAC,CAAC,QAAO,SAAQ,GAAG,SAAS,KAAK,KAAK,cAAc,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;KAC7G,QAAQ;MAAE,IAAI;MAAO,OAAO;MAAQ,MAAM;MAAO,YAAY;MAAM,UAAU;MAAO,MAAM;KAAK,SAAS;MACtG,GAAG,UAAU,QAAQ,QAAQ,EAAE,WAAW,KAAK,CAAC;MAChD,KAAK,MAAM,QAAQ,OAAO;OACxB,MAAM,OAAO,KAAK,KAAK,cAAc,IAAI;OACzC,MAAM,OAAO,KAAK,SAAS,UAAU,IAAI,EAAE,MAAM,IAAM,IAAI,CAAC;OAC5D,gBAAgB,KAAK,KAAK,QAAQ,QAAQ,IAAI,GAAG,GAAG,aAAa,MAAM,MAAM,GAAG,IAAI;MACtF;KACF,CAAC;IACH;IAMA,MAAM,cAAc,eAAe,cAAc;IACjD,MAAM,aAA+B,CAEnC,GAAI,QAAQ,IAAI,QAAQ,IAAI,cAAc,CAAC,GAC3C,GAAG,KAAK,MAAM,QAAO,UAAS,MAAM,QAAQ,CAAC,CAAC,KAAI,WAAU;KAAE,MAAM,MAAM;KAAM,QAAQ,MAAM;IAAO,EAAE,CACzG;IAEA,KAAK,MAAM,SAAS,SAAS,QAAQ,CAAC,GAAG;KACvC,MAAM,SAAS,WAAW,MAAK,cAAa,MAAM,WAAW,KAAA,KAAa,UAAU,WAAW,MAAM,MAAM,KACtG,WAAW,MAAK,cAAa,UAAU,SAAS,MAAM,IAAI;KAC/D,MAAM,OAAO,KAAK,KAAK,SAAS,QAAQ,SAAS,MAAM,IAAI,KAAK,YAAY,MAAM,IAAI,CAAC;KACvF,MAAM,SAAS;MACb,IAAI,QAAQ,MAAM;MAClB,OAAO,QAAQ,QAAQ,MAAM;MAC7B,MAAM;MACN,YAAY;MACZ,UAAU;MACV,MAAM;KACR;KAEA,IAAI,WAAW,KAAA,GAAW;MACxB,MAAM,cAAc,YAAY,MAAK,cAAa,UAAU,WAAW,MAAM,MAAM;MACnF,QAAQ;OACN,GAAG;OACH,MAAM,eAAe,CAAC,QAAQ,IAAI,QAAQ,IACtC,iEACA;MACN,GAAG,IAAI;MACP;KACF;KACA,IAAI,CAAC,GAAG,WAAW,IAAI,GAAG;MACxB,QAAQ;OAAE,GAAG;OAAQ,MAAM;MAA2B,GAAG,IAAI;MAC7D;KACF;KAEA,QACE;MAAE,GAAG;MAAQ,YAAY;MAAM,MAAM,OAAO,SAAS,MAAM,OAAO,OAAO,iBAAiB,MAAM;KAAO,SACjG,GAAG,OAAO,MAAM,OAAO,MAAM;MAAE,WAAW;MAAM,OAAO;KAAK,CAAC,CACrE;IACF;IAIA,IAAI,mBAAmB,QAAQ,QAAQ,IAAI,QAAQ,GAAG;KACpD,MAAM,UAAU,GAAG,WAAW,QAAQ,UAAU,IAAI,GAAG,aAAa,QAAQ,YAAY,MAAM,IAAI;KAClG,KAAK,kBAAkB,KAAK,UAAU,aAAa,cAAc,CAAC,MAAM,KAAK,UAAU,aAAa,OAAO,CAAC;IAC9G;IAEA,IAAI,CAAC,QAAQ,SAAS;KAGpB,KAAK,UAAU,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,KAAI,OAAM,KAAK,MAAM,MAAK,SAAQ,KAAK,OAAO,EAAE,CAAC,CAAE,KAAK;KAC3F,OAAO;IACT;IAEA,KAAK,MAAM,CAAC,IAAI,UAAU,SAAS;KACjC,MAAM;KACN,KAAK,QAAQ,KAAK,KAAK,MAAM,MAAK,SAAQ,KAAK,OAAO,EAAE,CAAC,CAAE,KAAK;IAClE;IAEA,IAAI,QAAQ,IAAI,QAAQ,KAAK,KAAK,QAAQ,qBAAqB,KAAA,GAAW;KACxE,KAAK,WAAW;KAGhB,KAAK,QAAQ,iBAAiB;IAChC;IAEA,OAAO;GACT,SACO,OAAO;IAGZ,IAAI,kBAAkB,KAAK,GACzB,OAAO;KAAE,GAAG;KAAM,WAAW;KAAM,eAAe;KAAM,OAAO;IAAwB;IAGzF,MAAM,OAAO,KAAK,QAAQ,SAAS,IAAI,uBAAuB,KAAK,QAAQ,KAAK,IAAI,MAAM;IAC1F,OAAO;KAAE,GAAG;KAAM,OAAO,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,IAAI;IAAO;GAC9F,UACQ;IACN,GAAG,OAAO,SAAS;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GACrD;EACF;;EAGA,OAAqD;GACnD,MAAM,MAAM,KAAK,WAAW;GAC5B,IAAI,QAAkB,CAAC;GACvB,IAAI;IACF,QAAQ,GAAG,YAAY,GAAG;GAC5B,QACM;IACJ,OAAO,CAAC;GACV;GAEA,OAAO,MACJ,QAAO,SAAQ,KAAK,SAAS,MAAM,CAAC,CAAC,CACrC,SAAS,SAAS;IACjB,IAAI;KACF,MAAM,QAAQ,GAAG,SAAS,KAAK,KAAK,KAAK,IAAI,CAAC;KAC9C,OAAO,CAAC;MAAE;MAAM,WAAW,MAAM;MAAM,WAAW,KAAK,MAAM,MAAM,OAAO;KAAE,CAAC;IAC/E,QACM;KACJ,OAAO,CAAC;IACV;GACF,CAAC,CAAC,CACD,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;EAC7C;;EAGA,kBAAyC;GACvC,KAAK,eAAe,KAAK,QAAQ,CAAC,CAAC,cAAc;IAC/C,KAAK,aAAa;GACpB,CAAC;GACD,OAAO,KAAK;EACd;EAEA,MAAc,UAAyB;GACrC,MAAM,MAAM,KAAK,WAAW;GAC5B,MAAM,SAAS,KAAK,KAAK;GAEzB,KAAK,MAAM,QAAQ,QAAQ;IACzB,MAAM,MAAM,SAAS,IAAI;IACzB,IAAI,KAAK,MAAM,IAAI,KAAK,IAAI,CAAC,EAAE,QAAQ,KACrC;IACF,IAAI;KACF,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,IAAI,CAAC;KACvD,KAAK,MAAM,IAAI,KAAK,MAAM;MAAE;MAAK,WAAW,QAAQ,MAAK,UAAS,MAAM,SAAS;KAAE,CAAC;IACtF,QACM;KAEJ,KAAK,MAAM,IAAI,KAAK,MAAM;MAAE;MAAK,WAAW;KAAM,CAAC;IACrD;GACF;GAEA,MAAM,UAAU,IAAI,IAAI,OAAO,KAAI,SAAQ,KAAK,IAAI,CAAC;GACrD,KAAK,MAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,KAAK,CAAC,GACtC,IAAI,CAAC,QAAQ,IAAI,IAAI,GACnB,KAAK,MAAM,OAAO,IAAI;EAE5B;EAEA,SAAiB,SAAiB,UAAkB,QAAsB;GACxE,IAAI,CAAC,GAAG,WAAW,MAAM,GACvB;GACF,MAAM,SAAS,KAAK,KAAK,SAAS,QAAQ;GAC1C,GAAG,UAAU,KAAK,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;GAGtD,MAAM,aAAa,KAAK,QAAQ,KAAK,WAAW,CAAC;GACjD,GAAG,OAAO,QAAQ,QAAQ;IACxB,WAAW;IACX,OAAO;IACP,SAAQ,SAAQ,CAAC,SAAS,YAAY,KAAK,QAAQ,IAAI,CAAC;GAC1D,CAAC;EACH;EAEA,QAAsB;GACpB,MAAM,EAAE,SAAS,KAAK,QAAQ,UAAU;GACxC,KAAK,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI;EACnE;EAEA,aAA6B;GAC3B,MAAM,aAAa,KAAK,QAAQ,UAAU,CAAC,CAAC;GAC5C,OAAO,KAAK,WAAW,UAAU,IAAI,aAAa,KAAK,QAAQ,KAAK,QAAQ,UAAU,UAAU;EAClG;CACF;;;;;;CCroBsC,UAAA;CACX,UAAA;CA8Bd,gBAAb,MAA2B;EAKN;EAJnB;EACA,SAAgC;EAEhC,YACE,SACA,SACA;GAFiB,KAAA,UAAA;GAGjB,KAAK,WAAW;IACd,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,UAAU,SAAS,QAAQ,IAAI;IAC/B,KAAK,GAAG,QAAQ,MAAM,UAAU,OAAO,KAAK,YAAY,QAAQ,IAAI,EAAE,GAAG,QAAQ;IACjF,UAAU,QAAQ,MAAM,UAAU;GACpC;EACF;EAEA,IAAI,WAAmB;GACrB,OAAO,KAAK,SAAS;EACvB;EAEA,IAAI,WAAmB;GACrB,OAAO,KAAK,SAAS;EACvB;EAEA,MAAM,QAAuB;GAC3B,MAAM,KAAK,gBAAgB,KAAK,SAAS,MAAM,KAAK,SAAS,IAAI;EACnE;;EAGA,MAAM,UAAiC;GACrC,MAAM,EAAE,MAAM,SAAS,KAAK;GAC5B,MAAM,KAAK,MAAM;GACjB,IAAI;IACF,MAAM,KAAK,gBAAgB,MAAM,IAAI;IACrC,OAAO,EAAE,IAAI,KAAK;GACpB,SACO,OAAO;IACZ,OAAO;KAAE,IAAI;KAAO,OAAO,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAI;GACzG;EACF;;;;;;EAOA,MAAM,OAAO,MAA2D;GACtE,IAAI,KAAK,SAAS,KAAK,SAAS,QAAQ,KAAK,SAAS,KAAK,SAAS,MAClE,OAAO,EAAE,IAAI,KAAK;GAEpB,MAAM,WAAW;IAAE,MAAM,KAAK,SAAS;IAAM,MAAM,KAAK,SAAS;GAAK;GACtE,IAAI,KAAK,SAAS,SAAS,QAAQ,CAAE,MAAM,WAAW,KAAK,IAAI,GAC7D,OAAO;IAAE,IAAI;IAAO,OAAO,QAAQ,KAAK,KAAK;GAAoB;GAGnE,MAAM,KAAK,MAAM;GACjB,IAAI;IACF,MAAM,KAAK,gBAAgB,KAAK,MAAM,KAAK,IAAI;IAC/C,OAAO,EAAE,IAAI,KAAK;GACpB,SACO,OAAO;IACZ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACrE,IAAI;KACF,MAAM,KAAK,gBAAgB,SAAS,MAAM,SAAS,IAAI;IACzD,QACM,CAEN;IACA,OAAO;KAAE,IAAI;KAAO,OAAO,kBAAkB;IAAU;GACzD;EACF;EAEA,MAAM,MAAM,QAAQ,MAAqB;GACvC,MAAM,SAAS,KAAK;GACpB,KAAK,SAAS;GACd,IAAI,CAAC,QACH;GACF,IAAI;IACF,MAAM,OAAO,MAAM,KAAK;GAC1B,QACM,CAEN;EACF;;EAGA,MAAc,gBAAgB,MAAY,MAAc,WAAW,GAAkB;GACnF,IAAI;GACJ,KAAK,IAAI,UAAU,GAAG,WAAW,UAAU,WACzC,IAAI;IACF,MAAM,KAAK,OAAO,MAAM,IAAI;IAC5B;GACF,SACO,OAAO;IACZ,YAAY;IACZ,IAAI,UAAU,UACZ,MAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,GAAG,CAAC;GACzD;GAEF,MAAM;EACR;EAEA,MAAc,OAAO,MAAY,MAA6B;GAC5D,MAAM,MAAM,KAAK,QAAQ,IAAI;GAC7B,MAAM,SAAS,MAAM;IACnB,OAAO,KAAK,QAAQ;IACpB;IACA,UAAU,SAAS,IAAI;IACvB,YAAY,KAAK,QAAQ,WAAW;IACpC,GAAI,QAAQ,OAAO,CAAC,IAAI,EAAE,KAAK;KAAE,MAAM,IAAI;KAAM,KAAK,IAAI;IAAI,EAAE;GAClE,CAAC;GAGD,MAAM,aAAa,OAAO,MAAM;GAChC,MAAM,UAAU,IAAI,SAAgB,YAAY;IAC9C,YAAY,KAAK,UAAS,UAAS,QAAQ,KAAc,CAAC;GAC5D,CAAC;GAED,MAAM,UAAU,MAAM,QAAQ,KAAK,CACjC,OAAO,MAAM,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,UAAmB,KAAc,GACxE,OACF,CAAC;GACD,IAAI,YAAY,MACd,MAAM;GAER,KAAK,SAAS;GACd,KAAK,SAAS,OAAO;GACrB,KAAK,SAAS,OAAO;GACrB,KAAK,SAAS,WAAW,SAAS,IAAI;GACtC,KAAK,SAAS,WAAW,QAAQ,OAAO,SAAS;GACjD,KAAK,SAAS,MAAM,GAAG,KAAK,SAAS,SAAS,KAAK,YAAY,IAAI,EAAE,GAAG;EAC1E;CACF;;;;;;CCnKM,MAAM;CAGC,WAAb,MAAsB;EACpB,4BAA6B,IAAI,IAAgC;EAEjE,UAAU,UAAyB,UAAqC;GACtE,MAAM,MAAM,YAAY;GACxB,MAAM,SAAS,KAAK,UAAU,IAAI,GAAG,qBAAK,IAAI,IAAmB;GACjE,OAAO,IAAI,QAAQ;GACnB,KAAK,UAAU,IAAI,KAAK,MAAM;GAE9B,aAAa;IACX,OAAO,OAAO,QAAQ;IACtB,IAAI,OAAO,SAAS,GAClB,KAAK,UAAU,OAAO,GAAG;GAC7B;EACF;EAEA,QAAQ,SAA2B;GACjC,KAAK,SAAS,KAAK,OAAO;GAC1B,IAAI,QAAQ,UACV,KAAK,SAAS,QAAQ,UAAU,OAAO;EAC3C;EAEA,IAAI,kBAA0B;GAC5B,IAAI,QAAQ;GACZ,KAAK,MAAM,UAAU,KAAK,UAAU,OAAO,GAAG,SAAS,OAAO;GAC9D,OAAO;EACT;EAEA,SAAiB,KAAa,SAA2B;GACvD,MAAM,SAAS,KAAK,UAAU,IAAI,GAAG;GACrC,IAAI,CAAC,QACH;GACF,KAAK,MAAM,YAAY,CAAC,GAAG,MAAM,GAC/B,IAAI;IACF,SAAS,OAAO;GAClB,QACM;IACJ,OAAO,OAAO,QAAQ;GACxB;EAEJ;CACF;;;;;;CC9CgC,YAAA;CAE1B,aAAa;CACb,mBAAmB;CAaZ,eAAb,MAA0B;EAOK;EAN7B,SAAiC,CAAC;EAClC,YAA2C;EAC3C,SAAiB;;EAEjB,UAAkB;EAElB,YAAY,MAA+B;GAAd,KAAA,OAAA;EAAe;EAE5C,IAAI,WAAmB;GACrB,OAAO,KAAK;EACd;EAEA,OAAa;GACX,IAAI,KAAK,QACP;GACF,KAAK,SAAS;GACd,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,KAAK,MAAM,MAAM,CAAC;IAC5D,KAAK,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,OAAO,MAAM,IAAW,IAAI,CAAC;GACnF,QACM;IACJ,KAAK,SAAS,CAAC;GACjB;EACF;EAEA,OAAO,UAAkB,OAA8C,KAAK,KAAK,IAAI,GAAS;GAC5F,KAAK,KAAK;GACV,KAAK,OAAO,KAAK;IAAE;IAAU;IAAI,GAAG;GAAM,CAAC;GAC3C,KAAK,WAAW;GAChB,IAAI,KAAK,OAAO,SAAS,YACvB,KAAK,OAAO,OAAO,GAAG,KAAK,OAAO,SAAS,UAAU;GACvD,KAAK,aAAa;EACpB;EAEA,MAAsB;GACpB,KAAK,KAAK;GACV,OAAO,CAAC,GAAG,KAAK,MAAM;EACxB;;EAGA,UAAU,UAAkB,UAAkB,MAAM,KAAK,IAAI,GAAG,eAA8B,MAAqB;GACjH,KAAK,KAAK;GACV,MAAM,QAAQ,MAAM;GACpB,MAAM,OAAO,KAAK,OAAO,QAAO,UAAS,MAAM,aAAa,QAAQ;GACpE,MAAM,SAAS,KAAK,QAAO,UAAS,MAAM,MAAM,KAAK;GAErD,IAAI,OAAO;GACX,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,cAAc,KAAA,GACtB,QAAQ,KAAK,IAAI,MAAM,WAAW,QAAQ;GAE9C,IAAI,iBAAiB,MACnB,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,IAAI,cAAc,KAAK,CAAC;GAEzD,MAAM,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAK,UAAS,MAAM,SAAS,OAAO;GAC1E,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAK,UAAS,MAAM,SAAS,UAAU,MAAM,SAAS,OAAO;GAElG,OAAO;IACL;IACA,aAAa,KAAK,WAAW,IAAI,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,QAAQ,CAAC;IAChF,UAAU,OAAO,QAAO,UAAS,MAAM,SAAS,OAAO,CAAC,CAAC;IACzD,SAAS,OAAO,QAAO,UAAS,MAAM,SAAS,OAAO,CAAC,CAAC;IACxD,gBAAgB,OAAO,QAAO,UAAS,MAAM,SAAS,gBAAgB,CAAC,CAAC;IACxE,aAAa,WAAW,MAAM;IAC9B,YAAY,UAAU,MAAM;IAC5B,eAAe,UAAU,aAAa;IACtC,QAAQ,KAAK,MAAM,EAAe;GACpC;EACF;EAEA,UAAgB;GACd,IAAI,KAAK,cAAc,MACrB,aAAa,KAAK,SAAS;GAC7B,KAAK,YAAY;GACjB,KAAK,KAAK;EACZ;EAEA,eAA6B;GAC3B,IAAI,KAAK,cAAc,MACrB;GACF,KAAK,YAAY,iBAAiB;IAChC,KAAK,YAAY;IACjB,KAAK,KAAK;GACZ,GAAG,gBAAgB;GACnB,KAAK,UAAU,MAAM;EACvB;EAEA,OAAqB;GACnB,IAAI;IACF,gBAAgB,KAAK,MAAM,GAAG,KAAK,UAAU;KAAE,SAAS;KAAG,QAAQ,KAAK;IAAO,CAAC,EAAE,GAAG;GACvF,QACM,CAEN;EACF;CACF;;;;;ACvGA,eAAe,kBAAmC;CAChD,IAAI,QAAQ,aAAa,SACvB,OAAO;CAET,IAAI,QAAQ,aAAa,UACvB,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,cAAc,UAAU,CAAC,MAAM,cAAc,GAAG,EAAE,SAAS,IAAK,CAAC;EAC1F,MAAM,QAAQ,wBAAwB,KAAK,MAAM,CAAC,GAAG;EACrD,MAAM,OAAO,uBAAuB,KAAK,MAAM,CAAC,GAAG;EACnD,MAAM,UAAU,OAAO,WAAW,SAAS,GAAG;EAE9C,OAAO,UAAU,IADF,OAAO,WAAW,QAAQ,GACnB,IAAS,UAAW,MAAM;CAClD,QACM;EACJ,OAAO;CACT;CAGF,IAAI,QAAQ,aAAa,SACvB,IAAI;EAEF,MAAM,EAAE,WAAW,MAAM,cAAc,kBAAkB;GAAC;GAAc;GAAmB;GAAY;EAAM,GAAG,EAAE,SAAS,IAAK,CAAC;EAEjI,MAAM,SADO,OAAO,MAAM,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAK,UAAS,MAAM,KAAK,CAAC,CAAC,SAAS,CACjE,KAAQ,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,KAAI,UAAS,MAAM,QAAQ,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;EACjF,MAAM,UAAU,OAAO,WAAW,MAAM,MAAM,GAAG;EACjD,MAAM,SAAS,OAAO,WAAW,MAAM,MAAM,GAAG;EAChD,OAAO,UAAU,IAAK,SAAS,UAAW,MAAM;CAClD,QACM;EACJ,OAAO;CACT;CAGF,OAAO;AACT;;AAGA,SAAgB,aAAqE;CACnF,IAAI;EACF,MAAM,OAAO,GAAG,aAAa,iBAAiB,MAAM;EACpD,MAAM,QAAQ,QAAwB,OAAO,SAAS,IAAI,OAAO,IAAI,IAAI,cAAc,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,GAAG,MAAM,KAAK,EAAE;EACtH,MAAM,QAAQ,KAAK,UAAU;EAC7B,MAAM,YAAY,KAAK,cAAc;EACrC,MAAM,YAAY,KAAK,WAAW;EAClC,MAAM,WAAW,KAAK,UAAU;EAEhC,OAAO;GACL,mBAAmB,QAAQ,KAAM,QAAQ,aAAa,QAAS,MAAM;GACrE,iBAAiB,YAAY,KAAM,YAAY,YAAY,YAAa,MAAM;EAChF;CACF,QACM;EACJ,MAAM,QAAQ,GAAG,SAAS;EAC1B,MAAM,OAAO,GAAG,QAAQ;EACxB,OAAO;GAAE,mBAAmB,QAAQ,KAAM,QAAQ,QAAQ,QAAS,MAAM;GAAG,iBAAiB;EAAE;CACjG;AACF;;;;;;AAOA,SAAgB,iBAAgC;CAC9C,MAAM,WAAqB,CAAC;CAE5B,MAAM,WAAW,SAAuB;EACtC,IAAI;GACF,MAAM,MAAM,OAAO,SAAS,GAAG,aAAa,MAAM,MAAM,CAAC,CAAC,KAAK,GAAG,EAAE;GACpE,IAAI,CAAC,OAAO,SAAS,GAAG,GACtB;GACF,MAAM,UAAU,MAAM;GACtB,IAAI,UAAU,KAAK,UAAU,KAC3B,SAAS,KAAK,OAAO;EACzB,QACM,CAEN;CACF;CAEA,IAAI;EACF,KAAK,MAAM,QAAQ,GAAG,YAAY,oBAAoB,GACpD,IAAI,KAAK,WAAW,cAAc,GAChC,QAAQ,KAAK,KAAK,sBAAsB,MAAM,MAAM,CAAC;CAE3D,QACM,CAEN;CAEA,IAAI;EACF,KAAK,MAAM,SAAS,GAAG,YAAY,kBAAkB,GAAG;GACtD,MAAM,MAAM,KAAK,KAAK,oBAAoB,KAAK;GAC/C,KAAK,MAAM,SAAS,GAAG,YAAY,GAAG,GACpC,IAAI,kBAAkB,KAAK,KAAK,GAC9B,QAAQ,KAAK,KAAK,KAAK,KAAK,CAAC;EAEnC;CACF,QACM,CAEN;CAEA,OAAO,SAAS,SAAS,IAAI,KAAK,IAAI,GAAG,QAAQ,IAAI;AACvD;AAEA,eAAe,UAAU,QAA2D;CAClF,IAAI;EACF,MAAM,QAAQ,MAAM,GAAG,SAAS,OAAO,MAAM;EAC7C,MAAM,aAAa,MAAM,SAAS,MAAM;EACxC,MAAM,YAAY,MAAM,SAAS,MAAM;EACvC,OAAO;GACL,MAAM;GACN;GACA;GACA,aAAa,aAAa,KAAM,aAAa,aAAa,aAAc,MAAM;EAChF;CACF,QACM;EACJ,OAAO;CACT;AACF;;;;;;AAOA,eAAsB,WAAW,QAAoB,aAA4D;CAC/G,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,UAAU;CACjC,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,SAAS,WAAW;CAE1B,IAAI,QAAQ,aAAa,SACvB,QAAQ,KAAK,CAAC;CAChB,IAAI,OAAO,oBAAoB,KAAK,QAAQ,aAAa,SACvD,OAAO,kBAAkB,MAAM,gBAAgB;CAEjD,MAAM,cAAc,eAAe;CAEnC,MAAM,SAAS,MAAM,QAAQ,IAAI,OAAO,UAAU,KAAI,UAAS,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,EAAA,CAAG,QAC7F,SAA4C,SAAS,IACxD;CAEA,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,QAAQ,OACjB,IAAI,OAAO,kBAAkB,KAAK,KAAK,eAAe,OAAO,iBAC3D,OAAO,KAAK,QAAQ,KAAK,KAAK,MAAM,KAAK,YAAY,QAAQ,CAAC,EAAE,OAAO;CAG3E,IAAI,OAAO,oBAAoB,KAAK,OAAO,qBAAqB,OAAO,mBACrE,OAAO,KAAK,aAAa,OAAO,kBAAkB,QAAQ,CAAC,EAAE,OAAO;CAEtE,IAAI,OAAO,kBAAkB,KAAK,OAAO,mBAAmB,OAAO,iBACjE,OAAO,KAAK,WAAW,OAAO,gBAAgB,QAAQ,CAAC,EAAE,OAAO;CAElE,MAAM,aAAa,OAAO,QAAQ,MAAM,CAAC,IAAI;CAC7C,IAAI,OAAO,aAAa,KAAK,cAAc,OAAO,YAChD,OAAO,KAAK,QAAQ,WAAW,QAAQ,CAAC,EAAE,eAAe,OAAO,YAAY;CAE9E,IAAI,gBAAgB,QAAQ,OAAO,cAAc,KAAK,eAAe,OAAO,aAC1E,OAAO,KAAK,sBAAsB,YAAY,QAAQ,CAAC,EAAE,GAAG;CAG9D,OAAO;EACL,SAAS,OAAO;EAChB;EACA,SAAS,CAAC,GAAG,OAAO;EACpB,UAAU,GAAG,OAAO,IAAI;EACxB,mBAAmB,OAAO;EAC1B,iBAAiB,OAAO;EACxB;EACA;EACA;EACA,WAAW,KAAK,IAAI;CACtB;AACF;AAEA,SAAgB,cAAc,QAA8B;CAC1D,OAAO;EACL,SAAS,OAAO;EAChB,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU;EAC1B,SAAS;GAAC;GAAG;GAAG;EAAC;EACjB,UAAU,GAAG,OAAO,IAAI;EACxB,mBAAmB;EACnB,iBAAiB;EACjB,aAAa;EACb,OAAO,CAAC;EACR,QAAQ,CAAC;EACT,WAAW;CACb;AACF;;;CAlMM,gBAAgB,UAAU,QAAQ;;;;;;CCNE,UAAA;CAM7B,cAAb,MAAyB;EAMJ;EACA;EACA;EAPnB;EACA,eAAuB;EACvB,WAAmB;EAEnB,YACE,WACA,aACA,eACA;GAHiB,KAAA,YAAA;GACA,KAAA,cAAA;GACA,KAAA,gBAAA;GAEjB,KAAK,UAAU,cAAc,UAAU,CAAC;EAC1C;EAEA,IAAI,OAAiB;GACnB,OAAO,KAAK;EACd;;EAGA,MAAM,KAAK,MAAM,KAAK,IAAI,GAAkB;GAC1C,MAAM,SAAS,KAAK,UAAU;GAC9B,IAAI,CAAC,OAAO,SAAS;IACnB,IAAI,KAAK,QAAQ,SACf,KAAK,UAAU;KAAE,GAAG,KAAK;KAAS,SAAS;IAAM;IACnD;GACF;GACA,IAAI,MAAM,KAAK,eAAe,OAAO,YACnC;GAEF,KAAK,eAAe;GACpB,KAAK,UAAU,MAAM,WAAW,QAAQ,KAAK,WAAW;GAExD,IAAI,KAAK,QAAQ,OAAO,SAAS,GAAG;IAClC,IAAI,CAAC,KAAK,UAAU;KAClB,KAAK,WAAW;KAChB,KAAK,cAAc,OAAO;MACxB,UAAU;MACV,OAAO;MACP,QAAQ;MACR,QAAQ,KAAK,QAAQ,OAAO,KAAK,IAAI;KACvC,CAAC;IACH;IACA;GACF;GAEA,IAAI,KAAK,UAAU;IACjB,KAAK,WAAW;IAChB,KAAK,cAAc,OAAO;KACxB,UAAU;KACV,OAAO;KACP,QAAQ;KACR,QAAQ;IACV,CAAC;GACH;EACF;CACF;;;;;;CClDM,oBAAoB;CACpB,oBAAoB;CACpB,mBAAmB;CAOZ,WAAb,MAAsB;EAMD;EACA;EANnB,0BAA2B,IAAI,IAAuB;EACtD,QAAuC;EACvC,SAAiB;EAEjB,YACE,KACA,WACA;GAFiB,KAAA,MAAA;GACA,KAAA,YAAA;EAChB;EAEH,IAAI,YAAoB;GACtB,OAAO,KAAK;EACd;EAEA,OAAO,UAAkB,MAAqB;GAC5C,IAAI,KAAK,UAAU,CAAC,KAAK,UAAU,CAAC,CAAC,SACnC;GAEF,MAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ,KAAK,CAAC;GAC9C,OAAO,KAAK,IAAI;GAChB,KAAK,QAAQ,IAAI,UAAU,MAAM;GAEjC,IAAI,OAAO,UAAU,mBAAmB;IACtC,KAAK,MAAM;IACX;GACF;GACA,KAAK,UAAU,iBAAiB;IAC9B,KAAK,QAAQ;IACb,KAAK,MAAM;GACb,GAAG,iBAAiB;GACpB,KAAK,MAAM,MAAM;EACnB;EAEA,QAAc;GACZ,IAAI,KAAK,QAAQ,SAAS,GACxB;GAEF,MAAM,UAAU,CAAC,GAAG,KAAK,QAAQ,QAAQ,CAAC;GAC1C,KAAK,QAAQ,MAAM;GAEnB,KAAK,MAAM,CAAC,UAAU,UAAU,SAC9B,IAAI;IACF,KAAK,MAAM,UAAU,KAAK;GAC5B,QACM,CAEN;EAEJ;EAEA,KAAK,UAAiF;GACpF,MAAM,SAAS,KAAK,UAAU;GAC9B,MAAM,QAAuB,CAAC;GAC9B,IAAI,YAAY;GAEhB,KAAK,MAAM,QAAQ,KAAK,cAAc,QAAQ,GAC5C,IAAI;IACF,MAAM,QAAQ,GAAG,SAAS,IAAI;IAC9B,MAAM,KAAK;KAAE,MAAM,KAAK,SAAS,IAAI;KAAG,WAAW,MAAM;IAAK,CAAC;IAC/D,IAAI,SAAS,KAAK,YAAY,QAAQ,GACpC,YAAY,MAAM;GACtB,QACM,CAEN;GAGF,OAAO;IAAE,SAAS,OAAO;IAAS;IAAW;GAAM;EACrD;;EAGA,SAAS,UAAkB,MAAyB;GAClD,MAAM,UAAU,CAAC,KAAK,YAAY,QAAQ,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC;GAC1E,MAAM,QAAmB,CAAC;GAE1B,KAAK,MAAM,QAAQ,SAAS;IAC1B,IAAI,MAAM,UAAU,MAClB;IACF,MAAM,QAAQ,KAAK,cAAc,MAAM,OAAO,MAAM,MAAM;IAC1D,MAAM,QAAQ,GAAG,KAAK;GACxB;GAEA,OAAO,MAAM,MAAM,CAAC,IAAI;EAC1B;EAEA,MAAM,UAAwB;GAC5B,KAAK,MAAM,QAAQ,KAAK,cAAc,QAAQ,GAC5C,IAAI;IACF,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;GACjC,QACM,CAEN;EAEJ;EAEA,UAAgB;GACd,KAAK,SAAS;GACd,IAAI,KAAK,UAAU,MACjB,aAAa,KAAK,KAAK;GACzB,KAAK,QAAQ;GACb,KAAK,MAAM;EACb;EAEA,MAAc,UAAkB,OAAwB;GACtD,MAAM,EAAE,aAAa,KAAK,UAAU;GACpC,MAAM,OAAO,KAAK,YAAY,QAAQ;GACtC,GAAG,UAAU,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;GAI1C,IAAI,UAAoB,CAAC;GACzB,IAAI,QAAQ;GAEZ,MAAM,eAAqB;IACzB,IAAI,QAAQ,WAAW,GACrB;IACF,MAAM,UAAU,GAAG,QAAQ,KAAK,IAAI,EAAE;IAEtC,KADoB,GAAG,WAAW,IAAI,IAAI,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,KACjD,SAAO,WAAW,OAAO,IAAI,UAC7C,KAAK,OAAO,QAAQ;IACtB,GAAG,eAAe,KAAK,YAAY,QAAQ,GAAG,OAAO;IACrD,UAAU,CAAC;IACX,QAAQ;GACV;GAEA,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,OAAO,KAAK,UAAU,IAAI;IAChC,MAAM,OAAO,SAAO,WAAW,IAAI,IAAI;IACvC,IAAI,QAAQ,KAAK,QAAQ,OAAO,UAC9B,OAAO;IACT,QAAQ,KAAK,IAAI;IACjB,SAAS;GACX;GAEA,OAAO;EACT;EAEA,OAAe,UAAwB;GACrC,MAAM,EAAE,SAAS,KAAK,UAAU;GAChC,KAAK,IAAI,QAAQ,OAAO,GAAG,SAAS,GAAG,SAAS;IAC9C,MAAM,OAAO,KAAK,YAAY,UAAU,KAAK;IAC7C,IAAI,CAAC,GAAG,WAAW,IAAI,GACrB;IACF,GAAG,WAAW,MAAM,KAAK,YAAY,UAAU,QAAQ,CAAC,CAAC;GAC3D;GACA,IAAI,GAAG,WAAW,KAAK,YAAY,QAAQ,CAAC,GAC1C,GAAG,WAAW,KAAK,YAAY,QAAQ,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC;EAE3E;EAEA,cAAsB,MAAc,MAAyB;GAC3D,IAAI;GACJ,IAAI;IACF,SAAS,GAAG,SAAS,MAAM,GAAG;GAChC,QACM;IACJ,OAAO,CAAC;GACV;GAEA,IAAI;IACF,MAAM,OAAO,GAAG,UAAU,MAAM,CAAC,CAAC;IAClC,MAAM,SAAS,KAAK,IAAI,MAAM,gBAAgB;IAC9C,MAAM,SAAS,SAAO,MAAM,MAAM;IAClC,GAAG,SAAS,QAAQ,QAAQ,GAAG,QAAQ,OAAO,MAAM;IAIpD,MAAM,MAFO,OAAO,SAAS,MAEjB,CAAA,CAAK,MAAM,IAAI,CAAC,CAAC,QAAO,UAAS,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,SAAoB,CAAC;IAC3B,KAAK,MAAM,SAAS,IAAI,MAAM,OAAO,SAAS,IAAI,CAAC,GACjD,IAAI;KACF,OAAO,KAAK,KAAK,MAAM,KAAK,CAAY;IAC1C,QACM,CAEN;IAEF,OAAO,OAAO,MAAM,CAAC,IAAI;GAC3B,UACQ;IACN,GAAG,UAAU,MAAM;GACrB;EACF;EAEA,YAAoB,UAA0B;GAC5C,OAAO,KAAK,KAAK,KAAK,KAAK,GAAG,SAAS,KAAK;EAC9C;EAEA,YAAoB,UAAkB,OAAuB;GAC3D,OAAO,KAAK,KAAK,KAAK,KAAK,GAAG,SAAS,OAAO,OAAO;EACvD;EAEA,cAAsB,UAA4B;GAChD,MAAM,EAAE,SAAS,KAAK,UAAU;GAChC,MAAM,UAAU,CAAC,KAAK,YAAY,QAAQ,CAAC;GAC3C,KAAK,IAAI,QAAQ,GAAG,SAAS,MAAM,SAAS,QAAQ,KAAK,KAAK,YAAY,UAAU,KAAK,CAAC;GAC1F,OAAO;EACT;CACF;;;;;;CC1NuB,YAAA;CAC4E,cAAA;CAW7F,eAAmD;EACvD,SAAS;EACT,aAAa;EACb,kBAAkB;EAClB,aAAa;EACb,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CAEM,QAA4C;EAChD,SAAS;EACT,aAAa;EACb,kBAAkB;EAClB,aAAa;EACb,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CASa,sBAAb,MAAiC;EAMZ;EACA;EACA;EAPnB,4BAA6B,IAAI,IAAoB;EACrD,aAAoC;EACpC,eAAsC;EAEtC,YACE,SACA,WACA,eACA;GAHiB,KAAA,UAAA;GACA,KAAA,YAAA;GACA,KAAA,gBAAA;EAChB;EAEH,IAAI,mBAA4B;GAC9B,OAAO,KAAK,QAAQ;EACtB;EAEA,SAAyB;GACvB,MAAM,WAAW,KAAK,UAAU,CAAC,CAAC;GAClC,OAAO;IACL,SAAS,SAAS;IAClB,UAAU,KAAK,QAAQ;IACvB,QAAQ,SAAS;IACjB,SAAS,SAAS;IAClB,aAAa,SAAS;IACtB,iBAAiB,SAAS;IAC1B,aAAa,SAAS;IACtB,QAAQ,SAAS;IACjB,YAAY,SAAS;IACrB,YAAY,KAAK;IACjB,cAAc,KAAK;GACrB;EACF;;EAGA,aAAa,OAA0B,MAAM,KAAK,IAAI,GAAY;GAChE,MAAM,WAAW,KAAK,UAAU,CAAC,CAAC;GAClC,IAAI,CAAC,SAAS,SACZ,OAAO;GAWT,IAAI,CATkB;IACpB,SAAS,SAAS;IAClB,aAAa,SAAS;IACtB,kBAAkB,SAAS;IAC3B,aAAa,SAAS;IACtB,OAAO,SAAS;IAChB,QAAQ,SAAS;IACjB,kBAAkB,SAAS;GAC7B,EAAE,MAAM,SAEN,OAAO;GAET,MAAM,QAAQ,KAAK,UAAU,IAAI,GAAG,MAAM,SAAS,GAAG,MAAM,QAAQ,KAAK;GACzE,OAAO,EAAE,SAAS,aAAa,KAAK,QAAQ;EAC9C;;EAGA,SAAS,OAA0B,MAAM,KAAK,IAAI,GAAS;GACzD,KAAK,UAAU,IAAI,GAAG,MAAM,SAAS,GAAG,MAAM,UAAU,MAAM,KAAK,UAAU,CAAC,CAAC,SAAS,UAAU;EACpG;;EAGA,OAAO,OAAgC;GACrC,KAAU,SAAS,KAAK,CAAC,CAAC,OAAO,UAAmB;IAClD,OAAO,KAAK,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;GAC9F,CAAC;EACH;EAEA,MAAM,SAAS,OAA4C;GAEzD,IAAI,CAAC,KAAK,aAAa,KAAK,GAC1B,OAAO;GACT,KAAK,SAAS,KAAK;GAEnB,OAAO,KAAK,aACV,sBAAsB,MAAM,MAAM,SAAS,CACzC,GAAG,MAAM,MAAM,IAAI,MAAM,SAAS,IAAI,aAAa,MAAM,WACzD,MAAM,MACR,CAAC,CACH;EACF;;EAGA,MAAM,SAAS,YAAoD,CAAC,GAA6C;GAC/G,MAAM,SAAS,UAAU,UAAU,KAAK,UAAU,CAAC,CAAC,SAAS;GAC7D,IAAI,OAAO,WAAW,GACpB,OAAO;IAAE,IAAI;IAAO,OAAO;GAAwB;GAErD,MAAM,QAAQ,KAAK,aAAa,UAAU,QAAQ;GAClD,IAAI,UAAU,MACZ,OAAO;IAAE,IAAI;IAAO,OAAO;GAA0B;GAEvD,MAAM,SAAS,MAAM,oBACnB,OACA,QACA,sBAAsB,wBAAwB,CAAC,sCAAsC,CAAC,CACxF;GACA,KAAK,SAAS,OAAO,KAAK,sBAAsB,OAAO,SAAS,aAAa;GAC7E,OAAO;EACT;EAEA,MAAM,YAAY,YAAmC,CAAC,GAAmG;GACvJ,MAAM,QAAQ,KAAK,aAAa,UAAU,QAAQ;GAClD,IAAI,UAAU,MACZ,OAAO;IAAE,IAAI;IAAO,OAAO,CAAC;IAAG,OAAO;GAA0B;GAElE,MAAM,SAAS,MAAM,kBAAkB,KAAK;GAC5C,KAAK,SAAS,OAAO,KAAK,GAAG,OAAO,MAAM,OAAO,kBAAkB,OAAO,SAAS,eAAe;GAClG,OAAO;EACT;;EAGA,MAAM,YAAY,OAA4E;GAC5F,OAAO,oBAAoB,KAAK;EAClC;EAEA,aAAqB,eAAuC;GAC1D,MAAM,QAAQ,eAAe,KAAK,KAAK,KAAK,QAAQ,iBAAiB;GACrE,OAAO,MAAM,SAAS,IAAI,QAAQ;EACpC;EAEA,MAAc,aAAa,MAAgC;GACzD,MAAM,WAAW,KAAK,UAAU,CAAC,CAAC;GAClC,IAAI,SAAS,OAAO,WAAW,GAAG;IAChC,KAAK,SAAS,uBAAuB;IACrC,OAAO;GACT;GAEA,MAAM,QAAQ,KAAK,aAAa;GAChC,IAAI,UAAU,MAAM;IAClB,KAAK,SAAS,yBAAyB;IACvC,OAAO;GACT;GAEA,MAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI;GACrE,KAAK,SAAS,OAAO,KAAK,SAAS,OAAO,SAAS,aAAa;GAChE,OAAO,OAAO;EAChB;EAEA,SAAiB,SAAuB;GACtC,KAAK,aAAa;GAClB,KAAK,eAAe,KAAK,IAAI;EAC/B;CACF;;;;;AC5CA,SAAgB,aAAa,aAAqB,YAAqD;CACrG,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,gBAAgB,WAAW;CACxC,SACO,OAAO;EACZ,OAAO;GAAE,IAAI;GAAO,OAAO,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAAI;CACzH;CAEA,IAAI;EACF,MAAM,MAAM,iBAAiB,UAAU;EACvC,MAAM,UAAU,gBAAgB,GAAG,CAAC,CAAC,OAAO;GAAE,MAAM;GAAQ,QAAQ;EAAM,CAAC;EAC3E,MAAM,WAAW,KAAK,UAAU,OAAO;GAAE,MAAM;GAAQ,QAAQ;EAAM,CAAC;EACtE,IAAI,CAAC,SAAO,KAAK,OAAO,CAAC,CAAC,OAAO,SAAO,KAAK,QAAQ,CAAC,GACpD,OAAO;GAAE,IAAI;GAAO,OAAO;EAAiD;CAEhF,SACO,OAAO;EACZ,OAAO;GAAE,IAAI;GAAO,OAAO,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAAI;CACzH;CAEA,IAAI,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,GAC9C,OAAO;EAAE,IAAI;EAAO,OAAO,8BAA8B,KAAK;CAAU;CAG1E,OAAO,EAAE,IAAI,KAAK;AACpB;;;CA/JgC,YAAA;CAQnB,WAAb,MAAsB;EAIS;EAH7B,SAAuD;EACvD,cAAsB;EAEtB,YAAY,KAA8B;GAAb,KAAA,MAAA;EAAc;EAE3C,IAAI,YAAoB;GACtB,OAAO,KAAK;EACd;EAEA,IAAI,WAAmB;GACrB,OAAO,KAAK,KAAK,KAAK,KAAK,iBAAiB;EAC9C;EAEA,IAAI,UAAkB;GACpB,OAAO,KAAK,KAAK,KAAK,KAAK,iBAAiB;EAC9C;EAEA,IAAI,UAAmB;GACrB,OAAO,GAAG,WAAW,KAAK,QAAQ,KAAK,GAAG,WAAW,KAAK,OAAO;EACnE;;EAGA,OAA6C;GAC3C,IAAI,CAAC,KAAK,SAAS;IACjB,KAAK,SAAS;IACd,OAAO;GACT;GAEA,MAAM,MAAM,CAAC,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC,KAAK,SAAS;IACtD,IAAI;KACF,OAAO,GAAG,GAAG,SAAS,IAAI,CAAC,CAAC;IAC9B,QACM;KACJ,OAAO;IACT;GACF,CAAC,CAAC,CAAC,KAAK,GAAG;GAEX,IAAI,KAAK,WAAW,QAAQ,QAAQ,KAAK,aACvC,OAAO,KAAK;GAEd,IAAI;IACF,KAAK,SAAS;KACZ,MAAM,GAAG,aAAa,KAAK,UAAU,MAAM;KAC3C,KAAK,GAAG,aAAa,KAAK,SAAS,MAAM;IAC3C;IACA,KAAK,cAAc;IACnB,OAAO,KAAK;GACd,QACM;IACJ,KAAK,SAAS;IACd,OAAO;GACT;EACF;EAEA,KAAK,aAAqB,YAAqD;GAC7E,MAAM,aAAa,aAAa,aAAa,UAAU;GACvD,IAAI,CAAC,WAAW,IACd,OAAO;IAAE,IAAI;IAAO,OAAO,WAAW;GAAM;GAE9C,GAAG,UAAU,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;GAC1C,gBAAgB,KAAK,UAAU,GAAG,YAAY,QAAQ,EAAE,GAAG;GAC3D,gBAAgB,KAAK,SAAS,GAAG,WAAW,QAAQ,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;GAC1E,KAAK,SAAS;GACd,KAAK,cAAc;GACnB,OAAO,EAAE,IAAI,KAAK;EACpB;EAEA,QAAc;GACZ,KAAK,MAAM,QAAQ,CAAC,KAAK,UAAU,KAAK,OAAO,GAC7C,IAAI;IACF,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;GACjC,QACM,CAEN;GAEF,KAAK,SAAS;EAChB;EAEA,OAAO,SAA6B;GAClC,MAAM,OAAkB;IACtB;IACA,aAAa,KAAK;IAClB,SAAS;IACT,QAAQ;IACR,WAAW;IACX,SAAS;IACT,eAAe;IACf,aAAa;IACb,YAAY;IACZ,OAAO;GACT;GAEA,IAAI,CAAC,KAAK,SACR,OAAO,UAAU;IAAE,GAAG;IAAM,OAAO;GAAsD,IAAI;GAG/F,MAAM,OAAO,KAAK,KAAK;GACvB,IAAI,SAAS,MACX,OAAO;IAAE,GAAG;IAAM,OAAO;GAA2C;GAEtE,IAAI;IACF,MAAM,OAAO,IAAI,gBAAgB,KAAK,IAAI;IAC1C,MAAM,UAAU,IAAI,KAAK,KAAK,OAAO;IACrC,MAAM,gBAAgB,KAAK,OAAO,QAAQ,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAU;IAC9E,OAAO;KACL,GAAG;KACH,SAAS,KAAK,QAAQ,QAAQ,OAAO,IAAI;KACzC,QAAQ,KAAK,OAAO,QAAQ,OAAO,IAAI;KACvC,WAAW,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,YAAY;KAChD,SAAS,QAAQ,YAAY;KAC7B;KACA,aAAa,KAAK;KAClB,YAAY,aAAa,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC;KAC9C,OAAO,gBAAgB,IAAI,gCAAgC;IAC7D;GACF,SACO,OAAO;IACZ,OAAO;KAAE,GAAG;KAAM,OAAO,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAI;GAC5G;EACF;CACF;;;;;;;;;;;;;ACtGA,SAAgB,cAAc,OAAwB;CACpD,IAAI,MAAM,WAAW,KAAK,MAAM,SAAS,UACvC,OAAO;CACT,IAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GACtE,OAAO;CAET,MAAM,UAAU,MAAM,QAAQ,SAAS,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC7D,IAAI,QAAQ,WAAW,GACrB,OAAO;CACT,OAAO,QAAQ,MAAM,GAAG,CAAC,CAAC,OAAM,YAAW,YAAY,MAAM,YAAY,OAAO,YAAY,QAAQ,CAAC,QAAQ,SAAS,GAAG,CAAC;AAC5H;;;;;AAsIA,SAAS,YAAY,SAAgC;CACnD,IAAI,GAAG,WAAW,KAAK,KAAK,SAAS,YAAY,CAAC,GAChD,OAAO;CAET,MAAM,cAAc,GAAG,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,QAAO,UAAS,MAAM,YAAY,CAAC;CACxG,IAAI,YAAY,WAAW,GACzB,OAAO;CAET,MAAM,QAAQ,KAAK,KAAK,SAAS,YAAY,EAAE,CAAE,IAAI;CACrD,OAAO,GAAG,WAAW,KAAK,KAAK,OAAO,YAAY,CAAC,IAAI,QAAQ;AACjE;AAEA,SAAS,aAAa,MAA0D;CAC9E,IAAI;EACF,MAAM,SAAS,eAAe,KAAK,MAAM,GAAG,aAAa,KAAK,KAAK,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC;EACxF,OAAO,kBAAkB,KAAK,SAAS,OAAO;CAChD,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAS,WAAW,MAAsB;CACxC,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,GAAG,YAAY,MAAM;EAAE,eAAe;EAAM,WAAW;CAAK,CAAC,GAC/E,IAAI,MAAM,OAAO,KAAK,MAAM,SAAS,MACnC,SAAS;CAEb,OAAO,KAAK,IAAI,GAAG,KAAK;AAC1B;;;CAzMgC,YAAA;CACkB,aAAA;CACrB,eAAA;CASvB,OAAO;CAEP,cAAc;CACd,YAAY;CACZ,WAAW;CAGX,iBAAiB,KAAK;EAC1B,SAAS;EACT,YAAY;CACd,CAAC;CAuBY,YAAb,MAAuB;EACQ;EAA7B,YAAY,SAAmE;GAAlD,KAAA,UAAA;EAAmD;;EAGhF,IAAI,YAAoB;GACtB,OAAO,KAAK,KAAK,KAAK,QAAQ,UAAU,KAAK;EAC/C;;EAGA,IAAI,SAAkB;GACpB,OAAO,GAAG,WAAW,KAAK,KAAK,KAAK,WAAW,YAAY,CAAC;EAC9D;;EAGA,aAAqB;GACnB,IAAI,KAAK,QACP,OAAO,KAAK;GACd,OAAO,KAAK,QAAQ,YAAY,KAAK;EACvC;EAEA,SAAmB;GACjB,OAAO;IAAE,QAAQ,KAAK;IAAQ,KAAK,KAAK;IAAW,MAAM,KAAK,SAAS;GAAE;EAC3E;EAEA,WAA0B;GACxB,IAAI;IACF,MAAM,SAAS,aAAa,KAAK,MAAM,GAAG,aAAa,KAAK,KAAK,KAAK,WAAW,IAAI,GAAG,MAAM,CAAC,CAAC;IAChG,OAAO,kBAAkB,KAAK,SAAS,OAAO;GAChD,QACM;IACJ,OAAO;GACT;EACF;;;;;;EAOA,MAAM,QAAQ,aAAqB,eAAe,aAAuC;GACvF,IAAI,CAAC,aAAa,WAAW,GAC3B,OAAO;IAAE,IAAI;IAAO,OAAO;GAAkC;GAE/D,MAAM,UAAU,KAAK,KAAK,KAAK,QAAQ,UAAU,eAAe,KAAK,IAAI,GAAG;GAE5E,IAAI;IACF,OAAO,MAAM,KAAK,MAAM,aAAa,SAAS,YAAY;GAC5D,SACO,OAAO;IACZ,OAAO;KAAE,IAAI;KAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAE;GACpF,UACQ;IACN,GAAG,OAAO,SAAS;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GACrD;EACF;;EAGA,SAAkB;GAChB,MAAM,UAAU,GAAG,WAAW,KAAK,SAAS;GAC5C,GAAG,OAAO,KAAK,WAAW;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;GAC1D,OAAO;EACT;EAEA,MAAc,MAAM,aAAqB,SAAiB,cAAgD;GACxG,IAAI;GACJ,IAAI;IACF,UAAU,MAAM,QAAQ,WAAW;GACrC,SACO,OAAO;IACZ,OAAO;KAAE,IAAI;KAAO,OAAO,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAI;GACxH;GAEA,IAAI,QAAQ,WAAW,GACrB,OAAO;IAAE,IAAI;IAAO,OAAO;GAAuB;GACpD,IAAI,QAAQ,SAAS,aACnB,OAAO;IAAE,IAAI;IAAO,OAAO,6BAA6B,YAAY;GAAU;GAGhF,IADc,QAAQ,QAAQ,OAAO,UAAU,QAAQ,MAAM,MAAM,CAC/D,IAAQ,WACV,OAAO;IAAE,IAAI;IAAO,OAAO,8BAA8B,KAAK,MAAM,YAAY,OAAO,IAAI,EAAE;GAAiB;GAEhH,MAAM,WAAW,QAAQ,MAAK,UAAS,CAAC,cAAc,MAAM,IAAI,CAAC;GACjE,IAAI,aAAa,KAAA,GACf,OAAO;IAAE,IAAI;IAAO,OAAO,0CAA0C,SAAS;GAAO;GAEvF,GAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;GACzC,MAAM,WAAW,aAAa,SAAS,EAAE,OAAO,QAAQ,KAAI,UAAS,MAAM,IAAI,EAAE,CAAC;GAElF,MAAM,OAAO,YAAY,OAAO;GAChC,IAAI,SAAS,MACX,OAAO;IAAE,IAAI;IAAO,OAAO;GAA4C;GAEzE,MAAM,WAAW,aAAa,IAAI;GAClC,MAAM,OAAe;IACnB,MAAM,UAAU,QAAQ;IACxB,SAAS,UAAU,WAAW;IAC9B,YAAY,KAAK,IAAI;IACrB,OAAO,WAAW,IAAI;GACxB;GAGA,MAAM,WAAW,GAAG,KAAK,UAAU;GACnC,GAAG,OAAO,UAAU;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;GACpD,IAAI,GAAG,WAAW,KAAK,SAAS,GAC9B,GAAG,WAAW,KAAK,WAAW,QAAQ;GAExC,IAAI;IACF,GAAG,WAAW,MAAM,KAAK,SAAS;IAClC,gBAAgB,KAAK,KAAK,KAAK,WAAW,IAAI,GAAG,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,GAAG;GACvF,SACO,OAAO;IACZ,GAAG,OAAO,KAAK,WAAW;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;IAC1D,IAAI,GAAG,WAAW,QAAQ,GACxB,GAAG,WAAW,UAAU,KAAK,SAAS;IACxC,OAAO;KAAE,IAAI;KAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAAE;GACpF,UACQ;IACN,GAAG,OAAO,UAAU;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GACtD;GAEA,OAAO;IAAE,IAAI;IAAM;GAAK;EAC1B;CACF;;;;;;;;AC9HA,SAAS,iBAAyB;CAChC,IAAI;EAEF,OADiB,KAAK,MAAM,GAAG,aAAa,KAAK,KAAK,aAAa,cAAc,GAAG,MAAM,CACnF,CAAA,CAAS,WAAW;CAC7B,QACM;EACJ,OAAO;CACT;AACF;;AAeA,SAAS,aAAa,UAA0B;CAC9C,OAAO,aAAa,aAAa,aAAa,OAAO,cAAc;AACrE;;;;;;AAOA,eAAsB,gBAAgB,SAA6C;CACjF,MAAM,WAAW,YAAY;CAC7B,IAAI,aAAa,QAAQ,SAAS,QAAQ,QAAQ,OAAO,eAAe,SAAS,GAAG,GAAG;EACrF,OAAO,MAAM,wBAAwB,SAAS,IAAI,OAAO,SAAS,IAAI,kCAAkC;EACxG,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,aAAa,MACf,aAAa;CAEf,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,IAAI,YAAY,YAAY,WAAW;CACrD,MAAM,KAAK;CACX,MAAM,gBAAgB;CAEtB,MAAM,UAAU,IAAI,aAAa,kBAAkB;CACnD,MAAM,OAAO,IAAI,YAAY,eAAe,MAAM,OAAO,QAAQ,IAAI;CACrE,MAAM,MAAM,IAAI,SAAS,aAAa;CACtC,MAAM,WAAW,IAAI,SAAS,sBAAsB,MAAM,OAAO,IAAI;CACrE,MAAM,UAAU,IAAI,aAAa,kBAAkB;CACnD,MAAM,gBAAgB,IAAI,oBACxB,eACM,MAAM,OAAO,qBACb,MAAM,OAAO,IACrB;CACA,MAAM,cAAc,IAAI,kBAChB,MAAM,OAAO,OACnB,WAAU,gBAAgB,gBAAgB,QAAQ;EAAE;EAAY;EAAU,MAAM,GAAG,QAAQ;CAAE,CAAC,CAAC,GAC/F,aACF;CAIA,IAAI;CACJ,MAAM,UAAU,IAAI,cAAc;EAChC;EACA,iBAAiB,MAAM,OAAO;EAC9B,mBAAmB;GACjB,YAAY,MAAM;GAClB,aAAa,QAAQ;GACrB,QAAQ,IAAI;GACZ,OAAO,mBAAmB,MAAM,SAAS,MAAM,OAAO,QAAQ,YAAY;EAC5E;EACA,wBAAwB,mBAAmB;CAC7C,CAAC;CAKD,IAAI,CAAC,KAAK,aAAa;EACrB,KAAK,sBAAA,IAAsC;EAC3C,OAAO,KAAK,kFAAmG;CACjH;CAEA,MAAM,aAAa,MAAM,OAAO;CAChC,MAAM,eAAe,QAAQ,SAAS,KAAA,IAAY,WAAW,OAAO,UAAU,QAAQ,IAAI;CAC1F,IAAI,iBAAiB,MAAM;EACzB,OAAO,MAAM,yBAAyB,OAAO,QAAQ,IAAI,EAAE,0CAA0C;EACrG,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,WAAW;EAAE,MAAM;EAAc,MAAM,QAAQ,QAAQ,WAAW;CAAK;CAC7E,IAAI,CAAC,OAAO,UAAU,SAAS,IAAI,KAAK,SAAS,QAAQ,KAAK,SAAS,OAAO,OAAO;EACnF,OAAO,MAAM,yBAAyB,OAAO,QAAQ,IAAI,GAAG;EAC5D,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,QAAQ,aAAa;EACvB,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,QAAQ,MAAM,CAAC,EAAE,GAAG;EACjE;CACF;CAGA,MAAM,WAAW,cAAc;EAAE,GAAG;EAAY,MAAM,SAAS;CAAK,GAAG,KAAK,aAAa,KAAK,oBAAoB;CAClH,IAAI,SAAS,kBAAkB,MAAM;EACnC,OAAO,MAAM,sBAAsB,SAAS,eAAe;EAC3D,OAAO,KAAK,wHAAwH;EACpI,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,CAAE,MAAM,WAAW,SAAS,IAAI,GAAI;EACtC,OAAO,MAAM,gBAAgB,SAAS,KAAK,qDAAqD;EAChG,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,SAAS,SAAS,WAAW,QAAQ,SAAS,SAAS,WAAW,MACpE,MAAM,cAAc;EAAE,MAAM,SAAS;EAAM,MAAM,SAAS;CAAK,CAAC;CAElE,MAAM,KAAK,IAAI,UAAU;EAAE;EAAU,UAAU,KAAK,KAAK,aAAa,OAAO,SAAS,MAAM;CAAE,CAAC;CAC/F,MAAM,MAAM,IAAI,SAAS;CACzB,IAAI;CACJ,MAAM,QAAQ,SAAS;CAEvB,MAAM,gBAAgB,IAAI,cACxB;EACE,QAAQ,YAAY;GAClB,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,kCAAkC;GACpD,OAAO,IAAI,MAAM,OAAO;EAC1B;EACA,kBAAkB,MAAM,OAAO,QAAQ,KAAK;EAC5C,WAAY,MAAM,OAAO,QAAQ,IAAI,UAAU,IAAI,KAAK,IAAI;CAC9D,GACA;EAAE,MAAM,SAAS;EAAM,MAAM,SAAS;EAAM,KAAK,MAAM,OAAO,QAAQ,IAAI;CAAQ,CACpF;CAEA,MAAM,aAAa,IAAI,WAAW,OAAO,KAAK;EAC5C,YAAY,MAAM;EAClB,SAAS,cAAc;EACvB,aAAY,UAAS,cAAc;GACjC;GACA;GACA,SAAS,cAAc;GACvB;GACA;GACA;GACA;GACA,SAAS,SAAS;GAClB;EACF,CAAC;EACD;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI,eAAe;CACnB,MAAM,WAAW,OAAO,WAAkC;EACxD,IAAI,cACF;EACF,eAAe;EACf,OAAO,KAAK,GAAG,OAAO,cAAc,WAAW,MAAM,CAAC,CAAC,OAAO,WAAW;EACzE,aAAa;EACb,KAAK,QAAQ;EACb,MAAM,WAAW,QAAQ;EACzB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,MAAM,cAAc,MAAM,IAAI;EAC9B,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,cAAc;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd,kBAAkB,SAAS,4BAA4B;CACzD,CAAC;CAED,MAAM,cAAc,MAAM;CAG1B,MAAM,QAAQ,KAAK;CAEnB,MAAM,WAAW,cAAc;CAC/B,MAAM,UAAmB;EACvB,SAAS,eAAe;EACxB,KAAK,QAAQ;EACb,KAAK,SAAS;EACd,UAAU,GAAG,SAAS,SAAS,KAAK,aAAa,SAAS,QAAQ,EAAE,GAAG,SAAS;EAChF,UAAU,SAAS;EACnB,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,WAAW,KAAK,IAAI;EACpB;EACA;EACA,YAAY,MAAM;EAClB,SAAS;EACT;CACF;CACA,aAAa,OAAO;CAEpB,OAAO,IAAI,eAAe,QAAQ,QAAQ,IAAI,SAAS,KAAK;CAC5D,OAAO,KAAK,YAAY,MAAM,MAAM;CACpC,OAAO,KAAK,YAAY,QAAQ,OAAO,KAAK,cAAc,KAAK,sBAAsB;CACrF,OAAO,KAAK,YAAY,KAAK,WAAW,IAAI,aAAa,aAAa,KAAK,uBAAuB,wBAAwB,KAAK,SAAS,UAAU,+BAA+B,IAAI;CACrL,OAAO,KAAK,YAAY,MAAM,OAAO,KAAK,UAAU,GAAG,SAAS,UAAU,QAAQ,MAAM,OAAO,KAAK,SAAS,OAAO,MAAM,OAAO,KAAK,KAAK,KAAK,eAAe;CAC/J,OAAO,KAAK,YAAY,YAAY;CACpC,IAAI,GAAG,QAAQ;EACb,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC;EACzB,OAAO,KAAK,mBAAmB,SAAS,OAAO,KAAK,KAAK,KAAK,OAAO,KAAK,YAAY,OAAO,KAAK,IAAI,KAAK,UAAU,GAAG,+CAA+C;CACzK;CACA,IAAI,MAAM,gBAAgB,MACxB,OAAO,KAAK,sBAAsB,MAAM,aAAa;CACvD,KAAK,MAAM,SAAS,WAAW,MAAM,GACnC,OAAO,KAAK,KAAK,MAAM,GAAG,OAAO,EAAE,EAAE,GAAG,MAAM,OAAO,QAAQ,GAAG,MAAM,OAAO,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC;CAEzG,IAAI,WAAW,eAAe,QAAQ,MACpC,YAAY,SAAS,GAAG;CAE1B,IAAI,QAAQ,WACV,WAAgB,SAAS,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,OAAO,UAAmB;EAC1E,OAAO,MAAM,oBAAoB,KAAK;CACxC,CAAC;CAGH,yBAAyB;EACvB,MAAM,KAAK;EAIX,MAAM,WAAW,cAAc,MAAM,OAAO,SAAS,KAAK,aAAa,KAAK,oBAAoB;EAChG,IAAI,SAAS,kBAAkB,MAAM;GACnC,OAAO,KAAK,+CAA+C,SAAS,cAAc,8BAA8B;GAChH,MAAM,cAAc,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;EACjD;EAEA,OAAO,KAAK,qBAAqB,MAAM,QAAQ,OAAO,oBAAoB;EAE1E,IAAI,CAAC,QAAQ,WACX;EACF,WAAgB,SAAS,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,OAAO,UAAmB;GAC1E,OAAO,MAAM,wCAAwC,KAAK;EAC5D,CAAC;CACH;CAEA,QAAQ,GAAG,gBAAgB,KAAK,SAAS,QAAQ,CAAC;CAClD,QAAQ,GAAG,iBAAiB,KAAK,SAAS,SAAS,CAAC;AACtD;;;CAvS8B,SAAA;CACD,aAAA;CACD,UAAA;CACA,WAAA;CACsD,YAAA;CAC3D,YAAA;CACK,UAAA;CAWrB,WAAA;CACyB,cAAA;CACL,UAAA;CACmB,YAAA;CACI,aAAA;CACpB,oBAAA;CACL,YAAA;CACK,cAAA;CACD,aAAA;CACD,kBAAA;CACH,eAAA;CACW,mBAAA;CACN,aAAA;CACH,gBAAA;CACF,SAAA;CACC,QAAA;CACA,eAAA;CAGb,cAAc,KAAK,QAAQ,cAAc,IAAI,IAAI,MAAM,YAAY,GAAG,CAAC,CAAC;;;;;;;;;AC5BrF,IAAM,YAAY,cAAc,YAAY,GAAG;AAC/C,IAAM,eAAe;AACrB,IAAM,mBAAmB;AAEzB,IAAM,QAAQ;;;;;;;;;;;;uDAYyC,aAAa;;;;;;;;;;;;;;;;;AAkBpE,IAAM,cAAuB,QAAQ,OAAO,UAAU;AACtD,IAAM,SAAS,MAAc,SAA0B,MAAM,IAAI,QAAQ,KAAK,GAAG,KAAK,WAAW;AACjG,IAAM,OAAO,SAAyB,MAAM,KAAK,IAAI;AACrD,IAAM,QAAQ,SAAyB,MAAM,KAAK,IAAI;AACtD,IAAM,SAAS,SAAyB,MAAM,MAAM,IAAI;AAExD,SAAS,KAAK,SAAwB;CACpC,QAAQ,OAAO,MAAM,GAAG,MAAM,MAAM,OAAO,EAAE,GAAG,QAAQ,GAAG;CAC3D,QAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAQ,YAAW,WAAW,SAAS,EAAE,CAAC;AACvD;;AAQA,SAAS,gBAAgB,MAA+C;CACtE,MAAM,OAAiB,CAAC;CACxB,IAAI;CACJ,IAAI;CAEJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,MAAM,KAAK;EACjB,MAAM,SAAS,IAAI,QAAQ,GAAG;EAC9B,MAAM,OAAO,WAAW,KAAK,MAAM,IAAI,MAAM,GAAG,MAAM;EACtD,IAAI,SAAS,eAAe,SAAS,UAAU;GAC7C,KAAK,KAAK,GAAG;GACb;EACF;EACA,MAAM,QAAQ,WAAW,KAAK,KAAK,EAAE,SAAS,IAAI,MAAM,SAAS,CAAC;EAClE,IAAI,UAAU,KAAA,KAAa,MAAM,WAAW,GAC1C,KAAK,GAAG,KAAK,mBAAmB;EAClC,IAAI,SAAS,aACX,UAAU;OAEV,OAAO;CACX;CAEA,OAAO;EAAE;EAAM;EAAS;CAAK;AAC/B;;AAGA,SAAS,cAAc,OAAuB;CAC5C,IAAI,MAAM,YAAY,KAAA,GACpB,QAAQ,IAAI,kBAAkB,KAAK,QAAQ,MAAM,OAAO;CAC1D,IAAI,MAAM,SAAS,KAAA,GACjB,QAAQ,IAAI,eAAe,KAAK,QAAQ,MAAM,IAAI;AACtD;AAYA,SAAS,aAAa,MAAyB;CAC7C,MAAM,EAAE,WAAW,UAAU;EAC3B,MAAM;EACN,SAAS;GACP,UAAU;IAAE,MAAM;IAAU,OAAO;GAAI;GACvC,QAAQ;IAAE,MAAM;IAAU,OAAO;GAAI;GACrC,QAAQ,EAAE,MAAM,SAAS;GACzB,gBAAgB,EAAE,MAAM,UAAU;GAClC,QAAQ,EAAE,MAAM,UAAU;GAC1B,cAAc,EAAE,MAAM,UAAU;GAChC,gBAAgB,EAAE,MAAM,UAAU;EACpC;EACA,kBAAkB;CACpB,CAAC;CAED,IAAI;CACJ,IAAI,OAAO,SAAS,KAAA,GAAW;EAC7B,OAAO,OAAO,SAAS,OAAO,MAAM,EAAE;EACtC,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAO,OACjD,KAAK,iBAAiB,OAAO,MAAM;CACvC;CAEA,OAAO;EACL,QAAQ,OAAO;EACf;EACA,MAAM,OAAO;EACb,WAAW,OAAO,oBAAoB;EACtC,MAAM,OAAO,SAAS;EACtB,YAAY,OAAO,eAAe;EAClC,aAAa,OAAO,oBAAoB;CAC1C;AACF;;AAGA,SAAS,YAAY,OAA0B;CAC7C,MAAM,OAAiB,CAAC;CACxB,IAAI,MAAM,WAAW,KAAA,GACnB,KAAK,KAAK,YAAY,MAAM,MAAM;CACpC,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,KAAK,UAAU,OAAO,MAAM,IAAI,CAAC;CACxC,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,KAAK,UAAU,MAAM,IAAI;CAChC,IAAI,CAAC,MAAM,WACT,KAAK,KAAK,gBAAgB;CAC5B,IAAI,MAAM,MACR,KAAK,KAAK,QAAQ;CACpB,OAAO;AACT;;;;;;AAOA,SAAS,cAAwB;CAC/B,IAAI,WAA0B;CAC9B,MAAM,mBAA2B,aAAa,YAAY,QAAQ,KAAK;CAEvE,OAAO,QAAQ,SAAS,KAAK,QAAQ;EACnC,IAAI,QAAQ,OACV,OAAO,WAAW;EACpB,IAAI,IAAI,WAAW,WAAW,KAAK,IAAI,MAAM,CAAkB,MAAM,OACnE,OAAO,YAAY,WAAW;EAChC,OAAO;CACT,CAAC;AACH;;AAGA,SAAS,UAAU,MAAoB;CACrC,IAAI;EACF,IAAI,GAAG,SAAS,IAAI,CAAC,CAAC,OAAO,kBAC3B;EACF,GAAG,OAAO,GAAG,KAAK,KAAK,EAAE,OAAO,KAAK,CAAC;EACtC,GAAG,WAAW,MAAM,GAAG,KAAK,GAAG;CACjC,QACM,CAEN;AACF;AAEA,SAAS,QAAQ,MAAc,QAAQ,IAAY;CACjD,IAAI;EACF,OAAO,GAAG,aAAa,MAAM,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ;CACpF,QACM;EACJ,OAAO;CACT;AACF;AAEA,eAAe,GAAG,MAA+B;CAC/C,MAAM,QAAQ,aAAa,IAAI;CAC/B,MAAM,EAAE,oBAAoB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,SAAA,GAAA,YAAA;CAE5B,IAAI,MAAM,YAAY;EACpB,MAAM,gBAAgB;GACpB,YAAY,MAAM;GAClB,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,WAAW,MAAM;GACjB,MAAM,MAAM;GACZ,aAAa,MAAM;EACrB,CAAC;EACD;CACF;CAEA,MAAM,EAAE,cAAc,gBAAgB,gBAAgB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,YAAA,GAAA,eAAA;CACtD,MAAM,EAAE,eAAe,UAAU,eAAe,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,WAAA,GAAA,cAAA;CAEhD,MAAM,WAAW,YAAY;CAC7B,IAAI,aAAa,QAAQ,eAAe,SAAS,GAAG,GAAG;EACrD,QAAQ,OAAO,MAAM,GAAG,MAAM,iBAAiB,EAAE,QAAQ,SAAS,IAAI,OAAO,SAAS,IAAI,GAAG;EAC7F,QAAQ,OAAO,MAAM,GAAG,IAAI,iCAAiC,EAAE,GAAG;EAClE;CACF;CACA,IAAI,aAAa,MACf,aAAa;CAEf,GAAG,UAAU,KAAK,QAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;CAC7D,UAAU,aAAa;CACvB,MAAM,MAAM,GAAG,SAAS,eAAe,GAAG;CAE1C,MAAM,QAAQ,MAAM,QAAQ,UAAU;EAAC,GAAG,YAAY;EAAG;EAAW;EAAM;EAAgB,GAAG,YAAY,KAAK;CAAC,GAAG;EAChH,UAAU;EACV,KAAK;EACL,KAAK;GAAE,GAAG,QAAQ;GAAK,cAAc;GAAU,iBAAiB;EAAW;EAC3E,OAAO;GAAC;GAAU;GAAK;EAAG;EAC1B,aAAa;CACf,CAAC;CACD,MAAM,MAAM;CACZ,GAAG,UAAU,GAAG;CAEhB,MAAM,UAAU,MAAM,eAAe,KAAK;CAC1C,IAAI,YAAY,MAAM;EACpB,MAAM,SAAS,QAAQ,aAAa;EACpC,QAAQ,OAAO,MAAM,GAAG,MAAM,MAAM,OAAO,EAAE,mCAAmC;EAChF,IAAI,OAAO,SAAS,GAClB,QAAQ,OAAO,MAAM,GAAG,IAAI,GAAG,cAAc,EAAE,EAAE,IAAI,OAAO,GAAG;EACjE,QAAQ,KAAK,CAAC;CAChB;CAEA,QAAQ,OAAO,MAAM,GAAG,MAAM,mBAAmB,EAAE,QAAQ,QAAQ,IAAI,IAAI;CAC3E,QAAQ,OAAO,MAAM,KAAK,KAAK,QAAQ,GAAG,EAAE,GAAG;CAC/C,QAAQ,OAAO,MAAM,KAAK,IAAI,WAAW,QAAQ,YAAY,EAAE,GAAG;CAClE,QAAQ,OAAO,MAAM,KAAK,IAAI,WAAW,QAAQ,UAAU,EAAE,GAAG;CAChE,QAAQ,OAAO,MAAM,KAAK,IAAI,WAAW,QAAQ,SAAS,EAAE,GAAG;AACjE;AAEA,eAAe,eAAe,OAAqB,YAAY,KAAO;CACpE,MAAM,EAAE,gBAAgB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,YAAA,GAAA,eAAA;CACxB,MAAM,WAAW,KAAK,IAAI,IAAI;CAE9B,SAAS;EACP,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAClD,OAAO;EAET,MAAM,UAAU,YAAY;EAC5B,IAAI,YAAY,QAAQ,QAAQ,QAAQ,MAAM,KAC5C,OAAO;EAET,IAAI,KAAK,IAAI,IAAI,UACf,OAAO;EACT,MAAM,MAAM,GAAG;CACjB;AACF;AAEA,eAAe,OAAsB;CACnC,MAAM,EAAE,cAAc,gBAAgB,aAAa,oBAAoB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,YAAA,GAAA,eAAA;CAEvE,MAAM,UAAU,YAAY;CAC5B,IAAI,YAAY,MAAM;EACpB,QAAQ,OAAO,MAAM,8BAA8B;EACnD;CACF;CACA,IAAI,CAAC,eAAe,QAAQ,GAAG,GAAG;EAChC,aAAa;EACb,QAAQ,OAAO,MAAM,yDAAyD;EAC9E;CACF;CAEA,QAAQ,OAAO,MAAM,gBAAgB,QAAQ,IAAI,IAAI;CAGrD,IAAI,CAAE,MAAM,gBAAgB,OAAO,GACjC,OAAO,QAAQ,KAAK,SAAS;CAE/B,IAAI,MAAM,YAAY,QAAQ,KAAK,GAAK,GAAG;EACzC,aAAa;EACb,QAAQ,OAAO,MAAM,GAAG,MAAM,SAAS,EAAE,GAAG;EAC5C;CACF;CAEA,QAAQ,OAAO,MAAM,GAAG,IAAI,mCAAmC,EAAE,GAAG;CACpE,UAAU,QAAQ,GAAG;CACrB,MAAM,YAAY,QAAQ,KAAK,GAAI;CACnC,aAAa;CACb,QAAQ,OAAO,MAAM,GAAG,MAAM,SAAS,EAAE,YAAY;AACvD;AAEA,eAAe,YAAY,KAAa,WAAqC;CAC3E,MAAM,EAAE,mBAAmB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,YAAA,GAAA,eAAA;CAC3B,MAAM,WAAW,KAAK,IAAI,IAAI;CAE9B,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,IAAI,CAAC,eAAe,GAAG,GACrB,OAAO;EACT,MAAM,MAAM,GAAG;CACjB;CACA,OAAO,CAAC,eAAe,GAAG;AAC5B;AAEA,SAAS,OAAO,KAAa,MAA4B;CACvD,IAAI;EACF,QAAQ,KAAK,KAAK,IAAI;CACxB,QACM,CAEN;AACF;;AAGA,SAAS,UAAU,KAAmB;CACpC,IAAI,QAAQ,aAAa,SAAS;EAChC,UAAU,YAAY;GAAC;GAAQ,OAAO,GAAG;GAAG;GAAM;EAAI,GAAG,EAAE,aAAa,KAAK,CAAC;EAC9E;CACF;CACA,OAAO,KAAK,SAAS;AACvB;AAEA,eAAe,QAAQ,MAA+B;CACpD,MAAM,KAAK;CACX,MAAM,GAAG,IAAI;AACf;AAEA,eAAe,OAAO,MAA+B;CACnD,MAAM,EAAE,WAAW,UAAU;EAC3B,MAAM;EACN,SAAS,EAAE,MAAM,EAAE,MAAM,UAAU,EAAE;EACrC,kBAAkB;CACpB,CAAC;CACD,MAAM,EAAE,gBAAgB,cAAc,gBAAgB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,YAAA,GAAA,eAAA;CACtD,MAAM,EAAE,cAAc,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,QAAA,GAAA,WAAA;CACtB,MAAM,EAAE,aAAa,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,WAAA,GAAA,cAAA;CACrB,MAAM,UAAU,YAAY;CAE5B,IAAI,YAAY,MAAM;EACpB,IAAI,OAAO,MACT,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,EAAE,SAAS,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG;OAEvE,QAAQ,OAAO,MAAM,8BAA8B;EACrD,QAAQ,WAAW;EACnB;CACF;CAEA,MAAM,UAAU,eAAe,QAAQ,GAAG;CAC1C,MAAM,QAAQ,UAAU,MAAM,aAAa,OAAO,IAAI;EAAE,WAAW;EAAO,UAAU;CAAM;CAE1F,IAAI,OAAO,MAAM;EAEf,MAAM,EAAE,OAAO,QAAQ,GAAG,SAAS;EACnC,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU;GAAE;GAAS,WAAW,MAAM;GAAW,UAAU,MAAM;GAAU,GAAG;EAAK,GAAG,MAAM,CAAC,EAAE,GAAG;EAC/H,IAAI,CAAC,SACH,QAAQ,WAAW;EACrB;CACF;CAEA,MAAM,SAAS,eAAe,KAAK,IAAI,IAAI,QAAQ,SAAS;CAC5D,MAAM,QAAQ,CAAC,UACX,MAAM,MAAM,6BAA6B,IACzC,MAAM,WACJ,MAAM,MAAM,oCAAoC,IAChD,MAAM,YAAY,MAAM,SAAS,IAAI,MAAM,MAAM,4BAA4B;CAEnF,MAAM,KAAK,IAAI,UAAU,EAAE,SAAS,CAAC;CACrC,MAAM,OAAgC;EACpC,CAAC,UAAU,KAAK;EAChB,CAAC,OAAO,UAAU,GAAG,QAAQ,IAAI,QAAQ,WAAW,OAAO,QAAQ,GAAG,CAAC;EACvE,CAAC,OAAO,GAAG,QAAQ,IAAI,GAAG,IAAI,IAAI,QAAQ,SAAS,EAAE,GAAG;EACxD,CAAC,WAAW,QAAQ,OAAO;EAC3B,CAAC,WAAW,QAAQ,UAAU;EAC9B,CAAC,SAAS,QAAQ,QAAQ;EAC1B,CAAC,UAAU,QAAQ,UAAU;EAC7B,CAAC,OAAO,QAAQ,OAAO;EACvB,CAAC,MAAM,GAAG,SAAS,YAAY,GAAG,OAAO,CAAC,CAAC,MAAM,QAAQ,YAAY,4CAA4C,OAAO;CAC1H;CAEA,QAAQ,OAAO,MAAM,GAAG,KAAK,eAAe,QAAQ,SAAS,EAAE,GAAG;CAClE,KAAK,MAAM,CAAC,OAAO,UAAU,MAC3B,QAAQ,OAAO,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG;CAC7D,IAAI,CAAC,SACH,QAAQ,WAAW;AACvB;AAEA,SAAS,eAAe,IAAoB;CAC1C,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;CACjD,IAAI,UAAU,IACZ,OAAO,GAAG,QAAQ;CACpB,MAAM,UAAU,KAAK,MAAM,UAAU,EAAE;CACvC,IAAI,UAAU,IACZ,OAAO,GAAG,QAAQ;CACpB,MAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;CACrC,IAAI,QAAQ,IACV,OAAO,GAAG,MAAM,IAAI,UAAU,GAAG;CACnC,OAAO,GAAG,KAAK,MAAM,QAAQ,EAAE,EAAE,IAAI,QAAQ,GAAG;AAClD;;AAGA,SAAS,aAAa,UAAmC;CACvD,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,KAAK,SAAS,gBAAgB;GAAE,OAAO,QAAQ;GAAO,QAAQ,QAAQ;GAAQ,UAAU;EAAK,CAAC;EACpG,MAAM,eAAqB;GACzB,SAAS,UAAU,QAAQ,QAAQ,CAAC;GACpC,SAAS,SAAS,QAAQ,QAAQ,CAAC;GACnC,QAAQ,OAAO,MAAM,QAAQ;EAC/B;EAEA,QAAQ,MAAM,GAAG,QAAQ,MAAM;EAC/B,GAAG,SAAS,WAAW,WAAW;GAChC,QAAQ,MAAM,IAAI,QAAQ,MAAM;GAChC,GAAG,MAAM;GACT,QAAQ,OAAO,MAAM,IAAI;GACzB,QAAQ,MAAM;EAChB,CAAC;CACH,CAAC;AACH;;AAGA,eAAe,WAA0B;CACvC,MAAM,EAAE,cAAc,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,QAAA,GAAA,WAAA;CACtB,MAAM,EAAE,aAAa,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,WAAA,GAAA,cAAA;CACrB,MAAM,KAAK,IAAI,UAAU,EAAE,SAAS,CAAC;CAErC,IAAI,CAAC,GAAG,QAAQ;EACd,QAAQ,OAAO,MAAM,iEAAiE;EACtF;CACF;CACA,GAAG,OAAO;CACV,QAAQ,OAAO,MAAM,GAAG,MAAM,mBAAmB,EAAE,kDAAkD;AACvG;AAEA,eAAe,YAAY,MAA+B;CACxD,MAAM,EAAE,WAAW,UAAU;EAC3B,MAAM;EACN,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,EAAE;EACtC,kBAAkB;CACpB,CAAC;CACD,MAAM,EAAE,uBAAuB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,WAAA,GAAA,cAAA;CAC/B,MAAM,EAAE,iBAAiB,MAAA,QAAA,QAAA,CAAA,CAAA,YAAA,aAAA,GAAA,gBAAA;CACzB,MAAM,QAAQ,IAAI,aAAa,kBAAkB;CAEjD,IAAI,OAAO,UAAU,MAAM;EACzB,MAAM,cAAc;EACpB,QAAQ,OAAO,MAAM,yCAAyC,mBAAmB,GAAG;EACpF,QAAQ,OAAO,MAAM,GAAG,IAAI,8EAA8E,EAAE,GAAG;EAC/G;CACF;CAEA,MAAM,cAAc,QAAQ,MAAM,UAAU;CAC5C,IAAI,WAAW,QAAQ,IAAI;CAE3B,IAAI,aAAa,KAAA,KAAa,aAAa;EACzC,WAAW,MAAM,aAAa,8BAA8B;EAC5D,MAAM,QAAQ,MAAM,aAAa,aAAa;EAC9C,IAAI,aAAa,OACf,KAAK,4BAA4B;CACrC;CAEA,IAAI,aAAa,KAAA,KAAa,SAAS,WAAW,GAChD,KAAK,yFAAyF;CAGhG,MAAM,YAAY,QAAQ;CAC1B,QAAQ,OAAO,MAAM,GAAG,MAAM,iBAAiB,EAAE,MAAM,mBAAmB,eAAe;CACzF,IAAI,SAAS,SAAS,GACpB,QAAQ,OAAO,MAAM,GAAG,IAAI,IAAI,SAAS,qEAAqE,EAAE,GAAG;CACrH,QAAQ,OAAO,MAAM,GAAG,IAAI,8DAA8D,EAAE,GAAG;AACjG;AAEA,SAAS,UAAgB;CACvB,MAAM,WAAW,KAAK,MAAM,GAAG,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC;CAChG,QAAQ,OAAO,MAAM,GAAG,SAAS,WAAW,QAAQ,GAAG;AACzD;AAEA,eAAe,OAAsB;CACnC,MAAM,EAAE,MAAM,GAAG,aAAa,gBAAgB,QAAQ,KAAK,MAAM,CAAC,CAAC;CACnE,MAAM,CAAC,UAAU,IAAI,GAAG,QAAQ;CAEhC,IAAI,YAAY,MAAM,KAAK,WAAW,GAAG;EACvC,cAAc,QAAQ;EACtB,MAAM,GAAG,CAAC,CAAC;EACX;CACF;CAEA,QAAQ,SAAR;EACE,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,GAAG,IAAI;GACb;EACF,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,KAAK;GACX;EACF,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,QAAQ,IAAI;GAClB;EACF,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,OAAO,IAAI;GACjB;EACF,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,YAAY,IAAI;GACtB;EACF,KAAK;GACH,cAAc,QAAQ;GACtB,MAAM,SAAS;GACf;EACF,KAAK;EACL,KAAK;EACL,KAAK;GACH,QAAQ,OAAO,MAAM,KAAK;GAC1B;EACF,KAAK;EACL,KAAK;EACL,KAAK;GACH,QAAQ;GACR;EACF;GACE,IAAI,QAAQ,WAAW,GAAG,GAAG;IAE3B,cAAc,QAAQ;IACtB,MAAM,GAAG,IAAI;IACb;GACF;GACA,QAAQ,OAAO,MAAM,oBAAoB,QAAQ,MAAM,OAAO;GAC9D,QAAQ,KAAK,CAAC;CAClB;AACF;AAEK,KAAK,CAAC,CAAC,OAAO,UAAmB;CACpC,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAC7D,CAAC"}
|