Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ecosystem-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- run: pnpm test:ecosystem
env:
ECOSYSTEM_DEVTOOLS_REF: ${{ inputs.ref }}
ECOSYSTEM_DEVFRAME_REF: ${{ inputs.ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: failure()
uses: actions/upload-artifact@v4
Expand Down
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

**`devframe`** is the framework-neutral container for one devtool integration, portable across viewers. Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/spa/embedded outputs) without caring how it'll be displayed. A devframe app runs standalone (CLI, static deploy, embedded SPA) just as well as it mounts inside a hub.

**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. See `examples/minimal-vite-devframe-hub/` for a working ~120-line Vite host demonstrating the protocol end to end.

## Stack & Structure

ESM TypeScript library. Bundled with `tsdown`. Tested with `vitest`. pnpm workspaces with catalog dependencies (`pnpm-workspace.yaml`); workspace globs reserve `playground`, `docs`, `packages/*`, `examples/*` for future additions.
Expand Down Expand Up @@ -50,6 +52,16 @@ All node-side warnings and errors use structured diagnostics via [`nostics`](htt

Prefix: **`DF`**. Codes are sequential 4-digit numbers (e.g. `DF0033`). Check the existing diagnostics file to find the next available number.

Range allocation:
- `DF00xx–DF07xx` — `devframe` core (RPC, host, storage, streams, …)
- `DF80xx–DF89xx` — `@devframes/hub`. Sub-ranges:
- `DF80xx` — hub context / lifecycle
- `DF81xx` — docks
- `DF82xx` — terminals
- `DF83xx` — messages
- `DF84xx` — commands
- `DF85xx` — built-in RPC commands

### Adding a new error

1. **Define the code** in the appropriate `diagnostics.ts`:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</p>

<p align="center">
Framework-neutral foundation for building generic DevTools.
Framework-neutral foundation for building devframes.
</p>

<p align="center">
Expand Down
5 changes: 5 additions & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export const alias = {
'devframe/helpers/vite': r('devframe/src/helpers/vite.ts'),
'devframe/adapters/embedded': r('devframe/src/adapters/embedded.ts'),
'devframe/adapters/mcp': r('devframe/src/adapters/mcp/index.ts'),
'@devframes/hub/client': r('hub/src/client/index.ts'),
'@devframes/hub/constants': r('hub/src/constants.ts'),
'@devframes/hub/node': r('hub/src/node/index.ts'),
'@devframes/hub/types': r('hub/src/types/index.ts'),
'@devframes/hub': r('hub/src/index.ts'),
'@devframes/nuxt/runtime/plugin.client': r('nuxt/src/runtime/plugin.client.ts'),
'@devframes/nuxt': r('nuxt/src/index.ts'),
'devframe/recipes/open-helpers': r('devframe/src/recipes/open-helpers.ts'),
Expand Down
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function guideItems(prefix: string): DefaultTheme.NavItemWithLink[] {
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
{ text: 'Client', link: `${prefix}/guide/client` },
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
{ text: 'Hub (multi-tool)', link: `${prefix}/guide/hub` },
{ text: 'Agent-Native (experimental)', link: `${prefix}/guide/agent-native` },
]
}
Expand Down Expand Up @@ -98,7 +99,7 @@ export function devframeNav(prefix = ''): DefaultTheme.NavItem[] {

export default withMermaid(defineConfig({
title: 'Devframe',
description: 'Framework-neutral foundation for building generic DevTools — RPC layer, hosts, and adapters.',
description: 'Framework-neutral foundation for building generic devframes — RPC layer, hosts, and adapters.',
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],
['link', { rel: 'apple-touch-icon', href: '/logo.svg' }],
Expand Down
4 changes: 2 additions & 2 deletions docs/adapters/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ Running the resulting binary:
my-devframe # dev server at http://localhost:9999/
my-devframe --port 8080
my-devframe build --out-dir dist-static
my-devframe build --out-dir dist-static --base /devtools/
my-devframe build --out-dir dist-static --base /devframe/
my-devframe mcp # stdio MCP server (experimental)
```

Standalone CLI serves the SPA at `/` by default. The `/__devtools/` prefix is for *hosted* adapters where devframe mounts alongside an existing app — see [Mount paths](./#mount-paths).
Standalone CLI serves the SPA at `/` by default. The `/__devframe/` prefix is for *hosted* adapters where devframe mounts alongside an existing app — see [Mount paths](./#mount-paths).

## Options

Expand Down
2 changes: 1 addition & 1 deletion docs/adapters/embedded.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ await createEmbedded(devframe, { ctx: existingCtx })

| Option | Required | Description |
|--------|----------|-------------|
| `ctx` | ✓ | Target `DevToolsNodeContext` the devframe is registered into. |
| `ctx` | ✓ | Target `DevframeNodeContext` the devframe is registered into. |

Useful when a host loads devframes based on runtime conditions (feature flags, user opt-in, dynamic discovery) rather than static config.
2 changes: 1 addition & 1 deletion docs/errors/DF0006.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Register the function with `ctx.rpc.register(defineRpcFunction({ name }))` befor

## Source

- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `RpcFunctionsHost.invokeLocal()` throws `DF0006` when the requested method has not been registered on this host.
- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `RpcFunctionsHost.invokeLocal()` throws `DF0006` when the requested method has not been registered on this host.
4 changes: 2 additions & 2 deletions docs/errors/DF0007.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ outline: deep

## Message

> AsyncLocalStorage is not set, it likely to be an internal bug of the DevTools foundation
> AsyncLocalStorage is not set, it likely to be an internal bug of the Devframe foundation

## Cause

Expand All @@ -18,4 +18,4 @@ Only call `getCurrentRpcSession()` from RPC handlers executed by the server. Rep

## Source

- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `getCurrentRpcSession()` throws `DF0007` when called outside the RPC dispatch async context.
- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `getCurrentRpcSession()` throws `DF0007` when called outside the RPC dispatch async context.
4 changes: 2 additions & 2 deletions docs/errors/DF0008.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ outline: deep

## Cause

`DevToolsViewHost.hostStatic()` was asked to mount a directory that doesn't exist on disk.
`DevframeViewHost.hostStatic()` was asked to mount a directory that doesn't exist on disk.

## Fix

Verify the `distDir` path resolves correctly (run your SPA build first, and check the resolved absolute path).

## Source

- [`packages/devframe/src/node/host-views.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-views.ts) — `DevToolsViewHost.hostStatic()` throws `DF0008` when the resolved `distDir` does not exist on disk.
- [`packages/devframe/src/node/host-views.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-views.ts) — `DevframeViewHost.hostStatic()` throws `DF0008` when the resolved `distDir` does not exist on disk.
2 changes: 1 addition & 1 deletion docs/errors/DF0012.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Delete the file to reset to defaults, or investigate how it became malformed.

## Source

- [`packages/devframe/src/node/storage.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/storage.ts) — `createStorage()` catches `JSON.parse` errors and logs `DF0012` (with the cause attached) before falling back to `initialValue`.
- [`packages/devframe/src/node/storage.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/storage.ts) — `createStorage()` catches `JSON.parse` errors and logs `DF0012` (with the cause attached) before falling back to `initialValue`.
2 changes: 1 addition & 1 deletion docs/errors/DF0013.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Pass `initialValue` on the first call: `ctx.rpc.sharedState.get(key, { initialVa

## Source

- [`packages/devframe/src/node/rpc-shared-state.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/rpc-shared-state.ts) — `RpcSharedStateHost.get()` throws `DF0013` when neither an existing entry nor an `initialValue` is provided for a key.
- [`packages/devframe/src/node/rpc-shared-state.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/rpc-shared-state.ts) — `RpcSharedStateHost.get()` throws `DF0013` when neither an existing entry nor an `initialValue` is provided for a key.
2 changes: 1 addition & 1 deletion docs/errors/DF0014.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ If you didn't intend for this function to be agent-exposed, remove the `agent` f

## Source

- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.
- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.
2 changes: 1 addition & 1 deletion docs/errors/DF0015.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ ctx.agent.registerTool({ id: 'my-tool', /* new config */ })

## Source

- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerTool()` throws `DF0015` when the tool id collides with an existing agent tool or an RPC function's `agent` exposure.
- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerTool()` throws `DF0015` when the tool id collides with an existing agent tool or an RPC function's `agent` exposure.
2 changes: 1 addition & 1 deletion docs/errors/DF0016.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ Pick a distinct id or unregister the existing resource first via the handle retu

## Source

- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerResource()` throws `DF0016` when the resource id is already registered on the host.
- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerResource()` throws `DF0016` when the resource id is already registered on the host.
2 changes: 1 addition & 1 deletion docs/errors/DF0017.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ The agent-native surface is experimental and may change without a major version

## Source

- [`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/adapters/mcp/build-server.ts) — `createMcpServer()` throws `DF0017` when the requested transport is unsupported or when the underlying transport fails to `connect()`.
- [`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/adapters/mcp/build-server.ts) — `createMcpServer()` throws `DF0017` when the requested transport is unsupported or when the underlying transport fails to `connect()`.
2 changes: 1 addition & 1 deletion docs/errors/DF0019.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ defineRpcFunction({

## Source

- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.
- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.
2 changes: 1 addition & 1 deletion docs/errors/DF0020.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ Or convert the payload to a JSON-safe shape (e.g. an array of entries, an ISO st

## Source

- [`packages/devframe/src/rpc/serialization.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/serialization.ts) — the strict JSON serializer throws `DF0020` (with the offending path and runtime type) when a `jsonSerializable: true` payload contains a non-JSON value.
- [`packages/devframe/src/rpc/serialization.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/serialization.ts) — the strict JSON serializer throws `DF0020` (with the offending path and runtime type) when a `jsonSerializable: true` payload contains a non-JSON value.
2 changes: 1 addition & 1 deletion docs/errors/DF0021.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ ctx.rpc.register(defineRpcFunction({ name: 'my-plugin:fn', handler: () => 1 }),

## Source

- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0021` when an RPC name is already registered and `force` is not set.
- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0021` when an RPC name is already registered and `force` is not set.
2 changes: 1 addition & 1 deletion docs/errors/DF0022.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Call `ctx.rpc.register()` first, or pass `force: true` to `update()` to register

## Source

- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.update()` throws `DF0022` when the named function was never registered.
- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.update()` throws `DF0022` when the named function was never registered.
2 changes: 1 addition & 1 deletion docs/errors/DF0023.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Confirm the function name matches a registration. RPC names are namespaced — t

## Source

- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — collector `get()`/lookup paths throw `DF0023` when consumers ask for a function that has not been registered.
- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — collector `get()`/lookup paths throw `DF0023` when consumers ask for a function that has not been registered.
4 changes: 2 additions & 2 deletions docs/errors/DF0024.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ Add either `handler: ...` directly on the definition, or `setup: ctx => ({ handl

## Source

- [`packages/devframe/src/rpc/handler.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/handler.ts) — invocation throws `DF0024` when neither `handler` nor a `setup` returning `{ handler }` is provided.
- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — dump generation also requires a handler and throws `DF0024` if the definition is incomplete.
- [`packages/devframe/src/rpc/handler.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/handler.ts) — invocation throws `DF0024` when neither `handler` nor a `setup` returning `{ handler }` is provided.
- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — dump generation also requires a handler and throws `DF0024` if the definition is incomplete.
2 changes: 1 addition & 1 deletion docs/errors/DF0025.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Re-run `createBuild` to regenerate the dump, or check that the call site uses th

## Source

- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0025` when a client calls a function name that is not present in the baked dump store.
- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0025` when a client calls a function name that is not present in the baked dump store.
2 changes: 1 addition & 1 deletion docs/errors/DF0026.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ defineRpcFunction({

## Source

- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0026` when none of the pre-computed inputs matches the call's args and no `fallback` was configured.
- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0026` when none of the pre-computed inputs matches the call's args and no `fallback` was configured.
2 changes: 1 addition & 1 deletion docs/errors/DF0027.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Drop the `dump` field, or change the function `type` to `'static'` / `'query'` i

## Source

- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0027` when a `dump` field is attached to an `'action'` or `'event'` function.
- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0027` when a `dump` field is attached to an `'action'` or `'event'` function.
2 changes: 1 addition & 1 deletion docs/errors/DF0028.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Remove `snapshot: true`, or change the function `type` to `'query'`.

## Source

- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0028` when `snapshot: true` is set on a function whose type is not `'query'`.
- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0028` when `snapshot: true` is set on a function whose type is not `'query'`.
2 changes: 1 addition & 1 deletion docs/errors/DF0029.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ This is a soft warning — the stream keeps running and remaining chunks still f

## Source

- [`packages/devframe/src/client/rpc-streaming.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/client/rpc-streaming.ts) — the client subscription queue logs `DF0029` (with the dropped chunk count) when buffered chunks exceed `highWaterMark`.
- [`packages/devframe/src/client/rpc-streaming.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/client/rpc-streaming.ts) — the client subscription queue logs `DF0029` (with the dropped chunk count) when buffered chunks exceed `highWaterMark`.
2 changes: 1 addition & 1 deletion docs/errors/DF0030.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ A client subscribed to a stream id that the server-side channel doesn't know abo

## Source

- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — the streaming subscribe/unsubscribe paths log `DF0030` when a client references an `id` that no producer has started (and no replay buffer covers).
- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — the streaming subscribe/unsubscribe paths log `DF0030` when a client references an `id` that no producer has started (and no replay buffer covers).
2 changes: 1 addition & 1 deletion docs/errors/DF0031.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ catch (err) {

## Source

- [`packages/devframe/src/utils/streaming-channel.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/utils/streaming-channel.ts) — `stream.write()` throws `DF0031` when called after the stream has been closed, errored, or aborted by the consumer.
- [`packages/devframe/src/utils/streaming-channel.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/utils/streaming-channel.ts) — `stream.write()` throws `DF0031` when called after the stream has been closed, errored, or aborted by the consumer.
Loading
Loading