@comapeo/map-server 1.0.0-pre.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 (179) hide show
  1. package/README.md +610 -0
  2. package/dist/context.d.ts +46 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +181 -0
  5. package/dist/index.d.ts +25 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +112 -0
  8. package/dist/lib/constants.d.ts +7 -0
  9. package/dist/lib/constants.d.ts.map +1 -0
  10. package/dist/lib/constants.js +6 -0
  11. package/dist/lib/download-request.d.ts +17 -0
  12. package/dist/lib/download-request.d.ts.map +1 -0
  13. package/dist/lib/download-request.js +113 -0
  14. package/dist/lib/errors.d.ts +88 -0
  15. package/dist/lib/errors.d.ts.map +1 -0
  16. package/dist/lib/errors.js +158 -0
  17. package/dist/lib/event-stream-response.d.ts +17 -0
  18. package/dist/lib/event-stream-response.d.ts.map +1 -0
  19. package/dist/lib/event-stream-response.js +39 -0
  20. package/dist/lib/event-target.d.ts +9 -0
  21. package/dist/lib/event-target.d.ts.map +1 -0
  22. package/dist/lib/event-target.js +4 -0
  23. package/dist/lib/fetch-api.d.ts +3 -0
  24. package/dist/lib/fetch-api.d.ts.map +1 -0
  25. package/dist/lib/fetch-api.js +16 -0
  26. package/dist/lib/map-share.d.ts +52 -0
  27. package/dist/lib/map-share.d.ts.map +1 -0
  28. package/dist/lib/map-share.js +142 -0
  29. package/dist/lib/secret-stream-fetch.d.ts +7 -0
  30. package/dist/lib/secret-stream-fetch.d.ts.map +1 -0
  31. package/dist/lib/secret-stream-fetch.js +34 -0
  32. package/dist/lib/self-evicting-map.d.ts +16 -0
  33. package/dist/lib/self-evicting-map.d.ts.map +1 -0
  34. package/dist/lib/self-evicting-map.js +29 -0
  35. package/dist/lib/state-update-event.d.ts +8 -0
  36. package/dist/lib/state-update-event.d.ts.map +1 -0
  37. package/dist/lib/state-update-event.js +10 -0
  38. package/dist/lib/utils.d.ts +32 -0
  39. package/dist/lib/utils.d.ts.map +1 -0
  40. package/dist/lib/utils.js +96 -0
  41. package/dist/middlewares/localhost-only.d.ts +11 -0
  42. package/dist/middlewares/localhost-only.d.ts.map +1 -0
  43. package/dist/middlewares/localhost-only.js +10 -0
  44. package/dist/middlewares/parse-request.d.ts +11 -0
  45. package/dist/middlewares/parse-request.d.ts.map +1 -0
  46. package/dist/middlewares/parse-request.js +25 -0
  47. package/dist/routes/downloads.d.ts +15 -0
  48. package/dist/routes/downloads.d.ts.map +1 -0
  49. package/dist/routes/downloads.js +60 -0
  50. package/dist/routes/map-shares.d.ts +19 -0
  51. package/dist/routes/map-shares.d.ts.map +1 -0
  52. package/dist/routes/map-shares.js +192 -0
  53. package/dist/routes/maps.d.ts +6 -0
  54. package/dist/routes/maps.d.ts.map +1 -0
  55. package/dist/routes/maps.js +118 -0
  56. package/dist/routes/root.d.ts +6 -0
  57. package/dist/routes/root.d.ts.map +1 -0
  58. package/dist/routes/root.js +29 -0
  59. package/dist/types.d.ts +110 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +96 -0
  62. package/node_modules/@envelop/instrumentation/LICENSE +21 -0
  63. package/node_modules/@envelop/instrumentation/README.md +30 -0
  64. package/node_modules/@envelop/instrumentation/cjs/index.js +5 -0
  65. package/node_modules/@envelop/instrumentation/cjs/instrumentation.js +89 -0
  66. package/node_modules/@envelop/instrumentation/cjs/package.json +1 -0
  67. package/node_modules/@envelop/instrumentation/esm/index.js +2 -0
  68. package/node_modules/@envelop/instrumentation/esm/instrumentation.js +82 -0
  69. package/node_modules/@envelop/instrumentation/package.json +57 -0
  70. package/node_modules/@envelop/instrumentation/typings/index.d.cts +1 -0
  71. package/node_modules/@envelop/instrumentation/typings/index.d.ts +1 -0
  72. package/node_modules/@envelop/instrumentation/typings/instrumentation.d.cts +44 -0
  73. package/node_modules/@envelop/instrumentation/typings/instrumentation.d.ts +44 -0
  74. package/node_modules/@whatwg-node/disposablestack/cjs/AsyncDisposableStack.js +73 -0
  75. package/node_modules/@whatwg-node/disposablestack/cjs/DisposableStack.js +62 -0
  76. package/node_modules/@whatwg-node/disposablestack/cjs/SupressedError.js +16 -0
  77. package/node_modules/@whatwg-node/disposablestack/cjs/index.js +11 -0
  78. package/node_modules/@whatwg-node/disposablestack/cjs/package.json +1 -0
  79. package/node_modules/@whatwg-node/disposablestack/cjs/symbols.js +20 -0
  80. package/node_modules/@whatwg-node/disposablestack/cjs/utils.js +11 -0
  81. package/node_modules/@whatwg-node/disposablestack/esm/AsyncDisposableStack.js +69 -0
  82. package/node_modules/@whatwg-node/disposablestack/esm/DisposableStack.js +58 -0
  83. package/node_modules/@whatwg-node/disposablestack/esm/SupressedError.js +12 -0
  84. package/node_modules/@whatwg-node/disposablestack/esm/index.js +7 -0
  85. package/node_modules/@whatwg-node/disposablestack/esm/symbols.js +16 -0
  86. package/node_modules/@whatwg-node/disposablestack/esm/utils.js +7 -0
  87. package/node_modules/@whatwg-node/disposablestack/package.json +44 -0
  88. package/node_modules/@whatwg-node/disposablestack/typings/AsyncDisposableStack.d.cts +15 -0
  89. package/node_modules/@whatwg-node/disposablestack/typings/AsyncDisposableStack.d.ts +15 -0
  90. package/node_modules/@whatwg-node/disposablestack/typings/DisposableStack.d.cts +14 -0
  91. package/node_modules/@whatwg-node/disposablestack/typings/DisposableStack.d.ts +14 -0
  92. package/node_modules/@whatwg-node/disposablestack/typings/SupressedError.d.cts +5 -0
  93. package/node_modules/@whatwg-node/disposablestack/typings/SupressedError.d.ts +5 -0
  94. package/node_modules/@whatwg-node/disposablestack/typings/index.d.cts +4 -0
  95. package/node_modules/@whatwg-node/disposablestack/typings/index.d.ts +4 -0
  96. package/node_modules/@whatwg-node/disposablestack/typings/symbols.d.cts +5 -0
  97. package/node_modules/@whatwg-node/disposablestack/typings/symbols.d.ts +5 -0
  98. package/node_modules/@whatwg-node/disposablestack/typings/utils.d.cts +2 -0
  99. package/node_modules/@whatwg-node/disposablestack/typings/utils.d.ts +2 -0
  100. package/node_modules/@whatwg-node/promise-helpers/cjs/index.js +270 -0
  101. package/node_modules/@whatwg-node/promise-helpers/cjs/package.json +1 -0
  102. package/node_modules/@whatwg-node/promise-helpers/esm/index.js +257 -0
  103. package/node_modules/@whatwg-node/promise-helpers/package.json +43 -0
  104. package/node_modules/@whatwg-node/promise-helpers/typings/index.d.cts +31 -0
  105. package/node_modules/@whatwg-node/promise-helpers/typings/index.d.ts +31 -0
  106. package/node_modules/@whatwg-node/server/README.md +590 -0
  107. package/node_modules/@whatwg-node/server/cjs/createServerAdapter.js +368 -0
  108. package/node_modules/@whatwg-node/server/cjs/index.js +17 -0
  109. package/node_modules/@whatwg-node/server/cjs/package.json +1 -0
  110. package/node_modules/@whatwg-node/server/cjs/plugins/types.js +0 -0
  111. package/node_modules/@whatwg-node/server/cjs/plugins/useContentEncoding.js +73 -0
  112. package/node_modules/@whatwg-node/server/cjs/plugins/useCors.js +124 -0
  113. package/node_modules/@whatwg-node/server/cjs/plugins/useErrorHandling.js +52 -0
  114. package/node_modules/@whatwg-node/server/cjs/types.js +0 -0
  115. package/node_modules/@whatwg-node/server/cjs/utils.js +599 -0
  116. package/node_modules/@whatwg-node/server/cjs/uwebsockets.js +241 -0
  117. package/node_modules/@whatwg-node/server/esm/createServerAdapter.js +365 -0
  118. package/node_modules/@whatwg-node/server/esm/index.js +11 -0
  119. package/node_modules/@whatwg-node/server/esm/plugins/types.js +0 -0
  120. package/node_modules/@whatwg-node/server/esm/plugins/useContentEncoding.js +70 -0
  121. package/node_modules/@whatwg-node/server/esm/plugins/useCors.js +120 -0
  122. package/node_modules/@whatwg-node/server/esm/plugins/useErrorHandling.js +46 -0
  123. package/node_modules/@whatwg-node/server/esm/types.js +0 -0
  124. package/node_modules/@whatwg-node/server/esm/utils.js +588 -0
  125. package/node_modules/@whatwg-node/server/esm/uwebsockets.js +234 -0
  126. package/node_modules/@whatwg-node/server/package.json +46 -0
  127. package/node_modules/@whatwg-node/server/typings/createServerAdapter.d.cts +19 -0
  128. package/node_modules/@whatwg-node/server/typings/createServerAdapter.d.ts +19 -0
  129. package/node_modules/@whatwg-node/server/typings/index.d.cts +11 -0
  130. package/node_modules/@whatwg-node/server/typings/index.d.ts +11 -0
  131. package/node_modules/@whatwg-node/server/typings/plugins/types.d.cts +76 -0
  132. package/node_modules/@whatwg-node/server/typings/plugins/types.d.ts +76 -0
  133. package/node_modules/@whatwg-node/server/typings/plugins/useContentEncoding.d.cts +2 -0
  134. package/node_modules/@whatwg-node/server/typings/plugins/useContentEncoding.d.ts +2 -0
  135. package/node_modules/@whatwg-node/server/typings/plugins/useCors.d.cts +14 -0
  136. package/node_modules/@whatwg-node/server/typings/plugins/useCors.d.ts +14 -0
  137. package/node_modules/@whatwg-node/server/typings/plugins/useErrorHandling.d.cts +13 -0
  138. package/node_modules/@whatwg-node/server/typings/plugins/useErrorHandling.d.ts +13 -0
  139. package/node_modules/@whatwg-node/server/typings/types.d.cts +100 -0
  140. package/node_modules/@whatwg-node/server/typings/types.d.ts +100 -0
  141. package/node_modules/@whatwg-node/server/typings/utils.d.cts +42 -0
  142. package/node_modules/@whatwg-node/server/typings/utils.d.ts +42 -0
  143. package/node_modules/@whatwg-node/server/typings/uwebsockets.d.cts +32 -0
  144. package/node_modules/@whatwg-node/server/typings/uwebsockets.d.ts +32 -0
  145. package/node_modules/tslib/CopyrightNotice.txt +15 -0
  146. package/node_modules/tslib/LICENSE.txt +12 -0
  147. package/node_modules/tslib/README.md +164 -0
  148. package/node_modules/tslib/SECURITY.md +41 -0
  149. package/node_modules/tslib/modules/index.d.ts +38 -0
  150. package/node_modules/tslib/modules/index.js +70 -0
  151. package/node_modules/tslib/modules/package.json +3 -0
  152. package/node_modules/tslib/package.json +47 -0
  153. package/node_modules/tslib/tslib.d.ts +460 -0
  154. package/node_modules/tslib/tslib.es6.html +1 -0
  155. package/node_modules/tslib/tslib.es6.js +402 -0
  156. package/node_modules/tslib/tslib.es6.mjs +401 -0
  157. package/node_modules/tslib/tslib.html +1 -0
  158. package/node_modules/tslib/tslib.js +484 -0
  159. package/package.json +87 -0
  160. package/src/context.ts +203 -0
  161. package/src/index.ts +193 -0
  162. package/src/lib/constants.ts +6 -0
  163. package/src/lib/download-request.ts +142 -0
  164. package/src/lib/errors.ts +187 -0
  165. package/src/lib/event-stream-response.ts +57 -0
  166. package/src/lib/event-target.ts +11 -0
  167. package/src/lib/fetch-api.ts +18 -0
  168. package/src/lib/map-share.ts +185 -0
  169. package/src/lib/secret-stream-fetch.ts +42 -0
  170. package/src/lib/self-evicting-map.ts +35 -0
  171. package/src/lib/state-update-event.ts +14 -0
  172. package/src/lib/utils.ts +110 -0
  173. package/src/middlewares/localhost-only.ts +16 -0
  174. package/src/middlewares/parse-request.ts +34 -0
  175. package/src/routes/downloads.ts +92 -0
  176. package/src/routes/map-shares.ts +246 -0
  177. package/src/routes/maps.ts +146 -0
  178. package/src/routes/root.ts +37 -0
  179. package/src/types.ts +152 -0
@@ -0,0 +1,590 @@
1
+ # WHATWG Node Generic Server Adapter
2
+
3
+ `@whatwg-node/server` helps you to create a generic server implementation by using WHATWG Fetch API
4
+ for Node.js, AWS Lambda, Cloudflare Workers, Deno, Express, Fastify, Koa, Next.js and Sveltekit.
5
+
6
+ Once you create an adapter with `createServerAdapter`, you don't need to install any other platform
7
+ specific package since the generic adapter will handle it automatically.
8
+
9
+ ## How to start
10
+
11
+ Let's create a basic Hello World server adapter.
12
+
13
+ ```ts
14
+ // myServerAdapter.ts
15
+ import { createServerAdapter } from '@whatwg-node/server'
16
+
17
+ export default createServerAdapter((request: Request) => {
18
+ return new Response(`Hello World!`, { status: 200 })
19
+ })
20
+ ```
21
+
22
+ ## Integrations
23
+
24
+ You can use your server adapter with the following integrations:
25
+
26
+ ### Node.js
27
+
28
+ [Node.js](https://nodejs.org/api/http.html) is the most popular server side JavaScript runtime.
29
+
30
+ ```ts
31
+ import { createServer } from 'http'
32
+ import myServerAdapter from './myServerAdapter'
33
+
34
+ // You can create your Node server instance by using our adapter
35
+ const nodeServer = createServer(myServerAdapter)
36
+ // Then start listening on some port
37
+ nodeServer.listen(4000)
38
+ ```
39
+
40
+ ### AWS Lambda
41
+
42
+ AWS Lambda is a serverless computing platform that makes it easy to build applications that run on
43
+ the AWS cloud. Our adapter is platform agnostic so they can fit together easily. In order to reduce
44
+ the boilerplate we prefer to use
45
+ [Serverless Express from Vendia](https://github.com/vendia/serverless-express).
46
+
47
+ ```ts
48
+ import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda'
49
+ import type { Handler } from '@aws-cdk/aws-lambda'
50
+ import myServerAdapter from './myServerAdapter'
51
+
52
+ interface ServerContext {
53
+ event: APIGatewayEvent
54
+ lambdaContext: Context
55
+ }
56
+
57
+ export async function handler(
58
+ event: APIGatewayEvent,
59
+ lambdaContext: Context
60
+ ): Promise<APIGatewayProxyResult> {
61
+ const url = new URL(event.path, 'http://localhost')
62
+ if (event.queryStringParameters != null) {
63
+ for (const name in event.queryStringParameters) {
64
+ const value = event.queryStringParameters[name]
65
+ if (value != null) {
66
+ url.searchParams.set(name, value)
67
+ }
68
+ }
69
+ }
70
+
71
+ const response = await myServerAdapter.fetch(
72
+ url,
73
+ {
74
+ // For v1.0 you should use event.httpMethod
75
+ method: event.requestContext.http.method,
76
+ headers: event.headers as HeadersInit,
77
+ body: event.body
78
+ ? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
79
+ : undefined
80
+ },
81
+ {
82
+ event,
83
+ lambdaContext
84
+ }
85
+ )
86
+
87
+ const responseHeaders: Record<string, string> = {}
88
+
89
+ response.headers.forEach((value, name) => {
90
+ responseHeaders[name] = value
91
+ })
92
+
93
+ return {
94
+ statusCode: response.status,
95
+ headers: responseHeaders,
96
+ body: await response.text(),
97
+ isBase64Encoded: false
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Cloudflare Workers
103
+
104
+ Cloudflare Workers provides a serverless execution environment that allows you to create entirely
105
+ new applications or augment existing ones without configuring or maintaining infrastructure. It uses
106
+ Fetch API already so we can use our adapter as an event listener like below;
107
+
108
+ ```ts
109
+ import myServerAdapter from './myServerAdapter'
110
+
111
+ self.addEventListener('fetch', myServerAdapter)
112
+ ```
113
+
114
+ ### Deno
115
+
116
+ [Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust](https://deno.land/).
117
+ You can use our adapter as a Deno request handler like below;
118
+
119
+ ```ts
120
+ import myServerAdapter from './myServerAdapter.ts'
121
+
122
+ Deno.serve(myServerAdapter)
123
+ ```
124
+
125
+ ### Express
126
+
127
+ [Express is the most popular web framework for Node.js.](https://expressjs.com/) It is a minimalist
128
+ framework that provides a robust set of features to handle HTTP on Node.js applications.
129
+
130
+ You can easily integrate your adapter into your Express application with a few lines of code.
131
+
132
+ ```ts
133
+ import express from 'express'
134
+ import myServerAdapter from './myServerAdapter'
135
+
136
+ const app = express()
137
+
138
+ // Bind our adapter to `/mypath` endpoint
139
+ app.use('/mypath', myServerAdapter)
140
+
141
+ app.listen(4000, () => {
142
+ console.log('Running the server at http://localhost:4000/mypath')
143
+ })
144
+ ```
145
+
146
+ ### Fastify
147
+
148
+ [Fastify is one of the popular HTTP server frameworks for Node.js.](https://www.fastify.io/). You
149
+ can use your adapter easily with Fastify.
150
+
151
+ So you can benefit from the powerful plugins of Fastify ecosystem.
152
+ [See the ecosystem](https://www.fastify.io/docs/latest/Guides/Ecosystem/)
153
+
154
+ ```ts
155
+ import fastify, { FastifyReply, FastifyRequest } from 'fastify'
156
+ import myServerAdapter from './myServerAdapter'
157
+
158
+ // This is the fastify instance you have created
159
+ const app = fastify({ logger: true })
160
+
161
+ /**
162
+ * We pass the incoming HTTP request to our adapter
163
+ * and handle the response using Fastify's `reply` API
164
+ * Learn more about `reply` https://www.fastify.io/docs/latest/Reply/
165
+ **/
166
+ app.route({
167
+ url: '/mypath',
168
+ method: ['GET', 'POST', 'OPTIONS'],
169
+ handler: (req, reply) =>
170
+ myServerAdapter.handleNodeRequestAndResponse(req, reply, {
171
+ req,
172
+ reply
173
+ })
174
+ })
175
+
176
+ app.listen(4000)
177
+ ```
178
+
179
+ ### Koa
180
+
181
+ [Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive.](https://koajs.com/)
182
+ You can add your adapter to your Koa application with a few lines of code then
183
+ [benefit middlewares written for Koa.](https://github.com/koajs/koa/wiki)
184
+
185
+ ```ts
186
+ import Koa from 'koa'
187
+ import myServerAdapter from './myServerAdapter'
188
+
189
+ const app = new Koa()
190
+
191
+ app.use(async ctx => {
192
+ const response = await myServerAdapter.handleNodeRequestAndResponse(ctx.request, ctx.res, ctx)
193
+
194
+ // Set status code
195
+ ctx.status = response.status
196
+
197
+ // Set headers
198
+ response.headers.forEach((value, key) => {
199
+ ctx.append(key, value)
200
+ })
201
+
202
+ ctx.body = response.body
203
+ })
204
+
205
+ app.listen(4000, () => {
206
+ console.log('Running the server at http://localhost:4000')
207
+ })
208
+ ```
209
+
210
+ ### Next.js
211
+
212
+ [Next.js](https://nextjs.org/) is a web framework that allows you to build websites very quickly and
213
+ our new server adapter can be integrated with Next.js easily as an API Route.
214
+
215
+ ```ts
216
+ // pages/api/myEndpoint.ts
217
+ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
218
+ import type { NextApiRequest, NextApiResponse } from 'next'
219
+ import myServerAdapter from './myServerAdapter'
220
+
221
+ export const config = {
222
+ api: {
223
+ // Disable body parsing if you expect a request other than JSON
224
+ bodyParser: false
225
+ }
226
+ }
227
+
228
+ export default myServerAdapter
229
+ ```
230
+
231
+ ### SvelteKit
232
+
233
+ [SvelteKit](https://kit.svelte.dev/) is the fastest way to build svelte apps. It is very simple, and
234
+ let you build frontend & backend in a single place
235
+
236
+ ```ts
237
+ import myServerAdapter from './myServerAdapter'
238
+
239
+ export { myServerAdapter as get, myServerAdapter as post }
240
+ ```
241
+
242
+ ### Bun
243
+
244
+ [Bun](https://bun.sh/) is a modern JavaScript runtime like Node or Deno, and it supports Fetch API
245
+ as a first class citizen. So the configuration is really simple like any other JS runtime;
246
+
247
+ ```ts
248
+ import myServerAdapter from './myServerAdapter'
249
+
250
+ const server = Bun.serve(myServerAdapter)
251
+
252
+ console.info(`Server is running on ${server.hostname}`)
253
+ ```
254
+
255
+ ## File Uploads / Multipart Requests
256
+
257
+ Multipart requests are a type of HTTP request that allows you to send blobs together with regular
258
+ text data which has a mime-type `multipart/form-data`.
259
+
260
+ For example, if you send a multipart request from a browser with `FormData`, you can get the same
261
+ `FormData` object in your request handler.
262
+
263
+ ```ts
264
+ import { createServerAdapter } from '@whatwg-node/server'
265
+
266
+ const myServerAdapter = createServerAdapter(async request => {
267
+ // Parse the request as `FormData`
268
+ const formData = await request.formData()
269
+ // Select the file
270
+ const file = formData.get('file')
271
+ // Process it as a string
272
+ const fileTextContent = await file.text()
273
+ // Select the other text parameter
274
+ const regularTextData = formData.get('additionalStuff')
275
+ // ...
276
+ return Response.json({ message: 'ok' })
277
+ })
278
+ ```
279
+
280
+ You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN
281
+ documentation.
282
+
283
+ ## Routing and Middlewares
284
+
285
+ We'd recommend to use `fets` to handle routing and middleware approach. It uses
286
+ `@whatwg-node/server` under the hood.
287
+
288
+ > Learn more about `fets` [here](https://github.com/ardatan/fets)
289
+
290
+ ## Plugin System
291
+
292
+ You can create your own plugins to extend the functionality of your server adapter.
293
+
294
+ ### `onRequest`
295
+
296
+ This hook is invoked for ANY incoming HTTP request. Here you can manipulate the request or create a
297
+ short circuit before the server adapter handles the request.
298
+
299
+ For example, you can shortcut the manually handle an HTTP request, short-circuiting the HTTP
300
+ handler:
301
+
302
+ ```ts
303
+ import { createServerAdapter, type ServerAdapterPlugin } from '@whatwg-node/server'
304
+
305
+ const myPlugin: ServerAdapterPlugin = {
306
+ onRequest({ request, endResponse, fetchAPI }) {
307
+ if (!request.headers.get('authorization')) {
308
+ endResponse(
309
+ new fetchAPI.Response(null, {
310
+ status: 401,
311
+ headers: {
312
+ 'Content-Type': 'application/json'
313
+ }
314
+ })
315
+ )
316
+ }
317
+ }
318
+ }
319
+
320
+ const myServerAdapter = createServerAdapter(
321
+ async request => {
322
+ return new Response(`Hello World!`, { status: 200 })
323
+ },
324
+ {
325
+ plugins: [myPlugin]
326
+ }
327
+ )
328
+ ```
329
+
330
+ Possible usage examples of this hook are:
331
+
332
+ - Manipulate the request
333
+ - Short circuit before the adapter handles the request
334
+
335
+ | Payload field | Description |
336
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
337
+ | `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). |
338
+ | `serverContext` | The early context object that is shared between all hooks and the entire execution. [Learn more about the context](/docs/features/context). |
339
+ | `fetchAPI` | WHATWG Fetch API implementation. [Learn more about the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). |
340
+ | `url` | WHATWG URL object of the incoming request. [Learn more about the URL object](https://developer.mozilla.org/en-US/docs/Web/API/URL). |
341
+ | `endResponse` | A function that allows you to end the request early and send a response to the client. |
342
+
343
+ ### `onResponse`
344
+
345
+ This hook is invoked after a HTTP request has been processed and after the response has been
346
+ forwarded to the client. Here you can perform any cleanup or logging operations, or you can
347
+ manipulate the outgoing response object.
348
+
349
+ ```ts
350
+ import { createServerAdapter, type ServerAdapterPlugin } from '@whatwg-node/server'
351
+
352
+ const requestTimeMap = new WeakMap<Request, number>()
353
+
354
+ const myPlugin: ServerAdapterPlugin = {
355
+ onRequest({ request }) {
356
+ requestTimeMap.set(request, Date.now())
357
+ },
358
+ onResponse({ request, serverContext, response }) {
359
+ console.log(`Request to ${request.url} has been processed with status ${response.status}`)
360
+ // Add some headers
361
+ response.headers.set('X-Server-Name', 'My Server')
362
+ console.log(`Request to ${request.url} took ${Date.now() - requestTimeMap.get(request)}ms`)
363
+ }
364
+ }
365
+ ```
366
+
367
+ **Example actions in this hook:**
368
+
369
+ - Specify custom response format
370
+ - Logging/Metrics
371
+
372
+ | Field Name | Description |
373
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
374
+ | `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). |
375
+ | `serverContext` | The final context object that is shared between all hooks and the execution. [Learn more about the context](/docs/features/context). |
376
+ | `response` | The outgoing HTTP response as WHATWG `Response` object. [Learn more about the response interface](https://developer.mozilla.org/en-US/docs/Web/API/Response). |
377
+
378
+ ### `onDispose`
379
+
380
+ In order to clean up resources when the server is shut down, you can use `onDispose`,
381
+ `Symbol.asyncDispose` or `Symbol.syncDispose` to clean up resources.
382
+
383
+ ```ts
384
+ export const useMyPlugin = () => {
385
+ return {
386
+ async onDispose() {
387
+ // Clean up resources
388
+ await stopConnection()
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ [You can learn more about Explicit Resource Management below](#explicit-resource-management)
395
+
396
+ ## `Request.signal` for awareness of client disconnection
397
+
398
+ In the real world, a lot of HTTP requests are dropped or canceled. This can happen due to a flakey
399
+ internet connection, navigation to a new view or page within a web or native app or the user simply
400
+ closing the app. In this case, the server can stop processing the request and save resources.
401
+
402
+ You can utilize `request.signal` to cancel pending asynchronous operations when the client
403
+ disconnects.
404
+
405
+ ```ts
406
+ import { createServerAdapter } from '@whatwg-node/server'
407
+
408
+ const myServerAdapter = createServerAdapter(async request => {
409
+ const upstreamRes = await fetch('https://api.example.com/data', {
410
+ // When the client disconnects, the fetch request will be canceled
411
+ signal: request.signal
412
+ })
413
+ return Response.json({
414
+ data: await upstreamRes.json()
415
+ })
416
+ })
417
+ ```
418
+
419
+ The execution cancelation API is built on top of the AbortController and AbortSignal APIs.
420
+
421
+ [Learn more about AbortController and AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
422
+
423
+ ## Explicit Resource Management
424
+
425
+ While implementing your server with `@whatwg-node/server`, you need to control over the lifecycle of
426
+ your resources. This is especially important when you are dealing with resources that need to be
427
+ cleaned up when they are no longer needed, or clean up the operations in a queue when the server is
428
+ shutting down.
429
+
430
+ ### Dispose the Server Adapter
431
+
432
+ The server adapter supports
433
+ [Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management)
434
+ approach that allows you to dispose of resources when they are no longer needed. This can be done in
435
+ two ways shown below;
436
+
437
+ #### `await using` syntax
438
+
439
+ We use the `await using` syntax to create a new instance of `adapter` and dispose of it when the
440
+ block is exited. Notice that we are using a block to limit the scope of `adapter` within `{ }`. So
441
+ resources will be disposed of when the block is exited.
442
+
443
+ ```ts
444
+ console.log('Adapter is starting')
445
+ {
446
+ await using adapter = createServerAdapter(/* ... */)
447
+ }
448
+ console.log('Adapter is disposed')
449
+ ```
450
+
451
+ #### `dispose` method
452
+
453
+ We create a new instance of `adapter` and dispose of it using the `dispose` method.
454
+
455
+ ```ts
456
+ console.log('Adapter is starting')
457
+ const adapter = createServerAdapter(/* ... */)
458
+ await adapter.dispose()
459
+ console.log('Adapter is disposed')
460
+ ```
461
+
462
+ In the first example, we use the `await using` syntax to create a new instance of `adapter` and
463
+ dispose of it when the block is exited. In the second example,
464
+
465
+ #### Dispose on Node.js
466
+
467
+ When running your adapter on Node.js, you can use process event listeners or server's `close` event
468
+ to trigger the adapter's disposal. Or you can configure the adapter to handle this automatically by
469
+ listening `process` exit signals.
470
+
471
+ ##### Explicit disposal
472
+
473
+ We can dispose of the adapter instance when the server is closed like below.
474
+
475
+ ```ts
476
+ import { createServer } from 'http'
477
+ import { createServerAdapter } from '@whatwg-node/server'
478
+
479
+ const adapter = createServerAdapter(/* ... */)
480
+
481
+ const server = createServer(adapter)
482
+ server.listen(4000, () => {
483
+ console.info('Server is running on http://localhost:4000')
484
+ })
485
+ server.once('close', async () => {
486
+ await adapter.dispose()
487
+ console.info('Server is disposed so is adapter')
488
+ })
489
+ ```
490
+
491
+ ##### Automatic disposal
492
+
493
+ `disposeOnProcessTerminate` option will register an event listener for `process` termination in
494
+ Node.js
495
+
496
+ ```ts
497
+ import { createServer } from 'http'
498
+ import { createServerAdapter } from '@whatwg-node/server'
499
+
500
+ createServer(
501
+ createServerAdapter(/* ... */, {
502
+ disposeOnProcessTerminate: true,
503
+ plugins: [/* ... */]
504
+ })
505
+ ).listen(4000, () => {
506
+ console.info('Server is running on http://localhost:4000')
507
+ })
508
+ ```
509
+
510
+ ### Plugin Disposal
511
+
512
+ If you have plugins that need some internal resources to be disposed of, you can use the `onDispose`
513
+ hook to dispose of them. This hook will be invoked when the adapter instance is disposed like above.
514
+
515
+ ```ts
516
+ let dbConnection: Connection
517
+ const plugin = {
518
+ onPluginInit: async () => {
519
+ dbConnection = await createConnection()
520
+ },
521
+ onDispose: async () => {
522
+ // Dispose of resources
523
+ await dbConnection.close()
524
+ }
525
+ }
526
+ ```
527
+
528
+ Or you can flush a queue of operations when the server is shutting down.
529
+
530
+ ```ts
531
+ const backgroundJobs: Promise<void>[] = []
532
+
533
+ const plugin = {
534
+ onRequest() {
535
+ backgroundJobs.push(
536
+ sendAnalytics({
537
+ /* ... */
538
+ })
539
+ )
540
+ },
541
+ onDispose: async () => {
542
+ // Flush the queue of background jobs
543
+ await Promise.all(backgroundJobs)
544
+ }
545
+ }
546
+ ```
547
+
548
+ But for this kind of purposes, `waitUntil` can be a better choice.
549
+
550
+ ### Background jobs
551
+
552
+ If you have background jobs that need to be completed before the environment is shut down.
553
+ `waitUntil` is better choice than `onDispose`. In this case, those jobs will keep running in the
554
+ background but in case of disposal, they will be awaited. `waitUntil` works so similar to
555
+ [Cloudflare Workers' `waitUntil` function](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#parameters).
556
+
557
+ But the adapter handles `waitUntil` even if it is not provided by the environment.
558
+
559
+ ```ts
560
+ const adapter = createServerAdapter(async (request, context) => {
561
+ const args = await request.json()
562
+ if (!args.name) {
563
+ return Response.json({ error: 'Name is required' }, { status: 400 })
564
+ }
565
+ // This does not block the response
566
+ context.waitUntil(
567
+ fetch('http://my-analytics.com/analytics', {
568
+ method: 'POST',
569
+ body: JSON.stringify({
570
+ name: args.name,
571
+ userAgent: request.headers.get('User-Agent')
572
+ })
573
+ })
574
+ )
575
+ return Response.json({ greetings: `Hello, ${args.name}` })
576
+ })
577
+
578
+ const res = await adapter.fetch('http://localhost:4000', {
579
+ method: 'POST',
580
+ headers: {
581
+ 'Content-Type': 'application/json'
582
+ },
583
+ body: JSON.stringify({ name: 'John' })
584
+ })
585
+
586
+ console.log(await res.json()) // { greetings: "Hello, John" }
587
+
588
+ await adapter.dispose()
589
+ // The fetch request for `analytics` will be awaited here
590
+ ```