[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69BSXPVQWSQVYNT6QK38VN1":3,"skill-same:01M69BSXPVQWSQVYNT6QK38VN1":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":50,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M69BSXPVQWSQVYNT6QK38VN1","webview-trpc-messaging",null,"Implements tRPC-based communication between VS Code extension host and React webviews. Use when creating new webview procedures (queries, mutations, subscriptions), adding a new webview router, wiring up a webview controller, using the tRPC client from React components, applying telemetry middleware (telemetryMiddlewareBody), or supporting AbortSignal-based cancellation in webview operations.","microsoft\u002Fvscode-documentdb","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69BRX0BQJ0QGRNCNZAAAXBV",33,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fwebview-trpc-messaging\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"webview trpc messaging","trpc","messaging","router","new","when","wiring","middleware","host","mutations","queries","up","components","use","controller","communication","code","telemetry","react components","react","2026-11-10 10:12:30",false,"---\nname: webview-trpc-messaging\ndescription: Implements tRPC-based communication between VS Code extension host and React webviews. Use when creating new webview procedures (queries, mutations, subscriptions), adding a new webview router, wiring up a webview controller, using the tRPC client from React components, applying telemetry middleware (telemetryMiddlewareBody), or supporting AbortSignal-based cancellation in webview operations.\n---\n\n# Webview tRPC Messaging\n\nType-safe RPC communication between the VS Code extension host (server) and React webviews (client) using tRPC.\n\n## Architecture Overview\n\n```\nReact Webview (client)                    Extension Host (server)\n─────────────────────                     ──────────────────────\nuseTrpcClient() hook                      WebviewController\n  └─ createTRPCClient                       └─ setupTrpc()\n       └─ vscodeLink ──── postMessage ────►     ├─ callerFactory(appRouter)\n            (send\u002FonReceive)              ◄─────┤   └─ procedure(input)\n                                                └─ abort\u002Fsubscription.stop\n```\n\n**Key files** (read as needed for implementation details):\n\n| File                                                     | Purpose                                                                                                                                                       |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `@microsoft\u002Fvscode-ext-webview` (shared)                 | tRPC init via `initWebviewTrpc`, `publicProcedure`, `router`, `BaseRouterContext`                                                                             |\n| `@microsoft\u002Fvscode-ext-webview\u002Fhost` (telemetry)         | `telemetryMiddlewareBody`, `ProcedureLogger`, `TelemetryRunner` (consumer builds `publicProcedureWithTelemetry`)                                              |\n| `src\u002Fwebviews\u002F_integration\u002Ftrpc.ts`                      | Consumer tRPC instance: `publicProcedureWithTelemetry`, the DocumentDB `TelemetryRunner`, and the `RpcEnrichment` shape it contributes to `ctx.actionContext` |\n| `src\u002Fwebviews\u002F_integration\u002FappRouter.ts`                 | Root router + `publicProcedureWithTelemetry` wiring + DocumentDB `BaseRouterContext`                                                                          |\n| `src\u002Fwebviews\u002F_integration\u002Fconfiguration.ts`             | Consumer-owned knobs (telemetry namespace, bundle layout, dev-server host)                                                                                    |\n| `@microsoft\u002Fvscode-ext-webview\u002Fhost` (WebviewController) | `WebviewController` + `openWebview` factory: WebviewPanel lifecycle, tRPC dispatcher (queries, mutations, subscriptions, abort)                               |\n| `src\u002Fwebviews\u002F_integration\u002FopenAppWebview.ts`            | DocumentDB factory preset that pre-fills router + bundle layout (`openAppWebview`)                                                                            |\n| `src\u002Fwebviews\u002F_integration\u002FuseTrpcClient.ts`             | React hook providing the tRPC client (pre-typed against `AppRouter`)                                                                                          |\n| `@microsoft\u002Fvscode-ext-webview\u002Fwebview` (vscodeLink)     | Custom tRPC link bridging `postMessage` transport                                                                                                             |\n\n## Creating a New Router\n\nEach webview maintains its own router. Follow this pattern:\n\n### 1. Define the router context\n\nExtend `BaseRouterContext` with view-specific fields:\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002Fdocumentdb\u002FmyView\u002FmyViewRouter.ts\nimport { type BaseRouterContext } from '..\u002F..\u002F_integration\u002FappRouter';\n\nexport type RouterContext = BaseRouterContext & {\n  clusterId: string;\n  viewId: string;\n  databaseName: string;\n  \u002F\u002F add view-specific fields\n};\n```\n\n### 2. Define procedures\n\n```typescript\nimport { z } from 'zod';\nimport {\n  publicProcedure,\n  publicProcedureWithTelemetry,\n  router,\n  type WithTelemetry,\n} from '..\u002F..\u002F_integration\u002FappRouter';\nimport { type RouterContext } from '.\u002FmyViewRouter';\n\nexport const myViewRouter = router({\n  \u002F\u002F Query with telemetry (preferred for operations that touch external services)\n  getData: publicProcedureWithTelemetry.input(z.object({ id: z.string() })).query(async ({ input, ctx }) => {\n    const myCtx = ctx as WithTelemetry\u003CRouterContext>;\n    \u002F\u002F Instrumented procedure: myCtx.actionContext (the full IActionContext) is present.\n    myCtx.actionContext.telemetry.properties.itemId = input.id;\n    \u002F\u002F myCtx.signal is the AbortSignal for cancellation\n    return { data: 'result' };\n  }),\n\n  \u002F\u002F Mutation without telemetry (rare, use for fire-and-forget)\n  doAction: publicProcedure.input(z.string()).mutation(({ input }) => {\n    \u002F\u002F lightweight operation\n  }),\n});\n```\n\n### 3. Register in appRouter\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002F_integration\u002FappRouter.ts\nimport { myViewRouter } from '..\u002F..\u002Fdocumentdb\u002FmyView\u002FmyViewRouter';\n\nexport const appRouter = router({\n  common: commonRouter,\n  mongoClusters: {\n    documentView: documentViewRouter,\n    collectionView: collectionViewRouter,\n    myView: myViewRouter, \u002F\u002F \u003C-- add here\n  },\n});\n```\n\n### 4. Create the controller\n\nConstruction-only panels are opened with a factory function that builds the\nconfig + router context and calls the `openAppWebview` preset (which pre-fills\nthe app router, caller factory, and bundle layout):\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002Fdocumentdb\u002FmyView\u002FmyViewController.ts\nimport * as vscode from 'vscode';\nimport { API } from '..\u002F..\u002F..\u002FDocumentDBExperiences';\nimport { type AppWebviewController, openAppWebview } from '..\u002F..\u002F_integration\u002FopenAppWebview';\nimport { type RouterContext } from '.\u002FmyViewRouter';\n\nexport function openMyViewPanel(initialData: MyViewConfig): AppWebviewController\u003CMyViewConfig> {\n  const title = `${initialData.databaseName}`;\n\n  const trpcContext: RouterContext = {\n    dbExperience: API.DocumentDB,\n    webviewName: 'myView',\n    clusterId: initialData.clusterId,\n    viewId: initialData.viewId,\n    databaseName: initialData.databaseName,\n  };\n\n  return openAppWebview({\n    title,\n    webviewName: 'myView',\n    config: initialData,\n    context: trpcContext,\n  });\n}\n```\n\nThe returned `AppWebviewController` handle exposes `panel`, `onDisposed`,\n`revealToForeground`, `isDisposed`, and `dispose`. Genuinely stateful panels may\nstill extend `WebviewController` from `@microsoft\u002Fvscode-ext-webview\u002Fhost`\ndirectly instead of using the factory.\n\n> **Important:** The `webviewName` field passed to `openAppWebview` is the\n> **registry key** (`viewType`, must match a key in `WebviewRegistry`, e.g.\n> `collectionView`). The `webviewName` in the tRPC context is a **telemetry\n> label** used in telemetry event names. These may be the same string but serve\n> different purposes -- do not confuse them.\n\n### 5. Register in WebviewRegistry\n\nAdd your React component to the registry. The key must match the `webviewName`\npassed to `openAppWebview` (`viewType`). The `WebviewName` type (exported from\nthe same file) ensures compile-time validation of webview names.\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002F_integration\u002FWebviewRegistry.ts\nimport { MyView } from '..\u002F..\u002Fdocumentdb\u002FmyView\u002FMyView';\n\nexport const WebviewRegistry = {\n  collectionView: CollectionView,\n  documentView: DocumentView,\n  myViewName: MyView, \u002F\u002F \u003C-- add your entry\n} as const;\n\nexport type WebviewName = keyof typeof WebviewRegistry;\n```\n\n## Telemetry: `publicProcedure` vs `publicProcedureWithTelemetry`\n\n| Base                           | When to use                                                                  | `ctx.actionContext`                                      |\n| ------------------------------ | ---------------------------------------------------------------------------- | -------------------------------------------------------- |\n| `publicProcedure`              | Fire-and-forget, no external calls, telemetry reported separately            | absent (do not read it)                                  |\n| `publicProcedureWithTelemetry` | **Default choice.** Any procedure touching DB, network, or user-visible work | Guaranteed, injected by the DocumentDB `TelemetryRunner` |\n\n`publicProcedureWithTelemetry` is `publicProcedure.use(telemetryMiddlewareBody(documentDbTelemetryRunner, ...))` (built in `trpc.ts`). The framework's `telemetryMiddlewareBody` delegates to the DocumentDB `TelemetryRunner`, which wraps the call in `callWithTelemetryAndErrorHandling`, contributes the full `IActionContext` to `ctx.actionContext`, auto-generates a telemetry event named `documentDB.rpc.{type}.{path}`, and records errors, duration, and abort status.\n\n`actionContext` is **not** a field on the base `RouterContext` — it is an additive enrichment. Narrow to `WithTelemetry\u003CRouterContext>` (`= RouterContext & { actionContext }`) in an instrumented procedure to read it; a plain `publicProcedure` procedure narrows to bare `RouterContext`, so reading `actionContext` there is a compile error instead of a runtime `undefined`.\n\nAccess telemetry safely:\n\n```typescript\nconst myCtx = ctx as WithTelemetry\u003CRouterContext>;\nmyCtx.actionContext.telemetry.properties.myCustomProp = 'value';\nmyCtx.actionContext.telemetry.measurements.itemCount = items.length;\n```\n\n## AbortSignal Support\n\nEvery tRPC operation (query, mutation, subscription) receives its own `AbortController`. Cancellation flows:\n\n```\nClient (React)                              Server (Extension Host)\n──────────────                              ──────────────────────\n\u002F\u002F Queries\u002FMutations:\nac = new AbortController()\ntrpcClient.myProc.query(input,\n  { signal: ac.signal })\nac.abort()  →  sends 'abort' msg  →  abortController.abort()\n                                        → ctx.signal.aborted = true\n\n\u002F\u002F Subscriptions:\nsub = trpcClient.mySub.subscribe(...)\nsub.unsubscribe()  →  'subscription.stop'  →  abortController.abort()\n```\n\n### Using abort in procedures\n\n```typescript\n\u002F\u002F Pass signal to APIs that accept it (MongoDB driver, fetch, etc.)\ngetData: publicProcedureWithTelemetry\n    .input(z.object({ filter: z.record(z.unknown()) }))\n    .query(async ({ input, ctx }) => {\n        const myCtx = ctx as RouterContext;\n\n        \u002F\u002F Option 1: Pass to driver (preferred)\n        const cursor = collection.find(input.filter, { signal: myCtx.signal });\n\n        \u002F\u002F Option 2: Manual check in loops\n        for (const item of items) {\n            if (myCtx.signal?.aborted) return;\n            await processItem(item);\n        }\n    }),\n```\n\n### Client-side abort\n\n```tsx\nconst trpcClient = useTrpcClient();\nconst abortControllerRef = useRef\u003CAbortController>();\n\nconst runQuery = async () => {\n  abortControllerRef.current?.abort(); \u002F\u002F cancel previous\n  const ac = new AbortController();\n  abortControllerRef.current = ac;\n\n  const result = await trpcClient.mongoClusters.collectionView.myQuery.query(input, { signal: ac.signal });\n};\n```\n\nWhen `publicProcedureWithTelemetry` detects an aborted signal, the DocumentDB `TelemetryRunner` sets `telemetry.properties.aborted = 'true'` and `result = 'Canceled'` automatically.\n\n## Subscriptions\n\nSubscriptions stream multiple values from server to client using async generators:\n\n```typescript\n\u002F\u002F Server (router)\nstreamData: publicProcedureWithTelemetry\n    .input(z.object({ batchSize: z.number() }))\n    .subscription(async function* ({ input, ctx }) {\n        const myCtx = ctx as RouterContext;\n\n        for (let i = 0; i \u003C total; i += input.batchSize) {\n            if (myCtx.signal?.aborted) return; \u002F\u002F check before each yield\n            const batch = await fetchBatch(i, input.batchSize);\n            yield batch;\n        }\n    }),\n\n\u002F\u002F Client (React)\nconst sub = trpcClient.mongoClusters.myView.streamData.subscribe(\n    { batchSize: 100 },\n    {\n        onData(batch) { \u002F* handle each batch *\u002F },\n        onComplete() { \u002F* all done *\u002F },\n        onError(err) { \u002F* handle error *\u002F },\n    },\n);\n\n\u002F\u002F To stop:\nsub.unsubscribe();\n```\n\n## Client-Side Hook Usage\n\n```tsx\nimport { useTrpcClient } from '..\u002F_integration\u002FuseTrpcClient';\nimport { useConfiguration } from '@microsoft\u002Fvscode-ext-webview\u002Freact';\n\nexport const MyComponent = () => {\n  const trpcClient = useTrpcClient();\n  const config = useConfiguration\u003CMyViewConfig>();\n\n  useEffect(() => {\n    trpcClient.mongoClusters.myView.getData.query({ id: config.documentId }).then(setData);\n  }, []);\n};\n```\n\n`useConfiguration\u003CT>()` retrieves the initial config passed to `WebviewController` constructor (serialized via `encodeURIComponent(JSON.stringify(...))`).\n\n## Common Pitfalls\n\n- **Never use `any`** in procedure context casts — narrow with `ctx as WithTelemetry\u003CRouterContext>` when the procedure reads telemetry (`ctx.actionContext.telemetry`), or `ctx as RouterContext` otherwise\n- **Always prefer `publicProcedureWithTelemetry`** unless you have a specific reason not to\n- **Always check `myCtx.signal?.aborted`** in long-running loops — not checking causes wasted work after client cancels\n- **Do not mutate the shared `context` object** — `WebviewController` clones it per-operation already, but router code should treat `ctx` as read-only\n- **Input validation uses `zod`** — always define `.input(z.object({...}))` for type safety\n- **The `commonRouter`** handles cross-cutting concerns (error reporting, telemetry events, surveys, URL opening) — do not duplicate these in view-specific routers\n","# Webview tRPC Messaging\n\nType-safe RPC communication between the VS Code extension host (server) and React webviews (client) using tRPC.\n\n## Architecture Overview\n\n```\nReact Webview (client)                    Extension Host (server)\n─────────────────────                     ──────────────────────\nuseTrpcClient() hook                      WebviewController\n  └─ createTRPCClient                       └─ setupTrpc()\n       └─ vscodeLink ──── postMessage ────►     ├─ callerFactory(appRouter)\n            (send\u002FonReceive)              ◄─────┤   └─ procedure(input)\n                                                └─ abort\u002Fsubscription.stop\n```\n\n**Key files** (read as needed for implementation details):\n\n| File                                                     | Purpose                                                                                                                                                       |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `@microsoft\u002Fvscode-ext-webview` (shared)                 | tRPC init via `initWebviewTrpc`, `publicProcedure`, `router`, `BaseRouterContext`                                                                             |\n| `@microsoft\u002Fvscode-ext-webview\u002Fhost` (telemetry)         | `telemetryMiddlewareBody`, `ProcedureLogger`, `TelemetryRunner` (consumer builds `publicProcedureWithTelemetry`)                                              |\n| `src\u002Fwebviews\u002F_integration\u002Ftrpc.ts`                      | Consumer tRPC instance: `publicProcedureWithTelemetry`, the DocumentDB `TelemetryRunner`, and the `RpcEnrichment` shape it contributes to `ctx.actionContext` |\n| `src\u002Fwebviews\u002F_integration\u002FappRouter.ts`                 | Root router + `publicProcedureWithTelemetry` wiring + DocumentDB `BaseRouterContext`                                                                          |\n| `src\u002Fwebviews\u002F_integration\u002Fconfiguration.ts`             | Consumer-owned knobs (telemetry namespace, bundle layout, dev-server host)                                                                                    |\n| `@microsoft\u002Fvscode-ext-webview\u002Fhost` (WebviewController) | `WebviewController` + `openWebview` factory: WebviewPanel lifecycle, tRPC dispatcher (queries, mutations, subscriptions, abort)                               |\n| `src\u002Fwebviews\u002F_integration\u002FopenAppWebview.ts`            | DocumentDB factory preset that pre-fills router + bundle layout (`openAppWebview`)                                                                            |\n| `src\u002Fwebviews\u002F_integration\u002FuseTrpcClient.ts`             | React hook providing the tRPC client (pre-typed against `AppRouter`)                                                                                          |\n| `@microsoft\u002Fvscode-ext-webview\u002Fwebview` (vscodeLink)     | Custom tRPC link bridging `postMessage` transport                                                                                                             |\n\n## Creating a New Router\n\nEach webview maintains its own router. Follow this pattern:\n\n### 1. Define the router context\n\nExtend `BaseRouterContext` with view-specific fields:\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002Fdocumentdb\u002FmyView\u002FmyViewRouter.ts\nimport { type BaseRouterContext } from '..\u002F..\u002F_integration\u002FappRouter';\n\nexport type RouterContext = BaseRouterContext & {\n  clusterId: string;\n  viewId: string;\n  databaseName: string;\n  \u002F\u002F add view-specific fields\n};\n```\n\n### 2. Define procedures\n\n```typescript\nimport { z } from 'zod';\nimport {\n  publicProcedure,\n  publicProcedureWithTelemetry,\n  router,\n  type WithTelemetry,\n} from '..\u002F..\u002F_integration\u002FappRouter';\nimport { type RouterContext } from '.\u002FmyViewRouter';\n\nexport const myViewRouter = router({\n  \u002F\u002F Query with telemetry (preferred for operations that touch external services)\n  getData: publicProcedureWithTelemetry.input(z.object({ id: z.string() })).query(async ({ input, ctx }) => {\n    const myCtx = ctx as WithTelemetry\u003CRouterContext>;\n    \u002F\u002F Instrumented procedure: myCtx.actionContext (the full IActionContext) is present.\n    myCtx.actionContext.telemetry.properties.itemId = input.id;\n    \u002F\u002F myCtx.signal is the AbortSignal for cancellation\n    return { data: 'result' };\n  }),\n\n  \u002F\u002F Mutation without telemetry (rare, use for fire-and-forget)\n  doAction: publicProcedure.input(z.string()).mutation(({ input }) => {\n    \u002F\u002F lightweight operation\n  }),\n});\n```\n\n### 3. Register in appRouter\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002F_integration\u002FappRouter.ts\nimport { myViewRouter } from '..\u002F..\u002Fdocumentdb\u002FmyView\u002FmyViewRouter';\n\nexport const appRouter = router({\n  common: commonRouter,\n  mongoClusters: {\n    documentView: documentViewRouter,\n    collectionView: collectionViewRouter,\n    myView: myViewRouter, \u002F\u002F \u003C-- add here\n  },\n});\n```\n\n### 4. Create the controller\n\nConstruction-only panels are opened with a factory function that builds the\nconfig + router context and calls the `openAppWebview` preset (which pre-fills\nthe app router, caller factory, and bundle layout):\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002Fdocumentdb\u002FmyView\u002FmyViewController.ts\nimport * as vscode from 'vscode';\nimport { API } from '..\u002F..\u002F..\u002FDocumentDBExperiences';\nimport { type AppWebviewController, openAppWebview } from '..\u002F..\u002F_integration\u002FopenAppWebview';\nimport { type RouterContext } from '.\u002FmyViewRouter';\n\nexport function openMyViewPanel(initialData: MyViewConfig): AppWebviewController\u003CMyViewConfig> {\n  const title = `${initialData.databaseName}`;\n\n  const trpcContext: RouterContext = {\n    dbExperience: API.DocumentDB,\n    webviewName: 'myView',\n    clusterId: initialData.clusterId,\n    viewId: initialData.viewId,\n    databaseName: initialData.databaseName,\n  };\n\n  return openAppWebview({\n    title,\n    webviewName: 'myView',\n    config: initialData,\n    context: trpcContext,\n  });\n}\n```\n\nThe returned `AppWebviewController` handle exposes `panel`, `onDisposed`,\n`revealToForeground`, `isDisposed`, and `dispose`. Genuinely stateful panels may\nstill extend `WebviewController` from `@microsoft\u002Fvscode-ext-webview\u002Fhost`\ndirectly instead of using the factory.\n\n> **Important:** The `webviewName` field passed to `openAppWebview` is the\n> **registry key** (`viewType`, must match a key in `WebviewRegistry`, e.g.\n> `collectionView`). The `webviewName` in the tRPC context is a **telemetry\n> label** used in telemetry event names. These may be the same string but serve\n> different purposes -- do not confuse them.\n\n### 5. Register in WebviewRegistry\n\nAdd your React component to the registry. The key must match the `webviewName`\npassed to `openAppWebview` (`viewType`). The `WebviewName` type (exported from\nthe same file) ensures compile-time validation of webview names.\n\n```typescript\n\u002F\u002F src\u002Fwebviews\u002F_integration\u002FWebviewRegistry.ts\nimport { MyView } from '..\u002F..\u002Fdocumentdb\u002FmyView\u002FMyView';\n\nexport const WebviewRegistry = {\n  collectionView: CollectionView,\n  documentView: DocumentView,\n  myViewName: MyView, \u002F\u002F \u003C-- add your entry\n} as const;\n\nexport type WebviewName = keyof typeof WebviewRegistry;\n```\n\n## Telemetry: `publicProcedure` vs `publicProcedureWithTelemetry`\n\n| Base                           | When to use                                                                  | `ctx.actionContext`                                      |\n| ------------------------------ | ---------------------------------------------------------------------------- | -------------------------------------------------------- |\n| `publicProcedure`              | Fire-and-forget, no external calls, telemetry reported separately            | absent (do not read it)                                  |\n| `publicProcedureWithTelemetry` | **Default choice.** Any procedure touching DB, network, or user-visible work | Guaranteed, injected by the DocumentDB `TelemetryRunner` |\n\n`publicProcedureWithTelemetry` is `publicProcedure.use(telemetryMiddlewareBody(documentDbTelemetryRunner, ...))` (built in `trpc.ts`). The framework's `telemetryMiddlewareBody` delegates to the DocumentDB `TelemetryRunner`, which wraps the call in `callWithTelemetryAndErrorHandling`, contributes the full `IActionContext` to `ctx.actionContext`, auto-generates a telemetry event named `documentDB.rpc.{type}.{path}`, and records errors, duration, and abort status.\n\n`actionContext` is **not** a field on the base `RouterContext` — it is an additive enrichment. Narrow to `WithTelemetry\u003CRouterContext>` (`= RouterContext & { actionContext }`) in an instrumented procedure to read it; a plain `publicProcedure` procedure narrows to bare `RouterContext`, so reading `actionContext` there is a compile error instead of a runtime `undefined`.\n\nAccess telemetry safely:\n\n```typescript\nconst myCtx = ctx as WithTelemetry\u003CRouterContext>;\nmyCtx.actionContext.telemetry.properties.myCustomProp = 'value';\nmyCtx.actionContext.telemetry.measurements.itemCount = items.length;\n```\n\n## AbortSignal Support\n\nEvery tRPC operation (query, mutation, subscription) receives its own `AbortController`. Cancellation flows:\n\n```\nClient (React)                              Server (Extension Host)\n──────────────                              ──────────────────────\n\u002F\u002F Queries\u002FMutations:\nac = new AbortController()\ntrpcClient.myProc.query(input,\n  { signal: ac.signal })\nac.abort()  →  sends 'abort' msg  →  abortController.abort()\n                                        → ctx.signal.aborted = true\n\n\u002F\u002F Subscriptions:\nsub = trpcClient.mySub.subscribe(...)\nsub.unsubscribe()  →  'subscription.stop'  →  abortController.abort()\n```\n\n### Using abort in procedures\n\n```typescript\n\u002F\u002F Pass signal to APIs that accept it (MongoDB driver, fetch, etc.)\ngetData: publicProcedureWithTelemetry\n    .input(z.object({ filter: z.record(z.unknown()) }))\n    .query(async ({ input, ctx }) => {\n        const myCtx = ctx as RouterContext;\n\n        \u002F\u002F Option 1: Pass to driver (preferred)\n        const cursor = collection.find(input.filter, { signal: myCtx.signal });\n\n        \u002F\u002F Option 2: Manual check in loops\n        for (const item of items) {\n            if (myCtx.signal?.aborted) return;\n            await processItem(item);\n        }\n    }),\n```\n\n### Client-side abort\n\n```tsx\nconst trpcClient = useTrpcClient();\nconst abortControllerRef = useRef\u003CAbortController>();\n\nconst runQuery = async () => {\n  abortControllerRef.current?.abort(); \u002F\u002F cancel previous\n  const ac = new AbortController();\n  abortControllerRef.current = ac;\n\n  const result = await trpcClient.mongoClusters.collectionView.myQuery.query(input, { signal: ac.signal });\n};\n```\n\nWhen `publicProcedureWithTelemetry` detects an aborted signal, the DocumentDB `TelemetryRunner` sets `telemetry.properties.aborted = 'true'` and `result = 'Canceled'` automatically.\n\n## Subscriptions\n\nSubscriptions stream multiple values from server to client using async generators:\n\n```typescript\n\u002F\u002F Server (router)\nstreamData: publicProcedureWithTelemetry\n    .input(z.object({ batchSize: z.number() }))\n    .subscription(async function* ({ input, ctx }) {\n        const myCtx = ctx as RouterContext;\n\n        for (let i = 0; i \u003C total; i += input.batchSize) {\n            if (myCtx.signal?.aborted) return; \u002F\u002F check before each yield\n            const batch = await fetchBatch(i, input.batchSize);\n            yield batch;\n        }\n    }),\n\n\u002F\u002F Client (React)\nconst sub = trpcClient.mongoClusters.myView.streamData.subscribe(\n    { batchSize: 100 },\n    {\n        onData(batch) { \u002F* handle each batch *\u002F },\n        onComplete() { \u002F* all done *\u002F },\n        onError(err) { \u002F* handle error *\u002F },\n    },\n);\n\n\u002F\u002F To stop:\nsub.unsubscribe();\n```\n\n## Client-Side Hook Usage\n\n```tsx\nimport { useTrpcClient } from '..\u002F_integration\u002FuseTrpcClient';\nimport { useConfiguration } from '@microsoft\u002Fvscode-ext-webview\u002Freact';\n\nexport const MyComponent = () => {\n  const trpcClient = useTrpcClient();\n  const config = useConfiguration\u003CMyViewConfig>();\n\n  useEffect(() => {\n    trpcClient.mongoClusters.myView.getData.query({ id: config.documentId }).then(setData);\n  }, []);\n};\n```\n\n`useConfiguration\u003CT>()` retrieves the initial config passed to `WebviewController` constructor (serialized via `encodeURIComponent(JSON.stringify(...))`).\n\n## Common Pitfalls\n\n- **Never use `any`** in procedure context casts — narrow with `ctx as WithTelemetry\u003CRouterContext>` when the procedure reads telemetry (`ctx.actionContext.telemetry`), or `ctx as RouterContext` otherwise\n- **Always prefer `publicProcedureWithTelemetry`** unless you have a specific reason not to\n- **Always check `myCtx.signal?.aborted`** in long-running loops — not checking causes wasted work after client cancels\n- **Do not mutate the shared `context` object** — `WebviewController` clones it per-operation already, but router code should treat `ctx` as read-only\n- **Input validation uses `zod`** — always define `.input(z.object({...}))` for type safety\n- **The `commonRouter`** handles cross-cutting concerns (error reporting, telemetry events, surveys, URL opening) — do not duplicate these in view-specific routers\n",{"name":5,"description":7},"main","e15fa6c12e64ad9483e1211805956043ce91720d",".github\u002Fskills\u002Fwebview-trpc-messaging",22,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-14 05:21:32",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add microsoft\u002Fvscode-documentdb --skill webview-trpc-messaging",{"tool":59,"command":60},"bunx","bunx skills add microsoft\u002Fvscode-documentdb --skill webview-trpc-messaging",{"tool":62,"command":63},"pnpm","pnpm dlx skills add microsoft\u002Fvscode-documentdb --skill webview-trpc-messaging","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fwebview-trpc-messaging\u002FSKILL.md",true,[],[68,105,137,165,184,204],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":84,"publishedAt":39},"01M69BSZH60F9JW3N87ZBSKTW8","writing-release-notes","Generates release notes and changelog entries for the DocumentDB extension for Visual Studio Code, and announces releases on GitHub Discussions. Use when preparing version releases, creating patch update notes, writing changelog entries, documenting new features and fixes, or posting a release or patch release announcement to Discussions. Handles both major\u002Fminor versions (new file) and patch updates (append to existing file).","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fwriting-release-notes\u002FSKILL.md",[74,79],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[85,86,87,88,89,90,91,92,93,35,23,24,94,95,96,97,98,99,100,101,102,103,104],"notes","release","release notes","writing","writing release notes","github","update","visual studio code","visual studio","updates","studio","patch","changelog","announcement","release announcement","releases","features","append","posting","fixes",{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":117,"publishedAt":39},"01M69BSV4V52VBDF3RZ3E6Y61T","ux-pr-review","Prepare and run an agent-assisted UX \u002F workflow review of a pull request. Use when the operator says \"I'm about to work on a UX PR review, please prepare\", \"prepare a UX review for PR","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fux-pr-review\u002FSKILL.md",[111,114],{"categoryNo":112,"categoryName":113,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":115,"categoryName":116,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000032","设计师",[118,119,120,121,122,123,124,125,126,127,128,129,130,32,24,131,132,133,134,135,136],"ux pr review","pr","pr review","review","ux","pull request","prepare","workflow","run","pull","ux review","for","work on","workflow review","work","please","request","about","operator",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":143,"publishedAt":39},"01M69BSRV752CSC1NBMFWDCKB5","tree-cluster-architecture","Patterns for cluster nodes and tree data providers in vscode-documentdb. Use when adding or changing a cluster tree item, discovery provider, Connections\u002FAzure\u002FDiscovery tree hierarchy, tree\u002Flist mode, cluster identity, treeId\u002FclusterId lookup, Collection View import\u002Fexport resolution, reveal behavior, credentials, shell actions, or copy connection string support.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftree-cluster-architecture\u002FSKILL.md",[],[144,145,146,147,148,149,150,151,152,153,24,154,155,156,157,158,159,160,161,162,163,164],"tree cluster architecture","architecture","tree","cluster","azure","support","vscode","identity","actions","mode","collection","hierarchy","credentials","reveal","export","data","list","patterns","Providers","STRING","import",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":39},"01M69BSPMFSH3FAR4R5TGDB468","telemetry-instrumentation","Instrumentation patterns for telemetry in the vscode-documentdb extension. Use when adding telemetry to a feature, enriching existing telemetry with properties or measurements, linking related events with correlation IDs, instrumenting commands or tree views, suppressing noisy events, or deciding what data points to capture. Also use when asked to \"add telemetry\" or \"improve telemetry\" for any feature.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftelemetry-instrumentation\u002FSKILL.md",[],[172,36,173,150,174,175,24,176,177,159,161,178,179,146,180,129,32,181,182,183],"telemetry instrumentation","instrumentation","ids","improve","linking","commands","add","events","feature","Existing","with","properties",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":191,"publishedAt":39},"01M69BSMRAT64W6F3F15FVQ9QK","skill-creator","Guide for creating effective GitHub Copilot skills (.github\u002Fskills\u002F) in this repository. Use when creating a new skill, updating an existing skill, or when asked about skill structure, format, or best practices for the vscode-documentdb project.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fskill-creator\u002FSKILL.md",[190],{"categoryNo":112,"categoryName":113,"taxonomyType":77,"taxonomyTypeDesc":78},[192,193,194,90,195,150,196,129,197,23,32,181,24,198,199,200,135,201,202,203],"skill creator","creator","github copilot","best practices","copilot","project","structure","github copilot skills","copilot skills","format","repository","new skill",{"skillNo":205,"name":206,"nameZh":6,"description":207,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":208,"categories":209,"keywords":210,"publishedAt":39},"01M69BSJZA3VPPP4XMY7G2QA6V","review-external-pr","Triage an external contributor's PR and prepare it for merge. Detects push permission, base branch, draft state, size, and history quality, then picks one of three paths (review-only, direct push to contributor branch, or a `reviews\u002F` staging branch). Use when triaging or reviewing a community PR, deciding whether maintainer changes are needed, choosing a merge strategy (squash\u002Fmerge\u002Frebase), or merging an external contribution.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Freview-external-pr\u002FSKILL.md",[],[119,211,121,212,124,213,214,215,216,217,218,24,219,220,221,222,223,224,225,226,129,227],"review external pr","triage","merge","community","reviews","branch","only","contributor","paths","permission","rebase","direct","push","history","three","squash","strategy"]