{
  "$schema": "https://hyperjs.ai/schema/registry-item.json",
  "name": "subscribe",
  "version": "0.1.0",
  "description": "route.subscribe() primitive — projects to SSE, MCP resource notifications, tRPC subscriptions.",
  "readme": "# @hyper/subscribe\n\n`subscribe()` primitive — projects to SSE, MCP resource notifications, tRPC subscriptions.\n\n## Install\n\nComponents are installed as source into your repo, not pulled from npm:\n\n```bash\nbunx hyper add subscribe\n```\n\nWires the alias `@hyper/subscribe` to `src/hyper/subscribe/` (configurable in `hyper.config.json`). See [hyperjs.ai](https://hyperjs.ai) for the full registry.\n\n## Usage\n\n```ts\nimport { Hyper } from \"@hyper/core\"\nimport { subscribe } from \"@hyper/subscribe\"\n\nexport default new Hyper()\n  .use(\n    subscribe(\"/events\", async function* () {\n      yield { data: { type: \"tick\", at: Date.now() } }\n    }),\n  )\n  .listen(3000)\n```\n\n## Docs\n\nSee the [main README](../../README.md) and [docs/](../../docs) for guides and integration recipes.\n\n## License\n\nMIT\n",
  "registryDeps": [
    "core"
  ],
  "peerDeps": {},
  "optionalPeerDeps": {},
  "files": [
    {
      "path": "subscribe/index.ts",
      "contents": "/**\n * @hyper/subscribe — `route.subscribe()` primitive.\n *\n * A single subscription definition projects to:\n *   - HTTP: Server-Sent Events on GET <path>\n *   - MCP:  resource notifications (via resources/subscribe)\n *   - tRPC: subscription procedure\n *\n * The user writes an async generator producing events; we serialize to\n * each protocol. This v0 ships the HTTP→SSE projection; the MCP + tRPC\n * adapters read the same iterator factory so the shape stays uniform.\n */\n\nimport { route, sse } from \"@hyper/core\"\nimport type { CallableRoute, RouteMeta } from \"@hyper/core\"\n\nexport interface SubscribeEvent<T = unknown> {\n  readonly event?: string\n  readonly data: T\n  readonly id?: string\n}\n\nexport type SubscribeHandler<T> = (args: {\n  req: Request\n  signal: AbortSignal\n}) => AsyncIterable<SubscribeEvent<T>>\n\nexport interface SubscribeOptions {\n  readonly name?: string\n  readonly description?: string\n  readonly meta?: RouteMeta\n}\n\n/**\n * Build a subscription route. Returns a `CallableRoute` so callers can\n * call it in tests or via MCP/tRPC without a server.\n */\nexport function subscribe<T>(\n  path: string,\n  handler: SubscribeHandler<T>,\n  opts: SubscribeOptions = {},\n): CallableRoute {\n  const meta: RouteMeta = {\n    ...(opts.name && { name: opts.name }),\n    ...(opts.description && { description: opts.description }),\n    ...(opts.meta ?? {}),\n    subscription: true,\n  }\n  return route\n    .get(path)\n    .meta(meta)\n    .handle(async (c) => {\n      const controller = new AbortController()\n      const signal = controller.signal\n      c.req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true })\n      const source = handler({ req: c.req, signal })\n      const stringified = (async function* () {\n        for await (const ev of source) {\n          yield {\n            data: typeof ev.data === \"string\" ? ev.data : JSON.stringify(ev.data),\n            ...(ev.event && { event: ev.event }),\n            ...(ev.id && { id: ev.id }),\n          }\n        }\n      })()\n      return sse(stringified, { signal })\n    })\n}\n\n/**\n * Collect a finite number of events from a subscription — useful for\n * MCP `resources/read` snapshots and for tests.\n */\nexport async function collect<T>(\n  handler: SubscribeHandler<T>,\n  n: number,\n  req: Request = new Request(\"http://local/\"),\n): Promise<readonly SubscribeEvent<T>[]> {\n  const out: SubscribeEvent<T>[] = []\n  const ctrl = new AbortController()\n  for await (const ev of handler({ req, signal: ctrl.signal })) {\n    out.push(ev)\n    if (out.length >= n) break\n  }\n  ctrl.abort()\n  return out\n}\n",
      "sha256": "e096aff51c6d3f5b1cd6fc94964b2b77fc101b4ea0f2c7f4e484acd1cb665783"
    }
  ],
  "subpaths": {}
}