npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@powerduck/openapi-request

v0.2.16

Published

OpenAPI 3.2 collection debugger with HTTP, SSE and WebSocket support, plus response write-back

Readme

@powerduck/openapi-request

npm version license downloads website

OpenAPI 3.2 collection debugger with first-class support for HTTP, SSE, WebSocket, GraphQL, gRPC, and MCP. createClient() is the single UI-facing surface: it answers what the UI needs before sending (prepare), runs one-shot calls (send), opens long-lived sessions (connect), discovers schemas (discover), and writes live responses back into the OpenAPI document (writeback).


Install

npm install @powerduck/openapi-request

Quick Start

Prepare and send one operation

import { createClient } from "@powerduck/openapi-request";

const client = createClient();

// Inspect the request without sending — protocol, transport, stream kind,
// and display mode are all derived from the operation's x-extensions.
const prepared = client.prepare({
  spec,
  target: { operationId: "getUserById" },
  values: { path: { id: "123" }, query: { include: "profile" } },
});

console.log(prepared.protocol);      // "http"
console.log(prepared.stream.kind);   // "none" | "sse" | "websocket" | ...
console.log(prepared.display.mode);  // "response" | "event-list" | "duplex-session"

// Send the call. The result includes a derived OpenAPI response fragment
// and, by default, a deep copy of the spec with that response merged in.
const result = await client.send({
  spec,
  target: { operationId: "getUserById" },
  values: { path: { id: "123" } },
});

console.log(result.responseStatusCode); // "200"
console.log(result.patchedSpec);        // spec with the 200 response written back

Batch replay with schema inference

const results = client.sendMany(
  spec,
  [
    { target: { operationId: "listUsers" }, values: { query: { limit: 10 } } },
    { target: { operationId: "createUser" }, values: { requestBody: { name: "Ada" } } },
  ],
  { serverUrl: "https://api.example.com" },
);

Long-lived sessions (WebSocket / MCP / gRPC bidi)

const session = client.connect({
  spec,
  target: { operationId: "chatStream" },
  // ...protocol-specific options (websocket / mcp / grpc)
});

session.on("message", (msg) => console.log(msg));
await session.send({ text: "hello" });
await session.close();

Discover MCP / gRPC capabilities

const mcpCaps = await client.discover({ protocol: "mcp", url: "http://localhost:3000/mcp" });
const grpcCaps = await client.discover({ protocol: "grpc", endpoint: "localhost:50051" });

Write a live response back into the spec

const prepared = client.prepare({ spec, target: { operationId: "getUser" } });
const result = await client.send({ spec, target: { operationId: "getUser" } });

const patched = client.writeback(spec, prepared, result);
// `patched` is a new spec object with the response merged under the
// operation's 200 (or observed status) response.

Links


Protocols

| Protocol | Transport | Stream kind | |-------------|----------------------|------------------------------| | HTTP | HTTP/1.1, HTTP/2 | none, sse, ndjson, chunked | | WebSocket | ws / wss | websocket (duplex session) | | GraphQL | HTTP POST | none, graphql-stream | | gRPC | HTTP/2 | unary, server/client/bidi stream | | MCP | streamable-http, stdio| mcp-http-stream, mcp-stdio |

The protocol is derived from the operation's x-protocol extension ("http" | "sse" | "websocket" | "graphql" | "grpc" | "mcp"). When omitted, it defaults to "http". The shorthand "ws" is accepted and normalized to "websocket".


API Reference

createClient(options?)

| Option | Type | Description | |--------|------|-------------| | writeBack | WriteBackOptions | Defaults for the response write-back step | | response | ToResponseOptions | Defaults for deriving an OpenAPI Response Object from a live call |

Returns a ProtoClient:

| Method | Signature | Description | |--------|-----------|-------------| | prepare | (opts: SendOptions) => PreparedRequest | Resolve protocol, transport, stream kind, and display mode without sending | | send | (opts: SendOptions) => Promise<SendResult> | Execute one operation and run the full write-back pipeline | | sendMany | (spec, targets, shared?) => Promise<SendResult[]> | Batch replay across operations, accumulating inferred schemas | | connect | (opts: ManualSessionOptions) => AnyManualSession | Open a long-lived WebSocket / MCP / gRPC session | | discover | (opts) => Promise<any> | Discover MCP tools/resources/prompts or gRPC services | | writeback | (spec, prepared, result, opts?) => OpenApiDocument | Merge one call's response into a spec copy | | dispose | () => void | Release client-level resources |

SendOptions

| Field | Type | Description | |-------|------|-------------| | spec | OpenApiDocument | The complete OpenAPI 3.2 document (required) | | target | { path?, method?, operationId? } | Which operation to run (required) | | values | RequestValues | Path / query / header / cookie / requestBody values | | serverUrl | string | Override spec.servers[0].url | | variables | Record<string, string> | Server variable values | | auth | AuthConfig | Bearer / API key / Basic / custom scheme values | | timeout | number | Per-request timeout (ms) | | writeBack | boolean | Set false to skip spec patching (still produces responseFragment) | | websocket / graphql / mcp / grpc | protocol options | Per-protocol configuration |

SendResult

| Field | Type | Description | |-------|------|-------------| | responseStatusCode | string | Observed HTTP / RPC status | | responseFragment | any | OpenAPI 3.2 Response Object derived from the live call | | patchedSpec | OpenApiDocument \| undefined | Deep copy of the spec with the response merged in | | writeBackSkippedReason | string | Why write-back did not happen (when applicable) |

PreparedRequest

Returned by prepare(). Carries the resolved protocol, transport, display.mode, stream.kind, and OpenAPI extensions the UI needs to pick a renderer before any bytes are sent.


Utility Exports

| Function | Description | |----------|-------------| | locateOperation(spec, target) | Find an operation by operationId or path+method | | inferSchema(value) / inferSchemaFromMany(values) | Infer a JSON Schema from sample values | | mergeSchema(base, incoming) | Merge an inferred schema into an existing one | | sampleFromSchema(schema) | Produce an example value from a JSON Schema | | toResponseObject(result, options?) | Convert a live call into an OpenAPI Response Object | | writeBackResponse(spec, path, method, fragment, options?) | Merge a response fragment into a spec copy | | probeStreamingResponse(response) | Detect SSE / chunked / ndjson on a raw fetch Response |

| Class / Type | Description | |--------------|-------------| | ProtoKitError | Structured error carrying status, headers, and parsed body | | HttpAdapter | Low-level HTTP adapter | | SseParser | SSE event stream parser | | GraphQLAdapter | GraphQL operation runner | | McpAdapter | MCP client adapter | | AdapterRegistry | Register custom protocol adapters |


Links

License

MIT © POWERDUCK LIMITED

0.2.13 — GraphQL response bounds and cancellation

GraphQL forwards pre-aborted signals, enforces maxResponseSize while reading JSON and SSE bodies, and honors maxStreamMs for streams. Response sizes count UTF-8 bytes. Body readers are cancelled and released on completion or failure. Successful non-JSON or malformed JSON responses report GRAPHQL_INVALID_RESPONSE rather than appearing successful.

0.2.14 — bounded schema sampling

Example generation now shares a 10,000-node work budget across recursive branches and caps explicitly increased recursion depth at 128. Small nested array schemas can no longer expand exponentially until the host exhausts its heap. Exhausted branches yield null, as depth-limited branches already do; ordinary bounded samples retain their existing shape.

Multipart uploads and repeated fields

The Node runtime supplies fs as its default file resolver. Multipart file descriptors such as { __file: "/absolute/path/file.txt" } stream the selected file instead of silently omitting it. Missing/non-file upload sources fail before dispatch; file validation honors cancellation and a bounded timeout. A supplied runner.fileResolver is used as-is and must implement stat and createReadStream (plus readFile when loading certificates); it never inherits local filesystem access implicitly.

For repeated multipart or URL-encoded fields, pass ordered entries in values.body: [["tag", "one"], ["tag", "two"], ["logo", {"__file":"/absolute/path/logo.png"}]]. Object bodies remain supported for unique names. File paths are local to the process running the request; a remote MCP/CLI host needs files on that host. HTTP TRACE, QUERY, WebDAV, and custom OpenAPI 3.2 methods are covered by wire-level regression tests.

GET bodies and response limits

Explicit request bodies are retained for GET and HEAD by setting the generated collection item's protocolProfileBehavior.disableBodyPruning to true. GET supports JSON, URL-encoded and multipart file bodies; server and intermediary support still determines whether an endpoint accepts them. HEAD retains its normal response semantics (no response body).

runner.requester.maxResponseSize is the hard HTTP response limit in bytes. Omit it for no requester-level size limit; positive values are configurable, and zero is rejected. Limit errors carry code: "maxResponseSize" and include the configured byte threshold. The separate top-level maxResponseSize controls response sampling. Powerduck Desktop applies its own default (2 MiB) and maximum (32 MiB).