@revealui/mcp 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE.commercial +90 -0
  2. package/README.md +5 -13
  3. package/dist/adapters/db.d.ts +16 -7
  4. package/dist/adapters/db.d.ts.map +1 -1
  5. package/dist/adapters/db.js +6 -24
  6. package/dist/adapters/db.js.map +1 -1
  7. package/dist/client.d.ts +307 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +492 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/hypervisor.d.ts +18 -0
  12. package/dist/hypervisor.d.ts.map +1 -1
  13. package/dist/hypervisor.js +83 -4
  14. package/dist/hypervisor.js.map +1 -1
  15. package/dist/index.d.ts +6 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +8 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/metering.d.ts +59 -0
  20. package/dist/metering.d.ts.map +1 -0
  21. package/dist/metering.js +25 -0
  22. package/dist/metering.js.map +1 -0
  23. package/dist/oauth.d.ts +171 -0
  24. package/dist/oauth.d.ts.map +1 -0
  25. package/dist/oauth.js +292 -0
  26. package/dist/oauth.js.map +1 -0
  27. package/dist/remote-client.d.ts +86 -0
  28. package/dist/remote-client.d.ts.map +1 -0
  29. package/dist/remote-client.js +130 -0
  30. package/dist/remote-client.js.map +1 -0
  31. package/dist/servers/adapter.d.ts.map +1 -1
  32. package/dist/servers/adapter.js +4 -0
  33. package/dist/servers/adapter.js.map +1 -1
  34. package/dist/servers/factories/revealui-content.d.ts +85 -0
  35. package/dist/servers/factories/revealui-content.d.ts.map +1 -0
  36. package/dist/servers/factories/revealui-content.js +471 -0
  37. package/dist/servers/factories/revealui-content.js.map +1 -0
  38. package/dist/servers/revealui-content.d.ts +12 -18
  39. package/dist/servers/revealui-content.d.ts.map +1 -1
  40. package/dist/servers/revealui-content.js +14 -220
  41. package/dist/servers/revealui-content.js.map +1 -1
  42. package/dist/servers/revealui-memory.d.ts +24 -0
  43. package/dist/servers/revealui-memory.d.ts.map +1 -0
  44. package/dist/servers/revealui-memory.js +339 -0
  45. package/dist/servers/revealui-memory.js.map +1 -0
  46. package/dist/streamable-http.d.ts +72 -0
  47. package/dist/streamable-http.d.ts.map +1 -0
  48. package/dist/streamable-http.js +120 -0
  49. package/dist/streamable-http.js.map +1 -0
  50. package/package.json +24 -11
  51. package/dist/stores/postgres-idempotency.d.ts +0 -32
  52. package/dist/stores/postgres-idempotency.d.ts.map +0 -1
  53. package/dist/stores/postgres-idempotency.js +0 -63
  54. package/dist/stores/postgres-idempotency.js.map +0 -1
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Streamable HTTP server helper for `@revealui/mcp` (Stage 1 PR-1.1).
3
+ *
4
+ * Wraps `@modelcontextprotocol/sdk`'s `StreamableHTTPServerTransport` with
5
+ * session routing: one `MCP Server` + transport pair per concurrent client,
6
+ * identified by the `Mcp-Session-Id` header. Initialize requests without a
7
+ * session header allocate a new server via `createServer()`; subsequent
8
+ * requests route to the matching session.
9
+ *
10
+ * Returns a Node `(req, res) => Promise<void>` handler suitable for use with
11
+ * `http.createServer`, Express, Fastify, or any framework that exposes the
12
+ * raw Node request/response pair. A Web-Standard (Request → Response)
13
+ * variant can layer on top later — the session-routing logic is independent
14
+ * of the concrete transport flavour.
15
+ *
16
+ * The SDK's `StreamableHTTPServerTransport` is a one-session-per-instance
17
+ * primitive (see the `this.sessionId` field in its implementation). That's
18
+ * why we maintain an external `Map<sessionId, { server, transport }>` and
19
+ * not rely on a single transport to multiplex.
20
+ */
21
+ import type { IncomingMessage, ServerResponse } from 'node:http';
22
+ import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
23
+ export type StreamableHttpHandlerOptions = {
24
+ /**
25
+ * Called once per new session — returns a fresh `Server` instance to
26
+ * connect to the session's transport. Keep this function pure; do not
27
+ * share Server instances across sessions.
28
+ */
29
+ createServer: () => Server | Promise<Server>;
30
+ /** Generate session IDs. Default: `crypto.randomUUID()`. */
31
+ sessionIdGenerator?: () => string;
32
+ /** Called when a new session is initialized. */
33
+ onSessionInitialized?: (sessionId: string) => void | Promise<void>;
34
+ /** Called when a session is terminated (DELETE or transport close). */
35
+ onSessionClosed?: (sessionId: string) => void | Promise<void>;
36
+ /**
37
+ * Allowed `Origin` headers for DNS rebinding protection. If set, requests
38
+ * with other origins are rejected. Mirrors the SDK's option.
39
+ */
40
+ allowedOrigins?: string[];
41
+ /**
42
+ * Allowed `Host` headers for DNS rebinding protection. If set, requests
43
+ * with other hosts are rejected.
44
+ */
45
+ allowedHosts?: string[];
46
+ /**
47
+ * If true, responses are raw JSON instead of SSE streams. Useful for
48
+ * deployments that can't hold open long-lived connections (many
49
+ * serverless request/response platforms). Default: false.
50
+ */
51
+ enableJsonResponse?: boolean;
52
+ };
53
+ export type StreamableHttpHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
54
+ /**
55
+ * Build a Node HTTP request handler that routes MCP Streamable HTTP traffic
56
+ * to per-session `Server` + `Transport` pairs, allocating new sessions on
57
+ * InitializeRequest and reusing them on follow-up requests via the
58
+ * `Mcp-Session-Id` header.
59
+ *
60
+ * ```ts
61
+ * import { createServer as createHttpServer } from 'node:http';
62
+ * import { createNodeStreamableHttpHandler } from '@revealui/mcp/streamable-http';
63
+ *
64
+ * const mcpHandler = createNodeStreamableHttpHandler({
65
+ * createServer: () => makeMyMcpServer(),
66
+ * });
67
+ *
68
+ * createHttpServer(mcpHandler).listen(3000);
69
+ * ```
70
+ */
71
+ export declare function createNodeStreamableHttpHandler(options: StreamableHttpHandlerOptions): StreamableHttpHandler;
72
+ //# sourceMappingURL=streamable-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamable-http.d.ts","sourceRoot":"","sources":["../src/streamable-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAWxE,MAAM,MAAM,4BAA4B,GAAG;IACzC;;;;OAIG;IACH,YAAY,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,4DAA4D;IAC5D,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAElC,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE,uEAAuE;IACvE,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAWjG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,4BAA4B,GACpC,qBAAqB,CAqDvB"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Streamable HTTP server helper for `@revealui/mcp` (Stage 1 PR-1.1).
3
+ *
4
+ * Wraps `@modelcontextprotocol/sdk`'s `StreamableHTTPServerTransport` with
5
+ * session routing: one `MCP Server` + transport pair per concurrent client,
6
+ * identified by the `Mcp-Session-Id` header. Initialize requests without a
7
+ * session header allocate a new server via `createServer()`; subsequent
8
+ * requests route to the matching session.
9
+ *
10
+ * Returns a Node `(req, res) => Promise<void>` handler suitable for use with
11
+ * `http.createServer`, Express, Fastify, or any framework that exposes the
12
+ * raw Node request/response pair. A Web-Standard (Request → Response)
13
+ * variant can layer on top later — the session-routing logic is independent
14
+ * of the concrete transport flavour.
15
+ *
16
+ * The SDK's `StreamableHTTPServerTransport` is a one-session-per-instance
17
+ * primitive (see the `this.sessionId` field in its implementation). That's
18
+ * why we maintain an external `Map<sessionId, { server, transport }>` and
19
+ * not rely on a single transport to multiplex.
20
+ */
21
+ import { randomUUID } from 'node:crypto';
22
+ import { StreamableHTTPServerTransport, } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
23
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
24
+ // ---------------------------------------------------------------------------
25
+ // Handler factory
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Build a Node HTTP request handler that routes MCP Streamable HTTP traffic
29
+ * to per-session `Server` + `Transport` pairs, allocating new sessions on
30
+ * InitializeRequest and reusing them on follow-up requests via the
31
+ * `Mcp-Session-Id` header.
32
+ *
33
+ * ```ts
34
+ * import { createServer as createHttpServer } from 'node:http';
35
+ * import { createNodeStreamableHttpHandler } from '@revealui/mcp/streamable-http';
36
+ *
37
+ * const mcpHandler = createNodeStreamableHttpHandler({
38
+ * createServer: () => makeMyMcpServer(),
39
+ * });
40
+ *
41
+ * createHttpServer(mcpHandler).listen(3000);
42
+ * ```
43
+ */
44
+ export function createNodeStreamableHttpHandler(options) {
45
+ const sessions = new Map();
46
+ const generateSessionId = options.sessionIdGenerator ?? (() => randomUUID());
47
+ return async function mcpStreamableHttpHandler(req, res) {
48
+ const body = req.method === 'POST' ? await readJsonBody(req) : undefined;
49
+ const rawSession = req.headers['mcp-session-id'];
50
+ const sessionId = typeof rawSession === 'string' ? rawSession : undefined;
51
+ // Existing session — delegate to its transport.
52
+ if (sessionId) {
53
+ const entry = sessions.get(sessionId);
54
+ if (entry) {
55
+ await entry.transport.handleRequest(req, res, body);
56
+ return;
57
+ }
58
+ // Known-bad session header — let the transport produce the 404.
59
+ // Fall through to fresh-session handling, which will reject
60
+ // non-init requests.
61
+ }
62
+ // No session header: only an Initialize request may start a new session.
63
+ if (!sessionId && req.method === 'POST' && isInitializeRequest(body)) {
64
+ await handleNewSession(body);
65
+ return;
66
+ }
67
+ sendJsonError(res, 400, sessionId ? 'Unknown session ID' : 'Session ID required');
68
+ return;
69
+ async function handleNewSession(parsedBody) {
70
+ const server = await options.createServer();
71
+ const transportOptions = {
72
+ sessionIdGenerator: generateSessionId,
73
+ enableJsonResponse: options.enableJsonResponse,
74
+ allowedHosts: options.allowedHosts,
75
+ allowedOrigins: options.allowedOrigins,
76
+ onsessioninitialized: async (id) => {
77
+ sessions.set(id, { server, transport });
78
+ await options.onSessionInitialized?.(id);
79
+ },
80
+ onsessionclosed: async (id) => {
81
+ sessions.delete(id);
82
+ await options.onSessionClosed?.(id);
83
+ },
84
+ };
85
+ const transport = new StreamableHTTPServerTransport(transportOptions);
86
+ await server.connect(transport);
87
+ await transport.handleRequest(req, res, parsedBody);
88
+ }
89
+ };
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Helpers
93
+ // ---------------------------------------------------------------------------
94
+ async function readJsonBody(req) {
95
+ const chunks = [];
96
+ for await (const chunk of req) {
97
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
98
+ }
99
+ if (chunks.length === 0)
100
+ return undefined;
101
+ const raw = Buffer.concat(chunks).toString('utf8');
102
+ if (!raw)
103
+ return undefined;
104
+ try {
105
+ return JSON.parse(raw);
106
+ }
107
+ catch {
108
+ return undefined;
109
+ }
110
+ }
111
+ function sendJsonError(res, status, message) {
112
+ res.statusCode = status;
113
+ res.setHeader('content-type', 'application/json');
114
+ res.end(JSON.stringify({
115
+ jsonrpc: '2.0',
116
+ error: { code: -32000, message },
117
+ id: null,
118
+ }));
119
+ }
120
+ //# sourceMappingURL=streamable-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamable-http.js","sourceRoot":"","sources":["../src/streamable-http.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revealui/mcp",
3
- "version": "0.1.10",
4
- "description": "Model Context Protocol integrations for RevealUI adapter framework, hypervisor, and MCP contracts",
3
+ "version": "0.2.0",
4
+ "description": "Model Context Protocol framework MCP server hypervisor, adapter pattern, tool discovery, and adapters for Stripe, Supabase, and Vercel.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/RevealUIStudio/revealui.git",
@@ -12,15 +12,16 @@
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
13
13
  "dotenv": "^17.4.1",
14
14
  "jose": "^6.2.2",
15
- "@revealui/config": "0.3.4",
16
- "@revealui/contracts": "1.3.7",
17
- "@revealui/core": "0.5.6"
15
+ "@revealui/config": "0.4.0",
16
+ "@revealui/contracts": "1.4.0",
17
+ "@revealui/core": "0.6.0",
18
+ "@revealui/security": "0.3.0"
18
19
  },
19
20
  "devDependencies": {
20
- "@electric-sql/pglite": "^0.4.3",
21
+ "@electric-sql/pglite": "^0.4.4",
21
22
  "pg": "^8.20.0",
22
- "typescript": "^6.0.2",
23
- "vitest": "^4.1.3"
23
+ "typescript": "^6.0.3",
24
+ "vitest": "^4.1.5"
24
25
  },
25
26
  "engines": {
26
27
  "node": ">=24.13.0"
@@ -66,9 +67,21 @@
66
67
  "types": "./dist/auth.d.ts",
67
68
  "import": "./dist/auth.js"
68
69
  },
69
- "./stores/postgres": {
70
- "types": "./dist/stores/postgres-idempotency.d.ts",
71
- "import": "./dist/stores/postgres-idempotency.js"
70
+ "./streamable-http": {
71
+ "types": "./dist/streamable-http.d.ts",
72
+ "import": "./dist/streamable-http.js"
73
+ },
74
+ "./oauth": {
75
+ "types": "./dist/oauth.d.ts",
76
+ "import": "./dist/oauth.js"
77
+ },
78
+ "./client": {
79
+ "types": "./dist/client.d.ts",
80
+ "import": "./dist/client.js"
81
+ },
82
+ "./remote-client": {
83
+ "types": "./dist/remote-client.d.ts",
84
+ "import": "./dist/remote-client.js"
72
85
  }
73
86
  },
74
87
  "files": [
@@ -1,32 +0,0 @@
1
- /**
2
- * Postgres-backed Idempotency Store
3
- *
4
- * Persistent implementation of the {@link IdempotencyStore} interface
5
- * using a PostgreSQL table. Stores MCP responses keyed by idempotency
6
- * key with automatic TTL expiration via a `expires_at` column.
7
- *
8
- * Table schema (auto-created on first use):
9
- * mcp_idempotency_cache (
10
- * key TEXT PRIMARY KEY,
11
- * response JSONB NOT NULL,
12
- * expires_at TIMESTAMPTZ NOT NULL
13
- * )
14
- */
15
- import type { IdempotencyStore } from '../servers/adapter.js';
16
- interface PgClient {
17
- query(text: string, values?: unknown[]): Promise<{
18
- rows: Record<string, unknown>[];
19
- }>;
20
- }
21
- /**
22
- * Create a Postgres-backed idempotency store.
23
- *
24
- * @param client - Any object with a `.query(text, values?)` method
25
- * (e.g. `pg.Pool`, `pg.Client`, `@neondatabase/serverless` pool).
26
- * @param opts - Optional configuration
27
- */
28
- export declare function createPostgresIdempotencyStore(client: PgClient, opts?: {
29
- ensureTable?: boolean;
30
- }): IdempotencyStore;
31
- export {};
32
- //# sourceMappingURL=postgres-idempotency.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"postgres-idempotency.d.ts","sourceRoot":"","sources":["../../src/stores/postgres-idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,uBAAuB,CAAC;AAE3E,UAAU,QAAQ;IAChB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;CACvF;AAYD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,QAAQ,EAChB,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,gBAAgB,CAwClB"}
@@ -1,63 +0,0 @@
1
- /**
2
- * Postgres-backed Idempotency Store
3
- *
4
- * Persistent implementation of the {@link IdempotencyStore} interface
5
- * using a PostgreSQL table. Stores MCP responses keyed by idempotency
6
- * key with automatic TTL expiration via a `expires_at` column.
7
- *
8
- * Table schema (auto-created on first use):
9
- * mcp_idempotency_cache (
10
- * key TEXT PRIMARY KEY,
11
- * response JSONB NOT NULL,
12
- * expires_at TIMESTAMPTZ NOT NULL
13
- * )
14
- */
15
- const TABLE = 'mcp_idempotency_cache';
16
- const CREATE_TABLE_SQL = `
17
- CREATE TABLE IF NOT EXISTS ${TABLE} (
18
- key TEXT PRIMARY KEY,
19
- response JSONB NOT NULL,
20
- expires_at TIMESTAMPTZ NOT NULL
21
- );
22
- `;
23
- /**
24
- * Create a Postgres-backed idempotency store.
25
- *
26
- * @param client - Any object with a `.query(text, values?)` method
27
- * (e.g. `pg.Pool`, `pg.Client`, `@neondatabase/serverless` pool).
28
- * @param opts - Optional configuration
29
- */
30
- export function createPostgresIdempotencyStore(client, opts) {
31
- let tableEnsured = opts?.ensureTable === false;
32
- async function ensureTable() {
33
- if (tableEnsured)
34
- return;
35
- await client.query(CREATE_TABLE_SQL);
36
- tableEnsured = true;
37
- }
38
- return {
39
- async get(key) {
40
- await ensureTable();
41
- const { rows } = await client.query(`SELECT response, expires_at FROM ${TABLE} WHERE key = $1 AND expires_at > NOW()`, [key]);
42
- const row = rows[0];
43
- if (!row)
44
- return null;
45
- return {
46
- response: row.response,
47
- expiresAt: new Date(row.expires_at).getTime(),
48
- };
49
- },
50
- async set(key, response, ttlMs) {
51
- await ensureTable();
52
- const expiresAt = new Date(Date.now() + ttlMs).toISOString();
53
- await client.query(`INSERT INTO ${TABLE} (key, response, expires_at)
54
- VALUES ($1, $2, $3)
55
- ON CONFLICT (key) DO UPDATE SET response = $2, expires_at = $3`, [key, JSON.stringify(response), expiresAt]);
56
- },
57
- async delete(key) {
58
- await ensureTable();
59
- await client.query(`DELETE FROM ${TABLE} WHERE key = $1`, [key]);
60
- },
61
- };
62
- }
63
- //# sourceMappingURL=postgres-idempotency.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"postgres-idempotency.js","sourceRoot":"","sources":["../../src/stores/postgres-idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,MAAM,KAAK,GAAG,uBAAuB,CAAC;AAEtC,MAAM,gBAAgB,GAAG;+BACM,KAAK;;;;;CAKnC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAC5C,MAAgB,EAChB,IAAgC;IAEhC,IAAI,YAAY,GAAG,IAAI,EAAE,WAAW,KAAK,KAAK,CAAC;IAE/C,KAAK,UAAU,WAAW;QACxB,IAAI,YAAY;YAAE,OAAO;QACzB,MAAM,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACrC,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,GAAW;YACnB,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CACjC,oCAAoC,KAAK,wCAAwC,EACjF,CAAC,GAAG,CAAC,CACN,CAAC;YACF,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,OAAO;gBACL,QAAQ,EAAE,GAAG,CAAC,QAAuB;gBACrC,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAoB,CAAC,CAAC,OAAO,EAAE;aACxD,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,QAAqB,EAAE,KAAa;YACzD,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,MAAM,CAAC,KAAK,CAChB,eAAe,KAAK;;wEAE4C,EAChE,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,CAC3C,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,GAAW;YACtB,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,KAAK,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACnE,CAAC;KACF,CAAC;AACJ,CAAC"}