@atproto/lex-server 0.0.7 → 0.0.9

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/dist/nodejs.d.ts CHANGED
@@ -1,33 +1,312 @@
1
1
  import { IncomingMessage, Server as HttpServer, ServerOptions, ServerResponse } from 'node:http';
2
2
  import { ListenOptions } from 'node:net';
3
3
  import { FetchHandler } from './lex-server.js';
4
+ /**
5
+ * Upgrades an HTTP request to a WebSocket connection for Node.js.
6
+ *
7
+ * This function must be passed to the {@link LexRouter} constructor to enable
8
+ * subscription (WebSocket) support on Node.js. It creates a WebSocket instance
9
+ * and a placeholder response that signals the need for protocol upgrade.
10
+ *
11
+ * The actual upgrade is handled internally when the response is sent through
12
+ * {@link sendResponse}.
13
+ *
14
+ * @param request - The incoming HTTP request to upgrade
15
+ * @returns An object containing the WebSocket and upgrade response
16
+ * @throws {TypeError} If the request is not a valid WebSocket upgrade request
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { LexRouter } from '@atproto/lex-server'
21
+ * import { upgradeWebSocket } from '@atproto/lex-server/nodejs'
22
+ *
23
+ * // Pass to router for subscription support
24
+ * const router = new LexRouter({ upgradeWebSocket })
25
+ *
26
+ * // Now you can add subscription handlers
27
+ * router.add(subscribeRepos, async function* (ctx) {
28
+ * for await (const event of eventStream) {
29
+ * yield event
30
+ * }
31
+ * })
32
+ * ```
33
+ */
4
34
  export declare function upgradeWebSocket(request: Request): {
5
35
  response: Response;
6
36
  socket: WebSocket;
7
37
  };
38
+ /**
39
+ * Sends a fetch API Response through a Node.js ServerResponse.
40
+ *
41
+ * Handles both regular HTTP responses and WebSocket upgrades. For WebSocket
42
+ * upgrades (status 101), delegates to the WebSocket upgrade handler.
43
+ *
44
+ * This function is used internally by {@link toRequestListener} and
45
+ * {@link createServer}, but can be used directly for custom integrations.
46
+ *
47
+ * @param req - The Node.js IncomingMessage
48
+ * @param res - The Node.js ServerResponse to write to
49
+ * @param response - The fetch API Response to send
50
+ * @throws {TypeError} If headers have already been sent
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import http from 'node:http'
55
+ * import { sendResponse } from '@atproto/lex-server/nodejs'
56
+ *
57
+ * const server = http.createServer(async (req, res) => {
58
+ * const response = new Response('Hello, World!', {
59
+ * headers: { 'Content-Type': 'text/plain' }
60
+ * })
61
+ * await sendResponse(req, res, response)
62
+ * })
63
+ * ```
64
+ */
65
+ export declare function sendResponse(req: IncomingMessage, res: ServerResponse, response: Response): Promise<void>;
66
+ /**
67
+ * Network address type for Node.js TCP connections.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const addr: NetAddr = {
72
+ * transport: 'tcp',
73
+ * hostname: '192.168.1.100',
74
+ * port: 54321
75
+ * }
76
+ * ```
77
+ */
8
78
  export type NetAddr = {
79
+ /** Always 'tcp' for Node.js HTTP connections. */
9
80
  transport: 'tcp';
81
+ /** The IP address of the remote client. */
10
82
  hostname: string;
83
+ /** The port number of the remote client. */
11
84
  port: number;
12
85
  };
86
+ /**
87
+ * Connection metadata for Node.js HTTP requests.
88
+ *
89
+ * Provides information about the client connection, including the remote
90
+ * address and a promise that resolves when the connection is fully closed
91
+ * (including WebSocket connections).
92
+ */
13
93
  export type NodeConnectionInfo = {
94
+ /** Promise that resolves when the connection is fully closed. */
14
95
  completed: Promise<void>;
96
+ /** The remote address of the client, if available. */
15
97
  remoteAddr: NetAddr | undefined;
16
98
  };
99
+ /**
100
+ * Interface for objects that can handle fetch-style requests.
101
+ *
102
+ * Used by {@link createServer} and {@link serve} to accept either
103
+ * a fetch handler function or an object with a `fetch` method
104
+ * (like {@link LexRouter}).
105
+ */
17
106
  export interface HandlerObject {
107
+ /** The fetch handler method. */
18
108
  fetch: FetchHandler;
19
109
  }
110
+ /**
111
+ * Converts a fetch-style handler to a Node.js request listener.
112
+ *
113
+ * The returned listener can be used with Node.js HTTP servers directly,
114
+ * or as middleware in frameworks like Express (supports the `next` callback).
115
+ *
116
+ * @typeParam Request - The request class type (default: IncomingMessage)
117
+ * @typeParam Response - The response class type (default: ServerResponse)
118
+ * @param fetchHandler - The fetch-style handler function
119
+ * @returns A Node.js RequestListener compatible with http.createServer
120
+ *
121
+ * @example Using as Express middleware
122
+ * ```typescript
123
+ * import express from 'express'
124
+ * import { toRequestListener } from '@atproto/lex-server/nodejs'
125
+ * import { LexRouter } from '@atproto/lex-server'
126
+ *
127
+ * const router = new LexRouter()
128
+ * // Register handlers...
129
+ *
130
+ * const app = express()
131
+ *
132
+ * // Mount the XRPC router
133
+ * app.use('/xrpc', toRequestListener(router.fetch))
134
+ * ```
135
+ */
20
136
  export declare function toRequestListener<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(fetchHandler: FetchHandler): (req: InstanceType<Request>, res: InstanceType<Response> & {
21
137
  req: InstanceType<Request>;
22
138
  }, next?: (err?: unknown) => void) => void;
139
+ /**
140
+ * Options for creating an XRPC server.
141
+ *
142
+ * Extends Node.js {@link ServerOptions} with additional options for graceful shutdown.
143
+ */
23
144
  export type CreateServerOptions<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse> = ServerOptions<Request, Response> & {
145
+ /**
146
+ * Timeout in milliseconds for graceful termination.
147
+ *
148
+ * When `terminate()` is called, the server will wait up to this duration
149
+ * for active connections to complete before forcibly closing them.
150
+ */
24
151
  gracefulTerminationTimeout?: number;
25
152
  };
153
+ /**
154
+ * Extended HTTP server with graceful shutdown support.
155
+ *
156
+ * Extends the standard Node.js HttpServer with a `terminate()` method
157
+ * for graceful shutdown and implements `AsyncDisposable` for use with
158
+ * `await using`.
159
+ *
160
+ * @typeParam Request - The request class type
161
+ * @typeParam Response - The response class type
162
+ *
163
+ * @example Graceful shutdown
164
+ * ```typescript
165
+ * const server = createServer(router)
166
+ * server.listen(3000)
167
+ *
168
+ * process.on('SIGTERM', async () => {
169
+ * console.log('Shutting down...')
170
+ * await server.terminate()
171
+ * console.log('Server stopped')
172
+ * })
173
+ * ```
174
+ *
175
+ * @example Using with await using
176
+ * ```typescript
177
+ * await using server = await serve(router, { port: 3000 })
178
+ * // Server will be automatically terminated when scope exits
179
+ * ```
180
+ */
26
181
  export interface Server<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse> extends HttpServer<Request, Response>, AsyncDisposable {
182
+ /**
183
+ * Gracefully terminates the server.
184
+ *
185
+ * Stops accepting new connections and waits for active connections
186
+ * to complete (up to `gracefulTerminationTimeout`).
187
+ *
188
+ * @returns Promise that resolves when the server is fully stopped
189
+ */
27
190
  terminate(): Promise<void>;
28
191
  [Symbol.asyncDispose](): Promise<void>;
29
192
  }
193
+ /**
194
+ * Creates an HTTP server configured for XRPC request handling.
195
+ *
196
+ * The server includes graceful shutdown support and can be used with
197
+ * either a fetch handler function or an object with a `fetch` method
198
+ * (like {@link LexRouter}).
199
+ *
200
+ * Note: This creates the server but does not start listening. Call
201
+ * `server.listen()` to start the server, or use {@link serve} for
202
+ * a combined create-and-listen operation.
203
+ *
204
+ * @typeParam Request - The request class type
205
+ * @typeParam Response - The response class type
206
+ * @param handler - A fetch handler or object with fetch method
207
+ * @param options - Server configuration options
208
+ * @returns An HTTP server with graceful shutdown support
209
+ *
210
+ * @example Basic usage
211
+ * ```typescript
212
+ * import { LexRouter } from '@atproto/lex-server'
213
+ * import { createServer, upgradeWebSocket } from '@atproto/lex-server/nodejs'
214
+ *
215
+ * const router = new LexRouter({ upgradeWebSocket })
216
+ * router.add(myMethod, myHandler)
217
+ *
218
+ * const server = createServer(router)
219
+ * server.listen(3000, () => {
220
+ * console.log('Server listening on port 3000')
221
+ * })
222
+ * ```
223
+ *
224
+ * @example With graceful termination timeout
225
+ * ```typescript
226
+ * const server = createServer(router, {
227
+ * gracefulTerminationTimeout: 10000 // 10 seconds
228
+ * })
229
+ * ```
230
+ */
30
231
  export declare function createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler: FetchHandler | HandlerObject, options?: CreateServerOptions<Request, Response>): Server<Request, Response>;
232
+ /**
233
+ * Combined options for creating and starting an XRPC server.
234
+ *
235
+ * Includes both server creation options and network listen options.
236
+ *
237
+ * @typeParam Request - The request class type
238
+ * @typeParam Response - The response class type
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const options: StartServerOptions = {
243
+ * port: 3000,
244
+ * host: '0.0.0.0',
245
+ * gracefulTerminationTimeout: 10000
246
+ * }
247
+ * ```
248
+ */
31
249
  export type StartServerOptions<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse> = ListenOptions & CreateServerOptions<Request, Response>;
250
+ /**
251
+ * Creates and starts an HTTP server, returning when it's ready to accept connections.
252
+ *
253
+ * This is a convenience function that combines {@link createServer} and `server.listen()`
254
+ * into a single async operation. The returned promise resolves once the server
255
+ * is actively listening.
256
+ *
257
+ * @typeParam Request - The request class type
258
+ * @typeParam Response - The response class type
259
+ * @param handler - A fetch handler or object with fetch method (like {@link LexRouter})
260
+ * @param options - Combined server and listen options
261
+ * @returns Promise resolving to the running server
262
+ *
263
+ * @example Basic usage
264
+ * ```typescript
265
+ * import { LexRouter } from '@atproto/lex-server'
266
+ * import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
267
+ *
268
+ * const router = new LexRouter({ upgradeWebSocket })
269
+ *
270
+ * // Register handlers
271
+ * router.add(getProfile, async (ctx) => {
272
+ * return { body: await db.getProfile(ctx.params.actor) }
273
+ * })
274
+ *
275
+ * // Start server on port 3000
276
+ * const server = await serve(router, { port: 3000 })
277
+ * console.log('Server listening on port 3000')
278
+ *
279
+ * // Graceful shutdown
280
+ * process.on('SIGTERM', () => server.terminate())
281
+ * process.on('SIGINT', () => server.terminate())
282
+ * ```
283
+ *
284
+ * @example With all options
285
+ * ```typescript
286
+ * const server = await serve(router, {
287
+ * port: 3000,
288
+ * host: '0.0.0.0',
289
+ * gracefulTerminationTimeout: 15000,
290
+ * })
291
+ * ```
292
+ *
293
+ * @example Using with await using (auto-cleanup)
294
+ * ```typescript
295
+ * async function main() {
296
+ * await using server = await serve(router, { port: 3000 })
297
+ *
298
+ * // Server is running...
299
+ * console.log('Server listening on port 3000')
300
+ *
301
+ * // Wait for termination signal
302
+ * await Promise.race([
303
+ * once(process, 'SIGINT'),
304
+ * once(process, 'SIGTERM'),
305
+ * ])
306
+ *
307
+ * // Server will be automatically terminated when scope exits
308
+ * }
309
+ * ```
310
+ */
32
311
  export declare function serve<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse<InstanceType<Request>> = typeof ServerResponse>(handler: FetchHandler | HandlerObject, options?: StartServerOptions<Request, Response>): Promise<Server<Request, Response>>;
33
312
  //# sourceMappingURL=nodejs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../src/nodejs.ts"],"names":[],"mappings":"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"}
package/dist/nodejs.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.upgradeWebSocket = upgradeWebSocket;
4
+ exports.sendResponse = sendResponse;
4
5
  exports.toRequestListener = toRequestListener;
5
6
  exports.createServer = createServer;
6
7
  exports.serve = serve;
@@ -18,6 +19,36 @@ function isUpgradeRequest(request, upgrade) {
18
19
  request.headers.get('connection')?.toLowerCase() === 'upgrade' &&
19
20
  request.headers.get('upgrade')?.toLowerCase() === upgrade);
20
21
  }
22
+ /**
23
+ * Upgrades an HTTP request to a WebSocket connection for Node.js.
24
+ *
25
+ * This function must be passed to the {@link LexRouter} constructor to enable
26
+ * subscription (WebSocket) support on Node.js. It creates a WebSocket instance
27
+ * and a placeholder response that signals the need for protocol upgrade.
28
+ *
29
+ * The actual upgrade is handled internally when the response is sent through
30
+ * {@link sendResponse}.
31
+ *
32
+ * @param request - The incoming HTTP request to upgrade
33
+ * @returns An object containing the WebSocket and upgrade response
34
+ * @throws {TypeError} If the request is not a valid WebSocket upgrade request
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { LexRouter } from '@atproto/lex-server'
39
+ * import { upgradeWebSocket } from '@atproto/lex-server/nodejs'
40
+ *
41
+ * // Pass to router for subscription support
42
+ * const router = new LexRouter({ upgradeWebSocket })
43
+ *
44
+ * // Now you can add subscription handlers
45
+ * router.add(subscribeRepos, async function* (ctx) {
46
+ * for await (const event of eventStream) {
47
+ * yield event
48
+ * }
49
+ * })
50
+ * ```
51
+ */
21
52
  function upgradeWebSocket(request) {
22
53
  if (!isUpgradeRequest(request, 'websocket')) {
23
54
  throw new TypeError('upgradeWebSocket() expects a WebSocket upgrade');
@@ -79,6 +110,33 @@ function handleWebSocketUpgrade(req, response) {
79
110
  req.emit(kUpgradeEvent, ws);
80
111
  });
81
112
  }
113
+ /**
114
+ * Sends a fetch API Response through a Node.js ServerResponse.
115
+ *
116
+ * Handles both regular HTTP responses and WebSocket upgrades. For WebSocket
117
+ * upgrades (status 101), delegates to the WebSocket upgrade handler.
118
+ *
119
+ * This function is used internally by {@link toRequestListener} and
120
+ * {@link createServer}, but can be used directly for custom integrations.
121
+ *
122
+ * @param req - The Node.js IncomingMessage
123
+ * @param res - The Node.js ServerResponse to write to
124
+ * @param response - The fetch API Response to send
125
+ * @throws {TypeError} If headers have already been sent
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * import http from 'node:http'
130
+ * import { sendResponse } from '@atproto/lex-server/nodejs'
131
+ *
132
+ * const server = http.createServer(async (req, res) => {
133
+ * const response = new Response('Hello, World!', {
134
+ * headers: { 'Content-Type': 'text/plain' }
135
+ * })
136
+ * await sendResponse(req, res, response)
137
+ * })
138
+ * ```
139
+ */
82
140
  async function sendResponse(req, res, response) {
83
141
  // Invalid usage
84
142
  if (res.headersSent) {
@@ -90,7 +148,7 @@ async function sendResponse(req, res, response) {
90
148
  res.statusCode = response.status;
91
149
  res.statusMessage = response.statusText;
92
150
  for (const [key, value] of response.headers) {
93
- res.appendHeader(key, value);
151
+ res.setHeader(key, value);
94
152
  }
95
153
  if (response.body != null && req.method !== 'HEAD') {
96
154
  const stream = node_stream_1.Readable.fromWeb(response.body);
@@ -210,6 +268,32 @@ function toConnectionInfo(req) {
210
268
  : undefined,
211
269
  };
212
270
  }
271
+ /**
272
+ * Converts a fetch-style handler to a Node.js request listener.
273
+ *
274
+ * The returned listener can be used with Node.js HTTP servers directly,
275
+ * or as middleware in frameworks like Express (supports the `next` callback).
276
+ *
277
+ * @typeParam Request - The request class type (default: IncomingMessage)
278
+ * @typeParam Response - The response class type (default: ServerResponse)
279
+ * @param fetchHandler - The fetch-style handler function
280
+ * @returns A Node.js RequestListener compatible with http.createServer
281
+ *
282
+ * @example Using as Express middleware
283
+ * ```typescript
284
+ * import express from 'express'
285
+ * import { toRequestListener } from '@atproto/lex-server/nodejs'
286
+ * import { LexRouter } from '@atproto/lex-server'
287
+ *
288
+ * const router = new LexRouter()
289
+ * // Register handlers...
290
+ *
291
+ * const app = express()
292
+ *
293
+ * // Mount the XRPC router
294
+ * app.use('/xrpc', toRequestListener(router.fetch))
295
+ * ```
296
+ */
213
297
  function toRequestListener(fetchHandler) {
214
298
  return ((req, res, next) => {
215
299
  handleRequest(req, res, fetchHandler).catch((err) => {
@@ -228,6 +312,44 @@ function toRequestListener(fetchHandler) {
228
312
  });
229
313
  });
230
314
  }
315
+ /**
316
+ * Creates an HTTP server configured for XRPC request handling.
317
+ *
318
+ * The server includes graceful shutdown support and can be used with
319
+ * either a fetch handler function or an object with a `fetch` method
320
+ * (like {@link LexRouter}).
321
+ *
322
+ * Note: This creates the server but does not start listening. Call
323
+ * `server.listen()` to start the server, or use {@link serve} for
324
+ * a combined create-and-listen operation.
325
+ *
326
+ * @typeParam Request - The request class type
327
+ * @typeParam Response - The response class type
328
+ * @param handler - A fetch handler or object with fetch method
329
+ * @param options - Server configuration options
330
+ * @returns An HTTP server with graceful shutdown support
331
+ *
332
+ * @example Basic usage
333
+ * ```typescript
334
+ * import { LexRouter } from '@atproto/lex-server'
335
+ * import { createServer, upgradeWebSocket } from '@atproto/lex-server/nodejs'
336
+ *
337
+ * const router = new LexRouter({ upgradeWebSocket })
338
+ * router.add(myMethod, myHandler)
339
+ *
340
+ * const server = createServer(router)
341
+ * server.listen(3000, () => {
342
+ * console.log('Server listening on port 3000')
343
+ * })
344
+ * ```
345
+ *
346
+ * @example With graceful termination timeout
347
+ * ```typescript
348
+ * const server = createServer(router, {
349
+ * gracefulTerminationTimeout: 10000 // 10 seconds
350
+ * })
351
+ * ```
352
+ */
231
353
  function createServer(handler, options = {}) {
232
354
  const fetchHandler = typeof handler === 'function' ? handler : handler.fetch.bind(handler);
233
355
  const listener = toRequestListener(fetchHandler);
@@ -257,6 +379,67 @@ function createServer(handler, options = {}) {
257
379
  });
258
380
  return server;
259
381
  }
382
+ /**
383
+ * Creates and starts an HTTP server, returning when it's ready to accept connections.
384
+ *
385
+ * This is a convenience function that combines {@link createServer} and `server.listen()`
386
+ * into a single async operation. The returned promise resolves once the server
387
+ * is actively listening.
388
+ *
389
+ * @typeParam Request - The request class type
390
+ * @typeParam Response - The response class type
391
+ * @param handler - A fetch handler or object with fetch method (like {@link LexRouter})
392
+ * @param options - Combined server and listen options
393
+ * @returns Promise resolving to the running server
394
+ *
395
+ * @example Basic usage
396
+ * ```typescript
397
+ * import { LexRouter } from '@atproto/lex-server'
398
+ * import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
399
+ *
400
+ * const router = new LexRouter({ upgradeWebSocket })
401
+ *
402
+ * // Register handlers
403
+ * router.add(getProfile, async (ctx) => {
404
+ * return { body: await db.getProfile(ctx.params.actor) }
405
+ * })
406
+ *
407
+ * // Start server on port 3000
408
+ * const server = await serve(router, { port: 3000 })
409
+ * console.log('Server listening on port 3000')
410
+ *
411
+ * // Graceful shutdown
412
+ * process.on('SIGTERM', () => server.terminate())
413
+ * process.on('SIGINT', () => server.terminate())
414
+ * ```
415
+ *
416
+ * @example With all options
417
+ * ```typescript
418
+ * const server = await serve(router, {
419
+ * port: 3000,
420
+ * host: '0.0.0.0',
421
+ * gracefulTerminationTimeout: 15000,
422
+ * })
423
+ * ```
424
+ *
425
+ * @example Using with await using (auto-cleanup)
426
+ * ```typescript
427
+ * async function main() {
428
+ * await using server = await serve(router, { port: 3000 })
429
+ *
430
+ * // Server is running...
431
+ * console.log('Server listening on port 3000')
432
+ *
433
+ * // Wait for termination signal
434
+ * await Promise.race([
435
+ * once(process, 'SIGINT'),
436
+ * once(process, 'SIGTERM'),
437
+ * ])
438
+ *
439
+ * // Server will be automatically terminated when scope exits
440
+ * }
441
+ * ```
442
+ */
260
443
  async function serve(handler, options) {
261
444
  const server = createServer(handler, options);
262
445
  server.listen(options);