@vibesdotdev/terminal-client 0.0.0-stage → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +102 -2
- package/dist/ansi/index.d.ts +11 -0
- package/dist/ansi/index.d.ts.map +1 -0
- package/dist/ansi/index.js +11 -0
- package/dist/ansi/index.js.map +1 -0
- package/dist/ansi/keymap.d.ts +77 -0
- package/dist/ansi/keymap.d.ts.map +1 -0
- package/dist/ansi/keymap.js +257 -0
- package/dist/ansi/keymap.js.map +1 -0
- package/dist/api-client-plugin.d.ts +4 -0
- package/dist/api-client-plugin.d.ts.map +1 -0
- package/dist/api-client-plugin.js +16 -0
- package/dist/api-client-plugin.js.map +1 -0
- package/dist/client/http-session.d.ts +44 -0
- package/dist/client/http-session.d.ts.map +1 -0
- package/dist/client/http-session.js +457 -0
- package/dist/client/http-session.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client.d.ts +56 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +169 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/pty-bridge/index.d.ts +15 -0
- package/dist/pty-bridge/index.d.ts.map +1 -0
- package/dist/pty-bridge/index.js +15 -0
- package/dist/pty-bridge/index.js.map +1 -0
- package/dist/pty-bridge/protocol.d.ts +6 -0
- package/dist/pty-bridge/protocol.d.ts.map +1 -0
- package/dist/pty-bridge/protocol.js +6 -0
- package/dist/pty-bridge/protocol.js.map +1 -0
- package/dist/pty-bridge/server.d.ts +7 -0
- package/dist/pty-bridge/server.d.ts.map +1 -0
- package/dist/pty-bridge/server.js +9 -0
- package/dist/pty-bridge/server.js.map +1 -0
- package/dist/remote-session.d.ts +51 -0
- package/dist/remote-session.d.ts.map +1 -0
- package/dist/remote-session.js +326 -0
- package/dist/remote-session.js.map +1 -0
- package/dist/schemas/http.d.ts +61 -0
- package/dist/schemas/http.d.ts.map +1 -0
- package/dist/schemas/http.js +45 -0
- package/dist/schemas/http.js.map +1 -0
- package/dist/schemas/remote.d.ts +25 -0
- package/dist/schemas/remote.d.ts.map +1 -0
- package/dist/schemas/remote.js +23 -0
- package/dist/schemas/remote.js.map +1 -0
- package/dist/schemas/session.d.ts +26 -0
- package/dist/schemas/session.d.ts.map +1 -0
- package/dist/schemas/session.js +17 -0
- package/dist/schemas/session.js.map +1 -0
- package/dist/schemas/store.d.ts +29 -0
- package/dist/schemas/store.d.ts.map +1 -0
- package/dist/schemas/store.js +22 -0
- package/dist/schemas/store.js.map +1 -0
- package/dist/schemas/terminal.d.ts +296 -0
- package/dist/schemas/terminal.d.ts.map +1 -0
- package/dist/schemas/terminal.js +243 -0
- package/dist/schemas/terminal.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/session-store.d.ts +176 -0
- package/dist/server/session-store.d.ts.map +1 -0
- package/dist/server/session-store.js +462 -0
- package/dist/server/session-store.js.map +1 -0
- package/dist/server/sse-stream.d.ts +32 -0
- package/dist/server/sse-stream.d.ts.map +1 -0
- package/dist/server/sse-stream.js +87 -0
- package/dist/server/sse-stream.js.map +1 -0
- package/dist/svelte/HostPicker.svelte +256 -0
- package/dist/svelte/HostPicker.svelte.d.ts +30 -0
- package/dist/svelte/HostPicker.svelte.d.ts.map +1 -0
- package/dist/svelte/README.md +52 -0
- package/dist/svelte/ReconnectBanner.svelte +88 -0
- package/dist/svelte/ReconnectBanner.svelte.d.ts +16 -0
- package/dist/svelte/ReconnectBanner.svelte.d.ts.map +1 -0
- package/dist/svelte/SessionTabs.svelte +192 -0
- package/dist/svelte/SessionTabs.svelte.d.ts +17 -0
- package/dist/svelte/SessionTabs.svelte.d.ts.map +1 -0
- package/dist/svelte/Terminal.svelte +567 -0
- package/dist/svelte/Terminal.svelte.d.ts +25 -0
- package/dist/svelte/Terminal.svelte.d.ts.map +1 -0
- package/dist/svelte/TerminalMobileToolbar.svelte +56 -0
- package/dist/svelte/TerminalMobileToolbar.svelte.d.ts +14 -0
- package/dist/svelte/TerminalMobileToolbar.svelte.d.ts.map +1 -0
- package/dist/svelte/TerminalWorkspace.svelte +562 -0
- package/dist/svelte/TerminalWorkspace.svelte.d.ts +25 -0
- package/dist/svelte/TerminalWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/ambient.d.ts +9 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts +4 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts.map +1 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.js +74 -0
- package/dist/svelte/index.d.ts +27 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +26 -0
- package/dist/svelte/styles.css +8 -0
- package/dist/svelte/terminal-session-authority.d.ts +17 -0
- package/dist/svelte/terminal-session-authority.d.ts.map +1 -0
- package/dist/svelte/terminal-session-authority.js +25 -0
- package/dist/svelte/terminal-svelte.plugin.d.ts +19 -0
- package/dist/svelte/terminal-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/terminal-svelte.plugin.js +42 -0
- package/dist/svelte/terminal-theme.d.ts +11 -0
- package/dist/svelte/terminal-theme.d.ts.map +1 -0
- package/dist/svelte/terminal-theme.js +116 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte +65 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts +5 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.d.ts +25 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.js +116 -0
- package/dist/svelte/types.d.ts +160 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +51 -0
- package/dist/svelte/workspace.d.ts +3 -0
- package/dist/svelte/workspace.d.ts.map +1 -0
- package/dist/svelte/workspace.js +2 -0
- package/dist/terminal-client.plugin.d.ts +3 -0
- package/dist/terminal-client.plugin.d.ts.map +1 -0
- package/dist/terminal-client.plugin.js +3 -0
- package/dist/terminal-client.plugin.js.map +1 -0
- package/dist/terminal-runtime.d.ts +30 -0
- package/dist/terminal-runtime.d.ts.map +1 -0
- package/dist/terminal-runtime.js +673 -0
- package/dist/terminal-runtime.js.map +1 -0
- package/dist/terminal.consumer.plugin.d.ts +14 -0
- package/dist/terminal.consumer.plugin.d.ts.map +1 -0
- package/dist/terminal.consumer.plugin.js +31 -0
- package/dist/terminal.consumer.plugin.js.map +1 -0
- package/dist/tools/terminal.impl.consumer.d.ts +11 -0
- package/dist/tools/terminal.impl.consumer.d.ts.map +1 -0
- package/dist/tools/terminal.impl.consumer.js +20 -0
- package/dist/tools/terminal.impl.consumer.js.map +1 -0
- package/dist/tools/terminal.schemas.d.ts +3 -0
- package/dist/tools/terminal.schemas.d.ts.map +1 -0
- package/dist/tools/terminal.schemas.js +3 -0
- package/dist/tools/terminal.schemas.js.map +1 -0
- package/dist/tools/terminal.unified.descriptor.d.ts +218 -0
- package/dist/tools/terminal.unified.descriptor.d.ts.map +1 -0
- package/dist/tools/terminal.unified.descriptor.js +36 -0
- package/dist/tools/terminal.unified.descriptor.js.map +1 -0
- package/dist/tui/index.d.ts +9 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +8 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/terminal-open.descriptor.d.ts +9 -0
- package/dist/tui/terminal-open.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal-open.descriptor.js +18 -0
- package/dist/tui/terminal-open.descriptor.js.map +1 -0
- package/dist/tui/terminal-open.impl.consumer.d.ts +9 -0
- package/dist/tui/terminal-open.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal-open.impl.consumer.js +13 -0
- package/dist/tui/terminal-open.impl.consumer.js.map +1 -0
- package/dist/tui/terminal-registry.descriptor.d.ts +11 -0
- package/dist/tui/terminal-registry.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal-registry.descriptor.js +17 -0
- package/dist/tui/terminal-registry.descriptor.js.map +1 -0
- package/dist/tui/terminal-registry.impl.consumer.d.ts +71 -0
- package/dist/tui/terminal-registry.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal-registry.impl.consumer.js +190 -0
- package/dist/tui/terminal-registry.impl.consumer.js.map +1 -0
- package/dist/tui/terminal-tui.plugin.d.ts +10 -0
- package/dist/tui/terminal-tui.plugin.d.ts.map +1 -0
- package/dist/tui/terminal-tui.plugin.js +32 -0
- package/dist/tui/terminal-tui.plugin.js.map +1 -0
- package/dist/tui/terminal.descriptor.d.ts +10 -0
- package/dist/tui/terminal.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal.descriptor.js +25 -0
- package/dist/tui/terminal.descriptor.js.map +1 -0
- package/dist/tui/terminal.impl.consumer.d.ts +14 -0
- package/dist/tui/terminal.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal.impl.consumer.js +189 -0
- package/dist/tui/terminal.impl.consumer.js.map +1 -0
- package/dist/tui/terminal.state-key.d.ts +30 -0
- package/dist/tui/terminal.state-key.d.ts.map +1 -0
- package/dist/tui/terminal.state-key.js +9 -0
- package/dist/tui/terminal.state-key.js.map +1 -0
- package/dist/tui/terminal.state.d.ts +152 -0
- package/dist/tui/terminal.state.d.ts.map +1 -0
- package/dist/tui/terminal.state.js +149 -0
- package/dist/tui/terminal.state.js.map +1 -0
- package/dist/workspace.d.ts +155 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +292 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +228 -4
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibesdotdev/terminal-client — shared server-side session store.
|
|
3
|
+
*
|
|
4
|
+
* An in-memory map of live PTY sessions paired with ownership metadata and a
|
|
5
|
+
* backlog ring (for SSE late-attach replay). The store is transport-agnostic: the
|
|
6
|
+
* caller supplies an `opener` function that resolves a real `ExecutionSession` from
|
|
7
|
+
* whatever source is appropriate for its surface:
|
|
8
|
+
*
|
|
9
|
+
* - hq-web passes `openRemoteSession(runtime, hostRef, opts)` — the session rides
|
|
10
|
+
* L4's `runtime/rpc` to a fleet host.
|
|
11
|
+
* - consumer-hq passes `openLocalSession(runtime, opts)` — the session is a direct
|
|
12
|
+
* Bun PTY on this machine.
|
|
13
|
+
*
|
|
14
|
+
* The rest of the store — ownership enforcement, backlog capture, eviction on exit,
|
|
15
|
+
* and the write verbs — is identical regardless of transport and lives here once.
|
|
16
|
+
*
|
|
17
|
+
* ### Detach vs. kill
|
|
18
|
+
*
|
|
19
|
+
* A browser tab close must NOT kill the PTY — the underlying shell keeps running
|
|
20
|
+
* so the user can re-attach. `detachSession()` records a last-active timestamp and
|
|
21
|
+
* removes the tab's SSE subscription without touching the PTY. The PTY is ONLY torn
|
|
22
|
+
* down by an explicit `killSession()` or when the idle timeout fires.
|
|
23
|
+
*
|
|
24
|
+
* ### Idle timeout
|
|
25
|
+
*
|
|
26
|
+
* When a stored session has no active SSE subscriber AND its `lastActiveMs` is
|
|
27
|
+
* older than `idleTimeoutMs` (default: 30 min), the reaper kills it and evicts it.
|
|
28
|
+
* The reaper runs every `reaperIntervalMs` (default: 60 s).
|
|
29
|
+
*
|
|
30
|
+
* ### maxSessions
|
|
31
|
+
*
|
|
32
|
+
* `openSession` rejects with `{ reason: 'MAX_SESSIONS' }` when the owner already
|
|
33
|
+
* has `maxSessions` (or more) active sessions, before opening a new PTY. The
|
|
34
|
+
* caller surfaces this as a user-readable error.
|
|
35
|
+
*
|
|
36
|
+
* SERVER-ONLY. Import only from `+server.ts` files or `$lib/server/**`.
|
|
37
|
+
*/
|
|
38
|
+
import { CapabilityError, type RuntimeLifecycle, type RuntimeScope } from '@vibesdotdev/kernel';
|
|
39
|
+
/** The slice of the runtime the store needs: lifecycle-owned timers only. */
|
|
40
|
+
export type TerminalSessionStoreRuntime = {
|
|
41
|
+
lifecycle: Pick<RuntimeLifecycle, 'interval' | 'timeout' | 'now'>;
|
|
42
|
+
scope: Pick<RuntimeScope, 'lifecycle'>;
|
|
43
|
+
};
|
|
44
|
+
import type { ExecutionSession } from '../client.ts';
|
|
45
|
+
/**
|
|
46
|
+
* Options forwarded to the opener function. A superset of the session options
|
|
47
|
+
* both local and remote openers share.
|
|
48
|
+
*/
|
|
49
|
+
export { SessionOpenOptionsSchema, type SessionOpenOptions } from '../schemas/session.ts';
|
|
50
|
+
import { type SessionOpenOptions } from '../schemas/session.ts';
|
|
51
|
+
import { type TerminalSessionStoreOptions } from '../schemas/store.ts';
|
|
52
|
+
export { TerminalSessionStoreOptionsSchema, type TerminalSessionStoreOptions } from '../schemas/store.ts';
|
|
53
|
+
/**
|
|
54
|
+
* A function that opens a real `ExecutionSession`. Supplied by each surface so
|
|
55
|
+
* the store never imports surface-specific deps (remote RPC for hq-web; local
|
|
56
|
+
* PTY for consumer-hq).
|
|
57
|
+
*/
|
|
58
|
+
export type { SessionOpener } from '../schemas/store.ts';
|
|
59
|
+
import { type SessionOpener } from '../schemas/store.ts';
|
|
60
|
+
/**
|
|
61
|
+
* Typed rejection reason returned by `openSession` when the owner is at their
|
|
62
|
+
* session cap. This lets the API route surface a human-readable denial.
|
|
63
|
+
*/
|
|
64
|
+
export type SessionOpenError = CapabilityError;
|
|
65
|
+
/**
|
|
66
|
+
* Session metadata exposed for the wave-2 tabs UI and the GET sessions list.
|
|
67
|
+
* Wire-format backward compatible (additive only).
|
|
68
|
+
*/
|
|
69
|
+
export interface SessionMetadata {
|
|
70
|
+
id: string;
|
|
71
|
+
ownerId: string;
|
|
72
|
+
command: string;
|
|
73
|
+
startedAt: number;
|
|
74
|
+
/** true when no SSE subscriber is currently attached. */
|
|
75
|
+
detached: boolean;
|
|
76
|
+
exited: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface StoredSession {
|
|
79
|
+
session: ExecutionSession;
|
|
80
|
+
/** The user that opened this session — only its owner may drive/read it. */
|
|
81
|
+
ownerId: string;
|
|
82
|
+
/** Ring buffer of recent output so a late-attaching SSE stream sees scrollback. */
|
|
83
|
+
backlog: Uint8Array[];
|
|
84
|
+
exited: boolean;
|
|
85
|
+
/** Typed failure retained when the session ended abnormally. */
|
|
86
|
+
exitFailure?: CapabilityError;
|
|
87
|
+
/** Command used to open this session — for metadata / list display. */
|
|
88
|
+
command: string;
|
|
89
|
+
/** Unix ms when the session was opened. */
|
|
90
|
+
startedAt: number;
|
|
91
|
+
/**
|
|
92
|
+
* Unix ms of the last SSE subscriber detach (or session open if never detached).
|
|
93
|
+
* Used by the idle reaper.
|
|
94
|
+
*/
|
|
95
|
+
lastActiveMs: number;
|
|
96
|
+
/**
|
|
97
|
+
* Count of currently attached SSE subscribers. The session is "detached" when
|
|
98
|
+
* this drops to zero — the PTY keeps running; only a kill or idle timeout ends it.
|
|
99
|
+
*/
|
|
100
|
+
subscriberCount: number;
|
|
101
|
+
/** Stop capturing output after terminal cleanup. */
|
|
102
|
+
unsubscribeOutput: () => void;
|
|
103
|
+
}
|
|
104
|
+
export interface TerminalSessionStore {
|
|
105
|
+
/**
|
|
106
|
+
* Open a new session via the supplied opener and register it in the store,
|
|
107
|
+
* keyed by the resolved session id and tagged with the given ownerId.
|
|
108
|
+
*
|
|
109
|
+
* Ownership enforcement: only the user that opened a session may later
|
|
110
|
+
* drive or read it (`getOwnedSession` treats a mismatch as "not found").
|
|
111
|
+
*
|
|
112
|
+
* Throws `SessionOpenError` when `maxSessions` is exceeded.
|
|
113
|
+
*/
|
|
114
|
+
openSession(ownerId: string, opts: SessionOpenOptions, opener?: SessionOpener): Promise<{
|
|
115
|
+
id: string;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Return the stored session only if it belongs to `ownerId`. A mismatch is
|
|
119
|
+
* treated as "not found" so the bridge never leaks another user's PTY.
|
|
120
|
+
*/
|
|
121
|
+
getOwnedSession(id: string, ownerId: string): StoredSession | undefined;
|
|
122
|
+
writeToSession(id: string, ownerId: string, data: Uint8Array): boolean;
|
|
123
|
+
resizeSession(id: string, ownerId: string, cols: number, rows: number): boolean;
|
|
124
|
+
signalSession(id: string, ownerId: string, sig: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Refresh a session's idle clock to now. Called by the tool surface after a
|
|
127
|
+
* successful owned-session operation (write/read/resize) so an actively
|
|
128
|
+
* driven but SSE-unattached session is not reaped as idle. Returns false
|
|
129
|
+
* (no-op) when the session is missing, not owned, or already exited.
|
|
130
|
+
*/
|
|
131
|
+
touchSession(id: string, ownerId: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Whether a session id is still tracked by the store (owner-agnostic).
|
|
134
|
+
* Lets consumers garbage-collect side tables keyed by session id once a
|
|
135
|
+
* session has been killed, exited, or idle-reaped.
|
|
136
|
+
*/
|
|
137
|
+
hasSession(id: string): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Kill and evict the session. Explicit — the PTY is torn down immediately.
|
|
140
|
+
* This is what the "Kill session" UI button calls. NOT what browser tab close
|
|
141
|
+
* calls — use `detachSession` for that.
|
|
142
|
+
*/
|
|
143
|
+
killSession(id: string, ownerId: string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Detach an SSE subscriber without killing the PTY.
|
|
146
|
+
*
|
|
147
|
+
* Call when a browser tab closes or an SSE connection drops. The PTY keeps
|
|
148
|
+
* running. The session is eligible for idle eviction if no other subscriber
|
|
149
|
+
* re-attaches within `idleTimeoutMs`.
|
|
150
|
+
*
|
|
151
|
+
* Returns true when a session was found and detached; false when not found
|
|
152
|
+
* (already killed, wrong owner, etc.).
|
|
153
|
+
*/
|
|
154
|
+
detachSession(id: string, ownerId: string): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Increment the subscriber count for a session (called when an SSE stream
|
|
157
|
+
* attaches). Clears the idle-eviction clock while a subscriber is present.
|
|
158
|
+
*/
|
|
159
|
+
attachSession(id: string, ownerId: string): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* List metadata for all sessions owned by `ownerId`. Used by the wave-2 tabs
|
|
162
|
+
* UI and the GET /api/sessions endpoint. Wire format: additive only.
|
|
163
|
+
*/
|
|
164
|
+
listSessions(ownerId: string): SessionMetadata[];
|
|
165
|
+
/** Tear down the idle reaper (test teardown / graceful shutdown). */
|
|
166
|
+
dispose(): Promise<void>;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create an isolated session store bound to the given opener.
|
|
170
|
+
*
|
|
171
|
+
* Call once at module scope (e.g. at the top of `$lib/server/terminal-store.ts`)
|
|
172
|
+
* so the store survives across requests within a single server process.
|
|
173
|
+
*/
|
|
174
|
+
export declare function createTerminalSessionStore(runtime: TerminalSessionStoreRuntime, options?: TerminalSessionStoreOptions): TerminalSessionStore;
|
|
175
|
+
export declare function createTerminalSessionStore(runtime: TerminalSessionStoreRuntime, opener: SessionOpener, options?: TerminalSessionStoreOptions): TerminalSessionStore;
|
|
176
|
+
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/server/session-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibesdotdev/terminal-client — shared server-side session store.
|
|
3
|
+
*
|
|
4
|
+
* An in-memory map of live PTY sessions paired with ownership metadata and a
|
|
5
|
+
* backlog ring (for SSE late-attach replay). The store is transport-agnostic: the
|
|
6
|
+
* caller supplies an `opener` function that resolves a real `ExecutionSession` from
|
|
7
|
+
* whatever source is appropriate for its surface:
|
|
8
|
+
*
|
|
9
|
+
* - hq-web passes `openRemoteSession(runtime, hostRef, opts)` — the session rides
|
|
10
|
+
* L4's `runtime/rpc` to a fleet host.
|
|
11
|
+
* - consumer-hq passes `openLocalSession(runtime, opts)` — the session is a direct
|
|
12
|
+
* Bun PTY on this machine.
|
|
13
|
+
*
|
|
14
|
+
* The rest of the store — ownership enforcement, backlog capture, eviction on exit,
|
|
15
|
+
* and the write verbs — is identical regardless of transport and lives here once.
|
|
16
|
+
*
|
|
17
|
+
* ### Detach vs. kill
|
|
18
|
+
*
|
|
19
|
+
* A browser tab close must NOT kill the PTY — the underlying shell keeps running
|
|
20
|
+
* so the user can re-attach. `detachSession()` records a last-active timestamp and
|
|
21
|
+
* removes the tab's SSE subscription without touching the PTY. The PTY is ONLY torn
|
|
22
|
+
* down by an explicit `killSession()` or when the idle timeout fires.
|
|
23
|
+
*
|
|
24
|
+
* ### Idle timeout
|
|
25
|
+
*
|
|
26
|
+
* When a stored session has no active SSE subscriber AND its `lastActiveMs` is
|
|
27
|
+
* older than `idleTimeoutMs` (default: 30 min), the reaper kills it and evicts it.
|
|
28
|
+
* The reaper runs every `reaperIntervalMs` (default: 60 s).
|
|
29
|
+
*
|
|
30
|
+
* ### maxSessions
|
|
31
|
+
*
|
|
32
|
+
* `openSession` rejects with `{ reason: 'MAX_SESSIONS' }` when the owner already
|
|
33
|
+
* has `maxSessions` (or more) active sessions, before opening a new PTY. The
|
|
34
|
+
* caller surfaces this as a user-readable error.
|
|
35
|
+
*
|
|
36
|
+
* SERVER-ONLY. Import only from `+server.ts` files or `$lib/server/**`.
|
|
37
|
+
*/
|
|
38
|
+
import { CapabilityError, CaughtFailureSchema, isCapabilityError } from '@vibesdotdev/kernel';
|
|
39
|
+
import * as z from 'zod/v4';
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Public option types
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
/**
|
|
44
|
+
* Options forwarded to the opener function. A superset of the session options
|
|
45
|
+
* both local and remote openers share.
|
|
46
|
+
*/
|
|
47
|
+
export { SessionOpenOptionsSchema } from "../schemas/session.js";
|
|
48
|
+
import { SessionOpenOptionsSchema } from "../schemas/session.js";
|
|
49
|
+
import { TerminalSessionStoreOptionsSchema } from "../schemas/store.js";
|
|
50
|
+
export { TerminalSessionStoreOptionsSchema } from "../schemas/store.js";
|
|
51
|
+
import { SessionOpenerSchema, ExecutionSessionSchema, SessionIdentitySchema, ResizeSchema, SignalSchema } from "../schemas/store.js";
|
|
52
|
+
function captureCleanupFailure(operation) {
|
|
53
|
+
try {
|
|
54
|
+
operation();
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
catch (cause) {
|
|
58
|
+
return CaughtFailureSchema.parse(cause);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Store factory options
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Store factory (one per surface module — no shared global across apps)
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
const MAX_BACKLOG = 256;
|
|
68
|
+
export function createTerminalSessionStore(runtime, openerOrOptions, providedOptions = {}) {
|
|
69
|
+
let openerCandidate;
|
|
70
|
+
try {
|
|
71
|
+
openerCandidate = SessionOpenerSchema.safeParse(openerOrOptions);
|
|
72
|
+
}
|
|
73
|
+
catch (cause) {
|
|
74
|
+
throw new CapabilityError('The terminal session store configuration is invalid.', 'INVALID_INPUT', {
|
|
75
|
+
reason: 'terminal-session-store-options-invalid',
|
|
76
|
+
retryable: false,
|
|
77
|
+
cause
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const defaultOpener = openerCandidate.success ? openerCandidate.data : undefined;
|
|
81
|
+
const optionsInput = openerCandidate.success ? providedOptions : (openerOrOptions ?? {});
|
|
82
|
+
let options;
|
|
83
|
+
try {
|
|
84
|
+
options = TerminalSessionStoreOptionsSchema.parse(optionsInput);
|
|
85
|
+
}
|
|
86
|
+
catch (cause) {
|
|
87
|
+
throw new CapabilityError('The terminal session store configuration is invalid.', 'INVALID_INPUT', {
|
|
88
|
+
reason: 'terminal-session-store-options-invalid',
|
|
89
|
+
retryable: false,
|
|
90
|
+
cause
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const maxSessions = options.maxSessions;
|
|
94
|
+
const idleTimeoutMs = options.idleTimeoutMs;
|
|
95
|
+
const reaperIntervalMs = options.reaperIntervalMs;
|
|
96
|
+
const sessions = new Map();
|
|
97
|
+
const pendingByOwner = new Map();
|
|
98
|
+
const evictionTimers = new Set();
|
|
99
|
+
let disposed = false;
|
|
100
|
+
let disposal;
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Idle reaper — runs every `reaperIntervalMs`, evicts sessions that have been
|
|
103
|
+
// detached (subscriberCount === 0) for longer than `idleTimeoutMs`.
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
const reap = () => {
|
|
106
|
+
const now = runtime.lifecycle.now();
|
|
107
|
+
const failures = [];
|
|
108
|
+
for (const [id, stored] of sessions) {
|
|
109
|
+
if (stored.exited)
|
|
110
|
+
continue;
|
|
111
|
+
if (stored.subscriberCount > 0)
|
|
112
|
+
continue;
|
|
113
|
+
if (now - stored.lastActiveMs >= idleTimeoutMs) {
|
|
114
|
+
const failure = captureCleanupFailure(() => stored.session.kill());
|
|
115
|
+
if (failure) {
|
|
116
|
+
failures.push(failure);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
stored.exited = true;
|
|
120
|
+
stored.unsubscribeOutput();
|
|
121
|
+
sessions.delete(id);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (failures.length === 1)
|
|
125
|
+
throw failures[0];
|
|
126
|
+
if (failures.length > 1)
|
|
127
|
+
throw new AggregateError(failures, 'Idle terminal cleanup failed.');
|
|
128
|
+
};
|
|
129
|
+
// A finite host (one CLI invocation) starts no autonomous background work:
|
|
130
|
+
// the lifecycle refuses the interval, and its sessions end with the
|
|
131
|
+
// invocation, so there is nothing to reap. Any other refusal propagates.
|
|
132
|
+
const disposeReaper = runtime.scope.lifecycle === 'finite'
|
|
133
|
+
? () => { }
|
|
134
|
+
: runtime.lifecycle.interval(reap, reaperIntervalMs);
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Internal helpers
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
function countActiveSessions(ownerId) {
|
|
139
|
+
let count = 0;
|
|
140
|
+
for (const stored of sessions.values()) {
|
|
141
|
+
if (stored.ownerId === ownerId && !stored.exited)
|
|
142
|
+
count++;
|
|
143
|
+
}
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
146
|
+
function scheduleEviction(id) {
|
|
147
|
+
let cancel = () => { };
|
|
148
|
+
cancel = runtime.lifecycle.timeout(() => {
|
|
149
|
+
evictionTimers.delete(cancel);
|
|
150
|
+
const stored = sessions.get(id);
|
|
151
|
+
stored?.unsubscribeOutput();
|
|
152
|
+
sessions.delete(id);
|
|
153
|
+
}, 5_000);
|
|
154
|
+
evictionTimers.add(cancel);
|
|
155
|
+
}
|
|
156
|
+
function admitIdentity(input) {
|
|
157
|
+
try {
|
|
158
|
+
return SessionIdentitySchema.parse(input);
|
|
159
|
+
}
|
|
160
|
+
catch (cause) {
|
|
161
|
+
throw new CapabilityError('The terminal session identity is invalid.', 'INVALID_INPUT', {
|
|
162
|
+
reason: 'terminal-session-identity-invalid',
|
|
163
|
+
retryable: false,
|
|
164
|
+
cause
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Public methods
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
async function openSession(ownerIdInput, optsInput, requestOpener) {
|
|
172
|
+
if (disposed) {
|
|
173
|
+
throw new CapabilityError('The terminal session store is disposed.', 'CONFLICT', {
|
|
174
|
+
reason: 'terminal-session-store-disposed',
|
|
175
|
+
retryable: false
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
let ownerId;
|
|
179
|
+
let opts;
|
|
180
|
+
try {
|
|
181
|
+
ownerId = SessionIdentitySchema.parse(ownerIdInput);
|
|
182
|
+
opts = SessionOpenOptionsSchema.parse(optsInput);
|
|
183
|
+
}
|
|
184
|
+
catch (cause) {
|
|
185
|
+
throw new CapabilityError('The terminal session request is invalid.', 'INVALID_INPUT', {
|
|
186
|
+
reason: 'terminal-session-request-invalid',
|
|
187
|
+
retryable: false,
|
|
188
|
+
cause
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
const opener = requestOpener ?? defaultOpener;
|
|
192
|
+
if (!opener) {
|
|
193
|
+
throw new CapabilityError('The terminal session opener is unavailable.', 'UNAVAILABLE', {
|
|
194
|
+
reason: 'terminal-session-opener-missing',
|
|
195
|
+
retryable: false
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
let admittedOpener;
|
|
199
|
+
try {
|
|
200
|
+
admittedOpener = SessionOpenerSchema.parse(opener);
|
|
201
|
+
}
|
|
202
|
+
catch (cause) {
|
|
203
|
+
throw new CapabilityError('The terminal session opener is invalid.', 'INVALID_INPUT', {
|
|
204
|
+
reason: 'terminal-session-opener-invalid',
|
|
205
|
+
retryable: false,
|
|
206
|
+
cause
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// maxSessions enforcement: reject before opening the PTY.
|
|
210
|
+
let reserved = false;
|
|
211
|
+
if (maxSessions > 0) {
|
|
212
|
+
const current = countActiveSessions(ownerId) + (pendingByOwner.get(ownerId) ?? 0);
|
|
213
|
+
if (current >= maxSessions) {
|
|
214
|
+
throw new CapabilityError('The terminal session limit has been reached.', 'RATE_LIMITED', {
|
|
215
|
+
reason: 'MAX_SESSIONS',
|
|
216
|
+
retryable: true,
|
|
217
|
+
details: { current, max: maxSessions }
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
pendingByOwner.set(ownerId, (pendingByOwner.get(ownerId) ?? 0) + 1);
|
|
221
|
+
reserved = true;
|
|
222
|
+
}
|
|
223
|
+
let session;
|
|
224
|
+
try {
|
|
225
|
+
session = ExecutionSessionSchema.parse(await admittedOpener(opts));
|
|
226
|
+
}
|
|
227
|
+
catch (cause) {
|
|
228
|
+
const failure = CaughtFailureSchema.parse(cause);
|
|
229
|
+
if (isCapabilityError(failure))
|
|
230
|
+
throw failure;
|
|
231
|
+
throw new CapabilityError('The terminal session could not be opened.', 'UNAVAILABLE', {
|
|
232
|
+
reason: 'terminal-session-open-failed',
|
|
233
|
+
retryable: true,
|
|
234
|
+
cause: failure
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
if (reserved) {
|
|
239
|
+
const pending = (pendingByOwner.get(ownerId) ?? 1) - 1;
|
|
240
|
+
if (pending > 0)
|
|
241
|
+
pendingByOwner.set(ownerId, pending);
|
|
242
|
+
else
|
|
243
|
+
pendingByOwner.delete(ownerId);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (disposed) {
|
|
247
|
+
session.kill();
|
|
248
|
+
await session.exited();
|
|
249
|
+
throw new CapabilityError('The terminal session store was disposed while opening.', 'CANCELLED', {
|
|
250
|
+
reason: 'terminal-session-open-cancelled',
|
|
251
|
+
retryable: false
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (sessions.has(session.id)) {
|
|
255
|
+
session.kill();
|
|
256
|
+
await session.exited();
|
|
257
|
+
throw new CapabilityError('The terminal session id is already active.', 'CONFLICT', {
|
|
258
|
+
reason: 'terminal-session-id-conflict',
|
|
259
|
+
retryable: false
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const now = runtime.lifecycle.now();
|
|
263
|
+
const stored = {
|
|
264
|
+
session,
|
|
265
|
+
ownerId,
|
|
266
|
+
backlog: [],
|
|
267
|
+
exited: false,
|
|
268
|
+
command: opts.command,
|
|
269
|
+
startedAt: now,
|
|
270
|
+
lastActiveMs: now,
|
|
271
|
+
subscriberCount: 0,
|
|
272
|
+
unsubscribeOutput: () => { }
|
|
273
|
+
};
|
|
274
|
+
sessions.set(session.id, stored);
|
|
275
|
+
// Capture output into the backlog ring so a slightly-late SSE attach
|
|
276
|
+
// replays what already happened (e.g. the shell prompt). Cap to bound memory.
|
|
277
|
+
stored.unsubscribeOutput = session.onOutput((bytes) => {
|
|
278
|
+
stored.backlog.push(new Uint8Array(bytes));
|
|
279
|
+
if (stored.backlog.length > MAX_BACKLOG)
|
|
280
|
+
stored.backlog.shift();
|
|
281
|
+
});
|
|
282
|
+
void session.exited()
|
|
283
|
+
.then(() => ({ kind: 'exit' }), (cause) => ({
|
|
284
|
+
kind: 'failure',
|
|
285
|
+
failure: CapabilityError.from(CaughtFailureSchema.parse(cause))
|
|
286
|
+
}))
|
|
287
|
+
.then((outcome) => {
|
|
288
|
+
stored.exited = true;
|
|
289
|
+
if (outcome.kind === 'failure')
|
|
290
|
+
stored.exitFailure = outcome.failure;
|
|
291
|
+
stored.unsubscribeOutput();
|
|
292
|
+
if (disposed)
|
|
293
|
+
sessions.delete(session.id);
|
|
294
|
+
else
|
|
295
|
+
scheduleEviction(session.id);
|
|
296
|
+
});
|
|
297
|
+
return { id: session.id };
|
|
298
|
+
}
|
|
299
|
+
function getOwnedSession(idInput, ownerIdInput) {
|
|
300
|
+
const id = admitIdentity(idInput);
|
|
301
|
+
const ownerId = admitIdentity(ownerIdInput);
|
|
302
|
+
const stored = sessions.get(id);
|
|
303
|
+
if (!stored || stored.ownerId !== ownerId)
|
|
304
|
+
return undefined;
|
|
305
|
+
return stored;
|
|
306
|
+
}
|
|
307
|
+
function writeToSession(id, ownerId, data) {
|
|
308
|
+
const stored = getOwnedSession(id, ownerId);
|
|
309
|
+
if (!stored || stored.exited)
|
|
310
|
+
return false;
|
|
311
|
+
let copied;
|
|
312
|
+
try {
|
|
313
|
+
copied = new Uint8Array(data);
|
|
314
|
+
}
|
|
315
|
+
catch (cause) {
|
|
316
|
+
throw new CapabilityError('The terminal input bytes are invalid.', 'INVALID_INPUT', {
|
|
317
|
+
reason: 'terminal-input-invalid',
|
|
318
|
+
retryable: false,
|
|
319
|
+
cause
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
stored.session.write(copied);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
function resizeSession(id, ownerId, cols, rows) {
|
|
326
|
+
const stored = getOwnedSession(id, ownerId);
|
|
327
|
+
if (!stored || stored.exited)
|
|
328
|
+
return false;
|
|
329
|
+
let size;
|
|
330
|
+
try {
|
|
331
|
+
size = ResizeSchema.parse({ cols, rows });
|
|
332
|
+
}
|
|
333
|
+
catch (cause) {
|
|
334
|
+
throw new CapabilityError('The terminal size is invalid.', 'INVALID_INPUT', {
|
|
335
|
+
reason: 'terminal-size-invalid',
|
|
336
|
+
retryable: false,
|
|
337
|
+
cause
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
stored.session.resize(size.cols, size.rows);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
function signalSession(id, ownerId, sig) {
|
|
344
|
+
const stored = getOwnedSession(id, ownerId);
|
|
345
|
+
if (!stored || stored.exited)
|
|
346
|
+
return false;
|
|
347
|
+
let signal;
|
|
348
|
+
try {
|
|
349
|
+
signal = SignalSchema.parse(sig);
|
|
350
|
+
}
|
|
351
|
+
catch (cause) {
|
|
352
|
+
throw new CapabilityError('The terminal signal is invalid.', 'INVALID_INPUT', {
|
|
353
|
+
reason: 'terminal-signal-invalid',
|
|
354
|
+
retryable: false,
|
|
355
|
+
cause
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
stored.session.signal(signal);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
function touchSession(id, ownerId) {
|
|
362
|
+
const stored = getOwnedSession(id, ownerId);
|
|
363
|
+
if (!stored || stored.exited)
|
|
364
|
+
return false;
|
|
365
|
+
stored.lastActiveMs = runtime.lifecycle.now();
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
function hasSession(id) {
|
|
369
|
+
return sessions.has(admitIdentity(id));
|
|
370
|
+
}
|
|
371
|
+
function killSession(id, ownerId) {
|
|
372
|
+
const stored = getOwnedSession(id, ownerId);
|
|
373
|
+
if (!stored)
|
|
374
|
+
return false;
|
|
375
|
+
stored.session.kill();
|
|
376
|
+
stored.exited = true;
|
|
377
|
+
scheduleEviction(id);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
function detachSession(id, ownerId) {
|
|
381
|
+
const stored = getOwnedSession(id, ownerId);
|
|
382
|
+
if (!stored || stored.exited)
|
|
383
|
+
return false;
|
|
384
|
+
if (stored.subscriberCount > 0) {
|
|
385
|
+
stored.subscriberCount = Math.max(0, stored.subscriberCount - 1);
|
|
386
|
+
}
|
|
387
|
+
stored.lastActiveMs = runtime.lifecycle.now();
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
function attachSession(id, ownerId) {
|
|
391
|
+
const stored = getOwnedSession(id, ownerId);
|
|
392
|
+
if (!stored || stored.exited)
|
|
393
|
+
return false;
|
|
394
|
+
stored.subscriberCount++;
|
|
395
|
+
stored.lastActiveMs = runtime.lifecycle.now();
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
function listSessions(ownerId) {
|
|
399
|
+
const admittedOwner = admitIdentity(ownerId);
|
|
400
|
+
const result = [];
|
|
401
|
+
for (const stored of sessions.values()) {
|
|
402
|
+
if (stored.ownerId !== admittedOwner)
|
|
403
|
+
continue;
|
|
404
|
+
result.push({
|
|
405
|
+
id: stored.session.id,
|
|
406
|
+
ownerId: admittedOwner,
|
|
407
|
+
command: stored.command,
|
|
408
|
+
startedAt: stored.startedAt,
|
|
409
|
+
detached: stored.subscriberCount === 0 && !stored.exited,
|
|
410
|
+
exited: stored.exited
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return result;
|
|
414
|
+
}
|
|
415
|
+
function dispose() {
|
|
416
|
+
disposal ??= (async () => {
|
|
417
|
+
disposed = true;
|
|
418
|
+
disposeReaper();
|
|
419
|
+
for (const cancel of evictionTimers)
|
|
420
|
+
cancel();
|
|
421
|
+
evictionTimers.clear();
|
|
422
|
+
const failures = [];
|
|
423
|
+
const active = [...sessions.values()];
|
|
424
|
+
for (const stored of active) {
|
|
425
|
+
stored.unsubscribeOutput();
|
|
426
|
+
const failure = captureCleanupFailure(() => stored.session.kill());
|
|
427
|
+
if (failure)
|
|
428
|
+
failures.push(failure);
|
|
429
|
+
}
|
|
430
|
+
for (const stored of active) {
|
|
431
|
+
const outcome = await stored.session.exited().then(() => ({ kind: 'exit' }), (cause) => ({
|
|
432
|
+
kind: 'failure',
|
|
433
|
+
failure: CaughtFailureSchema.parse(cause)
|
|
434
|
+
}));
|
|
435
|
+
if (outcome.kind === 'failure')
|
|
436
|
+
failures.push(outcome.failure);
|
|
437
|
+
}
|
|
438
|
+
sessions.clear();
|
|
439
|
+
pendingByOwner.clear();
|
|
440
|
+
if (failures.length === 1)
|
|
441
|
+
throw failures[0];
|
|
442
|
+
if (failures.length > 1)
|
|
443
|
+
throw new AggregateError(failures, 'Terminal session store cleanup failed.');
|
|
444
|
+
})();
|
|
445
|
+
return disposal;
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
openSession,
|
|
449
|
+
getOwnedSession,
|
|
450
|
+
writeToSession,
|
|
451
|
+
resizeSession,
|
|
452
|
+
signalSession,
|
|
453
|
+
killSession,
|
|
454
|
+
detachSession,
|
|
455
|
+
attachSession,
|
|
456
|
+
listSessions,
|
|
457
|
+
touchSession,
|
|
458
|
+
hasSession,
|
|
459
|
+
dispose
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/server/session-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAIjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAS5B,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;GAGG;AACH,OAAO,EAAE,wBAAwB,EAA2B,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAA2B,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,iCAAiC,EAAoC,MAAM,qBAAqB,CAAC;AAC1G,OAAO,EAAE,iCAAiC,EAAoC,MAAM,qBAAqB,CAAC;AAQ1G,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,YAAY,EAAE,YAAY,EAAsB,MAAM,qBAAqB,CAAC;AAqDzJ,SAAS,qBAAqB,CAAC,SAAqB;IACnD,IAAI,CAAC;QACJ,SAAS,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;AACF,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,8EAA8E;AAC9E,wEAAwE;AACxE,8EAA8E;AAE9E,MAAM,WAAW,GAAG,GAAG,CAAC;AA6FxB,MAAM,UAAU,0BAA0B,CACzC,OAAoC,EACpC,eAA6D,EAC7D,kBAA+C,EAAE;IAEjD,IAAI,eAAiE,CAAC;IACtE,IAAI,CAAC;QACJ,eAAe,GAAG,mBAAmB,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,eAAe,CAAC,sDAAsD,EAAE,eAAe,EAAE;YAClG,MAAM,EAAE,wCAAwC;YAChD,SAAS,EAAE,KAAK;YAChB,KAAK;SACL,CAAC,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IACzF,IAAI,OAA2D,CAAC;IAChE,IAAI,CAAC;QACJ,OAAO,GAAG,iCAAiC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,eAAe,CAAC,sDAAsD,EAAE,eAAe,EAAE;YAClG,MAAM,EAAE,wCAAwC;YAChD,SAAS,EAAE,KAAK;YAChB,KAAK;SACL,CAAC,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACxC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC5C,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAElD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAc,CAAC;IAC7C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,QAAmC,CAAC;IAExC,8EAA8E;IAC9E,8EAA8E;IAC9E,oEAAoE;IACpE,8EAA8E;IAE9E,MAAM,IAAI,GAAG,GAAS,EAAE;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAoB,EAAE,CAAC;QACrC,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACrC,IAAI,MAAM,CAAC,MAAM;gBAAE,SAAS;YAC5B,IAAI,MAAM,CAAC,eAAe,GAAG,CAAC;gBAAE,SAAS;YACzC,IAAI,GAAG,GAAG,MAAM,CAAC,YAAY,IAAI,aAAa,EAAE,CAAC;gBAChD,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBACnE,IAAI,OAAO,EAAE,CAAC;oBACb,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACvB,SAAS;gBACV,CAAC;gBACD,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;gBACrB,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;QACF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,cAAc,CAAC,QAAQ,EAAE,+BAA+B,CAAC,CAAC;IAC9F,CAAC,CAAC;IACF,2EAA2E;IAC3E,oEAAoE;IACpE,yEAAyE;IACzE,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,KAAK,QAAQ;QACzD,CAAC,CAAC,GAAG,EAAE,GAAE,CAAC;QACV,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAEtD,8EAA8E;IAC9E,mBAAmB;IACnB,8EAA8E;IAE9E,SAAS,mBAAmB,CAAC,OAAe;QAC3C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,KAAK,EAAE,CAAC;QAC3D,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,SAAS,gBAAgB,CAAC,EAAU;QACnC,IAAI,MAAM,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QACtB,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE;YACvC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,MAAM,EAAE,iBAAiB,EAAE,CAAC;YAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC,EAAE,KAAK,CAAC,CAAC;QACV,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,SAAS,aAAa,CAAC,KAAa;QACnC,IAAI,CAAC;YACJ,OAAO,qBAAqB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,2CAA2C,EAAE,eAAe,EAAE;gBACvF,MAAM,EAAE,mCAAmC;gBAC3C,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;IACjB,8EAA8E;IAE9E,KAAK,UAAU,WAAW,CACzB,YAAoB,EACpB,SAA6B,EAC7B,aAA6B;QAE7B,IAAI,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,eAAe,CAAC,yCAAyC,EAAE,UAAU,EAAE;gBAChF,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,OAAe,CAAC;QACpB,IAAI,IAAwB,CAAC;QAC7B,IAAI,CAAC;YACJ,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACpD,IAAI,GAAG,wBAAwB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,0CAA0C,EAAE,eAAe,EAAE;gBACtF,MAAM,EAAE,kCAAkC;gBAC1C,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,aAAa,IAAI,aAAa,CAAC;QAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,eAAe,CAAC,6CAA6C,EAAE,aAAa,EAAE;gBACvF,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,cAA6B,CAAC;QAClC,IAAI,CAAC;YACJ,cAAc,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,yCAAyC,EAAE,eAAe,EAAE;gBACrF,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;QACD,0DAA0D;QAC1D,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAClF,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;gBAC5B,MAAM,IAAI,eAAe,CAAC,8CAA8C,EAAE,cAAc,EAAE;oBACzF,MAAM,EAAE,cAAc;oBACtB,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE;iBACtC,CAAC,CAAC;YACJ,CAAC;YACD,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACpE,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;QAED,IAAI,OAAyB,CAAC;QAC9B,IAAI,CAAC;YACJ,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjD,IAAI,iBAAiB,CAAC,OAAO,CAAC;gBAAE,MAAM,OAAO,CAAC;YAC9C,MAAM,IAAI,eAAe,CAAC,2CAA2C,EAAE,aAAa,EAAE;gBACrF,MAAM,EAAE,8BAA8B;gBACtC,SAAS,EAAE,IAAI;gBACf,KAAK,EAAE,OAAO;aACd,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,IAAI,QAAQ,EAAE,CAAC;gBACd,MAAM,OAAO,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACvD,IAAI,OAAO,GAAG,CAAC;oBAAE,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;;oBACjD,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACF,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,IAAI,eAAe,CAAC,wDAAwD,EAAE,WAAW,EAAE;gBAChG,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,IAAI,eAAe,CAAC,4CAA4C,EAAE,UAAU,EAAE;gBACnF,MAAM,EAAE,8BAA8B;gBACtC,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QAEpC,MAAM,MAAM,GAAkB;YAC7B,OAAO;YACP,OAAO;YACP,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,GAAG;YACd,YAAY,EAAE,GAAG;YACjB,eAAe,EAAE,CAAC;YAClB,iBAAiB,EAAE,GAAG,EAAE,GAAE,CAAC;SAC3B,CAAC;QACF,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAEjC,qEAAqE;QACrE,8EAA8E;QAC9E,MAAM,CAAC,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;YACrD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3C,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,WAAW;gBAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,KAAK,OAAO,CAAC,MAAM,EAAE;aACnB,IAAI,CACJ,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC,EACjC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACX,IAAI,EAAE,SAAkB;YACxB,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/D,CAAC,CACF;aACA,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACjB,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;YACrB,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;gBAAE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;YACrE,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC3B,IAAI,QAAQ;gBAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;;gBACrC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEJ,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;IAC3B,CAAC;IAED,SAAS,eAAe,CAAC,OAAe,EAAE,YAAoB;QAC7D,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO,SAAS,CAAC;QAC5D,OAAO,MAAM,CAAC;IACf,CAAC;IAED,SAAS,cAAc,CAAC,EAAU,EAAE,OAAe,EAAE,IAAgB;QACpE,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,IAAI,MAAkB,CAAC;QACvB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,uCAAuC,EAAE,eAAe,EAAE;gBACnF,MAAM,EAAE,wBAAwB;gBAChC,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,aAAa,CAAC,EAAU,EAAE,OAAe,EAAE,IAAY,EAAE,IAAY;QAC7E,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,IAAI,IAAkC,CAAC;QACvC,IAAI,CAAC;YACJ,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,+BAA+B,EAAE,eAAe,EAAE;gBAC3E,MAAM,EAAE,uBAAuB;gBAC/B,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,aAAa,CAAC,EAAU,EAAE,OAAe,EAAE,GAAW;QAC9D,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,eAAe,CAAC,iCAAiC,EAAE,eAAe,EAAE;gBAC7E,MAAM,EAAE,yBAAyB;gBACjC,SAAS,EAAE,KAAK;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,YAAY,CAAC,EAAU,EAAE,OAAe;QAChD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,UAAU,CAAC,EAAU;QAC7B,OAAO,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,WAAW,CAAC,EAAU,EAAE,OAAe;QAC/C,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,aAAa,CAAC,EAAU,EAAE,OAAe;QACjD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,IAAI,MAAM,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,aAAa,CAAC,EAAU,EAAE,OAAe;QACjD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,CAAC,eAAe,EAAE,CAAC;QACzB,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC;IACb,CAAC;IAED,SAAS,YAAY,CAAC,OAAe;QACpC,MAAM,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,OAAO,KAAK,aAAa;gBAAE,SAAS;YAC/C,MAAM,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE;gBACrB,OAAO,EAAE,aAAa;gBACtB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,QAAQ,EAAE,MAAM,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM;gBACxD,MAAM,EAAE,MAAM,CAAC,MAAM;aACrB,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,SAAS,OAAO;QACf,QAAQ,KAAK,CAAC,KAAK,IAAI,EAAE;YACxB,QAAQ,GAAG,IAAI,CAAC;YAChB,aAAa,EAAE,CAAC;YAChB,KAAK,MAAM,MAAM,IAAI,cAAc;gBAAE,MAAM,EAAE,CAAC;YAC9C,cAAc,CAAC,KAAK,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAoB,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACtC,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;gBAC7B,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBACnE,IAAI,OAAO;oBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;YACD,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CACjD,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC,EACjC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACX,IAAI,EAAE,SAAkB;oBACxB,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;iBACzC,CAAC,CACF,CAAC;gBACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;oBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAChE,CAAC;YACD,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,cAAc,CAAC,KAAK,EAAE,CAAC;YACvB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC7C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,cAAc,CAAC,QAAQ,EAAE,wCAAwC,CAAC,CAAC;QACvG,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,OAAO;QACN,WAAW;QACX,eAAe;QACf,cAAc;QACd,aAAa;QACb,aAAa;QACb,WAAW;QACX,aAAa;QACb,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,UAAU;QACV,OAAO;KACP,CAAC;AACH,CAAC"}
|