@travetto/web-http 8.0.0-alpha.3 → 8.0.0-alpha.30

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/README.md CHANGED
@@ -13,10 +13,38 @@ npm install @travetto/web-http
13
13
  yarn add @travetto/web-http
14
14
  ```
15
15
 
16
- This module provides basic for running [http](https://nodejs.org/api/http.html). [https](https://nodejs.org/api/https.html) and [http2](https://nodejs.org/api/http2.html) servers, along with support for tls key generation during development.
16
+ This module provides basic for running [http](https://nodejs.org/api/http.html). [https](https://nodejs.org/api/https.html) and [http2](https://nodejs.org/api/http2.html) servers, along with support for tls key generation during development.
17
+
18
+ ## CLI - web:http
19
+ By default, the framework provides a default [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/registry/decorator.ts#L20) for [WebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/types.ts#L19) that will follow default behaviors, and spin up the server.
20
+
21
+ **Terminal: Help for web:http**
22
+ ```bash
23
+ $ trv web:http --help
24
+
25
+ Usage: web:http [options]
26
+
27
+ Description:
28
+ Start the configured web HTTP server for a module.
29
+
30
+ Initializes registry and server bindings, supports restart-aware development
31
+ flags, and can attempt to clear conflicting port owners in local workflows.
32
+
33
+ Options:
34
+ -p, --port <number> Port to run on
35
+ --kill-conflict, --no-kill-conflict Kill conflicting port owner (default: true)
36
+ -m, --module <module> Module to run for
37
+ --profile <string> Application profiles
38
+ --restart-on-change, --no-restart-on-change Should the invocation automatically restart on source changes (default: true)
39
+ -d, --debug-ipc Should the invocation support debugging via IPC (e.g. from VSCode)
40
+ --help display help for command
41
+
42
+ Examples:
43
+ Starting a web server on port 8000
44
+ trv web:http -m <MODULE> -p 8000
45
+ ```
17
46
 
18
47
  ## Running a Server
19
- By default, the framework provides a default [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/registry/decorator.ts#L27) for [WebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/types.ts#L19) that will follow default behaviors, and spin up the server.
20
48
 
21
49
  **Terminal: Standard application**
22
50
  ```bash
@@ -89,7 +117,6 @@ Listening on port { port: 3000 }
89
117
  **Code: Standard Web Http Config**
90
118
  ```typescript
91
119
  export class WebHttpConfig {
92
-
93
120
  /**
94
121
  * What version of HTTP to use
95
122
  * Version 2 requires SSL for direct browser access
@@ -125,8 +152,8 @@ export class WebHttpConfig {
125
152
 
126
153
  @PostConstruct()
127
154
  async finalizeConfig(): Promise<void> {
128
- this.tls ??= (this.httpVersion === '2' || !!this.tlsKeys);
129
- this.port = (this.port < 0 ? await NetUtil.getFreePort() : this.port);
155
+ this.tls ??= this.httpVersion === '2' || !!this.tlsKeys;
156
+ this.port = this.port < 0 ? await NetUtil.getFreePort() : this.port;
130
157
  this.bindAddress ||= NetUtil.getLocalAddress();
131
158
 
132
159
  if (!this.tls) {
@@ -138,9 +165,10 @@ export class WebHttpConfig {
138
165
  }
139
166
  this.tlsKeys = await WebTlsUtil.generateKeyPair();
140
167
  } else {
141
- if (this.tlsKeys.key.length < 100) { // We have files or resources
142
- this.tlsKeys.key = await RuntimeResources.readText(this.tlsKeys.key);
143
- this.tlsKeys.cert = await RuntimeResources.readText(this.tlsKeys.cert);
168
+ if (this.tlsKeys.key.length < 100) {
169
+ // We have files or resources
170
+ this.tlsKeys.key = await RuntimeResources.readUTF8(this.tlsKeys.key);
171
+ this.tlsKeys.cert = await RuntimeResources.readUTF8(this.tlsKeys.cert);
144
172
  }
145
173
  }
146
174
 
@@ -150,21 +178,20 @@ export class WebHttpConfig {
150
178
  ```
151
179
 
152
180
  ### Creating a Custom CLI Entry Point
153
- To customize a Web server, you may need to construct an entry point using the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/registry/decorator.ts#L27) decorator. This could look like:
181
+ To customize a Web server, you may need to construct an entry point using the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/registry/decorator.ts#L20) decorator. This could look like:
154
182
 
155
183
  **Code: Application entry point for Web Applications**
156
184
  ```typescript
157
- import { Env, toConcrete } from '@travetto/runtime';
158
185
  import { CliCommand } from '@travetto/cli';
159
186
  import { DependencyRegistryIndex } from '@travetto/di';
160
187
  import { Registry } from '@travetto/registry';
161
- import { type WebHttpServer, WebHttpConfig } from '@travetto/web-http';
188
+ import { Env, toConcrete } from '@travetto/runtime';
189
+ import { WebHttpConfig, type WebHttpServer } from '@travetto/web-http';
162
190
 
163
191
  import './config-override.ts';
164
192
 
165
193
  @CliCommand({ runTarget: true })
166
194
  export class SampleApp {
167
-
168
195
  preMain(): void {
169
196
  Env.NODE_ENV.set('production');
170
197
  }
@@ -260,7 +287,6 @@ Listening on port { port: 3000 }
260
287
  **Code: Implementation**
261
288
  ```typescript
262
289
  export class NodeWebHttpServer implements WebHttpServer {
263
-
264
290
  @Inject()
265
291
  serverConfig: WebHttpConfig;
266
292
 
@@ -271,7 +297,7 @@ export class NodeWebHttpServer implements WebHttpServer {
271
297
  configService: ConfigurationService;
272
298
 
273
299
  async serve(): Promise<WebServerHandle> {
274
- const handle = await WebHttpUtil.startHttpServer({ ...this.serverConfig, dispatcher: this.router, });
300
+ const handle = await WebHttpUtil.startHttpServer({ ...this.serverConfig, dispatcher: this.router });
275
301
  console.log('Initialized', await this.configService.initBanner());
276
302
  console.log('Listening', { port: this.serverConfig.port });
277
303
  return handle;
@@ -279,7 +305,7 @@ export class NodeWebHttpServer implements WebHttpServer {
279
305
  }
280
306
  ```
281
307
 
282
- Current the [NodeWebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/node.ts#L13) is the only provided [WebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/types.ts#L19) implementation. It supports http/1.1, http/2, and tls, and is the same foundation as used by express, koa, and other popular frameworks.
308
+ Current the [NodeWebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/node.ts#L13) is the only provided [WebHttpServer](https://github.com/travetto/travetto/tree/main/module/web-http/src/types.ts#L19) implementation. It supports http/1.1, http/2, and tls, and is the same foundation as used by express, koa, and other popular frameworks.
283
309
 
284
310
  ## Standard Utilities
285
311
  The module also provides standard utilities for starting http servers programmatically:
@@ -315,18 +341,18 @@ static buildHandler(dispatcher: WebDispatcher): (request: HttpRequest, response:
315
341
  return async (request: HttpRequest, response: HttpResponse): Promise<void> => {
316
342
  const webRequest = this.toWebRequest(request);
317
343
  const webResponse = await dispatcher.dispatch({ request: webRequest });
318
- this.respondToServerResponse(webResponse, response);
344
+ await this.respondToServerResponse(webResponse, response);
319
345
  };
320
346
  }
321
347
  ```
322
348
 
323
349
  we can see the structure for integrating the server behavior with the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative support for creating Web Applications") module dispatcher:
324
- * Converting the node primitive request to a [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11)
350
+ * Converting the node primitive request to a [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11)
325
351
  * Dispatching the request through the framework
326
352
  * Receiving the [WebResponse](https://github.com/travetto/travetto/tree/main/module/web/src/types/response.ts#L3) and sending that back over the primitive response.
327
353
 
328
354
  ## TLS Support
329
- Additionally the framework supports TLS out of the box, by allowing you to specify your public and private keys for the cert. In dev mode, the framework will also automatically generate a self-signed cert if:
355
+ Additionally the framework supports TLS out of the box, by allowing you to specify your public and private keys for the cert. In dev mode, the framework will also automatically generate a self-signed cert if:
330
356
  * TLS support is configured
331
357
  * [node-forge](https://www.npmjs.com/package/node-forge) is installed
332
358
  * Not running in production
@@ -334,4 +360,4 @@ Additionally the framework supports TLS out of the box, by allowing you to speci
334
360
 
335
361
  This is useful for local development where you implicitly trust the cert.
336
362
 
337
- TLS support can be enabled by setting `web.http.tls: true` in your config. The key/cert can be specified as string directly in the config file/environment variables. The key/cert can also be specified as a path to be picked up by [RuntimeResources](https://github.com/travetto/travetto/tree/main/module/runtime/src/resources.ts#L8).
363
+ TLS support can be enabled by setting `web.http.tls: true` in your config. The key/cert can be specified as string directly in the config file/environment variables. The key/cert can also be specified as a path to be picked up by [RuntimeResources](https://github.com/travetto/travetto/tree/main/module/runtime/src/resources.ts#L8).
package/__index__.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './src/config.ts';
2
2
  export * from './src/http.ts';
3
+ export * from './src/node.ts';
3
4
  export * from './src/tls.ts';
4
5
  export * from './src/types.ts';
5
- export * from './src/node.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/web-http",
3
- "version": "8.0.0-alpha.3",
3
+ "version": "8.0.0-alpha.30",
4
4
  "type": "module",
5
5
  "description": "Web HTTP Server Support",
6
6
  "keywords": [
@@ -27,11 +27,11 @@
27
27
  "directory": "module/web-http"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/web": "^8.0.0-alpha.3"
30
+ "@travetto/web": "^8.0.0-alpha.29"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/cli": "^8.0.0-alpha.4",
34
- "@travetto/test": "^8.0.0-alpha.3"
33
+ "@travetto/cli": "^8.0.0-alpha.32",
34
+ "@travetto/test": "^8.0.0-alpha.25"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@travetto/test": {
package/src/config.ts CHANGED
@@ -1,18 +1,17 @@
1
1
  import { Config, EnvVar } from '@travetto/config';
2
+ import { PostConstruct } from '@travetto/di';
3
+ import { Runtime, RuntimeError, RuntimeResources } from '@travetto/runtime';
2
4
  import { Ignore, Secret } from '@travetto/schema';
3
- import { RuntimeError, Runtime, RuntimeResources } from '@travetto/runtime';
4
5
  import { NetUtil } from '@travetto/web';
5
- import { PostConstruct } from '@travetto/di';
6
6
 
7
- import type { WebSecureKeyPair } from './types.ts';
8
7
  import { WebTlsUtil } from './tls.ts';
8
+ import type { WebSecureKeyPair } from './types.ts';
9
9
 
10
10
  /**
11
11
  * Web HTTP configuration
12
12
  */
13
13
  @Config('web.http')
14
14
  export class WebHttpConfig {
15
-
16
15
  /**
17
16
  * What version of HTTP to use
18
17
  * Version 2 requires SSL for direct browser access
@@ -48,8 +47,8 @@ export class WebHttpConfig {
48
47
 
49
48
  @PostConstruct()
50
49
  async finalizeConfig(): Promise<void> {
51
- this.tls ??= (this.httpVersion === '2' || !!this.tlsKeys);
52
- this.port = (this.port < 0 ? await NetUtil.getFreePort() : this.port);
50
+ this.tls ??= this.httpVersion === '2' || !!this.tlsKeys;
51
+ this.port = this.port < 0 ? await NetUtil.getFreePort() : this.port;
53
52
  this.bindAddress ||= NetUtil.getLocalAddress();
54
53
 
55
54
  if (!this.tls) {
@@ -61,12 +60,13 @@ export class WebHttpConfig {
61
60
  }
62
61
  this.tlsKeys = await WebTlsUtil.generateKeyPair();
63
62
  } else {
64
- if (this.tlsKeys.key.length < 100) { // We have files or resources
65
- this.tlsKeys.key = await RuntimeResources.readText(this.tlsKeys.key);
66
- this.tlsKeys.cert = await RuntimeResources.readText(this.tlsKeys.cert);
63
+ if (this.tlsKeys.key.length < 100) {
64
+ // We have files or resources
65
+ this.tlsKeys.key = await RuntimeResources.readUTF8(this.tlsKeys.key);
66
+ this.tlsKeys.cert = await RuntimeResources.readUTF8(this.tlsKeys.cert);
67
67
  }
68
68
  }
69
69
 
70
70
  this.fetchUrl = `${this.tls ? 'https' : 'http'}://${this.bindAddress}:${this.port}`;
71
71
  }
72
- }
72
+ }
package/src/http.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type net from 'node:net';
2
1
  import http from 'node:http';
3
2
  import http2 from 'node:http2';
4
3
  import https from 'node:https';
4
+ import type net from 'node:net';
5
5
  import { TLSSocket } from 'node:tls';
6
6
 
7
- import { WebBodyUtil, WebCommonUtil, type WebDispatcher, WebRequest, WebResponse } from '@travetto/web';
8
7
  import { type BinaryType, BinaryUtil, castTo, ShutdownManager } from '@travetto/runtime';
8
+ import { WebBodyUtil, WebCommonUtil, type WebDispatcher, WebRequest, WebResponse } from '@travetto/web';
9
9
 
10
10
  import type { WebSecureKeyPair, WebServerHandle } from './types.ts';
11
11
 
@@ -24,7 +24,6 @@ type WebHttpServerConfig = {
24
24
  };
25
25
 
26
26
  export class WebHttpUtil {
27
-
28
27
  /**
29
28
  * Build a simple request handler
30
29
  * @param dispatcher
@@ -33,7 +32,7 @@ export class WebHttpUtil {
33
32
  return async (request: HttpRequest, response: HttpResponse): Promise<void> => {
34
33
  const webRequest = this.toWebRequest(request);
35
34
  const webResponse = await dispatcher.dispatch({ request: webRequest });
36
- this.respondToServerResponse(webResponse, response);
35
+ await this.respondToServerResponse(webResponse, response);
37
36
  };
38
37
  }
39
38
 
@@ -69,9 +68,7 @@ export class WebHttpUtil {
69
68
  socket.on('close', () => activeConnections.delete(socket));
70
69
  });
71
70
 
72
- target.listen(config.port, config.bindAddress)
73
- .on('error', reject)
74
- .on('listening', resolve);
71
+ target.listen(config.port, config.bindAddress).on('error', reject).on('listening', resolve);
75
72
 
76
73
  await promise;
77
74
 
@@ -115,7 +112,7 @@ export class WebHttpUtil {
115
112
  },
116
113
  httpMethod: castTo(request.method?.toUpperCase()),
117
114
  path,
118
- httpQuery: Object.fromEntries(new URLSearchParams(query)),
115
+ httpQuery: Object.fromEntries(new URLSearchParams(query))
119
116
  },
120
117
  headers: request.headers,
121
118
  body: WebBodyUtil.markRawBinary(request)
@@ -127,14 +124,20 @@ export class WebHttpUtil {
127
124
  */
128
125
  static async respondToServerResponse(webResponse: WebResponse, response: HttpResponse): Promise<void> {
129
126
  const binaryResponse = new WebResponse<BinaryType>({ context: webResponse.context, ...WebBodyUtil.toBinaryMessage(webResponse) });
130
- binaryResponse.headers.forEach((value, key) => response.setHeader(key, value));
127
+ binaryResponse.headers.forEach((value, key) => {
128
+ response.setHeader(key, value);
129
+ });
131
130
  response.statusCode = WebCommonUtil.getStatusCode(binaryResponse);
132
131
 
133
132
  if (binaryResponse.body) {
134
- await BinaryUtil.pipeline(binaryResponse.body, response);
133
+ try {
134
+ await BinaryUtil.pipeline(binaryResponse.body, response);
135
+ } catch (error) {
136
+ console.error('Failed to send response', { error });
137
+ }
135
138
  }
136
- if (!response.closed) {
139
+ if (!response.closed && !response.destroyed) {
137
140
  response.end();
138
141
  }
139
142
  }
140
- }
143
+ }
package/src/node.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import type { ConfigurationService } from '@travetto/config';
1
2
  import { Inject, Injectable } from '@travetto/di';
2
3
  import type { StandardWebRouter } from '@travetto/web';
3
- import type { ConfigurationService } from '@travetto/config';
4
4
 
5
5
  import type { WebHttpConfig } from './config.ts';
6
6
  import { WebHttpUtil } from './http.ts';
@@ -11,7 +11,6 @@ import type { WebHttpServer, WebServerHandle } from './types.ts';
11
11
  */
12
12
  @Injectable()
13
13
  export class NodeWebHttpServer implements WebHttpServer {
14
-
15
14
  @Inject()
16
15
  serverConfig: WebHttpConfig;
17
16
 
@@ -22,9 +21,9 @@ export class NodeWebHttpServer implements WebHttpServer {
22
21
  configService: ConfigurationService;
23
22
 
24
23
  async serve(): Promise<WebServerHandle> {
25
- const handle = await WebHttpUtil.startHttpServer({ ...this.serverConfig, dispatcher: this.router, });
24
+ const handle = await WebHttpUtil.startHttpServer({ ...this.serverConfig, dispatcher: this.router });
26
25
  console.log('Initialized', await this.configService.initBanner());
27
26
  console.log('Listening', { port: this.serverConfig.port });
28
27
  return handle;
29
28
  }
30
- }
29
+ }
package/src/tls.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Runtime } from '@travetto/runtime';
1
+ import { type Any, Runtime } from '@travetto/runtime';
2
2
 
3
3
  import type { WebSecureKeyPair } from './types.ts';
4
4
 
@@ -6,13 +6,12 @@ import type { WebSecureKeyPair } from './types.ts';
6
6
  * Utils for generating key pairs
7
7
  */
8
8
  export class WebTlsUtil {
9
-
10
9
  /**
11
10
  * Generate TLS key pair on demand
12
11
  * @param subj The subject for the app
13
12
  */
14
13
  static async generateKeyPair(subj = { C: 'US', ST: 'CA', O: 'TRAVETTO', OU: 'WEB', CN: 'DEV' }): Promise<WebSecureKeyPair> {
15
- let forge;
14
+ let forge: Any;
16
15
 
17
16
  try {
18
17
  forge = (await import('node-forge')).default;
@@ -47,4 +46,4 @@ export class WebTlsUtil {
47
46
  key: pki.privateKeyToPem(keys.privateKey)
48
47
  };
49
48
  }
50
- }
49
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Any } from '@travetto/runtime';
2
2
 
3
- export type WebSecureKeyPair = { cert: string, key: string };
3
+ export type WebSecureKeyPair = { cert: string; key: string };
4
4
 
5
5
  /**
6
6
  * Handle for a web server
@@ -18,4 +18,4 @@ export type WebServerHandle<T = Any> = {
18
18
  */
19
19
  export interface WebHttpServer {
20
20
  serve(): Promise<WebServerHandle>;
21
- }
21
+ }
@@ -1,17 +1,23 @@
1
- import { Runtime, toConcrete } from '@travetto/runtime';
1
+ import { CliCommand, type CliCommandShape, CliDebugIpcFlag, CliModuleFlag, CliProfilesFlag, CliRestartOnChangeFlag } from '@travetto/cli';
2
2
  import { DependencyRegistryIndex } from '@travetto/di';
3
- import { CliCommand, CliDebugIpcFlag, CliModuleFlag, CliProfilesFlag, CliRestartOnChangeFlag, type CliCommandShape } from '@travetto/cli';
4
- import { NetUtil } from '@travetto/web';
5
3
  import { Registry } from '@travetto/registry';
4
+ import { Runtime, toConcrete } from '@travetto/runtime';
5
+ import { NetUtil } from '@travetto/web';
6
6
 
7
7
  import type { WebHttpServer } from '../src/types.ts';
8
8
 
9
9
  /**
10
- * Run a web server
10
+ * Start the configured web HTTP server for a module.
11
+ *
12
+ * Initializes registry and server bindings, supports restart-aware development
13
+ * flags, and can attempt to clear conflicting port owners in local workflows.
14
+ *
15
+ * @example
16
+ * Starting a web server on port 8000
17
+ * > trv web:http -m <MODULE> -p 8000
11
18
  */
12
19
  @CliCommand()
13
20
  export class WebHttpCommand implements CliCommandShape {
14
-
15
21
  /** Port to run on */
16
22
  port?: number;
17
23
 
@@ -25,7 +31,7 @@ export class WebHttpCommand implements CliCommandShape {
25
31
  profile: string[];
26
32
 
27
33
  @CliRestartOnChangeFlag()
28
- restartOnChange: boolean = true;
34
+ restartOnChange: boolean = Runtime.localDevelopment;
29
35
 
30
36
  @CliDebugIpcFlag()
31
37
  debugIpc?: boolean;
@@ -53,4 +59,4 @@ export class WebHttpCommand implements CliCommandShape {
53
59
  throw err;
54
60
  }
55
61
  }
56
- }
62
+ }
@@ -1,6 +1,6 @@
1
1
  import { Inject, Injectable } from '@travetto/di';
2
- import { type WebFilterContext, WebResponse, type WebDispatcher, WebBodyUtil } from '@travetto/web';
3
2
  import { BinaryUtil, castTo } from '@travetto/runtime';
3
+ import { WebBodyUtil, type WebDispatcher, type WebFilterContext, WebResponse } from '@travetto/web';
4
4
 
5
5
  import { WebTestDispatchUtil } from '@travetto/web/support/test/dispatch-util.ts';
6
6
 
@@ -11,22 +11,21 @@ import type { WebHttpConfig } from '../../src/config.ts';
11
11
  */
12
12
  @Injectable()
13
13
  export class FetchWebDispatcher implements WebDispatcher {
14
-
15
14
  @Inject()
16
15
  config: WebHttpConfig;
17
16
 
18
17
  async dispatch({ request }: WebFilterContext): Promise<WebResponse> {
19
18
  const baseRequest = await WebTestDispatchUtil.applyRequestBody(request);
20
19
  const finalPath = WebTestDispatchUtil.buildPath(baseRequest);
21
- const body: RequestInit['body'] = WebBodyUtil.isRawBinary(request.body) ?
22
- await BinaryUtil.toBinaryArray(request.body) :
23
- castTo(request.body);
24
- const { context: { httpMethod: method }, headers } = request;
20
+ const body: RequestInit['body'] = WebBodyUtil.isRawBinary(request.body)
21
+ ? await BinaryUtil.toBinaryArray(request.body)
22
+ : castTo(request.body);
23
+ const {
24
+ context: { httpMethod: method },
25
+ headers
26
+ } = request;
25
27
 
26
- const response = await fetch(
27
- `${this.config.fetchUrl}${finalPath}`,
28
- { method, headers, body }
29
- );
28
+ const response = await fetch(`${this.config.fetchUrl}${finalPath}`, { method, headers, body });
30
29
 
31
30
  return await WebTestDispatchUtil.finalizeResponseBody(
32
31
  new WebResponse({
@@ -36,4 +35,4 @@ export class FetchWebDispatcher implements WebDispatcher {
36
35
  })
37
36
  );
38
37
  }
39
- }
38
+ }