@vielzeug/codex 3.1.1 → 26.10.1

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 (63) hide show
  1. package/README.md +1 -1
  2. package/data/catalog.json +166 -84
  3. package/data/llms-full.txt +3492 -1583
  4. package/data/llms.txt +9 -7
  5. package/data/manifest.json +1 -1
  6. package/data/packages/arsenal.json +40 -16
  7. package/data/packages/assay.json +12 -20
  8. package/data/packages/clockwork.json +31 -28
  9. package/data/packages/codex.json +5 -5
  10. package/data/packages/coins.json +4 -4
  11. package/data/packages/conduit.json +33 -34
  12. package/data/packages/courier.json +18 -17
  13. package/data/packages/dnd.json +11 -11
  14. package/data/packages/familiar.json +27 -6
  15. package/data/packages/flux.json +11 -8
  16. package/data/packages/focus.json +17 -7
  17. package/data/packages/forge.json +4 -4
  18. package/data/packages/gesture.json +11 -5
  19. package/data/packages/herald.json +16 -17
  20. package/data/packages/illusionist.json +5 -5
  21. package/data/packages/keymap.json +16 -12
  22. package/data/packages/ledger.json +26 -26
  23. package/data/packages/lingua.json +4 -4
  24. package/data/packages/mesh.json +30 -32
  25. package/data/packages/necromancer.json +7 -6
  26. package/data/packages/orbit.json +11 -10
  27. package/data/packages/ore.json +6 -5
  28. package/data/packages/postmaster.json +26 -26
  29. package/data/packages/prism.json +50 -42
  30. package/data/packages/pulse.json +34 -34
  31. package/data/packages/refine.json +7 -5
  32. package/data/packages/ripple.json +4 -4
  33. package/data/packages/rune.json +9 -9
  34. package/data/packages/sandbox.json +4 -4
  35. package/data/packages/scout.json +17 -11
  36. package/data/packages/scroll.json +14 -14
  37. package/data/packages/sentinel.json +26 -15
  38. package/data/packages/sigil.json +6 -6
  39. package/data/packages/sourcerer.json +4 -4
  40. package/data/packages/spell.json +31 -36
  41. package/data/packages/tandem.json +29 -0
  42. package/data/packages/tavern.json +23 -0
  43. package/data/packages/tempo.json +45 -43
  44. package/data/packages/vault.json +15 -25
  45. package/data/packages/ward.json +27 -30
  46. package/data/packages/wayfinder.json +34 -27
  47. package/data/refine.json +987 -134
  48. package/data/search.json +341 -306
  49. package/dist/http.js +2 -2
  50. package/dist/http.js.map +1 -1
  51. package/dist/index.d.ts +3 -4
  52. package/dist/index.js +3 -4
  53. package/dist/index.js.map +1 -1
  54. package/dist/refine.d.ts +1 -1
  55. package/dist/refine.js +1 -1
  56. package/dist/server.d.ts +1 -1
  57. package/dist/server.js +1 -1
  58. package/dist/tools/refine.js +1 -1
  59. package/package.json +5 -12
  60. package/skills/vielzeug/SKILL.md +57 -55
  61. package/dist/advanced.d.ts +0 -12
  62. package/dist/advanced.js +0 -13
  63. package/dist/advanced.js.map +0 -1
@@ -1,25 +1,25 @@
1
1
  {
2
- "apiSource": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseRoomTimeoutError,\n PulseTimeoutError,\n} from './errors';\nexport { createPulse } from './pulse';\nexport type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';\n",
2
+ "apiSource": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseRoomTimeoutError,\n PulseTimeoutError,\n} from './errors';\nexport { createPulse } from './pulse';\nexport type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';\n",
3
3
  "docs": {
4
- "index": "---\ntitle: Pulse — Typed WebSocket sessions\ndescription: Explicitly connected, typed WebSocket sessions with scoped channels, ref-counted rooms with reactive presence, reconnect restoration, and heartbeat.\npackage: pulse\ncategory: websockets\nkeywords: [websocket, realtime, channels, presence, rooms, reconnect, heartbeat, typed-messaging, ripple]\nrelated: [herald, ripple, courier, clockwork]\nexports:\n [\n createPulse,\n Pulse,\n PulseChannel,\n RoomScope,\n RoomScopeBase,\n PresenceRoomScope,\n ExternalStore,\n PulseOptions,\n PulseSchema,\n ChannelDefinition,\n ChannelDefinitions,\n RoomDefinition,\n RoomDefinitions,\n RoomOptions,\n OutgoingMessage,\n OutgoingTransform,\n PulseError,\n PulseConnectionError,\n PulseTimeoutError,\n PulseRoomTimeoutError,\n PulseAbortError,\n PulseDisposedError,\n PulseProtocolError,\n PulseEvent,\n ]\nenvironments: [browser, node]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"pulse\" />\n\n## Why Pulse?\n\nNative WebSocket leaves connection ownership, event routing, reconnect restoration, and cleanup to each application. Pulse provides those boundaries while making readiness explicit: applications connect before sending, and disconnected messages never disappear silently.\n\n```ts\n// Before\nconst socket = new WebSocket('wss://api.example.com/ws');\nsocket.addEventListener('message', (event) => route(JSON.parse(event.data)));\nsocket.addEventListener('close', () => setTimeout(() => reconnect(), 1_000));\n\n// After\nconst pulse = createPulse<{ server: { 'chat:message': { text: string } }; client: { 'chat:send': { text: string } } }>(\n 'wss://api.example.com/ws',\n { reconnect: true },\n);\ntry {\n await pulse.connect();\n pulse.on('chat:message', (message) => console.log(message.text));\n pulse.send('chat:send', { text: 'Hello!' });\n} catch (error) {\n console.error('Pulse connection failed:', error);\n}\n```\n\n| Feature | Pulse | Native WebSocket | socket.io-client |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"pulse\" type=\"size\" /> | 0 B | ~44 kB gzip |\n| Explicit readiness | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Session restoration | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Protocol-specific |\n| Typed scoped channels | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Basic |\n| Typed rooms with presence | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Zero runtime dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Pulse when** you need a typed WebSocket session whose reconnect and cleanup behavior must be deterministic.\n\n**Consider native WebSocket when** a single untyped connection does not need retry, routing, or session restoration.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/pulse\n```\n\n```sh [npm]\nnpm install @vielzeug/pulse\n```\n\n```sh [yarn]\nyarn add @vielzeug/pulse\n```\n\n:::\n\n## Quick Start\n\nDefine the protocol schema at construction time, create scopes, then connect before sending.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype Schema = {\n server: { 'chat:message': { text: string } };\n client: { 'chat:send': { text: string } };\n channels: {\n chat: {\n client: { send: { text: string } };\n server: { message: { text: string } };\n };\n };\n rooms: {\n lobby: { presence: { name: string } };\n };\n};\n\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: true,\n});\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\nconst chat = pulse.channel('chat');\nconst lobby = pulse.room('lobby');\n\ntry {\n await pulse.connect();\n chat.send('send', { text: 'Hello!' });\n await lobby.joined;\n lobby.updatePresence({ name: 'Ada' });\n} catch (error) {\n console.error('Pulse connection failed:', error);\n}\n\npulse.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- **`connect()`** — explicit readiness; application messages throw while disconnected.\n- **`channel()`** — named, schema-bound scopes with independent disposal and reference-counted server subscriptions.\n- **`room()`** — named, schema-bound ref-counted room scopes with optional reactive presence. The first scope sends `join`; the last disposal sends `leave`.\n- **`reconnect`** — ordered restoration of channel subscriptions, room memberships, and local presence state.\n- **`transform`** — one synchronous transform or filter for application messages.\n- **`tap()`** — subscribe to lifecycle events (status changes, errors, disposal) via a typed `PulseEvent` stream.\n- **`heartbeat`** — ping/pong liveness detection that uses the same reconnect controller.\n- **`status` and `rooms`** — framework-neutral external stores for transport and confirmed membership state.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](/ripple/) — bridges Pulse external stores into computed values and effects.\n- [Herald](/herald/) — receives routed Pulse events in an in-process application bus.\n- [Courier](/courier/) — handles request/response traffic alongside a Pulse session.\n- [Clockwork](/clockwork/) — models application-level authentication or session workflows.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
- "api": "---\ntitle: API — Pulse\ndescription: Complete API reference for Pulse, including schema types, options, scopes, and error classes.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createPulse()` | Create a typed WebSocket session instance. | Sync (returns `Pulse`) | Does not open the connection — call `connect()`. |\n| `Pulse` | Main instance: channels, rooms, messaging, lifecycle. | Sync methods, async `connect()`/`wait()` | `send()` throws while disconnected. |\n| `PulseChannel` | Scoped channel namespace with independent disposal. | Sync methods, async `wait()` | Each call returns a new scope; ref-counted subscription. |\n| `RoomScope` | Ref-counted room membership with optional presence. | Sync methods, async `joined` | `joined` rejects on transport close or timeout. |\n| `PulseSchema` | Declares server/client events, channels, and rooms. | Type-only | Infer all named scope types from this schema. |\n| `PulseOptions` | Configuration: heartbeat, reconnect, transform. | Type-only | `reconnect` and `heartbeat` default to `false`. |\n| `ExternalStore` | Framework-neutral snapshot and change subscription. | Sync | `subscribe()` does not emit the initial snapshot. |\n| `PulseError` | Base class for all Pulse errors. | Runtime | Check `instanceof` against subclasses. |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/pulse` | All public exports: `createPulse`, types, and error classes. |\n\n## `createPulse()`\n\n```ts\nfunction createPulse<S extends PulseSchema = PulseSchema>(url: string, options?: PulseOptions): Pulse<S>\n```\n\nCreates a Pulse instance. The WebSocket is not opened until `connect()` is called.\n\n### Type parameters\n\n| Parameter | Constraint | Description |\n| --- | --- | --- |\n| `S` | `PulseSchema` | Schema declaring server events, client events, channels, and rooms. |\n\n### Parameters\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `url` | `string` | WebSocket URL. |\n| `options` | `PulseOptions` | Optional configuration. |\n\n### Returns\n\n`Pulse<S>` — the Pulse instance.\n\n---\n\n## `PulseSchema`\n\n```ts\ntype PulseSchema = {\n server?: MessageMap;\n client?: MessageMap;\n channels?: ChannelDefinitions;\n rooms?: RoomDefinitions;\n};\n```\n\nDeclare all protocol surfaces once at construction. Named scopes infer their types from this schema.\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `server` | `MessageMap` | Root events the server sends. |\n| `client` | `MessageMap` | Root events the client sends. |\n| `channels` | `ChannelDefinitions` | Named channel schemas. |\n| `rooms` | `RoomDefinitions` | Named room schemas with optional presence. |\n\n---\n\n## `PulseOptions`\n\n```ts\ntype PulseOptions = {\n heartbeat?: boolean | HeartbeatOptions;\n protocols?: string | string[];\n reconnect?: boolean | ReconnectOptions;\n transform?: OutgoingTransform;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `heartbeat` | `boolean \\| HeartbeatOptions` | `false` | Ping/pong keep-alive. |\n| `protocols` | `string \\| string[]` | — | Sub-protocols passed to the WebSocket constructor. |\n| `reconnect` | `boolean \\| ReconnectOptions` | `false` | Auto-reconnect on unexpected close. |\n| `transform` | `OutgoingTransform` | — | Transform or filter outgoing application messages. |\n\n---\n\n## `HeartbeatOptions`\n\n```ts\ntype HeartbeatOptions = {\n interval?: number;\n timeout?: number;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `interval` | `number` | `30_000` | Interval between pings in ms. |\n| `timeout` | `number` | `5_000` | How long to wait for a pong before treating the connection as dead. |\n\n---\n\n## `ReconnectOptions`\n\n```ts\ntype ReconnectOptions = {\n delay?: number | ((attempt: number) => number);\n maxAttempts?: number;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `delay` | `number \\| ((attempt: number) => number)` | Full-jitter exponential backoff capped at 30 s | Delay between reconnect attempts in ms. `attempt` is zero-based. |\n| `maxAttempts` | `number` | `5` | Maximum number of reconnect attempts after initial failure. |\n\n---\n\n## `OutgoingMessage`\n\n```ts\ntype OutgoingMessage = { channel?: string; event: string; payload: unknown };\n```\n\nAn outgoing application message before it is serialized.\n\n---\n\n## `OutgoingTransform`\n\n```ts\ntype OutgoingTransform = (message: Readonly<OutgoingMessage>) => OutgoingMessage | null;\n```\n\nTransform or filter outgoing application messages. Internal protocol frames (subscribe, join, leave, presence, ping) bypass this hook. Return `null` to drop the message.\n\n---\n\n## `Pulse`\n\n```ts\ntype Pulse<S extends PulseSchema = PulseSchema> = {\n // Channels\n channel<K extends keyof ChannelMap<S> & string>(\n name: K,\n ): PulseChannel<ChannelMap<S>[K]['server'], ChannelMap<S>[K]['client']>;\n\n // Connection\n connect(): Promise<void>;\n disconnect(code?: number, reason?: string): void;\n\n // Lifecycle\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n\n // Messaging\n on<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe;\n once<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe;\n send<K extends EventKey<ClientEvents<S>>>(event: K, payload: ClientEvents<S>[K]): void;\n wait<K extends EventKey<ServerEvents<S>>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<ServerEvents<S>[K]>;\n\n // Rooms\n room<K extends keyof RoomMap<S> & string>(name: K, opts?: RoomOptions): RoomScope<RoomMap<S>[K]>;\n readonly rooms: ExternalStore<ReadonlySet<string>>;\n\n // Status\n readonly status: ExternalStore<PulseStatus>;\n\n // Tap\n tap(handler: (event: PulseEvent) => void, options?: { signal?: AbortSignal }): () => void;\n\n [Symbol.dispose](): void;\n};\n```\n\n### `channel(name)`\n\nCreates an isolated message namespace over the shared connection. Each call returns an independently disposable scope. The server subscription is reference-counted.\n\n### `connect()`\n\nExplicitly opens the connection. Resolves after session restoration completes. Rejects if the connection closes before opening.\n\n### `disconnect(code?, reason?)`\n\nCloses the connection without triggering reconnection. Default code is `1000`.\n\n### `dispose()`\n\nPermanently closes the connection and releases all resources. Idempotent.\n\n### `on(event, handler)`\n\nSubscribes to a typed server event. Returns an unsubscribe function.\n\n### `once(event, handler)`\n\nSubscribes once — auto-removes after first invocation.\n\n### `send(event, payload)`\n\nSends a typed event to the server. Throws `PulseConnectionError` unless the connection is open.\n\n### `wait(event, opts?)`\n\nResolves on the next emission of the given server event. Rejects when `opts.signal` aborts, the timeout elapses, or the instance is disposed.\n\n### `room(name, opts?)`\n\nCreates a ref-counted room scope. The first scope sends `join`; the last disposal sends `leave`. When the room definition includes `presence`, the scope exposes reactive presence state.\n\n### `rooms`\n\nReactive set of rooms the client is currently a confirmed member of.\n\n### `status`\n\nReactive connection status: `'connecting' | 'open' | 'reconnecting' | 'closed'`.\n\n### `tap(handler, options?)`\n\nSubscribes to lifecycle events emitted by the Pulse instance. The handler receives a discriminated-union `PulseEvent`. Returns an unsubscribe function.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `handler` | `(event: PulseEvent) => void` | Called for each lifecycle event. |\n| `options.signal` | `AbortSignal` | Optional signal to stop the subscription. |\n\n```ts\nconst pulse = createPulse(url, { reconnect: true });\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\n```\n\n---\n\n## `ExternalStore`\n\n```ts\ninterface ExternalStore<T> {\n getSnapshot(): T;\n subscribe(listener: () => void): Unsubscribe;\n}\n```\n\nA framework-neutral state source used by `pulse.status`, `pulse.rooms`, and room `presence`. `getSnapshot()` returns the current immutable snapshot. `subscribe()` notifies once for each distinct snapshot and returns an unsubscribe function; listener errors are reported through `pulse.tap()` without interrupting Pulse state transitions.\n\n---\n\n## `PulseEvent`\n\n```ts\ntype PulseEvent =\n | { type: 'status-change'; status: PulseStatus }\n | { type: 'error'; error: PulseError }\n | { type: 'dispose' };\n```\n\nA discriminated union of lifecycle events emitted by a `Pulse` instance. Inspect `event.type` to narrow the payload.\n\n| `type` | Payload | When |\n| --- | --- | --- |\n| `status-change` | `status: PulseStatus` | The connection status transitions. |\n| `error` | `error: PulseError` | A typed transport or protocol error occurs. |\n| `dispose` | — | The instance is disposed. |\n\n---\n\n## `PulseChannel`\n\n```ts\ntype PulseChannel<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap> = {\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n dispose(): void;\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe;\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe;\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void;\n wait<K extends EventKey<TServer>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<TServer[K]>;\n [Symbol.dispose](): void;\n};\n```\n\n---\n\n## `RoomScope`\n\n```ts\ntype RoomScope<R extends RoomDefinition = RoomDefinition> = R extends { presence: infer P }\n ? P extends undefined\n ? RoomScopeBase\n : PresenceRoomScope<P>\n : RoomScopeBase;\n```\n\nA room scope. When the room definition includes `presence`, the scope is a `PresenceRoomScope`; otherwise it is a `RoomScopeBase`.\n\n### `RoomScopeBase`\n\n```ts\ntype RoomScopeBase = {\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n readonly joined: Promise<void>;\n dispose(): void;\n [Symbol.dispose](): void;\n};\n```\n\n### `PresenceRoomScope`\n\n```ts\ntype PresenceRoomScope<T = unknown> = RoomScopeBase & {\n readonly presence: ExternalStore<ReadonlyMap<string, T>>;\n updatePresence(state: T): void;\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe;\n onLeave(handler: (memberId: string) => void): Unsubscribe;\n};\n```\n\n| Member | Type | Description |\n| --- | --- | --- |\n| `presence` | `ExternalStore<ReadonlyMap<string, T>>` | Reactive map of `memberId → state`. |\n| `updatePresence(state)` | `(state: T) => void` | Broadcast this client's presence state. Throws `PulseConnectionError` unless open. |\n| `onJoin(handler)` | `(handler) => Unsubscribe` | Called whenever a new member joins with their initial state. |\n| `onLeave(handler)` | `(handler) => Unsubscribe` | Called whenever a member leaves. |\n\n### `RoomOptions`\n\n```ts\ntype RoomOptions = {\n signal?: AbortSignal;\n timeout?: number;\n};\n```\n\n| Option | Type | Description |\n| --- | --- | --- |\n| `signal` | `AbortSignal` | Aborts the join, rejecting `joined` with `PulseAbortError`. |\n| `timeout` | `number` | Join timeout in ms. Rejects `joined` with `PulseRoomTimeoutError`. |\n\n---\n\n## Errors\n\nAll errors extend `PulseError`.\n\n### `PulseError`\n\nBase class for all Pulse errors.\n\n### `PulseConnectionError`\n\nTransport failure, send while disconnected, or room join rejected on close.\n\n### `PulseProtocolError`\n\nMalformed frame or server error frame.\n\n### `PulseTimeoutError`\n\n`wait()` timed out before the server event arrived.\n\n### `PulseRoomTimeoutError`\n\nRoom scope `joined` timed out before the server confirmed membership.\n\n### `PulseAbortError`\n\n`wait()` or room `joined` aborted via AbortSignal.\n\n### `PulseDisposedError`\n\nOperation attempted after disposal.\n\n---\n\n## Channel and room definitions\n\n### `ChannelDefinition`\n\n```ts\ntype ChannelDefinition = { client: MessageMap; server: MessageMap };\n```\n\n### `ChannelDefinitions`\n\n```ts\ntype ChannelDefinitions = Record<string, ChannelDefinition>;\n```\n\n### `RoomDefinition`\n\n```ts\ntype RoomDefinition = { presence?: unknown };\n```\n\n### `RoomDefinitions`\n\n```ts\ntype RoomDefinitions = Record<string, RoomDefinition>;\n```\n\n---\n\n## Utility types\n\n### `MessageMap`\n\n```ts\ntype MessageMap = Record<string, unknown>;\n```\n\n### `EventKey`\n\n```ts\ntype EventKey<T extends MessageMap> = keyof T & string;\n```\n\n### `ServerEvents`\n\n```ts\ntype ServerEvents<S extends PulseSchema> = S extends { server: infer M extends MessageMap } ? M : MessageMap;\n```\n\nExtract server events from a schema, defaulting to an empty map.\n\n### `ClientEvents`\n\n```ts\ntype ClientEvents<S extends PulseSchema> = S extends { client: infer M extends MessageMap } ? M : MessageMap;\n```\n\nExtract client events from a schema, defaulting to an empty map.\n\n### `RoomMap`\n\n```ts\ntype RoomMap<S extends PulseSchema> = S extends { rooms: infer R extends RoomDefinitions } ? R : RoomDefinitions;\n```\n\nExtract room definitions from a schema, defaulting to an empty map.\n\n### `Unsubscribe`\n\n```ts\ntype Unsubscribe = () => void;\n```\n\n### `PulseStatus`\n\n```ts\ntype PulseStatus = 'connecting' | 'open' | 'reconnecting' | 'closed';\n```\n",
6
- "usage": "---\ntitle: Usage — Pulse\ndescription: Practical guide for connecting, sending, subscribing, joining rooms, and managing lifecycle with Pulse.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n[[toc]]\n\n## Basic Usage\n\nDeclare server events, client events, channel schemas, and room schemas once at construction. Named scopes infer their types from this schema.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype Schema = {\n // Root events the server sends\n server: { 'chat:message': { text: string }; notice: string };\n // Root events the client sends\n client: { 'chat:send': { text: string } };\n // Named channel scopes\n channels: {\n chat: {\n client: { send: { text: string } };\n server: { message: { text: string } };\n };\n alerts: {\n client: { subscribe: { topic: string } };\n server: { alert: { topic: string; severity: 'info' | 'warn' | 'error' } };\n };\n };\n // Named room scopes with optional presence state\n rooms: {\n lobby: { presence: { name: string; color: string } };\n announcements: {};\n };\n};\n```\n\n## Create and connect\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: { delay: 1_000, maxAttempts: 5 },\n heartbeat: { interval: 30_000, timeout: 5_000 },\n});\n\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\n\ntry {\n await pulse.connect();\n} catch (error) {\n console.error('Connection failed:', error);\n}\n```\n\n`connect()` opens the WebSocket and resolves after session restoration completes. `send()` throws `PulseConnectionError` while disconnected — Pulse never silently drops or buffers application messages.\n\n## Send and receive root events\n\n```ts\npulse.on('chat:message', (message) => console.log(message.text));\npulse.send('chat:send', { text: 'Hello!' });\n```\n\n## Channels\n\nEach `channel()` call returns an independently disposable scope. The server subscription is reference-counted: the first scope sends `subscribe`, the last disposal sends `unsubscribe`.\n\n```ts\nconst chat = pulse.channel('chat');\n\nchat.on('message', (message) => console.log(message.text));\nchat.send('send', { text: 'Hello!' });\n\n// Later\nchat.dispose();\n```\n\nUse `using` for automatic cleanup:\n\n```ts\n{\n using chat = pulse.channel('chat');\n chat.on('message', (message) => console.log(message.text));\n} // chat.dispose() called automatically\n```\n\n## Rooms and presence\n\nEach `room()` call returns a ref-counted room scope. The first scope sends `join`; the last disposal sends `leave`. When the room definition includes `presence`, the scope exposes reactive presence state.\n\n```ts\nconst lobby = pulse.room('lobby');\n\n// joined resolves when the server confirms membership\nawait lobby.joined;\n\n// Reactive presence map: memberId → state\nlobby.onJoin((memberId, state) => console.log(`${memberId} joined: ${state.name}`));\nlobby.onLeave((memberId) => console.log(`${memberId} left`));\n\n// Broadcast your presence\nlobby.updatePresence({ name: 'Ada', color: 'blue' });\n\n// Read current presence\nfor (const [memberId, state] of lobby.presence.getSnapshot()) {\n console.log(`${memberId}: ${state.name}`);\n}\n\n// Leave\nlobby.dispose();\n```\n\nPlain rooms (without presence) work the same way but don't expose presence members:\n\n```ts\nconst announcements = pulse.room('announcements');\nawait announcements.joined;\nannouncements.dispose();\n```\n\n### Room scope options\n\n```ts\n// Timeout if the server doesn't confirm in time\nconst lobby = pulse.room('lobby', { timeout: 5_000 });\ntry {\n await lobby.joined;\n} catch (error) {\n console.error('Join failed:', error);\n}\n\n// Abort via AbortSignal\nconst ctrl = new AbortController();\nconst lobby = pulse.room('lobby', { signal: ctrl.signal });\nctrl.abort(); // joined rejects with PulseAbortError, scope auto-disposes\n```\n\n### Observe the rooms set\n\n`pulse.rooms` is an external store that tracks confirmed room memberships:\n\n```ts\nconst printRooms = () => console.log('Joined rooms:', [...pulse.rooms.getSnapshot()]);\nconst unsubscribe = pulse.rooms.subscribe(printRooms);\n\nprintRooms();\n// Later\nunsubscribe();\n```\n\n## Reconnect\n\nWhen the connection drops unexpectedly, Pulse reconnects using the configured strategy. On reconnect, it restores:\n\n1. Channel subscriptions (sends `subscribe` for each active channel).\n2. Room memberships (sends `join` for each active room scope).\n3. Local presence state (sends `presence` with the last successfully published state).\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: {\n delay: (attempt) => Math.min(1_000 * 2 ** attempt, 30_000),\n maxAttempts: 5,\n },\n});\n```\n\n`joined` rejects on transport close. For post-reconnect membership, read `pulse.rooms` instead.\n\n## Heartbeat\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n heartbeat: { interval: 30_000, timeout: 5_000 },\n});\n```\n\nPulse sends periodic pings. If a pong doesn't arrive before the timeout, it forces a reconnect using the same reconnect controller.\n\n## Transform outgoing messages\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n transform: (message) => {\n // Add a timestamp to all messages\n return { ...message, payload: { ...message.payload, ts: Date.now() } };\n },\n});\n```\n\nReturn `null` to drop a message:\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n transform: (message) => (message.event === 'debug' ? null : message),\n});\n```\n\n## Wait for a specific event\n\n```ts\nconst notice = await pulse.wait('notice', { timeout: 10_000 });\nconsole.log(notice);\n```\n\n## Dispose\n\n```ts\npulse.dispose();\n```\n\nDisposal is idempotent. It closes the connection, rejects pending room joins, clears all listeners, and aborts all scope disposal signals.\n\n## Error handling\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: true,\n});\n\npulse.tap((event) => {\n if (event.type === 'error') {\n if (event.error instanceof PulseConnectionError) {\n console.error('Connection error:', event.error);\n } else if (event.error instanceof PulseProtocolError) {\n console.error('Protocol error:', event.error);\n }\n }\n});\n```\n\n| Error | When |\n| --- | --- |\n| `PulseConnectionError` | Transport failure, send while disconnected, room join rejected on close. |\n| `PulseProtocolError` | Malformed frame or server error frame. |\n| `PulseTimeoutError` | `wait()` times out. |\n| `PulseRoomTimeoutError` | Room scope `joined` times out. |\n| `PulseAbortError` | `wait()` or room `joined` aborted via AbortSignal. |\n| `PulseDisposedError` | Operation attempted after disposal. |\n\n## Working with Other Vielzeug Libraries\n\nPulse external stores plug directly into Flux and Ripple bridges:\n\n```ts\nimport { fromStore } from '@vielzeug/flux';\nimport { effect, fromSubscribable } from '@vielzeug/ripple';\n\nconst statusStream = fromStore(pulse.status);\nconst presence = fromSubscribable(lobby.presence, { signal: lobby.disposalSignal });\nconst presenceEffect = effect(() => console.log([...presence.value]));\n\nconst statusSubscription = statusStream.subscribe((status) => console.log(status));\n// Later\nstatusSubscription.unsubscribe();\npresenceEffect.dispose();\n```\n\n## Best Practices\n\n- Await `connect()` before sending; never assume construction opens the transport.\n- Define the full schema at `createPulse()` so named scopes are type-safe without per-call generics.\n- Use `using` declarations for channel and room scopes so disposal is automatic at block exit.\n- Always call `dispose()` when done — it closes the connection, rejects pending joins, and clears listeners.\n- Call `tap()` to observe lifecycle events; Pulse reports transport and protocol errors there rather than throwing asynchronously.\n- Read `pulse.rooms` for post-reconnect membership; `joined` rejects on transport close.\n- Set a `timeout` on room scopes when the server may never confirm membership.\n- Keep `transform` synchronous; resolve async policy decisions before calling `send()`.\n",
7
- "examples": "---\ntitle: Examples — Pulse\ndescription: Practical examples for common Pulse usage patterns.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n- [Basic Connection](./examples/basic-connection.md)\n- [Channel Multiplexing](./examples/channels.md)\n- [Outgoing Transform](./examples/middleware.md)\n- [Reconnect and Heartbeat](./examples/reconnect-and-heartbeat.md)\n- [Rooms and Presence](./examples/rooms-and-presence.md)\n"
4
+ "index": "---\ntitle: 'Pulse: Typed WebSocket sessions'\ndescription: Explicitly connected, typed WebSocket sessions with scoped channels, ref-counted rooms with reactive presence, reconnect restoration, and heartbeat.\npackage: pulse\ncategory: websockets\nkeywords: [websocket, realtime, channels, presence, rooms, reconnect, heartbeat, typed-messaging, ripple]\nrelated: [herald, ripple, courier, clockwork]\nexports:\n [\n createPulse,\n Pulse,\n PulseChannel,\n RoomScope,\n RoomScopeBase,\n PresenceRoomScope,\n Subscribable,\n PulseOptions,\n PulseSchema,\n ChannelDefinition,\n ChannelDefinitions,\n RoomDefinition,\n RoomDefinitions,\n RoomOptions,\n OutgoingMessage,\n OutgoingTransform,\n PulseError,\n PulseConnectionError,\n PulseTimeoutError,\n PulseRoomTimeoutError,\n PulseAbortError,\n PulseDisposedError,\n PulseProtocolError,\n PulseEvent,\n ]\nenvironments: [browser, node]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"pulse\" />\n\n## Why Pulse?\n\nNative WebSocket leaves connection ownership, event routing, reconnect restoration, and cleanup to each application. Pulse provides those boundaries while making readiness explicit: applications connect before sending, and disconnected messages never disappear silently.\n\n```ts\n// Before\nconst socket = new WebSocket('wss://api.example.com/ws');\nsocket.addEventListener('message', (event) => route(JSON.parse(event.data)));\nsocket.addEventListener('close', () => setTimeout(() => reconnect(), 1_000));\n\n// After\nconst pulse = createPulse<{ server: { 'chat:message': { text: string } }; client: { 'chat:send': { text: string } } }>(\n 'wss://api.example.com/ws',\n { reconnect: true },\n);\ntry {\n await pulse.connect();\n pulse.on('chat:message', (message) => console.log(message.text));\n pulse.send('chat:send', { text: 'Hello!' });\n} catch (error) {\n console.error('Pulse connection failed:', error);\n}\n```\n\n| Feature | Pulse | Native WebSocket | socket.io-client |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"pulse\" type=\"size\" /> | 0 B | ~44 kB gzip |\n| Explicit readiness | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Manual | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Session restoration | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Protocol-specific |\n| Typed scoped channels | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Basic |\n| Typed rooms with presence | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Zero runtime dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Pulse when** you need a typed WebSocket session whose reconnect and cleanup behavior must be deterministic.\n\n**Consider native WebSocket when** a single untyped connection does not need retry, routing, or session restoration.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/pulse\n```\n\n```sh [npm]\nnpm install @vielzeug/pulse\n```\n\n```sh [yarn]\nyarn add @vielzeug/pulse\n```\n\n:::\n\n## Quick Start\n\nDefine the protocol schema at construction time, create scopes, then connect before sending.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype Schema = {\n server: { 'chat:message': { text: string } };\n client: { 'chat:send': { text: string } };\n channels: {\n chat: {\n client: { send: { text: string } };\n server: { message: { text: string } };\n };\n };\n rooms: {\n lobby: { presence: { name: string } };\n };\n};\n\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: true,\n});\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\nconst chat = pulse.channel('chat');\nconst lobby = pulse.room('lobby');\n\ntry {\n await pulse.connect();\n chat.send('send', { text: 'Hello!' });\n await lobby.joined;\n lobby.updatePresence({ name: 'Ada' });\n} catch (error) {\n console.error('Pulse connection failed:', error);\n}\n\npulse.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- **`connect()`**: explicit readiness; application messages throw while disconnected.\n- **`channel()`**: named, schema-bound scopes with independent disposal and reference-counted server subscriptions.\n- **`room()`**: named, schema-bound ref-counted room scopes with optional reactive presence. The first scope sends `join`; the last disposal sends `leave`.\n- **`reconnect`**: ordered restoration of channel subscriptions, room memberships, and local presence state.\n- **`transform`**: one synchronous transform or filter for application messages.\n- **`tap()`**: subscribe to lifecycle events (status changes, errors, disposal) via a typed `PulseEvent` stream.\n- **`heartbeat`**: ping/pong liveness detection that uses the same reconnect controller.\n- **`status` and `rooms`**: framework-neutral external stores for transport and confirmed membership state.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](/ripple/): bridges Pulse external stores into computed values and effects.\n- [Herald](/herald/): receives routed Pulse events in an in-process application bus.\n- [Courier](/courier/): handles request/response traffic alongside a Pulse session.\n- [Clockwork](/clockwork/): models application-level authentication or session workflows.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
+ "api": "---\ntitle: 'API: Pulse'\ndescription: Complete API reference for Pulse, including schema types, options, scopes, and error classes.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createPulse()` | Create a typed WebSocket session instance. | Sync (returns `Pulse`) | Does not open the connection: call `connect()`. |\n| `Pulse` | Main instance: channels, rooms, messaging, lifecycle. | Sync methods, async `connect()`/`wait()` | `send()` throws while disconnected. |\n| `PulseChannel` | Scoped channel namespace with independent disposal. | Sync methods, async `wait()` | Each call returns a new scope; ref-counted subscription. |\n| `RoomScope` | Ref-counted room membership with optional presence. | Sync methods, async `joined` | `joined` rejects on transport close or timeout. |\n| `PulseSchema` | Declares server/client events, channels, and rooms. | Type-only | Infer all named scope types from this schema. |\n| `PulseOptions` | Configuration: heartbeat, reconnect, transform. | Type-only | `reconnect` and `heartbeat` default to `false`. |\n| `Subscribable` | Framework-neutral snapshot and change subscription. | Sync | `subscribe()` does not emit the initial snapshot. |\n| `PulseError` | Base class for all Pulse errors. | Runtime | Check `instanceof` against subclasses. |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/pulse` | All public exports: `createPulse`, types, and error classes. |\n| `@vielzeug/pulse/testing` | Wire-protocol-accurate `MockWebSocket` for tests and demos. |\n\n## `createPulse()`\n\n```ts\nfunction createPulse<S extends PulseSchema = PulseSchema>(url: string, options?: PulseOptions): Pulse<S>\n```\n\nCreates a Pulse instance. The WebSocket is not opened until `connect()` is called.\n\n### Type parameters\n\n| Parameter | Constraint | Description |\n| --- | --- | --- |\n| `S` | `PulseSchema` | Schema declaring server events, client events, channels, and rooms. |\n\n### Parameters\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `url` | `string` | WebSocket URL. |\n| `options` | `PulseOptions` | Optional configuration. |\n\n### Returns\n\n`Pulse<S>`: the Pulse instance.\n\n---\n\n## `PulseSchema`\n\n```ts\ntype PulseSchema = {\n server?: MessageMap;\n client?: MessageMap;\n channels?: ChannelDefinitions;\n rooms?: RoomDefinitions;\n};\n```\n\nDeclare all protocol surfaces once at construction. Named scopes infer their types from this schema.\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `server` | `MessageMap` | Root events the server sends. |\n| `client` | `MessageMap` | Root events the client sends. |\n| `channels` | `ChannelDefinitions` | Named channel schemas. |\n| `rooms` | `RoomDefinitions` | Named room schemas with optional presence. |\n\n---\n\n## `PulseOptions`\n\n```ts\ntype PulseOptions = {\n heartbeat?: boolean | HeartbeatOptions;\n protocols?: string | string[];\n reconnect?: boolean | ReconnectOptions;\n transform?: OutgoingTransform;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `heartbeat` | `boolean \\| HeartbeatOptions` | `false` | Ping/pong keep-alive. |\n| `protocols` | `string \\| string[]` | N/A | Sub-protocols passed to the WebSocket constructor. |\n| `reconnect` | `boolean \\| ReconnectOptions` | `false` | Auto-reconnect on unexpected close. |\n| `transform` | `OutgoingTransform` | N/A | Transform or filter outgoing application messages. |\n\n---\n\n## `HeartbeatOptions`\n\n```ts\ntype HeartbeatOptions = {\n interval?: number;\n timeout?: number;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `interval` | `number` | `30_000` | Interval between pings in ms. |\n| `timeout` | `number` | `5_000` | How long to wait for a pong before treating the connection as dead. |\n\n---\n\n## `ReconnectOptions`\n\n```ts\ntype ReconnectOptions = {\n delay?: number | ((attempt: number) => number);\n maxAttempts?: number;\n};\n```\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `delay` | `number \\| ((attempt: number) => number)` | Full-jitter exponential backoff capped at 30 s | Delay between reconnect attempts in ms. `attempt` is zero-based. |\n| `maxAttempts` | `number` | `5` | Maximum number of reconnect attempts after initial failure. |\n\n---\n\n## `OutgoingMessage`\n\n```ts\ntype OutgoingMessage = { channel?: string; event: string; payload: unknown };\n```\n\nAn outgoing application message before it is serialized.\n\n---\n\n## `OutgoingTransform`\n\n```ts\ntype OutgoingTransform = (message: Readonly<OutgoingMessage>) => OutgoingMessage | null;\n```\n\nTransform or filter outgoing application messages. Internal protocol frames (subscribe, join, leave, presence, ping) bypass this hook. Return `null` to drop the message.\n\n---\n\n## `Pulse`\n\n```ts\ntype Pulse<S extends PulseSchema = PulseSchema> = {\n // Channels\n channel<K extends keyof ChannelMap<S> & string>(\n name: K,\n ): PulseChannel<ChannelMap<S>[K]['server'], ChannelMap<S>[K]['client']>;\n\n // Connection\n connect(): Promise<void>;\n disconnect(code?: number, reason?: string): void;\n\n // Lifecycle\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n\n // Messaging\n on<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe;\n once<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe;\n send<K extends EventKey<ClientEvents<S>>>(event: K, payload: ClientEvents<S>[K]): void;\n wait<K extends EventKey<ServerEvents<S>>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<ServerEvents<S>[K]>;\n\n // Rooms\n room<K extends keyof RoomMap<S> & string>(name: K, opts?: RoomOptions): RoomScope<RoomMap<S>[K]>;\n readonly rooms: Subscribable<ReadonlySet<string>>;\n\n // Status\n readonly status: Subscribable<PulseStatus>;\n\n // Tap\n tap(handler: (event: PulseEvent) => void, options?: { signal?: AbortSignal }): () => void;\n\n [Symbol.dispose](): void;\n};\n```\n\n### `channel(name)`\n\nCreates an isolated message namespace over the shared connection. Each call returns an independently disposable scope. The server subscription is reference-counted.\n\n### `connect()`\n\nExplicitly opens the connection. Resolves after session restoration completes. Rejects if the connection closes before opening.\n\n### `disconnect(code?, reason?)`\n\nCloses the connection without triggering reconnection. Default code is `1000`.\n\n### `dispose()`\n\nPermanently closes the connection and releases all resources. Idempotent.\n\n### `on(event, handler)`\n\nSubscribes to a typed server event. Returns an unsubscribe function.\n\n### `once(event, handler)`\n\nSubscribes once: auto-removes after first invocation.\n\n### `send(event, payload)`\n\nSends a typed event to the server. Throws `PulseConnectionError` unless the connection is open.\n\n### `wait(event, opts?)`\n\nResolves on the next emission of the given server event. Rejects when `opts.signal` aborts, the timeout elapses, or the instance is disposed.\n\n### `room(name, opts?)`\n\nCreates a ref-counted room scope. The first scope sends `join`; the last disposal sends `leave`. When the room definition includes `presence`, the scope exposes reactive presence state.\n\n### `rooms`\n\nReactive set of rooms the client is currently a confirmed member of.\n\n### `status`\n\nReactive connection status: `'connecting' | 'open' | 'reconnecting' | 'closed'`.\n\n### `tap(handler, options?)`\n\nSubscribes to lifecycle events emitted by the Pulse instance. The handler receives a discriminated-union `PulseEvent`. Returns an unsubscribe function.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `handler` | `(event: PulseEvent) => void` | Called for each lifecycle event. |\n| `options.signal` | `AbortSignal` | Optional signal to stop the subscription. |\n\n```ts\nconst pulse = createPulse(url, { reconnect: true });\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\n```\n\n---\n\n## `Subscribable`\n\n```ts\ninterface Subscribable<T> {\n getSnapshot(): T;\n subscribe(listener: () => void): Unsubscribe;\n}\n```\n\nA framework-neutral state source used by `pulse.status`, `pulse.rooms`, and room `presence`. `getSnapshot()` returns the current immutable snapshot. `subscribe()` notifies once for each distinct snapshot and returns an unsubscribe function; listener errors are reported through `pulse.tap()` without interrupting Pulse state transitions.\n\n---\n\n## `PulseEvent`\n\n```ts\ntype PulseEvent =\n | { type: 'status-change'; status: PulseStatus }\n | { type: 'error'; error: PulseError }\n | { type: 'dispose' };\n```\n\nA discriminated union of lifecycle events emitted by a `Pulse` instance. Inspect `event.type` to narrow the payload.\n\n| `type` | Payload | When |\n| --- | --- | --- |\n| `status-change` | `status: PulseStatus` | The connection status transitions. |\n| `error` | `error: PulseError` | A typed transport or protocol error occurs. |\n| `dispose` | N/A | The instance is disposed. |\n\n---\n\n## `PulseChannel`\n\n```ts\ntype PulseChannel<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap> = {\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n dispose(): void;\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe;\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe;\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void;\n wait<K extends EventKey<TServer>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<TServer[K]>;\n [Symbol.dispose](): void;\n};\n```\n\n---\n\n## `RoomScope`\n\n```ts\ntype RoomScope<R extends RoomDefinition = RoomDefinition> = R extends { presence: infer P }\n ? P extends undefined\n ? RoomScopeBase\n : PresenceRoomScope<P>\n : RoomScopeBase;\n```\n\nA room scope. When the room definition includes `presence`, the scope is a `PresenceRoomScope`; otherwise it is a `RoomScopeBase`.\n\n### `RoomScopeBase`\n\n```ts\ntype RoomScopeBase = {\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n readonly joined: Promise<void>;\n dispose(): void;\n [Symbol.dispose](): void;\n};\n```\n\n### `PresenceRoomScope`\n\n```ts\ntype PresenceRoomScope<T = unknown> = RoomScopeBase & {\n readonly presence: Subscribable<ReadonlyMap<string, T>>;\n updatePresence(state: T): void;\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe;\n onLeave(handler: (memberId: string) => void): Unsubscribe;\n};\n```\n\n| Member | Type | Description |\n| --- | --- | --- |\n| `presence` | `Subscribable<ReadonlyMap<string, T>>` | Reactive map of `memberId → state`. |\n| `updatePresence(state)` | `(state: T) => void` | Broadcast this client's presence state. Throws `PulseConnectionError` unless open. |\n| `onJoin(handler)` | `(handler) => Unsubscribe` | Called whenever a new member joins with their initial state. |\n| `onLeave(handler)` | `(handler) => Unsubscribe` | Called whenever a member leaves. |\n\n### `RoomOptions`\n\n```ts\ntype RoomOptions = {\n signal?: AbortSignal;\n timeout?: number;\n};\n```\n\n| Option | Type | Description |\n| --- | --- | --- |\n| `signal` | `AbortSignal` | Aborts the join, rejecting `joined` with `PulseAbortError`. |\n| `timeout` | `number` | Join timeout in ms. Rejects `joined` with `PulseRoomTimeoutError`. |\n\n---\n\n## Errors\n\nAll errors extend `PulseError`.\n\n### `PulseError`\n\nBase class for all Pulse errors.\n\n### `PulseConnectionError`\n\nTransport failure, send while disconnected, or room join rejected on close.\n\n### `PulseProtocolError`\n\nMalformed frame or server error frame.\n\n### `PulseTimeoutError`\n\n`wait()` timed out before the server event arrived.\n\n### `PulseRoomTimeoutError`\n\nRoom scope `joined` timed out before the server confirmed membership.\n\n### `PulseAbortError`\n\n`wait()` or room `joined` aborted via AbortSignal.\n\n### `PulseDisposedError`\n\nOperation attempted after disposal.\n\n---\n\n## Channel and room definitions\n\n### `ChannelDefinition`\n\n```ts\ntype ChannelDefinition = { client: MessageMap; server: MessageMap };\n```\n\n### `ChannelDefinitions`\n\n```ts\ntype ChannelDefinitions = Record<string, ChannelDefinition>;\n```\n\n### `RoomDefinition`\n\n```ts\ntype RoomDefinition = { presence?: unknown };\n```\n\n### `RoomDefinitions`\n\n```ts\ntype RoomDefinitions = Record<string, RoomDefinition>;\n```\n\n---\n\n## Utility types\n\n### `MessageMap`\n\n```ts\ntype MessageMap = Record<string, unknown>;\n```\n\n### `EventKey`\n\n```ts\ntype EventKey<T extends MessageMap> = keyof T & string;\n```\n\n### `ServerEvents`\n\n```ts\ntype ServerEvents<S extends PulseSchema> = S extends { server: infer M extends MessageMap } ? M : MessageMap;\n```\n\nExtract server events from a schema, defaulting to an empty map.\n\n### `ClientEvents`\n\n```ts\ntype ClientEvents<S extends PulseSchema> = S extends { client: infer M extends MessageMap } ? M : MessageMap;\n```\n\nExtract client events from a schema, defaulting to an empty map.\n\n### `RoomMap`\n\n```ts\ntype RoomMap<S extends PulseSchema> = S extends { rooms: infer R extends RoomDefinitions } ? R : RoomDefinitions;\n```\n\nExtract room definitions from a schema, defaulting to an empty map.\n\n### `Unsubscribe`\n\n```ts\ntype Unsubscribe = () => void;\n```\n\n### `PulseStatus`\n\n```ts\ntype PulseStatus = 'connecting' | 'open' | 'reconnecting' | 'closed';\n```\n\n---\n\n## Testing APIs\n\nImport from `@vielzeug/pulse/testing`.\n\n### `MockWebSocket`\n\n```ts\nimport { MockWebSocket } from '@vielzeug/pulse/testing';\n\n// Tests: drive the socket pulse created\nconst socket = MockWebSocket.instances.at(-1)!;\nsocket.open();\nsocket.receive({ type: 'joined', room: 'crm' });\n\n// Demos: subclass and install as the global\nclass ScriptedWebSocket extends MockWebSocket {\n constructor(url: string, protocols?: string | string[]) {\n super(url, protocols, { autoOpen: true });\n setTimeout(() => this.receive({ room: 'crm', type: 'joined' }), 60);\n }\n}\n(globalThis as Record<string, unknown>).WebSocket = ScriptedWebSocket;\n```\n\nA wire-protocol-accurate `WebSocket` stand-in: pulse dials it through `new WebSocket(url, protocols)`, tests drive it through `open()`, `receive()`, `drop()`, `error()`, and `close()`, and every outbound frame lands in `sentMessages` for `frames()` to decode. Instances register on the static `MockWebSocket.instances` array in construction order, so a test can grab the socket pulse just created. `MockWebSocket.deferClose = true` parks closes in `CLOSING` until `finishClose()` runs, for testing reconnect timing. Requires a browser-like environment for the `CloseEvent`/`MessageEvent` globals.\n\n### `frames(socket)`\n\nDecodes every frame the socket sent, in order.\n\n```ts\nimport { frames, MockWebSocket } from '@vielzeug/pulse/testing';\n\nexpect(frames(MockWebSocket.instances[0]!)).toContainEqual({ room: 'lobby', type: 'join' });\n```\n",
6
+ "usage": "---\ntitle: 'Usage: Pulse'\ndescription: Practical guide for connecting, sending, subscribing, joining rooms, and managing lifecycle with Pulse.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n[[toc]]\n\n## Basic Usage\n\nDeclare server events, client events, channel schemas, and room schemas once at construction. Named scopes infer their types from this schema.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype Schema = {\n // Root events the server sends\n server: { 'chat:message': { text: string }; notice: string };\n // Root events the client sends\n client: { 'chat:send': { text: string } };\n // Named channel scopes\n channels: {\n chat: {\n client: { send: { text: string } };\n server: { message: { text: string } };\n };\n alerts: {\n client: { subscribe: { topic: string } };\n server: { alert: { topic: string; severity: 'info' | 'warn' | 'error' } };\n };\n };\n // Named room scopes with optional presence state\n rooms: {\n lobby: { presence: { name: string; color: string } };\n announcements: {};\n };\n};\n```\n\n## Create and connect\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: { delay: 1_000, maxAttempts: 5 },\n heartbeat: { interval: 30_000, timeout: 5_000 },\n});\n\npulse.tap((event) => {\n if (event.type === 'error') console.error(event.error);\n if (event.type === 'status-change') console.log('status:', event.status);\n});\n\ntry {\n await pulse.connect();\n} catch (error) {\n console.error('Connection failed:', error);\n}\n```\n\n`connect()` opens the WebSocket and resolves after session restoration completes. `send()` throws `PulseConnectionError` while disconnected: Pulse never silently drops or buffers application messages.\n\n## Send and receive root events\n\n```ts\npulse.on('chat:message', (message) => console.log(message.text));\npulse.send('chat:send', { text: 'Hello!' });\n```\n\n## Channels\n\nEach `channel()` call returns an independently disposable scope. The server subscription is reference-counted: the first scope sends `subscribe`, the last disposal sends `unsubscribe`.\n\n```ts\nconst chat = pulse.channel('chat');\n\nchat.on('message', (message) => console.log(message.text));\nchat.send('send', { text: 'Hello!' });\n\n// Later\nchat.dispose();\n```\n\nUse `using` for automatic cleanup:\n\n```ts\n{\n using chat = pulse.channel('chat');\n chat.on('message', (message) => console.log(message.text));\n} // chat.dispose() called automatically\n```\n\n## Rooms and presence\n\nEach `room()` call returns a ref-counted room scope. The first scope sends `join`; the last disposal sends `leave`. When the room definition includes `presence`, the scope exposes reactive presence state.\n\n```ts\nconst lobby = pulse.room('lobby');\n\n// joined resolves when the server confirms membership\nawait lobby.joined;\n\n// Reactive presence map: memberId → state\nlobby.onJoin((memberId, state) => console.log(`${memberId} joined: ${state.name}`));\nlobby.onLeave((memberId) => console.log(`${memberId} left`));\n\n// Broadcast your presence\nlobby.updatePresence({ name: 'Ada', color: 'blue' });\n\n// Read current presence\nfor (const [memberId, state] of lobby.presence.getSnapshot()) {\n console.log(`${memberId}: ${state.name}`);\n}\n\n// Leave\nlobby.dispose();\n```\n\nPlain rooms (without presence) work the same way but don't expose presence members:\n\n```ts\nconst announcements = pulse.room('announcements');\nawait announcements.joined;\nannouncements.dispose();\n```\n\n### Room scope options\n\n```ts\n// Timeout if the server doesn't confirm in time\nconst lobby = pulse.room('lobby', { timeout: 5_000 });\ntry {\n await lobby.joined;\n} catch (error) {\n console.error('Join failed:', error);\n}\n\n// Abort via AbortSignal\nconst ctrl = new AbortController();\nconst lobby = pulse.room('lobby', { signal: ctrl.signal });\nctrl.abort(); // joined rejects with PulseAbortError, scope auto-disposes\n```\n\n### Observe the rooms set\n\n`pulse.rooms` is an external store that tracks confirmed room memberships:\n\n```ts\nconst printRooms = () => console.log('Joined rooms:', [...pulse.rooms.getSnapshot()]);\nconst unsubscribe = pulse.rooms.subscribe(printRooms);\n\nprintRooms();\n// Later\nunsubscribe();\n```\n\n## Reconnect\n\nWhen the connection drops unexpectedly, Pulse reconnects using the configured strategy. On reconnect, it restores:\n\n1. Channel subscriptions (sends `subscribe` for each active channel).\n2. Room memberships (sends `join` for each active room scope).\n3. Local presence state (sends `presence` with the last successfully published state).\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: {\n delay: (attempt) => Math.min(1_000 * 2 ** attempt, 30_000),\n maxAttempts: 5,\n },\n});\n```\n\n`joined` rejects on transport close. For post-reconnect membership, read `pulse.rooms` instead.\n\n## Heartbeat\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n heartbeat: { interval: 30_000, timeout: 5_000 },\n});\n```\n\nPulse sends periodic pings. If a pong doesn't arrive before the timeout, it forces a reconnect using the same reconnect controller.\n\n## Transform outgoing messages\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n transform: (message) => {\n // Add a timestamp to all messages\n return { ...message, payload: { ...message.payload, ts: Date.now() } };\n },\n});\n```\n\nReturn `null` to drop a message:\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n transform: (message) => (message.event === 'debug' ? null : message),\n});\n```\n\n## Wait for a specific event\n\n```ts\nconst notice = await pulse.wait('notice', { timeout: 10_000 });\nconsole.log(notice);\n```\n\n## Dispose\n\n```ts\npulse.dispose();\n```\n\nDisposal is idempotent. It closes the connection, rejects pending room joins, clears all listeners, and aborts all scope disposal signals.\n\n## Error handling\n\n```ts\nconst pulse = createPulse<Schema>('wss://api.example.com/ws', {\n reconnect: true,\n});\n\npulse.tap((event) => {\n if (event.type === 'error') {\n if (event.error instanceof PulseConnectionError) {\n console.error('Connection error:', event.error);\n } else if (event.error instanceof PulseProtocolError) {\n console.error('Protocol error:', event.error);\n }\n }\n});\n```\n\n| Error | When |\n| --- | --- |\n| `PulseConnectionError` | Transport failure, send while disconnected, room join rejected on close. |\n| `PulseProtocolError` | Malformed frame or server error frame. |\n| `PulseTimeoutError` | `wait()` times out. |\n| `PulseRoomTimeoutError` | Room scope `joined` times out. |\n| `PulseAbortError` | `wait()` or room `joined` aborted via AbortSignal. |\n| `PulseDisposedError` | Operation attempted after disposal. |\n\n## Working with Other Vielzeug Libraries\n\nPulse external stores plug directly into Flux and Ripple bridges:\n\n```ts\nimport { fromStore } from '@vielzeug/flux';\nimport { effect, fromSubscribable } from '@vielzeug/ripple';\n\nconst statusStream = fromStore(pulse.status);\nconst presence = fromSubscribable(lobby.presence, { signal: lobby.disposalSignal });\nconst presenceEffect = effect(() => console.log([...presence.value]));\n\nconst statusSubscription = statusStream.subscribe((status) => console.log(status));\n// Later\nstatusSubscription.unsubscribe();\npresenceEffect.dispose();\n```\n\n## Best Practices\n\n- Await `connect()` before sending; never assume construction opens the transport.\n- Define the full schema at `createPulse()` so named scopes are type-safe without per-call generics.\n- Use `using` declarations for channel and room scopes so disposal is automatic at block exit.\n- Always call `dispose()` when done: it closes the connection, rejects pending joins, and clears listeners.\n- Call `tap()` to observe lifecycle events; Pulse reports transport and protocol errors there rather than throwing asynchronously.\n- Read `pulse.rooms` for post-reconnect membership; `joined` rejects on transport close.\n- Set a `timeout` on room scopes when the server may never confirm membership.\n- Keep `transform` synchronous; resolve async policy decisions before calling `send()`.\n",
7
+ "examples": "---\ntitle: 'Examples: Pulse'\ndescription: Practical examples for common Pulse usage patterns.\npackage: pulse\ncategory: websockets\n---\n\n<!-- markdownlint-disable MD025 -->\n\n- [Basic Connection](./examples/basic-connection.md)\n- [Channel Multiplexing](./examples/channels.md)\n- [Outgoing Transform](./examples/middleware.md)\n- [Reconnect and Heartbeat](./examples/reconnect-and-heartbeat.md)\n- [Rooms and Presence](./examples/rooms-and-presence.md)\n"
8
8
  },
9
9
  "examples": [
10
10
  {
11
11
  "id": "channels",
12
- "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Isolated channel namespace — listeners and sends are scoped to 'chat'\nconst pulse = createPulse('wss://api.example.com/ws')\nconst chat = pulse.channel('chat')\n\n// Listeners scoped to the channel\nchat.on('message', ({ from, text }) => {\n console.log('[chat] ' + from + ': ' + text)\n})\n\ntry {\n await pulse.connect()\n // Send scoped to the channel\n chat.send('send', { text: 'hey!' })\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// Wait with a per-event timeout\ntry {\n const msg = await chat.wait('message', { timeout: 3_000 })\n console.log('got:', msg.text)\n} catch (err) {\n console.log('channel wait timed out:', err.message)\n}\n\n// Disposing the channel removes all its listeners\n// but the underlying pulse connection stays open\nchat.dispose()\nconsole.log('channel disposed, pulse still open:', pulse.status.getSnapshot())\n\npulse.dispose()",
12
+ "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Isolated channel namespace: listeners and sends are scoped to 'chat'\nconst pulse = createPulse('wss://api.example.com/ws')\nconst chat = pulse.channel('chat')\n\n// Listeners scoped to the channel\nchat.on('message', ({ from, text }) => {\n console.log('[chat] ' + from + ': ' + text)\n})\n\ntry {\n await pulse.connect()\n // Send scoped to the channel\n chat.send('send', { text: 'hey!' })\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// Wait with a per-event timeout\ntry {\n const msg = await chat.wait('message', { timeout: 3_000 })\n console.log('got:', msg.text)\n} catch (err) {\n console.log('channel wait timed out:', err.message)\n}\n\n// Disposing the channel removes all its listeners\n// but the underlying pulse connection stays open\nchat.dispose()\nconsole.log('channel disposed, pulse still open:', pulse.status.getSnapshot())\n\npulse.dispose()",
13
13
  "name": "Typed Channels"
14
14
  },
15
15
  {
16
16
  "id": "connect-and-send",
17
- "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Typed WebSocket client: on(), once(), send(), wait()\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5 },\n})\n\n// Observe runtime events via tap()\npulse.tap((event) => {\n if (event.type === 'error') console.log('transport error:', event.error.message)\n})\n\n// Subscribe before connecting — listeners are synchronous\nconst unsub = pulse.on('chat:message', ({ from, text }) => {\n console.log('[' + from + '] ' + text)\n})\n\n// One-shot listener: fires once and auto-removes\npulse.once('chat:message', (msg) => {\n console.log('first message:', msg.text)\n})\n\n// Connect; send when open\ntry {\n await pulse.connect()\n pulse.send('chat:send', { text: 'Hello, world!' })\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// Await next server event with a 5 s deadline\ntry {\n const msg = await pulse.wait('chat:message', { timeout: 500 })\n console.log('received:', msg.text)\n} catch (err) {\n console.log('wait ended:', err.message)\n}\n\nunsub()\npulse.dispose()",
17
+ "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Typed WebSocket client: on(), once(), send(), wait()\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5 },\n})\n\n// Observe runtime events via tap()\npulse.tap((event) => {\n if (event.type === 'error') console.log('transport error:', event.error.message)\n})\n\n// Subscribe before connecting: listeners are synchronous\nconst unsub = pulse.on('chat:message', ({ from, text }) => {\n console.log('[' + from + '] ' + text)\n})\n\n// One-shot listener: fires once and auto-removes\npulse.once('chat:message', (msg) => {\n console.log('first message:', msg.text)\n})\n\n// Connect; send when open\ntry {\n await pulse.connect()\n pulse.send('chat:send', { text: 'Hello, world!' })\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// Await next server event with a 5 s deadline\ntry {\n const msg = await pulse.wait('chat:message', { timeout: 500 })\n console.log('received:', msg.text)\n} catch (err) {\n console.log('wait ended:', err.message)\n}\n\nunsub()\npulse.dispose()",
18
18
  "name": "Connect & Send"
19
19
  },
20
20
  {
21
21
  "id": "lifecycle",
22
- "code": "import { createPulse, PulseDisposedError } from '@vielzeug/pulse'\n\n// Status signal, disposalSignal, and error handling on dispose\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { delay: 1_000, maxAttempts: 3 },\n heartbeat: { interval: 30_000, timeout: 5_000 },\n})\n\n// Observe runtime events via tap()\npulse.tap((event) => {\n if (event.type === 'error') console.log('Pulse error:', event.error.message)\n})\n\n// Construction is closed. connect() makes the transport available.\nconsole.log('initial status:', pulse.status.getSnapshot())\n\n// disposalSignal aborts when dispose() is called\npulse.disposalSignal.addEventListener('abort', () => {\n console.log('disposal signal fired')\n})\n\ntry {\n await pulse.connect()\n console.log('connected:', pulse.status.getSnapshot())\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// dispose() is idempotent — safe to call multiple times\npulse.dispose()\npulse.dispose()\nconsole.log('disposed:', pulse.disposed)\n\n// Methods reject with PulseDisposedError after dispose\ntry {\n await pulse.connect()\n} catch (err) {\n if (err instanceof PulseDisposedError) {\n console.log('connect() rejected with PulseDisposedError — correct')\n }\n}",
22
+ "code": "import { createPulse, PulseDisposedError } from '@vielzeug/pulse'\n\n// Status signal, disposalSignal, and error handling on dispose\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { delay: 1_000, maxAttempts: 3 },\n heartbeat: { interval: 30_000, timeout: 5_000 },\n})\n\n// Observe runtime events via tap()\npulse.tap((event) => {\n if (event.type === 'error') console.log('Pulse error:', event.error.message)\n})\n\n// Construction is closed. connect() makes the transport available.\nconsole.log('initial status:', pulse.status.getSnapshot())\n\n// disposalSignal aborts when dispose() is called\npulse.disposalSignal.addEventListener('abort', () => {\n console.log('disposal signal fired')\n})\n\ntry {\n await pulse.connect()\n console.log('connected:', pulse.status.getSnapshot())\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// dispose() is idempotent: safe to call multiple times\npulse.dispose()\npulse.dispose()\nconsole.log('disposed:', pulse.disposed)\n\n// Methods reject with PulseDisposedError after dispose\ntry {\n await pulse.connect()\n} catch (err) {\n if (err instanceof PulseDisposedError) {\n console.log('connect() rejected with PulseDisposedError: correct')\n }\n}",
23
23
  "name": "Lifecycle & Disposal"
24
24
  },
25
25
  {
@@ -29,7 +29,7 @@
29
29
  },
30
30
  {
31
31
  "id": "rooms-presence",
32
- "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Room scopes: ref-counted membership with reactive presence\nconst pulse = createPulse('wss://api.example.com/ws')\nconst lobby = pulse.room('lobby')\n\ntry {\n await pulse.connect()\n\n // Wait for server confirmation\n await lobby.joined\n console.log('joined lobby, rooms:', [...pulse.rooms.getSnapshot()])\n\n // Broadcast our own presence\n lobby.updatePresence({ avatar: '/me.png', name: 'Alice', status: 'online' })\n\n // Reactive presence map: memberId → state\n const printMembers = () => {\n for (const [id, state] of lobby.presence.getSnapshot()) {\n console.log(' ' + id + ': ' + state.name + ' (' + state.status + ')')\n }\n }\n\n // React to individual joins and leaves\n lobby.onJoin((id, state) => console.log(state.name + ' joined'))\n lobby.onLeave((id) => console.log(id + ' left'))\n} catch (err) {\n console.log('connection or room operation failed:', err.message)\n}\n\n// Dispose the room scope — sends leave when last scope is released\nlobby.dispose()\nconsole.log('rooms after leave:', [...pulse.rooms.getSnapshot()])\n\npulse.dispose()",
32
+ "code": "import { createPulse } from '@vielzeug/pulse'\n\n// Room scopes: ref-counted membership with reactive presence\nconst pulse = createPulse('wss://api.example.com/ws')\nconst lobby = pulse.room('lobby')\n\ntry {\n await pulse.connect()\n\n // Wait for server confirmation\n await lobby.joined\n console.log('joined lobby, rooms:', [...pulse.rooms.getSnapshot()])\n\n // Broadcast our own presence\n lobby.updatePresence({ avatar: '/me.png', name: 'Alice', status: 'online' })\n\n // Reactive presence map: memberId → state\n const printMembers = () => {\n for (const [id, state] of lobby.presence.getSnapshot()) {\n console.log(' ' + id + ': ' + state.name + ' (' + state.status + ')')\n }\n }\n\n // React to individual joins and leaves\n lobby.onJoin((id, state) => console.log(state.name + ' joined'))\n lobby.onLeave((id) => console.log(id + ' left'))\n} catch (err) {\n console.log('connection or room operation failed:', err.message)\n}\n\n// Dispose the room scope: sends leave when last scope is released\nlobby.dispose()\nconsole.log('rooms after leave:', [...pulse.rooms.getSnapshot()])\n\npulse.dispose()",
33
33
  "name": "Rooms & Presence"
34
34
  }
35
35
  ],
@@ -42,30 +42,30 @@
42
42
  "PulseRoomTimeoutError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseRoomTimeoutError,\n PulseTimeoutError,\n} from './errors';",
43
43
  "PulseTimeoutError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseRoomTimeoutError,\n PulseTimeoutError,\n} from './errors';",
44
44
  "createPulse": "export { createPulse } from './pulse';",
45
- "ChannelDefinition": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
46
- "ChannelDefinitions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
47
- "ClientEvents": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
48
- "EventKey": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
49
- "ExternalStore": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
50
- "HeartbeatOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
51
- "MessageMap": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
52
- "OutgoingMessage": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
53
- "OutgoingTransform": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
54
- "PresenceRoomScope": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
55
- "Pulse": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
56
- "PulseChannel": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
57
- "PulseEvent": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
58
- "PulseOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
59
- "PulseSchema": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
60
- "PulseStatus": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
61
- "ReconnectOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
62
- "RoomDefinition": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
63
- "RoomDefinitions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
64
- "RoomMap": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
65
- "RoomOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
66
- "RoomScope": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
67
- "RoomScopeBase": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
68
- "ServerEvents": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';",
69
- "Unsubscribe": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n ExternalStore,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Unsubscribe,\n} from './types';"
45
+ "ChannelDefinition": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
46
+ "ChannelDefinitions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
47
+ "ClientEvents": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
48
+ "EventKey": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
49
+ "HeartbeatOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
50
+ "MessageMap": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
51
+ "OutgoingMessage": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
52
+ "OutgoingTransform": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
53
+ "PresenceRoomScope": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
54
+ "Pulse": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
55
+ "PulseChannel": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
56
+ "PulseEvent": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
57
+ "PulseOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
58
+ "PulseSchema": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
59
+ "PulseStatus": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
60
+ "ReconnectOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
61
+ "RoomDefinition": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
62
+ "RoomDefinitions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
63
+ "RoomMap": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
64
+ "RoomOptions": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
65
+ "RoomScope": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
66
+ "RoomScopeBase": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
67
+ "ServerEvents": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
68
+ "Subscribable": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';",
69
+ "Unsubscribe": "export type {\n ChannelDefinition,\n ChannelDefinitions,\n ClientEvents,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n OutgoingMessage,\n OutgoingTransform,\n PresenceRoomScope,\n Pulse,\n PulseChannel,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n ReconnectOptions,\n RoomDefinition,\n RoomDefinitions,\n RoomMap,\n RoomOptions,\n RoomScope,\n RoomScopeBase,\n ServerEvents,\n Subscribable,\n Unsubscribe,\n} from './types';"
70
70
  }
71
71
  }
@@ -1,12 +1,14 @@
1
1
  {
2
- "apiSource": "/**\n * Refine components register through their explicit component entry points.\n *\n * Keeping the package root free of registration side effects makes dependency\n * ownership and bundle contents obvious to application code.\n */\nexport { RefineError } from './errors';\n",
2
+ "apiSource": "/**\n * Refine components register through their explicit component entry points.\n *\n * Keeping the package root free of registration side effects makes dependency\n * ownership and bundle contents obvious to application code.\n */\nexport { RefineError } from './errors';\nexport { eventFieldChecked, eventFieldValue } from './inputs/shared/native-field-event';\n",
3
3
  "docs": {
4
- "index": "---\ntitle: Refine — Web component library\ndescription: Accessible, themeable web components built with Ore for framework and vanilla DOM apps.\npackage: refine\ncategory: ui-components\nkeywords: [web-components, accessible, themeable, ui, components, design-system]\nrelated: [ore, orbit, forge, keymap]\nexports:\n [\n ore-accordion,\n ore-accordion-item,\n ore-alert,\n ore-async,\n ore-avatar,\n ore-avatar-group,\n ore-badge,\n ore-box,\n ore-breadcrumb,\n ore-breadcrumb-item,\n ore-button,\n ore-button-group,\n ore-calendar,\n ore-card,\n ore-carousel,\n ore-carousel-slide,\n ore-chat-message,\n ore-checkbox,\n ore-checkbox-group,\n ore-chip,\n ore-code-window,\n ore-column,\n ore-combobox,\n ore-combobox-option,\n ore-command-palette,\n ore-command-palette-item,\n ore-copy-command,\n ore-counter,\n ore-datagrid,\n ore-date-picker,\n ore-dialog,\n ore-drawer,\n ore-file-input,\n ore-grid,\n ore-grid-item,\n ore-icon,\n ore-input,\n ore-keyboard-key,\n ore-keyboard-shortcut,\n ore-list,\n ore-list-item,\n ore-marquee,\n ore-menu,\n ore-menu-item,\n ore-menu-separator,\n ore-message-composer,\n ore-navbar,\n ore-navbar-item,\n ore-navigation-menu,\n ore-navigation-menu-item,\n ore-navigation-menu-panel,\n ore-number-input,\n ore-otp-input,\n ore-pagination,\n ore-password-strength,\n ore-popover,\n ore-progress,\n ore-qr-code,\n ore-qr-scanner,\n ore-radio,\n ore-radio-group,\n ore-rating,\n ore-select,\n ore-separator,\n ore-sidebar,\n ore-sidebar-group,\n ore-sidebar-item,\n ore-skeleton,\n ore-slider,\n ore-stats,\n ore-step,\n ore-stepper,\n ore-switch,\n ore-tab-item,\n ore-tab-panel,\n ore-table,\n ore-tabs,\n ore-td,\n ore-text,\n ore-th,\n ore-tr,\n ore-textarea,\n ore-time-picker,\n ore-toast,\n ore-tooltip,\n ore-typing-indicator,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"refine\" />\n\n## Why Refine?\n\nEvery project needs UI primitives. Refine provides accessible web components that work natively anywhere HTML is rendered—no framework required.\n\n```html\n<!-- Before — roll your own button with ARIA -->\n<button class=\"btn btn-primary\" role=\"button\" aria-pressed=\"false\" tabindex=\"0\">\n <span class=\"btn-spinner\" aria-hidden=\"true\"></span>\n Save\n</button>\n\n<!-- After — Refine -->\n<ore-button variant=\"solid\" color=\"primary\" loading>Save</ore-button>\n```\n\n| Feature | Refine | Shoelace | Material Web |\n| ------------------ | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"refine\" type=\"size\" /> | ~145 kB | ~200 kB |\n| Built with | Ore | Lit | Lit |\n| Runtime dependencies | Vielzeug UI primitives + Lucide | Lit + utilities | Lit + Material foundations |\n| Accessible | WCAG AA | WCAG AA | WCAG AA |\n| Framework agnostic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Refine when** you want accessible web components that match the Vielzeug design system without a heavy framework dependency.\n\n**Consider Shoelace or Material Web** if your team is already standardized on those ecosystems and you need their established component catalogs.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/refine\n```\n\n```sh [npm]\nnpm install @vielzeug/refine\n```\n\n```sh [yarn]\nyarn add @vielzeug/refine\n```\n\n:::\n\n## Quick Start\n\n```ts\n// 1. Import global styles once\nimport '@vielzeug/refine/fouc.css'; // Hide unupgraded custom elements until first paint\nimport '@vielzeug/refine/tokens.css'; // Tokens, animations, cascade layers\n\n// 2. Register only the elements you need\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/card';\n```\n\n```html\n<ore-button variant=\"solid\" color=\"primary\">Save</ore-button>\n<ore-input label=\"Email\" type=\"email\" required></ore-input>\n<ore-card padding=\"lg\">\n <span slot=\"header\">Account</span>\n <p>Card content goes here.</p>\n</ore-card>\n```\n\n```ts\n```\n\n### CDN / Vanilla HTML\n\nUse the self-contained IIFE bundle to load Refine directly from a CDN in any HTML page — no build step required:\n\n```html\n<!-- 1. Styles -->\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/fouc.css\" />\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/tokens.css\" />\n\n<!-- 2. All components (IIFE — registers global Refine namespace) -->\n<script src=\"https://unpkg.com/@vielzeug/refine/dist/refine.iife.js\"></script>\n```\n\nFor bundler-based projects that still want a CDN URL, use the ESM bundle via an import map:\n\n```html\n<script type=\"importmap\">\n {\n \"imports\": {\n \"@vielzeug/refine\": \"https://esm.sh/@vielzeug/refine\",\n \"@vielzeug/refine/button\": \"https://esm.sh/@vielzeug/refine/button\",\n \"@vielzeug/refine/input\": \"https://esm.sh/@vielzeug/refine/input\"\n }\n }\n</script>\n\n<script type=\"module\">\n import '@vielzeug/refine/button';\n import '@vielzeug/refine/input';\n</script>\n```\n\n### Package Entry Points\n\n| Import | Purpose |\n| ------------------------ | ----------------------------------------- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Global design tokens and cascade layers |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset (includes FOUC suppression) |\n\nComponent registration happens through side-effect imports such as `@vielzeug/refine/button` and `@vielzeug/refine/dialog`.\n\n### Components\n\n**Content:** `ore-avatar`, `ore-avatar-group`, `ore-breadcrumb`, `ore-card`, `ore-carousel`, `ore-carousel-slide`, `ore-chat-message`, `ore-icon`, `ore-keyboard-key`, `ore-keyboard-shortcut`, `ore-list`, `ore-list-item`, `ore-marquee`, `ore-pagination`, `ore-qr-code`, `ore-separator`, `ore-stats`, `ore-step`, `ore-stepper`, `ore-table`, `ore-text`\n\n**Disclosure:** `ore-accordion`, `ore-accordion-item`, `ore-tabs`, `ore-tab-item`, `ore-tab-panel`\n\n**Feedback:** `ore-alert`, `ore-async`, `ore-badge`, `ore-chip`, `ore-password-strength`, `ore-progress`, `ore-skeleton`, `ore-toast`, `ore-typing-indicator`\n\n**Inputs:** `ore-button`, `ore-button-group`, `ore-calendar`, `ore-checkbox`, `ore-checkbox-group`, `ore-column`, `ore-combobox`, `ore-counter`, `ore-datagrid`, `ore-date-picker`, `ore-file-input`, `ore-input`, `ore-message-composer`, `ore-number-input`, `ore-otp-input`, `ore-qr-scanner`, `ore-radio`, `ore-radio-group`, `ore-rating`, `ore-select`, `ore-slider`, `ore-switch`, `ore-textarea`, `ore-time-picker`\n\n**Layout:** `ore-box`, `ore-grid`, `ore-grid-item`, `ore-navbar`, `ore-sidebar`\n\n**Overlay:** `ore-command-palette`, `ore-command-palette-item`, `ore-dialog`, `ore-drawer`, `ore-menu`, `ore-popover`, `ore-tooltip`\n\n## Component portfolio\n\nRefine keeps one public package and classifies components by composition level rather than stability. All listed components remain supported and tested.\n\n- **Foundations** — buttons, inputs, selection controls, disclosure, dialogs, menus, tooltips, layout primitives, and feedback indicators. Start here when building an application-specific interface.\n- **Composed patterns** — data grids, command palettes, navigation menus, steppers, calendars, date/time pickers, file inputs, and message composers. These package recurring accessibility and interaction behavior that is costly to rebuild correctly.\n- **Presentation patterns** — cards, chat messages, code windows, stats, marquees, avatars, badges, and skeletons. Use these when their product vocabulary matches the application; prefer foundations when it does not.\n\nClassification guides discovery and bundle selection. It is not a reason to remove working components or move them into unstable packages; per-component subpath imports remain the tree-shaking boundary.\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Accessible** — keyboard navigation, ARIA wiring, and focus management across interactive components\n- **Themeable** — global tokens plus component-level CSS custom properties\n- **Framework agnostic** — works anywhere HTML can be rendered\n- **Tree-shakeable** — import only the component entry points you register\n- **Comprehensive surface** — inputs, content, disclosure, feedback, layout, and overlay primitives\n- **Typed framework declarations** — DOM, React, and Vue tags derive from the component property contracts\n\n</div>\n\n### Prerequisites\n\n- Browser runtime with Custom Elements support.\n- Import `@vielzeug/refine/fouc.css` and `@vielzeug/refine/tokens.css` before rendering components.\n- For SSR, render placeholders server-side and hydrate components only on the client.\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ore](/ore/) — Web component runtime that powers Refine\n- [Orbit](/orbit/) — Floating UI positioning used in Refine's overlays\n- [Forge](/forge/) — Form state management for use with Refine inputs\n- [Keymap](/keymap/) — Keyboard shortcut manager that powers the command palette's global trigger\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
- "api": "---\ntitle: Refine — API Reference\ndescription: Published component, stylesheet, framework type, and error entry points for @vielzeug/refine.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `@vielzeug/refine/<component>` | Register one custom element and export its public types | Sync | Importing a type alone does not register the element |\n| `@vielzeug/refine/tokens.css` | Load required theme tokens, animations, and cascade layers | CSS | Import once before components render |\n| `@vielzeug/refine/fouc.css` | Hide unregistered `ore-*` elements until upgrade | CSS | Load before first paint |\n| `@vielzeug/refine/styles/preflight.css` | Apply the optional browser reset | CSS | The reset affects global elements |\n| `@vielzeug/refine/frameworks/elements` | Register typed DOM tag mappings | Types only | Import for side effects in TypeScript |\n| `@vielzeug/refine/frameworks/react` | Register typed React JSX elements | Types only | Does not provide runtime wrappers |\n| `@vielzeug/refine/frameworks/vue` | Register typed Vue global components | Types only | Does not install a Vue plugin |\n| `RefineError` | Base class for package-defined public errors | Sync | Component configuration warnings do not throw this error |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/refine` | Export `RefineError` without registering components |\n| `@vielzeug/refine/<component>` | Register one component and export its tag constant, props, events, and related types |\n| `@vielzeug/refine/tokens.css` | Required global design contract |\n| `@vielzeug/refine/fouc.css` | Optional pre-upgrade visibility rule |\n| `@vielzeug/refine/styles/*` | Focused theme, animation, layer, and preflight stylesheets |\n| `@vielzeug/refine/frameworks/*` | TypeScript augmentations for DOM, React, and Vue |\n\n## Styles\n\n```ts\nimport '@vielzeug/refine/fouc.css';\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n`tokens.css` is required. It defines tokens, animations, and cascade-layer order without resetting global elements. `preflight.css` is optional and includes FOUC suppression.\n\n| Import path | Purpose |\n| --- | --- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Tokens, animation helpers, and cascade layers |\n| `@vielzeug/refine/styles/theme.css` | Theme token declarations |\n| `@vielzeug/refine/styles/animation.css` | Animation helpers |\n| `@vielzeug/refine/styles/layers.css` | Cascade layer declarations |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser reset and FOUC suppression |\n\n## Components\n\nEach component uses one registration and type entry point:\n\n```ts\nimport '@vielzeug/refine/button';\nimport type { OreButtonProps } from '@vielzeug/refine/button';\n```\n\n| Area | Components |\n| --- | --- |\n| Content | `avatar`, `avatar-group`, `breadcrumb`, `card`, `carousel`, `chat-message`, `code-window`, `copy-command`, `icon`, `keyboard-key`, `list`, `list-item`, `marquee`, `pagination`, `separator`, `stats`, `step`, `stepper`, `table`, `text` |\n| Disclosure | `accordion`, `accordion-item`, `tabs`, `tab-item`, `tab-panel` |\n| Feedback | `alert`, `async`, `badge`, `chip`, `password-strength`, `progress`, `skeleton`, `toast`, `typing-indicator` |\n| Inputs | `button`, `button-group`, `calendar`, `checkbox`, `checkbox-group`, `combobox`, `counter`, `datagrid`, `date-picker`, `file-input`, `input`, `message-composer`, `number-input`, `otp-input`, `radio`, `radio-group`, `rating`, `select`, `slider`, `switch`, `textarea`, `time-picker` |\n| Layout | `box`, `grid`, `grid-item`, `navbar`, `sidebar` |\n| Overlays | `command-palette`, `dialog`, `drawer`, `menu`, `navigation-menu`, `popover`, `tooltip` |\n\nEach component page lists its attributes, JavaScript properties, events, slots, parts, and CSS custom properties.\n\n## Framework Types\n\n```ts\nimport type {} from '@vielzeug/refine/frameworks/elements';\nimport type {} from '@vielzeug/refine/frameworks/react';\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\nThe declarations cover every supported tag and derive component properties from Refine's authoritative `HTMLElementTagNameMap`. They provide types only; component registration still uses component subpaths.\n\n## Events and Form Controls\n\nForm controls expose `.value` or `.checked` and dispatch standard `input` and `change` events. Read the property from `event.currentTarget`.\n\nStateful overlays expose controlled `open`, optional `default-open`, and an `open-change` custom event with `{ open, reason }` detail. Component pages define additional detail fields and reasons.\n\n## Types\n\nComponent props follow the `Ore<Component>Props` naming pattern. Event maps follow `Ore<Component>Events`. Import both from the component subpath that owns them.\n\n`RefineElementMap`, `RefineReactIntrinsicElements`, and `RefineVueGlobalComponents` are exported from their respective `frameworks/*` type entry points.\n\n## Errors\n\n### `RefineError`\n\n```ts\nclass RefineError extends Error {\n constructor(message: string, opts?: ErrorOptions);\n}\n```\n\nBase class for public Refine errors. It preserves `cause` through `ErrorOptions`.\n",
6
- "usage": "---\ntitle: Refine — Usage Guide\ndescription: Register Refine components, configure properties, handle events, compose slots, and integrate frameworks.\n---\n\n[[toc]]\n\n## Basic Usage\n\nLoad the global design contract once, then register only the components used by the page.\n\n```ts\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/dialog';\n\nconst input = document.querySelector('ore-input');\nconst save = document.querySelector('ore-button');\n\nif (!(input instanceof HTMLElement) || !(save instanceof HTMLElement)) {\n throw new Error('Expected Refine controls');\n}\n\nsave.addEventListener('click', () => {\n console.log((input as HTMLElement & { value: string }).value);\n});\n```\n\nAdd the browser reset only when the application owns global element styling:\n\n```ts\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n## Set Attributes and Properties\n\nUse attributes for serializable HTML state.\n\n```html\n<ore-button variant=\"outline\" color=\"secondary\" size=\"lg\" disabled>\n Save changes\n</ore-button>\n```\n\nUse JavaScript properties for arrays, objects, callbacks, and controlled state.\n\n```ts\nimport '@vielzeug/refine/datagrid';\nimport type { OreDataGridProps } from '@vielzeug/refine/datagrid';\n\nconst grid = document.querySelector('ore-datagrid') as HTMLElement & OreDataGridProps;\ngrid.columns = [\n { key: 'name', label: 'Name', sortable: true },\n { key: 'role', label: 'Role' },\n];\ngrid.rows = [\n { id: '1', name: 'Ada', role: 'Admin' },\n { id: '2', name: 'Linus', role: 'Editor' },\n];\n```\n\n## Handle Events\n\nForm controls dispatch standard `input` and `change` events. Read their current property from `event.currentTarget`.\n\n```ts\nconst input = document.querySelector('ore-input');\ninput?.addEventListener('input', (event) => {\n const target = event.currentTarget as HTMLElement & { value: string };\n console.log(target.value);\n});\n```\n\nCustom events use kebab-case names and typed detail objects.\n\n```ts\nimport type { OreDialogEvents } from '@vielzeug/refine/dialog';\n\nconst dialog = document.querySelector('ore-dialog');\ndialog?.addEventListener('open-change', (event: CustomEvent<OreDialogEvents['open-change']>) => {\n console.log(event.detail.open, event.detail.reason);\n});\n```\n\n## Compose Slots\n\nDefault slots hold primary content. Named slots identify component-owned regions.\n\n```html\n<ore-card>\n <span slot=\"header\">Account</span>\n <p>Profile settings</p>\n <div slot=\"footer\">\n <ore-button variant=\"ghost\">Cancel</ore-button>\n <ore-button>Save</ore-button>\n </div>\n</ore-card>\n```\n\nInput components commonly expose `prefix`, `suffix`, `label`, `helper`, and `error` slots. Each component reference lists its supported slots.\n\n## Load TypeScript Framework Declarations\n\nImport the declaration matching the framework once in an application type entry point.\n\n::: code-group\n\n```ts [DOM]\nimport type {} from '@vielzeug/refine/frameworks/elements';\n```\n\n```ts [React]\nimport type {} from '@vielzeug/refine/frameworks/react';\n```\n\n```ts [Vue]\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\n:::\n\nThese entry points provide typed tags and component properties. They do not register components or install runtime adapters.\n\n## Framework Integration\n\nRefine elements use native DOM properties, events, and slots. Frameworks differ only in how they forward custom-element properties and events.\n\n::: code-group\n\n```tsx [React 19]\nimport '@vielzeug/refine/button';\n\nexport function SaveButton() {\n return <ore-button variant=\"solid\">Save</ore-button>;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport '@vielzeug/refine/button';\n</script>\n\n<template>\n <ore-button variant=\"solid\">Save</ore-button>\n</template>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import '@vielzeug/refine/button';\n</script>\n\n<ore-button variant=\"solid\">Save</ore-button>\n```\n\n:::\n\nSee [Framework Integration](./frameworks.md) for custom events, controlled properties, SSR guards, and framework-specific configuration.\n\n## Working with Other Vielzeug Libraries\n\n### Ore\n\nUse Ore when authoring a custom element that composes Refine components.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { define, html } from '@vielzeug/ore';\n\ndefine('save-panel', {\n setup() {\n return html`<ore-button @click=${() => console.log('save')}>Save</ore-button>`;\n },\n});\n```\n\n### Ripple\n\nUse Ripple to drive component properties from application state.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { effect, signal } from '@vielzeug/ripple';\n\nconst loading = signal(false);\nconst button = document.querySelector('ore-button') as HTMLElement & { loading?: boolean };\nconst loadingEffect = effect(() => {\n button.loading = loading.value;\n});\n\nloadingEffect.dispose();\n```\n\n## Best Practices\n\n- **Import** `tokens.css` once before rendering components.\n- **Register** components through explicit subpaths.\n- **Set** structured data and callbacks as JavaScript properties.\n- **Read** form state from the event target instead of custom event payloads.\n- **Provide** accessible labels for icon-only controls.\n- **Use** component reference pages for supported slots, parts, and CSS properties.\n- **Dispose** application-owned Ripple effects and service handles.\n- **Guard** DOM-dependent component registration during SSR.\n- **Working with AI agents?** Install the `vielzeug` skill (`npx -y @vielzeug/codex skills install`) so agents reach for Refine components and tokens instead of custom widgets — see the [Codex usage guide](../codex/usage.md#agent-skill).\n"
4
+ "index": "---\ntitle: 'Refine: Web component library'\ndescription: Accessible, themeable web components built with Ore for framework and vanilla DOM apps.\npackage: refine\ncategory: ui-components\nkeywords: [web-components, accessible, themeable, ui, components, design-system]\nrelated: [ore, orbit, forge, keymap]\nexports:\n [\n ore-accordion,\n ore-accordion-item,\n ore-alert,\n ore-async,\n ore-avatar,\n ore-avatar-group,\n ore-badge,\n ore-box,\n ore-breadcrumb,\n ore-breadcrumb-item,\n ore-button,\n ore-button-group,\n ore-calendar,\n ore-card,\n ore-carousel,\n ore-carousel-slide,\n ore-chat-message,\n ore-checkbox,\n ore-checkbox-group,\n ore-chip,\n ore-code-window,\n ore-column,\n ore-combobox,\n ore-combobox-option,\n ore-command-palette,\n ore-command-palette-item,\n ore-cookie-banner,\n ore-copy-command,\n ore-counter,\n ore-datagrid,\n ore-date-picker,\n ore-dialog,\n ore-drawer,\n ore-file-input,\n ore-grid,\n ore-grid-item,\n ore-icon,\n ore-input,\n ore-keyboard-key,\n ore-keyboard-shortcut,\n ore-list,\n ore-list-item,\n ore-marquee,\n ore-menu,\n ore-menu-item,\n ore-menu-separator,\n ore-message-composer,\n ore-navbar,\n ore-navbar-item,\n ore-navigation-menu,\n ore-navigation-menu-item,\n ore-navigation-menu-panel,\n ore-number-input,\n ore-otp-input,\n ore-pagination,\n ore-password-strength,\n ore-popover,\n ore-progress,\n ore-qr-code,\n ore-qr-scanner,\n ore-radio,\n ore-radio-group,\n ore-rating,\n ore-select,\n ore-separator,\n ore-sidebar,\n ore-sidebar-group,\n ore-sidebar-item,\n ore-skeleton,\n ore-slider,\n ore-speech-player,\n ore-stats,\n ore-step,\n ore-stepper,\n ore-switch,\n ore-tab-item,\n ore-tab-panel,\n ore-table,\n ore-tabs,\n ore-td,\n ore-text,\n ore-th,\n ore-tr,\n ore-textarea,\n ore-time-picker,\n ore-toast,\n ore-tooltip,\n ore-typing-indicator,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"refine\" />\n\n## Why Refine?\n\nEvery project needs UI primitives. Refine provides accessible web components that work natively anywhere HTML is rendered, no framework required.\n\n```html\n<!-- Before: roll your own button with ARIA -->\n<button class=\"btn btn-primary\" role=\"button\" aria-pressed=\"false\" tabindex=\"0\">\n <span class=\"btn-spinner\" aria-hidden=\"true\"></span>\n Save\n</button>\n\n<!-- After: Refine -->\n<ore-button variant=\"solid\" color=\"primary\" loading>Save</ore-button>\n```\n\n| Feature | Refine | Shoelace | Material Web |\n| ------------------ | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"refine\" type=\"size\" /> | ~145 kB | ~200 kB |\n| Built with | Ore | Lit | Lit |\n| Runtime dependencies | Vielzeug UI primitives + Lucide | Lit + utilities | Lit + Material foundations |\n| Accessible | WCAG AA | WCAG AA | WCAG AA |\n| Framework agnostic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Refine when** you want accessible web components that match the Vielzeug design system without a heavy framework dependency.\n\n**Consider Shoelace or Material Web** if your team is already standardized on those ecosystems and you need their established component catalogs.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/refine\n```\n\n```sh [npm]\nnpm install @vielzeug/refine\n```\n\n```sh [yarn]\nyarn add @vielzeug/refine\n```\n\n:::\n\n## Quick Start\n\n```ts\n// 1. Import global styles once\nimport '@vielzeug/refine/fouc.css'; // Hide unupgraded custom elements until first paint\nimport '@vielzeug/refine/tokens.css'; // Tokens, animations, cascade layers\n\n// 2. Register only the elements you need\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/card';\n```\n\n```html\n<ore-button variant=\"solid\" color=\"primary\">Save</ore-button>\n<ore-input label=\"Email\" type=\"email\" required></ore-input>\n<ore-card padding=\"lg\">\n <span slot=\"header\">Account</span>\n <p>Card content goes here.</p>\n</ore-card>\n```\n\n```ts\n```\n\n### CDN / Vanilla HTML\n\nUse the self-contained IIFE bundle to load Refine directly from a CDN in any HTML page: no build step required:\n\n```html\n<!-- 1. Styles -->\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/fouc.css\" />\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/tokens.css\" />\n\n<!-- 2. All components (IIFE: registers global Refine namespace) -->\n<script src=\"https://unpkg.com/@vielzeug/refine/dist/refine.iife.js\"></script>\n```\n\nFor bundler-based projects that still want a CDN URL, use the ESM bundle via an import map:\n\n```html\n<script type=\"importmap\">\n {\n \"imports\": {\n \"@vielzeug/refine\": \"https://esm.sh/@vielzeug/refine\",\n \"@vielzeug/refine/button\": \"https://esm.sh/@vielzeug/refine/button\",\n \"@vielzeug/refine/input\": \"https://esm.sh/@vielzeug/refine/input\"\n }\n }\n</script>\n\n<script type=\"module\">\n import '@vielzeug/refine/button';\n import '@vielzeug/refine/input';\n</script>\n```\n\n### Package Entry Points\n\n| Import | Purpose |\n| ------------------------ | ----------------------------------------- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Global design tokens and cascade layers |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset (includes FOUC suppression) |\n\nComponent registration happens through side-effect imports such as `@vielzeug/refine/button` and `@vielzeug/refine/dialog`.\n\n### Components\n\n**Content:** `ore-avatar`, `ore-avatar-group`, `ore-breadcrumb`, `ore-card`, `ore-carousel`, `ore-carousel-slide`, `ore-chat-message`, `ore-icon`, `ore-keyboard-key`, `ore-keyboard-shortcut`, `ore-list`, `ore-list-item`, `ore-marquee`, `ore-pagination`, `ore-qr-code`, `ore-separator`, `ore-stats`, `ore-step`, `ore-stepper`, `ore-table`, `ore-text`\n\n**Disclosure:** `ore-accordion`, `ore-accordion-item`, `ore-tabs`, `ore-tab-item`, `ore-tab-panel`\n\n**Feedback:** `ore-alert`, `ore-async`, `ore-badge`, `ore-chip`, `ore-cookie-banner`, `ore-password-strength`, `ore-progress`, `ore-skeleton`, `ore-toast`, `ore-typing-indicator`\n\n**Inputs:** `ore-button`, `ore-button-group`, `ore-calendar`, `ore-checkbox`, `ore-checkbox-group`, `ore-column`, `ore-combobox`, `ore-counter`, `ore-datagrid`, `ore-date-picker`, `ore-file-input`, `ore-input`, `ore-message-composer`, `ore-number-input`, `ore-otp-input`, `ore-qr-scanner`, `ore-radio`, `ore-radio-group`, `ore-rating`, `ore-select`, `ore-slider`, `ore-switch`, `ore-textarea`, `ore-time-picker`\n\n**Layout:** `ore-box`, `ore-grid`, `ore-grid-item`, `ore-navbar`, `ore-sidebar`\n\n**Overlay:** `ore-command-palette`, `ore-command-palette-item`, `ore-dialog`, `ore-drawer`, `ore-menu`, `ore-popover`, `ore-tooltip`\n\n## Component portfolio\n\nRefine keeps one public package and classifies components by composition level rather than stability. All listed components remain supported and tested.\n\n- **Foundations**: buttons, inputs, selection controls, disclosure, dialogs, menus, tooltips, layout primitives, and feedback indicators. Start here when building an application-specific interface.\n- **Composed patterns**: data grids, command palettes, navigation menus, steppers, calendars, date/time pickers, file inputs, and message composers. These package recurring accessibility and interaction behavior that is costly to rebuild correctly.\n- **Presentation patterns**: cards, chat messages, code windows, stats, marquees, avatars, badges, and skeletons. Use these when their product vocabulary matches the application; prefer foundations when it does not.\n\nClassification guides discovery and bundle selection. It is not a reason to remove working components or move them into unstable packages; per-component subpath imports remain the tree-shaking boundary.\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Accessible**: keyboard navigation, ARIA wiring, and focus management across interactive components\n- **Themeable**: global tokens plus component-level CSS custom properties\n- **Framework agnostic**: works anywhere HTML can be rendered\n- **Tree-shakeable**: import only the component entry points you register\n- **Comprehensive surface**: inputs, content, disclosure, feedback, layout, and overlay primitives\n- **Typed framework declarations**: DOM, React, and Vue tags derive from the component property contracts\n\n</div>\n\n### Prerequisites\n\n- Browser runtime with Custom Elements support.\n- Import `@vielzeug/refine/fouc.css` and `@vielzeug/refine/tokens.css` before rendering components.\n- For SSR, render placeholders server-side and hydrate components only on the client.\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ore](/ore/): Web component runtime that powers Refine\n- [Orbit](/orbit/): Floating UI positioning used in Refine's overlays\n- [Forge](/forge/): Form state management for use with Refine inputs\n- [Keymap](/keymap/): Keyboard shortcut manager that powers the command palette's global trigger\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
+ "api": "---\ntitle: 'Refine: API Reference'\ndescription: Published component, stylesheet, framework type, and error entry points for @vielzeug/refine.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `@vielzeug/refine/<component>` | Register one custom element and export its public types | Sync | Importing a type alone does not register the element |\n| `@vielzeug/refine/tokens.css` | Load required theme tokens, animations, and cascade layers | CSS | Import once before components render |\n| `@vielzeug/refine/fouc.css` | Hide unregistered `ore-*` elements until upgrade | CSS | Load before first paint |\n| `@vielzeug/refine/styles/preflight.css` | Apply the optional browser reset | CSS | The reset affects global elements |\n| `@vielzeug/refine/frameworks/elements` | Register typed DOM tag mappings | Types only | Import for side effects in TypeScript |\n| `@vielzeug/refine/frameworks/react` | Register typed React JSX elements | Types only | Does not provide runtime wrappers |\n| `@vielzeug/refine/frameworks/vue` | Register typed Vue global components | Types only | Does not install a Vue plugin |\n| `@vielzeug/refine/theme` | `createThemeController()`: light/dark/system document theme | Sync | Does not persist the preference or set an accent color |\n| `eventFieldValue()` / `eventFieldChecked()` | Read a form control's value/checked from `event.currentTarget` | Sync | Exported from the package root; guards non-field targets |\n| `RefineError` | Base class for package-defined public errors | Sync | Component configuration warnings do not throw this error |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/refine` | Export `RefineError` and the `eventFieldValue`/`eventFieldChecked` field readers without registering components |\n| `@vielzeug/refine/<component>` | Register one component and export its tag constant, props, events, and related types |\n| `@vielzeug/refine/theme` | `createThemeController()` and its theme types |\n| `@vielzeug/refine/tokens.css` | Required global design contract |\n| `@vielzeug/refine/fouc.css` | Optional pre-upgrade visibility rule |\n| `@vielzeug/refine/styles/*` | Focused theme, animation, layer, and preflight stylesheets |\n| `@vielzeug/refine/frameworks/*` | TypeScript augmentations for DOM, React, and Vue |\n\n## Styles\n\n```ts\nimport '@vielzeug/refine/fouc.css';\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n`tokens.css` is required. It defines tokens, animations, and cascade-layer order without resetting global elements. `preflight.css` is optional and includes FOUC suppression.\n\n| Import path | Purpose |\n| --- | --- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Tokens, animation helpers, and cascade layers |\n| `@vielzeug/refine/styles/theme.css` | Theme token declarations |\n| `@vielzeug/refine/styles/animation.css` | Animation helpers |\n| `@vielzeug/refine/styles/layers.css` | Cascade layer declarations |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser reset and FOUC suppression |\n\n## Components\n\nEach component uses one registration and type entry point:\n\n```ts\nimport '@vielzeug/refine/button';\nimport type { OreButtonProps } from '@vielzeug/refine/button';\n```\n\n| Area | Components |\n| --- | --- |\n| Content | `avatar`, `avatar-group`, `breadcrumb`, `card`, `carousel`, `chat-message`, `code-window`, `copy-command`, `icon`, `keyboard-key`, `list`, `list-item`, `marquee`, `pagination`, `separator`, `speech-player`, `stats`, `step`, `stepper`, `table`, `text` |\n| Disclosure | `accordion`, `accordion-item`, `tabs`, `tab-item`, `tab-panel` |\n| Feedback | `alert`, `async`, `badge`, `chip`, `cookie-banner`, `password-strength`, `progress`, `skeleton`, `toast`, `typing-indicator` |\n| Inputs | `button`, `button-group`, `calendar`, `checkbox`, `checkbox-group`, `combobox`, `counter`, `datagrid`, `date-picker`, `file-input`, `input`, `message-composer`, `number-input`, `otp-input`, `radio`, `radio-group`, `rating`, `select`, `slider`, `switch`, `textarea`, `time-picker` |\n| Layout | `box`, `grid`, `grid-item`, `navbar`, `sidebar` |\n| Overlays | `command-palette`, `dialog`, `drawer`, `menu`, `navigation-menu`, `popover`, `tooltip` |\n\nEach component page lists its attributes, JavaScript properties, events, slots, parts, and CSS custom properties.\n\n## Framework Types\n\n```ts\nimport type {} from '@vielzeug/refine/frameworks/elements';\nimport type {} from '@vielzeug/refine/frameworks/react';\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\nThe declarations cover every supported tag and derive component properties from Refine's authoritative `HTMLElementTagNameMap`. They provide types only; component registration still uses component subpaths.\n\n## Events and Form Controls\n\nForm controls expose `.value` or `.checked` and dispatch standard `input` and `change` events. Read the property from `event.currentTarget`.\n\nStateful overlays expose controlled `open`, optional `default-open`, and an `open-change` custom event with `{ open, reason }` detail. Component pages define additional detail fields and reasons.\n\n## Utilities\n\n### `createThemeController(options?)`\n\n```ts\nimport { createThemeController } from '@vielzeug/refine/theme';\n\nconst theme = createThemeController(); // starts in 'system' mode\ntheme.setPreference('dark');\ntheme.dispose();\n```\n\nOwns the light/dark/system theme for the document: a reactive `preference` signal, the effective `resolved` mode (`'dark' | 'light'` after resolving `system` against the OS), and the DOM application `styles/theme.css` expects: the `.dark` class plus the `color-scheme` property on the root element, so every `light-dark()` token resolves to the right branch. While in `system` mode the controller tracks the OS `prefers-color-scheme` via sentinel's `createMediaQuery`.\n\nThe controller does **not** persist the preference or expose an accent color: those are consumer concerns (wire `watch(controller.preference, save)` for persistence; set `--color-primary-hue` yourself). Options: `initial` (starting preference, default `'system'`), `root` (element carrying the classes, default `document.documentElement`), `target` (window whose `matchMedia` backs `system`). The handle is disposable: `dispose()`, `disposed`, `disposalSignal`, `[Symbol.dispose]`.\n\n### `eventFieldValue(event)` / `eventFieldChecked(event)`\n\n```ts\nimport { eventFieldChecked, eventFieldValue } from '@vielzeug/refine';\n\ninput.addEventListener('input', (event) => {\n const value = eventFieldValue(event); // string | undefined: never a blind cast\n});\n```\n\nGuarded readers for the standard `input`/`change` event pattern above: they read `value`/`checked` from `event.currentTarget` only when the target actually carries it, so a handler wired to the wrong element yields `undefined`/`false` instead of a crash or a lie. Exported from the package root, which registers no components.\n\n## Types\n\nComponent props follow the `Ore<Component>Props` naming pattern. Event maps follow `Ore<Component>Events`. Import both from the component subpath that owns them.\n\n`RefineElementMap`, `RefineReactIntrinsicElements`, and `RefineVueGlobalComponents` are exported from their respective `frameworks/*` type entry points.\n\n## Errors\n\n### `RefineError`\n\n```ts\nclass RefineError extends Error {\n constructor(message: string, opts?: ErrorOptions);\n}\n```\n\nBase class for public Refine errors. It preserves `cause` through `ErrorOptions`.\n",
6
+ "usage": "---\ntitle: 'Refine: Usage Guide'\ndescription: Register Refine components, configure properties, handle events, compose slots, and integrate frameworks.\n---\n\n[[toc]]\n\n## Basic Usage\n\nLoad the global design contract once, then register only the components used by the page.\n\n```ts\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/dialog';\n\nconst input = document.querySelector('ore-input');\nconst save = document.querySelector('ore-button');\n\nif (!(input instanceof HTMLElement) || !(save instanceof HTMLElement)) {\n throw new Error('Expected Refine controls');\n}\n\nsave.addEventListener('click', () => {\n console.log((input as HTMLElement & { value: string }).value);\n});\n```\n\nAdd the browser reset only when the application owns global element styling:\n\n```ts\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n## Set Attributes and Properties\n\nUse attributes for serializable HTML state.\n\n```html\n<ore-button variant=\"outline\" color=\"secondary\" size=\"lg\" disabled>\n Save changes\n</ore-button>\n```\n\nUse JavaScript properties for arrays, objects, callbacks, and controlled state.\n\n```ts\nimport '@vielzeug/refine/datagrid';\nimport type { OreDataGridProps } from '@vielzeug/refine/datagrid';\n\nconst grid = document.querySelector('ore-datagrid') as HTMLElement & OreDataGridProps;\ngrid.columns = [\n { key: 'name', label: 'Name', sortable: true },\n { key: 'role', label: 'Role' },\n];\ngrid.rows = [\n { id: '1', name: 'Ada', role: 'Admin' },\n { id: '2', name: 'Linus', role: 'Editor' },\n];\n```\n\n## Handle Events\n\nForm controls dispatch standard `input` and `change` events. Read their current property from `event.currentTarget`.\n\n```ts\nconst input = document.querySelector('ore-input');\ninput?.addEventListener('input', (event) => {\n const target = event.currentTarget as HTMLElement & { value: string };\n console.log(target.value);\n});\n```\n\nCustom events use kebab-case names and typed detail objects.\n\n```ts\nimport type { OreDialogEvents } from '@vielzeug/refine/dialog';\n\nconst dialog = document.querySelector('ore-dialog');\ndialog?.addEventListener('open-change', (event: CustomEvent<OreDialogEvents['open-change']>) => {\n console.log(event.detail.open, event.detail.reason);\n});\n```\n\n## Compose Slots\n\nDefault slots hold primary content. Named slots identify component-owned regions.\n\n```html\n<ore-card>\n <span slot=\"header\">Account</span>\n <p>Profile settings</p>\n <div slot=\"footer\">\n <ore-button variant=\"ghost\">Cancel</ore-button>\n <ore-button>Save</ore-button>\n </div>\n</ore-card>\n```\n\nInput components commonly expose `prefix`, `suffix`, `label`, `helper`, and `error` slots. Each component reference lists its supported slots.\n\n## Load TypeScript Framework Declarations\n\nImport the declaration matching the framework once in an application type entry point.\n\n::: code-group\n\n```ts [DOM]\nimport type {} from '@vielzeug/refine/frameworks/elements';\n```\n\n```ts [React]\nimport type {} from '@vielzeug/refine/frameworks/react';\n```\n\n```ts [Vue]\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\n:::\n\nThese entry points provide typed tags and component properties. They do not register components or install runtime adapters.\n\n## Framework Integration\n\nRefine elements use native DOM properties, events, and slots. Frameworks differ only in how they forward custom-element properties and events.\n\n::: code-group\n\n```tsx [React 19]\nimport '@vielzeug/refine/button';\n\nexport function SaveButton() {\n return <ore-button variant=\"solid\">Save</ore-button>;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport '@vielzeug/refine/button';\n</script>\n\n<template>\n <ore-button variant=\"solid\">Save</ore-button>\n</template>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import '@vielzeug/refine/button';\n</script>\n\n<ore-button variant=\"solid\">Save</ore-button>\n```\n\n:::\n\nSee [Framework Integration](./frameworks.md) for custom events, controlled properties, SSR guards, and framework-specific configuration.\n\n## Working with Other Vielzeug Libraries\n\n### Ore\n\nUse Ore when authoring a custom element that composes Refine components.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { define, html } from '@vielzeug/ore';\n\ndefine('save-panel', {\n setup() {\n return html`<ore-button @click=${() => console.log('save')}>Save</ore-button>`;\n },\n});\n```\n\n### Ripple\n\nUse Ripple to drive component properties from application state.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { effect, signal } from '@vielzeug/ripple';\n\nconst loading = signal(false);\nconst button = document.querySelector('ore-button') as HTMLElement & { loading?: boolean };\nconst loadingEffect = effect(() => {\n button.loading = loading.value;\n});\n\nloadingEffect.dispose();\n```\n\n## Best Practices\n\n- **Import** `tokens.css` once before rendering components.\n- **Register** components through explicit subpaths.\n- **Set** structured data and callbacks as JavaScript properties.\n- **Read** form state from the event target instead of custom event payloads.\n- **Provide** accessible labels for icon-only controls.\n- **Use** component reference pages for supported slots, parts, and CSS properties.\n- **Dispose** application-owned Ripple effects and service handles.\n- **Guard** DOM-dependent component registration during SSR.\n- **Working with AI agents?** Install the `vielzeug` skill (`npx -y @vielzeug/codex skills install`) so agents reach for Refine components and tokens instead of custom widgets: see the [Codex usage guide](../codex/usage.md#agent-skill).\n"
7
7
  },
8
8
  "examples": [],
9
9
  "typeSignatures": {
10
- "RefineError": "export { RefineError } from './errors';"
10
+ "RefineError": "export { RefineError } from './errors';",
11
+ "eventFieldChecked": "export { eventFieldChecked, eventFieldValue } from './inputs/shared/native-field-event';",
12
+ "eventFieldValue": "export { eventFieldChecked, eventFieldValue } from './inputs/shared/native-field-event';"
11
13
  }
12
14
  }