@askdb/studio 0.2.0-beta.35 → 0.2.0-beta.36

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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Package-manager helpers shared by the setup wizard (`setup.ts`) and the
3
+ * execute-driver install endpoint (`server.ts`).
4
+ *
5
+ * Only allowlisted package names ever reach these helpers — never
6
+ * browser-supplied strings — but {@link packageManagerSpawnSpec} re-checks
7
+ * every argument anyway because Windows needs `shell: true` (see below).
8
+ */
9
+ import { existsSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ /** The package manager implied by a lockfile directly inside `dir`, if any. */
12
+ export function lockfilePackageManager(dir) {
13
+ if (existsSync(join(dir, "pnpm-lock.yaml")))
14
+ return "pnpm";
15
+ if (existsSync(join(dir, "bun.lockb")) || existsSync(join(dir, "bun.lock")))
16
+ return "bun";
17
+ if (existsSync(join(dir, "yarn.lock")))
18
+ return "yarn";
19
+ if (existsSync(join(dir, "package-lock.json")))
20
+ return "npm";
21
+ return null;
22
+ }
23
+ /** Arguments that add `packages` as dependencies with the given package manager. */
24
+ export function packageManagerAddArgs(pm, packages) {
25
+ return pm === "npm" ? ["install", "--save", ...packages] : ["add", ...packages];
26
+ }
27
+ /** Human-readable install command, e.g. `pnpm add pg`. */
28
+ export function formatInstallCommand(pm, packages) {
29
+ return [pm, ...packageManagerAddArgs(pm, packages)].join(" ");
30
+ }
31
+ // npm package names (optionally scoped, optionally with a version/range suffix)
32
+ // and simple `--flag` arguments. Nothing a shell would interpret.
33
+ const SAFE_ARG = /^(?:--[a-z][a-z-]*|(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:@[a-z0-9.^~<>=*_-]+)?|add|install)$/i;
34
+ /**
35
+ * How to spawn a package manager on this platform.
36
+ *
37
+ * On Windows, package managers are `.cmd` shims, and Node refuses to spawn
38
+ * `.cmd`/`.bat` files without a shell (EINVAL since Node 18.20.2 / 20.12.2,
39
+ * CVE-2024-27980). So Windows gets `shell: true` with the bare command name.
40
+ * Arguments are validated against a strict allowlist pattern first, so the
41
+ * shell never sees anything it could interpret.
42
+ */
43
+ export function packageManagerSpawnSpec(pm, args, platform = process.platform) {
44
+ for (const arg of args) {
45
+ if (!SAFE_ARG.test(arg)) {
46
+ throw new Error(`Refusing to pass unsafe argument to ${pm}: ${JSON.stringify(arg)}`);
47
+ }
48
+ }
49
+ if (platform === "win32") {
50
+ return { command: pm, args: [...args], shell: true };
51
+ }
52
+ return { command: pm, args: [...args], shell: false };
53
+ }
54
+ //# sourceMappingURL=package-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-manager.js","sourceRoot":"","sources":["../src/package-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,+EAA+E;AAC/E,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3D,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1F,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,QAA2B;IACnF,OAAO,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAAC,EAAkB,EAAE,QAA2B;IAClF,OAAO,CAAC,EAAE,EAAE,GAAG,qBAAqB,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChE,CAAC;AAQD,gFAAgF;AAChF,kEAAkE;AAClE,MAAM,QAAQ,GAAG,4GAA4G,CAAC;AAE9H;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAkB,EAClB,IAAuB,EACvB,WAA4B,OAAO,CAAC,QAAQ;IAE5C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,uCAAuC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ /**
3
+ * Request guard for the Studio local server.
4
+ *
5
+ * Studio's API can execute SQL against the configured database and write
6
+ * schema files, so it must not be drivable by arbitrary web pages the
7
+ * developer happens to visit. Three layers, applied before routing:
8
+ *
9
+ * 1. Host allowlist (every request, including static assets) — defeats DNS
10
+ * rebinding: a page on `evil.example` that rebinds to 127.0.0.1 still sends
11
+ * `Host: evil.example:<port>`.
12
+ * 2. Origin + content-type checks on state-changing `/api/*` requests —
13
+ * cross-site `fetch`/form posts carry a foreign `Origin`, and requiring
14
+ * `application/json` forces a CORS preflight that Studio never answers.
15
+ * 3. A per-launch random session token, embedded in the served `index.html`
16
+ * and required on every `/api/*` request as `x-askdb-studio-token`. A
17
+ * cross-origin page can't read `index.html`, so it can't learn the token.
18
+ */
19
+ export declare const STUDIO_TOKEN_HEADER = "x-askdb-studio-token";
20
+ export declare const STUDIO_TOKEN_META_NAME = "askdb-studio-token";
21
+ export type GuardFailure = {
22
+ status: 403 | 415;
23
+ message: string;
24
+ };
25
+ export declare function createStudioSessionToken(): string;
26
+ /** Hosts that mean "listen on every interface". */
27
+ export declare function isWildcardHost(host: string | undefined): boolean;
28
+ /** True for hosts only reachable from this machine. */
29
+ export declare function isLoopbackHost(host: string | undefined): boolean;
30
+ /**
31
+ * Host allowlist. Accepts `localhost`, `127.0.0.1`, `[::1]`, and the explicitly
32
+ * configured listen host (or, for wildcard binds, this machine's interface
33
+ * addresses) — and only on the port the connection actually arrived on.
34
+ */
35
+ export declare function checkHost(req: IncomingMessage, listenHost: string | undefined): GuardFailure | null;
36
+ /**
37
+ * `/api/*` checks: same-origin `Origin` on state-changing requests, the
38
+ * per-launch session token, and a JSON content type on requests with a body.
39
+ * Call after {@link checkHost} has passed.
40
+ */
41
+ export declare function checkApiRequest(req: IncomingMessage, sessionToken: string): GuardFailure | null;
42
+ /** Insert the session-token `<meta>` tag into the served `index.html`. */
43
+ export declare function injectSessionToken(html: string, sessionToken: string): string;
44
+ //# sourceMappingURL=request-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-guard.d.ts","sourceRoot":"","sources":["../src/request-guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAC1D,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIhE;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAShE;AAgDD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CAenG;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2B/F;AA4BD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAG7E"}
@@ -0,0 +1,170 @@
1
+ import { randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { networkInterfaces } from "node:os";
3
+ /**
4
+ * Request guard for the Studio local server.
5
+ *
6
+ * Studio's API can execute SQL against the configured database and write
7
+ * schema files, so it must not be drivable by arbitrary web pages the
8
+ * developer happens to visit. Three layers, applied before routing:
9
+ *
10
+ * 1. Host allowlist (every request, including static assets) — defeats DNS
11
+ * rebinding: a page on `evil.example` that rebinds to 127.0.0.1 still sends
12
+ * `Host: evil.example:<port>`.
13
+ * 2. Origin + content-type checks on state-changing `/api/*` requests —
14
+ * cross-site `fetch`/form posts carry a foreign `Origin`, and requiring
15
+ * `application/json` forces a CORS preflight that Studio never answers.
16
+ * 3. A per-launch random session token, embedded in the served `index.html`
17
+ * and required on every `/api/*` request as `x-askdb-studio-token`. A
18
+ * cross-origin page can't read `index.html`, so it can't learn the token.
19
+ */
20
+ export const STUDIO_TOKEN_HEADER = "x-askdb-studio-token";
21
+ export const STUDIO_TOKEN_META_NAME = "askdb-studio-token";
22
+ export function createStudioSessionToken() {
23
+ return randomBytes(32).toString("hex");
24
+ }
25
+ /** Hosts that mean "listen on every interface". */
26
+ export function isWildcardHost(host) {
27
+ if (host === undefined)
28
+ return false;
29
+ const h = host.trim().toLowerCase();
30
+ return h === "" || h === "0.0.0.0" || h === "::" || h === "[::]";
31
+ }
32
+ /** True for hosts only reachable from this machine. */
33
+ export function isLoopbackHost(host) {
34
+ if (host === undefined)
35
+ return true;
36
+ const h = host.trim().toLowerCase();
37
+ return (h === "localhost" ||
38
+ h === "::1" ||
39
+ h === "[::1]" ||
40
+ /^127(?:\.\d{1,3}){3}$/.test(h));
41
+ }
42
+ // A Host header is `hostname[:port]` — nothing else. Rejecting userinfo, paths,
43
+ // and whitespace up front matters because `new URL("http://" + host)` would
44
+ // happily parse `evil@127.0.0.1` as hostname 127.0.0.1.
45
+ const HOST_HEADER_PATTERN = /^(?:[a-z0-9.-]+|\[[0-9a-f:.]+\])(?::\d{1,5})?$/i;
46
+ /** Parse a Host header into its normalized `hostname` / `host` / numeric port. */
47
+ function parseHostHeader(value) {
48
+ if (!HOST_HEADER_PATTERN.test(value))
49
+ return null;
50
+ try {
51
+ const url = new URL(`http://${value}`);
52
+ return { hostname: url.hostname, host: url.host, port: url.port === "" ? 80 : Number(url.port) };
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }
58
+ /** Normalize a bare host/IP the same way `URL` normalizes a Host header's hostname. */
59
+ function normalizeHostname(host) {
60
+ const trimmed = host.trim();
61
+ if (trimmed === "")
62
+ return null;
63
+ const bracketed = trimmed.includes(":") && !trimmed.startsWith("[") ? `[${trimmed}]` : trimmed;
64
+ try {
65
+ return new URL(`http://${bracketed}`).hostname;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "[::1]"]);
72
+ function isAllowedHostname(hostname, listenHost) {
73
+ if (LOOPBACK_HOSTNAMES.has(hostname))
74
+ return true;
75
+ if (listenHost === undefined)
76
+ return false;
77
+ if (isWildcardHost(listenHost)) {
78
+ // Bound to every interface: accept this machine's own interface addresses
79
+ // (e.g. the LAN IP the CLI prints). IP literals can't be DNS-rebound.
80
+ for (const entries of Object.values(networkInterfaces())) {
81
+ for (const entry of entries ?? []) {
82
+ if (normalizeHostname(entry.address) === hostname)
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ return normalizeHostname(listenHost) === hostname;
89
+ }
90
+ /**
91
+ * Host allowlist. Accepts `localhost`, `127.0.0.1`, `[::1]`, and the explicitly
92
+ * configured listen host (or, for wildcard binds, this machine's interface
93
+ * addresses) — and only on the port the connection actually arrived on.
94
+ */
95
+ export function checkHost(req, listenHost) {
96
+ const raw = req.headers.host;
97
+ const parsed = typeof raw === "string" ? parseHostHeader(raw) : null;
98
+ if (!parsed ||
99
+ parsed.port !== req.socket.localPort ||
100
+ !isAllowedHostname(parsed.hostname, listenHost)) {
101
+ return {
102
+ status: 403,
103
+ message: "Studio rejected this request: unrecognized Host header. Open Studio via http://127.0.0.1:<port> or http://localhost:<port>.",
104
+ };
105
+ }
106
+ return null;
107
+ }
108
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
109
+ /**
110
+ * `/api/*` checks: same-origin `Origin` on state-changing requests, the
111
+ * per-launch session token, and a JSON content type on requests with a body.
112
+ * Call after {@link checkHost} has passed.
113
+ */
114
+ export function checkApiRequest(req, sessionToken) {
115
+ const method = (req.method ?? "GET").toUpperCase();
116
+ if (!SAFE_METHODS.has(method)) {
117
+ const origin = req.headers.origin;
118
+ if (origin !== undefined && !isSameOrigin(origin, req.headers.host)) {
119
+ return { status: 403, message: "Studio rejected this request: cross-origin requests are not allowed." };
120
+ }
121
+ }
122
+ if (!tokenMatches(req.headers[STUDIO_TOKEN_HEADER], sessionToken)) {
123
+ return {
124
+ status: 403,
125
+ message: "Studio rejected this request: missing or invalid session token. Reload Studio in the browser (the token changes every launch).",
126
+ };
127
+ }
128
+ if (!SAFE_METHODS.has(method) && (method !== "DELETE" || hasRequestBody(req))) {
129
+ const contentType = req.headers["content-type"];
130
+ const mediaType = typeof contentType === "string" ? contentType.split(";")[0].trim().toLowerCase() : "";
131
+ if (mediaType !== "application/json") {
132
+ return { status: 415, message: "Studio API requests must use Content-Type: application/json." };
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ function isSameOrigin(origin, hostHeader) {
138
+ if (typeof origin !== "string" || typeof hostHeader !== "string")
139
+ return false;
140
+ const host = parseHostHeader(hostHeader);
141
+ if (!host)
142
+ return false;
143
+ try {
144
+ const url = new URL(origin);
145
+ return url.protocol === "http:" && url.host === host.host;
146
+ }
147
+ catch {
148
+ // Includes `Origin: null` (sandboxed iframes, file://, some redirects).
149
+ return false;
150
+ }
151
+ }
152
+ function tokenMatches(provided, expected) {
153
+ if (typeof provided !== "string")
154
+ return false;
155
+ const a = Buffer.from(provided, "utf8");
156
+ const b = Buffer.from(expected, "utf8");
157
+ return a.length === b.length && timingSafeEqual(a, b);
158
+ }
159
+ function hasRequestBody(req) {
160
+ const length = req.headers["content-length"];
161
+ if (length !== undefined && Number(length) > 0)
162
+ return true;
163
+ return req.headers["transfer-encoding"] !== undefined;
164
+ }
165
+ /** Insert the session-token `<meta>` tag into the served `index.html`. */
166
+ export function injectSessionToken(html, sessionToken) {
167
+ const tag = `<meta name="${STUDIO_TOKEN_META_NAME}" content="${sessionToken}" />`;
168
+ return html.includes("</head>") ? html.replace("</head>", ` ${tag}\n </head>`) : `${tag}\n${html}`;
169
+ }
170
+ //# sourceMappingURL=request-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-guard.js","sourceRoot":"","sources":["../src/request-guard.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { createServer } from "node:http";
1
+ import { type Server } from "node:http";
2
2
  import { createPgvectorStore } from "@askdb/rag";
3
3
  import { type Workspace } from "@askdb/enrich";
4
4
  import type { SetupReason, StudioWorkspaceDto } from "./shared/api.js";
5
+ /** @internal Tests only — serve client assets from another directory. */
6
+ export declare function setStudioClientDirForTests(dir: string | undefined): void;
5
7
  export type StudioOptions = {
6
8
  schema: string;
7
9
  host?: string;
@@ -14,8 +16,17 @@ export type StudioOptions = {
14
16
  */
15
17
  setupReason?: SetupReason | null;
16
18
  };
17
- export type StudioServer = ReturnType<typeof createServer>;
19
+ export type StudioServer = Server & {
20
+ /**
21
+ * Per-launch session token. Every `/api/*` request must send it as the
22
+ * `x-askdb-studio-token` header; the browser app reads it from a `<meta>`
23
+ * tag injected into the served `index.html`.
24
+ */
25
+ readonly sessionToken: string;
26
+ };
18
27
  export declare function setStudioPgvectorStoreFactoryForTests(factory: typeof createPgvectorStore | undefined): void;
19
28
  export declare function createStudioServer(options: StudioOptions): StudioServer;
20
29
  export declare function serializeWorkspace(workspace: Workspace): StudioWorkspaceDto;
30
+ /** Largest JSON request body Studio accepts. */
31
+ export declare const MAX_JSON_BODY_BYTES: number;
21
32
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AAgCpF,OAAO,EAOL,mBAAmB,EAOpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAaL,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAYV,WAAW,EAKX,kBAAkB,EAGnB,MAAM,iBAAiB,CAAC;AAUzB,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AA0D3D,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,mBAAmB,GAAG,SAAS,GAC9C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAwJvE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,kBAAkB,CA8C3E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAwCjG,OAAO,EAOL,mBAAmB,EAOpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAaL,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAYV,WAAW,EAKX,kBAAkB,EAGnB,MAAM,iBAAiB,CAAC;AA4BzB,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAExE;AAMD,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAClC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AA0DF,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,mBAAmB,GAAG,SAAS,GAC9C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAuKvE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,kBAAkB,CA+C3E;AAmnDD,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,QAAc,CAAC"}