workerdeck 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"instance-DyBeVgsK.mjs","names":["FILENAME","MAX_PAYLOAD_BYTES"],"sources":["../src/lib/http.ts","../src/apns/json-registry.ts","../src/apns/activities.ts","../src/apns/devices.ts","../src/apns/routes.ts","../src/apns/client.ts","../src/apns/live-activity.ts","../src/apns/forwarder.ts","../src/apns/live-activities.ts","../src/auth/auth-key.ts","../src/auth/auth-sessions.ts","../src/auth/origin.ts","../src/auth/secret.ts","../src/auth/cookie-sessions.ts","../src/auth/throttle.ts","../src/auth/auth.ts","../src/config.ts","../src/auth/login-page.ts","../src/lib/keep-awake.ts","../src/lib/static.ts","../src/lib/instance.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\n\n// Read a request body, bounded. Resolves `null` if the body exceeds `maxBytes` or the socket\n// errors - the caller answers 413 either way; it never resolves twice, and it never buffers\n// past the cap, which is the whole point of not using a body parser here.\nexport function readBody(req: IncomingMessage, maxBytes: number): Promise<string | null> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = []\n let size = 0\n let settled = false\n const finish = (value: string | null): void => {\n if (!settled) {\n settled = true\n resolve(value)\n }\n }\n req.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > maxBytes) {\n finish(null)\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => finish(Buffer.concat(chunks).toString('utf8')))\n req.on('error', () => finish(null))\n })\n}\n\n// A JSON answer that is never cached - these routes all carry auth state.\nexport function respondJson(res: ServerResponse, status: number, body: Record<string, unknown>, headers?: Record<string, string>): void {\n res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store', ...headers }).end(JSON.stringify(body))\n}\n\nexport type RequestAuthenticator = (req: IncomingMessage) => unknown\n\n// Awaited on purpose: a config file's `authenticate` may be async, and a pending promise is not `null`.\nexport async function isAuthenticated(authenticate: RequestAuthenticator, req: IncomingMessage): Promise<boolean> {\n const principal = await authenticate(req)\n return principal !== null && principal !== undefined && principal !== false\n}\n\n// A cross-site page can send `text/plain` without a preflight; it cannot send `application/json`.\nexport function isJsonRequest(req: IncomingMessage): boolean {\n return (req.headers['content-type'] ?? '').split(';')[0]!.trim().toLowerCase() === 'application/json'\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { join } from 'node:path'\nimport { readJsonOr, writeJsonAtomic } from '@workerdeck/server'\nimport type { ApnsEnvironment } from './client.ts'\nimport { isAuthenticated, isJsonRequest, readBody, respondJson, type RequestAuthenticator } from '../lib/http.ts'\n\nexport type JsonRegistryOptions = {\n dir: string | null\n onError?: (error: unknown, context: { op: string; path: string }) => void\n}\n\nexport type JsonRecordFile = { load: () => Promise<unknown[]>; save: (records: unknown[]) => Promise<void> }\n\nexport type PushRouteHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean>\n\n// 64 hex chars today; the upper bound is loose because Apple reserved the right to grow the token.\nexport const TOKEN_PATTERN = /^[0-9a-fA-F]{32,200}$/\n\nconst MAX_BODY_BYTES = 4096\n\nexport function isEnvironment(value: unknown): value is ApnsEnvironment {\n return value === 'development' || value === 'production'\n}\n\n// Missing, unreadable and corrupt all load as empty: push is a side channel and must never refuse a boot.\nexport function jsonRecordFile(options: JsonRegistryOptions, filename: string, field: string): JsonRecordFile {\n const path = options.dir === null ? null : join(options.dir, filename)\n return {\n load: async () => {\n if (path === null) {\n return []\n }\n const records = ((await readJsonOr(path, null)) as Record<string, unknown> | null)?.[field]\n return Array.isArray(records) ? records : []\n },\n save: async (records) => {\n if (path === null) {\n return\n }\n try {\n await writeJsonAtomic(path, { [field]: records }, { indent: 2, trailingNewline: true })\n } catch (error) {\n options.onError?.(error, { op: 'write', path })\n }\n },\n }\n}\n\n// The shared preamble of every `/apns/*` route: claimed with or without a registry, authenticated before anything is\n// parsed (a token is an address this gateway can buzz), JSON only, bounded.\nexport function jsonPushRoute<R>(\n route: string,\n registry: R | null,\n authenticate: RequestAuthenticator,\n handle: (registry: R, req: IncomingMessage, res: ServerResponse, body: Record<string, unknown>) => Promise<void>,\n): PushRouteHandler {\n return async (req, res) => {\n let pathname: string\n try {\n pathname = new URL(req.url ?? '/', 'http://internal').pathname\n } catch {\n return false\n }\n if (pathname !== route) {\n return false\n }\n if (registry === null) {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' })\n res.end('this gateway runs without push\\n')\n return true\n }\n if (req.method !== 'POST' && req.method !== 'DELETE') {\n res.writeHead(405, { allow: 'POST, DELETE' }).end()\n return true\n }\n if (!(await isAuthenticated(authenticate, req))) {\n respondJson(res, 401, { error: 'unauthorized' })\n return true\n }\n if (!isJsonRequest(req)) {\n respondJson(res, 415, { error: 'expected content-type application/json' })\n return true\n }\n const raw = await readBody(req, MAX_BODY_BYTES)\n if (raw === null) {\n respondJson(res, 413, { error: 'body too large' })\n res.once('finish', () => req.destroy())\n return true\n }\n let body: Record<string, unknown>\n try {\n body = JSON.parse(raw) as Record<string, unknown>\n } catch {\n respondJson(res, 400, { error: 'invalid JSON body' })\n return true\n }\n await handle(registry, req, res, body)\n return true\n }\n}\n","import type { ApnsEnvironment } from './client.ts'\nimport {\n isEnvironment,\n jsonPushRoute,\n jsonRecordFile,\n TOKEN_PATTERN,\n type JsonRegistryOptions,\n type PushRouteHandler,\n} from './json-registry.ts'\nimport { respondJson, type RequestAuthenticator } from '../lib/http.ts'\n\n// `starting` is a card the gateway has asked APNs to raise but whose update token has not come\n// back yet. It is the state that forbids a second start for the same pair - that is how a phone\n// ends up with two cards for one session.\nexport type ActivityPhase = 'starting' | 'live' | 'ending'\n\nexport type ActivityRecord = {\n deviceToken: string\n sessionId: string\n hostId?: string\n environment: ApnsEnvironment\n updateToken?: string\n phase: ActivityPhase\n startedAt: number\n // Not persisted: both are optimisations whose loss across a restart costs one redundant push,\n // and a boot ends every card it finds anyway.\n lastPushAt?: number\n contentHash?: string\n}\n\nexport type ActivityRegistry = {\n list(): ActivityRecord[]\n get(deviceToken: string, sessionId: string): ActivityRecord | undefined\n forSession(sessionId: string): ActivityRecord[]\n start(record: Omit<ActivityRecord, 'phase' | 'startedAt'> & { startedAt?: number }): Promise<ActivityRecord>\n // Route-side: the app reporting a token it was handed. Matching by `(deviceToken, sessionId)` when\n // the app knows its device token, else by the one tokenless record for the session.\n attach(\n match: { sessionId: string; deviceToken?: string; environment: ApnsEnvironment },\n updateToken: string,\n ): Promise<ActivityRecord | undefined>\n touch(\n deviceToken: string,\n sessionId: string,\n changes: Partial<Pick<ActivityRecord, 'phase' | 'lastPushAt' | 'contentHash'>>,\n ): Promise<void>\n remove(deviceToken: string, sessionId: string): Promise<void>\n removeByUpdateToken(updateToken: string): Promise<void>\n}\n\nconst FILENAME = 'apns-activities.json'\n\nfunction keyOf(deviceToken: string, sessionId: string): string {\n return `${deviceToken}:${sessionId}`\n}\n\ntype Persisted = Pick<ActivityRecord, 'deviceToken' | 'sessionId' | 'hostId' | 'environment' | 'updateToken' | 'phase' | 'startedAt'>\n\nfunction persistable(record: ActivityRecord): Persisted {\n const { deviceToken, sessionId, hostId, environment, updateToken, phase, startedAt } = record\n return { deviceToken, sessionId, hostId, environment, updateToken, phase, startedAt }\n}\n\nexport async function createActivityRegistry(options: JsonRegistryOptions): Promise<ActivityRegistry> {\n const file = jsonRecordFile(options, FILENAME, 'activities')\n const records = new Map<string, ActivityRecord>()\n // A lost record costs an orphaned card that `stale-date` and the app's reconcile both close.\n for (const record of (await file.load()) as ActivityRecord[]) {\n if (typeof record?.deviceToken === 'string' && typeof record.sessionId === 'string' && isEnvironment(record.environment)) {\n records.set(keyOf(record.deviceToken, record.sessionId), record)\n }\n }\n const persist = (): Promise<void> => file.save([...records.values()].map(persistable))\n\n return {\n list: () => [...records.values()],\n get: (deviceToken, sessionId) => records.get(keyOf(deviceToken, sessionId)),\n forSession: (sessionId) => [...records.values()].filter((record) => record.sessionId === sessionId),\n async start(record) {\n const existing = records.get(keyOf(record.deviceToken, record.sessionId))\n if (existing !== undefined) {\n return existing\n }\n const created: ActivityRecord = { ...record, phase: 'starting', startedAt: record.startedAt ?? Date.now() }\n records.set(keyOf(created.deviceToken, created.sessionId), created)\n await persist()\n return created\n },\n async attach(match, updateToken) {\n const direct = match.deviceToken === undefined ? undefined : records.get(keyOf(match.deviceToken, match.sessionId))\n const candidates =\n direct !== undefined\n ? [direct]\n : [...records.values()].filter(\n (record) =>\n record.sessionId === match.sessionId && record.environment === match.environment && record.updateToken === undefined,\n )\n // Ambiguity is two devices waiting on one session, and guessing would paint the wrong phone.\n const target = candidates.length === 1 ? candidates[0] : undefined\n if (target === undefined || target.updateToken === updateToken) {\n return target\n }\n target.updateToken = updateToken\n target.phase = 'live'\n await persist()\n return target\n },\n async touch(deviceToken, sessionId, changes) {\n const record = records.get(keyOf(deviceToken, sessionId))\n if (record === undefined) {\n return\n }\n const structural = changes.phase !== undefined && changes.phase !== record.phase\n Object.assign(record, changes)\n if (structural) {\n await persist()\n }\n },\n async remove(deviceToken, sessionId) {\n if (!records.delete(keyOf(deviceToken, sessionId))) {\n return\n }\n await persist()\n },\n async removeByUpdateToken(updateToken) {\n let removed = false\n for (const [key, record] of records) {\n if (record.updateToken === updateToken) {\n records.delete(key)\n removed = true\n }\n }\n if (removed) {\n await persist()\n }\n },\n }\n}\n\nexport function createActivityRoute(registry: ActivityRegistry | null, authenticate: RequestAuthenticator): PushRouteHandler {\n return jsonPushRoute('/apns/activities', registry, authenticate, async (activities, req, res, body) => {\n const token = body.token\n if (typeof token !== 'string' || !TOKEN_PATTERN.test(token)) {\n respondJson(res, 400, { error: 'token must be a hex Live Activity update token' })\n return\n }\n\n if (req.method === 'DELETE') {\n await activities.removeByUpdateToken(token)\n res.writeHead(204).end()\n return\n }\n\n const sessionId = body.sessionId\n if (typeof sessionId !== 'string' || sessionId === '') {\n respondJson(res, 400, { error: 'sessionId is required' })\n return\n }\n if (!isEnvironment(body.environment)) {\n respondJson(res, 400, { error: \"environment must be 'development' or 'production'\" })\n return\n }\n const deviceToken = typeof body.deviceToken === 'string' && TOKEN_PATTERN.test(body.deviceToken) ? body.deviceToken : undefined\n\n const record = await activities.attach({ sessionId, deviceToken, environment: body.environment }, token)\n if (record === undefined) {\n // A card this gateway never raised. The app forgets the token rather than retrying, which is\n // what stops a phone with two gateways from POSTing at whichever one answers first.\n respondJson(res, 404, { error: 'no activity awaiting a token for this session' })\n return\n }\n respondJson(res, 200, { attached: true, sessionId })\n })\n}\n","import type { SessionNotificationType } from '@workerdeck/protocol'\nimport type { ApnsEnvironment } from './client.ts'\nimport {\n isEnvironment,\n jsonPushRoute,\n jsonRecordFile,\n TOKEN_PATTERN,\n type JsonRegistryOptions,\n type PushRouteHandler,\n} from './json-registry.ts'\nimport { respondJson, type RequestAuthenticator } from '../lib/http.ts'\n\nexport const NOTIFY_EVENTS: readonly SessionNotificationType[] = [\n 'permission_requested',\n 'turn_completed',\n 'session_error',\n 'session_closed',\n]\n\n// A session closing is bookkeeping, not news - it fires whenever a tab goes away - so it is out of\n// the default. The other three are what someone is actually waiting on. A device that says nothing\n// gets this; `[]` is how a device says \"no alerts at all\" without giving up its token.\nexport const DEFAULT_NOTIFY: readonly SessionNotificationType[] = ['permission_requested', 'turn_completed', 'session_error']\n\nexport type DeviceRecord = {\n token: string\n environment: ApnsEnvironment\n // The client's own id for this gateway, echoed back in every payload so an app with two gateways knows which woke it; never interpreted here.\n hostId?: string\n bundleId?: string\n platform?: string\n // The Live Activity push-to-start token: device-level like the alert token, and the only way a\n // gateway can raise a card on a phone whose app is not running. Absent means this device cannot\n // be started at - an older app, or Live Activities switched off in Settings.\n liveActivityStartToken?: string\n // Which notification types this device wants. Absent means `DEFAULT_NOTIFY`; an explicit empty\n // array means none. Per-device rather than per-gateway because a phone and an iPad watching the\n // same sessions do not want the same interruptions.\n notify?: SessionNotificationType[]\n updatedAt: number\n}\n\nexport type DeviceRegistry = {\n list(): DeviceRecord[]\n register(record: Omit<DeviceRecord, 'updatedAt'>): Promise<void>\n remove(token: string): Promise<void>\n clearStartToken(token: string): Promise<void>\n}\n\nconst FILENAME = 'apns-devices.json'\n// Every registered device is sent every session's notifications, and the file is rewritten per registration.\nexport const MAX_DEVICES = 64\n\nfunction isNotifyList(value: unknown): value is SessionNotificationType[] {\n return Array.isArray(value) && value.every((entry) => NOTIFY_EVENTS.includes(entry as SessionNotificationType))\n}\n\nfunction sameNotify(a: SessionNotificationType[] | undefined, b: SessionNotificationType[] | undefined): boolean {\n if (a === undefined || b === undefined) {\n return a === b\n }\n return a.length === b.length && a.every((entry, index) => entry === b[index])\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 && value.length <= 200 ? value : undefined\n}\n\nexport function wantsNotification(device: Pick<DeviceRecord, 'notify'>, type: SessionNotificationType): boolean {\n return (device.notify ?? DEFAULT_NOTIFY).includes(type)\n}\n\nexport async function createDeviceRegistry(options: JsonRegistryOptions): Promise<DeviceRegistry> {\n const file = jsonRecordFile(options, FILENAME, 'devices')\n const devices = new Map<string, DeviceRecord>()\n for (const record of (await file.load()) as DeviceRecord[]) {\n if (typeof record?.token === 'string' && isEnvironment(record.environment)) {\n devices.set(record.token, record)\n }\n }\n const persist = (): Promise<void> => file.save([...devices.values()])\n\n return {\n list: () => [...devices.values()],\n async register(record) {\n const existing = devices.get(record.token)\n devices.set(record.token, { ...record, updatedAt: Date.now() })\n if (\n existing !== undefined &&\n existing.environment === record.environment &&\n existing.hostId === record.hostId &&\n existing.liveActivityStartToken === record.liveActivityStartToken &&\n sameNotify(existing.notify, record.notify)\n ) {\n return\n }\n await persist()\n },\n async clearStartToken(token) {\n const existing = devices.get(token)\n if (existing === undefined || existing.liveActivityStartToken === undefined) {\n return\n }\n delete existing.liveActivityStartToken\n await persist()\n },\n async remove(token) {\n if (!devices.delete(token)) {\n return\n }\n await persist()\n },\n }\n}\n\nexport function createDeviceRoute(registry: DeviceRegistry | null, authenticate: RequestAuthenticator): PushRouteHandler {\n return jsonPushRoute('/apns/devices', registry, authenticate, async (devices, req, res, body) => {\n const token = body.token\n if (typeof token !== 'string' || !TOKEN_PATTERN.test(token)) {\n respondJson(res, 400, { error: 'token must be a hex APNs device token' })\n return\n }\n\n if (req.method === 'DELETE') {\n await devices.remove(token)\n res.writeHead(204).end()\n return\n }\n\n if (!isEnvironment(body.environment)) {\n respondJson(res, 400, { error: \"environment must be 'development' or 'production'\" })\n return\n }\n // Omitted leaves whatever is on record - an older app that never sends the field must not erase\n // it - while an explicit null is how the app says Live Activities were switched off.\n const startTokenGiven = Object.hasOwn(body, 'liveActivityStartToken')\n const startToken = body.liveActivityStartToken\n if (startTokenGiven && startToken !== null && (typeof startToken !== 'string' || !TOKEN_PATTERN.test(startToken))) {\n respondJson(res, 400, { error: 'liveActivityStartToken must be a hex token or null' })\n return\n }\n // Same three-state rule as the start token: omitted leaves the record alone, so an older app\n // that never sends the field keeps whatever it last chose rather than being reset to defaults.\n const notifyGiven = Object.hasOwn(body, 'notify')\n if (notifyGiven && !isNotifyList(body.notify)) {\n respondJson(res, 400, { error: `notify must be an array of ${NOTIFY_EVENTS.join(', ')}` })\n return\n }\n const previous = devices.list().find((record) => record.token === token)\n if (previous === undefined && devices.list().length >= MAX_DEVICES) {\n respondJson(res, 409, { error: `device limit reached (${MAX_DEVICES}); unregister one first` })\n return\n }\n const liveActivityStartToken = startTokenGiven ? ((startToken as string | null) ?? undefined) : previous?.liveActivityStartToken\n const notify = notifyGiven ? (body.notify as SessionNotificationType[]) : previous?.notify\n\n await devices.register({\n token,\n environment: body.environment,\n hostId: optionalString(body.hostId),\n bundleId: optionalString(body.bundleId),\n platform: optionalString(body.platform),\n ...(liveActivityStartToken === undefined ? {} : { liveActivityStartToken }),\n ...(notify === undefined ? {} : { notify }),\n })\n respondJson(res, 200, { registered: true, environment: body.environment })\n })\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createActivityRoute, type ActivityRegistry } from './activities.ts'\nimport { createDeviceRoute, type DeviceRegistry } from './devices.ts'\n\nexport type ApnsRouteHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean>\n\n// One object owns every `/apns/*` path in both states the gateway can be in. Passing null is a\n// gateway with no forwarder: the paths are still claimed, and answer 404 rather than falling\n// through to the dashboard's SPA catch-all, which serves GET and HEAD and would answer 405.\nexport function createApnsRoute(\n registries: { devices: DeviceRegistry; activities: ActivityRegistry } | null,\n authenticate: (req: IncomingMessage) => unknown,\n): ApnsRouteHandler {\n const device = createDeviceRoute(registries?.devices ?? null, authenticate)\n const activity = createActivityRoute(registries?.activities ?? null, authenticate)\n return async (req, res) => (await device(req, res)) || activity(req, res)\n}\n","import { createPrivateKey, type KeyObject, sign } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport { type ClientHttp2Session, connect, constants, type SecureClientSessionOptions } from 'node:http2'\n\nexport type ApnsEnvironment = 'development' | 'production'\n\nexport type ApnsConfig = {\n // A path, never the contents: a secret pasted into a config file is a secret in every backup of it.\n keyFile: string\n keyId: string\n teamId: string\n topic: string\n // Only the fallback for a device that registered without naming one; a device that named one is always routed by its answer.\n production?: boolean\n}\n\nconst HOSTS: Record<ApnsEnvironment, string> = {\n development: 'https://api.sandbox.push.apple.com',\n production: 'https://api.push.apple.com',\n}\n\nconst TOKEN_TTL_MS = 40 * 60 * 1000\nconst REQUEST_TIMEOUT_MS = 10_000\nconst DIAL_ATTEMPT_TIMEOUT_MS = 2_000\n\nexport type ApnsPushType = 'alert' | 'liveactivity'\n\nexport type ApnsRequest = {\n deviceToken: string\n environment: ApnsEnvironment\n payload: unknown\n // Live Activity pushes go to a different topic as well as a different type, and Apple rejects a\n // mismatched pair. Defaulting to 'alert' keeps every existing caller and its tests untouched.\n pushType?: ApnsPushType\n priority?: 5 | 10\n // Unix seconds after which Apple stops trying; 0 is not \"no expiry\" but \"attempt once, never store\".\n expiration?: number\n // Apple caps this at 64 bytes, so never pass an identifier of unbounded length.\n collapseId?: string\n}\n\nexport type ApnsResult =\n | { ok: true; apnsId?: string }\n | {\n ok: false\n status: number\n reason: string\n unregistered: boolean\n }\n\nexport type ApnsClient = {\n send(request: ApnsRequest): Promise<ApnsResult>\n close(): void\n}\n\nfunction topicFor(topic: string, pushType: ApnsPushType | undefined): string {\n return pushType === 'liveactivity' ? `${topic}.push-type.liveactivity` : topic\n}\n\nfunction base64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\nexport async function loadApnsKey(keyFile: string): Promise<KeyObject> {\n let pem: string\n try {\n pem = await readFile(keyFile, 'utf8')\n } catch (error) {\n throw new Error(`apns: cannot read the auth key at ${keyFile}: ` + `${error instanceof Error ? error.message : String(error)}`, {\n cause: error,\n })\n }\n let key: KeyObject\n try {\n key = createPrivateKey(pem)\n } catch (error) {\n throw new Error(`apns: ${keyFile} is not a private key (${error instanceof Error ? error.message : String(error)})`, { cause: error })\n }\n if (key.asymmetricKeyType !== 'ec') {\n throw new Error(\n `apns: ${keyFile} is a ${key.asymmetricKeyType ?? 'unknown'} key, not EC - an APNs auth ` +\n 'key is the .p8 downloaded from Keys in the developer portal, not a certificate',\n )\n }\n return key\n}\n\nexport function createProviderToken(key: KeyObject, keyId: string, teamId: string): { get(now?: number): string; invalidate(): void } {\n let cached: { token: string; issuedAt: number } | null = null\n return {\n get(now = Date.now()) {\n if (cached !== null && now - cached.issuedAt < TOKEN_TTL_MS) {\n return cached.token\n }\n const header = base64url(JSON.stringify({ alg: 'ES256', kid: keyId }))\n const claims = base64url(JSON.stringify({ iss: teamId, iat: Math.floor(now / 1000) }))\n const signingInput = `${header}.${claims}`\n const signature = sign('sha256', Buffer.from(signingInput), {\n key,\n dsaEncoding: 'ieee-p1363',\n })\n const token = `${signingInput}.${base64url(signature)}`\n cached = { token, issuedAt: now }\n return token\n },\n invalidate() {\n cached = null\n },\n }\n}\n\nfunction createSessionPool(hosts: Record<ApnsEnvironment, string>): {\n get(environment: ApnsEnvironment): ClientHttp2Session\n lastFailure(environment: ApnsEnvironment): string | undefined\n // Only ever called on a *connecting* session: destroying a connected one cancels its siblings mid-flight.\n discard(environment: ApnsEnvironment, session: ClientHttp2Session): void\n close(): void\n} {\n const sessions = new Map<ApnsEnvironment, ClientHttp2Session>()\n const failures = new Map<ApnsEnvironment, string>()\n const drop = (environment: ApnsEnvironment, session: ClientHttp2Session): void => {\n if (sessions.get(environment) === session) {\n sessions.delete(environment)\n }\n }\n return {\n get(environment) {\n const existing = sessions.get(environment)\n if (existing !== undefined && !existing.closed && !existing.destroyed) {\n return existing\n }\n const session = connect(hosts[environment], {\n // The cast is @types/node not surfacing the net/tls options http2 forwards.\n autoSelectFamilyAttemptTimeout: DIAL_ATTEMPT_TIMEOUT_MS,\n } as SecureClientSessionOptions)\n sessions.set(environment, session)\n failures.delete(environment)\n // Without this listener a GOAWAY-then-error takes the whole process down, and push is a side channel.\n session.on('error', (error) => {\n failures.set(environment, `${(error as NodeJS.ErrnoException).code ?? 'error'}: ${error.message}`)\n drop(environment, session)\n })\n session.on('goaway', (code, _lastStreamId, data) => {\n const detail = data !== undefined && data.length > 0 ? `: ${data.toString('utf8').slice(0, 200)}` : ''\n failures.set(environment, `GOAWAY ${code}${detail}`)\n drop(environment, session)\n })\n session.on('close', () => drop(environment, session))\n session.setTimeout(5 * 60_000, () => session.close())\n return session\n },\n lastFailure: (environment) => failures.get(environment),\n discard(environment, session) {\n drop(environment, session)\n if (!session.destroyed) {\n session.destroy()\n }\n },\n close() {\n for (const session of sessions.values()) {\n session.close()\n }\n sessions.clear()\n },\n }\n}\n\nfunction describeStreamError(error: Error): string {\n const cause = (error as Error & { cause?: unknown }).cause\n if (!(cause instanceof AggregateError)) {\n return error.message\n }\n const parts = cause.errors.map((inner) => (inner instanceof Error ? inner.message : String(inner))).filter((message) => message !== '')\n if (parts.length === 0) {\n return error.message\n }\n return `${error.message.replace(' (caused by: )', '')} (caused by: ${parts.join('; ')})`\n}\n\ntype Retry = 'never' | 'now' | 'redial'\n\ntype Attempt = { result: ApnsResult; retry: Retry }\n\nexport function createApnsClient(\n config: ApnsConfig,\n key: KeyObject,\n // Test seam only. The real endpoints are deliberately not configurable: redirecting them would exfiltrate every push.\n options: { hosts?: Record<ApnsEnvironment, string>; retryDelayMs?: number } = {},\n): ApnsClient {\n const providerToken = createProviderToken(key, config.keyId, config.teamId)\n const pool = createSessionPool(options.hosts ?? HOSTS)\n const retryDelayMs = options.retryDelayMs ?? 1000\n\n const post = (request: ApnsRequest, jwt: string): Promise<Attempt> =>\n new Promise((resolve) => {\n const body = Buffer.from(JSON.stringify(request.payload))\n const session = pool.get(request.environment)\n let stream: ReturnType<ClientHttp2Session['request']>\n try {\n stream = session.request({\n ':method': 'POST',\n ':path': `/3/device/${request.deviceToken}`,\n authorization: `bearer ${jwt}`,\n 'apns-topic': topicFor(config.topic, request.pushType),\n 'apns-push-type': request.pushType ?? 'alert',\n 'apns-priority': String(request.priority ?? 10),\n 'apns-expiration': String(request.expiration ?? 0),\n 'content-length': String(body.byteLength),\n ...(request.collapseId === undefined ? {} : { 'apns-collapse-id': request.collapseId }),\n })\n } catch (error) {\n resolve({\n result: {\n ok: false,\n status: 0,\n reason: error instanceof Error ? error.message : String(error),\n unregistered: false,\n },\n retry: 'now',\n })\n return\n }\n\n let status = 0\n let apnsId: string | undefined\n const chunks: Buffer[] = []\n let settled = false\n const settle = (result: ApnsResult, retry: Retry = 'never'): void => {\n if (settled) {\n return\n }\n settled = true\n resolve({ result, retry })\n }\n\n const transportRetry = (): Retry => {\n if (stream.pending) {\n return 'redial'\n }\n if (stream.rstCode === constants.NGHTTP2_REFUSED_STREAM) {\n return 'now'\n }\n return 'never'\n }\n const dropDoomedDial = (): void => {\n if (stream.pending && session.connecting) {\n pool.discard(request.environment, session)\n }\n }\n\n stream.setTimeout(REQUEST_TIMEOUT_MS, () => {\n const retry = transportRetry()\n dropDoomedDial()\n stream.close()\n settle({ ok: false, status: 0, reason: 'Timeout', unregistered: false }, retry)\n })\n stream.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n const id = headers['apns-id']\n apnsId = typeof id === 'string' ? id : undefined\n })\n stream.on('data', (chunk: Buffer) => chunks.push(chunk))\n stream.on('error', (error) => {\n const retry = transportRetry()\n dropDoomedDial()\n const cause = pool.lastFailure(request.environment)\n const message = describeStreamError(error)\n settle(\n {\n ok: false,\n status,\n reason: cause !== undefined ? `${cause} (stream: ${message})` : message,\n unregistered: false,\n },\n retry,\n )\n })\n stream.on('end', () => {\n if (status === 200) {\n settle({ ok: true, apnsId })\n return\n }\n let reason = `HTTP ${status}`\n try {\n const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8')) as { reason?: string }\n if (typeof parsed.reason === 'string') {\n reason = parsed.reason\n }\n } catch {}\n settle({\n ok: false,\n status,\n reason,\n unregistered: reason === 'Unregistered' || reason === 'BadDeviceToken',\n })\n })\n // 'end' never fires on a destroyed readable, so without this last-resort settle the send hangs forever.\n stream.on('close', () => {\n const retry = transportRetry()\n settle(\n {\n ok: false,\n status,\n reason: pool.lastFailure(request.environment) ?? `connection closed (RST code ${stream.rstCode})`,\n unregistered: false,\n },\n retry,\n )\n })\n stream.end(body)\n })\n\n const wait = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))\n\n return {\n async send(request) {\n let attempt = await post(request, providerToken.get())\n if (!attempt.result.ok && attempt.retry !== 'never') {\n if (attempt.retry === 'redial') {\n await wait(retryDelayMs)\n }\n attempt = await post(request, providerToken.get())\n }\n const first = attempt.result\n // The one *response* worth a retry: the cached JWT aged out against Apple's clock, so no duplicate is possible.\n if (!first.ok && first.reason === 'ExpiredProviderToken') {\n providerToken.invalidate()\n return (await post(request, providerToken.get())).result\n }\n return first\n },\n close: pool.close,\n }\n}\n","import { createHash } from 'node:crypto'\nimport {\n CHECKLIST_TEXT_MAX,\n isAgentRecord,\n parseUserQuestions,\n subagentLabel,\n type PermissionRequest,\n type SessionInfo,\n type SubagentInfo,\n} from '@workerdeck/protocol'\nimport type { ApnsRequest } from './client.ts'\n\n// The wire contract with `apps/ios/WorkerDeckKit/Sources/WorkerDeckActivity`. Every field name here\n// is decoded by name over there, and `packages/cli/test/live-activity.test.ts` writes the fixtures\n// that Swift test asserts against - rename a field on one side only and that test fails.\nexport type ActivityAttributes = {\n sessionId: string\n hostId?: string\n engine?: string\n cwdLeaf: string\n}\n\nexport type ActivityChoice = { index: number; label: string }\n\nexport type ActivityAgent = { name: string; state: SubagentInfo['status'] }\n\nexport type ActivityRequestState = {\n id: string\n kind: 'permission' | 'question' | 'plan'\n choices: ActivityChoice[]\n inputJSON?: string\n}\n\nexport type ActivityContentState = {\n phase: ActivityPhaseName\n title: string\n headline: string\n detail?: string\n startedAtMs: number\n expiresAtMs?: number\n pendingCount: number\n steps?: { done: number; total: number }\n agents?: ActivityAgent[]\n request?: ActivityRequestState\n epoch?: number\n seq?: number\n // Always null on the wire. The app writes a value locally between an intent's tap and the\n // server's answer, and every push from here is what clears it.\n decision: null\n}\n\nexport type ActivityPhaseName = 'running' | 'approval' | 'question' | 'plan' | 'done' | 'failed' | 'parked' | 'ended'\n\nexport type LiveActivityPushKind = 'start' | 'update' | 'end'\n\nconst MAX_PAYLOAD_BYTES = 3800\nconst LIMIT = { title: 60, headline: 120, detail: 200, choiceLabel: 40, choices: 4, agentName: 24, agents: 4 } as const\nconst STALE_MS = 20 * 60 * 1000\nconst DISMISSAL_MS = 15 * 60 * 1000\n// Constant, and deliberately short. \"Latest timestamp wins\" only holds among pushes still\n// deliverable when an offline phone reconnects, so an urgent update must not outlive the\n// resolution that followed it and re-raise an answered question.\nconst EXPIRATION_MS = 10 * 60 * 1000\nconst END_EXPIRATION_MS = 4 * 60 * 60 * 1000\n\nconst WORKING = 'Working…'\n\nconst RELEVANCE: Record<ActivityPhaseName, number> = {\n approval: 100,\n question: 100,\n plan: 100,\n running: 50,\n done: 10,\n failed: 10,\n parked: 10,\n ended: 10,\n}\n\n// Where a tool's one interesting argument lives, in the order a card should prefer them.\nconst PREVIEW_KEYS = ['command', 'file_path', 'path', 'pattern', 'url', 'notebook_path', 'prompt', 'description'] as const\n\nexport function clamp(text: string, limit: number): string {\n const flat = text.replace(/\\s+/g, ' ').trim()\n return flat.length <= limit ? flat : `${flat.slice(0, limit - 1)}…`\n}\n\nexport function sessionLabel(info: SessionInfo): string {\n const title = info.title?.trim()\n if (title !== undefined && title !== '') {\n return title\n }\n return cwdLeaf(info.cwd) || info.id\n}\n\nexport function cwdLeaf(cwd: string): string {\n return cwd.split('/').filter(Boolean).at(-1) ?? ''\n}\n\nfunction previewOf(input: Record<string, unknown>): string | undefined {\n for (const key of PREVIEW_KEYS) {\n const value = input[key]\n if (typeof value === 'string' && value.trim() !== '') {\n return value\n }\n }\n return undefined\n}\n\nfunction requestKind(request: PermissionRequest): ActivityRequestState['kind'] {\n if (request.toolName === 'ExitPlanMode') {\n return 'plan'\n }\n return request.toolName === 'AskUserQuestion' && parseUserQuestions(request.input).length > 0 ? 'question' : 'permission'\n}\n\n// Real option buttons only when the whole answer fits one tap: one question, single-select, and few\n// enough options to draw. Anything else gets the question text and \"Answer in app\", because a card\n// that answers the first of three questions and drops the rest is worse than one that opens the app.\nfunction choicesFor(request: PermissionRequest, kind: ActivityRequestState['kind']): ActivityChoice[] {\n if (kind !== 'question') {\n return []\n }\n const questions = parseUserQuestions(request.input)\n const [question] = questions\n if (questions.length !== 1 || question === undefined || question.multiSelect === true || question.options.length > LIMIT.choices) {\n return []\n }\n return question.options.map((option, index) => ({ index, label: clamp(option.label, LIMIT.choiceLabel) }))\n}\n\n// Running first, then the most recently started, because a finished agent is history and the card\n// has room for four names at most.\nfunction agentsFor(info: SessionInfo): ActivityAgent[] {\n const records = (info.subagents ?? []).filter(isAgentRecord)\n const ranked = [...records].sort((a, b) => {\n const running = Number(b.status === 'running') - Number(a.status === 'running')\n return running !== 0 ? running : b.startedAt - a.startedAt\n })\n return ranked.slice(0, LIMIT.agents).map((sub) => ({ name: clamp(subagentLabel(sub), LIMIT.agentName), state: sub.status }))\n}\n\n// Exactly one, never the first of several: with two items in flight the card cannot say which one\n// it is drawing, and a wrong specific line is worse than an honest \"Working…\".\nfunction activeChecklistText(info: SessionInfo): string | undefined {\n const active = (info.checklist ?? []).filter((item) => item.status === 'in_progress')\n if (active.length !== 1) {\n return undefined\n }\n const text = clamp(active[0]!.text, Math.min(LIMIT.headline, CHECKLIST_TEXT_MAX))\n return text === '' ? undefined : text\n}\n\nfunction phaseFor(info: SessionInfo, request: PermissionRequest | undefined): ActivityPhaseName {\n switch (info.status) {\n case 'awaiting_approval': {\n if (request === undefined) {\n return 'running'\n }\n const kind = requestKind(request)\n return kind === 'permission' ? 'approval' : kind\n }\n case 'parked': {\n return 'parked'\n }\n case 'failed': {\n return 'failed'\n }\n case 'closed': {\n return 'ended'\n }\n case 'idle': {\n return 'done'\n }\n default: {\n return 'running'\n }\n }\n}\n\nfunction headlineFor(info: SessionInfo, phase: ActivityPhaseName, request: PermissionRequest | undefined): string {\n if (request !== undefined && (phase === 'approval' || phase === 'question' || phase === 'plan')) {\n if (phase === 'question') {\n const [question] = parseUserQuestions(request.input)\n return clamp(question?.header !== undefined && question.header !== '' ? question.header : 'A question', LIMIT.headline)\n }\n if (phase === 'plan') {\n return 'Plan ready for review'\n }\n return clamp(request.title ?? request.displayName ?? request.toolName, LIMIT.headline)\n }\n if (phase === 'running') {\n return activeChecklistText(info) ?? WORKING\n }\n const finals: Partial<Record<ActivityPhaseName, string>> = {\n done: 'Turn finished',\n failed: 'Session failed',\n parked: 'Parked',\n ended: 'Session ended',\n }\n return finals[phase] ?? ''\n}\n\nfunction detailFor(phase: ActivityPhaseName, request: PermissionRequest | undefined): string | undefined {\n if (request === undefined) {\n return undefined\n }\n if (phase === 'question') {\n const [question] = parseUserQuestions(request.input)\n return question === undefined ? undefined : clamp(question.question, LIMIT.detail)\n }\n if (phase === 'approval' || phase === 'plan') {\n const preview = previewOf(request.input) ?? request.description\n return preview === undefined ? undefined : clamp(preview, LIMIT.detail)\n }\n return undefined\n}\n\nexport function attributesFor(info: SessionInfo, hostId: string | undefined): ActivityAttributes {\n return {\n sessionId: info.id,\n ...(hostId === undefined ? {} : { hostId }),\n ...(info.engine === undefined ? {} : { engine: info.engine }),\n cwdLeaf: cwdLeaf(info.cwd),\n }\n}\n\nexport function projectContentState(options: {\n info: SessionInfo\n request?: PermissionRequest\n startedAtMs: number\n finalPhase?: ActivityPhaseName\n finalHeadline?: string\n}): ActivityContentState {\n const { info, request, startedAtMs } = options\n const phase = options.finalPhase ?? phaseFor(info, request)\n const kind = request === undefined ? undefined : requestKind(request)\n const choices = request === undefined || kind === undefined ? [] : choicesFor(request, kind)\n const total = info.checklist?.length ?? 0\n const agents = agentsFor(info)\n\n return {\n phase,\n title: clamp(sessionLabel(info), LIMIT.title),\n headline: options.finalHeadline ?? headlineFor(info, phase, request),\n ...(detailFor(phase, request) === undefined ? {} : { detail: detailFor(phase, request) }),\n startedAtMs,\n ...(request?.expiresAt === undefined ? {} : { expiresAtMs: request.expiresAt }),\n pendingCount: info.pendingPermissionCount,\n ...(total === 0 ? {} : { steps: { done: info.checklist!.filter((item) => item.status === 'completed').length, total } }),\n ...(agents.length === 0 ? {} : { agents }),\n ...(request === undefined || kind === undefined\n ? {}\n : {\n request: {\n id: request.id,\n kind,\n choices,\n // Only worth carrying when it is what enables the buttons: an answer is encoded by\n // rewriting the original input, so without choices there is nothing to rewrite.\n ...(choices.length === 0 ? {} : { inputJSON: JSON.stringify(request.input) }),\n },\n }),\n ...(info.epoch === undefined ? {} : { epoch: info.epoch }),\n seq: info.lastSeq,\n decision: null,\n }\n}\n\n// Hashes what the card *draws*, so a push is only spent on a visible change. `seq` and `epoch` are\n// deep-link freight that moves on every event, `decision` is the phone's; hashing any of them would\n// make every tool call a push and defeat the throttle entirely.\nexport function contentHash(state: ActivityContentState): string {\n const { seq: _seq, epoch: _epoch, decision: _decision, ...drawn } = state\n return createHash('sha256').update(JSON.stringify(drawn)).digest('base64url').slice(0, 22)\n}\n\n// Shrinks in the order that costs the card least: the answer buttons go before any text does,\n// because a question the user has to open the app for still reads correctly.\nfunction shrink(state: ActivityContentState): ActivityContentState | undefined {\n if (state.request?.inputJSON !== undefined) {\n return { ...state, request: { ...state.request, choices: [], inputJSON: undefined } }\n }\n if (state.agents !== undefined) {\n return { ...state, agents: undefined }\n }\n if (state.detail !== undefined && state.detail.length > 24) {\n return { ...state, detail: clamp(state.detail, Math.floor(state.detail.length / 2)) }\n }\n if (state.detail !== undefined) {\n return { ...state, detail: undefined }\n }\n if (state.headline.length > 24) {\n return { ...state, headline: clamp(state.headline, Math.floor(state.headline.length / 2)) }\n }\n return undefined\n}\n\nexport function buildLiveActivityPush(\n kind: LiveActivityPushKind,\n options: {\n attributes: ActivityAttributes\n state: ActivityContentState\n urgent?: boolean\n now?: number\n },\n): Omit<ApnsRequest, 'deviceToken' | 'environment'> {\n const now = options.now ?? Date.now()\n const seconds = Math.floor(now / 1000)\n let state = options.state\n\n const build = (content: ActivityContentState): Record<string, unknown> => ({\n aps: {\n timestamp: seconds,\n event: kind,\n ...(kind === 'start'\n ? {\n 'attributes-type': 'SessionActivityAttributes',\n attributes: options.attributes,\n // Apple requires an alert on a start. Terse on purpose: the ordinary permission push\n // already rang the phone with its authenticated actions, and a second sound for the\n // same request is noise.\n alert: { title: content.title, body: content.phase === 'running' ? 'working' : 'approval needed' },\n 'input-push-token': 1,\n }\n : {}),\n 'content-state': content,\n ...(kind === 'end'\n ? { 'dismissal-date': Math.floor((now + DISMISSAL_MS) / 1000) }\n : { 'stale-date': Math.floor((now + STALE_MS) / 1000) }),\n 'relevance-score': RELEVANCE[content.phase],\n },\n })\n\n let payload = build(state)\n while (Buffer.byteLength(JSON.stringify(payload)) > MAX_PAYLOAD_BYTES) {\n const smaller = shrink(state)\n if (smaller === undefined) {\n break\n }\n state = smaller\n payload = build(state)\n }\n\n return {\n payload,\n pushType: 'liveactivity',\n priority: kind === 'update' && options.urgent !== true ? 5 : 10,\n expiration: Math.floor((now + (kind === 'end' ? END_EXPIRATION_MS : EXPIRATION_MS)) / 1000),\n }\n}\n","import { createHash } from 'node:crypto'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { SessionNotification, SessionNotificationType } from '@workerdeck/protocol'\nimport { type ApnsClient, type ApnsConfig, type ApnsEnvironment, createApnsClient, loadApnsKey, type ApnsRequest } from './client.ts'\nimport { createActivityRegistry, type ActivityRegistry } from './activities.ts'\nimport { createDeviceRegistry, wantsNotification, type DeviceRegistry } from './devices.ts'\nimport { buildLiveActivityPush, clamp, sessionLabel, type ActivityAttributes, type ActivityContentState } from './live-activity.ts'\nimport { createApnsRoute } from './routes.ts'\n\n// Under APNs' 4 KB payload cap, with room for the alert dictionary to grow.\nconst MAX_PAYLOAD_BYTES = 3800\nconst BODY_LIMIT = 300\n\nexport type ApnsForwarder = {\n onNotification: (notification: SessionNotification) => void\n handleRequest: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>\n deviceCount: () => number\n activity: {\n start: (attributes: ActivityAttributes, state: ActivityContentState) => void\n update: (sessionId: string, state: ActivityContentState, options: { urgent: boolean }) => void\n end: (sessionId: string, state: ActivityContentState) => void\n // Every card this gateway raised, ended. Run on boot against what the last process left behind,\n // and again on a graceful close - a card whose gateway is gone has no other way to learn it.\n endAll: (headline: string) => Promise<void>\n count: () => number\n }\n close: () => void\n}\n\n// Wire contract with the iOS app, which registers its Approve/Deny actions under these exact strings; a mismatch means a notification with no buttons.\nconst CATEGORY = {\n permission: 'PERMISSION_REQUEST',\n event: 'SESSION_EVENT',\n} as const\n\n// Hashed rather than truncated to 64 bytes: two session ids sharing a prefix must not collapse into each other.\nfunction collapseKey(sessionId: string): string {\n return createHash('sha256').update(sessionId).digest('base64url').slice(0, 32)\n}\n\n// One banner per session per kind. Every type collapses - a session that errors five times should\n// occupy one line on the lock screen, not five - but each kind keeps its own key, because an\n// arriving approval replacing a \"Turn finished\" for the same session would be different news\n// silently overwritten rather than a repeat folded away.\nconst COLLAPSE_PREFIX: Record<SessionNotificationType, string> = {\n permission_requested: 'p',\n turn_completed: 't',\n session_error: 'e',\n session_closed: 'c',\n}\n\nfunction titleFor(notification: SessionNotification, name: string): string {\n switch (notification.type) {\n case 'permission_requested': {\n return `Approval needed - ${name}`\n }\n case 'turn_completed': {\n return notification.result?.isError === true ? `Turn failed - ${name}` : name\n }\n case 'session_error': {\n return `Session error - ${name}`\n }\n case 'session_closed': {\n return `Session ended - ${name}`\n }\n default: {\n return name\n }\n }\n}\n\nfunction bodyFor(notification: SessionNotification): string {\n const preview = clamp(notification.preview ?? '', BODY_LIMIT)\n if (preview !== '') {\n return preview\n }\n switch (notification.type) {\n case 'permission_requested': {\n return 'The agent is waiting for your approval.'\n }\n case 'turn_completed': {\n return 'The turn finished.'\n }\n case 'session_closed': {\n return `Closed by the ${notification.reason ?? 'server'}.`\n }\n default: {\n return 'Something needs your attention.'\n }\n }\n}\n\n// The payload carries routing only - never transcript text - and `requestId` is what a lock-screen Approve has to POST to.\nexport function buildPush(notification: SessionNotification, hostId: string | undefined): Omit<ApnsRequest, 'deviceToken' | 'environment'> {\n const permission = notification.type === 'permission_requested'\n const name = sessionLabel(notification.session)\n let body = bodyFor(notification)\n\n const build = (text: string): Record<string, unknown> => ({\n aps: {\n alert: { title: titleFor(notification, name), body: text },\n sound: 'default',\n category: permission ? CATEGORY.permission : CATEGORY.event,\n 'thread-id': notification.sessionId,\n },\n type: notification.type,\n sessionId: notification.sessionId,\n seq: notification.seq,\n // What dates the seq: a wake renumbers the log, so a payload that outlived one must be\n // recognisable as stale rather than scroll the reader to whatever now sits at that number.\n ...(notification.session.epoch === undefined ? {} : { epoch: notification.session.epoch }),\n ...(hostId === undefined ? {} : { hostId }),\n ...(notification.request === undefined ? {} : { requestId: notification.request.id }),\n })\n\n let payload = build(body)\n while (Buffer.byteLength(JSON.stringify(payload)) > MAX_PAYLOAD_BYTES && body.length > 16) {\n body = clamp(body, Math.floor(body.length / 2))\n payload = build(body)\n }\n\n return {\n payload,\n priority: permission || notification.type === 'session_error' ? 10 : 5,\n // Expire an approval push exactly when the server would have timed the request out; a dead approval on a lock screen is worse than none.\n expiration:\n permission && notification.request?.expiresAt !== undefined\n ? Math.floor(notification.request.expiresAt / 1000)\n : Math.floor(Date.now() / 1000) + 3600,\n collapseId: `${COLLAPSE_PREFIX[notification.type]}:${collapseKey(notification.sessionId)}`,\n }\n}\n\nexport async function createApnsForwarder(options: {\n config: ApnsConfig\n stateDir: string | null\n authenticate: (req: IncomingMessage) => unknown\n warn?: (message: string) => void\n}): Promise<ApnsForwarder> {\n const warn = options.warn ?? ((message: string) => process.stderr.write(`[workerdeck] apns: ${message}\\n`))\n const key = await loadApnsKey(options.config.keyFile)\n const client: ApnsClient = createApnsClient(options.config, key)\n const registry: DeviceRegistry = await createDeviceRegistry({\n dir: options.stateDir,\n onError: (error, context) =>\n warn(`device registry ${context.op} failed for ${context.path}: ` + `${error instanceof Error ? error.message : String(error)}`),\n })\n const activities: ActivityRegistry = await createActivityRegistry({\n dir: options.stateDir,\n onError: (error, context) =>\n warn(`activity registry ${context.op} failed for ${context.path}: ` + `${error instanceof Error ? error.message : String(error)}`),\n })\n const handleRequest = createApnsRoute({ devices: registry, activities }, options.authenticate)\n const fallbackEnvironment = options.config.production === false ? 'development' : 'production'\n\n // Per-session delivery chain: `turn_completed` collapses, so an out-of-order pair leaves the older text on the lock screen.\n const chains = new Map<string, Promise<void>>()\n\n const deliver = async (notification: SessionNotification): Promise<void> => {\n const devices = registry.list().filter((device) => wantsNotification(device, notification.type))\n if (devices.length === 0) {\n return\n }\n await Promise.all(\n devices.map(async (device) => {\n const push = buildPush(notification, device.hostId)\n const result = await client.send({\n ...push,\n deviceToken: device.token,\n environment: device.environment ?? fallbackEnvironment,\n })\n if (result.ok) {\n return\n }\n if (result.unregistered) {\n await registry.remove(device.token)\n return\n }\n warn(`push to ${device.token.slice(0, 8)}… failed: ${result.reason} (${result.status})`)\n }),\n )\n }\n\n const sendActivity = async (\n kind: 'start' | 'update' | 'end',\n target: { deviceToken: string; token: string; environment: ApnsEnvironment; sessionId: string },\n attributes: ActivityAttributes,\n state: ActivityContentState,\n urgent: boolean,\n ): Promise<void> => {\n const push = buildLiveActivityPush(kind, { attributes, state, urgent })\n const result = await client.send({ ...push, deviceToken: target.token, environment: target.environment })\n if (result.ok) {\n await activities.touch(target.deviceToken, target.sessionId, { lastPushAt: Date.now() })\n return\n }\n if (result.unregistered) {\n // A start token dying takes the device's ability to raise cards with it; an update token\n // dying is just that one card, already gone from the phone.\n if (kind === 'start') {\n await registry.clearStartToken(target.deviceToken)\n }\n await activities.remove(target.deviceToken, target.sessionId)\n return\n }\n warn(`live activity ${kind} for ${target.sessionId} failed: ${result.reason} (${result.status})`)\n }\n\n const startCards = async (attributes: ActivityAttributes, state: ActivityContentState): Promise<void> => {\n await Promise.all(\n registry.list().map(async (device) => {\n if (device.liveActivityStartToken === undefined) {\n return\n }\n // One card per (device, session), and never a second start while a record exists: a phone\n // that has not yet reported its update token is not a phone that needs asking again.\n if (activities.get(device.token, attributes.sessionId) !== undefined) {\n return\n }\n const environment = device.environment ?? fallbackEnvironment\n await activities.start({\n deviceToken: device.token,\n sessionId: attributes.sessionId,\n hostId: device.hostId,\n environment,\n })\n await sendActivity(\n 'start',\n { deviceToken: device.token, token: device.liveActivityStartToken, environment, sessionId: attributes.sessionId },\n { ...attributes, hostId: device.hostId },\n state,\n true,\n )\n }),\n )\n }\n\n const pushCards = async (kind: 'update' | 'end', sessionId: string, state: ActivityContentState, urgent: boolean): Promise<void> => {\n await Promise.all(\n activities.forSession(sessionId).map(async (record) => {\n if (record.updateToken === undefined) {\n // Held rather than dropped: the phone is still waking up, and the next push after the\n // token lands carries the latest state anyway.\n return\n }\n await sendActivity(\n kind,\n { deviceToken: record.deviceToken, token: record.updateToken, environment: record.environment, sessionId },\n { sessionId, hostId: record.hostId, cwdLeaf: '' },\n state,\n urgent,\n )\n if (kind === 'end') {\n await activities.remove(record.deviceToken, sessionId)\n }\n }),\n )\n }\n\n const queueActivity = (sessionId: string, run: () => Promise<void>): void => {\n const previous = chains.get(sessionId) ?? Promise.resolve()\n const next = previous.then(() =>\n run().catch((error: unknown) => {\n warn(error instanceof Error ? error.message : String(error))\n }),\n )\n chains.set(sessionId, next)\n void next.then(() => {\n if (chains.get(sessionId) === next) {\n chains.delete(sessionId)\n }\n })\n }\n\n return {\n activity: {\n start(attributes, state) {\n queueActivity(attributes.sessionId, () => startCards(attributes, state))\n },\n update(sessionId, state, { urgent }) {\n queueActivity(sessionId, () => pushCards('update', sessionId, state, urgent))\n },\n end(sessionId, state) {\n queueActivity(sessionId, () => pushCards('end', sessionId, state, true))\n },\n async endAll(headline) {\n await Promise.all(\n activities.list().map(async (record) => {\n if (record.updateToken !== undefined) {\n await sendActivity(\n 'end',\n {\n deviceToken: record.deviceToken,\n token: record.updateToken,\n environment: record.environment,\n sessionId: record.sessionId,\n },\n { sessionId: record.sessionId, hostId: record.hostId, cwdLeaf: '' },\n {\n phase: 'ended',\n title: record.sessionId,\n headline,\n startedAtMs: record.startedAt,\n pendingCount: 0,\n decision: null,\n },\n true,\n )\n }\n await activities.remove(record.deviceToken, record.sessionId)\n }),\n )\n },\n count: () => activities.list().length,\n },\n onNotification(notification) {\n const previous = chains.get(notification.sessionId) ?? Promise.resolve()\n const next = previous.then(() =>\n deliver(notification).catch((error: unknown) => {\n warn(error instanceof Error ? error.message : String(error))\n }),\n )\n chains.set(notification.sessionId, next)\n void next.then(() => {\n if (chains.get(notification.sessionId) === next) {\n chains.delete(notification.sessionId)\n }\n })\n },\n handleRequest,\n deviceCount: () => registry.list().length,\n close: client.close,\n }\n}\n","import type { PermissionRequest, SessionEventBody, SessionInfo } from '@workerdeck/protocol'\nimport {\n attributesFor,\n contentHash,\n projectContentState,\n type ActivityAttributes,\n type ActivityContentState,\n type ActivityPhaseName,\n} from './live-activity.ts'\n\n// Swallows the transient `running` a session flashes at `system_init` before dropping back to idle.\n// Without it, creating a session without a prompt raises a card and ends it two seconds later.\nconst START_DEBOUNCE_MS = 2_000\n// A quick follow-up prompt keeps the card and its update token, which is worth more than ending\n// promptly: a start costs a token round-trip through the phone.\nconst END_GRACE_MS = 20_000\nconst FLUSH_DEBOUNCE_MS = 5_000\nconst MIN_UPDATE_INTERVAL_MS = 30_000\nconst HEARTBEAT_MS = 10 * 60 * 1000\n// The system stops accepting updates at 8 h. Renewing before that is cheaper than explaining a\n// card that silently stopped moving.\nconst RENEW_MS = 7 * 60 * 60 * 1000 + 45 * 60 * 1000\n\nconst ENGAGED = new Set<SessionInfo['status']>(['running', 'awaiting_approval'])\n\nconst RELEVANT = new Set<SessionEventBody['type']>([\n 'status_changed',\n 'permission_requested',\n 'permission_resolved',\n 'checklist',\n 'turn_result',\n 'session_error',\n 'session_closed',\n])\n\n// The slice of `Runner` this needs, spelled out so the tests can drive a fake one and the CLI never\n// depends on more of core than it reads.\nexport type ActivityRunner = {\n readonly id: string\n info(): SessionInfo\n readonly pendingApprovals: PermissionRequest[]\n subscribe(listener: (event: { type: string }) => void, afterSeq?: number): () => void\n}\n\nexport type ActivitySource = {\n observe(listener: (runner: ActivityRunner) => void): () => void\n}\n\n// What the forwarder does with a decision. Fan-out across devices, the registry and APNs all live\n// on the other side of this seam; the driver only decides *when* and *what*.\nexport type ActivityTarget = {\n start(attributes: ActivityAttributes, state: ActivityContentState): void\n update(sessionId: string, state: ActivityContentState, options: { urgent: boolean }): void\n end(sessionId: string, state: ActivityContentState): void\n}\n\ntype Tracked = {\n attributes: ActivityAttributes\n startedAtMs: number\n // A card the gateway has committed to. Updates before this are folded into `pending` instead.\n started: boolean\n pending?: ActivityContentState\n lastHash?: string\n lastSentAt: number\n lastRequestId?: string\n lastWaiting: boolean\n timers: Map<TimerName, ReturnType<typeof setTimeout>>\n}\n\ntype TimerName = 'start' | 'end' | 'flush' | 'heartbeat' | 'renew'\n\nfunction finalPhaseFor(status: SessionInfo['status']): ActivityPhaseName {\n switch (status) {\n case 'parked': {\n return 'parked'\n }\n case 'failed': {\n return 'failed'\n }\n case 'closed': {\n return 'ended'\n }\n default: {\n return 'done'\n }\n }\n}\n\nfunction isWaiting(phase: ActivityPhaseName): boolean {\n return phase === 'approval' || phase === 'question' || phase === 'plan'\n}\n\nexport function driveLiveActivities(options: {\n source: ActivitySource\n target: ActivityTarget\n hostIdOf?: () => string | undefined\n}): () => void {\n const sessions = new Map<string, Tracked>()\n const unsubscribes = new Set<() => void>()\n\n const clear = (tracked: Tracked, name: TimerName): void => {\n const handle = tracked.timers.get(name)\n if (handle !== undefined) {\n clearTimeout(handle)\n tracked.timers.delete(name)\n }\n }\n\n const arm = (tracked: Tracked, name: TimerName, delay: number, run: () => void): void => {\n clear(tracked, name)\n tracked.timers.set(\n name,\n setTimeout(() => {\n tracked.timers.delete(name)\n run()\n }, delay),\n )\n }\n\n const forget = (sessionId: string): void => {\n const tracked = sessions.get(sessionId)\n if (tracked === undefined) {\n return\n }\n for (const name of tracked.timers.keys()) {\n clear(tracked, name)\n }\n sessions.delete(sessionId)\n }\n\n const send = (runner: ActivityRunner, tracked: Tracked, state: ActivityContentState, urgent: boolean): void => {\n const hash = contentHash(state)\n if (hash === tracked.lastHash) {\n return\n }\n tracked.lastHash = hash\n tracked.lastSentAt = Date.now()\n tracked.pending = undefined\n clear(tracked, 'flush')\n tracked.attributes = attributesFor(runner.info(), options.hostIdOf?.())\n if (!tracked.started) {\n tracked.started = true\n options.target.start(tracked.attributes, state)\n } else {\n options.target.update(runner.id, state, { urgent })\n }\n arm(tracked, 'heartbeat', HEARTBEAT_MS, () => {\n const current = sessions.get(runner.id)\n if (current !== undefined && current.started) {\n // Refreshes `stale-date` only. Without it a live card dims after 20 minutes of a turn that\n // is genuinely just thinking.\n options.target.update(runner.id, { ...state, seq: runner.info().lastSeq }, { urgent: false })\n current.lastSentAt = Date.now()\n }\n })\n }\n\n const finish = (runner: ActivityRunner, tracked: Tracked, phase: ActivityPhaseName): void => {\n if (tracked.started) {\n options.target.end(runner.id, projectContentState({ info: runner.info(), startedAtMs: tracked.startedAtMs, finalPhase: phase }))\n }\n forget(runner.id)\n }\n\n const evaluate = (runner: ActivityRunner, immediate = false): void => {\n const info = runner.info()\n const request = runner.pendingApprovals[0]\n const now = Date.now()\n\n if (!ENGAGED.has(info.status)) {\n const tracked = sessions.get(info.id)\n if (tracked === undefined) {\n return\n }\n clear(tracked, 'start')\n if (info.status === 'idle') {\n arm(tracked, 'end', END_GRACE_MS, () => finish(runner, tracked, 'done'))\n return\n }\n finish(runner, tracked, finalPhaseFor(info.status))\n return\n }\n\n let tracked = sessions.get(info.id)\n if (tracked === undefined) {\n tracked = {\n attributes: attributesFor(info, options.hostIdOf?.()),\n startedAtMs: now,\n started: false,\n lastSentAt: 0,\n lastWaiting: false,\n timers: new Map(),\n }\n sessions.set(info.id, tracked)\n }\n const current = tracked\n clear(current, 'end')\n\n const state = projectContentState({ info, request, startedAtMs: current.startedAtMs })\n const waiting = isWaiting(state.phase)\n // A new request, or the card crossing between working and waiting, is what a person is waiting\n // to see. Everything else - a checklist tick, a tool title - can ride a coalesced push.\n const urgent = waiting !== current.lastWaiting || state.request?.id !== current.lastRequestId\n current.lastWaiting = waiting\n current.lastRequestId = state.request?.id\n\n if (!current.started && current.timers.get('start') === undefined) {\n current.pending = state\n if (waiting || immediate) {\n // The whole point of the card. Debouncing an approval would mean the phone learns about it\n // two seconds after the desktop does. A renewal skips the debounce too: the flash it exists\n // to swallow cannot happen to a session that has been running for eight hours.\n send(runner, current, state, true)\n arm(current, 'renew', RENEW_MS, () => renew(runner))\n } else {\n arm(current, 'start', START_DEBOUNCE_MS, () => {\n const pending = sessions.get(runner.id)\n if (pending === undefined || !ENGAGED.has(runner.info().status)) {\n return\n }\n send(runner, pending, pending.pending ?? state, true)\n arm(pending, 'renew', RENEW_MS, () => renew(runner))\n })\n }\n return\n }\n\n if (!current.started) {\n current.pending = state\n return\n }\n\n if (urgent) {\n send(runner, current, state, true)\n return\n }\n\n current.pending = state\n const since = now - current.lastSentAt\n const delay = Math.max(FLUSH_DEBOUNCE_MS, MIN_UPDATE_INTERVAL_MS - since)\n arm(current, 'flush', delay, () => {\n const pending = sessions.get(runner.id)\n if (pending?.pending !== undefined) {\n send(runner, pending, pending.pending, false)\n }\n })\n }\n\n // End and start again rather than let the system's 8 h ceiling stop the card mid-turn. The card\n // blinks once; the alternative is one that quietly stops telling the truth.\n const renew = (runner: ActivityRunner): void => {\n const tracked = sessions.get(runner.id)\n if (tracked === undefined || !ENGAGED.has(runner.info().status)) {\n return\n }\n finish(runner, tracked, 'ended')\n evaluate(runner, true)\n }\n\n const stop = options.source.observe((runner) => {\n const unsubscribe = runner.subscribe((event) => {\n if (!RELEVANT.has(event.type as SessionEventBody['type'])) {\n return\n }\n evaluate(runner)\n if (event.type === 'session_closed') {\n unsubscribe()\n unsubscribes.delete(unsubscribe)\n }\n }, runner.info().lastSeq)\n unsubscribes.add(unsubscribe)\n evaluate(runner)\n })\n\n return () => {\n stop()\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n unsubscribes.clear()\n for (const sessionId of sessions.keys()) {\n forget(sessionId)\n }\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nexport type MaterializedAuthKey = {\n key: string\n source: 'stored' | 'created' | 'ephemeral'\n path: string | null\n}\n\nfunction generateKey(): string {\n return randomBytes(24).toString('hex')\n}\n\n// One printable-ASCII line of at least 12 chars, so a truncated or garbage file regenerates rather than half-working.\nfunction usableStoredKey(raw: string): string | null {\n const line = raw.split('\\n', 1)[0]?.trim() ?? ''\n return line.length >= 12 && /^[\\x21-\\x7e]+$/.test(line) ? line : null\n}\n\nexport async function materializeAuthKey(\n stateDir: string | null,\n options: { warn?: (message: string) => void } = {},\n): Promise<MaterializedAuthKey> {\n const warn = options.warn ?? ((message: string) => process.stderr.write(`[workerdeck] ${message}\\n`))\n if (stateDir === null) {\n return { key: generateKey(), source: 'ephemeral', path: null }\n }\n\n const path = join(stateDir, 'auth-key')\n let raw: string | null = null\n try {\n raw = await readFile(path, 'utf8')\n } catch {\n raw = null\n }\n if (raw !== null) {\n const key = usableStoredKey(raw)\n if (key !== null) {\n try {\n const { mode } = await stat(path)\n if ((mode & 0o077) !== 0) {\n warn(`auth key file ${path} is readable by other users ` + `(mode ${(mode & 0o777).toString(8)}) - run: chmod 600 ${path}`)\n }\n } catch {}\n return { key, source: 'stored', path }\n }\n }\n\n const key = generateKey()\n await mkdir(stateDir, { recursive: true, mode: 0o700 })\n await writeFile(path, `${key}\\n`, { mode: 0o600 })\n // writeFile's mode applies only on creation, so regenerating over a corrupt file would inherit its looser bits.\n await chmod(path, 0o600)\n return { key, source: 'created', path }\n}\n","import { readFile, stat } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { writeJsonAtomic } from '@workerdeck/server'\nimport type { CliSessionStore, StoredSession } from './auth.ts'\n\nconst FORMAT_VERSION = 1\nconst FILE_NAME = 'auth-sessions.json'\n\nexport type AuthSessionStoreOptions = {\n stateDir: string\n warn?: (message: string) => void\n now?: () => number\n}\n\ntype FileShape = { version: number; sessions: [string, number][] }\n\nfunction parseSessions(raw: string, now: number): [string, StoredSession][] {\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return []\n }\n const file = parsed as Partial<FileShape>\n if (file?.version !== FORMAT_VERSION || !Array.isArray(file.sessions)) {\n return []\n }\n const entries: [string, StoredSession][] = []\n for (const entry of file.sessions) {\n if (!Array.isArray(entry) || entry.length !== 2) {\n continue\n }\n const [key, expiresAt] = entry\n if (typeof key !== 'string' || key === '') {\n continue\n }\n if (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt) || expiresAt <= now) {\n continue\n }\n entries.push([key, { expiresAt }])\n }\n return entries\n}\n\nexport async function createAuthSessionStore(options: AuthSessionStoreOptions): Promise<CliSessionStore> {\n const now = options.now ?? Date.now\n const warn = options.warn ?? ((message: string) => process.stderr.write(`[workerdeck] ${message}\\n`))\n const path = join(options.stateDir, FILE_NAME)\n\n let initial: [string, StoredSession][] = []\n try {\n const raw = await readFile(path, 'utf8')\n initial = parseSessions(raw, now())\n try {\n const { mode } = await stat(path)\n if ((mode & 0o077) !== 0) {\n warn(`session file ${path} is readable by other users ` + `(mode ${(mode & 0o777).toString(8)}) - run: chmod 600 ${path}`)\n }\n } catch {}\n } catch {\n initial = []\n }\n\n // One chain, so two mutations in the same tick cannot interleave their writes and land the older table last.\n let pending: Promise<void> = Promise.resolve()\n let warned = false\n\n const write = async (entries: [string, StoredSession][]): Promise<void> => {\n const body: FileShape = { version: FORMAT_VERSION, sessions: entries.map(([key, s]) => [key, s.expiresAt]) }\n try {\n await writeJsonAtomic(path, body, { trailingNewline: true })\n warned = false\n } catch (error) {\n if (!warned) {\n warned = true\n warn(`could not persist browser logins to ${path}: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n }\n\n return {\n initial,\n save(entries) {\n pending = pending.then(() => write(entries))\n },\n flush: () => pending,\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport type OriginPolicy = { trustProxy: boolean; allowedOrigins: Set<string> }\n\n// Tri-state on purpose: absence means a non-browser client, because a browser sends Origin on every cross-site POST\n// and every upgrade. Each call site decides what to make of that; `null` and unparseable are foreign.\nexport type OriginVerdict = 'absent' | 'ok' | 'foreign'\n\nconst SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])\n\nexport function parseAllowedOrigins(entries: string[]): Set<string> {\n return new Set(\n entries.map((entry) => {\n try {\n return new URL(entry).origin\n } catch {\n throw new Error(`createCliAuth: allowedOrigins entry is not a valid origin: ${JSON.stringify(entry)}`)\n }\n }),\n )\n}\n\nexport function isLoopbackAddress(ip: string): boolean {\n return ip === '::1' || ip.startsWith('127.') || ip.startsWith('::ffff:127.')\n}\n\n// The last value is the one the trusted proxy appended; every earlier position is client-writable.\nexport function forwardedLast(value: string | string[] | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n const joined = Array.isArray(value) ? value.join(',') : value\n const last = joined.split(',').at(-1)?.trim()\n return last === '' ? undefined : last\n}\n\nexport function isSecure(req: IncomingMessage, trustProxy: boolean): boolean {\n if ((req.socket as { encrypted?: boolean }).encrypted === true) {\n return true\n }\n return trustProxy && forwardedLast(req.headers['x-forwarded-proto'])?.toLowerCase() === 'https'\n}\n\nexport function clientIp(req: IncomingMessage, trustProxy: boolean): string {\n return (trustProxy ? forwardedLast(req.headers['x-forwarded-for']) : undefined) ?? req.socket.remoteAddress ?? 'unknown'\n}\n\nexport function isUpgradeRequest(req: IncomingMessage): boolean {\n const upgrade = req.headers.upgrade\n return typeof upgrade === 'string' && upgrade.toLowerCase().includes('websocket')\n}\n\nexport function isUnsafeMethod(req: IncomingMessage): boolean {\n return !SAFE_METHODS.has((req.method ?? 'GET').toUpperCase())\n}\n\nexport function originVerdict(req: IncomingMessage, policy: OriginPolicy): OriginVerdict {\n const raw = req.headers.origin\n if (raw === undefined) {\n return 'absent'\n }\n let origin: string\n try {\n origin = new URL(raw).origin\n } catch {\n return 'foreign'\n }\n if (policy.allowedOrigins.has(origin)) {\n return 'ok'\n }\n const expected = expectedOrigin(req, policy.trustProxy)\n return expected !== null && origin === expected ? 'ok' : 'foreign'\n}\n\n// A keyless gateway has no credential for a hostile page to lack, so where the request came from is the whole\n// defense: a foreign Origin, or a cross-site unsafe request with none, is a drive-by and never reaches `/v1`.\nexport function isCrossSiteBrowserRequest(req: IncomingMessage, policy: OriginPolicy): boolean {\n if (originVerdict(req, policy) === 'foreign') {\n return true\n }\n return (isUnsafeMethod(req) || isUpgradeRequest(req)) && req.headers['sec-fetch-site'] === 'cross-site'\n}\n\n// A cookie-authenticated request must never carry a foreign Origin, whatever the method. Unsafe methods and upgrades\n// additionally require Origin *present*: browsers always send it there, so absence means a non-browser client\n// replaying the cookie.\nexport function cookieOriginAccepted(req: IncomingMessage, policy: OriginPolicy): boolean {\n const verdict = originVerdict(req, policy)\n if (verdict === 'foreign') {\n return false\n }\n return !((isUpgradeRequest(req) || isUnsafeMethod(req)) && verdict !== 'ok')\n}\n\nfunction expectedOrigin(req: IncomingMessage, trustProxy: boolean): string | null {\n const host = (trustProxy ? forwardedLast(req.headers['x-forwarded-host']) : undefined) ?? req.headers.host\n if (host === undefined || host === '') {\n return null\n }\n try {\n return new URL(`${isSecure(req, trustProxy) ? 'https' : 'http'}://${host}`).origin\n } catch {\n return null\n }\n}\n","import { createHash, timingSafeEqual } from 'node:crypto'\nimport type { IncomingMessage } from 'node:http'\nimport { isUpgradeRequest } from './origin.ts'\n\nexport function sha256(value: string): Buffer {\n return createHash('sha256').update(value).digest()\n}\n\n// Secret and session tokens are only ever compared as fixed-length digests, so no path compares secret material with\n// an early exit.\nexport function secretMatcher(secret: string | undefined): (candidate: string) => boolean {\n const digest = secret === undefined ? undefined : sha256(secret)\n return (candidate) => digest !== undefined && timingSafeEqual(sha256(candidate), digest)\n}\n\nexport function headerSecret(req: IncomingMessage): string | undefined {\n const key = req.headers['x-workerdeck-key']\n if (typeof key === 'string' && key !== '') {\n return key\n }\n const authorization = req.headers.authorization\n if (typeof authorization === 'string') {\n const match = /^Bearer\\s+(.+)$/i.exec(authorization)\n if (match !== null) {\n return match[1]\n }\n }\n return undefined\n}\n\n// Upgrades only. A query-string key lands in proxy access logs, so a REST call carrying `?key=` is not\n// authenticated by it and must stay that way: a leaked URL then buys one attach and nothing more.\nexport function querySecret(req: IncomingMessage): string | undefined {\n if (!isUpgradeRequest(req)) {\n return undefined\n }\n const key = new URL(req.url ?? '/', 'http://internal').searchParams.get('key')\n return key !== null && key !== '' ? key : undefined\n}\n","import { createHmac, randomBytes } from 'node:crypto'\nimport type { IncomingMessage } from 'node:http'\nimport { isSecure } from './origin.ts'\nimport { sha256 } from './secret.ts'\n\nexport type StoredSession = { expiresAt: number }\n\nexport type CliSessionStore = {\n initial?: Iterable<[string, StoredSession]>\n save(entries: [string, StoredSession][]): void\n flush?(): Promise<void>\n}\n\nexport type CookieSessionOptions = {\n secret: string | undefined\n cookieName: string\n ttlMs: number\n trustProxy: boolean\n store?: CliSessionStore\n}\n\nexport type CookieSessions = CookieSessionOptions & { entries: Map<string, StoredSession> }\n\n// Only successful logins insert, so this cap fences the secret-holder's own memory use and nobody else's.\nconst MAX_SESSIONS = 100\n\nexport function createCookieSessions(options: CookieSessionOptions): CookieSessions {\n const entries = new Map<string, StoredSession>()\n for (const [key, entry] of options.store?.initial ?? []) {\n if (entries.size >= MAX_SESSIONS) {\n break\n }\n entries.set(key, { expiresAt: entry.expiresAt })\n }\n return { ...options, entries }\n}\n\nexport function mintSession(sessions: CookieSessions): string {\n if (sessions.entries.size >= MAX_SESSIONS) {\n const oldest = sessions.entries.keys().next().value\n if (oldest !== undefined) {\n sessions.entries.delete(oldest)\n }\n }\n const token = randomBytes(32).toString('base64url')\n sessions.entries.set(tokenKey(sessions, token), { expiresAt: Date.now() + sessions.ttlMs })\n persist(sessions)\n return token\n}\n\nexport function hasSession(sessions: CookieSessions, req: IncomingMessage): boolean {\n const token = cookieToken(sessions, req)\n if (token === undefined || token === '') {\n return false\n }\n const key = tokenKey(sessions, token)\n const entry = sessions.entries.get(key)\n if (entry === undefined) {\n return false\n }\n if (entry.expiresAt <= Date.now()) {\n sessions.entries.delete(key)\n persist(sessions)\n return false\n }\n return true\n}\n\n// Deleting the table row is the invalidation; clearing the cookie is only tidiness.\nexport function revokeSession(sessions: CookieSessions, req: IncomingMessage): void {\n const token = cookieToken(sessions, req)\n if (token !== undefined && token !== '' && sessions.entries.delete(tokenKey(sessions, token))) {\n persist(sessions)\n }\n}\n\nexport function setCookieValue(sessions: CookieSessions, token: string, req: IncomingMessage): string {\n return [`${sessions.cookieName}=${token}`, `Max-Age=${Math.ceil(sessions.ttlMs / 1000)}`, ...cookieAttributes(sessions, req)].join('; ')\n}\n\nexport function clearCookieValue(sessions: CookieSessions, req: IncomingMessage): string {\n return [`${sessions.cookieName}=`, 'Max-Age=0', ...cookieAttributes(sessions, req)].join('; ')\n}\n\nfunction persist(sessions: CookieSessions): void {\n sessions.store?.save([...sessions.entries])\n}\n\n// Keys are `HMAC-SHA256(secret, token)`; never \"simplify\" that to a plain digest (`docs/GOTCHAS.md`).\nfunction tokenKey(sessions: CookieSessions, token: string): string {\n return sessions.secret === undefined ? sha256(token).toString('hex') : createHmac('sha256', sessions.secret).update(token).digest('hex')\n}\n\nfunction cookieToken(sessions: CookieSessions, req: IncomingMessage): string | undefined {\n const header = req.headers.cookie\n if (typeof header !== 'string') {\n return undefined\n }\n for (const part of header.split(';')) {\n const eq = part.indexOf('=')\n if (eq === -1) {\n continue\n }\n if (part.slice(0, eq).trim() === sessions.cookieName) {\n return part.slice(eq + 1).trim()\n }\n }\n return undefined\n}\n\n// Lax over Strict: Strict drops the cookie on an external top-level navigation, and buys nothing the Origin check\n// does not already cover.\nfunction cookieAttributes(sessions: CookieSessions, req: IncomingMessage): string[] {\n const attrs = ['Path=/', 'HttpOnly', 'SameSite=Lax']\n if (isSecure(req, sessions.trustProxy)) {\n attrs.push('Secure')\n }\n return attrs\n}\n","export type ThrottleOptions = { windowMs: number; maxFailuresPerIp: number; maxFailuresGlobal: number }\n\ntype FailureWindow = { count: number; windowStart: number }\n\n// A global cap sits behind the per-IP one because rotating IPs is trivial over IPv6.\nexport type LoginThrottle = ThrottleOptions & { failures: Map<string, FailureWindow>; global: FailureWindow }\n\nconst PRUNE_ABOVE = 256\n\nexport function createLoginThrottle(options: ThrottleOptions): LoginThrottle {\n return { ...options, failures: new Map(), global: { count: 0, windowStart: 0 } }\n}\n\nexport function ipBlockedMs(throttle: LoginThrottle, ip: string, now: number): number {\n return blockedMs(throttle.failures.get(ip), throttle.maxFailuresPerIp, throttle.windowMs, now)\n}\n\nexport function loginBlockedMs(throttle: LoginThrottle, ip: string, now: number): number {\n return Math.max(ipBlockedMs(throttle, ip, now), blockedMs(throttle.global, throttle.maxFailuresGlobal, throttle.windowMs, now))\n}\n\nexport function recordFailure(throttle: LoginThrottle, ip: string, now: number, countGlobally = true): void {\n const { failures, windowMs } = throttle\n if (failures.size > PRUNE_ABOVE) {\n for (const [key, entry] of failures) {\n if (now - entry.windowStart >= windowMs) {\n failures.delete(key)\n }\n }\n }\n const entry = failures.get(ip)\n if (entry === undefined || now - entry.windowStart >= windowMs) {\n failures.set(ip, { count: 1, windowStart: now })\n } else {\n entry.count += 1\n }\n if (!countGlobally) {\n return\n }\n if (now - throttle.global.windowStart >= windowMs) {\n throttle.global.count = 1\n throttle.global.windowStart = now\n } else {\n throttle.global.count += 1\n }\n}\n\nexport function forgiveIp(throttle: LoginThrottle, ip: string): void {\n throttle.failures.delete(ip)\n}\n\nfunction blockedMs(entry: FailureWindow | undefined, max: number, windowMs: number, now: number): number {\n return entry !== undefined && entry.count >= max && now - entry.windowStart < windowMs ? entry.windowStart + windowMs - now : 0\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Authenticator } from '@workerdeck/server'\nimport { readBody, respondJson } from '../lib/http.ts'\nimport {\n clearCookieValue,\n createCookieSessions,\n hasSession,\n mintSession,\n revokeSession,\n setCookieValue,\n type CliSessionStore,\n type CookieSessions,\n} from './cookie-sessions.ts'\nimport {\n clientIp,\n cookieOriginAccepted,\n isCrossSiteBrowserRequest,\n isLoopbackAddress,\n originVerdict,\n parseAllowedOrigins,\n type OriginPolicy,\n} from './origin.ts'\nimport { headerSecret, querySecret, secretMatcher } from './secret.ts'\nimport { createLoginThrottle, forgiveIp, ipBlockedMs, loginBlockedMs, recordFailure, type LoginThrottle } from './throttle.ts'\n\nexport type { CliSessionStore, StoredSession } from './cookie-sessions.ts'\n\nexport type CliAuthOptions = {\n // Unset disables auth entirely; that the CLI then binds loopback only is enforced by the caller, not here.\n secret?: string\n // No `__Host-` prefix: it requires `Secure`, and plain-HTTP localhost is the primary deployment.\n cookieName?: string\n // Fixed, never sliding: the auth hooks only see the request, so a renewed cookie has nowhere to ride back on.\n ttlMs?: number\n // Attacker-writable on a directly exposed port, hence off by default - but behind TLS termination it must be on,\n // or `Secure` is skipped and the Origin check computes `http://` where the browser says `https://`.\n trustProxy?: boolean\n allowedOrigins?: string[]\n throttle?: { windowMs?: number; maxFailuresPerIp?: number; maxFailuresGlobal?: number }\n sessions?: CliSessionStore\n}\n\nexport type CliPrincipal = {\n via: 'header' | 'cookie' | 'open'\n // One secret, one trust level: whoever holds it is the operator (still bounded by `allowedConfigDirRoots`).\n canManageProfiles: true\n}\n\nexport type CliAuth = {\n enabled: boolean\n authenticate: Authenticator\n // Claims `/auth` and everything under it; the static host must call this before anything else.\n handleAuthRequest(req: IncomingMessage, res: ServerResponse): boolean | Promise<boolean>\n // Gating the SPA shell is UX, not security: every byte of data sits behind `authenticate`.\n hasValidSession(req: IncomingMessage): boolean\n loginPage(req: IncomingMessage): { action: string; field: string; error?: string }\n}\n\ntype AuthState = {\n enabled: boolean\n policy: OriginPolicy\n sessions: CookieSessions\n throttle: LoginThrottle\n secretMatches: (candidate: string) => boolean\n}\n\nconst MIN_SECRET_LENGTH = 12\nconst DEFAULT_COOKIE_NAME = 'workerdeck_session'\nconst DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000\nconst DEFAULT_THROTTLE_WINDOW_MS = 15 * 60 * 1000\nconst DEFAULT_MAX_FAILURES_PER_IP = 10\nconst DEFAULT_MAX_FAILURES_GLOBAL = 100\nconst MAX_LOGIN_BODY_BYTES = 4096\nconst OPEN_PRINCIPAL: CliPrincipal = { via: 'open', canManageProfiles: true }\n\nexport function createCliAuth(options: CliAuthOptions = {}): CliAuth {\n const state = authState(options)\n const { enabled } = state\n return {\n enabled,\n authenticate: (req) => authenticateRequest(state, req),\n handleAuthRequest: (req, res) => {\n let pathname: string\n try {\n pathname = new URL(req.url ?? '/', 'http://internal').pathname\n } catch {\n return false\n }\n // The whole prefix is claimed, unknown subpaths included, so nothing under it falls through to the SPA catch-all.\n if (pathname !== '/auth' && !pathname.startsWith('/auth/')) {\n return false\n }\n return handleAuthRoute(state, pathname, req, res).then(() => true)\n },\n hasValidSession: (req) => (enabled ? hasSession(state.sessions, req) : true),\n loginPage,\n }\n}\n\nfunction authState(options: CliAuthOptions): AuthState {\n const { secret } = options\n if (secret !== undefined && secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `createCliAuth: secret must be at least ${MIN_SECRET_LENGTH} characters - ` +\n 'use a long random value, or leave it unset to run without auth on loopback',\n )\n }\n const cookieName = options.cookieName ?? DEFAULT_COOKIE_NAME\n const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n if (!(ttlMs > 0)) {\n throw new Error('createCliAuth: ttlMs must be positive')\n }\n const trustProxy = options.trustProxy === true\n const throttle = createLoginThrottle({\n windowMs: options.throttle?.windowMs ?? DEFAULT_THROTTLE_WINDOW_MS,\n maxFailuresPerIp: options.throttle?.maxFailuresPerIp ?? DEFAULT_MAX_FAILURES_PER_IP,\n maxFailuresGlobal: options.throttle?.maxFailuresGlobal ?? DEFAULT_MAX_FAILURES_GLOBAL,\n })\n const policy: OriginPolicy = { trustProxy, allowedOrigins: parseAllowedOrigins(options.allowedOrigins ?? []) }\n return {\n enabled: secret !== undefined,\n policy,\n sessions: createCookieSessions({ secret, cookieName, ttlMs, trustProxy, store: options.sessions }),\n throttle,\n secretMatches: secretMatcher(secret),\n }\n}\n\nfunction authenticateRequest(state: AuthState, req: IncomingMessage): CliPrincipal | null {\n if (!state.enabled) {\n return isCrossSiteBrowserRequest(req, state.policy) ? null : OPEN_PRINCIPAL\n }\n // The secret is not ambient - the sender chose to attach it - so no Origin check applies, and a\n // present-but-wrong header is a rejection rather than a fall-through to the cookie.\n const provided = headerSecret(req) ?? querySecret(req)\n if (provided !== undefined) {\n return headerAttempt(state, req, provided) ? { via: 'header', canManageProfiles: true } : null\n }\n if (!hasSession(state.sessions, req)) {\n return null\n }\n return cookieOriginAccepted(req, state.policy) ? { via: 'cookie', canManageProfiles: true } : null\n}\n\n// Per-IP only, never the global budget: one stale client must not lock the operator's others out. Loopback is exempt\n// for the same reason, since a local caller already has the machine and a rotated key leaves every local client stale.\nfunction headerAttempt(state: AuthState, req: IncomingMessage, provided: string): boolean {\n const ip = clientIp(req, state.policy.trustProxy)\n const throttled = !isLoopbackAddress(ip)\n const now = Date.now()\n if (throttled && ipBlockedMs(state.throttle, ip, now) > 0) {\n return false\n }\n if (state.secretMatches(provided)) {\n return true\n }\n if (throttled) {\n recordFailure(state.throttle, ip, now, false)\n }\n return false\n}\n\nasync function handleAuthRoute(state: AuthState, pathname: string, req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (pathname === '/auth/status') {\n if (req.method !== 'GET') {\n respondJson(res, 405, { error: 'method not allowed' }, { allow: 'GET' })\n } else {\n respondJson(res, 200, { enabled: state.enabled, authenticated: state.enabled ? hasSession(state.sessions, req) : true })\n }\n return\n }\n if (pathname === '/auth/login') {\n if (req.method !== 'POST') {\n respondJson(res, 405, { error: 'method not allowed' }, { allow: 'POST' })\n } else {\n await handleLogin(state, req, res)\n }\n return\n }\n if (pathname === '/auth/logout') {\n if (req.method !== 'POST') {\n respondJson(res, 405, { error: 'method not allowed' }, { allow: 'POST' })\n } else {\n handleLogout(state, req, res)\n }\n return\n }\n respondJson(res, 404, { error: 'not found' })\n}\n\nasync function handleLogin(state: AuthState, req: IncomingMessage, res: ServerResponse): Promise<void> {\n const json = wantsJson(req)\n if (!state.enabled) {\n respondJson(res, 409, { error: 'auth is disabled: no secret is configured' })\n return\n }\n // Refused before touching the throttle, so a hostile page cannot burn a victim IP's budget.\n // Absent Origin stays allowed (curl-style provisioning): a browser forgery always carries one.\n if (originVerdict(req, state.policy) === 'foreign') {\n respondJson(res, 403, { error: 'origin not allowed' })\n return\n }\n const ip = clientIp(req, state.policy.trustProxy)\n const blocked = loginBlockedMs(state.throttle, ip, Date.now())\n if (blocked > 0) {\n const retryAfter = String(Math.ceil(blocked / 1000))\n if (json) {\n respondJson(res, 429, { error: 'too many failed attempts' }, { 'retry-after': retryAfter })\n } else {\n respondRedirect(res, '/?auth=throttled', { 'retry-after': retryAfter })\n }\n return\n }\n const candidate = await readLoginSecret(req, res)\n if (candidate === undefined) {\n return\n }\n if (!state.secretMatches(candidate)) {\n recordFailure(state.throttle, ip, Date.now())\n if (json) {\n respondJson(res, 401, { error: 'invalid secret' })\n } else {\n respondRedirect(res, '/?auth=failed')\n }\n return\n }\n forgiveIp(state.throttle, ip)\n const cookie = setCookieValue(state.sessions, mintSession(state.sessions), req)\n if (json) {\n res.writeHead(204, { 'set-cookie': cookie, 'cache-control': 'no-store' }).end()\n } else {\n respondRedirect(res, '/', { 'set-cookie': cookie })\n }\n}\n\nasync function readLoginSecret(req: IncomingMessage, res: ServerResponse): Promise<string | undefined> {\n const body = await readBody(req, MAX_LOGIN_BODY_BYTES)\n if (body === null) {\n respondJson(res, 413, { error: 'body too large' })\n // The unread rest of an oversized request would desync the next keep-alive exchange on this socket.\n res.once('finish', () => req.destroy())\n return undefined\n }\n const contentType = (req.headers['content-type'] ?? '').split(';')[0].trim().toLowerCase()\n let candidate: unknown\n if (contentType === 'application/x-www-form-urlencoded') {\n candidate = new URLSearchParams(body).get('secret')\n } else if (contentType === 'application/json') {\n try {\n candidate = (JSON.parse(body) as { secret?: unknown }).secret\n } catch {\n respondJson(res, 400, { error: 'invalid body' })\n return undefined\n }\n } else {\n respondJson(res, 415, { error: 'expected application/x-www-form-urlencoded or application/json' })\n return undefined\n }\n if (typeof candidate !== 'string' || candidate === '') {\n respondJson(res, 400, { error: 'missing secret' })\n return undefined\n }\n return candidate\n}\n\nfunction handleLogout(state: AuthState, req: IncomingMessage, res: ServerResponse): void {\n const json = wantsJson(req)\n if (state.enabled) {\n // A forged logout is a nuisance, not a breach, so an absent Origin is allowed here.\n if (originVerdict(req, state.policy) === 'foreign') {\n respondJson(res, 403, { error: 'origin not allowed' })\n return\n }\n revokeSession(state.sessions, req)\n }\n const cookie = clearCookieValue(state.sessions, req)\n if (json) {\n res.writeHead(204, { 'set-cookie': cookie, 'cache-control': 'no-store' }).end()\n } else {\n respondRedirect(res, '/', { 'set-cookie': cookie })\n }\n}\n\nfunction loginPage(req: IncomingMessage): { action: string; field: string; error?: string } {\n let reason: string | null = null\n try {\n reason = new URL(req.url ?? '/', 'http://internal').searchParams.get('auth')\n } catch {\n reason = null\n }\n const error =\n reason === 'failed'\n ? 'Invalid access key. Try again.'\n : reason === 'throttled'\n ? 'Too many failed attempts. Wait a few minutes, then try again.'\n : undefined\n return { action: '/auth/login', field: 'secret', error }\n}\n\nfunction respondRedirect(res: ServerResponse, location: string, headers?: Record<string, string>): void {\n res.writeHead(303, { location, 'cache-control': 'no-store', ...headers }).end()\n}\n\nfunction wantsJson(req: IncomingMessage): boolean {\n return (req.headers.accept ?? '').includes('application/json')\n}\n","import { existsSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ProfileInfo } from '@workerdeck/protocol'\nimport type { ShellAgentWriteOption, WorkerServerOptions } from '@workerdeck/server'\nimport type { ApnsConfig } from './apns/client.ts'\nimport type { CliAuthOptions } from './auth/auth.ts'\n\nconst CONFIG_BASENAMES = ['workerdeck.config.mjs', 'workerdeck.config.js', 'workerdeck.config.cjs']\n\nexport type WorkerDeckConfig = WorkerServerOptions & {\n port?: number\n host?: string\n // Ignored entirely when the file also supplies `authenticate`.\n auth?: CliAuthOptions\n stateDir?: string | null\n allowedHosts?: string[]\n // Binding a listed host waives the auth requirement, and while unauthenticated every entry is also\n // accepted as a Host header. Auth on widens nothing.\n insecureHosts?: string[]\n webRoot?: string\n web?: boolean\n // Hold the machine awake while a session is waiting on it. CLI-only by design: an embedding host owns its own\n // power policy and must not have it changed by a library it imported.\n keepAwake?: boolean\n // Refused unless auth is on: CORS on an open gateway lets any allowlisted page drive it with no credential.\n corsOrigins?: string[]\n apns?: ApnsConfig\n}\n\nexport type CliFlags = {\n config?: string\n port?: number\n host?: string\n authKey?: string\n profiles: ProfileInfo[]\n cwdRoots: string[]\n fsRoots: string[]\n profileRoots: string[]\n profileStore?: boolean\n fsWrite?: boolean\n shell?: boolean\n shellAgentWrite?: ShellAgentWriteOption\n allowedOrigins: string[]\n allowedHosts: string[]\n insecureHosts: string[]\n trustProxy?: boolean\n approvalTimeoutMs?: number | null\n engineSleepAfterMs?: number\n effortDefaults?: Record<string, string>\n agentContextReset?: 'on' | 'off' | 'never'\n stateDir?: string\n parking?: boolean\n insecure?: boolean\n web?: boolean\n keepAwake?: boolean\n corsOrigins: string[]\n open?: boolean\n hotReload?: boolean\n help?: boolean\n version?: boolean\n}\n\nexport class ConfigError extends Error {}\n\ntype FlagSwitch = (flags: CliFlags) => void\ntype FlagValue = (flags: CliFlags, value: string, name: string) => void\n\nconst SWITCHES = new Map<string, FlagSwitch>([\n ['-h', (f) => (f.help = true)],\n ['--help', (f) => (f.help = true)],\n ['-v', (f) => (f.version = true)],\n ['--version', (f) => (f.version = true)],\n ['--no-profile-store', (f) => (f.profileStore = false)],\n ['--fs-write', (f) => (f.fsWrite = true)],\n ['--shell', (f) => (f.shell = true)],\n ['--trust-proxy', (f) => (f.trustProxy = true)],\n ['--no-parking-store', (f) => (f.parking = false)],\n ['--insecure', (f) => (f.insecure = true)],\n ['--no-web', (f) => (f.web = false)],\n ['--no-keep-awake', (f) => (f.keepAwake = false)],\n ['--open', (f) => (f.open = true)],\n ['--hot-reload', (f) => (f.hotReload = true)],\n])\n\nconst VALUED = new Map<string, FlagValue>([\n ['-c', (f, v) => (f.config = v)],\n ['--config', (f, v) => (f.config = v)],\n ['-p', (f, v, name) => (f.port = parsePort(v, name))],\n ['--port', (f, v, name) => (f.port = parsePort(v, name))],\n ['--host', (f, v) => (f.host = v)],\n ['--auth-key', (f, v) => (f.authKey = v)],\n ['--profile', (f, v) => f.profiles.push(parseProfile(v))],\n ['--profile-root', (f, v) => f.profileRoots.push(resolve(v))],\n ['--cwd-root', (f, v) => f.cwdRoots.push(resolve(v))],\n ['--fs-root', (f, v) => f.fsRoots.push(resolve(v))],\n ['--shell-agent-write', (f, v, name) => (f.shellAgentWrite = parseShellAgentWrite(v, name))],\n ['--allowed-origin', (f, v) => f.allowedOrigins.push(v)],\n ['--allowed-host', (f, v) => f.allowedHosts.push(v)],\n ['--insecure-host', (f, v) => f.insecureHosts.push(v)],\n ['--approval-timeout', (f, v, name) => (f.approvalTimeoutMs = parseDuration(v, name))],\n ['--engine-sleep-after', (f, v, name) => (f.engineSleepAfterMs = parseDuration(v, name) ?? 0)],\n ['--effort-default', (f, v, name) => (f.effortDefaults = { ...f.effortDefaults, ...parseEffortDefault(v, name) })],\n ['--agent-context-reset', (f, v, name) => (f.agentContextReset = parseAgentContextReset(v, name))],\n ['--state-dir', (f, v) => (f.stateDir = resolve(v))],\n ['--cors-origin', (f, v) => f.corsOrigins.push(v)],\n])\n\n// `none`, `0` and anything non-positive all mean the same thing: a prompt that never expires.\nfunction parseDuration(raw: string, source: string): number | null {\n if (raw === 'none' || raw === 'never') {\n return null\n }\n const match = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h)?$/.exec(raw)\n if (!match) {\n throw new ConfigError(`${source}: expected a duration like 300000, 30s, 5m - or 'none'; got: ${raw}`)\n }\n const scale = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 }[match[2] ?? 'ms']!\n const ms = Number(match[1]) * scale\n return ms > 0 ? ms : null\n}\n\nfunction parseEffortDefault(raw: string, source: string): Record<string, string> {\n const match = /^([^=\\s]+)=([^=\\s]+)$/.exec(raw)\n if (!match) {\n throw new ConfigError(`${source}: expected <model>=<effort>, like opus=high; got: ${raw}`)\n }\n return { [match[1]!]: match[2]! }\n}\n\nfunction parseAgentContextReset(raw: string, source: string): 'on' | 'off' | 'never' {\n if (raw === 'on' || raw === 'off' || raw === 'never') {\n return raw\n }\n throw new ConfigError(`${source}: expected on, off or never; got: ${raw}`)\n}\n\nfunction parseShellAgentWrite(value: string, name: string): ShellAgentWriteOption {\n if (value === 'read-only' || value === 'gated' || value === 'allow') {\n return value\n }\n throw new ConfigError(`${name} must be read-only, gated or allow (got '${value}')`)\n}\n\nfunction parsePort(raw: string, source: string): number {\n const port = Number(raw)\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new ConfigError(`${source}: not a valid port: ${raw}`)\n }\n return port\n}\n\nexport function parseArgs(argv: string[]): CliFlags {\n const flags: CliFlags = {\n profiles: [],\n profileRoots: [],\n cwdRoots: [],\n fsRoots: [],\n allowedOrigins: [],\n allowedHosts: [],\n insecureHosts: [],\n corsOrigins: [],\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!\n const toggle = SWITCHES.get(arg)\n if (toggle) {\n toggle(flags)\n continue\n }\n const take = VALUED.get(arg)\n if (!take) {\n throw new ConfigError(`unknown option: ${arg}`)\n }\n const value = argv[i + 1]\n if (value === undefined || value.startsWith('-')) {\n throw new ConfigError(`${arg} requires a value`)\n }\n take(flags, value, arg)\n i++\n }\n return flags\n}\n\n// A config dir is a credential store, so naming one stays deliberate and is never inferred.\nfunction parseProfile(raw: string): ProfileInfo {\n const eq = raw.indexOf('=')\n if (eq <= 0) {\n throw new ConfigError(`--profile expects name=dir, got: ${raw}`)\n }\n const name = raw.slice(0, eq)\n const dir = raw.slice(eq + 1)\n if (!dir) {\n throw new ConfigError(`--profile ${name}= is missing a directory`)\n }\n return { name, configDir: resolve(dir) }\n}\n\nexport type LoadedConfig = {\n path: string | null\n options: WorkerDeckConfig\n}\n\n// An implicit config is looked up in cwd only: walking parents would make what a command does depend on where it was run.\nexport async function loadConfigFile(explicit?: string, cwd = process.cwd()): Promise<LoadedConfig> {\n let path: string | null = null\n if (explicit) {\n path = isAbsolute(explicit) ? explicit : resolve(cwd, explicit)\n if (!existsSync(path)) {\n throw new ConfigError(`no config file at ${path}`)\n }\n } else {\n path = CONFIG_BASENAMES.map((name) => join(cwd, name)).find((p) => existsSync(p)) ?? null\n }\n if (!path) {\n return { path: null, options: {} }\n }\n\n let mod: { default?: unknown }\n try {\n // Non-literal so no bundler resolves it. The mtime query makes a hot reload see an edited file past the ESM cache.\n mod = (await import(`${pathToFileURL(path).href}?mtime=${statSync(path).mtimeMs}`)) as { default?: unknown }\n } catch (error) {\n throw new ConfigError(`failed to load ${path}: ${error instanceof Error ? error.message : String(error)}`)\n }\n const exported = mod.default\n if (exported === undefined) {\n throw new ConfigError(`${path} has no default export (expected WorkerServerOptions)`)\n }\n const options = typeof exported === 'function' ? await (exported as () => unknown)() : exported\n if (typeof options !== 'object' || options === null) {\n throw new ConfigError(`${path} default export is not an options object`)\n }\n return { path, options: options as WorkerDeckConfig }\n}\n\nconst LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost', '::ffff:127.0.0.1'])\n\nexport function isLoopback(host: string): boolean {\n return LOOPBACK.has(host)\n}\n\nexport type ResolvedConfig = {\n port: number\n host: string\n authKey?: string\n auth: CliAuthOptions\n stateDir: string | null\n configPath: string | null\n hostAuthenticates: boolean\n // A promise rather than a key, because resolution stays I/O-free; `startInstance` must keep it.\n generateAuthKey: boolean\n // Host header values to accept, or null to accept any; non-null only for an unauthenticated instance.\n allowedHosts: Set<string> | null\n webRoot?: string\n web: boolean\n keepAwake: boolean\n corsOrigins: string[]\n apns?: ApnsConfig\n open: boolean\n // Runtime profile CRUD over /v1/profiles. The store itself is opened by `startInstance`, which owns the state dir.\n profileStore: boolean\n options: WorkerServerOptions\n}\n\nexport function defaultStateDir(configPath: string | null): string {\n return configPath ? join(dirname(configPath), '.workerdeck') : join(homedir(), '.workerdeck')\n}\n\nexport function hostnameOf(hostHeader: string): string {\n try {\n return new URL(`http://${hostHeader}`).hostname.replace(/^\\[|\\]$/g, '').toLowerCase()\n } catch {\n return ''\n }\n}\n\nexport function isLoopbackHostname(hostname: string): boolean {\n if (LOOPBACK.has(hostname)) {\n return true\n }\n return /^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)\n}\n\n// An entry with a port could never match what the guard compares, so it is rejected rather than normalized into a gate\n// that looks armed and is not. Bare IPv6 is bracketed first because WHATWG URL requires it.\nfunction normalizeInsecureHost(raw: string): string {\n const entry = raw.trim()\n const looksLikeNamePort = /^[^:]+:\\d+$/.test(entry)\n const candidate = entry.includes(':') && !entry.startsWith('[') && !looksLikeNamePort ? `[${entry}]` : entry\n let url: URL\n try {\n url = new URL(`http://${candidate}`)\n } catch {\n throw new ConfigError(`not a host name or address in insecureHosts: ${JSON.stringify(raw)}`)\n }\n if (url.port !== '') {\n throw new ConfigError(\n `insecureHosts entry ${JSON.stringify(raw)} carries a port - name the host alone; ` +\n `the bind host and the Host header are both compared portless`,\n )\n }\n return url.hostname.replace(/^\\[|\\]$/g, '').toLowerCase()\n}\n\nexport function resolveInstanceConfig(\n flags: CliFlags,\n loaded: LoadedConfig,\n env: NodeJS.ProcessEnv = process.env,\n cwd = process.cwd(),\n): ResolvedConfig {\n const envPort = env.WORKERDECK_PORT ? parsePort(env.WORKERDECK_PORT, 'WORKERDECK_PORT') : undefined\n const port = flags.port ?? envPort ?? loaded.options.port ?? 8787\n const host = flags.host ?? env.WORKERDECK_HOST ?? loaded.options.host ?? '127.0.0.1'\n // `||`, not `??`: an empty secret is not a secret - `WORKERDECK_AUTH_KEY=` means \"unset\".\n const authKey = flags.authKey || env.WORKERDECK_AUTH_KEY || loaded.options.auth?.secret || undefined\n const hostAuthenticates = typeof loaded.options.authenticate === 'function'\n\n const insecureHosts = new Set([...flags.insecureHosts, ...(loaded.options.insecureHosts ?? [])].map(normalizeInsecureHost))\n const bindHost = host\n .trim()\n .replace(/^\\[|\\]$/g, '')\n .toLowerCase()\n\n // An unauthenticated gateway on a routable interface is a shell for anyone who can reach the port, so serving one\n // takes an explicit opt-out; absent all three of them auth goes on and a key is generated.\n const generateAuthKey =\n !authKey &&\n !hostAuthenticates &&\n !loaded.options.allowUnauthenticated &&\n !isLoopback(host) &&\n !flags.insecure &&\n !insecureHosts.has(bindHost)\n\n const stateDir =\n flags.parking === false || loaded.options.stateDir === null\n ? null\n : (flags.stateDir ?? env.WORKERDECK_STATE_DIR ?? loaded.options.stateDir ?? defaultStateDir(loaded.path))\n\n const envRoots = (name: string): string[] | undefined =>\n env[name]\n ?.split(':')\n .filter(Boolean)\n .map((p) => resolve(cwd, p))\n const cwdRoots = flags.cwdRoots.length ? flags.cwdRoots : envRoots('WORKERDECK_CWD_ROOTS')\n const fsRoots = flags.fsRoots.length ? flags.fsRoots : envRoots('WORKERDECK_FS_ROOTS')\n\n const auth: CliAuthOptions = {\n ...loaded.options.auth,\n secret: authKey,\n trustProxy: flags.trustProxy ?? loaded.options.auth?.trustProxy,\n allowedOrigins: [...(loaded.options.auth?.allowedOrigins ?? []), ...flags.allowedOrigins],\n }\n\n // `generateAuthKey` counts as auth here on the promise that `startInstance` materializes the key, which it asserts.\n const authEnabled = Boolean(authKey) || hostAuthenticates || generateAuthKey\n const allowedHosts = authEnabled\n ? null\n : new Set([\n ...LOOPBACK,\n ...[...flags.allowedHosts, ...(loaded.options.allowedHosts ?? [])].map((name) => name.toLowerCase()),\n ...insecureHosts,\n ])\n\n const web = flags.web ?? loaded.options.web ?? true\n const keepAwake = flags.keepAwake ?? loaded.options.keepAwake ?? true\n\n const corsOrigins = flags.corsOrigins.length ? flags.corsOrigins : (loaded.options.corsOrigins ?? [])\n for (const origin of corsOrigins) {\n // Exactness is the guarantee: a wildcard hands the API to every page that can reach this port.\n if (origin === '*') {\n throw new ConfigError('--cors-origin does not accept \"*\"')\n }\n let parsed: URL\n try {\n parsed = new URL(origin)\n } catch {\n throw new ConfigError(`--cors-origin expects a full origin, got: ${origin}`)\n }\n if (parsed.origin !== origin) {\n throw new ConfigError(`--cors-origin expects scheme://host[:port] with no path, got: ${origin}`)\n }\n }\n if (!web && flags.open) {\n throw new ConfigError('--open cannot be used with --no-web: there is no dashboard to open')\n }\n\n const {\n port: _p,\n host: _h,\n auth: _a,\n stateDir: _s,\n allowedHosts: _ah,\n insecureHosts: _ih,\n webRoot: _w,\n web: _web,\n keepAwake: _ka,\n corsOrigins: _cors,\n apns: _ap,\n ...serverOptions\n } = loaded.options\n const options: WorkerServerOptions = { ...serverOptions }\n if (cwdRoots?.length) {\n options.allowedCwdRoots = cwdRoots\n }\n if (fsRoots?.length || flags.fsWrite) {\n options.hostFiles = {\n ...loaded.options.hostFiles,\n ...(fsRoots?.length ? { roots: fsRoots } : {}),\n ...(flags.fsWrite ? { write: true } : {}),\n }\n }\n if (flags.shell || flags.shellAgentWrite !== undefined) {\n options.shell = {\n ...loaded.options.shell,\n ...(flags.shell ? { enabled: true } : {}),\n ...(flags.shellAgentWrite !== undefined ? { agentWrite: flags.shellAgentWrite } : {}),\n }\n }\n if (loaded.options.relay) {\n options.relay = {\n ...loaded.options.relay,\n key: env.WORKERDECK_RELAY_KEY || loaded.options.relay.key || undefined,\n log: loaded.options.relay.log ?? ((message) => process.stderr.write(`[workerdeck] ${message}\\n`)),\n }\n }\n // Flags replace rather than merge: a half-declared profile set is a credential mix-up.\n if (flags.profiles.length) {\n options.profiles = flags.profiles\n }\n if (flags.approvalTimeoutMs !== undefined) {\n options.approvalTimeoutMs = flags.approvalTimeoutMs\n }\n if (flags.engineSleepAfterMs !== undefined) {\n options.engineSleepAfterMs = flags.engineSleepAfterMs\n }\n if (flags.agentContextReset === 'never') {\n options.agentContextReset = false\n } else if (flags.agentContextReset !== undefined) {\n options.agentContextReset = { ...loaded.options.agentContextReset, default: flags.agentContextReset === 'on' }\n }\n if (flags.effortDefaults) {\n options.effortDefaults = { ...loaded.options.effortDefaults, ...flags.effortDefaults }\n }\n // --no-profile-store wins over a config file's own store: a flag that says \"off\" must not leave one wired up.\n if (flags.profileStore === false) {\n delete options.profileStore\n }\n const manageProfiles = flags.profileStore !== false\n // The home directory, because an operator principal already has full authority over this machine - it can start an\n // agent with bypassPermissions in any cwd. What this guard can meaningfully refuse is /etc and another user's home.\n if (manageProfiles && !options.allowedConfigDirRoots) {\n options.allowedConfigDirRoots = flags.profileRoots.length ? flags.profileRoots : [homedir()]\n }\n // `startInstance` opens the file store only when nothing else supplied one.\n const profileStore = manageProfiles && options.profileStore === undefined\n\n return {\n port,\n host,\n authKey,\n auth,\n stateDir,\n configPath: loaded.path,\n hostAuthenticates,\n generateAuthKey,\n allowedHosts,\n webRoot: loaded.options.webRoot,\n web,\n keepAwake,\n corsOrigins,\n apns: resolveApns(loaded),\n open: flags.open ?? false,\n profileStore,\n options,\n }\n}\n\n// A relative `keyFile` resolves against the config file's directory, never the cwd, so a deployment stays self-contained.\nfunction resolveApns(loaded: LoadedConfig): ApnsConfig | undefined {\n const apns = loaded.options.apns\n if (!apns) {\n return undefined\n }\n for (const field of ['keyFile', 'keyId', 'teamId', 'topic'] as const) {\n if (typeof apns[field] !== 'string' || apns[field] === '') {\n throw new ConfigError(`apns.${field} is required when apns is configured`)\n }\n }\n const base = loaded.path ? dirname(loaded.path) : process.cwd()\n return { ...apns, keyFile: resolve(base, apns.keyFile) }\n}\n","export type LoginPageOptions = {\n action: string\n field: string\n error?: string\n redirectTo?: string\n redirectField?: string\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c] ?? c)\n}\n\nexport function renderLoginPage(options: LoginPageOptions): string {\n const { action, field, error, redirectTo, redirectField } = options\n const hidden =\n redirectField && redirectTo ? `<input type=\"hidden\" name=\"${escapeHtml(redirectField)}\" value=\"${escapeHtml(redirectTo)}\">` : ''\n const alert = error ? `<p class=\"error\" role=\"alert\">${escapeHtml(error)}</p>` : ''\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<title>workerdeck</title>\n<style>\n :root {\n color-scheme: light dark;\n --bg: #fbfaf9; --fg: #1c1917; --muted: #78716c;\n --card: #ffffff; --border: #e7e5e4; --accent: #c2410c; --error: #b91c1c;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #1c1917; --fg: #fafaf9; --muted: #a8a29e;\n --card: #292524; --border: #44403c; --accent: #fb923c; --error: #fca5a5;\n }\n }\n * { box-sizing: border-box; }\n body {\n margin: 0; min-height: 100dvh; display: grid; place-items: center; padding: 1.5rem;\n background: var(--bg); color: var(--fg);\n font: 15px/1.5 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n }\n form {\n width: 100%; max-width: 22rem; background: var(--card);\n border: 1px solid var(--border); border-radius: 12px; padding: 1.75rem;\n }\n h1 { margin: 0 0 .25rem; font-size: 1.05rem; letter-spacing: -0.01em; }\n p.sub { margin: 0 0 1.25rem; color: var(--muted); font-size: .875rem; }\n label { display: block; font-size: .8125rem; font-weight: 500; margin-bottom: .375rem; }\n input[type=password] {\n width: 100%; padding: .5rem .625rem; font: inherit; color: inherit;\n background: var(--bg); border: 1px solid var(--border); border-radius: 7px;\n }\n input[type=password]:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }\n button {\n width: 100%; margin-top: 1rem; padding: .5rem .75rem; font: inherit; font-weight: 500;\n color: var(--bg); background: var(--fg); border: 0; border-radius: 7px; cursor: pointer;\n }\n button:hover { opacity: .9; }\n .error { margin: 0 0 1rem; color: var(--error); font-size: .8125rem; }\n</style>\n</head>\n<body>\n <form method=\"post\" action=\"${escapeHtml(action)}\">\n <h1>workerdeck</h1>\n <p class=\"sub\">This instance is protected. Enter its access key to continue.</p>\n ${alert}\n <label for=\"key\">Access key</label>\n <input id=\"key\" name=\"${escapeHtml(field)}\" type=\"password\" autocomplete=\"current-password\"\n autofocus required spellcheck=\"false\">\n ${hidden}\n <button type=\"submit\">Sign in</button>\n </form>\n</body>\n</html>\n`\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport type { SessionInfo } from '@workerdeck/protocol'\n\n// The states that are waiting on this machine: a turn in flight, and a session blocked on the operator, who is\n// coming back to it and would find the socket gone. Deliberately the same set `workerdeck guard` calls busy.\nconst AWAKE_STATUSES = new Set<SessionInfo['status']>(['starting', 'running', 'awaiting_approval'])\n\nexport type WakeLock = {\n readonly held: boolean\n set: (wanted: boolean) => void\n release: () => void\n}\n\nexport function sessionsNeedTheMachine(sessions: readonly SessionInfo[]): boolean {\n return sessions.some((session) => AWAKE_STATUSES.has(session.status))\n}\n\n// Both platforms hold the lock for as long as a child process lives, and both tie that child's life to *this* pid\n// rather than to a kill we might never get to make: `caffeinate -w` waits on a pid, and the inhibited command polls\n// one. So a SIGKILLed gateway releases within seconds instead of pinning the machine awake until reboot.\nfunction lockCommand(pid: number): { command: string; args: string[] } | null {\n if (process.platform === 'darwin') {\n // -i is idle sleep only. Never -d: keeping the screen lit is not what was asked for.\n return { command: 'caffeinate', args: ['-i', '-w', String(pid)] }\n }\n if (process.platform === 'linux') {\n return {\n command: 'systemd-inhibit',\n args: [\n '--what=idle:sleep',\n '--mode=block',\n '--who=workerdeck',\n '--why=a turn is running',\n 'sh',\n '-c',\n `while kill -0 ${pid} 2>/dev/null; do sleep 5; done`,\n ],\n }\n }\n return null\n}\n\nexport type WakeSource = {\n list: () => SessionInfo[]\n observe: (\n listener: (runner: {\n subscribe: (listener: (event: { type: string }) => void, afterSeq?: number) => unknown\n info: () => SessionInfo\n }) => void,\n ) => unknown\n}\n\n// Recomputed from the registry rather than counted up and down. A refcount has to be decremented on every exit path\n// a session can take - closed, failed, parked, evicted, drained - and one missed path pins the machine awake until\n// the gateway stops. Derived state cannot leak that way, so the sweep below is insurance, not the mechanism.\nexport function driveWakeLock(source: WakeSource, lock: Pick<WakeLock, 'set'>): void {\n const sync = (): void => lock.set(sessionsNeedTheMachine(source.list()))\n source.observe((runner) =>\n // Handed back, not dropped: a runner carried to another generation by a hot reload would otherwise keep\n // syncing a lock that belongs to a process state this observer no longer describes.\n runner.subscribe((event) => {\n if (event.type === 'status_changed' || event.type === 'session_closed' || event.type === 'session_error') {\n sync()\n }\n }, runner.info().lastSeq),\n )\n sync()\n}\n\nexport function createWakeLock(options: { pid?: number; onUnavailable?: (reason: string) => void } = {}): WakeLock {\n const plan = lockCommand(options.pid ?? process.pid)\n let child: ChildProcess | null = null\n let available = plan !== null\n\n const stop = (): void => {\n const running = child\n child = null\n running?.kill()\n }\n\n const start = (): void => {\n if (!plan || !available || child) {\n return\n }\n let spawned: ChildProcess\n try {\n spawned = spawn(plan.command, plan.args, { stdio: 'ignore' })\n } catch {\n available = false\n options.onUnavailable?.(`could not spawn ${plan.command}`)\n return\n }\n child = spawned\n // A missing binary is not an error - a box with neither tool must start and run normally - but stop trying.\n spawned.on('error', () => {\n if (child === spawned) {\n child = null\n }\n available = false\n options.onUnavailable?.(`${plan.command} is not available on this machine`)\n })\n spawned.on('exit', () => {\n if (child === spawned) {\n child = null\n }\n })\n // Never the reason the gateway cannot exit.\n spawned.unref()\n }\n\n return {\n get held(): boolean {\n return child !== null\n },\n set: (wanted) => (wanted ? start() : stop()),\n release: stop,\n }\n}\n","import { createHash } from 'node:crypto'\nimport { createReadStream } from 'node:fs'\nimport { readFile, stat } from 'node:fs/promises'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { join, resolve, sep } from 'node:path'\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 '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.wasm': 'application/wasm',\n '.txt': 'text/plain; charset=utf-8',\n}\n\nexport function contentTypeFor(pathname: string): string {\n const dot = pathname.lastIndexOf('.')\n if (dot < 0) {\n return 'application/octet-stream'\n }\n return CONTENT_TYPES[pathname.slice(dot).toLowerCase()] ?? 'application/octet-stream'\n}\n\nexport function looksLikeAsset(pathname: string): boolean {\n const dot = pathname.lastIndexOf('.')\n if (dot < 0) {\n return false\n }\n return pathname.slice(dot).toLowerCase() in CONTENT_TYPES\n}\n\nexport function resolveWithinRoot(root: string, pathname: string): string | null {\n let decoded: string\n try {\n decoded = decodeURIComponent(pathname)\n } catch {\n return null\n }\n if (decoded.includes('\\0')) {\n return null\n }\n // Never normalise `decoded` first: that collapses an absolute pathname's leading `..` segments and rebases the\n // escape attempt inside the root, leaving the containment check below with nothing to catch.\n const candidate = resolve(join(root, decoded))\n const base = resolve(root)\n if (candidate !== base && !candidate.startsWith(base + sep)) {\n return null\n }\n return candidate\n}\n\n// Images stay on this origin (plus loopback gateways, which a page cannot exfiltrate to), so markdown in a transcript\n// cannot beacon anywhere; `connect-src` stays open because the dashboard talks to other gateways by design. Inline\n// scripts are admitted by hash, computed from the document actually served.\nexport function dashboardCsp(html: string): string {\n const hashes = [...html.matchAll(/<script(?![^>]*\\bsrc=)[^>]*>([\\s\\S]*?)<\\/script>/gi)].map(\n (match) => `'sha256-${createHash('sha256').update(match[1]!, 'utf8').digest('base64')}'`,\n )\n return [\n \"default-src 'self'\",\n [\"script-src 'self' 'wasm-unsafe-eval'\", ...hashes].join(' '),\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: blob: http://127.0.0.1:* http://localhost:*\",\n \"font-src 'self' data:\",\n \"connect-src 'self' http: https: ws: wss:\",\n \"worker-src 'self' blob:\",\n \"object-src 'none'\",\n \"base-uri 'self'\",\n \"form-action 'self'\",\n \"frame-ancestors 'none'\",\n ].join('; ')\n}\n\nfunction htmlHeaders(html: string): Record<string, string> {\n return {\n 'content-security-policy': dashboardCsp(html),\n // The dashboard holds an ambient session cookie, so framing it elsewhere is only ever clickjacking.\n 'x-frame-options': 'DENY',\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'same-origin',\n }\n}\n\nexport function sendHtml(req: IncomingMessage, res: ServerResponse, status: number, html: string, cache: string): void {\n const body = Buffer.from(html, 'utf8')\n res.writeHead(status, {\n 'content-type': 'text/html; charset=utf-8',\n 'content-length': body.byteLength,\n 'cache-control': cache,\n ...htmlHeaders(html),\n })\n res.end(req.method === 'HEAD' ? undefined : body)\n}\n\nexport type ServeResult = 'served' | 'not-found' | 'method-not-allowed'\n\nexport async function serveFile(\n req: IncomingMessage,\n res: ServerResponse,\n filePath: string,\n options: { immutable?: boolean } = {},\n): Promise<ServeResult> {\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n return 'method-not-allowed'\n }\n\n let size: number\n try {\n const info = await stat(filePath)\n if (!info.isFile()) {\n return 'not-found'\n }\n size = info.size\n } catch {\n return 'not-found'\n }\n\n const contentType = contentTypeFor(filePath)\n if (contentType.startsWith('text/html')) {\n const html = await readFile(filePath, 'utf8').catch(() => undefined)\n if (html === undefined) {\n return 'not-found'\n }\n sendHtml(req, res, 200, html, options.immutable ? 'public, max-age=31536000, immutable' : 'no-cache, must-revalidate')\n return 'served'\n }\n res.writeHead(200, {\n 'content-type': contentType,\n 'content-length': size,\n 'cache-control': options.immutable ? 'public, max-age=31536000, immutable' : 'no-cache, must-revalidate',\n 'x-content-type-options': 'nosniff',\n })\n if (req.method === 'HEAD') {\n res.end()\n return 'served'\n }\n await new Promise<void>((resolvePromise) => {\n const stream = createReadStream(filePath)\n stream.on('error', () => {\n res.destroy()\n resolvePromise()\n })\n stream.on('end', () => resolvePromise())\n stream.pipe(res)\n })\n return 'served'\n}\n","import { existsSync } from 'node:fs'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { join } from 'node:path'\nimport {\n createFileProfileStore,\n createFileSessionStore,\n createFileSpendStore,\n createWorkerServer,\n type CarriedSession,\n type WorkerServer,\n type WorkerServerOptions,\n} from '@workerdeck/server'\nimport { dashboardDir } from '@workerdeck/web'\nimport { createApnsRoute } from '../apns/routes.ts'\nimport { createApnsForwarder } from '../apns/forwarder.ts'\nimport { driveLiveActivities } from '../apns/live-activities.ts'\nimport { materializeAuthKey, type MaterializedAuthKey } from '../auth/auth-key.ts'\nimport { createAuthSessionStore } from '../auth/auth-sessions.ts'\nimport { createCliAuth, type CliAuth } from '../auth/auth.ts'\nimport { hostnameOf, isLoopbackHostname, type ResolvedConfig } from '../config.ts'\nimport { renderLoginPage } from '../auth/login-page.ts'\nimport { createWakeLock, driveWakeLock, sessionsNeedTheMachine } from './keep-awake.ts'\nimport { looksLikeAsset, resolveWithinRoot, sendHtml, serveFile } from './static.ts'\n\n// The backstop, not the mechanism: the lock is driven by session transitions, and this only has to catch a release\n// that no event announced. Long, because holding a laptop awake for one extra minute is the cheap failure.\nconst WAKE_SWEEP_MS = 60_000\n\nexport type StartOptions = {\n quiet?: boolean\n // Live sessions handed over from a previous generation of this process. See `docs/DEVELOPMENT.md` §Hot reload.\n carried?: CarriedSession[]\n}\n\nexport type Instance = {\n server: WorkerServer\n // The carried ids this instance took on. Short of the handed-over list when one of them ended while it was held.\n adopted: string[]\n url: string\n port: number\n closed: Promise<void>\n // Let running turns finish before `close()`. See `WorkerServer.drain`.\n drain: WorkerServer['drain']\n close: () => Promise<void>\n}\n\nexport function resolveWebRoot(): string {\n if (existsSync(join(dashboardDir, 'index.html'))) {\n return dashboardDir\n }\n throw new Error(`no dashboard build at ${dashboardDir}\\n` + ` in a checkout: pnpm --filter @workerdeck/web run build`)\n}\n\n// Loopback *names* are what is checked, never the socket: a DNS-rebinding attacker's connection really does arrive\n// on 127.0.0.1, and the browser's Host header is the one thing they cannot control.\nexport function createHostGuard(allowedHosts: Set<string> | null): (req: IncomingMessage) => boolean {\n if (allowedHosts === null) {\n return () => true\n }\n return (req) => {\n const header = req.headers.host\n // No Host at all is an HTTP/1.0 client or a script, never a browser being driven cross-origin.\n if (header === undefined) {\n return true\n }\n const hostname = hostnameOf(header)\n if (hostname === '') {\n return false\n }\n return isLoopbackHostname(hostname) || allowedHosts.has(hostname)\n }\n}\n\n// Push is server-wide: a registered phone hears about every session, so only an operator may aim one. Mirrors the\n// server's own `isOperator`, since a declared session policy withdraws the unscoped-means-operator default.\nexport function isOperatorPrincipal(principal: unknown, policyDeclared: boolean): boolean {\n if (principal === null || principal === undefined || principal === false) {\n return false\n }\n const { operator, scope } = (typeof principal === 'object' ? principal : {}) as { operator?: unknown; scope?: unknown }\n if (typeof operator === 'boolean') {\n return operator\n }\n const scoped = typeof scope === 'object' && scope !== null && Object.keys(scope).length > 0\n return !scoped && !policyDeclared\n}\n\n// The order here is the contract: auth endpoints first (they are how a browser gets a session at all), then the APNs\n// route, then assets - ungated, being the app's own code - and documents last, the one place the auth decision is made.\nfunction createFallback(\n auth: CliAuth,\n webRoot: string | undefined,\n hostAllowed: (req: IncomingMessage) => boolean,\n apnsRoute: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>,\n): (req: IncomingMessage, res: ServerResponse) => Promise<void> {\n return async (req, res) => {\n if (!hostAllowed(req)) {\n res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' })\n res.end(\n 'unrecognised Host header.\\n\\nThis instance runs without auth, so it only answers to ' +\n 'loopback host names and the ones it was told to expect. Declare this name with ' +\n '--insecure-host <name> (which also lets it be bound without a key) or --allowed-host ' +\n '<name>, or set --auth-key.\\n',\n )\n return\n }\n if (await auth.handleAuthRequest(req, res)) {\n return\n }\n if (await apnsRoute(req, res)) {\n return\n }\n\n // Below the auth and APNs routes, because turning the *dashboard* off must not turn off the gateway's own surfaces.\n if (webRoot === undefined) {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' })\n res.end('the web dashboard is disabled on this gateway\\n')\n return\n }\n\n const pathname = new URL(req.url ?? '/', 'http://internal').pathname\n\n if (looksLikeAsset(pathname)) {\n const filePath = resolveWithinRoot(webRoot, pathname)\n if (!filePath) {\n res.writeHead(400, { 'content-type': 'text/plain; charset=utf-8' })\n res.end('bad request')\n return\n }\n const result = await serveFile(req, res, filePath, {\n immutable: pathname.startsWith('/assets/'),\n })\n if (result === 'served') {\n return\n }\n if (result === 'method-not-allowed') {\n res.writeHead(405, { allow: 'GET, HEAD' })\n res.end()\n return\n }\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' })\n res.end('not found')\n return\n }\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n res.writeHead(405, { allow: 'GET, HEAD' })\n res.end()\n return\n }\n\n if (auth.enabled && !auth.hasValidSession(req)) {\n sendHtml(req, res, 401, renderLoginPage(auth.loginPage(req)), 'no-store')\n return\n }\n\n const entry = join(webRoot, 'index.html')\n const result = await serveFile(req, res, entry, { immutable: false })\n if (result !== 'served') {\n res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' })\n res.end('dashboard build is missing its entry document')\n }\n }\n}\n\nexport async function startInstance(config: ResolvedConfig, options: StartOptions = {}): Promise<Instance> {\n const webRoot = config.web ? (config.webRoot ?? resolveWebRoot()) : undefined\n const generated: MaterializedAuthKey | null =\n config.generateAuthKey && !config.hostAuthenticates ? await materializeAuthKey(config.stateDir) : null\n const authOptions = config.hostAuthenticates\n ? { ...config.auth, secret: undefined }\n : generated\n ? { ...config.auth, secret: generated.key }\n : config.auth\n const sessions =\n authOptions.secret !== undefined && config.stateDir ? await createAuthSessionStore({ stateDir: config.stateDir }) : undefined\n const auth = createCliAuth(sessions ? { ...authOptions, sessions } : authOptions)\n // Keep this assert: it is the only thing between \"the config stood the Host-header guard down expecting auth\" and\n // \"createCliAuth got no secret\", which is an instance wide open while reporting itself authenticated.\n if (config.allowedHosts === null && !config.hostAuthenticates && !auth.enabled) {\n throw new Error(\n 'refusing to serve: the resolved config expects auth but no shared secret was ' +\n 'materialized - this instance would be open while believing itself authenticated',\n )\n }\n // Sibling of the assert above: CORS on an open gateway lets any allowlisted page drive it with no credential.\n if (config.corsOrigins.length > 0 && !config.hostAuthenticates && !auth.enabled) {\n throw new Error(\n 'refusing to serve: corsOrigins is set but this instance has no auth - ' +\n 'a cross-origin page could drive it with no credential. Set --auth-key.',\n )\n }\n const hostAllowed = createHostGuard(config.allowedHosts)\n\n // The same hook `/v1` runs, so a config file's own `authenticate` guards push too rather than the (then open) built-in.\n const authenticateRequest = config.hostAuthenticates ? config.options.authenticate! : auth.authenticate\n const policyDeclared = config.options.authorizeSession !== undefined\n const authenticatePush = async (req: IncomingMessage): Promise<unknown> => {\n if (!hostAllowed(req)) {\n return null\n }\n const principal = await authenticateRequest(req)\n return isOperatorPrincipal(principal, policyDeclared) ? principal : null\n }\n // A bad key path throws before we listen: better a refusal at startup than a phone that never buzzes.\n const apns =\n config.apns === undefined\n ? undefined\n : await createApnsForwarder({\n config: config.apns,\n stateDir: config.stateDir,\n authenticate: authenticatePush,\n })\n\n const fallback = createFallback(auth, webRoot, hostAllowed, apns?.handleRequest ?? createApnsRoute(null, authenticatePush))\n\n const { parking, profileStore, shell, spend } = stateDirDefaults(config)\n\n const server = createWorkerServer({\n ...config.options,\n ...(profileStore ? { profileStore } : {}),\n // On by default here, off in the library: a mispointed config dir should say so at startup.\n checkCredentials: config.options.checkCredentials ?? true,\n parking,\n shell,\n spend,\n // Composed, not replaced: turning push on must not unhook a config file's own observer.\n notifications:\n apns === undefined\n ? config.options.notifications\n : {\n ...config.options.notifications,\n onNotification: (notification) => {\n config.options.notifications?.onNotification?.(notification)\n apns.onNotification(notification)\n },\n },\n fallback,\n ...(config.corsOrigins.length ? { cors: { origins: config.corsOrigins } } : {}),\n // A config file's own `authenticate` wins outright: mixing two auth schemes on one hook is a bypass nobody meant to\n // write. The unauthenticated case still supplies one rather than `allowUnauthenticated`, so the Host check covers `/v1`.\n authenticate: (req) => (hostAllowed(req) ? authenticateRequest(req) : null),\n })\n\n const wake = config.keepAwake\n ? createWakeLock({\n onUnavailable: (reason) => {\n if (!options.quiet) {\n process.stderr.write(`[workerdeck] not holding the machine awake: ${reason}\\n`)\n }\n },\n })\n : undefined\n let sweep: ReturnType<typeof setInterval> | undefined\n if (wake) {\n driveWakeLock(server.registry, wake)\n sweep = setInterval(() => wake.set(sessionsNeedTheMachine(server.registry.list())), WAKE_SWEEP_MS)\n sweep.unref()\n }\n\n let stopActivities: (() => void) | undefined\n if (apns) {\n // Boot reconcile before the driver, and before any session rebuilds: whatever is in the\n // activities file belongs to a process that is gone, so its cards are lying about a turn that\n // died with it. A `kill -9` skips this, which is what `stale-date` is for.\n await apns.activity.endAll('Gateway restarted')\n stopActivities = driveLiveActivities({ source: server.registry, target: apns.activity })\n }\n\n // Before `listen`, never after: between the port opening and the adoption, an attach for a carried id reads its\n // dormant record and resumes a SECOND engine child on the same transcript. Registering first closes the window\n // outright rather than masking it, and `listInfo` then hides the record for free.\n const adopted: CarriedSession[] = []\n for (const carried of options.carried ?? []) {\n if (server.adoptSession(carried)) {\n adopted.push(carried)\n }\n }\n\n let port: number\n try {\n ;({ port } = await server.listen(config.port, config.host))\n } catch (error) {\n // Release before close: `close()` closes every runner the registry holds, which for a carried session is the\n // engine child this whole path exists to keep. Release first and the registry is empty, so it closes nothing.\n for (const carried of adopted) {\n server.releaseSession(carried.runner.id)\n }\n await server.close()\n throw error\n }\n const displayHost = config.host === '0.0.0.0' || config.host === '::' ? 'localhost' : config.host\n const url = `http://${displayHost.includes(':') ? `[${displayHost}]` : displayHost}:${port}`\n\n let resolveClosed: () => void = () => {}\n const closed = new Promise<void>((r) => {\n resolveClosed = r\n })\n\n if (!options.quiet) {\n const push = apns && { topic: config.apns?.topic, devices: apns.deviceCount(), cards: apns.activity.count() }\n process.stdout.write(bannerLines({ url, config, generated, authEnabled: auth.enabled, push }).join('\\n') + '\\n')\n }\n\n return {\n server,\n adopted: adopted.map((carried) => carried.runner.id),\n url,\n port,\n closed,\n drain: (drainOptions) => server.drain(drainOptions),\n close: async () => {\n if (sweep) {\n clearInterval(sweep)\n }\n wake?.release()\n stopActivities?.()\n await server.close()\n // The session table's writes are queued rather than awaited by the request that caused them, so a last-moment\n // login is lost unless the queue drains here.\n await sessions?.flush?.()\n if (apns) {\n // A card outlives the gateway that drew it, so a clean shutdown is the one chance to say so.\n // Capped, because a phone that cannot be reached must not hold the process open.\n await Promise.race([apns.activity.endAll('Gateway stopped'), new Promise((resolve) => setTimeout(resolve, 3_000))])\n }\n apns?.close()\n resolveClosed()\n },\n }\n}\n\ntype StateDirDefaults = Pick<WorkerServerOptions, 'parking' | 'profileStore' | 'shell' | 'spend'>\n\n// Everything the state dir backs by default. A config file's own store wins; with no state dir everything stays in memory,\n// and profile management stays refused rather than silently forgetting every profile on restart.\nexport function stateDirDefaults(config: ResolvedConfig): StateDirDefaults {\n const { stateDir } = config\n const parking = { ...config.options.parking }\n const shell = { ...config.options.shell }\n const spend = { ...config.options.spend }\n if (!stateDir) {\n return { parking, shell, spend }\n }\n parking.store ??= createFileSessionStore({\n dir: join(stateDir, 'parked'),\n onError: (error, context) => {\n process.stderr.write(\n `[workerdeck] parked-session store ${context.op} failed for ${context.path}: ` +\n `${error instanceof Error ? error.message : String(error)}\\n`,\n )\n },\n })\n if (shell.artifactDir === undefined) {\n shell.artifactDir = join(stateDir, 'shells')\n }\n spend.store ??= createFileSpendStore(join(stateDir, 'spend.json'), (error) => {\n process.stderr.write(`[workerdeck] spend ledger write failed: ${error instanceof Error ? error.message : String(error)}\\n`)\n })\n const profileStore = config.profileStore ? createFileProfileStore(join(stateDir, 'profiles.json')) : undefined\n return { parking, shell, spend, ...(profileStore ? { profileStore } : {}) }\n}\n\nexport type BannerInput = {\n url: string\n config: ResolvedConfig\n generated: MaterializedAuthKey | null\n authEnabled: boolean\n push: { topic: string | undefined; devices: number; cards: number } | undefined\n}\n\nexport function bannerLines({ url, config, generated, authEnabled, push }: BannerInput): string[] {\n const lines = ['', ` workerdeck ${url}`]\n if (config.hostAuthenticates) {\n lines.push(' auth: the config file supplies its own `authenticate`')\n } else if (generated?.source === 'created') {\n lines.push(` auth: generated key ${generated.key}`, ` stored in ${generated.path} - later starts reuse it without printing it`)\n } else if (generated?.source === 'ephemeral') {\n lines.push(` auth: generated key ${generated.key}`, ' ephemeral - no state dir to keep it, so the next start mints a new one')\n } else if (generated?.source === 'stored') {\n lines.push(` auth: shared key from ${generated.path}`)\n } else if (authEnabled) {\n lines.push(' auth: shared key - browsers sign in, services send a header')\n } else {\n lines.push(' NO AUTH - anyone who can reach this port gets a session')\n }\n if (!config.web) {\n lines.push(' dashboard: off - bare gateway, /v1 and /auth only')\n }\n if (!config.keepAwake) {\n lines.push(' keep-awake: off - this machine may sleep mid-turn')\n }\n if (config.corsOrigins.length) {\n lines.push(` cors: ${config.corsOrigins.join(', ')} may call /v1 (still key-gated)`)\n }\n lines.push(\n config.stateDir\n ? ` parked sessions persist in ${join(config.stateDir, 'parked')}`\n : ' parked sessions are in memory only - a restart drops them',\n )\n if (push) {\n lines.push(\n ` push: APNs forwarder on ${push.topic} - ` +\n `${push.devices === 0 ? 'no devices registered yet' : `${push.devices} device(s)`}` +\n `${push.cards === 0 ? '' : `, ${push.cards} live card(s)`}`,\n )\n }\n if (config.configPath) {\n lines.push(` config ${config.configPath}`)\n }\n lines.push('')\n return lines\n}\n"],"mappings":";;;;;;;;;;;;AAKA,SAAgB,SAAS,KAAsB,UAA0C;CACvF,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,SAAmB,CAAC;EAC1B,IAAI,OAAO;EACX,IAAI,UAAU;EACd,MAAM,UAAU,UAA+B;GAC7C,IAAI,CAAC,SAAS;IACZ,UAAU;IACV,QAAQ,KAAK;GACf;EACF;EACA,IAAI,GAAG,SAAS,UAAkB;GAChC,QAAQ,MAAM;GACd,IAAI,OAAO,UAAU;IACnB,OAAO,IAAI;IACX;GACF;GACA,OAAO,KAAK,KAAK;EACnB,CAAC;EACD,IAAI,GAAG,aAAa,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC;EAClE,IAAI,GAAG,eAAe,OAAO,IAAI,CAAC;CACpC,CAAC;AACH;AAGA,SAAgB,YAAY,KAAqB,QAAgB,MAA+B,SAAwC;CACtI,IAAI,UAAU,QAAQ;EAAE,gBAAgB;EAAoB,iBAAiB;EAAY,GAAG;CAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC;AACjI;AAKA,eAAsB,gBAAgB,cAAoC,KAAwC;CAChH,MAAM,YAAY,MAAM,aAAa,GAAG;CACxC,OAAO,cAAc,QAAQ,cAAc,KAAA,KAAa,cAAc;AACxE;AAGA,SAAgB,cAAc,KAA+B;CAC3D,QAAQ,IAAI,QAAQ,mBAAmB,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAE,KAAK,CAAC,CAAC,YAAY,MAAM;AACrF;;;AC7BA,MAAa,gBAAgB;AAE7B,MAAM,iBAAiB;AAEvB,SAAgB,cAAc,OAA0C;CACtE,OAAO,UAAU,iBAAiB,UAAU;AAC9C;AAGA,SAAgB,eAAe,SAA8B,UAAkB,OAA+B;CAC5G,MAAM,OAAO,QAAQ,QAAQ,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ;CACrE,OAAO;EACL,MAAM,YAAY;GAChB,IAAI,SAAS,MACX,OAAO,CAAC;GAEV,MAAM,WAAY,MAAM,WAAW,MAAM,IAAI,EAAA,GAAwC;GACrF,OAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC;EAC7C;EACA,MAAM,OAAO,YAAY;GACvB,IAAI,SAAS,MACX;GAEF,IAAI;IACF,MAAM,gBAAgB,MAAM,GAAG,QAAQ,QAAQ,GAAG;KAAE,QAAQ;KAAG,iBAAiB;IAAK,CAAC;GACxF,SAAS,OAAO;IACd,QAAQ,UAAU,OAAO;KAAE,IAAI;KAAS;IAAK,CAAC;GAChD;EACF;CACF;AACF;AAIA,SAAgB,cACd,OACA,UACA,cACA,QACkB;CAClB,OAAO,OAAO,KAAK,QAAQ;EACzB,IAAI;EACJ,IAAI;GACF,WAAW,IAAI,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC,CAAC;EACxD,QAAQ;GACN,OAAO;EACT;EACA,IAAI,aAAa,OACf,OAAO;EAET,IAAI,aAAa,MAAM;GACrB,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAClE,IAAI,IAAI,kCAAkC;GAC1C,OAAO;EACT;EACA,IAAI,IAAI,WAAW,UAAU,IAAI,WAAW,UAAU;GACpD,IAAI,UAAU,KAAK,EAAE,OAAO,eAAe,CAAC,CAAC,CAAC,IAAI;GAClD,OAAO;EACT;EACA,IAAI,CAAE,MAAM,gBAAgB,cAAc,GAAG,GAAI;GAC/C,YAAY,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;GAC/C,OAAO;EACT;EACA,IAAI,CAAC,cAAc,GAAG,GAAG;GACvB,YAAY,KAAK,KAAK,EAAE,OAAO,yCAAyC,CAAC;GACzE,OAAO;EACT;EACA,MAAM,MAAM,MAAM,SAAS,KAAK,cAAc;EAC9C,IAAI,QAAQ,MAAM;GAChB,YAAY,KAAK,KAAK,EAAE,OAAO,iBAAiB,CAAC;GACjD,IAAI,KAAK,gBAAgB,IAAI,QAAQ,CAAC;GACtC,OAAO;EACT;EACA,IAAI;EACJ,IAAI;GACF,OAAO,KAAK,MAAM,GAAG;EACvB,QAAQ;GACN,YAAY,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;GACpD,OAAO;EACT;EACA,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI;EACrC,OAAO;CACT;AACF;;;ACjDA,MAAMA,aAAW;AAEjB,SAAS,MAAM,aAAqB,WAA2B;CAC7D,OAAO,GAAG,YAAY,GAAG;AAC3B;AAIA,SAAS,YAAY,QAAmC;CACtD,MAAM,EAAE,aAAa,WAAW,QAAQ,aAAa,aAAa,OAAO,cAAc;CACvF,OAAO;EAAE;EAAa;EAAW;EAAQ;EAAa;EAAa;EAAO;CAAU;AACtF;AAEA,eAAsB,uBAAuB,SAAyD;CACpG,MAAM,OAAO,eAAe,SAASA,YAAU,YAAY;CAC3D,MAAM,0BAAU,IAAI,IAA4B;CAEhD,KAAK,MAAM,UAAW,MAAM,KAAK,KAAK,GACpC,IAAI,OAAO,QAAQ,gBAAgB,YAAY,OAAO,OAAO,cAAc,YAAY,cAAc,OAAO,WAAW,GACrH,QAAQ,IAAI,MAAM,OAAO,aAAa,OAAO,SAAS,GAAG,MAAM;CAGnE,MAAM,gBAA+B,KAAK,KAAK,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;CAErF,OAAO;EACL,YAAY,CAAC,GAAG,QAAQ,OAAO,CAAC;EAChC,MAAM,aAAa,cAAc,QAAQ,IAAI,MAAM,aAAa,SAAS,CAAC;EAC1E,aAAa,cAAc,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,WAAW,OAAO,cAAc,SAAS;EAClG,MAAM,MAAM,QAAQ;GAClB,MAAM,WAAW,QAAQ,IAAI,MAAM,OAAO,aAAa,OAAO,SAAS,CAAC;GACxE,IAAI,aAAa,KAAA,GACf,OAAO;GAET,MAAM,UAA0B;IAAE,GAAG;IAAQ,OAAO;IAAY,WAAW,OAAO,aAAa,KAAK,IAAI;GAAE;GAC1G,QAAQ,IAAI,MAAM,QAAQ,aAAa,QAAQ,SAAS,GAAG,OAAO;GAClE,MAAM,QAAQ;GACd,OAAO;EACT;EACA,MAAM,OAAO,OAAO,aAAa;GAC/B,MAAM,SAAS,MAAM,gBAAgB,KAAA,IAAY,KAAA,IAAY,QAAQ,IAAI,MAAM,MAAM,aAAa,MAAM,SAAS,CAAC;GAClH,MAAM,aACJ,WAAW,KAAA,IACP,CAAC,MAAM,IACP,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,QACnB,WACC,OAAO,cAAc,MAAM,aAAa,OAAO,gBAAgB,MAAM,eAAe,OAAO,gBAAgB,KAAA,CAC/G;GAEN,MAAM,SAAS,WAAW,WAAW,IAAI,WAAW,KAAK,KAAA;GACzD,IAAI,WAAW,KAAA,KAAa,OAAO,gBAAgB,aACjD,OAAO;GAET,OAAO,cAAc;GACrB,OAAO,QAAQ;GACf,MAAM,QAAQ;GACd,OAAO;EACT;EACA,MAAM,MAAM,aAAa,WAAW,SAAS;GAC3C,MAAM,SAAS,QAAQ,IAAI,MAAM,aAAa,SAAS,CAAC;GACxD,IAAI,WAAW,KAAA,GACb;GAEF,MAAM,aAAa,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU,OAAO;GAC3E,OAAO,OAAO,QAAQ,OAAO;GAC7B,IAAI,YACF,MAAM,QAAQ;EAElB;EACA,MAAM,OAAO,aAAa,WAAW;GACnC,IAAI,CAAC,QAAQ,OAAO,MAAM,aAAa,SAAS,CAAC,GAC/C;GAEF,MAAM,QAAQ;EAChB;EACA,MAAM,oBAAoB,aAAa;GACrC,IAAI,UAAU;GACd,KAAK,MAAM,CAAC,KAAK,WAAW,SAC1B,IAAI,OAAO,gBAAgB,aAAa;IACtC,QAAQ,OAAO,GAAG;IAClB,UAAU;GACZ;GAEF,IAAI,SACF,MAAM,QAAQ;EAElB;CACF;AACF;AAEA,SAAgB,oBAAoB,UAAmC,cAAsD;CAC3H,OAAO,cAAc,oBAAoB,UAAU,cAAc,OAAO,YAAY,KAAK,KAAK,SAAS;EACrG,MAAM,QAAQ,KAAK;EACnB,IAAI,OAAO,UAAU,YAAY,CAAC,cAAc,KAAK,KAAK,GAAG;GAC3D,YAAY,KAAK,KAAK,EAAE,OAAO,iDAAiD,CAAC;GACjF;EACF;EAEA,IAAI,IAAI,WAAW,UAAU;GAC3B,MAAM,WAAW,oBAAoB,KAAK;GAC1C,IAAI,UAAU,GAAG,CAAC,CAAC,IAAI;GACvB;EACF;EAEA,MAAM,YAAY,KAAK;EACvB,IAAI,OAAO,cAAc,YAAY,cAAc,IAAI;GACrD,YAAY,KAAK,KAAK,EAAE,OAAO,wBAAwB,CAAC;GACxD;EACF;EACA,IAAI,CAAC,cAAc,KAAK,WAAW,GAAG;GACpC,YAAY,KAAK,KAAK,EAAE,OAAO,oDAAoD,CAAC;GACpF;EACF;EACA,MAAM,cAAc,OAAO,KAAK,gBAAgB,YAAY,cAAc,KAAK,KAAK,WAAW,IAAI,KAAK,cAAc,KAAA;EAGtH,IAAI,MADiB,WAAW,OAAO;GAAE;GAAW;GAAa,aAAa,KAAK;EAAY,GAAG,KAAK,MACxF,KAAA,GAAW;GAGxB,YAAY,KAAK,KAAK,EAAE,OAAO,gDAAgD,CAAC;GAChF;EACF;EACA,YAAY,KAAK,KAAK;GAAE,UAAU;GAAM;EAAU,CAAC;CACrD,CAAC;AACH;;;ACjKA,MAAa,gBAAoD;CAC/D;CACA;CACA;CACA;AACF;AAKA,MAAa,iBAAqD;CAAC;CAAwB;CAAkB;AAAe;AA2B5H,MAAM,WAAW;AAIjB,SAAS,aAAa,OAAoD;CACxE,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,UAAU,cAAc,SAAS,KAAgC,CAAC;AAChH;AAEA,SAAS,WAAW,GAA0C,GAAmD;CAC/G,IAAI,MAAM,KAAA,KAAa,MAAM,KAAA,GAC3B,OAAO,MAAM;CAEf,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,OAAO,UAAU,UAAU,EAAE,MAAM;AAC9E;AAEA,SAAS,eAAe,OAAoC;CAC1D,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,KAAK,MAAM,UAAU,MAAM,QAAQ,KAAA;AACxF;AAEA,SAAgB,kBAAkB,QAAsC,MAAwC;CAC9G,QAAQ,OAAO,UAAU,eAAA,CAAgB,SAAS,IAAI;AACxD;AAEA,eAAsB,qBAAqB,SAAuD;CAChG,MAAM,OAAO,eAAe,SAAS,UAAU,SAAS;CACxD,MAAM,0BAAU,IAAI,IAA0B;CAC9C,KAAK,MAAM,UAAW,MAAM,KAAK,KAAK,GACpC,IAAI,OAAO,QAAQ,UAAU,YAAY,cAAc,OAAO,WAAW,GACvE,QAAQ,IAAI,OAAO,OAAO,MAAM;CAGpC,MAAM,gBAA+B,KAAK,KAAK,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;CAEpE,OAAO;EACL,YAAY,CAAC,GAAG,QAAQ,OAAO,CAAC;EAChC,MAAM,SAAS,QAAQ;GACrB,MAAM,WAAW,QAAQ,IAAI,OAAO,KAAK;GACzC,QAAQ,IAAI,OAAO,OAAO;IAAE,GAAG;IAAQ,WAAW,KAAK,IAAI;GAAE,CAAC;GAC9D,IACE,aAAa,KAAA,KACb,SAAS,gBAAgB,OAAO,eAChC,SAAS,WAAW,OAAO,UAC3B,SAAS,2BAA2B,OAAO,0BAC3C,WAAW,SAAS,QAAQ,OAAO,MAAM,GAEzC;GAEF,MAAM,QAAQ;EAChB;EACA,MAAM,gBAAgB,OAAO;GAC3B,MAAM,WAAW,QAAQ,IAAI,KAAK;GAClC,IAAI,aAAa,KAAA,KAAa,SAAS,2BAA2B,KAAA,GAChE;GAEF,OAAO,SAAS;GAChB,MAAM,QAAQ;EAChB;EACA,MAAM,OAAO,OAAO;GAClB,IAAI,CAAC,QAAQ,OAAO,KAAK,GACvB;GAEF,MAAM,QAAQ;EAChB;CACF;AACF;AAEA,SAAgB,kBAAkB,UAAiC,cAAsD;CACvH,OAAO,cAAc,iBAAiB,UAAU,cAAc,OAAO,SAAS,KAAK,KAAK,SAAS;EAC/F,MAAM,QAAQ,KAAK;EACnB,IAAI,OAAO,UAAU,YAAY,CAAC,cAAc,KAAK,KAAK,GAAG;GAC3D,YAAY,KAAK,KAAK,EAAE,OAAO,wCAAwC,CAAC;GACxE;EACF;EAEA,IAAI,IAAI,WAAW,UAAU;GAC3B,MAAM,QAAQ,OAAO,KAAK;GAC1B,IAAI,UAAU,GAAG,CAAC,CAAC,IAAI;GACvB;EACF;EAEA,IAAI,CAAC,cAAc,KAAK,WAAW,GAAG;GACpC,YAAY,KAAK,KAAK,EAAE,OAAO,oDAAoD,CAAC;GACpF;EACF;EAGA,MAAM,kBAAkB,OAAO,OAAO,MAAM,wBAAwB;EACpE,MAAM,aAAa,KAAK;EACxB,IAAI,mBAAmB,eAAe,SAAS,OAAO,eAAe,YAAY,CAAC,cAAc,KAAK,UAAU,IAAI;GACjH,YAAY,KAAK,KAAK,EAAE,OAAO,qDAAqD,CAAC;GACrF;EACF;EAGA,MAAM,cAAc,OAAO,OAAO,MAAM,QAAQ;EAChD,IAAI,eAAe,CAAC,aAAa,KAAK,MAAM,GAAG;GAC7C,YAAY,KAAK,KAAK,EAAE,OAAO,8BAA8B,cAAc,KAAK,IAAI,IAAI,CAAC;GACzF;EACF;EACA,MAAM,WAAW,QAAQ,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,UAAU,KAAK;EACvE,IAAI,aAAa,KAAA,KAAa,QAAQ,KAAK,CAAC,CAAC,UAAA,IAAuB;GAClE,YAAY,KAAK,KAAK,EAAE,OAAO,kDAA8D,CAAC;GAC9F;EACF;EACA,MAAM,yBAAyB,kBAAoB,cAAgC,KAAA,IAAa,UAAU;EAC1G,MAAM,SAAS,cAAe,KAAK,SAAuC,UAAU;EAEpF,MAAM,QAAQ,SAAS;GACrB;GACA,aAAa,KAAK;GAClB,QAAQ,eAAe,KAAK,MAAM;GAClC,UAAU,eAAe,KAAK,QAAQ;GACtC,UAAU,eAAe,KAAK,QAAQ;GACtC,GAAI,2BAA2B,KAAA,IAAY,CAAC,IAAI,EAAE,uBAAuB;GACzE,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EAC3C,CAAC;EACD,YAAY,KAAK,KAAK;GAAE,YAAY;GAAM,aAAa,KAAK;EAAY,CAAC;CAC3E,CAAC;AACH;;;AC9JA,SAAgB,gBACd,YACA,cACkB;CAClB,MAAM,SAAS,kBAAkB,YAAY,WAAW,MAAM,YAAY;CAC1E,MAAM,WAAW,oBAAoB,YAAY,cAAc,MAAM,YAAY;CACjF,OAAO,OAAO,KAAK,QAAS,MAAM,OAAO,KAAK,GAAG,KAAM,SAAS,KAAK,GAAG;AAC1E;;;ACAA,MAAM,QAAyC;CAC7C,aAAa;CACb,YAAY;AACd;AAEA,MAAM,eAAe;AACrB,MAAM,qBAAqB;AAC3B,MAAM,0BAA0B;AAgChC,SAAS,SAAS,OAAe,UAA4C;CAC3E,OAAO,aAAa,iBAAiB,GAAG,MAAM,2BAA2B;AAC3E;AAEA,SAAS,UAAU,OAAgC;CACjD,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,WAAW;AAChD;AAEA,eAAsB,YAAY,SAAqC;CACrE,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,SAAS,MAAM;CACtC,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,qCAAqC,QAAQ,IAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAAK,EAC9H,OAAO,MACT,CAAC;CACH;CACA,IAAI;CACJ,IAAI;EACF,MAAM,iBAAiB,GAAG;CAC5B,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,SAAS,QAAQ,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,MAAM,CAAC;CACvI;CACA,IAAI,IAAI,sBAAsB,MAC5B,MAAM,IAAI,MACR,SAAS,QAAQ,QAAQ,IAAI,qBAAqB,UAAU,2GAE9D;CAEF,OAAO;AACT;AAEA,SAAgB,oBAAoB,KAAgB,OAAe,QAAmE;CACpI,IAAI,SAAqD;CACzD,OAAO;EACL,IAAI,MAAM,KAAK,IAAI,GAAG;GACpB,IAAI,WAAW,QAAQ,MAAM,OAAO,WAAW,cAC7C,OAAO,OAAO;GAIhB,MAAM,eAAe,GAFN,UAAU,KAAK,UAAU;IAAE,KAAK;IAAS,KAAK;GAAM,CAAC,CAEvC,EAAE,GADhB,UAAU,KAAK,UAAU;IAAE,KAAK;IAAQ,KAAK,KAAK,MAAM,MAAM,GAAI;GAAE,CAAC,CAC7C;GAKvC,MAAM,QAAQ,GAAG,aAAa,GAAG,UAJf,KAAK,UAAU,OAAO,KAAK,YAAY,GAAG;IAC1D;IACA,aAAa;GACf,CACmD,CAAC;GACpD,SAAS;IAAE;IAAO,UAAU;GAAI;GAChC,OAAO;EACT;EACA,aAAa;GACX,SAAS;EACX;CACF;AACF;AAEA,SAAS,kBAAkB,OAMzB;CACA,MAAM,2BAAW,IAAI,IAAyC;CAC9D,MAAM,2BAAW,IAAI,IAA6B;CAClD,MAAM,QAAQ,aAA8B,YAAsC;EAChF,IAAI,SAAS,IAAI,WAAW,MAAM,SAChC,SAAS,OAAO,WAAW;CAE/B;CACA,OAAO;EACL,IAAI,aAAa;GACf,MAAM,WAAW,SAAS,IAAI,WAAW;GACzC,IAAI,aAAa,KAAA,KAAa,CAAC,SAAS,UAAU,CAAC,SAAS,WAC1D,OAAO;GAET,MAAM,UAAU,QAAQ,MAAM,cAAc,EAE1C,gCAAgC,wBAClC,CAA+B;GAC/B,SAAS,IAAI,aAAa,OAAO;GACjC,SAAS,OAAO,WAAW;GAE3B,QAAQ,GAAG,UAAU,UAAU;IAC7B,SAAS,IAAI,aAAa,GAAI,MAAgC,QAAQ,QAAQ,IAAI,MAAM,SAAS;IACjG,KAAK,aAAa,OAAO;GAC3B,CAAC;GACD,QAAQ,GAAG,WAAW,MAAM,eAAe,SAAS;IAClD,MAAM,SAAS,SAAS,KAAA,KAAa,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,MAAM,CAAC,CAAC,MAAM,GAAG,GAAG,MAAM;IACpG,SAAS,IAAI,aAAa,UAAU,OAAO,QAAQ;IACnD,KAAK,aAAa,OAAO;GAC3B,CAAC;GACD,QAAQ,GAAG,eAAe,KAAK,aAAa,OAAO,CAAC;GACpD,QAAQ,WAAW,WAAkB,QAAQ,MAAM,CAAC;GACpD,OAAO;EACT;EACA,cAAc,gBAAgB,SAAS,IAAI,WAAW;EACtD,QAAQ,aAAa,SAAS;GAC5B,KAAK,aAAa,OAAO;GACzB,IAAI,CAAC,QAAQ,WACX,QAAQ,QAAQ;EAEpB;EACA,QAAQ;GACN,KAAK,MAAM,WAAW,SAAS,OAAO,GACpC,QAAQ,MAAM;GAEhB,SAAS,MAAM;EACjB;CACF;AACF;AAEA,SAAS,oBAAoB,OAAsB;CACjD,MAAM,QAAS,MAAsC;CACrD,IAAI,EAAE,iBAAiB,iBACrB,OAAO,MAAM;CAEf,MAAM,QAAQ,MAAM,OAAO,KAAK,UAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAE,CAAC,CAAC,QAAQ,YAAY,YAAY,EAAE;CACtI,IAAI,MAAM,WAAW,GACnB,OAAO,MAAM;CAEf,OAAO,GAAG,MAAM,QAAQ,QAAQ,kBAAkB,EAAE,EAAE,eAAe,MAAM,KAAK,IAAI,EAAE;AACxF;AAMA,SAAgB,iBACd,QACA,KAEA,UAA8E,CAAC,GACnE;CACZ,MAAM,gBAAgB,oBAAoB,KAAK,OAAO,OAAO,OAAO,MAAM;CAC1E,MAAM,OAAO,kBAAkB,QAAQ,SAAS,KAAK;CACrD,MAAM,eAAe,QAAQ,gBAAgB;CAE7C,MAAM,QAAQ,SAAsB,QAClC,IAAI,SAAS,YAAY;EACvB,MAAM,OAAO,OAAO,KAAK,KAAK,UAAU,QAAQ,OAAO,CAAC;EACxD,MAAM,UAAU,KAAK,IAAI,QAAQ,WAAW;EAC5C,IAAI;EACJ,IAAI;GACF,SAAS,QAAQ,QAAQ;IACvB,WAAW;IACX,SAAS,aAAa,QAAQ;IAC9B,eAAe,UAAU;IACzB,cAAc,SAAS,OAAO,OAAO,QAAQ,QAAQ;IACrD,kBAAkB,QAAQ,YAAY;IACtC,iBAAiB,OAAO,QAAQ,YAAY,EAAE;IAC9C,mBAAmB,OAAO,QAAQ,cAAc,CAAC;IACjD,kBAAkB,OAAO,KAAK,UAAU;IACxC,GAAI,QAAQ,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,oBAAoB,QAAQ,WAAW;GACvF,CAAC;EACH,SAAS,OAAO;GACd,QAAQ;IACN,QAAQ;KACN,IAAI;KACJ,QAAQ;KACR,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAC7D,cAAc;IAChB;IACA,OAAO;GACT,CAAC;GACD;EACF;EAEA,IAAI,SAAS;EACb,IAAI;EACJ,MAAM,SAAmB,CAAC;EAC1B,IAAI,UAAU;EACd,MAAM,UAAU,QAAoB,QAAe,YAAkB;GACnE,IAAI,SACF;GAEF,UAAU;GACV,QAAQ;IAAE;IAAQ;GAAM,CAAC;EAC3B;EAEA,MAAM,uBAA8B;GAClC,IAAI,OAAO,SACT,OAAO;GAET,IAAI,OAAO,YAAY,UAAU,wBAC/B,OAAO;GAET,OAAO;EACT;EACA,MAAM,uBAA6B;GACjC,IAAI,OAAO,WAAW,QAAQ,YAC5B,KAAK,QAAQ,QAAQ,aAAa,OAAO;EAE7C;EAEA,OAAO,WAAW,0BAA0B;GAC1C,MAAM,QAAQ,eAAe;GAC7B,eAAe;GACf,OAAO,MAAM;GACb,OAAO;IAAE,IAAI;IAAO,QAAQ;IAAG,QAAQ;IAAW,cAAc;GAAM,GAAG,KAAK;EAChF,CAAC;EACD,OAAO,GAAG,aAAa,YAAY;GACjC,SAAS,OAAO,QAAQ,cAAc,CAAC;GACvC,MAAM,KAAK,QAAQ;GACnB,SAAS,OAAO,OAAO,WAAW,KAAK,KAAA;EACzC,CAAC;EACD,OAAO,GAAG,SAAS,UAAkB,OAAO,KAAK,KAAK,CAAC;EACvD,OAAO,GAAG,UAAU,UAAU;GAC5B,MAAM,QAAQ,eAAe;GAC7B,eAAe;GACf,MAAM,QAAQ,KAAK,YAAY,QAAQ,WAAW;GAClD,MAAM,UAAU,oBAAoB,KAAK;GACzC,OACE;IACE,IAAI;IACJ;IACA,QAAQ,UAAU,KAAA,IAAY,GAAG,MAAM,YAAY,QAAQ,KAAK;IAChE,cAAc;GAChB,GACA,KACF;EACF,CAAC;EACD,OAAO,GAAG,aAAa;GACrB,IAAI,WAAW,KAAK;IAClB,OAAO;KAAE,IAAI;KAAM;IAAO,CAAC;IAC3B;GACF;GACA,IAAI,SAAS,QAAQ;GACrB,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC;IAChE,IAAI,OAAO,OAAO,WAAW,UAC3B,SAAS,OAAO;GAEpB,QAAQ,CAAC;GACT,OAAO;IACL,IAAI;IACJ;IACA;IACA,cAAc,WAAW,kBAAkB,WAAW;GACxD,CAAC;EACH,CAAC;EAED,OAAO,GAAG,eAAe;GACvB,MAAM,QAAQ,eAAe;GAC7B,OACE;IACE,IAAI;IACJ;IACA,QAAQ,KAAK,YAAY,QAAQ,WAAW,KAAK,+BAA+B,OAAO,QAAQ;IAC/F,cAAc;GAChB,GACA,KACF;EACF,CAAC;EACD,OAAO,IAAI,IAAI;CACjB,CAAC;CAEH,MAAM,QAAQ,OAA8B,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;CAE5F,OAAO;EACL,MAAM,KAAK,SAAS;GAClB,IAAI,UAAU,MAAM,KAAK,SAAS,cAAc,IAAI,CAAC;GACrD,IAAI,CAAC,QAAQ,OAAO,MAAM,QAAQ,UAAU,SAAS;IACnD,IAAI,QAAQ,UAAU,UACpB,MAAM,KAAK,YAAY;IAEzB,UAAU,MAAM,KAAK,SAAS,cAAc,IAAI,CAAC;GACnD;GACA,MAAM,QAAQ,QAAQ;GAEtB,IAAI,CAAC,MAAM,MAAM,MAAM,WAAW,wBAAwB;IACxD,cAAc,WAAW;IACzB,QAAQ,MAAM,KAAK,SAAS,cAAc,IAAI,CAAC,EAAA,CAAG;GACpD;GACA,OAAO;EACT;EACA,OAAO,KAAK;CACd;AACF;;;ACtRA,MAAMC,sBAAoB;AAC1B,MAAM,QAAQ;CAAE,OAAO;CAAI,UAAU;CAAK,QAAQ;CAAK,aAAa;CAAI,SAAS;CAAG,WAAW;CAAI,QAAQ;AAAE;AAC7G,MAAM,WAAW;AACjB,MAAM,eAAe;AAIrB,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAE1B,MAAM,UAAU;AAEhB,MAAM,YAA+C;CACnD,UAAU;CACV,UAAU;CACV,MAAM;CACN,SAAS;CACT,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,OAAO;AACT;AAGA,MAAM,eAAe;CAAC;CAAW;CAAa;CAAQ;CAAW;CAAO;CAAiB;CAAU;AAAa;AAEhH,SAAgB,MAAM,MAAc,OAAuB;CACzD,MAAM,OAAO,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC5C,OAAO,KAAK,UAAU,QAAQ,OAAO,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC,EAAE;AACnE;AAEA,SAAgB,aAAa,MAA2B;CACtD,MAAM,QAAQ,KAAK,OAAO,KAAK;CAC/B,IAAI,UAAU,KAAA,KAAa,UAAU,IACnC,OAAO;CAET,OAAO,QAAQ,KAAK,GAAG,KAAK,KAAK;AACnC;AAEA,SAAgB,QAAQ,KAAqB;CAC3C,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK;AAClD;AAEA,SAAS,UAAU,OAAoD;CACrE,KAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,MAAM;EACpB,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAChD,OAAO;CAEX;AAEF;AAEA,SAAS,YAAY,SAA0D;CAC7E,IAAI,QAAQ,aAAa,gBACvB,OAAO;CAET,OAAO,QAAQ,aAAa,qBAAqB,mBAAmB,QAAQ,KAAK,CAAC,CAAC,SAAS,IAAI,aAAa;AAC/G;AAKA,SAAS,WAAW,SAA4B,MAAsD;CACpG,IAAI,SAAS,YACX,OAAO,CAAC;CAEV,MAAM,YAAY,mBAAmB,QAAQ,KAAK;CAClD,MAAM,CAAC,YAAY;CACnB,IAAI,UAAU,WAAW,KAAK,aAAa,KAAA,KAAa,SAAS,gBAAgB,QAAQ,SAAS,QAAQ,SAAS,MAAM,SACvH,OAAO,CAAC;CAEV,OAAO,SAAS,QAAQ,KAAK,QAAQ,WAAW;EAAE;EAAO,OAAO,MAAM,OAAO,OAAO,MAAM,WAAW;CAAE,EAAE;AAC3G;AAIA,SAAS,UAAU,MAAoC;CAMrD,OAJe,CAAC,IADC,KAAK,aAAa,CAAC,EAAA,CAAG,OAAO,aACrB,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM;EACzC,MAAM,UAAU,OAAO,EAAE,WAAW,SAAS,IAAI,OAAO,EAAE,WAAW,SAAS;EAC9E,OAAO,YAAY,IAAI,UAAU,EAAE,YAAY,EAAE;CACnD,CACY,CAAC,CAAC,MAAM,GAAG,MAAM,MAAM,CAAC,CAAC,KAAK,SAAS;EAAE,MAAM,MAAM,cAAc,GAAG,GAAG,MAAM,SAAS;EAAG,OAAO,IAAI;CAAO,EAAE;AAC7H;AAIA,SAAS,oBAAoB,MAAuC;CAClE,MAAM,UAAU,KAAK,aAAa,CAAC,EAAA,CAAG,QAAQ,SAAS,KAAK,WAAW,aAAa;CACpF,IAAI,OAAO,WAAW,GACpB;CAEF,MAAM,OAAO,MAAM,OAAO,EAAE,CAAE,MAAM,KAAK,IAAI,MAAM,UAAU,kBAAkB,CAAC;CAChF,OAAO,SAAS,KAAK,KAAA,IAAY;AACnC;AAEA,SAAS,SAAS,MAAmB,SAA2D;CAC9F,QAAQ,KAAK,QAAb;EACE,KAAK,qBAAqB;GACxB,IAAI,YAAY,KAAA,GACd,OAAO;GAET,MAAM,OAAO,YAAY,OAAO;GAChC,OAAO,SAAS,eAAe,aAAa;EAC9C;EACA,KAAK,UACH,OAAO;EAET,KAAK,UACH,OAAO;EAET,KAAK,UACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,SACE,OAAO;CAEX;AACF;AAEA,SAAS,YAAY,MAAmB,OAA0B,SAAgD;CAChH,IAAI,YAAY,KAAA,MAAc,UAAU,cAAc,UAAU,cAAc,UAAU,SAAS;EAC/F,IAAI,UAAU,YAAY;GACxB,MAAM,CAAC,YAAY,mBAAmB,QAAQ,KAAK;GACnD,OAAO,MAAM,UAAU,WAAW,KAAA,KAAa,SAAS,WAAW,KAAK,SAAS,SAAS,cAAc,MAAM,QAAQ;EACxH;EACA,IAAI,UAAU,QACZ,OAAO;EAET,OAAO,MAAM,QAAQ,SAAS,QAAQ,eAAe,QAAQ,UAAU,MAAM,QAAQ;CACvF;CACA,IAAI,UAAU,WACZ,OAAO,oBAAoB,IAAI,KAAK;CAQtC,OAAO;EALL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,OAAO;CAEG,EAAE,UAAU;AAC1B;AAEA,SAAS,UAAU,OAA0B,SAA4D;CACvG,IAAI,YAAY,KAAA,GACd;CAEF,IAAI,UAAU,YAAY;EACxB,MAAM,CAAC,YAAY,mBAAmB,QAAQ,KAAK;EACnD,OAAO,aAAa,KAAA,IAAY,KAAA,IAAY,MAAM,SAAS,UAAU,MAAM,MAAM;CACnF;CACA,IAAI,UAAU,cAAc,UAAU,QAAQ;EAC5C,MAAM,UAAU,UAAU,QAAQ,KAAK,KAAK,QAAQ;EACpD,OAAO,YAAY,KAAA,IAAY,KAAA,IAAY,MAAM,SAAS,MAAM,MAAM;CACxE;AAEF;AAEA,SAAgB,cAAc,MAAmB,QAAgD;CAC/F,OAAO;EACL,WAAW,KAAK;EAChB,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,GAAI,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;EAC3D,SAAS,QAAQ,KAAK,GAAG;CAC3B;AACF;AAEA,SAAgB,oBAAoB,SAMX;CACvB,MAAM,EAAE,MAAM,SAAS,gBAAgB;CACvC,MAAM,QAAQ,QAAQ,cAAc,SAAS,MAAM,OAAO;CAC1D,MAAM,OAAO,YAAY,KAAA,IAAY,KAAA,IAAY,YAAY,OAAO;CACpE,MAAM,UAAU,YAAY,KAAA,KAAa,SAAS,KAAA,IAAY,CAAC,IAAI,WAAW,SAAS,IAAI;CAC3F,MAAM,QAAQ,KAAK,WAAW,UAAU;CACxC,MAAM,SAAS,UAAU,IAAI;CAE7B,OAAO;EACL;EACA,OAAO,MAAM,aAAa,IAAI,GAAG,MAAM,KAAK;EAC5C,UAAU,QAAQ,iBAAiB,YAAY,MAAM,OAAO,OAAO;EACnE,GAAI,UAAU,OAAO,OAAO,MAAM,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,UAAU,OAAO,OAAO,EAAE;EACvF;EACA,GAAI,SAAS,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,QAAQ,UAAU;EAC7E,cAAc,KAAK;EACnB,GAAI,UAAU,IAAI,CAAC,IAAI,EAAE,OAAO;GAAE,MAAM,KAAK,UAAW,QAAQ,SAAS,KAAK,WAAW,WAAW,CAAC,CAAC;GAAQ;EAAM,EAAE;EACtH,GAAI,OAAO,WAAW,IAAI,CAAC,IAAI,EAAE,OAAO;EACxC,GAAI,YAAY,KAAA,KAAa,SAAS,KAAA,IAClC,CAAC,IACD,EACE,SAAS;GACP,IAAI,QAAQ;GACZ;GACA;GAGA,GAAI,QAAQ,WAAW,IAAI,CAAC,IAAI,EAAE,WAAW,KAAK,UAAU,QAAQ,KAAK,EAAE;EAC7E,EACF;EACJ,GAAI,KAAK,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM;EACxD,KAAK,KAAK;EACV,UAAU;CACZ;AACF;AAKA,SAAgB,YAAY,OAAqC;CAC/D,MAAM,EAAE,KAAK,MAAM,OAAO,QAAQ,UAAU,WAAW,GAAG,UAAU;CACpE,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,GAAG,EAAE;AAC3F;AAIA,SAAS,OAAO,OAA+D;CAC7E,IAAI,MAAM,SAAS,cAAc,KAAA,GAC/B,OAAO;EAAE,GAAG;EAAO,SAAS;GAAE,GAAG,MAAM;GAAS,SAAS,CAAC;GAAG,WAAW,KAAA;EAAU;CAAE;CAEtF,IAAI,MAAM,WAAW,KAAA,GACnB,OAAO;EAAE,GAAG;EAAO,QAAQ,KAAA;CAAU;CAEvC,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,OAAO,SAAS,IACtD,OAAO;EAAE,GAAG;EAAO,QAAQ,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO,SAAS,CAAC,CAAC;CAAE;CAEtF,IAAI,MAAM,WAAW,KAAA,GACnB,OAAO;EAAE,GAAG;EAAO,QAAQ,KAAA;CAAU;CAEvC,IAAI,MAAM,SAAS,SAAS,IAC1B,OAAO;EAAE,GAAG;EAAO,UAAU,MAAM,MAAM,UAAU,KAAK,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC;CAAE;AAG9F;AAEA,SAAgB,sBACd,MACA,SAMkD;CAClD,MAAM,MAAM,QAAQ,OAAO,KAAK,IAAI;CACpC,MAAM,UAAU,KAAK,MAAM,MAAM,GAAI;CACrC,IAAI,QAAQ,QAAQ;CAEpB,MAAM,SAAS,aAA4D,EACzE,KAAK;EACH,WAAW;EACX,OAAO;EACP,GAAI,SAAS,UACT;GACE,mBAAmB;GACnB,YAAY,QAAQ;GAIpB,OAAO;IAAE,OAAO,QAAQ;IAAO,MAAM,QAAQ,UAAU,YAAY,YAAY;GAAkB;GACjG,oBAAoB;EACtB,IACA,CAAC;EACL,iBAAiB;EACjB,GAAI,SAAS,QACT,EAAE,kBAAkB,KAAK,OAAO,MAAM,gBAAgB,GAAI,EAAE,IAC5D,EAAE,cAAc,KAAK,OAAO,MAAM,YAAY,GAAI,EAAE;EACxD,mBAAmB,UAAU,QAAQ;CACvC,EACF;CAEA,IAAI,UAAU,MAAM,KAAK;CACzB,OAAO,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC,IAAIA,qBAAmB;EACrE,MAAM,UAAU,OAAO,KAAK;EAC5B,IAAI,YAAY,KAAA,GACd;EAEF,QAAQ;EACR,UAAU,MAAM,KAAK;CACvB;CAEA,OAAO;EACL;EACA,UAAU;EACV,UAAU,SAAS,YAAY,QAAQ,WAAW,OAAO,IAAI;EAC7D,YAAY,KAAK,OAAO,OAAO,SAAS,QAAQ,oBAAoB,kBAAkB,GAAI;CAC5F;AACF;;;ACnVA,MAAM,oBAAoB;AAC1B,MAAM,aAAa;AAmBnB,MAAM,WAAW;CACf,YAAY;CACZ,OAAO;AACT;AAGA,SAAS,YAAY,WAA2B;CAC9C,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,GAAG,EAAE;AAC/E;AAMA,MAAM,kBAA2D;CAC/D,sBAAsB;CACtB,gBAAgB;CAChB,eAAe;CACf,gBAAgB;AAClB;AAEA,SAAS,SAAS,cAAmC,MAAsB;CACzE,QAAQ,aAAa,MAArB;EACE,KAAK,wBACH,OAAO,qBAAqB;EAE9B,KAAK,kBACH,OAAO,aAAa,QAAQ,YAAY,OAAO,iBAAiB,SAAS;EAE3E,KAAK,iBACH,OAAO,mBAAmB;EAE5B,KAAK,kBACH,OAAO,mBAAmB;EAE5B,SACE,OAAO;CAEX;AACF;AAEA,SAAS,QAAQ,cAA2C;CAC1D,MAAM,UAAU,MAAM,aAAa,WAAW,IAAI,UAAU;CAC5D,IAAI,YAAY,IACd,OAAO;CAET,QAAQ,aAAa,MAArB;EACE,KAAK,wBACH,OAAO;EAET,KAAK,kBACH,OAAO;EAET,KAAK,kBACH,OAAO,iBAAiB,aAAa,UAAU,SAAS;EAE1D,SACE,OAAO;CAEX;AACF;AAGA,SAAgB,UAAU,cAAmC,QAA8E;CACzI,MAAM,aAAa,aAAa,SAAS;CACzC,MAAM,OAAO,aAAa,aAAa,OAAO;CAC9C,IAAI,OAAO,QAAQ,YAAY;CAE/B,MAAM,SAAS,UAA2C;EACxD,KAAK;GACH,OAAO;IAAE,OAAO,SAAS,cAAc,IAAI;IAAG,MAAM;GAAK;GACzD,OAAO;GACP,UAAU,aAAa,SAAS,aAAa,SAAS;GACtD,aAAa,aAAa;EAC5B;EACA,MAAM,aAAa;EACnB,WAAW,aAAa;EACxB,KAAK,aAAa;EAGlB,GAAI,aAAa,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,aAAa,QAAQ,MAAM;EACxF,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,GAAI,aAAa,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,aAAa,QAAQ,GAAG;CACrF;CAEA,IAAI,UAAU,MAAM,IAAI;CACxB,OAAO,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC,IAAI,qBAAqB,KAAK,SAAS,IAAI;EACzF,OAAO,MAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC,CAAC;EAC9C,UAAU,MAAM,IAAI;CACtB;CAEA,OAAO;EACL;EACA,UAAU,cAAc,aAAa,SAAS,kBAAkB,KAAK;EAErE,YACE,cAAc,aAAa,SAAS,cAAc,KAAA,IAC9C,KAAK,MAAM,aAAa,QAAQ,YAAY,GAAI,IAChD,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;EACtC,YAAY,GAAG,gBAAgB,aAAa,MAAM,GAAG,YAAY,aAAa,SAAS;CACzF;AACF;AAEA,eAAsB,oBAAoB,SAKf;CACzB,MAAM,OAAO,QAAQ,UAAU,YAAoB,QAAQ,OAAO,MAAM,sBAAsB,QAAQ,GAAG;CACzG,MAAM,MAAM,MAAM,YAAY,QAAQ,OAAO,OAAO;CACpD,MAAM,SAAqB,iBAAiB,QAAQ,QAAQ,GAAG;CAC/D,MAAM,WAA2B,MAAM,qBAAqB;EAC1D,KAAK,QAAQ;EACb,UAAU,OAAO,YACf,KAAK,mBAAmB,QAAQ,GAAG,cAAc,QAAQ,KAAK,IAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CACnI,CAAC;CACD,MAAM,aAA+B,MAAM,uBAAuB;EAChE,KAAK,QAAQ;EACb,UAAU,OAAO,YACf,KAAK,qBAAqB,QAAQ,GAAG,cAAc,QAAQ,KAAK,IAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CACrI,CAAC;CACD,MAAM,gBAAgB,gBAAgB;EAAE,SAAS;EAAU;CAAW,GAAG,QAAQ,YAAY;CAC7F,MAAM,sBAAsB,QAAQ,OAAO,eAAe,QAAQ,gBAAgB;CAGlF,MAAM,yBAAS,IAAI,IAA2B;CAE9C,MAAM,UAAU,OAAO,iBAAqD;EAC1E,MAAM,UAAU,SAAS,KAAK,CAAC,CAAC,QAAQ,WAAW,kBAAkB,QAAQ,aAAa,IAAI,CAAC;EAC/F,IAAI,QAAQ,WAAW,GACrB;EAEF,MAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,WAAW;GAC5B,MAAM,OAAO,UAAU,cAAc,OAAO,MAAM;GAClD,MAAM,SAAS,MAAM,OAAO,KAAK;IAC/B,GAAG;IACH,aAAa,OAAO;IACpB,aAAa,OAAO,eAAe;GACrC,CAAC;GACD,IAAI,OAAO,IACT;GAEF,IAAI,OAAO,cAAc;IACvB,MAAM,SAAS,OAAO,OAAO,KAAK;IAClC;GACF;GACA,KAAK,WAAW,OAAO,MAAM,MAAM,GAAG,CAAC,EAAE,YAAY,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;EACzF,CAAC,CACH;CACF;CAEA,MAAM,eAAe,OACnB,MACA,QACA,YACA,OACA,WACkB;EAClB,MAAM,OAAO,sBAAsB,MAAM;GAAE;GAAY;GAAO;EAAO,CAAC;EACtE,MAAM,SAAS,MAAM,OAAO,KAAK;GAAE,GAAG;GAAM,aAAa,OAAO;GAAO,aAAa,OAAO;EAAY,CAAC;EACxG,IAAI,OAAO,IAAI;GACb,MAAM,WAAW,MAAM,OAAO,aAAa,OAAO,WAAW,EAAE,YAAY,KAAK,IAAI,EAAE,CAAC;GACvF;EACF;EACA,IAAI,OAAO,cAAc;GAGvB,IAAI,SAAS,SACX,MAAM,SAAS,gBAAgB,OAAO,WAAW;GAEnD,MAAM,WAAW,OAAO,OAAO,aAAa,OAAO,SAAS;GAC5D;EACF;EACA,KAAK,iBAAiB,KAAK,OAAO,OAAO,UAAU,WAAW,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;CAClG;CAEA,MAAM,aAAa,OAAO,YAAgC,UAA+C;EACvG,MAAM,QAAQ,IACZ,SAAS,KAAK,CAAC,CAAC,IAAI,OAAO,WAAW;GACpC,IAAI,OAAO,2BAA2B,KAAA,GACpC;GAIF,IAAI,WAAW,IAAI,OAAO,OAAO,WAAW,SAAS,MAAM,KAAA,GACzD;GAEF,MAAM,cAAc,OAAO,eAAe;GAC1C,MAAM,WAAW,MAAM;IACrB,aAAa,OAAO;IACpB,WAAW,WAAW;IACtB,QAAQ,OAAO;IACf;GACF,CAAC;GACD,MAAM,aACJ,SACA;IAAE,aAAa,OAAO;IAAO,OAAO,OAAO;IAAwB;IAAa,WAAW,WAAW;GAAU,GAChH;IAAE,GAAG;IAAY,QAAQ,OAAO;GAAO,GACvC,OACA,IACF;EACF,CAAC,CACH;CACF;CAEA,MAAM,YAAY,OAAO,MAAwB,WAAmB,OAA6B,WAAmC;EAClI,MAAM,QAAQ,IACZ,WAAW,WAAW,SAAS,CAAC,CAAC,IAAI,OAAO,WAAW;GACrD,IAAI,OAAO,gBAAgB,KAAA,GAGzB;GAEF,MAAM,aACJ,MACA;IAAE,aAAa,OAAO;IAAa,OAAO,OAAO;IAAa,aAAa,OAAO;IAAa;GAAU,GACzG;IAAE;IAAW,QAAQ,OAAO;IAAQ,SAAS;GAAG,GAChD,OACA,MACF;GACA,IAAI,SAAS,OACX,MAAM,WAAW,OAAO,OAAO,aAAa,SAAS;EAEzD,CAAC,CACH;CACF;CAEA,MAAM,iBAAiB,WAAmB,QAAmC;EAE3E,MAAM,QADW,OAAO,IAAI,SAAS,KAAK,QAAQ,QAAQ,EAAA,CACpC,WACpB,IAAI,CAAC,CAAC,OAAO,UAAmB;GAC9B,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC7D,CAAC,CACH;EACA,OAAO,IAAI,WAAW,IAAI;EAC1B,KAAU,WAAW;GACnB,IAAI,OAAO,IAAI,SAAS,MAAM,MAC5B,OAAO,OAAO,SAAS;EAE3B,CAAC;CACH;CAEA,OAAO;EACL,UAAU;GACR,MAAM,YAAY,OAAO;IACvB,cAAc,WAAW,iBAAiB,WAAW,YAAY,KAAK,CAAC;GACzE;GACA,OAAO,WAAW,OAAO,EAAE,UAAU;IACnC,cAAc,iBAAiB,UAAU,UAAU,WAAW,OAAO,MAAM,CAAC;GAC9E;GACA,IAAI,WAAW,OAAO;IACpB,cAAc,iBAAiB,UAAU,OAAO,WAAW,OAAO,IAAI,CAAC;GACzE;GACA,MAAM,OAAO,UAAU;IACrB,MAAM,QAAQ,IACZ,WAAW,KAAK,CAAC,CAAC,IAAI,OAAO,WAAW;KACtC,IAAI,OAAO,gBAAgB,KAAA,GACzB,MAAM,aACJ,OACA;MACE,aAAa,OAAO;MACpB,OAAO,OAAO;MACd,aAAa,OAAO;MACpB,WAAW,OAAO;KACpB,GACA;MAAE,WAAW,OAAO;MAAW,QAAQ,OAAO;MAAQ,SAAS;KAAG,GAClE;MACE,OAAO;MACP,OAAO,OAAO;MACd;MACA,aAAa,OAAO;MACpB,cAAc;MACd,UAAU;KACZ,GACA,IACF;KAEF,MAAM,WAAW,OAAO,OAAO,aAAa,OAAO,SAAS;IAC9D,CAAC,CACH;GACF;GACA,aAAa,WAAW,KAAK,CAAC,CAAC;EACjC;EACA,eAAe,cAAc;GAE3B,MAAM,QADW,OAAO,IAAI,aAAa,SAAS,KAAK,QAAQ,QAAQ,EAAA,CACjD,WACpB,QAAQ,YAAY,CAAC,CAAC,OAAO,UAAmB;IAC9C,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;GAC7D,CAAC,CACH;GACA,OAAO,IAAI,aAAa,WAAW,IAAI;GACvC,KAAU,WAAW;IACnB,IAAI,OAAO,IAAI,aAAa,SAAS,MAAM,MACzC,OAAO,OAAO,aAAa,SAAS;GAExC,CAAC;EACH;EACA;EACA,mBAAmB,SAAS,KAAK,CAAC,CAAC;EACnC,OAAO,OAAO;CAChB;AACF;;;ACjUA,MAAM,oBAAoB;AAG1B,MAAM,eAAe;AACrB,MAAM,oBAAoB;AAC1B,MAAM,yBAAyB;AAC/B,MAAM,eAAe;AAGrB,MAAM,WAAW;AAEjB,MAAM,0BAAU,IAAI,IAA2B,CAAC,WAAW,mBAAmB,CAAC;AAE/E,MAAM,2BAAW,IAAI,IAA8B;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAsCD,SAAS,cAAc,QAAkD;CACvE,QAAQ,QAAR;EACE,KAAK,UACH,OAAO;EAET,KAAK,UACH,OAAO;EAET,KAAK,UACH,OAAO;EAET,SACE,OAAO;CAEX;AACF;AAEA,SAAS,UAAU,OAAmC;CACpD,OAAO,UAAU,cAAc,UAAU,cAAc,UAAU;AACnE;AAEA,SAAgB,oBAAoB,SAIrB;CACb,MAAM,2BAAW,IAAI,IAAqB;CAC1C,MAAM,+BAAe,IAAI,IAAgB;CAEzC,MAAM,SAAS,SAAkB,SAA0B;EACzD,MAAM,SAAS,QAAQ,OAAO,IAAI,IAAI;EACtC,IAAI,WAAW,KAAA,GAAW;GACxB,aAAa,MAAM;GACnB,QAAQ,OAAO,OAAO,IAAI;EAC5B;CACF;CAEA,MAAM,OAAO,SAAkB,MAAiB,OAAe,QAA0B;EACvF,MAAM,SAAS,IAAI;EACnB,QAAQ,OAAO,IACb,MACA,iBAAiB;GACf,QAAQ,OAAO,OAAO,IAAI;GAC1B,IAAI;EACN,GAAG,KAAK,CACV;CACF;CAEA,MAAM,UAAU,cAA4B;EAC1C,MAAM,UAAU,SAAS,IAAI,SAAS;EACtC,IAAI,YAAY,KAAA,GACd;EAEF,KAAK,MAAM,QAAQ,QAAQ,OAAO,KAAK,GACrC,MAAM,SAAS,IAAI;EAErB,SAAS,OAAO,SAAS;CAC3B;CAEA,MAAM,QAAQ,QAAwB,SAAkB,OAA6B,WAA0B;EAC7G,MAAM,OAAO,YAAY,KAAK;EAC9B,IAAI,SAAS,QAAQ,UACnB;EAEF,QAAQ,WAAW;EACnB,QAAQ,aAAa,KAAK,IAAI;EAC9B,QAAQ,UAAU,KAAA;EAClB,MAAM,SAAS,OAAO;EACtB,QAAQ,aAAa,cAAc,OAAO,KAAK,GAAG,QAAQ,WAAW,CAAC;EACtE,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,UAAU;GAClB,QAAQ,OAAO,MAAM,QAAQ,YAAY,KAAK;EAChD,OACE,QAAQ,OAAO,OAAO,OAAO,IAAI,OAAO,EAAE,OAAO,CAAC;EAEpD,IAAI,SAAS,aAAa,oBAAoB;GAC5C,MAAM,UAAU,SAAS,IAAI,OAAO,EAAE;GACtC,IAAI,YAAY,KAAA,KAAa,QAAQ,SAAS;IAG5C,QAAQ,OAAO,OAAO,OAAO,IAAI;KAAE,GAAG;KAAO,KAAK,OAAO,KAAK,CAAC,CAAC;IAAQ,GAAG,EAAE,QAAQ,MAAM,CAAC;IAC5F,QAAQ,aAAa,KAAK,IAAI;GAChC;EACF,CAAC;CACH;CAEA,MAAM,UAAU,QAAwB,SAAkB,UAAmC;EAC3F,IAAI,QAAQ,SACV,QAAQ,OAAO,IAAI,OAAO,IAAI,oBAAoB;GAAE,MAAM,OAAO,KAAK;GAAG,aAAa,QAAQ;GAAa,YAAY;EAAM,CAAC,CAAC;EAEjI,OAAO,OAAO,EAAE;CAClB;CAEA,MAAM,YAAY,QAAwB,YAAY,UAAgB;EACpE,MAAM,OAAO,OAAO,KAAK;EACzB,MAAM,UAAU,OAAO,iBAAiB;EACxC,MAAM,MAAM,KAAK,IAAI;EAErB,IAAI,CAAC,QAAQ,IAAI,KAAK,MAAM,GAAG;GAC7B,MAAM,UAAU,SAAS,IAAI,KAAK,EAAE;GACpC,IAAI,YAAY,KAAA,GACd;GAEF,MAAM,SAAS,OAAO;GACtB,IAAI,KAAK,WAAW,QAAQ;IAC1B,IAAI,SAAS,OAAO,oBAAoB,OAAO,QAAQ,SAAS,MAAM,CAAC;IACvE;GACF;GACA,OAAO,QAAQ,SAAS,cAAc,KAAK,MAAM,CAAC;GAClD;EACF;EAEA,IAAI,UAAU,SAAS,IAAI,KAAK,EAAE;EAClC,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU;IACR,YAAY,cAAc,MAAM,QAAQ,WAAW,CAAC;IACpD,aAAa;IACb,SAAS;IACT,YAAY;IACZ,aAAa;IACb,wBAAQ,IAAI,IAAI;GAClB;GACA,SAAS,IAAI,KAAK,IAAI,OAAO;EAC/B;EACA,MAAM,UAAU;EAChB,MAAM,SAAS,KAAK;EAEpB,MAAM,QAAQ,oBAAoB;GAAE;GAAM;GAAS,aAAa,QAAQ;EAAY,CAAC;EACrF,MAAM,UAAU,UAAU,MAAM,KAAK;EAGrC,MAAM,SAAS,YAAY,QAAQ,eAAe,MAAM,SAAS,OAAO,QAAQ;EAChF,QAAQ,cAAc;EACtB,QAAQ,gBAAgB,MAAM,SAAS;EAEvC,IAAI,CAAC,QAAQ,WAAW,QAAQ,OAAO,IAAI,OAAO,MAAM,KAAA,GAAW;GACjE,QAAQ,UAAU;GAClB,IAAI,WAAW,WAAW;IAIxB,KAAK,QAAQ,SAAS,OAAO,IAAI;IACjC,IAAI,SAAS,SAAS,gBAAgB,MAAM,MAAM,CAAC;GACrD,OACE,IAAI,SAAS,SAAS,yBAAyB;IAC7C,MAAM,UAAU,SAAS,IAAI,OAAO,EAAE;IACtC,IAAI,YAAY,KAAA,KAAa,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,CAAC,MAAM,GAC5D;IAEF,KAAK,QAAQ,SAAS,QAAQ,WAAW,OAAO,IAAI;IACpD,IAAI,SAAS,SAAS,gBAAgB,MAAM,MAAM,CAAC;GACrD,CAAC;GAEH;EACF;EAEA,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,UAAU;GAClB;EACF;EAEA,IAAI,QAAQ;GACV,KAAK,QAAQ,SAAS,OAAO,IAAI;GACjC;EACF;EAEA,QAAQ,UAAU;EAClB,MAAM,QAAQ,MAAM,QAAQ;EAC5B,MAAM,QAAQ,KAAK,IAAI,mBAAmB,yBAAyB,KAAK;EACxE,IAAI,SAAS,SAAS,aAAa;GACjC,MAAM,UAAU,SAAS,IAAI,OAAO,EAAE;GACtC,IAAI,SAAS,YAAY,KAAA,GACvB,KAAK,QAAQ,SAAS,QAAQ,SAAS,KAAK;EAEhD,CAAC;CACH;CAIA,MAAM,SAAS,WAAiC;EAC9C,MAAM,UAAU,SAAS,IAAI,OAAO,EAAE;EACtC,IAAI,YAAY,KAAA,KAAa,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,CAAC,MAAM,GAC5D;EAEF,OAAO,QAAQ,SAAS,OAAO;EAC/B,SAAS,QAAQ,IAAI;CACvB;CAEA,MAAM,OAAO,QAAQ,OAAO,SAAS,WAAW;EAC9C,MAAM,cAAc,OAAO,WAAW,UAAU;GAC9C,IAAI,CAAC,SAAS,IAAI,MAAM,IAAgC,GACtD;GAEF,SAAS,MAAM;GACf,IAAI,MAAM,SAAS,kBAAkB;IACnC,YAAY;IACZ,aAAa,OAAO,WAAW;GACjC;EACF,GAAG,OAAO,KAAK,CAAC,CAAC,OAAO;EACxB,aAAa,IAAI,WAAW;EAC5B,SAAS,MAAM;CACjB,CAAC;CAED,aAAa;EACX,KAAK;EACL,KAAK,MAAM,eAAe,cACxB,YAAY;EAEd,aAAa,MAAM;EACnB,KAAK,MAAM,aAAa,SAAS,KAAK,GACpC,OAAO,SAAS;CAEpB;AACF;;;AClRA,SAAS,cAAsB;CAC7B,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;AACvC;AAGA,SAAS,gBAAgB,KAA4B;CACnD,MAAM,OAAO,IAAI,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,KAAK;CAC9C,OAAO,KAAK,UAAU,MAAM,iBAAiB,KAAK,IAAI,IAAI,OAAO;AACnE;AAEA,eAAsB,mBACpB,UACA,UAAgD,CAAC,GACnB;CAC9B,MAAM,OAAO,QAAQ,UAAU,YAAoB,QAAQ,OAAO,MAAM,gBAAgB,QAAQ,GAAG;CACnG,IAAI,aAAa,MACf,OAAO;EAAE,KAAK,YAAY;EAAG,QAAQ;EAAa,MAAM;CAAK;CAG/D,MAAM,OAAO,KAAK,UAAU,UAAU;CACtC,IAAI,MAAqB;CACzB,IAAI;EACF,MAAM,MAAM,SAAS,MAAM,MAAM;CACnC,QAAQ;EACN,MAAM;CACR;CACA,IAAI,QAAQ,MAAM;EAChB,MAAM,MAAM,gBAAgB,GAAG;EAC/B,IAAI,QAAQ,MAAM;GAChB,IAAI;IACF,MAAM,EAAE,SAAS,MAAM,KAAK,IAAI;IAChC,KAAK,OAAO,QAAW,GACrB,KAAK,iBAAiB,KAAK,qCAA0C,OAAO,IAAA,CAAO,SAAS,CAAC,EAAE,qBAAqB,MAAM;GAE9H,QAAQ,CAAC;GACT,OAAO;IAAE;IAAK,QAAQ;IAAU;GAAK;EACvC;CACF;CAEA,MAAM,MAAM,YAAY;CACxB,MAAM,MAAM,UAAU;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACtD,MAAM,UAAU,MAAM,GAAG,IAAI,KAAK,EAAE,MAAM,IAAM,CAAC;CAEjD,MAAM,MAAM,MAAM,GAAK;CACvB,OAAO;EAAE;EAAK,QAAQ;EAAW;CAAK;AACxC;;;AClDA,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAUlB,SAAS,cAAc,KAAa,KAAwC;CAC1E,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,QAAQ;EACN,OAAO,CAAC;CACV;CACA,MAAM,OAAO;CACb,IAAI,MAAM,YAAY,kBAAkB,CAAC,MAAM,QAAQ,KAAK,QAAQ,GAClE,OAAO,CAAC;CAEV,MAAM,UAAqC,CAAC;CAC5C,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAC5C;EAEF,MAAM,CAAC,KAAK,aAAa;EACzB,IAAI,OAAO,QAAQ,YAAY,QAAQ,IACrC;EAEF,IAAI,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,KAC/E;EAEF,QAAQ,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;CACnC;CACA,OAAO;AACT;AAEA,eAAsB,uBAAuB,SAA4D;CACvG,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,OAAO,QAAQ,UAAU,YAAoB,QAAQ,OAAO,MAAM,gBAAgB,QAAQ,GAAG;CACnG,MAAM,OAAO,KAAK,QAAQ,UAAU,SAAS;CAE7C,IAAI,UAAqC,CAAC;CAC1C,IAAI;EAEF,UAAU,cAAc,MADN,SAAS,MAAM,MAAM,GACV,IAAI,CAAC;EAClC,IAAI;GACF,MAAM,EAAE,SAAS,MAAM,KAAK,IAAI;GAChC,KAAK,OAAO,QAAW,GACrB,KAAK,gBAAgB,KAAK,qCAA0C,OAAO,IAAA,CAAO,SAAS,CAAC,EAAE,qBAAqB,MAAM;EAE7H,QAAQ,CAAC;CACX,QAAQ;EACN,UAAU,CAAC;CACb;CAGA,IAAI,UAAyB,QAAQ,QAAQ;CAC7C,IAAI,SAAS;CAEb,MAAM,QAAQ,OAAO,YAAsD;EACzE,MAAM,OAAkB;GAAE,SAAS;GAAgB,UAAU,QAAQ,KAAK,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC;EAAE;EAC3G,IAAI;GACF,MAAM,gBAAgB,MAAM,MAAM,EAAE,iBAAiB,KAAK,CAAC;GAC3D,SAAS;EACX,SAAS,OAAO;GACd,IAAI,CAAC,QAAQ;IACX,SAAS;IACT,KAAK,uCAAuC,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;GAC/G;EACF;CACF;CAEA,OAAO;EACL;EACA,KAAK,SAAS;GACZ,UAAU,QAAQ,WAAW,MAAM,OAAO,CAAC;EAC7C;EACA,aAAa;CACf;AACF;;;AC/EA,MAAM,+BAAe,IAAI,IAAI;CAAC;CAAO;CAAQ;AAAS,CAAC;AAEvD,SAAgB,oBAAoB,SAAgC;CAClE,OAAO,IAAI,IACT,QAAQ,KAAK,UAAU;EACrB,IAAI;GACF,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC;EACxB,QAAQ;GACN,MAAM,IAAI,MAAM,8DAA8D,KAAK,UAAU,KAAK,GAAG;EACvG;CACF,CAAC,CACH;AACF;AAEA,SAAgB,kBAAkB,IAAqB;CACrD,OAAO,OAAO,SAAS,GAAG,WAAW,MAAM,KAAK,GAAG,WAAW,aAAa;AAC7E;AAGA,SAAgB,cAAc,OAA0D;CACtF,IAAI,UAAU,KAAA,GACZ;CAGF,MAAM,QADS,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI,MAAA,CACpC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK;CAC5C,OAAO,SAAS,KAAK,KAAA,IAAY;AACnC;AAEA,SAAgB,SAAS,KAAsB,YAA8B;CAC3E,IAAK,IAAI,OAAmC,cAAc,MACxD,OAAO;CAET,OAAO,cAAc,cAAc,IAAI,QAAQ,oBAAoB,CAAC,EAAE,YAAY,MAAM;AAC1F;AAEA,SAAgB,SAAS,KAAsB,YAA6B;CAC1E,QAAQ,aAAa,cAAc,IAAI,QAAQ,kBAAkB,IAAI,KAAA,MAAc,IAAI,OAAO,iBAAiB;AACjH;AAEA,SAAgB,iBAAiB,KAA+B;CAC9D,MAAM,UAAU,IAAI,QAAQ;CAC5B,OAAO,OAAO,YAAY,YAAY,QAAQ,YAAY,CAAC,CAAC,SAAS,WAAW;AAClF;AAEA,SAAgB,eAAe,KAA+B;CAC5D,OAAO,CAAC,aAAa,KAAK,IAAI,UAAU,MAAA,CAAO,YAAY,CAAC;AAC9D;AAEA,SAAgB,cAAc,KAAsB,QAAqC;CACvF,MAAM,MAAM,IAAI,QAAQ;CACxB,IAAI,QAAQ,KAAA,GACV,OAAO;CAET,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC;CACxB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,eAAe,IAAI,MAAM,GAClC,OAAO;CAET,MAAM,WAAW,eAAe,KAAK,OAAO,UAAU;CACtD,OAAO,aAAa,QAAQ,WAAW,WAAW,OAAO;AAC3D;AAIA,SAAgB,0BAA0B,KAAsB,QAA+B;CAC7F,IAAI,cAAc,KAAK,MAAM,MAAM,WACjC,OAAO;CAET,QAAQ,eAAe,GAAG,KAAK,iBAAiB,GAAG,MAAM,IAAI,QAAQ,sBAAsB;AAC7F;AAKA,SAAgB,qBAAqB,KAAsB,QAA+B;CACxF,MAAM,UAAU,cAAc,KAAK,MAAM;CACzC,IAAI,YAAY,WACd,OAAO;CAET,OAAO,GAAG,iBAAiB,GAAG,KAAK,eAAe,GAAG,MAAM,YAAY;AACzE;AAEA,SAAS,eAAe,KAAsB,YAAoC;CAChF,MAAM,QAAQ,aAAa,cAAc,IAAI,QAAQ,mBAAmB,IAAI,KAAA,MAAc,IAAI,QAAQ;CACtG,IAAI,SAAS,KAAA,KAAa,SAAS,IACjC,OAAO;CAET,IAAI;EACF,OAAO,IAAI,IAAI,GAAG,SAAS,KAAK,UAAU,IAAI,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC;CAC9E,QAAQ;EACN,OAAO;CACT;AACF;;;ACpGA,SAAgB,OAAO,OAAuB;CAC5C,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;AACnD;AAIA,SAAgB,cAAc,QAA4D;CACxF,MAAM,SAAS,WAAW,KAAA,IAAY,KAAA,IAAY,OAAO,MAAM;CAC/D,QAAQ,cAAc,WAAW,KAAA,KAAa,gBAAgB,OAAO,SAAS,GAAG,MAAM;AACzF;AAEA,SAAgB,aAAa,KAA0C;CACrE,MAAM,MAAM,IAAI,QAAQ;CACxB,IAAI,OAAO,QAAQ,YAAY,QAAQ,IACrC,OAAO;CAET,MAAM,gBAAgB,IAAI,QAAQ;CAClC,IAAI,OAAO,kBAAkB,UAAU;EACrC,MAAM,QAAQ,mBAAmB,KAAK,aAAa;EACnD,IAAI,UAAU,MACZ,OAAO,MAAM;CAEjB;AAEF;AAIA,SAAgB,YAAY,KAA0C;CACpE,IAAI,CAAC,iBAAiB,GAAG,GACvB;CAEF,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC,CAAC,aAAa,IAAI,KAAK;CAC7E,OAAO,QAAQ,QAAQ,QAAQ,KAAK,MAAM,KAAA;AAC5C;;;ACdA,MAAM,eAAe;AAErB,SAAgB,qBAAqB,SAA+C;CAClF,MAAM,0BAAU,IAAI,IAA2B;CAC/C,KAAK,MAAM,CAAC,KAAK,UAAU,QAAQ,OAAO,WAAW,CAAC,GAAG;EACvD,IAAI,QAAQ,QAAQ,cAClB;EAEF,QAAQ,IAAI,KAAK,EAAE,WAAW,MAAM,UAAU,CAAC;CACjD;CACA,OAAO;EAAE,GAAG;EAAS;CAAQ;AAC/B;AAEA,SAAgB,YAAY,UAAkC;CAC5D,IAAI,SAAS,QAAQ,QAAQ,cAAc;EACzC,MAAM,SAAS,SAAS,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EAC9C,IAAI,WAAW,KAAA,GACb,SAAS,QAAQ,OAAO,MAAM;CAElC;CACA,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAClD,SAAS,QAAQ,IAAI,SAAS,UAAU,KAAK,GAAG,EAAE,WAAW,KAAK,IAAI,IAAI,SAAS,MAAM,CAAC;CAC1F,QAAQ,QAAQ;CAChB,OAAO;AACT;AAEA,SAAgB,WAAW,UAA0B,KAA+B;CAClF,MAAM,QAAQ,YAAY,UAAU,GAAG;CACvC,IAAI,UAAU,KAAA,KAAa,UAAU,IACnC,OAAO;CAET,MAAM,MAAM,SAAS,UAAU,KAAK;CACpC,MAAM,QAAQ,SAAS,QAAQ,IAAI,GAAG;CACtC,IAAI,UAAU,KAAA,GACZ,OAAO;CAET,IAAI,MAAM,aAAa,KAAK,IAAI,GAAG;EACjC,SAAS,QAAQ,OAAO,GAAG;EAC3B,QAAQ,QAAQ;EAChB,OAAO;CACT;CACA,OAAO;AACT;AAGA,SAAgB,cAAc,UAA0B,KAA4B;CAClF,MAAM,QAAQ,YAAY,UAAU,GAAG;CACvC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,SAAS,QAAQ,OAAO,SAAS,UAAU,KAAK,CAAC,GAC1F,QAAQ,QAAQ;AAEpB;AAEA,SAAgB,eAAe,UAA0B,OAAe,KAA8B;CACpG,OAAO;EAAC,GAAG,SAAS,WAAW,GAAG;EAAS,WAAW,KAAK,KAAK,SAAS,QAAQ,GAAI;EAAK,GAAG,iBAAiB,UAAU,GAAG;CAAC,CAAC,CAAC,KAAK,IAAI;AACzI;AAEA,SAAgB,iBAAiB,UAA0B,KAA8B;CACvF,OAAO;EAAC,GAAG,SAAS,WAAW;EAAI;EAAa,GAAG,iBAAiB,UAAU,GAAG;CAAC,CAAC,CAAC,KAAK,IAAI;AAC/F;AAEA,SAAS,QAAQ,UAAgC;CAC/C,SAAS,OAAO,KAAK,CAAC,GAAG,SAAS,OAAO,CAAC;AAC5C;AAGA,SAAS,SAAS,UAA0B,OAAuB;CACjE,OAAO,SAAS,WAAW,KAAA,IAAY,OAAO,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI,WAAW,UAAU,SAAS,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACzI;AAEA,SAAS,YAAY,UAA0B,KAA0C;CACvF,MAAM,SAAS,IAAI,QAAQ;CAC3B,IAAI,OAAO,WAAW,UACpB;CAEF,KAAK,MAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;EACpC,MAAM,KAAK,KAAK,QAAQ,GAAG;EAC3B,IAAI,OAAO,IACT;EAEF,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,MAAM,SAAS,YACxC,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK;CAEnC;AAEF;AAIA,SAAS,iBAAiB,UAA0B,KAAgC;CAClF,MAAM,QAAQ;EAAC;EAAU;EAAY;CAAc;CACnD,IAAI,SAAS,KAAK,SAAS,UAAU,GACnC,MAAM,KAAK,QAAQ;CAErB,OAAO;AACT;;;AC/GA,MAAM,cAAc;AAEpB,SAAgB,oBAAoB,SAAyC;CAC3E,OAAO;EAAE,GAAG;EAAS,0BAAU,IAAI,IAAI;EAAG,QAAQ;GAAE,OAAO;GAAG,aAAa;EAAE;CAAE;AACjF;AAEA,SAAgB,YAAY,UAAyB,IAAY,KAAqB;CACpF,OAAO,UAAU,SAAS,SAAS,IAAI,EAAE,GAAG,SAAS,kBAAkB,SAAS,UAAU,GAAG;AAC/F;AAEA,SAAgB,eAAe,UAAyB,IAAY,KAAqB;CACvF,OAAO,KAAK,IAAI,YAAY,UAAU,IAAI,GAAG,GAAG,UAAU,SAAS,QAAQ,SAAS,mBAAmB,SAAS,UAAU,GAAG,CAAC;AAChI;AAEA,SAAgB,cAAc,UAAyB,IAAY,KAAa,gBAAgB,MAAY;CAC1G,MAAM,EAAE,UAAU,aAAa;CAC/B,IAAI,SAAS,OAAO,aACb;OAAA,MAAM,CAAC,KAAK,UAAU,UACzB,IAAI,MAAM,MAAM,eAAe,UAC7B,SAAS,OAAO,GAAG;CAAA;CAIzB,MAAM,QAAQ,SAAS,IAAI,EAAE;CAC7B,IAAI,UAAU,KAAA,KAAa,MAAM,MAAM,eAAe,UACpD,SAAS,IAAI,IAAI;EAAE,OAAO;EAAG,aAAa;CAAI,CAAC;MAE/C,MAAM,SAAS;CAEjB,IAAI,CAAC,eACH;CAEF,IAAI,MAAM,SAAS,OAAO,eAAe,UAAU;EACjD,SAAS,OAAO,QAAQ;EACxB,SAAS,OAAO,cAAc;CAChC,OACE,SAAS,OAAO,SAAS;AAE7B;AAEA,SAAgB,UAAU,UAAyB,IAAkB;CACnE,SAAS,SAAS,OAAO,EAAE;AAC7B;AAEA,SAAS,UAAU,OAAkC,KAAa,UAAkB,KAAqB;CACvG,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,OAAO,MAAM,MAAM,cAAc,WAAW,MAAM,cAAc,WAAW,MAAM;AAChI;;;ACaA,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAC5B,MAAM,iBAAiB;AACvB,MAAM,6BAA6B;AACnC,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AACpC,MAAM,uBAAuB;AAC7B,MAAM,iBAA+B;CAAE,KAAK;CAAQ,mBAAmB;AAAK;AAE5E,SAAgB,cAAc,UAA0B,CAAC,GAAY;CACnE,MAAM,QAAQ,UAAU,OAAO;CAC/B,MAAM,EAAE,YAAY;CACpB,OAAO;EACL;EACA,eAAe,QAAQ,oBAAoB,OAAO,GAAG;EACrD,oBAAoB,KAAK,QAAQ;GAC/B,IAAI;GACJ,IAAI;IACF,WAAW,IAAI,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC,CAAC;GACxD,QAAQ;IACN,OAAO;GACT;GAEA,IAAI,aAAa,WAAW,CAAC,SAAS,WAAW,QAAQ,GACvD,OAAO;GAET,OAAO,gBAAgB,OAAO,UAAU,KAAK,GAAG,CAAC,CAAC,WAAW,IAAI;EACnE;EACA,kBAAkB,QAAS,UAAU,WAAW,MAAM,UAAU,GAAG,IAAI;EACvE;CACF;AACF;AAEA,SAAS,UAAU,SAAoC;CACrD,MAAM,EAAE,WAAW;CACnB,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,mBAC1C,MAAM,IAAI,MACR,0CAA0C,kBAAkB,yFAE9D;CAEF,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,QAAQ,SAAS;CAC/B,IAAI,EAAE,QAAQ,IACZ,MAAM,IAAI,MAAM,uCAAuC;CAEzD,MAAM,aAAa,QAAQ,eAAe;CAC1C,MAAM,WAAW,oBAAoB;EACnC,UAAU,QAAQ,UAAU,YAAY;EACxC,kBAAkB,QAAQ,UAAU,oBAAoB;EACxD,mBAAmB,QAAQ,UAAU,qBAAqB;CAC5D,CAAC;CACD,MAAM,SAAuB;EAAE;EAAY,gBAAgB,oBAAoB,QAAQ,kBAAkB,CAAC,CAAC;CAAE;CAC7G,OAAO;EACL,SAAS,WAAW,KAAA;EACpB;EACA,UAAU,qBAAqB;GAAE;GAAQ;GAAY;GAAO;GAAY,OAAO,QAAQ;EAAS,CAAC;EACjG;EACA,eAAe,cAAc,MAAM;CACrC;AACF;AAEA,SAAS,oBAAoB,OAAkB,KAA2C;CACxF,IAAI,CAAC,MAAM,SACT,OAAO,0BAA0B,KAAK,MAAM,MAAM,IAAI,OAAO;CAI/D,MAAM,WAAW,aAAa,GAAG,KAAK,YAAY,GAAG;CACrD,IAAI,aAAa,KAAA,GACf,OAAO,cAAc,OAAO,KAAK,QAAQ,IAAI;EAAE,KAAK;EAAU,mBAAmB;CAAK,IAAI;CAE5F,IAAI,CAAC,WAAW,MAAM,UAAU,GAAG,GACjC,OAAO;CAET,OAAO,qBAAqB,KAAK,MAAM,MAAM,IAAI;EAAE,KAAK;EAAU,mBAAmB;CAAK,IAAI;AAChG;AAIA,SAAS,cAAc,OAAkB,KAAsB,UAA2B;CACxF,MAAM,KAAK,SAAS,KAAK,MAAM,OAAO,UAAU;CAChD,MAAM,YAAY,CAAC,kBAAkB,EAAE;CACvC,MAAM,MAAM,KAAK,IAAI;CACrB,IAAI,aAAa,YAAY,MAAM,UAAU,IAAI,GAAG,IAAI,GACtD,OAAO;CAET,IAAI,MAAM,cAAc,QAAQ,GAC9B,OAAO;CAET,IAAI,WACF,cAAc,MAAM,UAAU,IAAI,KAAK,KAAK;CAE9C,OAAO;AACT;AAEA,eAAe,gBAAgB,OAAkB,UAAkB,KAAsB,KAAoC;CAC3H,IAAI,aAAa,gBAAgB;EAC/B,IAAI,IAAI,WAAW,OACjB,YAAY,KAAK,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,OAAO,MAAM,CAAC;OAEvE,YAAY,KAAK,KAAK;GAAE,SAAS,MAAM;GAAS,eAAe,MAAM,UAAU,WAAW,MAAM,UAAU,GAAG,IAAI;EAAK,CAAC;EAEzH;CACF;CACA,IAAI,aAAa,eAAe;EAC9B,IAAI,IAAI,WAAW,QACjB,YAAY,KAAK,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,OAAO,OAAO,CAAC;OAExE,MAAM,YAAY,OAAO,KAAK,GAAG;EAEnC;CACF;CACA,IAAI,aAAa,gBAAgB;EAC/B,IAAI,IAAI,WAAW,QACjB,YAAY,KAAK,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,OAAO,OAAO,CAAC;OAExE,aAAa,OAAO,KAAK,GAAG;EAE9B;CACF;CACA,YAAY,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAC9C;AAEA,eAAe,YAAY,OAAkB,KAAsB,KAAoC;CACrG,MAAM,OAAO,UAAU,GAAG;CAC1B,IAAI,CAAC,MAAM,SAAS;EAClB,YAAY,KAAK,KAAK,EAAE,OAAO,4CAA4C,CAAC;EAC5E;CACF;CAGA,IAAI,cAAc,KAAK,MAAM,MAAM,MAAM,WAAW;EAClD,YAAY,KAAK,KAAK,EAAE,OAAO,qBAAqB,CAAC;EACrD;CACF;CACA,MAAM,KAAK,SAAS,KAAK,MAAM,OAAO,UAAU;CAChD,MAAM,UAAU,eAAe,MAAM,UAAU,IAAI,KAAK,IAAI,CAAC;CAC7D,IAAI,UAAU,GAAG;EACf,MAAM,aAAa,OAAO,KAAK,KAAK,UAAU,GAAI,CAAC;EACnD,IAAI,MACF,YAAY,KAAK,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,eAAe,WAAW,CAAC;OAE1F,gBAAgB,KAAK,oBAAoB,EAAE,eAAe,WAAW,CAAC;EAExE;CACF;CACA,MAAM,YAAY,MAAM,gBAAgB,KAAK,GAAG;CAChD,IAAI,cAAc,KAAA,GAChB;CAEF,IAAI,CAAC,MAAM,cAAc,SAAS,GAAG;EACnC,cAAc,MAAM,UAAU,IAAI,KAAK,IAAI,CAAC;EAC5C,IAAI,MACF,YAAY,KAAK,KAAK,EAAE,OAAO,iBAAiB,CAAC;OAEjD,gBAAgB,KAAK,eAAe;EAEtC;CACF;CACA,UAAU,MAAM,UAAU,EAAE;CAC5B,MAAM,SAAS,eAAe,MAAM,UAAU,YAAY,MAAM,QAAQ,GAAG,GAAG;CAC9E,IAAI,MACF,IAAI,UAAU,KAAK;EAAE,cAAc;EAAQ,iBAAiB;CAAW,CAAC,CAAC,CAAC,IAAI;MAE9E,gBAAgB,KAAK,KAAK,EAAE,cAAc,OAAO,CAAC;AAEtD;AAEA,eAAe,gBAAgB,KAAsB,KAAkD;CACrG,MAAM,OAAO,MAAM,SAAS,KAAK,oBAAoB;CACrD,IAAI,SAAS,MAAM;EACjB,YAAY,KAAK,KAAK,EAAE,OAAO,iBAAiB,CAAC;EAEjD,IAAI,KAAK,gBAAgB,IAAI,QAAQ,CAAC;EACtC;CACF;CACA,MAAM,eAAe,IAAI,QAAQ,mBAAmB,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY;CACzF,IAAI;CACJ,IAAI,gBAAgB,qCAClB,YAAY,IAAI,gBAAgB,IAAI,CAAC,CAAC,IAAI,QAAQ;MAC7C,IAAI,gBAAgB,oBACzB,IAAI;EACF,YAAa,KAAK,MAAM,IAAI,CAAC,CAA0B;CACzD,QAAQ;EACN,YAAY,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;EAC/C;CACF;MACK;EACL,YAAY,KAAK,KAAK,EAAE,OAAO,iEAAiE,CAAC;EACjG;CACF;CACA,IAAI,OAAO,cAAc,YAAY,cAAc,IAAI;EACrD,YAAY,KAAK,KAAK,EAAE,OAAO,iBAAiB,CAAC;EACjD;CACF;CACA,OAAO;AACT;AAEA,SAAS,aAAa,OAAkB,KAAsB,KAA2B;CACvF,MAAM,OAAO,UAAU,GAAG;CAC1B,IAAI,MAAM,SAAS;EAEjB,IAAI,cAAc,KAAK,MAAM,MAAM,MAAM,WAAW;GAClD,YAAY,KAAK,KAAK,EAAE,OAAO,qBAAqB,CAAC;GACrD;EACF;EACA,cAAc,MAAM,UAAU,GAAG;CACnC;CACA,MAAM,SAAS,iBAAiB,MAAM,UAAU,GAAG;CACnD,IAAI,MACF,IAAI,UAAU,KAAK;EAAE,cAAc;EAAQ,iBAAiB;CAAW,CAAC,CAAC,CAAC,IAAI;MAE9E,gBAAgB,KAAK,KAAK,EAAE,cAAc,OAAO,CAAC;AAEtD;AAEA,SAAS,UAAU,KAAyE;CAC1F,IAAI,SAAwB;CAC5B,IAAI;EACF,SAAS,IAAI,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC,CAAC,aAAa,IAAI,MAAM;CAC7E,QAAQ;EACN,SAAS;CACX;CAOA,OAAO;EAAE,QAAQ;EAAe,OAAO;EAAU,OAL/C,WAAW,WACP,mCACA,WAAW,cACT,kEACA,KAAA;CAC+C;AACzD;AAEA,SAAS,gBAAgB,KAAqB,UAAkB,SAAwC;CACtG,IAAI,UAAU,KAAK;EAAE;EAAU,iBAAiB;EAAY,GAAG;CAAQ,CAAC,CAAC,CAAC,IAAI;AAChF;AAEA,SAAS,UAAU,KAA+B;CAChD,QAAQ,IAAI,QAAQ,UAAU,GAAA,CAAI,SAAS,kBAAkB;AAC/D;;;ACxSA,MAAM,mBAAmB;CAAC;CAAyB;CAAwB;AAAuB;AAuDlG,IAAa,cAAb,cAAiC,MAAM,CAAC;AAKxC,MAAM,2BAAW,IAAI,IAAwB;CAC3C,CAAC,OAAO,MAAO,EAAE,OAAO,IAAK;CAC7B,CAAC,WAAW,MAAO,EAAE,OAAO,IAAK;CACjC,CAAC,OAAO,MAAO,EAAE,UAAU,IAAK;CAChC,CAAC,cAAc,MAAO,EAAE,UAAU,IAAK;CACvC,CAAC,uBAAuB,MAAO,EAAE,eAAe,KAAM;CACtD,CAAC,eAAe,MAAO,EAAE,UAAU,IAAK;CACxC,CAAC,YAAY,MAAO,EAAE,QAAQ,IAAK;CACnC,CAAC,kBAAkB,MAAO,EAAE,aAAa,IAAK;CAC9C,CAAC,uBAAuB,MAAO,EAAE,UAAU,KAAM;CACjD,CAAC,eAAe,MAAO,EAAE,WAAW,IAAK;CACzC,CAAC,aAAa,MAAO,EAAE,MAAM,KAAM;CACnC,CAAC,oBAAoB,MAAO,EAAE,YAAY,KAAM;CAChD,CAAC,WAAW,MAAO,EAAE,OAAO,IAAK;CACjC,CAAC,iBAAiB,MAAO,EAAE,YAAY,IAAK;AAC9C,CAAC;AAED,MAAM,yBAAS,IAAI,IAAuB;CACxC,CAAC,OAAO,GAAG,MAAO,EAAE,SAAS,CAAE;CAC/B,CAAC,aAAa,GAAG,MAAO,EAAE,SAAS,CAAE;CACrC,CAAC,OAAO,GAAG,GAAG,SAAU,EAAE,OAAO,UAAU,GAAG,IAAI,CAAE;CACpD,CAAC,WAAW,GAAG,GAAG,SAAU,EAAE,OAAO,UAAU,GAAG,IAAI,CAAE;CACxD,CAAC,WAAW,GAAG,MAAO,EAAE,OAAO,CAAE;CACjC,CAAC,eAAe,GAAG,MAAO,EAAE,UAAU,CAAE;CACxC,CAAC,cAAc,GAAG,MAAM,EAAE,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC;CACxD,CAAC,mBAAmB,GAAG,MAAM,EAAE,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC;CAC5D,CAAC,eAAe,GAAG,MAAM,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC;CACpD,CAAC,cAAc,GAAG,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC;CAClD,CAAC,wBAAwB,GAAG,GAAG,SAAU,EAAE,kBAAkB,qBAAqB,GAAG,IAAI,CAAE;CAC3F,CAAC,qBAAqB,GAAG,MAAM,EAAE,eAAe,KAAK,CAAC,CAAC;CACvD,CAAC,mBAAmB,GAAG,MAAM,EAAE,aAAa,KAAK,CAAC,CAAC;CACnD,CAAC,oBAAoB,GAAG,MAAM,EAAE,cAAc,KAAK,CAAC,CAAC;CACrD,CAAC,uBAAuB,GAAG,GAAG,SAAU,EAAE,oBAAoB,cAAc,GAAG,IAAI,CAAE;CACrF,CAAC,yBAAyB,GAAG,GAAG,SAAU,EAAE,qBAAqB,cAAc,GAAG,IAAI,KAAK,CAAE;CAC7F,CAAC,qBAAqB,GAAG,GAAG,SAAU,EAAE,iBAAiB;EAAE,GAAG,EAAE;EAAgB,GAAG,mBAAmB,GAAG,IAAI;CAAE,CAAE;CACjH,CAAC,0BAA0B,GAAG,GAAG,SAAU,EAAE,oBAAoB,uBAAuB,GAAG,IAAI,CAAE;CACjG,CAAC,gBAAgB,GAAG,MAAO,EAAE,WAAW,QAAQ,CAAC,CAAE;CACnD,CAAC,kBAAkB,GAAG,MAAM,EAAE,YAAY,KAAK,CAAC,CAAC;AACnD,CAAC;AAGD,SAAS,cAAc,KAAa,QAA+B;CACjE,IAAI,QAAQ,UAAU,QAAQ,SAC5B,OAAO;CAET,MAAM,QAAQ,+BAA+B,KAAK,GAAG;CACrD,IAAI,CAAC,OACH,MAAM,IAAI,YAAY,GAAG,OAAO,+DAA+D,KAAK;CAEtG,MAAM,QAAQ;EAAE,IAAI;EAAG,GAAG;EAAO,GAAG;EAAQ,GAAG;CAAU,EAAE,MAAM,MAAM;CACvE,MAAM,KAAK,OAAO,MAAM,EAAE,IAAI;CAC9B,OAAO,KAAK,IAAI,KAAK;AACvB;AAEA,SAAS,mBAAmB,KAAa,QAAwC;CAC/E,MAAM,QAAQ,wBAAwB,KAAK,GAAG;CAC9C,IAAI,CAAC,OACH,MAAM,IAAI,YAAY,GAAG,OAAO,oDAAoD,KAAK;CAE3F,OAAO,GAAG,MAAM,KAAM,MAAM,GAAI;AAClC;AAEA,SAAS,uBAAuB,KAAa,QAAwC;CACnF,IAAI,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,SAC3C,OAAO;CAET,MAAM,IAAI,YAAY,GAAG,OAAO,oCAAoC,KAAK;AAC3E;AAEA,SAAS,qBAAqB,OAAe,MAAqC;CAChF,IAAI,UAAU,eAAe,UAAU,WAAW,UAAU,SAC1D,OAAO;CAET,MAAM,IAAI,YAAY,GAAG,KAAK,2CAA2C,MAAM,GAAG;AACpF;AAEA,SAAS,UAAU,KAAa,QAAwB;CACtD,MAAM,OAAO,OAAO,GAAG;CACvB,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,OAChD,MAAM,IAAI,YAAY,GAAG,OAAO,sBAAsB,KAAK;CAE7D,OAAO;AACT;AAEA,SAAgB,UAAU,MAA0B;CAClD,MAAM,QAAkB;EACtB,UAAU,CAAC;EACX,cAAc,CAAC;EACf,UAAU,CAAC;EACX,SAAS,CAAC;EACV,gBAAgB,CAAC;EACjB,cAAc,CAAC;EACf,eAAe,CAAC;EAChB,aAAa,CAAC;CAChB;CACA,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,MAAM,SAAS,SAAS,IAAI,GAAG;EAC/B,IAAI,QAAQ;GACV,OAAO,KAAK;GACZ;EACF;EACA,MAAM,OAAO,OAAO,IAAI,GAAG;EAC3B,IAAI,CAAC,MACH,MAAM,IAAI,YAAY,mBAAmB,KAAK;EAEhD,MAAM,QAAQ,KAAK,IAAI;EACvB,IAAI,UAAU,KAAA,KAAa,MAAM,WAAW,GAAG,GAC7C,MAAM,IAAI,YAAY,GAAG,IAAI,kBAAkB;EAEjD,KAAK,OAAO,OAAO,GAAG;EACtB;CACF;CACA,OAAO;AACT;AAGA,SAAS,aAAa,KAA0B;CAC9C,MAAM,KAAK,IAAI,QAAQ,GAAG;CAC1B,IAAI,MAAM,GACR,MAAM,IAAI,YAAY,oCAAoC,KAAK;CAEjE,MAAM,OAAO,IAAI,MAAM,GAAG,EAAE;CAC5B,MAAM,MAAM,IAAI,MAAM,KAAK,CAAC;CAC5B,IAAI,CAAC,KACH,MAAM,IAAI,YAAY,aAAa,KAAK,yBAAyB;CAEnE,OAAO;EAAE;EAAM,WAAW,QAAQ,GAAG;CAAE;AACzC;AAQA,eAAsB,eAAe,UAAmB,MAAM,QAAQ,IAAI,GAA0B;CAClG,IAAI,OAAsB;CAC1B,IAAI,UAAU;EACZ,OAAO,WAAW,QAAQ,IAAI,WAAW,QAAQ,KAAK,QAAQ;EAC9D,IAAI,CAAC,WAAW,IAAI,GAClB,MAAM,IAAI,YAAY,qBAAqB,MAAM;CAErD,OACE,OAAO,iBAAiB,KAAK,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,MAAM,WAAW,CAAC,CAAC,KAAK;CAEvF,IAAI,CAAC,MACH,OAAO;EAAE,MAAM;EAAM,SAAS,CAAC;CAAE;CAGnC,IAAI;CACJ,IAAI;EAEF,MAAO,MAAM,OAAO,GAAG,cAAc,IAAI,CAAC,CAAC,KAAK,SAAS,SAAS,IAAI,CAAC,CAAC;CAC1E,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,kBAAkB,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CAC3G;CACA,MAAM,WAAW,IAAI;CACrB,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,YAAY,GAAG,KAAK,sDAAsD;CAEtF,MAAM,UAAU,OAAO,aAAa,aAAa,MAAO,SAA2B,IAAI;CACvF,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,MAAM,IAAI,YAAY,GAAG,KAAK,yCAAyC;CAEzE,OAAO;EAAE;EAAe;CAA4B;AACtD;AAEA,MAAM,2BAAW,IAAI,IAAI;CAAC;CAAa;CAAO;CAAa;AAAkB,CAAC;AAE9E,SAAgB,WAAW,MAAuB;CAChD,OAAO,SAAS,IAAI,IAAI;AAC1B;AAyBA,SAAgB,gBAAgB,YAAmC;CACjE,OAAO,aAAa,KAAK,QAAQ,UAAU,GAAG,aAAa,IAAI,KAAK,QAAQ,GAAG,aAAa;AAC9F;AAEA,SAAgB,WAAW,YAA4B;CACrD,IAAI;EACF,OAAO,IAAI,IAAI,UAAU,YAAY,CAAC,CAAC,SAAS,QAAQ,YAAY,EAAE,CAAC,CAAC,YAAY;CACtF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,mBAAmB,UAA2B;CAC5D,IAAI,SAAS,IAAI,QAAQ,GACvB,OAAO;CAET,OAAO,mCAAmC,KAAK,QAAQ;AACzD;AAIA,SAAS,sBAAsB,KAAqB;CAClD,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,oBAAoB,cAAc,KAAK,KAAK;CAClD,MAAM,YAAY,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,oBAAoB,IAAI,MAAM,KAAK;CACvG,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,UAAU,WAAW;CACrC,QAAQ;EACN,MAAM,IAAI,YAAY,gDAAgD,KAAK,UAAU,GAAG,GAAG;CAC7F;CACA,IAAI,IAAI,SAAS,IACf,MAAM,IAAI,YACR,uBAAuB,KAAK,UAAU,GAAG,EAAE,oGAE7C;CAEF,OAAO,IAAI,SAAS,QAAQ,YAAY,EAAE,CAAC,CAAC,YAAY;AAC1D;AAEA,SAAgB,sBACd,OACA,QACA,MAAyB,QAAQ,KACjC,MAAM,QAAQ,IAAI,GACF;CAChB,MAAM,UAAU,IAAI,kBAAkB,UAAU,IAAI,iBAAiB,iBAAiB,IAAI,KAAA;CAC1F,MAAM,OAAO,MAAM,QAAQ,WAAW,OAAO,QAAQ,QAAQ;CAC7D,MAAM,OAAO,MAAM,QAAQ,IAAI,mBAAmB,OAAO,QAAQ,QAAQ;CAEzE,MAAM,UAAU,MAAM,WAAW,IAAI,uBAAuB,OAAO,QAAQ,MAAM,UAAU,KAAA;CAC3F,MAAM,oBAAoB,OAAO,OAAO,QAAQ,iBAAiB;CAEjE,MAAM,gBAAgB,IAAI,IAAI,CAAC,GAAG,MAAM,eAAe,GAAI,OAAO,QAAQ,iBAAiB,CAAC,CAAE,CAAC,CAAC,IAAI,qBAAqB,CAAC;CAC1H,MAAM,WAAW,KACd,KAAK,CAAC,CACN,QAAQ,YAAY,EAAE,CAAC,CACvB,YAAY;CAIf,MAAM,kBACJ,CAAC,WACD,CAAC,qBACD,CAAC,OAAO,QAAQ,wBAChB,CAAC,WAAW,IAAI,KAChB,CAAC,MAAM,YACP,CAAC,cAAc,IAAI,QAAQ;CAE7B,MAAM,WACJ,MAAM,YAAY,SAAS,OAAO,QAAQ,aAAa,OACnD,OACC,MAAM,YAAY,IAAI,wBAAwB,OAAO,QAAQ,YAAY,gBAAgB,OAAO,IAAI;CAE3G,MAAM,YAAY,SAChB,IAAI,KAAK,EACL,MAAM,GAAG,CAAC,CACX,OAAO,OAAO,CAAC,CACf,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;CAC/B,MAAM,WAAW,MAAM,SAAS,SAAS,MAAM,WAAW,SAAS,sBAAsB;CACzF,MAAM,UAAU,MAAM,QAAQ,SAAS,MAAM,UAAU,SAAS,qBAAqB;CAErF,MAAM,OAAuB;EAC3B,GAAG,OAAO,QAAQ;EAClB,QAAQ;EACR,YAAY,MAAM,cAAc,OAAO,QAAQ,MAAM;EACrD,gBAAgB,CAAC,GAAI,OAAO,QAAQ,MAAM,kBAAkB,CAAC,GAAI,GAAG,MAAM,cAAc;CAC1F;CAIA,MAAM,eADc,QAAQ,OAAO,KAAK,qBAAqB,kBAEzD,uBACA,IAAI,IAAI;EACN,GAAG;EACH,GAAG,CAAC,GAAG,MAAM,cAAc,GAAI,OAAO,QAAQ,gBAAgB,CAAC,CAAE,CAAC,CAAC,KAAK,SAAS,KAAK,YAAY,CAAC;EACnG,GAAG;CACL,CAAC;CAEL,MAAM,MAAM,MAAM,OAAO,OAAO,QAAQ,OAAO;CAC/C,MAAM,YAAY,MAAM,aAAa,OAAO,QAAQ,aAAa;CAEjE,MAAM,cAAc,MAAM,YAAY,SAAS,MAAM,cAAe,OAAO,QAAQ,eAAe,CAAC;CACnG,KAAK,MAAM,UAAU,aAAa;EAEhC,IAAI,WAAW,KACb,MAAM,IAAI,YAAY,qCAAmC;EAE3D,IAAI;EACJ,IAAI;GACF,SAAS,IAAI,IAAI,MAAM;EACzB,QAAQ;GACN,MAAM,IAAI,YAAY,6CAA6C,QAAQ;EAC7E;EACA,IAAI,OAAO,WAAW,QACpB,MAAM,IAAI,YAAY,iEAAiE,QAAQ;CAEnG;CACA,IAAI,CAAC,OAAO,MAAM,MAChB,MAAM,IAAI,YAAY,oEAAoE;CAG5F,MAAM,EACJ,MAAM,IACN,MAAM,IACN,MAAM,IACN,UAAU,IACV,cAAc,KACd,eAAe,KACf,SAAS,IACT,KAAK,MACL,WAAW,KACX,aAAa,OACb,MAAM,KACN,GAAG,kBACD,OAAO;CACX,MAAM,UAA+B,EAAE,GAAG,cAAc;CACxD,IAAI,UAAU,QACZ,QAAQ,kBAAkB;CAE5B,IAAI,SAAS,UAAU,MAAM,SAC3B,QAAQ,YAAY;EAClB,GAAG,OAAO,QAAQ;EAClB,GAAI,SAAS,SAAS,EAAE,OAAO,QAAQ,IAAI,CAAC;EAC5C,GAAI,MAAM,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;CAEF,IAAI,MAAM,SAAS,MAAM,oBAAoB,KAAA,GAC3C,QAAQ,QAAQ;EACd,GAAG,OAAO,QAAQ;EAClB,GAAI,MAAM,QAAQ,EAAE,SAAS,KAAK,IAAI,CAAC;EACvC,GAAI,MAAM,oBAAoB,KAAA,IAAY,EAAE,YAAY,MAAM,gBAAgB,IAAI,CAAC;CACrF;CAEF,IAAI,OAAO,QAAQ,OACjB,QAAQ,QAAQ;EACd,GAAG,OAAO,QAAQ;EAClB,KAAK,IAAI,wBAAwB,OAAO,QAAQ,MAAM,OAAO,KAAA;EAC7D,KAAK,OAAO,QAAQ,MAAM,SAAS,YAAY,QAAQ,OAAO,MAAM,gBAAgB,QAAQ,GAAG;CACjG;CAGF,IAAI,MAAM,SAAS,QACjB,QAAQ,WAAW,MAAM;CAE3B,IAAI,MAAM,sBAAsB,KAAA,GAC9B,QAAQ,oBAAoB,MAAM;CAEpC,IAAI,MAAM,uBAAuB,KAAA,GAC/B,QAAQ,qBAAqB,MAAM;CAErC,IAAI,MAAM,sBAAsB,SAC9B,QAAQ,oBAAoB;MACvB,IAAI,MAAM,sBAAsB,KAAA,GACrC,QAAQ,oBAAoB;EAAE,GAAG,OAAO,QAAQ;EAAmB,SAAS,MAAM,sBAAsB;CAAK;CAE/G,IAAI,MAAM,gBACR,QAAQ,iBAAiB;EAAE,GAAG,OAAO,QAAQ;EAAgB,GAAG,MAAM;CAAe;CAGvF,IAAI,MAAM,iBAAiB,OACzB,OAAO,QAAQ;CAEjB,MAAM,iBAAiB,MAAM,iBAAiB;CAG9C,IAAI,kBAAkB,CAAC,QAAQ,uBAC7B,QAAQ,wBAAwB,MAAM,aAAa,SAAS,MAAM,eAAe,CAAC,QAAQ,CAAC;CAG7F,MAAM,eAAe,kBAAkB,QAAQ,iBAAiB,KAAA;CAEhE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,YAAY,OAAO;EACnB;EACA;EACA;EACA,SAAS,OAAO,QAAQ;EACxB;EACA;EACA;EACA,MAAM,YAAY,MAAM;EACxB,MAAM,MAAM,QAAQ;EACpB;EACA;CACF;AACF;AAGA,SAAS,YAAY,QAA8C;CACjE,MAAM,OAAO,OAAO,QAAQ;CAC5B,IAAI,CAAC,MACH;CAEF,KAAK,MAAM,SAAS;EAAC;EAAW;EAAS;EAAU;CAAO,GACxD,IAAI,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW,IACrD,MAAM,IAAI,YAAY,QAAQ,MAAM,qCAAqC;CAG7E,MAAM,OAAO,OAAO,OAAO,QAAQ,OAAO,IAAI,IAAI,QAAQ,IAAI;CAC9D,OAAO;EAAE,GAAG;EAAM,SAAS,QAAQ,MAAM,KAAK,OAAO;CAAE;AACzD;;;ACpeA,SAAS,WAAW,OAAuB;CACzC,OAAO,MAAM,QAAQ,aAAa,OAAO;EAAE,KAAK;EAAS,KAAK;EAAQ,KAAK;EAAQ,MAAK;EAAU,KAAK;CAAQ,EAAA,CAAG,MAAM,CAAC;AAC3H;AAEA,SAAgB,gBAAgB,SAAmC;CACjE,MAAM,EAAE,QAAQ,OAAO,OAAO,YAAY,kBAAkB;CAC5D,MAAM,SACJ,iBAAiB,aAAa,8BAA8B,WAAW,aAAa,EAAE,WAAW,WAAW,UAAU,EAAE,MAAM;CAChI,MAAM,QAAQ,QAAQ,iCAAiC,WAAW,KAAK,EAAE,QAAQ;CAEjF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA8CuB,WAAW,MAAM,EAAE;;;MAG7C,MAAM;;4BAEgB,WAAW,KAAK,EAAE;;MAExC,OAAO;;;;;;AAMb;;;ACxEA,MAAM,iCAAiB,IAAI,IAA2B;CAAC;CAAY;CAAW;AAAmB,CAAC;AAQlG,SAAgB,uBAAuB,UAA2C;CAChF,OAAO,SAAS,MAAM,YAAY,eAAe,IAAI,QAAQ,MAAM,CAAC;AACtE;AAKA,SAAS,YAAY,KAAyD;CAC5E,IAAI,QAAQ,aAAa,UAEvB,OAAO;EAAE,SAAS;EAAc,MAAM;GAAC;GAAM;GAAM,OAAO,GAAG;EAAC;CAAE;CAElE,IAAI,QAAQ,aAAa,SACvB,OAAO;EACL,SAAS;EACT,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,IAAI;EACvB;CACF;CAEF,OAAO;AACT;AAeA,SAAgB,cAAc,QAAoB,MAAmC;CACnF,MAAM,aAAmB,KAAK,IAAI,uBAAuB,OAAO,KAAK,CAAC,CAAC;CACvE,OAAO,SAAS,WAGd,OAAO,WAAW,UAAU;EAC1B,IAAI,MAAM,SAAS,oBAAoB,MAAM,SAAS,oBAAoB,MAAM,SAAS,iBACvF,KAAK;CAET,GAAG,OAAO,KAAK,CAAC,CAAC,OAAO,CAC1B;CACA,KAAK;AACP;AAEA,SAAgB,eAAe,UAAsE,CAAC,GAAa;CACjH,MAAM,OAAO,YAAY,QAAQ,OAAO,QAAQ,GAAG;CACnD,IAAI,QAA6B;CACjC,IAAI,YAAY,SAAS;CAEzB,MAAM,aAAmB;EACvB,MAAM,UAAU;EAChB,QAAQ;EACR,SAAS,KAAK;CAChB;CAEA,MAAM,cAAoB;EACxB,IAAI,CAAC,QAAQ,CAAC,aAAa,OACzB;EAEF,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,KAAK,SAAS,KAAK,MAAM,EAAE,OAAO,SAAS,CAAC;EAC9D,QAAQ;GACN,YAAY;GACZ,QAAQ,gBAAgB,mBAAmB,KAAK,SAAS;GACzD;EACF;EACA,QAAQ;EAER,QAAQ,GAAG,eAAe;GACxB,IAAI,UAAU,SACZ,QAAQ;GAEV,YAAY;GACZ,QAAQ,gBAAgB,GAAG,KAAK,QAAQ,kCAAkC;EAC5E,CAAC;EACD,QAAQ,GAAG,cAAc;GACvB,IAAI,UAAU,SACZ,QAAQ;EAEZ,CAAC;EAED,QAAQ,MAAM;CAChB;CAEA,OAAO;EACL,IAAI,OAAgB;GAClB,OAAO,UAAU;EACnB;EACA,MAAM,WAAY,SAAS,MAAM,IAAI,KAAK;EAC1C,SAAS;CACX;AACF;;;AC/GA,MAAM,gBAAwC;CAC5C,SAAS;CACT,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;CACR,SAAS;CACT,UAAU;CACV,QAAQ;CACR,SAAS;CACT,QAAQ;AACV;AAEA,SAAgB,eAAe,UAA0B;CACvD,MAAM,MAAM,SAAS,YAAY,GAAG;CACpC,IAAI,MAAM,GACR,OAAO;CAET,OAAO,cAAc,SAAS,MAAM,GAAG,CAAC,CAAC,YAAY,MAAM;AAC7D;AAEA,SAAgB,eAAe,UAA2B;CACxD,MAAM,MAAM,SAAS,YAAY,GAAG;CACpC,IAAI,MAAM,GACR,OAAO;CAET,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,YAAY,KAAK;AAC9C;AAEA,SAAgB,kBAAkB,MAAc,UAAiC;CAC/E,IAAI;CACJ,IAAI;EACF,UAAU,mBAAmB,QAAQ;CACvC,QAAQ;EACN,OAAO;CACT;CACA,IAAI,QAAQ,SAAS,IAAI,GACvB,OAAO;CAIT,MAAM,YAAY,QAAQ,KAAK,MAAM,OAAO,CAAC;CAC7C,MAAM,OAAO,QAAQ,IAAI;CACzB,IAAI,cAAc,QAAQ,CAAC,UAAU,WAAW,OAAO,GAAG,GACxD,OAAO;CAET,OAAO;AACT;AAKA,SAAgB,aAAa,MAAsB;CAIjD,OAAO;EACL;EACA,CAAC,wCAAwC,GAL5B,CAAC,GAAG,KAAK,SAAS,oDAAoD,CAAC,CAAC,CAAC,KACrF,UAAU,WAAW,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,IAAK,MAAM,CAAC,CAAC,OAAO,QAAQ,EAAE,EAIrC,CAAC,CAAC,CAAC,KAAK,GAAG;EAC5D;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAEA,SAAS,YAAY,MAAsC;CACzD,OAAO;EACL,2BAA2B,aAAa,IAAI;EAE5C,mBAAmB;EACnB,0BAA0B;EAC1B,mBAAmB;CACrB;AACF;AAEA,SAAgB,SAAS,KAAsB,KAAqB,QAAgB,MAAc,OAAqB;CACrH,MAAM,OAAO,OAAO,KAAK,MAAM,MAAM;CACrC,IAAI,UAAU,QAAQ;EACpB,gBAAgB;EAChB,kBAAkB,KAAK;EACvB,iBAAiB;EACjB,GAAG,YAAY,IAAI;CACrB,CAAC;CACD,IAAI,IAAI,IAAI,WAAW,SAAS,KAAA,IAAY,IAAI;AAClD;AAIA,eAAsB,UACpB,KACA,KACA,UACA,UAAmC,CAAC,GACd;CACtB,IAAI,IAAI,WAAW,SAAS,IAAI,WAAW,QACzC,OAAO;CAGT,IAAI;CACJ,IAAI;EACF,MAAM,OAAO,MAAM,KAAK,QAAQ;EAChC,IAAI,CAAC,KAAK,OAAO,GACf,OAAO;EAET,OAAO,KAAK;CACd,QAAQ;EACN,OAAO;CACT;CAEA,MAAM,cAAc,eAAe,QAAQ;CAC3C,IAAI,YAAY,WAAW,WAAW,GAAG;EACvC,MAAM,OAAO,MAAM,SAAS,UAAU,MAAM,CAAC,CAAC,YAAY,KAAA,CAAS;EACnE,IAAI,SAAS,KAAA,GACX,OAAO;EAET,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,YAAY,wCAAwC,2BAA2B;EACrH,OAAO;CACT;CACA,IAAI,UAAU,KAAK;EACjB,gBAAgB;EAChB,kBAAkB;EAClB,iBAAiB,QAAQ,YAAY,wCAAwC;EAC7E,0BAA0B;CAC5B,CAAC;CACD,IAAI,IAAI,WAAW,QAAQ;EACzB,IAAI,IAAI;EACR,OAAO;CACT;CACA,MAAM,IAAI,SAAe,mBAAmB;EAC1C,MAAM,SAAS,iBAAiB,QAAQ;EACxC,OAAO,GAAG,eAAe;GACvB,IAAI,QAAQ;GACZ,eAAe;EACjB,CAAC;EACD,OAAO,GAAG,aAAa,eAAe,CAAC;EACvC,OAAO,KAAK,GAAG;CACjB,CAAC;CACD,OAAO;AACT;;;ACpIA,MAAM,gBAAgB;AAoBtB,SAAgB,iBAAyB;CACvC,IAAI,WAAW,KAAK,cAAc,YAAY,CAAC,GAC7C,OAAO;CAET,MAAM,IAAI,MAAM,yBAAyB,aAAa,2DAAgE;AACxH;AAIA,SAAgB,gBAAgB,cAAqE;CACnG,IAAI,iBAAiB,MACnB,aAAa;CAEf,QAAQ,QAAQ;EACd,MAAM,SAAS,IAAI,QAAQ;EAE3B,IAAI,WAAW,KAAA,GACb,OAAO;EAET,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI,aAAa,IACf,OAAO;EAET,OAAO,mBAAmB,QAAQ,KAAK,aAAa,IAAI,QAAQ;CAClE;AACF;AAIA,SAAgB,oBAAoB,WAAoB,gBAAkC;CACxF,IAAI,cAAc,QAAQ,cAAc,KAAA,KAAa,cAAc,OACjE,OAAO;CAET,MAAM,EAAE,UAAU,UAAW,OAAO,cAAc,WAAW,YAAY,CAAC;CAC1E,IAAI,OAAO,aAAa,WACtB,OAAO;CAGT,OAAO,EADQ,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,MACxE,CAAC;AACrB;AAIA,SAAS,eACP,MACA,SACA,aACA,WAC8D;CAC9D,OAAO,OAAO,KAAK,QAAQ;EACzB,IAAI,CAAC,YAAY,GAAG,GAAG;GACrB,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAClE,IAAI,IACF,sRAIF;GACA;EACF;EACA,IAAI,MAAM,KAAK,kBAAkB,KAAK,GAAG,GACvC;EAEF,IAAI,MAAM,UAAU,KAAK,GAAG,GAC1B;EAIF,IAAI,YAAY,KAAA,GAAW;GACzB,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAClE,IAAI,IAAI,iDAAiD;GACzD;EACF;EAEA,MAAM,WAAW,IAAI,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC,CAAC;EAE5D,IAAI,eAAe,QAAQ,GAAG;GAC5B,MAAM,WAAW,kBAAkB,SAAS,QAAQ;GACpD,IAAI,CAAC,UAAU;IACb,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;IAClE,IAAI,IAAI,aAAa;IACrB;GACF;GACA,MAAM,SAAS,MAAM,UAAU,KAAK,KAAK,UAAU,EACjD,WAAW,SAAS,WAAW,UAAU,EAC3C,CAAC;GACD,IAAI,WAAW,UACb;GAEF,IAAI,WAAW,sBAAsB;IACnC,IAAI,UAAU,KAAK,EAAE,OAAO,YAAY,CAAC;IACzC,IAAI,IAAI;IACR;GACF;GACA,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAClE,IAAI,IAAI,WAAW;GACnB;EACF;EAEA,IAAI,IAAI,WAAW,SAAS,IAAI,WAAW,QAAQ;GACjD,IAAI,UAAU,KAAK,EAAE,OAAO,YAAY,CAAC;GACzC,IAAI,IAAI;GACR;EACF;EAEA,IAAI,KAAK,WAAW,CAAC,KAAK,gBAAgB,GAAG,GAAG;GAC9C,SAAS,KAAK,KAAK,KAAK,gBAAgB,KAAK,UAAU,GAAG,CAAC,GAAG,UAAU;GACxE;EACF;EAIA,IAAI,MADiB,UAAU,KAAK,KADtB,KAAK,SAAS,YACa,GAAO,EAAE,WAAW,MAAM,CAAC,MACrD,UAAU;GACvB,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAClE,IAAI,IAAI,+CAA+C;EACzD;CACF;AACF;AAEA,eAAsB,cAAc,QAAwB,UAAwB,CAAC,GAAsB;CACzG,MAAM,UAAU,OAAO,MAAO,OAAO,WAAW,eAAe,IAAK,KAAA;CACpE,MAAM,YACJ,OAAO,mBAAmB,CAAC,OAAO,oBAAoB,MAAM,mBAAmB,OAAO,QAAQ,IAAI;CACpG,MAAM,cAAc,OAAO,oBACvB;EAAE,GAAG,OAAO;EAAM,QAAQ,KAAA;CAAU,IACpC,YACE;EAAE,GAAG,OAAO;EAAM,QAAQ,UAAU;CAAI,IACxC,OAAO;CACb,MAAM,WACJ,YAAY,WAAW,KAAA,KAAa,OAAO,WAAW,MAAM,uBAAuB,EAAE,UAAU,OAAO,SAAS,CAAC,IAAI,KAAA;CACtH,MAAM,OAAO,cAAc,WAAW;EAAE,GAAG;EAAa;CAAS,IAAI,WAAW;CAGhF,IAAI,OAAO,iBAAiB,QAAQ,CAAC,OAAO,qBAAqB,CAAC,KAAK,SACrE,MAAM,IAAI,MACR,8JAEF;CAGF,IAAI,OAAO,YAAY,SAAS,KAAK,CAAC,OAAO,qBAAqB,CAAC,KAAK,SACtE,MAAM,IAAI,MACR,8IAEF;CAEF,MAAM,cAAc,gBAAgB,OAAO,YAAY;CAGvD,MAAM,sBAAsB,OAAO,oBAAoB,OAAO,QAAQ,eAAgB,KAAK;CAC3F,MAAM,iBAAiB,OAAO,QAAQ,qBAAqB,KAAA;CAC3D,MAAM,mBAAmB,OAAO,QAA2C;EACzE,IAAI,CAAC,YAAY,GAAG,GAClB,OAAO;EAET,MAAM,YAAY,MAAM,oBAAoB,GAAG;EAC/C,OAAO,oBAAoB,WAAW,cAAc,IAAI,YAAY;CACtE;CAEA,MAAM,OACJ,OAAO,SAAS,KAAA,IACZ,KAAA,IACA,MAAM,oBAAoB;EACxB,QAAQ,OAAO;EACf,UAAU,OAAO;EACjB,cAAc;CAChB,CAAC;CAEP,MAAM,WAAW,eAAe,MAAM,SAAS,aAAa,MAAM,iBAAiB,gBAAgB,MAAM,gBAAgB,CAAC;CAE1H,MAAM,EAAE,SAAS,cAAc,OAAO,UAAU,iBAAiB,MAAM;CAEvE,MAAM,SAAS,mBAAmB;EAChC,GAAG,OAAO;EACV,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;EAEvC,kBAAkB,OAAO,QAAQ,oBAAoB;EACrD;EACA;EACA;EAEA,eACE,SAAS,KAAA,IACL,OAAO,QAAQ,gBACf;GACE,GAAG,OAAO,QAAQ;GAClB,iBAAiB,iBAAiB;IAChC,OAAO,QAAQ,eAAe,iBAAiB,YAAY;IAC3D,KAAK,eAAe,YAAY;GAClC;EACF;EACN;EACA,GAAI,OAAO,YAAY,SAAS,EAAE,MAAM,EAAE,SAAS,OAAO,YAAY,EAAE,IAAI,CAAC;EAG7E,eAAe,QAAS,YAAY,GAAG,IAAI,oBAAoB,GAAG,IAAI;CACxE,CAAC;CAED,MAAM,OAAO,OAAO,YAChB,eAAe,EACb,gBAAgB,WAAW;EACzB,IAAI,CAAC,QAAQ,OACX,QAAQ,OAAO,MAAM,+CAA+C,OAAO,GAAG;CAElF,EACF,CAAC,IACD,KAAA;CACJ,IAAI;CACJ,IAAI,MAAM;EACR,cAAc,OAAO,UAAU,IAAI;EACnC,QAAQ,kBAAkB,KAAK,IAAI,uBAAuB,OAAO,SAAS,KAAK,CAAC,CAAC,GAAG,aAAa;EACjG,MAAM,MAAM;CACd;CAEA,IAAI;CACJ,IAAI,MAAM;EAIR,MAAM,KAAK,SAAS,OAAO,mBAAmB;EAC9C,iBAAiB,oBAAoB;GAAE,QAAQ,OAAO;GAAU,QAAQ,KAAK;EAAS,CAAC;CACzF;CAKA,MAAM,UAA4B,CAAC;CACnC,KAAK,MAAM,WAAW,QAAQ,WAAW,CAAC,GACxC,IAAI,OAAO,aAAa,OAAO,GAC7B,QAAQ,KAAK,OAAO;CAIxB,IAAI;CACJ,IAAI;EACD,CAAC,CAAE,QAAS,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,IAAI;CAC3D,SAAS,OAAO;EAGd,KAAK,MAAM,WAAW,SACpB,OAAO,eAAe,QAAQ,OAAO,EAAE;EAEzC,MAAM,OAAO,MAAM;EACnB,MAAM;CACR;CACA,MAAM,cAAc,OAAO,SAAS,aAAa,OAAO,SAAS,OAAO,cAAc,OAAO;CAC7F,MAAM,MAAM,UAAU,YAAY,SAAS,GAAG,IAAI,IAAI,YAAY,KAAK,YAAY,GAAG;CAEtF,IAAI,sBAAkC,CAAC;CACvC,MAAM,SAAS,IAAI,SAAe,MAAM;EACtC,gBAAgB;CAClB,CAAC;CAED,IAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,OAAO,QAAQ;GAAE,OAAO,OAAO,MAAM;GAAO,SAAS,KAAK,YAAY;GAAG,OAAO,KAAK,SAAS,MAAM;EAAE;EAC5G,QAAQ,OAAO,MAAM,YAAY;GAAE;GAAK;GAAQ;GAAW,aAAa,KAAK;GAAS;EAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI;CACjH;CAEA,OAAO;EACL;EACA,SAAS,QAAQ,KAAK,YAAY,QAAQ,OAAO,EAAE;EACnD;EACA;EACA;EACA,QAAQ,iBAAiB,OAAO,MAAM,YAAY;EAClD,OAAO,YAAY;GACjB,IAAI,OACF,cAAc,KAAK;GAErB,MAAM,QAAQ;GACd,iBAAiB;GACjB,MAAM,OAAO,MAAM;GAGnB,MAAM,UAAU,QAAQ;GACxB,IAAI,MAGF,MAAM,QAAQ,KAAK,CAAC,KAAK,SAAS,OAAO,iBAAiB,GAAG,IAAI,SAAS,YAAY,WAAW,SAAS,GAAK,CAAC,CAAC,CAAC;GAEpH,MAAM,MAAM;GACZ,cAAc;EAChB;CACF;AACF;AAMA,SAAgB,iBAAiB,QAA0C;CACzE,MAAM,EAAE,aAAa;CACrB,MAAM,UAAU,EAAE,GAAG,OAAO,QAAQ,QAAQ;CAC5C,MAAM,QAAQ,EAAE,GAAG,OAAO,QAAQ,MAAM;CACxC,MAAM,QAAQ,EAAE,GAAG,OAAO,QAAQ,MAAM;CACxC,IAAI,CAAC,UACH,OAAO;EAAE;EAAS;EAAO;CAAM;CAEjC,QAAQ,UAAU,uBAAuB;EACvC,KAAK,KAAK,UAAU,QAAQ;EAC5B,UAAU,OAAO,YAAY;GAC3B,QAAQ,OAAO,MACb,qCAAqC,QAAQ,GAAG,cAAc,QAAQ,KAAK,IACtE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAC9D;EACF;CACF,CAAC;CACD,IAAI,MAAM,gBAAgB,KAAA,GACxB,MAAM,cAAc,KAAK,UAAU,QAAQ;CAE7C,MAAM,UAAU,qBAAqB,KAAK,UAAU,YAAY,IAAI,UAAU;EAC5E,QAAQ,OAAO,MAAM,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;CAC5H,CAAC;CACD,MAAM,eAAe,OAAO,eAAe,uBAAuB,KAAK,UAAU,eAAe,CAAC,IAAI,KAAA;CACrG,OAAO;EAAE;EAAS;EAAO;EAAO,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;CAAG;AAC5E;AAUA,SAAgB,YAAY,EAAE,KAAK,QAAQ,WAAW,aAAa,QAA+B;CAChG,MAAM,QAAQ,CAAC,IAAI,iBAAiB,KAAK;CACzC,IAAI,OAAO,mBACT,MAAM,KAAK,yDAAyD;MAC/D,IAAI,WAAW,WAAW,WAC/B,MAAM,KAAK,0BAA0B,UAAU,OAAO,qBAAqB,UAAU,KAAK,6CAA6C;MAClI,IAAI,WAAW,WAAW,aAC/B,MAAM,KAAK,0BAA0B,UAAU,OAAO,gFAAgF;MACjI,IAAI,WAAW,WAAW,UAC/B,MAAM,KAAK,2BAA2B,UAAU,MAAM;MACjD,IAAI,aACT,MAAM,KAAK,+DAA+D;MAE1E,MAAM,KAAK,2DAA2D;CAExE,IAAI,CAAC,OAAO,KACV,MAAM,KAAK,qDAAqD;CAElE,IAAI,CAAC,OAAO,WACV,MAAM,KAAK,qDAAqD;CAElE,IAAI,OAAO,YAAY,QACrB,MAAM,KAAK,WAAW,OAAO,YAAY,KAAK,IAAI,EAAE,gCAAgC;CAEtF,MAAM,KACJ,OAAO,WACH,gCAAgC,KAAK,OAAO,UAAU,QAAQ,MAC9D,6DACN;CACA,IAAI,MACF,MAAM,KACJ,6BAA6B,KAAK,MAAM,KACnC,KAAK,YAAY,IAAI,8BAA8B,GAAG,KAAK,QAAQ,cACnE,KAAK,UAAU,IAAI,KAAK,KAAK,KAAK,MAAM,gBAC/C;CAEF,IAAI,OAAO,YACT,MAAM,KAAK,YAAY,OAAO,YAAY;CAE5C,MAAM,KAAK,EAAE;CACb,OAAO;AACT"}