@hyperttp/core 1.5.5 → 2.0.0

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 (85) hide show
  1. package/Core/HyperCore.d.ts +176 -141
  2. package/Core/HyperCore.d.ts.map +1 -1
  3. package/Core/HyperCore.js +1 -1
  4. package/Core/HyperCore.js.map +3 -3
  5. package/README.md +260 -316
  6. package/defaultConfig.d.ts +9 -4
  7. package/defaultConfig.d.ts.map +1 -1
  8. package/defaultConfig.js +1 -1
  9. package/defaultConfig.js.map +2 -2
  10. package/index.d.ts +3 -0
  11. package/index.d.ts.map +1 -1
  12. package/index.js +1 -1
  13. package/index.js.map +2 -2
  14. package/package.json +39 -33
  15. package/protocols/manager.d.ts +31 -0
  16. package/protocols/manager.d.ts.map +1 -0
  17. package/protocols/manager.js +2 -0
  18. package/protocols/manager.js.map +7 -0
  19. package/protocols/rest/index.d.ts +9 -0
  20. package/protocols/rest/index.d.ts.map +1 -0
  21. package/protocols/rest/index.js +2 -0
  22. package/protocols/rest/index.js.map +7 -0
  23. package/protocols/rest/receiver.d.ts +31 -0
  24. package/protocols/rest/receiver.d.ts.map +1 -0
  25. package/protocols/rest/receiver.js +2 -0
  26. package/protocols/rest/receiver.js.map +7 -0
  27. package/protocols/rest/sender.d.ts +19 -0
  28. package/protocols/rest/sender.d.ts.map +1 -0
  29. package/protocols/rest/sender.js +2 -0
  30. package/protocols/rest/sender.js.map +7 -0
  31. package/protocols/rest/type.d.ts +71 -0
  32. package/protocols/rest/type.d.ts.map +1 -0
  33. package/protocols/rest/type.js +2 -0
  34. package/protocols/rest/type.js.map +7 -0
  35. package/rest.d.ts +2 -0
  36. package/rest.d.ts.map +1 -0
  37. package/rest.js +2 -0
  38. package/rest.js.map +7 -0
  39. package/transports/fetch.d.ts +24 -0
  40. package/transports/fetch.d.ts.map +1 -0
  41. package/transports/fetch.js +2 -0
  42. package/transports/fetch.js.map +7 -0
  43. package/transports/manager.d.ts +17 -80
  44. package/transports/manager.d.ts.map +1 -1
  45. package/transports/manager.js +1 -6
  46. package/transports/manager.js.map +3 -3
  47. package/utils/abort.d.ts +23 -0
  48. package/utils/abort.d.ts.map +1 -0
  49. package/utils/abort.js +2 -0
  50. package/utils/abort.js.map +7 -0
  51. package/utils/errors.d.ts +12 -2
  52. package/utils/errors.d.ts.map +1 -1
  53. package/utils/errors.js +1 -1
  54. package/utils/errors.js.map +3 -3
  55. package/utils/merge.d.ts.map +1 -1
  56. package/utils/merge.js +1 -1
  57. package/utils/merge.js.map +2 -2
  58. package/Core/RequestBuilder.d.ts +0 -32
  59. package/Core/RequestBuilder.d.ts.map +0 -1
  60. package/Core/RequestBuilder.js +0 -2
  61. package/Core/RequestBuilder.js.map +0 -7
  62. package/transports/browser.d.ts +0 -37
  63. package/transports/browser.d.ts.map +0 -1
  64. package/transports/browser.js +0 -2
  65. package/transports/browser.js.map +0 -7
  66. package/transports/node.d.ts +0 -33
  67. package/transports/node.d.ts.map +0 -1
  68. package/transports/node.js +0 -2
  69. package/transports/node.js.map +0 -7
  70. package/utils/normalize.d.ts +0 -35
  71. package/utils/normalize.d.ts.map +0 -1
  72. package/utils/normalize.js +0 -2
  73. package/utils/normalize.js.map +0 -7
  74. package/utils/pipeline.d.ts +0 -63
  75. package/utils/pipeline.d.ts.map +0 -1
  76. package/utils/pipeline.js +0 -2
  77. package/utils/pipeline.js.map +0 -7
  78. package/utils/response.d.ts +0 -113
  79. package/utils/response.d.ts.map +0 -1
  80. package/utils/response.js +0 -2
  81. package/utils/response.js.map +0 -7
  82. package/utils/retryUtils.d.ts +0 -17
  83. package/utils/retryUtils.d.ts.map +0 -1
  84. package/utils/retryUtils.js +0 -2
  85. package/utils/retryUtils.js.map +0 -7
package/README.md CHANGED
@@ -1,419 +1,363 @@
1
- # @hyperttp/core ⚡
1
+ # @hyperttp/core
2
2
 
3
- > English | [Русский](https://github.com/IT-IF-OR/hyperttp-core/tree/main/lang/ru)
3
+ > Protocol-agnostic execution core for client and server communication.
4
4
 
5
+ **English** | [Русский](./lang/ru/README.md)
6
+
7
+ [![CI](https://github.com/IT-IF-OR/hyperttp-core/actions/workflows/ci.yml/badge.svg)](https://github.com/IT-IF-OR/hyperttp-core/actions/workflows/ci.yml)
5
8
  [![npm version](https://img.shields.io/npm/v/@hyperttp/core)](https://www.npmjs.com/package/@hyperttp/core)
6
9
  [![npm downloads](https://img.shields.io/npm/dm/@hyperttp/core)](https://www.npmjs.com/package/@hyperttp/core)
7
10
  [![bundle size](https://img.shields.io/bundlephobia/minzip/@hyperttp/core)](https://bundlephobia.com/package/@hyperttp/core)
8
11
  [![license](https://img.shields.io/npm/l/@hyperttp/core)](./LICENSE)
9
12
  [![typescript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
10
13
 
11
- ---
12
-
13
- ## 🌐 Language
14
-
15
- - 🇺🇸 English
16
- - 🇷🇺 [Русский](https://github.com/IT-IF-OR/hyperttp-core/tree/main/lang/ru)
17
-
18
- ---
19
-
20
- ## 🔗 Quick Links
21
-
22
- - 🏢 **Organization:** [github.com/IT-IF-OR](https://github.com/IT-IF-OR)
23
- - 👤 **Author:** [github.com/dirold2](https://github.com/dirold2)
24
- - 📦 **npm:** [npmjs.com/org/hyperttp](https://www.npmjs.com/org/hyperttp)
25
- - 🚀 **High-level client:** [`hyperttp`](https://www.npmjs.com/package/hyperttp) (all plugins pre-wired)
26
-
27
- ---
28
-
29
- **@hyperttp/core** is the low-level, high-performance engine of the Hyperttp ecosystem.
30
- It provides a thin, optimized HTTP core with an intelligent transport layer and an advanced
31
- pipeline-based plugin architecture — the foundation upon which the feature-rich [`hyperttp`](https://www.npmjs.com/package/hyperttp) client is built.
32
-
33
- > 💡 **Looking for a batteries-included client?** Install [`hyperttp`](https://www.npmjs.com/package/hyperttp) instead — it wraps `@hyperttp/core` with caching, rate limiting, queueing, parsing, and more.
34
-
35
- ---
36
-
37
- ## 🔥 Key Features
38
-
39
- - **⚡ Zero-Overhead Hot Paths:** Fast header merging, response mapping, and object creation loops optimized for high-throughput environments. Object pooling for `InternalRequest` eliminates allocations in the hot path.
40
- - **📍 LRU URL Caching:** Built-in string URL parsing cache (up to 512 entries) with automatic eviction to bypass repetitive `new URL()` overhead.
41
- - **🔀 Intelligent Isomorphism:** Automatically switches between optimal underlying transports (Native Bun, Undici, or Node.js native `fetch`) while maintaining a unified, clean API.
42
- - **🔌 Multi-Stage Hooks & Pipelines:** Granular control over the request lifecycle with sorted hook priorities and short-circuit capabilities.
43
- - **🗜️ Transparent Decompression:** Automatic out-of-the-box handling of `gzip`, `deflate`, and `br` (Brotli) content-encodings for both standard `Uint8Array` buffers and `ReadableStream` data.
44
- - **💎 Prototype Preservation:** Safe internal mapping that respects and carries forward custom prototypes passed via request configurations.
45
- - **🛡️ Security Hardening:** Response body size limiting (`maxResponseBytes`), CRLF injection prevention in headers, SSL certificate validation enforcement, and concurrent request throttling (`maxConcurrent`).
46
- - **📦 Tiny Footprint:** ~30.7 KB raw / ~9.6 KB gzip after minification — no unnecessary dependencies.
47
- - **🦀 Rust-Powered Toolchain:** Built and linted with OXC (`oxlint` + `oxfmt`) for blazing-fast development cycles.
48
-
49
- ---
50
-
51
- ## 📦 Ecosystem Packages
52
-
53
- | Package | Description | Repository |
54
- | ---------------------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------- |
55
- | **`@hyperttp/core`** (you are here) | Low-level core with transport pipeline | [GitHub](https://github.com/IT-IF-OR/hyperttp-core) |
56
- | [`@hyperttp/types`](https://www.npmjs.com/package/@hyperttp/types) | Shared TypeScript type definitions | [GitHub](https://github.com/dirold2/hyperttp-types) |
57
- | [`@hyperttp/transport-bun`](https://www.npmjs.com/package/@hyperttp/transport-bun) | Native Bun transport | [GitHub](https://github.com/IT-IF-OR/hyperttp-transport-bun) |
58
- | [`@hyperttp/transport-undici`](https://www.npmjs.com/package/@hyperttp/transport-undici) | Undici transport for Node.js | [GitHub](https://github.com/IT-IF-OR/hyperttp-transport-undici) |
59
- | [`hyperttp`](https://www.npmjs.com/package/hyperttp) | High-level client with all plugins pre-wired | [GitHub](https://github.com/dirold2/hyperttp) |
14
+ ## Positioning
15
+
16
+ `@hyperttp/core` is a small orchestration kernel, not another wrapper around `fetch` and not a
17
+ batteries-included HTTP framework. It connects three independent extension points:
18
+
19
+ - **Protocols** define communication semantics. A protocol can send through a `HyperSender`,
20
+ receive through a `HyperReceiver`, or implement both roles as a `HyperProtocol`.
21
+ - **Transports** own physical I/O. The current transport contract executes outgoing requests and can
22
+ additionally listen for incoming requests.
23
+ - **Plugins** add client request policy and cross-cutting behavior without expanding the core.
24
+
25
+ The core owns dispatch, lifecycle, plugin execution and resource coordination. REST is included as
26
+ the baseline protocol; additional protocols, optimized transports and higher-level behavior live in
27
+ separate packages.
28
+
29
+ ```text
30
+ Application
31
+ │
32
+ ├── Plugins: retry, cache, auth, tracing, metrics, logging, policy
33
+ │
34
+ HyperCore
35
+ │
36
+ ├── Protocol: prepare → send → parse
37
+ │ receive → handle → respond
38
+ │
39
+ └── Transport: execute (client) / listen (server)
40
+ Node.js / Bun / Deno / Browser / custom runtime
41
+ ```
60
42
 
61
- ---
43
+ This boundary is intentional. New client-side retries, caches, authentication strategies,
44
+ observability tools and other request policies should be plugins. New wire semantics should be
45
+ protocol packages. New I/O implementations should be transport packages.
62
46
 
63
- ## 🏗️ Architecture & Lifecycle
47
+ > Looking for a preconfigured HTTP client? Use
48
+ > [`hyperttp`](https://www.npmjs.com/package/hyperttp), which composes the core with application-level
49
+ > plugins and defaults.
64
50
 
65
- `HyperCore` shifts heavy lifting into structured execution pipelines:
51
+ ## Core properties
66
52
 
67
- ```
68
- ┌─────────────────────────────────────────────────────────────────┐
69
- │ HyperCore │
70
- │ │
71
- │ 1. Request Pipeline (onRequest) ─── short-circuit capable │
72
- │ │ │
73
- │ 2. Transport Execution (HyperTransport) │
74
- │ ├─ BunTransport (native Bun fetch) │
75
- │ ├─ UndiciTransport (Node.js) │
76
- │ └─ NodeTransport (global fetch) │
77
- │ │ │
78
- │ 3. Response Data Pipeline (onResponseData) │
79
- │ │ │
80
- │ 4. Internal Processing (decompression, mapping) │
81
- │ │ │
82
- │ 5. Response Pipeline (onResponse) │
83
- │ ├─ Mutators (sync/async modification) │
84
- │ └─ Side Effects (background, non-blocking) │
85
- │ │ │
86
- │ 6. Error Pipeline (onError) ─── recovery capable │
87
- └─────────────────────────────────────────────────────────────────┘
88
- ```
53
+ - Client and server orchestration.
54
+ - Protocol-independent request envelope and universal response shape.
55
+ - Client lifecycle: `prepare → send → parse`.
56
+ - Server lifecycle: `receive → handle → respond`.
57
+ - Per-protocol transport resolution and coordinated transport ownership.
58
+ - Blocking and background client plugin hooks.
59
+ - Runtime-aware transport selection with a browser-safe fetch fallback.
60
+ - Strict TypeScript contracts and module augmentation for typed protocol namespaces.
61
+ - No bundled runtime dependencies.
89
62
 
90
- ---
91
-
92
- ## 🚀 Quick Start
93
-
94
- ### Installation
63
+ ## Installation
95
64
 
96
65
  ```bash
97
- npm install @hyperttp/core
98
-
99
- # Optional: install a specific transport
100
- npm install @hyperttp/transport-bun # for Bun
101
- npm install @hyperttp/transport-undici # for Node.js
66
+ npm install @hyperttp/core @hyperttp/types
102
67
  ```
103
68
 
104
- ### Basic Usage
69
+ Compatible optimized transports can be installed separately. Ensure the selected transport release
70
+ declares compatibility with `@hyperttp/types@^0.3.0`; older transport releases target the v1 type
71
+ contracts and cannot be installed alongside core 2.0. Without an optional transport package, the
72
+ core uses its built-in `FetchTransport` fallback.
105
73
 
106
- `HyperCore` returns `HttpResponse<T>` objects with full control over the response body:
74
+ ## Client quick start
107
75
 
108
- ```typescript
76
+ ```ts
109
77
  import { HyperCore } from "@hyperttp/core";
110
78
 
111
- const http = new HyperCore({
112
- network: {
113
- userAgent: "MyApp/2.0",
114
- headers: { "X-Custom-Global": "Hyperttp" },
115
- },
79
+ const core = new HyperCore();
80
+
81
+ const response = await core.rest.get("https://example.com/users", {
82
+ query: { page: 1 },
116
83
  });
117
84
 
118
- // Returns HttpResponse<T> with full control
119
- const response = await http.get("https://api.example.com/data");
120
85
  console.log(response.status);
121
- console.log(response.headers);
86
+ console.log(response.data);
122
87
 
123
- // Built-in parsing methods
124
- const json = await response.json<User>();
125
- const text = await response.text();
126
- const buffer = await response.arrayBuffer();
127
-
128
- // Shortcut methods
129
- const userJson = await http.json<{ name: string }>("/users/123");
130
- const logsText = await http.text("/logs/latest");
131
-
132
- // Safely consume/drain a response without allocations
133
- await http.dump("/analytics/ping");
88
+ await core.destroy();
134
89
  ```
135
90
 
136
- ### Streaming
91
+ The same request can use the protocol-independent API:
137
92
 
138
- ```typescript
139
- // GET Streams
140
- const streamResponse = await http.stream("https://stream.example.com/audio");
141
- const reader = streamResponse.body.getReader();
142
-
143
- // POST Streams (e.g., LLM Completions / Audio Pipes)
144
- const chatStream = await http.postStream("/v1/chat/completions", {
145
- model: "gpt-4",
146
- stream: true,
93
+ ```ts
94
+ const response = await core.send({
95
+ protocol: "rest",
96
+ input: {
97
+ method: "GET",
98
+ url: "https://example.com/users",
99
+ query: { page: 1 },
100
+ },
147
101
  });
148
102
  ```
149
103
 
150
- ---
104
+ ## Server quick start
151
105
 
152
- ## 🔌 Plugin System
106
+ A transport may also expose `listen()`. `HyperCore` connects it to the receiver side of the selected
107
+ protocol:
153
108
 
154
- Extend `HyperCore` with custom hooks that integrate into the pipeline:
155
-
156
- ```typescript
109
+ ```ts
157
110
  import { HyperCore } from "@hyperttp/core";
158
111
 
159
- const http = new HyperCore();
160
-
161
- http.use({
162
- name: "performance-logger",
163
- priority: 100, // Sorted execution priority (higher runs first)
164
- mode: "background", // Forces onResponse to run as a non-blocking side-effect
165
-
166
- setup: (ctx) => {
167
- // Fired immediately on registration
168
- ctx.config.logger?.info?.("Plugin active");
169
- },
170
-
171
- onRequest: async (req, ctx) => {
172
- req.meta.startTime = performance.now();
173
- },
174
-
175
- onResponse: async (res, req, ctx) => {
176
- const duration = performance.now() - (req.meta.startTime as number);
177
- console.log(`[${req.method}] ${req.url} -> ${res.status} (${duration.toFixed(2)}ms)`);
178
- },
112
+ const core = new HyperCore();
179
113
 
180
- onError: async (error, req, ctx) => {
181
- console.error(`Request failed to ${req.url}: ${error.message}`);
182
- // Return a response object here to bypass/recover from the failure
114
+ const server = await core.listen({
115
+ protocol: "rest",
116
+ host: "127.0.0.1",
117
+ port: 3000,
118
+ handler(request) {
119
+ return {
120
+ status: 200,
121
+ body: {
122
+ method: request.method,
123
+ path: request.path,
124
+ },
125
+ };
183
126
  },
184
127
  });
128
+
129
+ // Closes active servers and releases every transport owned by this core.
130
+ await core.destroy();
185
131
  ```
186
132
 
187
- ### Plugin Phases
133
+ Protocols can be client-only, server-only or expose both roles. The current transport contract always
134
+ provides client `execute()` and can additionally provide server `listen()`.
188
135
 
189
- | Phase | Hook | Description |
190
- | ----------- | ---------------- | ------------------------------------------------------- |
191
- | **REQUEST** | `onRequest` | Intercepts before execution. Supports short-circuiting. |
192
- | **DATA** | `onResponseData` | Transforms raw transport responses before mapping. |
193
- | **FORMAT** | `onResponse` | Modifies client-facing responses. |
194
- | **ERROR** | `onError` | Catches and recovers from errors. |
136
+ ## Protocols
195
137
 
196
- > 💡 **Need caching, rate limiting, queueing, parsing, and more?** Use the [`hyperttp`](https://www.npmjs.com/package/hyperttp) meta-package — it comes with 8 pre-wired plugins.
138
+ REST is the only protocol implemented inside the core. External protocol modules are loaded lazily:
197
139
 
198
- ---
140
+ | Protocol | Package | Namespace |
141
+ | --------- | ------------------------------ | ---------------------------- |
142
+ | REST | built in | `core.rest` |
143
+ | GraphQL | `@hyperttp/protocol-graphql` | `core.graphql` |
144
+ | gRPC | `@hyperttp/protocol-grpc` | `core.grpc` |
145
+ | tRPC | `@hyperttp/protocol-trpc` | `core.trpc` |
146
+ | WebSocket | `@hyperttp/protocol-websocket` | `core.ws` / `core.websocket` |
147
+ | SSE | `@hyperttp/protocol-sse` | `core.sse` |
148
+ | MQTT | `@hyperttp/protocol-mqtt` | `core.mqtt` |
199
149
 
200
- ## 🌍 Transports
150
+ If an optional protocol package is missing, resolution fails with an installation hint instead of
151
+ silently selecting another protocol.
201
152
 
202
- Hyperttp automatically selects the optimal transport for your runtime:
153
+ ### Protocol roles
203
154
 
204
- | Transport | Runtime | Installation |
205
- | ------------------- | ----------------- | ---------------------------------------- |
206
- | **BunTransport** | Bun | `npm install @hyperttp/transport-bun` |
207
- | **UndiciTransport** | Node.js | `npm install @hyperttp/transport-undici` |
208
- | **NodeTransport** | Node.js / Browser | Built-in (uses global `fetch`) |
155
+ A unified protocol module can expose either or both roles:
209
156
 
210
- ### Custom Transport
157
+ ```ts
158
+ const protocol = {
159
+ protocol: "my-protocol",
160
+ sender: mySender, // optional client role
161
+ receiver: myReceiver, // optional server role
162
+ };
211
163
 
212
- Implement your own transport:
164
+ core.registerProtocol(protocol);
165
+ ```
213
166
 
214
- ```typescript
215
- import type { HyperTransport, TransportRequest, TransportResponse } from "@hyperttp/types";
167
+ A sender translates protocol input into a transport request and parses the transport response. A
168
+ receiver translates an incoming transport request into protocol input and serializes the application
169
+ response.
216
170
 
217
- class MyCustomTransport implements HyperTransport {
218
- async execute(req: TransportRequest): Promise<TransportResponse> {
219
- const response = await fetch(req.url, {
220
- method: req.method,
221
- headers: req.headers,
222
- body: req.body,
223
- signal: req.signal,
224
- });
171
+ Protocol packages can extend typed inputs and namespaces through `@hyperttp/types` module
172
+ augmentation:
225
173
 
226
- return {
227
- status: response.status,
228
- headers: Object.fromEntries(response.headers),
229
- body: response.body,
230
- url: response.url,
231
- };
174
+ ```ts
175
+ declare module "@hyperttp/types" {
176
+ interface ProtocolInputMap {
177
+ "my-protocol": MyProtocolInput;
232
178
  }
233
179
 
234
- async close(): Promise<void> {
235
- // Cleanup resources
180
+ interface HyperProtocols {
181
+ "my-protocol": MyProtocolMethods;
236
182
  }
237
183
  }
184
+ ```
185
+
186
+ ## Transports
187
+
188
+ A transport describes its client capabilities and optional server capability:
238
189
 
239
- const http = new HyperCore({}, new MyCustomTransport());
190
+ ```ts
191
+ interface HyperTransport {
192
+ execute(request: TransportRequest): Promise<TransportResponse>;
193
+ listen?(options: TransportListenOptions): Promise<TransportServer>;
194
+ close?(): Promise<void> | void;
195
+ destroy?(): Promise<void> | void;
196
+ }
240
197
  ```
241
198
 
242
- ---
199
+ - `execute()` is the required client role.
200
+ - `listen()` is the optional server role.
201
+ - `protocols` or `supports()` declares protocol capabilities.
202
+ - The core resolves and retains transports per protocol and closes a shared instance only after its
203
+ last owner releases it.
243
204
 
244
- ## 🛡️ Security
205
+ Runtime selection:
245
206
 
246
- @hyperttp/core includes several hardening mechanisms built into the core pipeline:
207
+ | Runtime | Preferred package | Fallback |
208
+ | -------------- | ---------------------------- | ------------------------- |
209
+ | Node.js | `@hyperttp/transport-undici` | built-in `FetchTransport` |
210
+ | Bun | `@hyperttp/transport-bun` | built-in `FetchTransport` |
211
+ | Deno | `@hyperttp/transport-deno` | built-in `FetchTransport` |
212
+ | Browser / edge | custom transport | built-in `FetchTransport` |
247
213
 
248
- | Protection | Mechanism | Default |
249
- |---|---|---|
250
- | **SSL Validation** | `rejectUnauthorized` enforced via `https.Agent` in NodeTransport | `true` |
251
- | **Response Size Limit** | `maxResponseBytes` — streaming transform aborts oversized responses | unlimited (0) |
252
- | **CRLF Injection** | `\r\n` stripped from all header values in `appendHeader()` | always on |
253
- | **Concurrency Throttle** | `maxConcurrent` — semaphore gates transport execution | unlimited (0) |
254
- | **Transport Whitelist** | only explicitly registered packages can be loaded dynamically | strict |
255
- | **Prototype Pollution** | all internal maps use `Object.create(null)` | always on |
214
+ A transport can also be supplied explicitly:
256
215
 
257
- Configurable via `HyperCore` constructor:
216
+ ```ts
217
+ import { HyperCore } from "@hyperttp/core";
218
+ import { UndiciTransport } from "@hyperttp/transport-undici";
258
219
 
259
- ```typescript
260
- const http = new HyperCore({
261
- network: {
262
- maxResponseBytes: 10 * 1024 * 1024, // 10 MB limit
263
- maxConcurrent: 50, // max 50 in-flight requests
264
- rejectUnauthorized: true,
265
- },
220
+ const core = new HyperCore({
221
+ customTransport: new UndiciTransport(),
266
222
  });
267
223
  ```
268
224
 
269
- ---
270
-
271
- ## 🔄 Extending Configuration
225
+ ## Plugins
272
226
 
273
- Create derived clients with merged configuration via `.extend()`:
227
+ Plugins are the intended extension mechanism for client request behavior that does not belong to
228
+ protocol semantics or physical I/O. The current hooks run in the client `send()` lifecycle:
274
229
 
275
- ```typescript
276
- // Inherit global pools and layers while changing specific presets
277
- const authenticatedClient = http.extend({
278
- network: { headers: { Authorization: "Bearer token_abc" } },
230
+ ```ts
231
+ core.use({
232
+ name: "request-logger",
233
+ phase: "DATA",
234
+ onRequest(request) {
235
+ console.log("request", request.protocol);
236
+ },
237
+ onResponse(response) {
238
+ console.log("response", response.status);
239
+ },
240
+ onError(error) {
241
+ console.error(error);
242
+ },
279
243
  });
280
244
  ```
281
245
 
282
- ### Graceful Resource Destruction
246
+ Hooks:
283
247
 
284
- Always clean up underlying transport configurations, socket pools, and keep-alive timers in microservice tear-downs:
248
+ - `onRequest` can modify a request or return an early response.
249
+ - `onResponse` can inspect or replace a response.
250
+ - `onError` can recover by returning a response.
251
+ - `mode: "background"` detaches response-side work from the blocking path.
252
+ - `enabled(config)` controls registration.
253
+ - `setup(context)` initializes a plugin.
285
254
 
286
- ```typescript
287
- // Performs a graceful or rapid shutdown sequence across transport selectors
288
- await http.destroy(true);
289
- ```
255
+ Examples of functionality that belongs in plugins:
290
256
 
291
- ---
257
+ - retries and circuit breakers;
258
+ - caching and request deduplication;
259
+ - authentication and request signing;
260
+ - tracing, metrics and structured logging;
261
+ - rate limiting, concurrency control and scheduling;
262
+ - schema validation and application-specific policies.
292
263
 
293
- ## 📊 Benchmarks
264
+ ## Built-in REST protocol
294
265
 
295
- ### Test Configuration
266
+ The REST package exposes client and server roles through `RestProtocol`.
296
267
 
297
- ```txt
298
- Requests 20000
299
- Concurrency 200
300
- Duration 20000
301
- Timeout 60000 ms
302
- ```
268
+ ```ts
269
+ const getResponse = await core.rest.get("/users", {
270
+ query: { page: 1, tag: ["a", "b"] },
271
+ headers: { accept: "application/json" },
272
+ timeout: 5_000,
273
+ });
303
274
 
304
- To run your own suite:
275
+ const postResponse = await core.rest.post("/users", {
276
+ name: "Ada",
277
+ });
305
278
 
306
- ```bash
307
- bun run bench.ts && npx tsx bench.ts
279
+ const streamResponse = await core.rest.stream("/events");
308
280
  ```
309
281
 
310
- ## 🟦 Bun 1.3.14 — BunTransport
282
+ REST behavior includes:
311
283
 
312
- ```bash
313
- npm install @hyperttp/transport-bun
314
- ```
315
-
316
- | Rank | Client | RPS | Avg | p50 | p90 | p99 | Errors |
317
- | ---- | ------------------ | ---------- | ------------ | ------------ | ------------ | ------------ | ------ |
318
- | 🥇 1 | bun-fetch | 24.34K | 8.18 ms | 8.50 ms | 10.62 ms | 14.10 ms | 0 |
319
- | 🥈 2 | node-fetch | 21.58K | 9.23 ms | 9.39 ms | 12.86 ms | 14.72 ms | 0 |
320
- | 🥉 3 | undici | 20.28K | 9.83 ms | 10.12 ms | 13.80 ms | 15.35 ms | 0 |
321
- | 4 | **@hyperttp/core** | **17.39K** | **11.46 ms** | **12.94 ms** | **15.53 ms** | **16.66 ms** | 0 |
322
- | 5 | ky | 12.80K | 15.58 ms | 13.95 ms | 20.62 ms | 22.77 ms | 0 |
323
- | 6 | request | 8.31K | 24.00 ms | 23.43 ms | 29.54 ms | 32.82 ms | 0 |
324
- | 7 | superagent | 7.96K | 25.09 ms | 24.95 ms | 26.85 ms | 28.46 ms | 0 |
325
- | 8 | axios | 6.19K | 32.21 ms | 31.93 ms | 34.15 ms | 39.09 ms | 0 |
326
- | 9 | got | 5.13K | 38.94 ms | 30.99 ms | 58.61 ms | 63.14 ms | 0 |
327
-
328
- ## 🟦 Bun 1.3.14 — NodeTransport
329
-
330
- | Rank | Client | RPS | Avg | p50 | p90 | p99 | Errors |
331
- | ---- | ------------------ | ---------- | ------------ | ------------ | ------------ | ------------ | ------ |
332
- | 🥇 1 | bun-fetch | 23.94K | 8.32 ms | 8.63 ms | 10.64 ms | 13.77 ms | 0 |
333
- | 🥈 2 | node-fetch | 21.71K | 9.18 ms | 9.35 ms | 12.58 ms | 14.40 ms | 0 |
334
- | 🥉 3 | undici | 20.22K | 9.86 ms | 10.15 ms | 13.65 ms | 15.41 ms | 0 |
335
- | 4 | **@hyperttp/core** | **17.80K** | **11.20 ms** | **12.38 ms** | **15.20 ms** | **17.29 ms** | 0 |
336
- | 5 | ky | 12.78K | 15.63 ms | 13.92 ms | 20.59 ms | 23.70 ms | 0 |
337
- | 6 | request | 8.20K | 24.31 ms | 23.62 ms | 29.13 ms | 34.53 ms | 0 |
338
- | 7 | superagent | 8.06K | 24.79 ms | 24.79 ms | 27.26 ms | 29.52 ms | 0 |
339
- | 8 | axios | 6.28K | 31.72 ms | 31.81 ms | 33.53 ms | 35.65 ms | 0 |
340
- | 9 | got | 5.17K | 38.56 ms | 30.85 ms | 58.12 ms | 61.60 ms | 0 |
341
-
342
- ## 🟦 Node.js v24.14.1 — UndiciTransport
284
+ - query serialization with repeated array parameters;
285
+ - header normalization;
286
+ - JSON serialization for plain object and array request bodies;
287
+ - JSON, text and binary response handling;
288
+ - timeout and user abort propagation;
289
+ - unbuffered stream mode;
290
+ - request decoding and response serialization for the server role.
343
291
 
344
- ```bash
345
- npm install @hyperttp/transport-undici
346
- ```
292
+ REST-specific public types are available from `@hyperttp/core/rest`.
347
293
 
348
- | Rank | Client | RPS | Avg | p50 | p90 | p99 | Errors |
349
- | ---- | ------------------ | ---------- | ------------ | ------------ | ------------ | ------------ | ------ |
350
- | 🥇 1 | undici | 13.84K | 14.41 ms | 13.54 ms | 15.39 ms | 26.23 ms | 0 |
351
- | 🥈 2 | **@hyperttp/core** | **11.95K** | **16.68 ms** | **14.90 ms** | **17.04 ms** | **26.58 ms** | 0 |
352
- | 3 | bun-fetch | 7.89K | 25.26 ms | 23.04 ms | 33.86 ms | 39.14 ms | 0 |
353
- | 4 | request | 6.99K | 28.58 ms | 27.19 ms | 34.91 ms | 39.22 ms | 0 |
354
- | 5 | ky | 5.97K | 33.42 ms | 30.05 ms | 41.58 ms | 64.01 ms | 0 |
355
- | 6 | axios | 4.71K | 42.39 ms | 40.65 ms | 48.48 ms | 56.20 ms | 0 |
356
- | 7 | node-fetch | 4.43K | 45.03 ms | 42.18 ms | 53.61 ms | 65.02 ms | 0 |
357
- | 8 | got | 4.20K | 47.51 ms | 45.20 ms | 55.32 ms | 67.58 ms | 0 |
358
- | 9 | superagent | 3.18K | 62.66 ms | 61.13 ms | 70.10 ms | 75.46 ms | 0 |
294
+ ## Lifecycle
359
295
 
360
- ## 🟦 Node.js v24.14.1 — NodeTransport
296
+ ```ts
297
+ const child = core.extend({ verbose: true }); // shares resolved transport leases
298
+ const isolated = core.create({}); // independent transport lifecycle
361
299
 
362
- | Rank | Client | RPS | Avg | p50 | p90 | p99 | Errors |
363
- | ---- | ------------------ | --------- | ------------ | ------------ | ------------ | ------------ | ------ |
364
- | 🥇 1 | undici | 15.45K | 12.91 ms | 12.68 ms | 13.69 ms | 16.96 ms | 0 |
365
- | 🥈 2 | bun-fetch | 8.31K | 24.00 ms | 22.34 ms | 30.38 ms | 36.20 ms | 0 |
366
- | 🥉 3 | request | 7.34K | 27.14 ms | 26.27 ms | 30.96 ms | 34.91 ms | 0 |
367
- | 4 | **@hyperttp/core** | **7.23K** | **27.58 ms** | **25.68 ms** | **33.76 ms** | **42.75 ms** | 0 |
368
- | 5 | ky | 6.41K | 31.14 ms | 28.43 ms | 37.55 ms | 61.30 ms | 0 |
369
- | 6 | axios | 4.87K | 40.96 ms | 39.70 ms | 45.47 ms | 56.43 ms | 0 |
370
- | 7 | node-fetch | 4.62K | 43.15 ms | 41.12 ms | 49.25 ms | 64.28 ms | 0 |
371
- | 8 | got | 4.58K | 43.63 ms | 41.57 ms | 48.48 ms | 65.02 ms | 0 |
372
- | 9 | superagent | 3.32K | 60.11 ms | 59.08 ms | 66.02 ms | 71.26 ms | 0 |
300
+ await child.destroy();
301
+ await isolated.destroy();
302
+ await core.destroy();
303
+ ```
373
304
 
374
- ### 📈 Performance Analysis
305
+ - `extend()` creates a related core and shares existing transport ownership.
306
+ - `create()` creates an independent core and does not inherit `customTransport` by default.
307
+ - `destroy()` is idempotent, closes tracked servers and releases transports.
308
+ - `destroy(false)` requests forced shutdown where the transport supports it.
375
309
 
376
- **Key Insights:**
310
+ ## Error handling
377
311
 
378
- - **Bun + BunTransport**: @hyperttp/core achieves **17.39K RPS** — only 14% slower than native `bun-fetch` (24.34K)
379
- - **Node.js + UndiciTransport**: @hyperttp/core reaches **11.95K RPS** with p99 latency of **26.58ms** — virtually identical to native undici (26.23ms)
380
- - **Zero Error Rate**: 0% errors across all scenarios in all benchmarks
381
- - **Memory Efficiency**: @hyperttp/core uses ~148-184MB in Bun, less than axios (201MB) and got (165-170MB)
312
+ ```ts
313
+ import { HyperClientError, TimeoutError } from "@hyperttp/core";
382
314
 
383
- ---
315
+ try {
316
+ await core.rest.get("/slow", { timeout: 1_000 });
317
+ } catch (error) {
318
+ if (TimeoutError.isTimeoutError(error)) {
319
+ console.error("Request timed out");
320
+ } else if (HyperClientError.isHyperClientError(error)) {
321
+ console.error(error.code, error.message);
322
+ }
323
+ }
324
+ ```
384
325
 
385
- ## 🛠️ Development
326
+ ## Scope
386
327
 
387
- This project uses the OXC toolchain for lightning-fast development:
328
+ The stable core is intentionally limited to:
388
329
 
389
- ```bash
390
- # Install dependencies
391
- bun install
330
+ 1. protocol registration and dispatch;
331
+ 2. transport resolution, sharing and shutdown;
332
+ 3. client and server lifecycle orchestration;
333
+ 4. plugin execution;
334
+ 5. the baseline REST protocol and universal error contracts.
392
335
 
393
- # Type checking
394
- bun run typecheck
336
+ Feature growth should happen through plugins, protocol packages and transport packages. Keeping this
337
+ boundary small makes runtime behavior predictable and allows the core API to stabilize independently
338
+ from the ecosystem around it.
395
339
 
396
- # Linting (oxlint — ~8ms)
397
- bun run lint
340
+ ## Runtime support
398
341
 
399
- # Formatting (oxfmt — ~25ms)
400
- bun run format
342
+ - Node.js 20 and newer;
343
+ - current stable Bun;
344
+ - current stable Deno;
345
+ - modern browsers and edge runtimes with Fetch and Web Streams.
401
346
 
402
- # Build
403
- bun run build
347
+ ## Development
404
348
 
405
- # Run tests
406
- bun run test
349
+ ```bash
350
+ npm install
351
+ npm run format:check
352
+ npm run lint
353
+ npm run typecheck
354
+ npm run test
355
+ npm run build
407
356
  ```
408
357
 
409
- ---
358
+ The required order is `lint → typecheck → test → build`. CI also verifies browser bundling, runtime
359
+ smoke tests and installation of the generated npm tarball.
410
360
 
411
- ## 📄 License
361
+ ## License
412
362
 
413
363
  MIT
414
-
415
- ---
416
-
417
- <p align="center">
418
- <a href="https://github.com/dirold2">dirold2</a>
419
- </p>