@wooksjs/event-http 0.6.6 → 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wooksjs/event-http",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "@wooksjs/event-http",
5
5
  "keywords": [
6
6
  "api",
@@ -26,7 +26,7 @@
26
26
  "directory": "packages/event-http"
27
27
  },
28
28
  "bin": {
29
- "setup-skills": "scripts/setup-skills.js"
29
+ "wooksjs-event-http-skill": "scripts/setup-skills.js"
30
30
  },
31
31
  "files": [
32
32
  "dist",
@@ -47,14 +47,14 @@
47
47
  "devDependencies": {
48
48
  "typescript": "^5.9.3",
49
49
  "vitest": "^3.2.4",
50
- "@wooksjs/event-core": "^0.6.6",
51
- "wooks": "^0.6.6"
50
+ "@wooksjs/event-core": "^0.7.0",
51
+ "wooks": "^0.7.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "@prostojs/logger": "^0.4.3",
55
- "@prostojs/router": "^0.3.1",
56
- "@wooksjs/event-core": "^0.6.6",
57
- "wooks": "^0.6.6"
55
+ "@prostojs/router": "^0.3.2",
56
+ "@wooksjs/event-core": "^0.7.0",
57
+ "wooks": "^0.7.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "rolldown -c ../../rolldown.config.mjs",
@@ -1,37 +1,44 @@
1
1
  ---
2
2
  name: wooksjs-event-http
3
- description: Wooks HTTP framework composable, lazy-evaluated HTTP server for Node.js. Load when building HTTP apps or REST APIs with wooks; defining routes or using the wooks router; using use-composables (useRequest, useResponse, useCookies, useHeaders, useBody, useProxy, useSearchParams, useRouteParams, useAuthorization, useSetHeaders, useSetCookies, useStatus, useAccept, useSetCacheControl); creating custom event context composables; working with @wooksjs/event-core context store (init, get, set, hook); serving static files; proxying requests; handling HTTP errors; setting status codes, content types, or cache control.
3
+ description: Use this skill when working with @wooksjs/event-http to create HTTP servers with createHttpApp(), register route handlers with app.get()/post()/put()/patch()/delete(), read request data with useRequest()/useHeaders()/useCookies()/useSearchParams()/useAuthorization()/useAccept(), control responses with useResponse() and HttpResponse (status, headers, cookies, cache control, streaming), throw HTTP errors with HttpError, test handlers with prepareTestHttpContext(), or integrate with existing Node.js servers via getServerCb().
4
4
  ---
5
5
 
6
6
  # @wooksjs/event-http
7
7
 
8
- A composable HTTP framework for Node.js built on async context (AsyncLocalStorage). Instead of middleware chains and mutated `req`/`res` objects, you call composable functions (`useRequest()`, `useCookies()`, etc.) anywhere in your handler values are computed on demand and cached per request.
8
+ HTTP adapter for Wooks. Composable-based request handling where every piece of data is available on demand, typed, and cached per request. No middleware, no `req`/`res` parametersjust function calls.
9
9
 
10
10
  ## How to use this skill
11
11
 
12
12
  Read the domain file that matches the task. Do not load all files — only what you need.
13
13
 
14
- | Domain | File | Load when... |
15
- |--------|------|------------|
16
- | Event context (core machinery) | [event-core.md](event-core.md) | Understanding the context store API (`init`/`get`/`set`/`hook`), creating custom composables, lazy evaluation and caching, building your own `use*()` functions |
17
- | HTTP app setup | [core.md](core.md) | Creating an HTTP app, server lifecycle, `createHttpApp`, `getServerCb`, testing with `prepareTestHttpContext`, logging |
18
- | Routing | [routing.md](routing.md) | Defining routes, route params (`:id`), wildcards (`*`), regex constraints (`:id(\\d+)`), optional params (`:tab?`), repeated params, path builders, HTTP method shortcuts, handler return values, router config |
19
- | Request utilities | [request.md](request.md) | `useRequest`, `useHeaders`, `useCookies`, `useSearchParams`, `useAuthorization`, `useAccept`, `useEventId`, reading IP, body limits |
20
- | Response & status | [response.md](response.md) | `useResponse`, `useStatus`, `useSetHeaders`, `useSetHeader`, `useSetCookies`, `useSetCookie`, `useSetCacheControl`, content type, status hooks, cookie hooks |
21
- | Error handling | [error-handling.md](error-handling.md) | `HttpError`, throwing errors, custom error bodies, error rendering, guard patterns |
22
- | Addons (body, static, proxy) | [addons.md](addons.md) | `useBody` (body parsing), `serveFile` (static files), `useProxy` (request proxying) |
14
+ | Domain | File | Load when... |
15
+ | -------------------- | -------------------------- | -------------------------------------------------------------------------------------- |
16
+ | Core setup & routing | [core.md](core.md) | Creating an app, registering routes, starting a server, understanding the architecture |
17
+ | Request composables | [request.md](request.md) | Reading request data: headers, cookies, query params, body, authorization, IP |
18
+ | Response API | [response.md](response.md) | Setting status, headers, cookies, cache control, sending responses, error handling |
19
+ | Testing | [testing.md](testing.md) | Writing tests for handlers and composables with `prepareTestHttpContext` |
23
20
 
24
21
  ## Quick reference
25
22
 
26
23
  ```ts
27
- import { createHttpApp, useRouteParams } from '@wooksjs/event-http'
28
-
29
- const app = createHttpApp()
30
- app.get('/hello/:name', () => {
31
- const { get } = useRouteParams<{ name: string }>()
32
- return { greeting: `Hello ${get('name')}!` }
33
- })
34
- app.listen(3000)
24
+ import { createHttpApp } from '@wooksjs/event-http'
25
+
26
+ // Composables (no arguments — context via AsyncLocalStorage)
27
+ import {
28
+ useRequest,
29
+ useResponse,
30
+ useHeaders,
31
+ useCookies,
32
+ useSearchParams,
33
+ useAuthorization,
34
+ useAccept,
35
+ useRouteParams,
36
+ useLogger,
37
+ } from '@wooksjs/event-http'
38
+
39
+ // Errors
40
+ import { HttpError } from '@wooksjs/event-http'
41
+
42
+ // Testing
43
+ import { prepareTestHttpContext } from '@wooksjs/event-http'
35
44
  ```
36
-
37
- Key composables: `useRequest()`, `useResponse()`, `useRouteParams()`, `useHeaders()`, `useSetHeaders()`, `useCookies()`, `useSetCookies()`, `useSearchParams()`, `useAuthorization()`, `useAccept()`, `useSetCacheControl()`, `useStatus()`, `useBody()`, `useProxy()`.
@@ -1,297 +1,152 @@
1
- # Core Concepts — @wooksjs/event-http
1
+ # Core setup & routing — @wooksjs/event-http
2
2
 
3
- > Covers HTTP app creation, server setup, how the HTTP adapter integrates with the event context system, testing, and logging.
3
+ > Creating an app, registering routes, server lifecycle, and architecture overview.
4
4
 
5
- For the underlying event context store API (`init`, `get`, `set`, `hook`, etc.) and how to create custom composables, see [event-core.md](event-core.md).
5
+ ## Concepts
6
6
 
7
- ## Mental Model
7
+ `@wooksjs/event-http` wraps a Node.js HTTP server with Wooks' composable architecture. Each incoming request gets its own `EventContext` (from `@wooksjs/event-core`), and handlers are plain functions that return their response. All request data is accessed through composables — on demand, typed, cached.
8
8
 
9
- `@wooksjs/event-http` is the HTTP adapter for Wooks. It turns every incoming HTTP request into an event with its own isolated context store. Instead of middleware chains and mutated `req`/`res` objects, you call composable functions (`useRequest()`, `useCookies()`, `useSetHeaders()`, etc.) from anywhere in your handler — values are computed on demand and cached per request.
10
-
11
- Key principles:
12
- 1. **Never mutate `req`** — Accumulate request context in the store instead.
13
- 2. **Never parse before needed** — Cookies, body, search params are only parsed when a composable is first called.
14
- 3. **No middleware sprawl** — Composable functions replace middleware. Each one is a focused, importable utility.
9
+ The adapter creates context, seeds it with the `IncomingMessage` and `HttpResponse`, looks up routes, and runs handlers. Handlers never receive `req`/`res` parameters.
15
10
 
16
11
  ## Installation
17
12
 
18
13
  ```bash
19
- npm install wooks @wooksjs/event-http
20
- ```
21
-
22
- ## Creating an HTTP App
23
-
24
- ```ts
25
- import { createHttpApp } from '@wooksjs/event-http'
26
-
27
- const app = createHttpApp()
28
-
29
- app.get('/hello', () => 'Hello World!')
30
-
31
- app.listen(3000, () => {
32
- console.log('Server running on port 3000')
33
- })
34
- ```
35
-
36
- `createHttpApp(opts?, wooks?)` returns a `WooksHttp` instance. Options:
37
-
38
- ```ts
39
- interface TWooksHttpOptions {
40
- logger?: TConsoleBase // custom logger
41
- eventOptions?: TEventOptions // event-level logger config
42
- onNotFound?: TWooksHandler // custom 404 handler
43
- router?: {
44
- ignoreTrailingSlash?: boolean // treat /path and /path/ as the same
45
- ignoreCase?: boolean // case-insensitive matching
46
- cacheLimit?: number // max cached parsed routes
47
- }
48
- requestLimits?: { // app-level body limits (overridable per-request)
49
- maxCompressed?: number // default: 1 MB
50
- maxInflated?: number // default: 10 MB
51
- maxRatio?: number // default: 100 (zip-bomb protection)
52
- readTimeoutMs?: number // default: 10 000 ms
53
- }
54
- }
14
+ pnpm add @wooksjs/event-http
55
15
  ```
56
16
 
57
- Example raise body limits for the entire app:
17
+ Peer dependencies: `@wooksjs/event-core`, `wooks`, `@prostojs/router`, `@prostojs/logger`.
58
18
 
59
- ```ts
60
- const app = createHttpApp({
61
- requestLimits: {
62
- maxCompressed: 50 * 1024 * 1024, // 50 MB
63
- maxInflated: 100 * 1024 * 1024, // 100 MB
64
- maxRatio: 200,
65
- },
66
- })
67
- ```
19
+ ## API Reference
68
20
 
69
- These defaults apply to every request but can still be overridden per-request via `useRequest()` (see [request.md](request.md)).
21
+ ### `createHttpApp(opts?): WooksHttp`
70
22
 
71
- ## Using with an Existing Node.js Server
23
+ Creates and returns a `WooksHttp` instance.
72
24
 
73
25
  ```ts
74
- import http from 'http'
75
26
  import { createHttpApp } from '@wooksjs/event-http'
76
27
 
77
28
  const app = createHttpApp()
78
29
  app.get('/hello', () => 'Hello World!')
79
-
80
- const server = http.createServer(app.getServerCb())
81
- server.listen(3000)
30
+ app.listen(3000)
82
31
  ```
83
32
 
84
- ## How HTTP Context Works
85
-
86
- When a request arrives, the adapter creates an HTTP-specific event context:
87
-
88
- ```
89
- Request arrives (req, res)
90
- → createHttpContext({ req, res }, options)
91
- → AsyncLocalStorage.run(httpContextStore, handler)
92
- → router matches path → handler runs
93
- → handler calls useRequest(), useCookies(), etc.
94
- → each composable calls useHttpContext()
95
- → reads/writes the HTTP context store via init(), get(), set()
96
- ```
33
+ **Options (`TWooksHttpOptions`):**
97
34
 
98
- ### The HTTP Context Store
35
+ | Option | Type | Description |
36
+ | --------------- | -------------------------- | ------------------------------------------------------------- |
37
+ | `logger` | `TConsoleBase` | Custom logger instance |
38
+ | `onNotFound` | `TWooksHandler` | Handler called when no route matches (default: 404 HttpError) |
39
+ | `router` | router options | Custom router configuration |
40
+ | `requestLimits` | `Omit<TRequestLimits, 'perRequest'>` | Default body size/timeout limits for all requests |
41
+ | `responseClass` | `typeof WooksHttpResponse` | Custom response subclass (default: `WooksHttpResponse`) |
42
+ | `defaultHeaders` | `Record<string, string \| string[]>` | Default headers applied to every response (e.g. from `securityHeaders()`) |
99
43
 
100
- The HTTP adapter extends the base event context with these sections:
44
+ ### Route registration
101
45
 
102
46
  ```ts
103
- interface THttpContextStore {
104
- searchParams?: TSearchParamsCache // cached query params
105
- cookies?: Record<string, string | null> // cached parsed cookies
106
- setCookies?: Record<string, TSetCookieData> // outgoing response cookies
107
- accept?: Record<string, boolean> // cached Accept header checks
108
- authorization?: TAuthCache // cached auth header parsing
109
- setHeader?: Record<string, string | string[]> // outgoing response headers
110
- request?: TRequestCache // cached request data (body, IP, etc.)
111
- response?: { responded: boolean } // response sent flag
112
- status?: { code: EHttpStatusCode } // response status code
113
- }
47
+ app.get(path, handler)
48
+ app.post(path, handler)
49
+ app.put(path, handler)
50
+ app.patch(path, handler)
51
+ app.delete(path, handler)
52
+ app.head(path, handler)
53
+ app.options(path, handler)
54
+ app.all(path, handler) // matches any HTTP method
114
55
  ```
115
56
 
116
- Every section is lazily initialized — it only exists when a composable first writes to it. This means zero overhead for unused features.
117
-
118
- ### Extending the HTTP Store for Custom Composables
119
-
120
- When creating custom composables for HTTP, extend the store type via the generic parameter on `useHttpContext`:
57
+ Handlers are plain functions. Return value becomes the response body:
121
58
 
122
59
  ```ts
123
- import { useHttpContext } from '@wooksjs/event-http'
124
-
125
- interface TMyStore {
126
- myFeature?: {
127
- parsedToken?: { userId: string; role: string } | null
128
- isAdmin?: boolean
129
- }
130
- }
131
-
132
- export function useMyFeature() {
133
- const { store } = useHttpContext<TMyStore>()
134
- const { init } = store('myFeature')
135
-
136
- const parsedToken = () =>
137
- init('parsedToken', () => {
138
- const { authRawCredentials, isBearer } = useAuthorization()
139
- if (!isBearer()) return null
140
- return decodeToken(authRawCredentials()!)
141
- })
142
-
143
- const isAdmin = () =>
144
- init('isAdmin', () => parsedToken()?.role === 'admin')
145
-
146
- return { parsedToken, isAdmin }
147
- }
148
- ```
149
-
150
- For the full context store API and more composable patterns, see [event-core.md](event-core.md).
151
-
152
- ## Server Lifecycle
60
+ app.get('/users/:id', () => {
61
+ const { params } = useRouteParams<{ id: string }>()
62
+ return { id: params.id } // → 200 application/json
63
+ })
153
64
 
154
- ### `listen(...)`
65
+ app.post('/users', async () => {
66
+ const { parseBody } = useBody() // from @wooksjs/http-body
67
+ const user = await parseBody<{ name: string }>()
68
+ return { created: user.name } // → 201 application/json (POST auto-status)
69
+ })
70
+ ```
155
71
 
156
- Starts a built-in HTTP server:
72
+ ### Server lifecycle
157
73
 
158
74
  ```ts
75
+ // Start server
159
76
  await app.listen(3000)
160
77
  await app.listen(3000, '0.0.0.0')
161
- await app.listen({ port: 3000, host: '0.0.0.0' })
162
- ```
163
-
164
- ### `close(server?)`
165
78
 
166
- Stops the server:
167
-
168
- ```ts
169
- await app.close()
170
- ```
171
-
172
- ### `getServer()`
173
-
174
- Returns the underlying `http.Server` instance (only available after `listen()`):
175
-
176
- ```ts
177
- const server = app.getServer()
178
- ```
179
-
180
- ### `attachServer(server)`
181
-
182
- Attaches an external server so `close()` can stop it:
183
-
184
- ```ts
79
+ // Use with existing server
80
+ import http from 'http'
185
81
  const server = http.createServer(app.getServerCb())
186
- app.attachServer(server)
187
82
  server.listen(3000)
188
- // later: await app.close()
83
+ app.attachServer(server)
84
+
85
+ // Stop server
86
+ await app.close()
189
87
  ```
190
88
 
191
89
  ### `getServerCb()`
192
90
 
193
- Returns the raw `(req, res) => void` callback for use with any Node.js HTTP server:
91
+ Returns a `(req, res) => void` callback for use with any Node.js HTTP server:
194
92
 
195
93
  ```ts
196
94
  const cb = app.getServerCb()
197
95
  http.createServer(cb).listen(3000)
198
- // or with https:
199
- https.createServer(sslOpts, cb).listen(443)
200
- ```
201
-
202
- ## Sharing Router Between Adapters
203
-
204
- Multiple adapters can share the same Wooks router:
205
-
206
- ```ts
207
- import { Wooks } from 'wooks'
208
- import { createHttpApp } from '@wooksjs/event-http'
209
-
210
- const wooks = new Wooks()
211
- const app1 = createHttpApp({}, wooks)
212
- const app2 = createHttpApp({}, wooks) // shares the same routes
96
+ // or with https, http2, etc.
213
97
  ```
214
98
 
215
- Or share via another adapter instance:
99
+ ## Routing
216
100
 
217
- ```ts
218
- const app1 = createHttpApp()
219
- const app2 = createHttpApp({}, app1) // shares app1's router
220
- ```
101
+ Built on [`@prostojs/router`](https://github.com/prostojs/router). Supports:
221
102
 
222
- ## Logging
103
+ - Static routes: `/api/users`
104
+ - Parametric routes: `/users/:id`
105
+ - Wildcards: `/static/*`
106
+ - Multiple wildcards: `/static/*/assets/*`
107
+ - Regex constraints: `/api/time/:hours(\\d{2})h:minutes(\\d{2})m`
108
+ - Regex wildcards: `/static/*(\\d+)`
223
109
 
224
- Get a scoped logger from the app:
110
+ Route params accessed via `useRouteParams()`:
225
111
 
226
112
  ```ts
227
- const app = createHttpApp()
228
- const logger = app.getLogger('[my-app]')
229
- logger.log('App started')
230
- ```
231
-
232
- Inside a handler, use the event-scoped logger:
233
-
234
- ```ts
235
- import { useEventLogger } from '@wooksjs/event-core'
236
-
237
- app.get('/process', () => {
238
- const logger = useEventLogger('my-handler')
239
- logger.log('Processing request') // tagged with event ID
240
- return 'ok'
113
+ app.get('/users/:id', () => {
114
+ const { params } = useRouteParams<{ id: string }>()
115
+ return { userId: params.id }
241
116
  })
242
117
  ```
243
118
 
244
- ## Testing
119
+ ## Auto-status
245
120
 
246
- `@wooksjs/event-http` exports a test utility for running composables outside a real server:
121
+ When no explicit status is set, it's inferred from the HTTP method and response body:
247
122
 
248
- ```ts
249
- import { prepareTestHttpContext } from '@wooksjs/event-http'
123
+ | Method | With body | Without body |
124
+ | ------ | ------------ | -------------- |
125
+ | GET | 200 OK | 204 No Content |
126
+ | POST | 201 Created | 204 No Content |
127
+ | PUT | 201 Created | 204 No Content |
128
+ | PATCH | 202 Accepted | 204 No Content |
129
+ | DELETE | 202 Accepted | 204 No Content |
250
130
 
251
- const runInContext = prepareTestHttpContext({
252
- url: '/users/42',
253
- method: 'GET',
254
- headers: { authorization: 'Bearer test-token' },
255
- params: { id: '42' },
256
- })
131
+ ## Handler chain
257
132
 
258
- runInContext(() => {
259
- const { get } = useRouteParams()
260
- console.log(get('id')) // '42'
261
-
262
- const { isBearer } = useAuthorization()
263
- console.log(isBearer()) // true
264
- })
265
- ```
266
-
267
- ### `prepareTestHttpContext(options)`
133
+ Multiple handlers can be registered for the same route. If one throws, the next is tried:
268
134
 
269
135
  ```ts
270
- interface TTestHttpContext {
271
- url: string
272
- method?: string // default: 'GET'
273
- headers?: Record<string, string>
274
- params?: Record<string, string | string[]>
275
- requestLimits?: TRequestLimits // app-level body limits for testing
276
- cachedContext?: {
277
- cookies?: Record<string, string | null>
278
- authorization?: TAuthCache
279
- body?: unknown // pre-parsed body
280
- rawBody?: string | Buffer | Promise<Buffer>
281
- raw?: Partial<THttpContextStore> // raw store sections
282
- }
283
- }
136
+ app.get('/resource', authHandler, mainHandler)
284
137
  ```
285
138
 
139
+ If all handlers throw, the last error is sent as the response.
140
+
286
141
  ## Best Practices
287
142
 
288
- - **Use `createHttpApp()` factory**Don't instantiate `WooksHttp` directly unless you need to extend the class.
289
- - **Use `getServerCb()` for custom servers** This gives you full control over HTTPS, HTTP/2, or any custom server setup.
290
- - **One composable per concern** Split auth, validation, user-fetching into separate composables. Compose them in handlers.
291
- - **Use `prepareTestHttpContext()` for unit testing composables** Test composable logic without starting a server.
143
+ - Return values directlythe framework handles serialization and status codes
144
+ - Use `HttpError` for error responses: `throw new HttpError(404)` or `throw new HttpError(400, 'Invalid input')`
145
+ - Use `useResponse()` only when you need explicit control over headers, cookies, or status
146
+ - For `getServerCb()`, call `attachServer(server)` if you want `close()` to work
292
147
 
293
148
  ## Gotchas
294
149
 
295
- - Composables must be called within a request handler (inside the async context). Calling them at module load time throws.
296
- - `listen()` returns a promisealways `await` it or attach error handling.
297
- - The framework auto-detects content type from your return value (string -> text/plain, object -> application/json). Override with `useSetHeaders().setContentType()`.
150
+ - Handlers receive no arguments all data comes from composables
151
+ - `listen()` returns a Promise — `await` it or handle rejection
152
+ - `getServerCb()` doesn't automatically attach the server call `attachServer()` if needed