@ggui-ai/mcp-server 0.8.0 → 0.10.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 (67) hide show
  1. package/dist/api-renders-routes.d.ts +21 -0
  2. package/dist/api-renders-routes.d.ts.map +1 -1
  3. package/dist/api-renders-routes.js +54 -28
  4. package/dist/api-renders-stream-route.d.ts +80 -0
  5. package/dist/api-renders-stream-route.d.ts.map +1 -0
  6. package/dist/api-renders-stream-route.js +311 -0
  7. package/dist/build-mcp.d.ts +48 -7
  8. package/dist/build-mcp.d.ts.map +1 -1
  9. package/dist/build-mcp.js +87 -6
  10. package/dist/code-module-variant.d.ts +150 -0
  11. package/dist/code-module-variant.d.ts.map +1 -0
  12. package/dist/code-module-variant.js +243 -0
  13. package/dist/code-routes.d.ts +12 -2
  14. package/dist/code-routes.d.ts.map +1 -1
  15. package/dist/code-routes.js +12 -2
  16. package/dist/console-session-routes.d.ts.map +1 -1
  17. package/dist/console-session-routes.js +11 -0
  18. package/dist/control-service.d.ts +29 -3
  19. package/dist/control-service.d.ts.map +1 -1
  20. package/dist/control-service.js +26 -2
  21. package/dist/ggui-session-channel/action-ingress.d.ts +2 -2
  22. package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -1
  23. package/dist/ggui-session-channel/channel-subscriptions.d.ts +4 -4
  24. package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -1
  25. package/dist/ggui-session-channel/internal-types.d.ts +63 -9
  26. package/dist/ggui-session-channel/internal-types.d.ts.map +1 -1
  27. package/dist/ggui-session-channel/outbound.d.ts +15 -5
  28. package/dist/ggui-session-channel/outbound.d.ts.map +1 -1
  29. package/dist/ggui-session-channel/outbound.js +64 -24
  30. package/dist/ggui-session-channel/socket-router.d.ts +8 -3
  31. package/dist/ggui-session-channel/socket-router.d.ts.map +1 -1
  32. package/dist/ggui-session-channel/socket-router.js +6 -1
  33. package/dist/ggui-session-channel/subscribe.d.ts +48 -3
  34. package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
  35. package/dist/ggui-session-channel/subscribe.js +97 -36
  36. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +23 -13
  37. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -1
  38. package/dist/ggui-session-channel/subscriber-lifecycle.js +24 -11
  39. package/dist/ggui-session-channel.d.ts +58 -11
  40. package/dist/ggui-session-channel.d.ts.map +1 -1
  41. package/dist/ggui-session-channel.js +55 -19
  42. package/dist/health-routes.d.ts +19 -3
  43. package/dist/health-routes.d.ts.map +1 -1
  44. package/dist/health-routes.js +26 -18
  45. package/dist/index.d.ts +6 -3
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +13 -1
  48. package/dist/instructions-presets.js +10 -10
  49. package/dist/mcp-apps-outbound.d.ts +88 -11
  50. package/dist/mcp-apps-outbound.d.ts.map +1 -1
  51. package/dist/mcp-apps-outbound.js +470 -77
  52. package/dist/mcp-endpoint-routes.d.ts +23 -5
  53. package/dist/mcp-endpoint-routes.d.ts.map +1 -1
  54. package/dist/mcp-endpoint-routes.js +69 -1
  55. package/dist/oauth-as-routes.d.ts +11 -0
  56. package/dist/oauth-as-routes.d.ts.map +1 -1
  57. package/dist/oauth-as-routes.js +45 -1
  58. package/dist/oauth.d.ts.map +1 -1
  59. package/dist/oauth.js +8 -1
  60. package/dist/runtime-bundle-hash.d.ts +55 -0
  61. package/dist/runtime-bundle-hash.d.ts.map +1 -0
  62. package/dist/runtime-bundle-hash.js +85 -0
  63. package/dist/runtime-bundle-route.js +1 -1
  64. package/dist/server.d.ts +239 -61
  65. package/dist/server.d.ts.map +1 -1
  66. package/dist/server.js +355 -143
  67. package/package.json +13 -12
@@ -60,6 +60,16 @@ export interface BuildMcpServerOptions {
60
60
  * host that owns the iframe CSP itself.
61
61
  */
62
62
  readonly publicBaseUrl?: string;
63
+ /**
64
+ * Live-channel origins for the static shell's CSP declaration —
65
+ * forwarded to `installMcpAppsOutbound`. Deployments that set no
66
+ * `publicBaseUrl` (the cloud pod) pass their `wsUrl` + its ws→http
67
+ * origin flip here so the mounted iframe's `connect-src` covers the
68
+ * SSE / HTTP-polling session API and the WebSocket; otherwise
69
+ * cross-origin hosts CSP-block every network rung of the failover
70
+ * ladder (#471 round 11).
71
+ */
72
+ readonly extraConnectUrls?: readonly (string | undefined)[];
63
73
  /**
64
74
  * Identity-kind allowlist for tool registration. When set, handlers
65
75
  * whose `allowedFor` field is non-empty AND does NOT intersect this
@@ -116,13 +126,44 @@ export interface BuildMcpServerOptions {
116
126
  * `resources/read` later.
117
127
  */
118
128
  readonly extraResources?: ReadonlyArray<(server: McpServer) => void>;
129
+ /**
130
+ * Withhold every handler's per-result bootstrap MATERIAL from tool
131
+ * results — the read-plane-only posture.
132
+ *
133
+ * By default a successful tool result carries the handler's
134
+ * `resultMeta` — for `ggui_render` / `ggui_update` that is the
135
+ * `ai.ggui/render` bootstrap a host may mount DIRECTLY without any
136
+ * further round-trip, plus the spec-canonical pointer `_meta.ui.
137
+ * resourceUri`. Setting this makes the server publish only the
138
+ * durable IDENTITY: `structuredContent.resourceUri` (the `ui://`
139
+ * locator) and — the same value, on the wire slot MCP Apps hosts
140
+ * read — `_meta.ui.resourceUri` (+ the legacy flat `ui/resourceUri`).
141
+ * `resultMeta` is never invoked (no bootstrap token is minted for a
142
+ * slice nobody receives); the pointer is derived from the validated
143
+ * OUTPUT itself, the single source of truth `resultMeta` reuses.
144
+ * A host MUST resolve every view by an authenticated `resources/read`
145
+ * — the persisted-locator path — before it can mount anything.
146
+ *
147
+ * Why the pointer stays (ggui#537): the identity IS the pointer.
148
+ * Spec-canonical hosts (claude.ai, Claude Desktop, `@ggui-ai/
149
+ * mcp-apps-react`'s chat-helpers, the OSS samples) mount the
150
+ * per-render self-contained shell that `_meta.ui.resourceUri` names —
151
+ * a `resources/read` the HOST performs, exactly the read-plane path
152
+ * this posture wants. The first arm (f8c93405d) stripped `_meta`
153
+ * wholesale, which took the pointer with it and left every such host
154
+ * with the declaration-level static shell and a result it could not
155
+ * mount from ("Waiting for tool result…", prod 2026-08-16→17).
156
+ *
157
+ * That is a deployment posture, not a debug switch: a hosted
158
+ * deployment that wants "views mount only through the read plane"
159
+ * (thread-scoped ownership checks, fresh per-read credentials, no
160
+ * inlined bootstrap material crossing a chat transcript) states it
161
+ * here by construction, and any host that still expects the inlined
162
+ * bootstrap fails loudly (a locator it cannot resolve) instead of
163
+ * silently mounting stale material. `structuredContent` and `content`
164
+ * are untouched; only `_meta` is withheld.
165
+ */
166
+ readonly withholdResultMeta?: boolean;
119
167
  }
120
- /**
121
- * Build a fresh MCP server with every handler registered.
122
- *
123
- * `getContext` is a late-binding accessor so the HTTP layer can thread
124
- * per-request context (via AsyncLocalStorage or a closure) without
125
- * leaking the shape into this module.
126
- */
127
168
  export declare function buildMcpServer(info: ServerInfo, handlers: ReadonlyArray<SharedHandler<ZodRawShape, ZodRawShape>>, getContext: () => HandlerContext, logger: Logger, opts?: BuildMcpServerOptions): McpServer;
128
169
  //# sourceMappingURL=build-mcp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-mcp.d.ts","sourceRoot":"","sources":["../src/build-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,iCAAiC,EACvC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iCAAiC,CAAC;IAC3D;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAElE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC;CACtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAChE,UAAU,EAAE,MAAM,cAAc,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,qBAA0B,GAC/B,SAAS,CAqLX"}
1
+ {"version":3,"file":"build-mcp.d.ts","sourceRoot":"","sources":["../src/build-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAK,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAEL,KAAK,iCAAiC,EACvC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iCAAiC,CAAC;IAC3D;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAC5D;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAElE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC;IAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAyBD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAChE,UAAU,EAAE,MAAM,cAAc,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,qBAA0B,GAC/B,SAAS,CAwPX"}
package/dist/build-mcp.js CHANGED
@@ -13,6 +13,7 @@ import { registerAppTool } from '@modelcontextprotocol/ext-apps/server';
13
13
  import { isRecord } from '@ggui-ai/protocol';
14
14
  import { z } from 'zod';
15
15
  import { isHandlerFailure, } from '@ggui-ai/mcp-server-handlers';
16
+ import { GGUI_RENDER_RESOURCE_URI } from '@ggui-ai/protocol/integrations/mcp-apps';
16
17
  import { installMcpAppsOutbound, } from './mcp-apps-outbound.js';
17
18
  /**
18
19
  * Build a fresh MCP server with every handler registered.
@@ -21,6 +22,23 @@ import { installMcpAppsOutbound, } from './mcp-apps-outbound.js';
21
22
  * per-request context (via AsyncLocalStorage or a closure) without
22
23
  * leaking the shape into this module.
23
24
  */
25
+ /**
26
+ * The identity-only `_meta` a withholding server publishes (ggui#537):
27
+ * when the validated output carries a `ui://` `resourceUri` (the durable
28
+ * locator `ggui_render` / `ggui_update` surface on structuredContent),
29
+ * mirror it onto the spec-canonical `_meta.ui.resourceUri` slot MCP
30
+ * Apps hosts read (+ the legacy flat key), and nothing else. Same value
31
+ * `resultMeta` would have stamped, without invoking it — no bootstrap
32
+ * material, no minted token. `undefined` for outputs without a locator.
33
+ */
34
+ function identityPointerMeta(validated) {
35
+ if (!isRecord(validated))
36
+ return undefined;
37
+ const uri = validated['resourceUri'];
38
+ if (typeof uri !== 'string' || !uri.startsWith('ui://'))
39
+ return undefined;
40
+ return { ui: { resourceUri: uri }, 'ui/resourceUri': uri };
41
+ }
24
42
  export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
25
43
  const server = new McpServer({
26
44
  name: info.name,
@@ -28,8 +46,13 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
28
46
  ...(info.description ? { description: info.description } : {}),
29
47
  ...(opts.instructions ? { instructions: opts.instructions } : {}),
30
48
  });
49
+ // Content-addressed shell URI (stale-shell bust) — when MCP Apps
50
+ // outbound wiring registers the shell, declarations advertising the
51
+ // BARE `ui://ggui/render` are rewritten to the versioned twin below
52
+ // so host prefetch caches key on content, not on a constant string.
53
+ let shellResourceUri;
31
54
  if (opts.mcpAppsOutbound) {
32
- installMcpAppsOutbound(server, {
55
+ ({ shellResourceUri } = installMcpAppsOutbound(server, {
33
56
  ...(opts.shellHtml !== undefined ? { shellHtml: opts.shellHtml } : {}),
34
57
  // Thread the same per-request context accessor + logger the tool
35
58
  // path uses (`getContext`, param 3 of `buildMcpServer`) so the
@@ -41,7 +64,10 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
41
64
  ...(opts.publicBaseUrl !== undefined
42
65
  ? { publicBaseUrl: opts.publicBaseUrl }
43
66
  : {}),
44
- });
67
+ ...(opts.extraConnectUrls !== undefined
68
+ ? { extraConnectUrls: opts.extraConnectUrls }
69
+ : {}),
70
+ }));
45
71
  }
46
72
  // Per-request resource registrars supplied by the host. Run BEFORE
47
73
  // tool registration so `tools/list` ordering is unaffected and any
@@ -119,17 +145,64 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
119
145
  const validated = z.object(handler.outputSchema).parse(data);
120
146
  // Per-result `_meta` — NOT merged into structuredContent, so
121
147
  // agents that typecheck against the tool signature never see
122
- // it. This is where view-only bootstrap material lives.
123
- const meta = await handler.resultMeta?.(data, input, ctx);
148
+ // it. This is where view-only bootstrap material lives. Under
149
+ // the withhold posture only the identity pointer is published,
150
+ // derived from the output (see `withholdResultMeta`).
151
+ const meta = opts.withholdResultMeta === true
152
+ ? identityPointerMeta(validated)
153
+ : await handler.resultMeta?.(data, input, ctx);
124
154
  logger.info('tool_invoked', {
125
155
  tool: handler.name,
126
156
  appId: ctx.appId,
127
157
  outcome: 'success',
128
158
  elapsedMs: Date.now() - start,
129
159
  });
160
+ // When the handler's output carries a `nextStep`, lead the
161
+ // model-visible content with the imperative in PLAIN TEXT.
162
+ // Burying the chain cue inside the JSON block proved fragile
163
+ // on live hosts (the first claude.ai #471 test: the agent
164
+ // rendered, never noticed `nextStep`, ended its turn, and the
165
+ // user's click had no listener). The JSON stays second —
166
+ // structured consumers read `structuredContent` anyway.
167
+ const nextStepHint = validated !== null &&
168
+ typeof validated === 'object' &&
169
+ 'nextStep' in validated &&
170
+ validated.nextStep &&
171
+ typeof validated.nextStep
172
+ .example === 'string'
173
+ ? validated.nextStep.example
174
+ : undefined;
175
+ // The gesture-poll wrapper below describes ggui_consume's
176
+ // semantics ("catch an immediate gesture", "waits up to 25s")
177
+ // — it is ONLY true when the nextStep IS the consume hint.
178
+ // Ungated, it decorated ggui_handshake results too (whose
179
+ // nextStep is a ggui_render example), telling agents a
180
+ // not-yet-rendered UI "has interactive actions" — a live agent
181
+ // flagged the contradiction against an actions=∅ contract
182
+ // (2026-08-12).
183
+ const gestureHint = nextStepHint !== undefined && nextStepHint.includes('ggui_consume')
184
+ ? nextStepHint
185
+ : undefined;
130
186
  return {
131
187
  structuredContent: validated,
132
188
  content: [
189
+ ...(gestureHint !== undefined
190
+ ? [
191
+ {
192
+ type: 'text',
193
+ // Gentle + bounded, deliberately: a forcing
194
+ // imperative hijacked live agents into polling
195
+ // instead of acting (matrix scenario 6), and an
196
+ // unbounded "re-call on empty" looped them past
197
+ // their turn budget. The poll is a latency
198
+ // optimization, not the delivery guarantee — when
199
+ // nobody is polling, a gesture rings the chat via
200
+ // ui/message and arrives as a new user message
201
+ // carrying its own consume directive.
202
+ text: `The UI has interactive actions. After this turn's work, you may call ${gestureHint} once to catch an immediate gesture (waits up to 25s); if events is empty, end your turn — later gestures arrive as new user messages.`,
203
+ },
204
+ ]
205
+ : []),
133
206
  { type: 'text', text: JSON.stringify(validated) },
134
207
  ],
135
208
  ...(meta !== undefined ? { _meta: meta } : {}),
@@ -164,11 +237,19 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
164
237
  // error on the handler author's side — fail loud rather than
165
238
  // silently registering the tool without its UI surface.
166
239
  if (handler._meta && 'ui' in handler._meta) {
167
- const ui = handler._meta['ui'];
168
- if (!isMcpUiToolMeta(ui)) {
240
+ const uiRaw = handler._meta['ui'];
241
+ if (!isMcpUiToolMeta(uiRaw)) {
169
242
  throw new Error(`Tool ${handler.name} declares _meta.ui with an invalid shape — ` +
170
243
  `expected { resourceUri?: string; visibility?: ('model' | 'app')[] }.`);
171
244
  }
245
+ // Declarations author the STABLE `ui://ggui/render` constant;
246
+ // registration swaps in the content-addressed twin so hosts
247
+ // prefetch (and cache) the shell by its content hash. Handlers
248
+ // stay host-cache-agnostic; the swap lives in ONE place.
249
+ const ui = shellResourceUri !== undefined &&
250
+ uiRaw.resourceUri === GGUI_RENDER_RESOURCE_URI
251
+ ? { ...uiRaw, resourceUri: shellResourceUri }
252
+ : uiRaw;
172
253
  registerAppTool(server, handler.name, {
173
254
  ...baseConfig,
174
255
  _meta: { ...handler._meta, ui },
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Strict-CSP module-variant delivery (ggui#522 slice 2).
3
+ *
4
+ * The `/code/<hash>.js` route serves STORED component bytes — compiled
5
+ * ESM whose imports are still BARE specifiers (`react`,
6
+ * `@ggui-ai/design/primitives`, …). A browser cannot import that
7
+ * standalone; the renderer historically bridged the gap in-frame by
8
+ * rewriting bare specifiers to `data:` shims and instantiating the
9
+ * module from a `blob:` URL — both of which a strict host CSP refuses.
10
+ *
11
+ * This module adds the server-side twin of that bridge: a SECOND,
12
+ * immutable route `/code/<hash>.m<rt>.js` serving the SAME stored bytes
13
+ * with the rewrite already applied — every bare specifier resolved to a
14
+ * static shim asset under `/_ggui/shims/<rt>/<name>.js` on the same
15
+ * origin. A frame whose `script-src` allows only the asset origin can
16
+ * `import()` the variant URL directly: no `blob:`, no `data:`, no
17
+ * `'unsafe-eval'`.
18
+ *
19
+ * `<rt>` — the 12-hex content hash of the runtime bundle — is the
20
+ * variant KEY, and it lives in the PATH deliberately: the asset CDN
21
+ * (CloudFront `CACHING_OPTIMIZED`) ignores query strings, so a
22
+ * query-keyed variant would collapse to one cached body across
23
+ * runtime versions. The hash versions the whole family: the shim
24
+ * files ship in the runtime bundle's dist, so any shim-affecting
25
+ * change also changes the bundle bytes, which changes `<rt>`, which
26
+ * changes every variant URL — immutable caching stays sound.
27
+ *
28
+ * The route serves only the CURRENT `<rt>`: after a redeploy, a
29
+ * CDN-evicted old-variant URL 404s (`no-store`) and the renderer falls
30
+ * back to its raw-bytes ladder — honest degradation, never stale shims.
31
+ *
32
+ * 3rd-party gadget packages (GG.8.2) have no static shim (their
33
+ * exports are derived per-render from the generated code); a module
34
+ * importing one is NOT asset-deliverable. Both ends respect that: the
35
+ * minter declines to stamp `codeModuleUrl`, and the route declines
36
+ * (404) any body whose rewrite leaves a bare specifier standing.
37
+ */
38
+ import type { Express } from "express";
39
+ import type { Logger } from "./logger.js";
40
+ /**
41
+ * Mint the `codeModuleUrl` for one render's component code, or decline.
42
+ *
43
+ * `undefined` ⇒ the code is not asset-deliverable (it imports a
44
+ * package with no static shim) — the slice then carries only the raw
45
+ * `codeUrl`/`codeB64` carriers and the renderer uses its blob ladder.
46
+ */
47
+ export type MintCodeModuleUrl = (args: {
48
+ /** RAW stored component code (markers included — stripped here). */
49
+ readonly code: string;
50
+ /** `sha256(code)` — the same hash `codeUrl` is composed from. */
51
+ readonly hash: string;
52
+ /** Code base URL, no trailing slash (the emitter already trims). */
53
+ readonly base: string;
54
+ }) => string | undefined;
55
+ /** The variant URL for (hash, rt) under `base` — ONE composition site. */
56
+ export declare function composeCodeModuleUrl(args: {
57
+ readonly base: string;
58
+ readonly hash: string;
59
+ readonly runtimeHash: string;
60
+ }): string;
61
+ /**
62
+ * Build the minter every `codeUrl` emitter injects. Coverability is
63
+ * decided from the code's own import list: every bare specifier must
64
+ * have a static shim ({@link ASSET_SHIM_FOR_SPECIFIER}) or the mint
65
+ * declines. Markers are stripped first — their embedded JSON can
66
+ * contain strings the import scanner would misread.
67
+ */
68
+ export declare function createCodeModuleUrlMinter(args: {
69
+ readonly runtimeHash: string;
70
+ }): MintCodeModuleUrl;
71
+ /**
72
+ * Rewrite stored component bytes into the servable asset-module
73
+ * variant — the same preprocessing the renderer's blob ladder applies
74
+ * (strip markers, hoist imports) plus the `asset-url` specifier
75
+ * rewrite. Declines when any bare specifier survives (no static shim
76
+ * exists for it) instead of serving a module that fails at eval.
77
+ */
78
+ export declare function rewriteToAssetModule(args: {
79
+ readonly code: string;
80
+ readonly shimBaseUrl: string;
81
+ }): {
82
+ readonly ok: true;
83
+ readonly code: string;
84
+ } | {
85
+ readonly ok: false;
86
+ readonly leftover: readonly string[];
87
+ };
88
+ /**
89
+ * Read the static shim files emitted by the iframe-runtime build
90
+ * (`dist/shims/<name>.js`) into memory, keyed by file name. Captured
91
+ * ONCE at composition — the immutable shim route must keep serving the
92
+ * exact bytes its URL family was stamped for, even across an in-place
93
+ * rebuild of the dist (same guarantee the hashed bundle route makes by
94
+ * serving captured bytes).
95
+ *
96
+ * `undefined` when the directory is missing/empty (bundle built by an
97
+ * older toolchain) — callers then skip the whole variant family.
98
+ */
99
+ export declare function captureShimSources(shimsDir: string): ReadonlyMap<string, Buffer> | undefined;
100
+ /**
101
+ * Mount `GET <urlPrefix>/:rt/:file` serving the captured shim sources
102
+ * with the immutable posture of the hashed-bundle route. Only the
103
+ * CURRENT `runtimeHash` is served; any other `rt` 404s `no-store` so a
104
+ * stale URL can never lock wrong bytes into a cache.
105
+ */
106
+ export declare function mountShimRoutes(opts: {
107
+ readonly app: Express;
108
+ readonly urlPrefix: string;
109
+ readonly runtimeHash: string;
110
+ readonly shims: ReadonlyMap<string, Buffer>;
111
+ }): void;
112
+ /**
113
+ * Options for the `/code/<hash>.m<rt>.js` variant route
114
+ * (mounted by `mountCodeRoutes` BEFORE the plain routes).
115
+ */
116
+ export interface CodeModuleVariantOptions {
117
+ /** 12-hex content hash of the runtime bundle currently served. */
118
+ readonly runtimeHash: string;
119
+ /**
120
+ * Absolute base of THIS build's shim directory
121
+ * (`<codeBaseUrl><urlPrefix>/<runtimeHash>`), no trailing slash —
122
+ * the rewrite embeds it into every import.
123
+ */
124
+ readonly shimBaseUrl: string;
125
+ }
126
+ /**
127
+ * RegExp route for the variant path. A RegExp (not a string pattern)
128
+ * on purpose: two params separated by literal dots inside one path
129
+ * segment sit exactly on path-to-regexp's least-stable parsing ground,
130
+ * and a silent non-match here would read as "variant never works".
131
+ */
132
+ export declare const CODE_MODULE_VARIANT_ROUTE: RegExp;
133
+ /**
134
+ * Mount the module-variant route. Serving posture mirrors the plain
135
+ * code route (immutable on success, `no-store` on every failure); the
136
+ * two decline arms are:
137
+ *
138
+ * - `rt` ≠ the current runtime hash → 404. The shims for another
139
+ * build are unknowable here; the renderer falls back to raw bytes.
140
+ * - leftover bare specifier after rewrite → 404. The module imports
141
+ * a package with no static shim (3rd-party gadget); serving it
142
+ * would fail at eval inside the frame instead of falling back.
143
+ */
144
+ export declare function mountCodeModuleVariantRoute(opts: {
145
+ readonly app: Express;
146
+ readonly codeStore: import("@ggui-ai/mcp-server-core").CodeStore;
147
+ readonly variant: CodeModuleVariantOptions;
148
+ readonly logger: Logger;
149
+ }): void;
150
+ //# sourceMappingURL=code-module-variant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-module-variant.d.ts","sourceRoot":"","sources":["../src/code-module-variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAU1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE;IACrC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,KAAK,MAAM,GAAG,SAAS,CAAC;AAEzB,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,MAAM,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,iBAAiB,CASpB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAQ9G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAkBzC;AAQD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C,GAAG,IAAI,CAyCP;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,QAAkD,CAAC;AAEzF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,0BAA0B,EAAE,SAAS,CAAC;IACjE,QAAQ,CAAC,OAAO,EAAE,wBAAwB,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,IAAI,CA8DP"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Strict-CSP module-variant delivery (ggui#522 slice 2).
3
+ *
4
+ * The `/code/<hash>.js` route serves STORED component bytes — compiled
5
+ * ESM whose imports are still BARE specifiers (`react`,
6
+ * `@ggui-ai/design/primitives`, …). A browser cannot import that
7
+ * standalone; the renderer historically bridged the gap in-frame by
8
+ * rewriting bare specifiers to `data:` shims and instantiating the
9
+ * module from a `blob:` URL — both of which a strict host CSP refuses.
10
+ *
11
+ * This module adds the server-side twin of that bridge: a SECOND,
12
+ * immutable route `/code/<hash>.m<rt>.js` serving the SAME stored bytes
13
+ * with the rewrite already applied — every bare specifier resolved to a
14
+ * static shim asset under `/_ggui/shims/<rt>/<name>.js` on the same
15
+ * origin. A frame whose `script-src` allows only the asset origin can
16
+ * `import()` the variant URL directly: no `blob:`, no `data:`, no
17
+ * `'unsafe-eval'`.
18
+ *
19
+ * `<rt>` — the 12-hex content hash of the runtime bundle — is the
20
+ * variant KEY, and it lives in the PATH deliberately: the asset CDN
21
+ * (CloudFront `CACHING_OPTIMIZED`) ignores query strings, so a
22
+ * query-keyed variant would collapse to one cached body across
23
+ * runtime versions. The hash versions the whole family: the shim
24
+ * files ship in the runtime bundle's dist, so any shim-affecting
25
+ * change also changes the bundle bytes, which changes `<rt>`, which
26
+ * changes every variant URL — immutable caching stays sound.
27
+ *
28
+ * The route serves only the CURRENT `<rt>`: after a redeploy, a
29
+ * CDN-evicted old-variant URL 404s (`no-store`) and the renderer falls
30
+ * back to its raw-bytes ladder — honest degradation, never stale shims.
31
+ *
32
+ * 3rd-party gadget packages (GG.8.2) have no static shim (their
33
+ * exports are derived per-render from the generated code); a module
34
+ * importing one is NOT asset-deliverable. Both ends respect that: the
35
+ * minter declines to stamp `codeModuleUrl`, and the route declines
36
+ * (404) any body whose rewrite leaves a bare specifier standing.
37
+ */
38
+ import fs from "node:fs";
39
+ import path from "node:path";
40
+ import { rewriteImports, findBareImportSpecifiers, stripMarkers, ASSET_SHIM_FOR_SPECIFIER, } from "@ggui-ai/design/rendering";
41
+ import { hoistImports } from "@ggui-ai/design/module-loader";
42
+ /** The variant URL for (hash, rt) under `base` — ONE composition site. */
43
+ export function composeCodeModuleUrl(args) {
44
+ return `${args.base}/code/${args.hash}.m${args.runtimeHash}.js`;
45
+ }
46
+ /**
47
+ * Build the minter every `codeUrl` emitter injects. Coverability is
48
+ * decided from the code's own import list: every bare specifier must
49
+ * have a static shim ({@link ASSET_SHIM_FOR_SPECIFIER}) or the mint
50
+ * declines. Markers are stripped first — their embedded JSON can
51
+ * contain strings the import scanner would misread.
52
+ */
53
+ export function createCodeModuleUrlMinter(args) {
54
+ const { runtimeHash } = args;
55
+ return ({ code, hash, base }) => {
56
+ const bare = findBareImportSpecifiers(stripMarkers(code));
57
+ if (!bare.every((spec) => spec in ASSET_SHIM_FOR_SPECIFIER)) {
58
+ return undefined;
59
+ }
60
+ return composeCodeModuleUrl({ base, hash, runtimeHash });
61
+ };
62
+ }
63
+ /**
64
+ * Rewrite stored component bytes into the servable asset-module
65
+ * variant — the same preprocessing the renderer's blob ladder applies
66
+ * (strip markers, hoist imports) plus the `asset-url` specifier
67
+ * rewrite. Declines when any bare specifier survives (no static shim
68
+ * exists for it) instead of serving a module that fails at eval.
69
+ */
70
+ export function rewriteToAssetModule(args) {
71
+ const rewritten = rewriteImports(hoistImports(stripMarkers(args.code)), {
72
+ mode: "asset-url",
73
+ shimBaseUrl: args.shimBaseUrl,
74
+ });
75
+ const leftover = findBareImportSpecifiers(rewritten);
76
+ if (leftover.length > 0)
77
+ return { ok: false, leftover };
78
+ return { ok: true, code: rewritten };
79
+ }
80
+ /**
81
+ * Read the static shim files emitted by the iframe-runtime build
82
+ * (`dist/shims/<name>.js`) into memory, keyed by file name. Captured
83
+ * ONCE at composition — the immutable shim route must keep serving the
84
+ * exact bytes its URL family was stamped for, even across an in-place
85
+ * rebuild of the dist (same guarantee the hashed bundle route makes by
86
+ * serving captured bytes).
87
+ *
88
+ * `undefined` when the directory is missing/empty (bundle built by an
89
+ * older toolchain) — callers then skip the whole variant family.
90
+ */
91
+ export function captureShimSources(shimsDir) {
92
+ let entries;
93
+ try {
94
+ entries = fs.readdirSync(shimsDir);
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ const out = new Map();
100
+ for (const entry of entries) {
101
+ if (!entry.endsWith(".js"))
102
+ continue;
103
+ try {
104
+ out.set(entry, fs.readFileSync(path.join(shimsDir, entry)));
105
+ }
106
+ catch {
107
+ // A vanishing file mid-capture degrades to "that shim 404s";
108
+ // the renderer's fallback ladder covers it.
109
+ }
110
+ }
111
+ return out.size > 0 ? out : undefined;
112
+ }
113
+ /** File-segment shape of a shim asset (`react.js`, `jsx-runtime.js`). */
114
+ const SHIM_FILE_REGEX = /^[a-z][a-z-]*\.js$/;
115
+ /** 12-hex runtime-hash path segment. */
116
+ const RUNTIME_HASH_REGEX = /^[a-f0-9]{12}$/;
117
+ /**
118
+ * Mount `GET <urlPrefix>/:rt/:file` serving the captured shim sources
119
+ * with the immutable posture of the hashed-bundle route. Only the
120
+ * CURRENT `runtimeHash` is served; any other `rt` 404s `no-store` so a
121
+ * stale URL can never lock wrong bytes into a cache.
122
+ */
123
+ export function mountShimRoutes(opts) {
124
+ const { app, urlPrefix, runtimeHash, shims } = opts;
125
+ app.get(`${urlPrefix}/:rt/:file`, (req, res) => {
126
+ // Same CORS rationale as the runtime bundle: module fetches from a
127
+ // sandboxed `srcdoc` iframe (`null` origin) always run CORS mode.
128
+ res.setHeader("Access-Control-Allow-Origin", "*");
129
+ const rt = req.params["rt"];
130
+ const file = req.params["file"];
131
+ if (typeof rt !== "string" ||
132
+ !RUNTIME_HASH_REGEX.test(rt) ||
133
+ typeof file !== "string" ||
134
+ !SHIM_FILE_REGEX.test(file)) {
135
+ res.setHeader("Cache-Control", "no-store");
136
+ res.status(400).json({
137
+ error: {
138
+ code: "invalid_request",
139
+ message: "shim path must be /<12-hex runtime hash>/<name>.js",
140
+ },
141
+ });
142
+ return;
143
+ }
144
+ const source = rt === runtimeHash ? shims.get(file) : undefined;
145
+ if (source === undefined) {
146
+ res.setHeader("Cache-Control", "no-store");
147
+ res.status(404).json({
148
+ error: {
149
+ code: "not_found",
150
+ message: rt === runtimeHash
151
+ ? "unknown shim module"
152
+ : "shim family not served by this runtime build",
153
+ },
154
+ });
155
+ return;
156
+ }
157
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
158
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
159
+ res.status(200).send(source);
160
+ });
161
+ }
162
+ /**
163
+ * RegExp route for the variant path. A RegExp (not a string pattern)
164
+ * on purpose: two params separated by literal dots inside one path
165
+ * segment sit exactly on path-to-regexp's least-stable parsing ground,
166
+ * and a silent non-match here would read as "variant never works".
167
+ */
168
+ export const CODE_MODULE_VARIANT_ROUTE = /^\/code\/([a-f0-9]{64})\.m([a-f0-9]{12})\.js$/;
169
+ /**
170
+ * Mount the module-variant route. Serving posture mirrors the plain
171
+ * code route (immutable on success, `no-store` on every failure); the
172
+ * two decline arms are:
173
+ *
174
+ * - `rt` ≠ the current runtime hash → 404. The shims for another
175
+ * build are unknowable here; the renderer falls back to raw bytes.
176
+ * - leftover bare specifier after rewrite → 404. The module imports
177
+ * a package with no static shim (3rd-party gadget); serving it
178
+ * would fail at eval inside the frame instead of falling back.
179
+ */
180
+ export function mountCodeModuleVariantRoute(opts) {
181
+ const { app, codeStore, variant, logger } = opts;
182
+ app.get(CODE_MODULE_VARIANT_ROUTE, async (req, res) => {
183
+ res.setHeader("Access-Control-Allow-Origin", "*");
184
+ // RegExp routes surface capture groups as positional params.
185
+ const hash = req.params["0"];
186
+ const rt = req.params["1"];
187
+ if (typeof hash !== "string" || typeof rt !== "string") {
188
+ res.setHeader("Cache-Control", "no-store");
189
+ res.status(400).json({
190
+ error: { code: "invalid_request", message: "malformed variant path" },
191
+ });
192
+ return;
193
+ }
194
+ if (rt !== variant.runtimeHash) {
195
+ res.setHeader("Cache-Control", "no-store");
196
+ res.status(404).json({
197
+ error: {
198
+ code: "not_found",
199
+ message: "variant family not served by this runtime build",
200
+ },
201
+ });
202
+ return;
203
+ }
204
+ try {
205
+ const code = await codeStore.get(hash);
206
+ if (code === null) {
207
+ res.setHeader("Cache-Control", "no-store");
208
+ res.status(404).json({
209
+ error: { code: "not_found", message: "unknown code hash" },
210
+ });
211
+ return;
212
+ }
213
+ const rewritten = rewriteToAssetModule({
214
+ code,
215
+ shimBaseUrl: variant.shimBaseUrl,
216
+ });
217
+ if (!rewritten.ok) {
218
+ logger.warn("code_module_variant_declined", {
219
+ hash,
220
+ leftover: [...rewritten.leftover],
221
+ });
222
+ res.setHeader("Cache-Control", "no-store");
223
+ res.status(404).json({
224
+ error: {
225
+ code: "not_found",
226
+ message: "module not asset-deliverable (bare imports without static shims)",
227
+ },
228
+ });
229
+ return;
230
+ }
231
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
232
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
233
+ res.status(200).send(rewritten.code);
234
+ }
235
+ catch (err) {
236
+ logger.warn("code_module_variant_failed", { hash, error: String(err) });
237
+ res.setHeader("Cache-Control", "no-store");
238
+ res.status(500).json({
239
+ error: { code: "internal", message: "variant fetch failed" },
240
+ });
241
+ }
242
+ });
243
+ }
@@ -30,6 +30,7 @@
30
30
  import type { CodeStore } from "@ggui-ai/mcp-server-core";
31
31
  import type { Express } from "express";
32
32
  import type { Logger } from "./logger.js";
33
+ import { type CodeModuleVariantOptions } from "./code-module-variant.js";
33
34
  interface MountOptions {
34
35
  /** Express app to mount onto. */
35
36
  readonly app: Express;
@@ -37,10 +38,19 @@ interface MountOptions {
37
38
  readonly codeStore: CodeStore;
38
39
  /** Structured logger for fetch-failure warnings. */
39
40
  readonly logger: Logger;
41
+ /**
42
+ * Strict-CSP module-variant family (`/code/<hash>.m<rt>.js`,
43
+ * ggui#522 slice 2). Present ⇒ the variant route mounts BEFORE the
44
+ * plain routes — ordering is load-bearing: `/code/:hash.js`'s param
45
+ * would otherwise swallow the variant path (`:hash` = `<hash>.m<rt>`)
46
+ * and 400 it at the hash gate.
47
+ */
48
+ readonly moduleVariant?: CodeModuleVariantOptions;
40
49
  }
41
50
  /**
42
- * Mount `GET /code/:hash.js` + `GET /contract/:hash.js` onto the
43
- * express app. Returns nothing — the routes self-register.
51
+ * Mount `GET /code/:hash.js` + `GET /contract/:hash.js` (+ the
52
+ * module-variant route when configured) onto the express app. Returns
53
+ * nothing — the routes self-register.
44
54
  */
45
55
  export declare function mountCodeRoutes(opts: MountOptions): void;
46
56
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"code-routes.d.ts","sourceRoot":"","sources":["../src/code-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CA6CxD"}
1
+ {"version":3,"file":"code-routes.d.ts","sourceRoot":"","sources":["../src/code-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,0BAA0B,CAAC;AAElC,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,wBAAwB,CAAC;CACnD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAqDxD"}