@ldtr/nestjs-webtransport 0.0.1 → 0.0.3

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
@@ -26,15 +26,10 @@ import { readFile } from "node:fs/promises"
26
26
 
27
27
  @WebTransportServer({ name: "main" })
28
28
  export class MainWebTransportServer implements WebTransportServerOptionsFactory {
29
- constructor(
30
- private readonly configService: ConfigService
31
- ) {
32
- }
33
-
34
29
  async options(): Promise<HttpServerInit> {
35
30
  const cert = await readFile("./certs/cert.pem", { encoding: "utf-8" })
36
31
  const privKey = await readFile("./certs/key.pem", { encoding: "utf-8" })
37
- const secret = this.configService.secret
32
+ const secret = "change-me"
38
33
  return {
39
34
  port: 3001,
40
35
  host: "0.0.0.0",
@@ -48,21 +43,58 @@ export class MainWebTransportServer implements WebTransportServerOptionsFactory
48
43
 
49
44
  @WebTransportGateway({ server: "main", path: "/events" })
50
45
  export class EventsGateway implements WebTransportGatewayLifecycle {
51
- onSession(session: WtSession): void {
52
- // new WebTransport session
46
+ async onSession(session: WtSession): Promise<void> {
47
+ console.log("new WebTransport session")
48
+ try {
49
+ // Create a unidirectional (Write/Only) stream from the server to the client.
50
+ const stream = await session.createStreamWO()
51
+
52
+ // This stream can be used to send data, but it cannot read data back.
53
+ await stream.write(new TextEncoder().encode("Hello from the server."))
54
+
55
+ await stream.closeWritable()
56
+ } catch (error) {
57
+ console.error(error)
58
+ }
59
+ }
60
+
61
+ async onStreamRW(stream: WtStreamRW): Promise<void> {
62
+ console.log("New bidirectional stream opened by client (Read/Write).")
63
+ await this.readStream(stream,
64
+ chunk => console.log(`Received chunk: ${chunk.length} byte(s).`),
65
+ async () => {
66
+ await stream.write(new TextEncoder().encode("Server received all chunks."))
67
+ await stream.closeWritable()
68
+ }
69
+ )
53
70
  }
54
71
 
55
- onStreamRW(stream: WtStreamRW): void {
56
- // new bidirectional stream
72
+ async onStreamRO(stream: WtStreamRO): Promise<void> {
73
+ console.log("New unidirectional stream opened by client (Read/Only).")
74
+ await this.readStream(stream)
57
75
  }
76
+
77
+ private async readStream(
78
+ stream: WtStreamRW | WtStreamRO,
79
+ onChunk?: (chunk: Uint8Array) => void | Promise<void>,
80
+ onClosed?: () => void | Promise<void>
81
+ ): Promise<void> {
82
+ try {
83
+ // The stream must be read immediately to consume incoming client data.
84
+ for await (const chunk of stream.read())
85
+ void onChunk?.(chunk)
58
86
 
59
- onStreamRO(stream: WtStreamRO): void {
60
- // new unidirectional stream
87
+ // Client closed stream
88
+ await onClosed?.()
89
+ } catch (error) {
90
+ console.error("Failed to read stream:", error)
91
+ throw error
92
+ }
61
93
  }
62
94
  }
63
95
  ```
64
96
 
65
- Then import them in your application with the ```WebTransportModule```:
97
+ Then import them in your application with the `WebTransportModule`:
66
98
 
67
99
  ```ts
68
100
  import { WebTransportModule } from "@ldtr/nestjs-webtransport"
@@ -70,7 +102,7 @@ import { Module } from "@nestjs/common"
70
102
 
71
103
  @Module({
72
104
  imports: [
73
- WebTransportModule],
105
+ WebTransportModule],
74
106
  providers: [
75
107
  EventsGateway,
76
108
  MainWebTransportServer]
package/dist/index.cjs CHANGED
@@ -170,8 +170,21 @@ var WtStreamWO = class extends AbstractWtStream {
170
170
  }
171
171
  };
172
172
  //#endregion
173
+ //#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorateMetadata.js
174
+ function __decorateMetadata(k, v) {
175
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
176
+ }
177
+ //#endregion
178
+ //#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorate.js
179
+ function __decorate(decorators, target, key, desc) {
180
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
181
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
182
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
183
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
184
+ }
185
+ //#endregion
173
186
  //#region src/lib/explorers/webtransport-gateway.explorer.ts
174
- var WebTransportGatewayExplorer = @((0, _nestjs_common.Injectable)()) class {
187
+ let WebTransportGatewayExplorer = class WebTransportGatewayExplorer {
175
188
  discoveryService;
176
189
  constructor(discoveryService) {
177
190
  this.discoveryService = discoveryService;
@@ -196,9 +209,10 @@ var WebTransportGatewayExplorer = @((0, _nestjs_common.Injectable)()) class {
196
209
  return gatewaysByNameByPath;
197
210
  }
198
211
  };
212
+ WebTransportGatewayExplorer = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof _nestjs_core.DiscoveryService === "undefined" ? Object : _nestjs_core.DiscoveryService])], WebTransportGatewayExplorer);
199
213
  //#endregion
200
214
  //#region src/lib/explorers/webtransport-server.explorer.ts
201
- var WebTransportServerExplorer = @((0, _nestjs_common.Injectable)()) class {
215
+ let WebTransportServerExplorer = class WebTransportServerExplorer {
202
216
  discoveryService;
203
217
  constructor(discoveryService) {
204
218
  this.discoveryService = discoveryService;
@@ -217,45 +231,14 @@ var WebTransportServerExplorer = @((0, _nestjs_common.Injectable)()) class {
217
231
  return serversByName;
218
232
  }
219
233
  };
220
- //#endregion
221
- //#region src/lib/bootstraps/web-transport-bootstrapper.ts
222
- var WebTransportBootstrapper = @((0, _nestjs_common.Injectable)()) class WebTransportBootstrapper {
223
- serverFactory;
224
- webTransportExplorer;
225
- logger = new _nestjs_common.Logger(WebTransportBootstrapper.name);
226
- h3ServersWithInfo = [];
227
- constructor(serverFactory, webTransportExplorer) {
228
- this.serverFactory = serverFactory;
229
- this.webTransportExplorer = webTransportExplorer;
230
- }
231
- async onApplicationBootstrap() {
232
- const serverBindings = this.webTransportExplorer.discover();
233
- this.h3ServersWithInfo = await this.serverFactory.createH3Servers(serverBindings);
234
- await Promise.all(this.h3ServersWithInfo.map(async ({ options, name, h3Server }) => {
235
- try {
236
- h3Server.startServer();
237
- await h3Server.ready;
238
- this.logger.log(`WebTransport server "${name}" is ready on ${options.host}:${options.port}`);
239
- } catch (e) {
240
- this.logger.error(`Failed to start WebTransport server "${name}" on ${options.host}:${options.port}`);
241
- throw e;
242
- }
243
- }));
244
- }
245
- async onApplicationShutdown() {
246
- await Promise.all(this.h3ServersWithInfo.map(async ({ name, h3Server }) => {
247
- h3Server.stopServer();
248
- await h3Server.closed;
249
- this.logger.log(`WebTransport server "${name}" stopped`);
250
- }));
251
- }
252
- };
234
+ WebTransportServerExplorer = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof _nestjs_core.DiscoveryService === "undefined" ? Object : _nestjs_core.DiscoveryService])], WebTransportServerExplorer);
253
235
  //#endregion
254
236
  //#region src/lib/explorers/web-transport.explorer.ts
255
- var WebTransportExplorer = @((0, _nestjs_common.Injectable)()) class WebTransportExplorer {
237
+ var _WebTransportExplorer;
238
+ let WebTransportExplorer = _WebTransportExplorer = class WebTransportExplorer {
256
239
  webTransportServerExplorer;
257
240
  webTransportGatewayExplorer;
258
- logger = new _nestjs_common.Logger(WebTransportExplorer.name);
241
+ logger = new _nestjs_common.Logger(_WebTransportExplorer.name);
259
242
  constructor(webTransportServerExplorer, webTransportGatewayExplorer) {
260
243
  this.webTransportServerExplorer = webTransportServerExplorer;
261
244
  this.webTransportGatewayExplorer = webTransportGatewayExplorer;
@@ -276,6 +259,7 @@ var WebTransportExplorer = @((0, _nestjs_common.Injectable)()) class WebTranspor
276
259
  return serverBindings;
277
260
  }
278
261
  };
262
+ WebTransportExplorer = _WebTransportExplorer = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof WebTransportServerExplorer === "undefined" ? Object : WebTransportServerExplorer, typeof WebTransportGatewayExplorer === "undefined" ? Object : WebTransportGatewayExplorer])], WebTransportExplorer);
279
263
  //#endregion
280
264
  //#region src/lib/bootstraps/consumers/abstract-stream.consumer.ts
281
265
  var AbstractStreamConsumer = class {
@@ -289,8 +273,9 @@ var AbstractStreamConsumer = class {
289
273
  };
290
274
  //#endregion
291
275
  //#region src/lib/bootstraps/consumers/stream-ro.consumer.ts
292
- var StreamROConsumer = @((0, _nestjs_common.Injectable)()) class StreamROConsumer extends AbstractStreamConsumer {
293
- logger = new _nestjs_common.Logger(StreamROConsumer.name);
276
+ var _StreamROConsumer;
277
+ let StreamROConsumer = _StreamROConsumer = class StreamROConsumer extends AbstractStreamConsumer {
278
+ logger = new _nestjs_common.Logger(_StreamROConsumer.name);
294
279
  generateStream(session) {
295
280
  return generateUStream(session);
296
281
  }
@@ -301,10 +286,12 @@ var StreamROConsumer = @((0, _nestjs_common.Injectable)()) class StreamROConsume
301
286
  fireAndForget(() => gateway.onStreamRO?.(stream), this.logger);
302
287
  }
303
288
  };
289
+ StreamROConsumer = _StreamROConsumer = __decorate([(0, _nestjs_common.Injectable)()], StreamROConsumer);
304
290
  //#endregion
305
291
  //#region src/lib/bootstraps/consumers/stream-rw.consumer.ts
306
- var StreamRWConsumer = @((0, _nestjs_common.Injectable)()) class StreamRWConsumer extends AbstractStreamConsumer {
307
- logger = new _nestjs_common.Logger(StreamRWConsumer.name);
292
+ var _StreamRWConsumer;
293
+ let StreamRWConsumer = _StreamRWConsumer = class StreamRWConsumer extends AbstractStreamConsumer {
294
+ logger = new _nestjs_common.Logger(_StreamRWConsumer.name);
308
295
  generateStream(session) {
309
296
  return generateBStream(session);
310
297
  }
@@ -315,6 +302,7 @@ var StreamRWConsumer = @((0, _nestjs_common.Injectable)()) class StreamRWConsume
315
302
  fireAndForget(() => gateway.onStreamRW?.(stream), this.logger);
316
303
  }
317
304
  };
305
+ StreamRWConsumer = _StreamRWConsumer = __decorate([(0, _nestjs_common.Injectable)()], StreamRWConsumer);
318
306
  //#endregion
319
307
  //#region src/lib/classes/wt-session.ts
320
308
  var WtSession = class {
@@ -333,6 +321,7 @@ var WtSession = class {
333
321
  };
334
322
  //#endregion
335
323
  //#region src/lib/bootstraps/server-factory.ts
324
+ var _ServerFactory;
336
325
  let webtransport;
337
326
  const getLib = eval(`import('@fails-components/webtransport')`).then((module) => {
338
327
  webtransport = module;
@@ -349,10 +338,10 @@ function createHandshakeResponse(request, host, status) {
349
338
  status
350
339
  };
351
340
  }
352
- var ServerFactory = @((0, _nestjs_common.Injectable)()) class ServerFactory {
341
+ let ServerFactory = _ServerFactory = class ServerFactory {
353
342
  streamRWConsumer;
354
343
  streamROConsumer;
355
- logger = new _nestjs_common.Logger(ServerFactory.name);
344
+ logger = new _nestjs_common.Logger(_ServerFactory.name);
356
345
  constructor(streamRWConsumer, streamROConsumer) {
357
346
  this.streamRWConsumer = streamRWConsumer;
358
347
  this.streamROConsumer = streamROConsumer;
@@ -406,9 +395,46 @@ var ServerFactory = @((0, _nestjs_common.Injectable)()) class ServerFactory {
406
395
  this.streamROConsumer.consume(gateway, webTransportSession, session).catch((error) => this.logger.error(error));
407
396
  }
408
397
  };
398
+ ServerFactory = _ServerFactory = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof StreamRWConsumer === "undefined" ? Object : StreamRWConsumer, typeof StreamROConsumer === "undefined" ? Object : StreamROConsumer])], ServerFactory);
399
+ //#endregion
400
+ //#region src/lib/bootstraps/web-transport-bootstrapper.ts
401
+ var _WebTransportBootstrapper;
402
+ let WebTransportBootstrapper = _WebTransportBootstrapper = class WebTransportBootstrapper {
403
+ serverFactory;
404
+ webTransportExplorer;
405
+ logger = new _nestjs_common.Logger(_WebTransportBootstrapper.name);
406
+ h3ServersWithInfo = [];
407
+ constructor(serverFactory, webTransportExplorer) {
408
+ this.serverFactory = serverFactory;
409
+ this.webTransportExplorer = webTransportExplorer;
410
+ }
411
+ async onApplicationBootstrap() {
412
+ const serverBindings = this.webTransportExplorer.discover();
413
+ this.h3ServersWithInfo = await this.serverFactory.createH3Servers(serverBindings);
414
+ await Promise.all(this.h3ServersWithInfo.map(async ({ options, name, h3Server }) => {
415
+ try {
416
+ h3Server.startServer();
417
+ await h3Server.ready;
418
+ this.logger.log(`WebTransport server "${name}" is ready on ${options.host}:${options.port}`);
419
+ } catch (e) {
420
+ this.logger.error(`Failed to start WebTransport server "${name}" on ${options.host}:${options.port}`);
421
+ throw e;
422
+ }
423
+ }));
424
+ }
425
+ async onApplicationShutdown() {
426
+ await Promise.all(this.h3ServersWithInfo.map(async ({ name, h3Server }) => {
427
+ h3Server.stopServer();
428
+ await h3Server.closed;
429
+ this.logger.log(`WebTransport server "${name}" stopped`);
430
+ }));
431
+ }
432
+ };
433
+ WebTransportBootstrapper = _WebTransportBootstrapper = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof ServerFactory === "undefined" ? Object : ServerFactory, typeof WebTransportExplorer === "undefined" ? Object : WebTransportExplorer])], WebTransportBootstrapper);
409
434
  //#endregion
410
435
  //#region src/lib/webtransport.module.ts
411
- var WebTransportModule = @((0, _nestjs_common.Module)({
436
+ let WebTransportModule = class WebTransportModule {};
437
+ WebTransportModule = __decorate([(0, _nestjs_common.Module)({
412
438
  imports: [_nestjs_core.DiscoveryModule],
413
439
  providers: [
414
440
  ServerFactory,
@@ -419,10 +445,15 @@ var WebTransportModule = @((0, _nestjs_common.Module)({
419
445
  WebTransportServerExplorer,
420
446
  WebTransportGatewayExplorer
421
447
  ]
422
- })) class {};
448
+ })], WebTransportModule);
423
449
  //#endregion
424
450
  exports.WebTransportGateway = WebTransportGateway;
425
- exports.WebTransportModule = WebTransportModule;
451
+ Object.defineProperty(exports, "WebTransportModule", {
452
+ enumerable: true,
453
+ get: function() {
454
+ return WebTransportModule;
455
+ }
456
+ });
426
457
  exports.WebTransportServer = WebTransportServer;
427
458
  exports.WtSession = WtSession;
428
459
  exports.WtStreamRO = WtStreamRO;
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
- {
1
+ {
2
2
  "name": "@ldtr/nestjs-webtransport",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
+ "description": "NestJS module for creating WebTransport servers and gateways.",
4
5
  "type": "module",
5
- "files": ["dist"],
6
+ "files": [
7
+ "dist"
8
+ ],
6
9
  "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
10
+ "types": "./dist/index.d.cts",
9
11
  "exports": {
10
12
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
+ "types": "./dist/index.d.cts",
13
14
  "require": "./dist/index.cjs"
14
15
  }
15
16
  },
@@ -21,12 +22,24 @@
21
22
  "@fails-components/webtransport": "^1.6.3",
22
23
  "@fails-components/webtransport-transport-http3-quiche": "^1.6.3",
23
24
  "reflect-metadata": "^0.1.13",
24
- "tslib": "^2.3.0",
25
+ "tslib": "^2.3.0"
26
+ },
27
+ "devDependencies": {
25
28
  "tsdown": "^0.22.3",
26
29
  "typescript": "^6.0.3"
27
30
  },
28
31
  "peerDependencies": {
29
32
  "@nestjs/common": "^11.0.0",
30
33
  "@nestjs/core": "^11.0.0"
31
- }
34
+ },
35
+ "keywords": [
36
+ "nestjs",
37
+ "webtransport",
38
+ "webtransport-server",
39
+ "http3",
40
+ "quic",
41
+ "gateway",
42
+ "module",
43
+ "typescript"
44
+ ]
32
45
  }
package/dist/index.d.mts DELETED
@@ -1,87 +0,0 @@
1
- import { DiscoveryService } from "@nestjs/core";
2
- import { HttpServerInit, HttpServerInit as HttpServerInit$1, WebTransportBidirectionalStream, WebTransportReceiveStream, WebTransportSendStream, WebTransportSession } from "@fails-components/webtransport";
3
-
4
- //#region src/lib/decorators/webtransport-gateway.decorator.d.ts
5
- type WebTransportGatewayOptions = {
6
- readonly path: string;
7
- readonly server: string;
8
- };
9
- declare function WebTransportGateway(options: WebTransportGatewayOptions): ClassDecorator;
10
- //#endregion
11
- //#region src/lib/decorators/webtransport-server.decorator.d.ts
12
- interface WebTransportServerOptions {
13
- readonly name: string;
14
- }
15
- declare function WebTransportServer(options: WebTransportServerOptions): ClassDecorator;
16
- //#endregion
17
- //#region src/lib/explorers/webtransport-server.explorer.d.ts
18
- type WebTransportServerOptionsFactory = {
19
- options(): HttpServerInit$1 | Promise<HttpServerInit$1>;
20
- };
21
- //#endregion
22
- //#region src/lib/classes/wt-session.d.ts
23
- declare class WtSession {
24
- private readonly webTransportSession;
25
- constructor(webTransportSession: WebTransportSession);
26
- createStreamWO(): Promise<WtStreamWO>;
27
- createStreamRW(): Promise<WtStreamRW>;
28
- }
29
- //#endregion
30
- //#region src/lib/classes/wt-stream.d.ts
31
- declare abstract class AbstractWtStream<TWebStream> {
32
- readonly session: WtSession;
33
- protected readonly webTransportStream: TWebStream;
34
- protected constructor(session: WtSession, webTransportStream: TWebStream);
35
- }
36
- interface WtReadableStream {
37
- read(): AsyncIterableIterator<Uint8Array>;
38
- closeReadable(): Promise<void>;
39
- }
40
- interface WtWritableStream {
41
- write(data: Uint8Array): Promise<void>;
42
- closeWritable(): Promise<void>;
43
- }
44
- declare class WtStreamRW extends AbstractWtStream<WebTransportBidirectionalStream> implements WtReadableStream, WtWritableStream {
45
- private readonly readableStreamHandler;
46
- private readonly writableStreamHandler;
47
- constructor(session: WtSession, webTransportStream: WebTransportBidirectionalStream);
48
- read(): AsyncIterableIterator<Uint8Array>;
49
- write(data: Uint8Array): Promise<void>;
50
- closeReadable(): Promise<void>;
51
- closeWritable(): Promise<void>;
52
- close(): Promise<void>;
53
- }
54
- declare class WtStreamRO extends AbstractWtStream<WebTransportReceiveStream> implements WtReadableStream {
55
- private readonly readableStreamHandler;
56
- constructor(session: WtSession, webTransportStream: WebTransportReceiveStream);
57
- read(): AsyncIterableIterator<Uint8Array>;
58
- closeReadable(): Promise<void>;
59
- }
60
- declare class WtStreamWO extends AbstractWtStream<WebTransportSendStream> implements WtWritableStream {
61
- private readonly writableStreamHandler;
62
- constructor(session: WtSession, webTransportStream: WebTransportSendStream);
63
- write(data: Uint8Array): Promise<void>;
64
- closeWritable(): Promise<void>;
65
- }
66
- //#endregion
67
- //#region src/lib/bootstraps/types.d.ts
68
- type WebTransportRequestHeader = {
69
- readonly ':path': string;
70
- };
71
- type WebTransportRequest = {
72
- readonly header: WebTransportRequestHeader;
73
- };
74
- //#endregion
75
- //#region src/lib/explorers/webtransport-gateway.explorer.d.ts
76
- type WebTransportGatewayLifecycle = {
77
- allowRequest?(request: WebTransportRequest): void | Promise<void>;
78
- onSession?(session: WtSession): void | Promise<void>;
79
- onSessionClosed?(session: WtSession): void | Promise<void>;
80
- onStreamRW?(stream: WtStreamRW): void | Promise<void>;
81
- onStreamRO?(stream: WtStreamRO): void | Promise<void>;
82
- };
83
- //#endregion
84
- //#region src/lib/webtransport.module.d.ts
85
- declare class WebTransportModule {}
86
- //#endregion
87
- export { type HttpServerInit, WebTransportGateway, type WebTransportGatewayLifecycle, type WebTransportGatewayOptions, WebTransportModule, WebTransportServer, type WebTransportServerOptions, type WebTransportServerOptionsFactory, WtSession, WtStreamRO, WtStreamRW, WtStreamWO };
package/dist/index.mjs DELETED
@@ -1,423 +0,0 @@
1
- import { HttpException, Injectable, Logger, Module, applyDecorators } from "@nestjs/common";
2
- import { DiscoveryModule, DiscoveryService } from "@nestjs/core";
3
- //#region src/lib/decorators/webtransport-gateway.decorator.ts
4
- const DiscoverableWebTransportGateway = DiscoveryService.createDecorator();
5
- function WebTransportGateway(options) {
6
- return applyDecorators(Injectable(), DiscoverableWebTransportGateway(options));
7
- }
8
- //#endregion
9
- //#region src/lib/decorators/webtransport-server.decorator.ts
10
- const DiscoverableWebTransportServer = DiscoveryService.createDecorator();
11
- function WebTransportServer(options) {
12
- return applyDecorators(Injectable(), DiscoverableWebTransportServer(options));
13
- }
14
- //#endregion
15
- //#region src/lib/bootstraps/helpers.ts
16
- async function* generateSession(stream) {
17
- const reader = stream.getReader();
18
- try {
19
- while (true) {
20
- const { done, value } = await reader.read();
21
- if (done) break;
22
- yield value;
23
- }
24
- } finally {
25
- reader.releaseLock();
26
- }
27
- }
28
- async function* generateBStream(session) {
29
- const reader = session.incomingBidirectionalStreams.getReader();
30
- try {
31
- while (true) {
32
- const { done, value } = await reader.read();
33
- if (done) break;
34
- yield value;
35
- }
36
- } finally {
37
- reader.releaseLock();
38
- }
39
- }
40
- async function* generateUStream(session) {
41
- const reader = session.incomingUnidirectionalStreams.getReader();
42
- try {
43
- while (true) {
44
- const { done, value } = await reader.read();
45
- if (done) break;
46
- yield value;
47
- }
48
- } finally {
49
- reader.releaseLock();
50
- }
51
- }
52
- async function* generateChunks(reader) {
53
- try {
54
- while (true) {
55
- const { done, value } = await reader.read();
56
- if (done) break;
57
- yield value;
58
- }
59
- } finally {
60
- reader.releaseLock();
61
- }
62
- }
63
- function fireAndForget(task, logger) {
64
- if (!task) return;
65
- try {
66
- Promise.resolve(task()).catch((error) => {
67
- logger?.error(error);
68
- });
69
- } catch (error) {
70
- logger?.error(error);
71
- }
72
- }
73
- //#endregion
74
- //#region src/lib/classes/wt-stream.ts
75
- var AbstractStreamHandler = class {
76
- closed = false;
77
- async close() {
78
- if (this.closed) return;
79
- this.closed = true;
80
- await this.localClose();
81
- }
82
- };
83
- var ReadableStreamHandler = class extends AbstractStreamHandler {
84
- reader;
85
- chunks;
86
- constructor(reader) {
87
- super();
88
- this.reader = reader;
89
- this.chunks = generateChunks(this.reader);
90
- }
91
- read() {
92
- if (this.closed) throw new Error("Can't read stream");
93
- return this.chunks;
94
- }
95
- async localClose() {
96
- await this.reader.cancel();
97
- }
98
- };
99
- var WritableStreamHandler = class extends AbstractStreamHandler {
100
- writer;
101
- constructor(writer) {
102
- super();
103
- this.writer = writer;
104
- }
105
- async write(chunk) {
106
- if (this.closed) throw new Error("Can't write stream");
107
- await this.writer.write(chunk);
108
- }
109
- async localClose() {
110
- await this.writer.close();
111
- }
112
- };
113
- var AbstractWtStream = class {
114
- session;
115
- webTransportStream;
116
- constructor(session, webTransportStream) {
117
- this.session = session;
118
- this.webTransportStream = webTransportStream;
119
- }
120
- };
121
- var WtStreamRW = class extends AbstractWtStream {
122
- readableStreamHandler;
123
- writableStreamHandler;
124
- constructor(session, webTransportStream) {
125
- super(session, webTransportStream);
126
- this.readableStreamHandler = new ReadableStreamHandler(webTransportStream.readable.getReader());
127
- this.writableStreamHandler = new WritableStreamHandler(webTransportStream.writable.getWriter());
128
- }
129
- read() {
130
- return this.readableStreamHandler.read();
131
- }
132
- async write(data) {
133
- await this.writableStreamHandler.write(data);
134
- }
135
- async closeReadable() {
136
- await this.readableStreamHandler.close();
137
- }
138
- async closeWritable() {
139
- await this.writableStreamHandler.close();
140
- }
141
- async close() {
142
- await Promise.all([this.closeReadable(), this.closeWritable()]);
143
- }
144
- };
145
- var WtStreamRO = class extends AbstractWtStream {
146
- readableStreamHandler;
147
- constructor(session, webTransportStream) {
148
- super(session, webTransportStream);
149
- this.readableStreamHandler = new ReadableStreamHandler(webTransportStream.getReader());
150
- }
151
- read() {
152
- return this.readableStreamHandler.read();
153
- }
154
- async closeReadable() {
155
- await this.readableStreamHandler.close();
156
- }
157
- };
158
- var WtStreamWO = class extends AbstractWtStream {
159
- writableStreamHandler;
160
- constructor(session, webTransportStream) {
161
- super(session, webTransportStream);
162
- this.writableStreamHandler = new WritableStreamHandler(webTransportStream.getWriter());
163
- }
164
- async write(data) {
165
- await this.writableStreamHandler.write(data);
166
- }
167
- async closeWritable() {
168
- await this.writableStreamHandler.close();
169
- }
170
- };
171
- //#endregion
172
- //#region src/lib/explorers/webtransport-gateway.explorer.ts
173
- var WebTransportGatewayExplorer = @Injectable() class {
174
- discoveryService;
175
- constructor(discoveryService) {
176
- this.discoveryService = discoveryService;
177
- }
178
- discover(serverNames) {
179
- const gatewaysByNameByPath = /* @__PURE__ */ new Map();
180
- const wrappers = this.discoveryService.getProviders({ metadataKey: DiscoverableWebTransportGateway.KEY });
181
- for (const wrapper of wrappers) {
182
- const metadata = this.discoveryService.getMetadataByDecorator(DiscoverableWebTransportGateway, wrapper);
183
- if (!metadata || !wrapper.metatype) continue;
184
- const { server } = metadata;
185
- let { path } = metadata;
186
- if (!path.startsWith("/")) path = "/" + path;
187
- const gateway = wrapper.instance;
188
- if (!gatewaysByNameByPath.has(server)) {
189
- if (!serverNames.includes(server)) throw new Error(`@WebTransportGateway() server name doesn't exist: ${server}`);
190
- gatewaysByNameByPath.set(server, /* @__PURE__ */ new Map());
191
- }
192
- if (gatewaysByNameByPath.get(server).has(path)) throw new Error(`Duplicate @WebTransportGateway() path: ${path}`);
193
- gatewaysByNameByPath.get(server).set(path, gateway);
194
- }
195
- return gatewaysByNameByPath;
196
- }
197
- };
198
- //#endregion
199
- //#region src/lib/explorers/webtransport-server.explorer.ts
200
- var WebTransportServerExplorer = @Injectable() class {
201
- discoveryService;
202
- constructor(discoveryService) {
203
- this.discoveryService = discoveryService;
204
- }
205
- discover() {
206
- const serversByName = /* @__PURE__ */ new Map();
207
- const wrappers = this.discoveryService.getProviders({ metadataKey: DiscoverableWebTransportServer.KEY });
208
- for (const wrapper of wrappers) {
209
- const metadata = this.discoveryService.getMetadataByDecorator(DiscoverableWebTransportServer, wrapper);
210
- if (!metadata || !wrapper.metatype) continue;
211
- const { name } = metadata;
212
- if (serversByName.has(name)) throw new Error(`Duplicate @WebTransportServer() name: ${name}`);
213
- const server = wrapper.instance;
214
- serversByName.set(name, server);
215
- }
216
- return serversByName;
217
- }
218
- };
219
- //#endregion
220
- //#region src/lib/bootstraps/web-transport-bootstrapper.ts
221
- var WebTransportBootstrapper = @Injectable() class WebTransportBootstrapper {
222
- serverFactory;
223
- webTransportExplorer;
224
- logger = new Logger(WebTransportBootstrapper.name);
225
- h3ServersWithInfo = [];
226
- constructor(serverFactory, webTransportExplorer) {
227
- this.serverFactory = serverFactory;
228
- this.webTransportExplorer = webTransportExplorer;
229
- }
230
- async onApplicationBootstrap() {
231
- const serverBindings = this.webTransportExplorer.discover();
232
- this.h3ServersWithInfo = await this.serverFactory.createH3Servers(serverBindings);
233
- await Promise.all(this.h3ServersWithInfo.map(async ({ options, name, h3Server }) => {
234
- try {
235
- h3Server.startServer();
236
- await h3Server.ready;
237
- this.logger.log(`WebTransport server "${name}" is ready on ${options.host}:${options.port}`);
238
- } catch (e) {
239
- this.logger.error(`Failed to start WebTransport server "${name}" on ${options.host}:${options.port}`);
240
- throw e;
241
- }
242
- }));
243
- }
244
- async onApplicationShutdown() {
245
- await Promise.all(this.h3ServersWithInfo.map(async ({ name, h3Server }) => {
246
- h3Server.stopServer();
247
- await h3Server.closed;
248
- this.logger.log(`WebTransport server "${name}" stopped`);
249
- }));
250
- }
251
- };
252
- //#endregion
253
- //#region src/lib/explorers/web-transport.explorer.ts
254
- var WebTransportExplorer = @Injectable() class WebTransportExplorer {
255
- webTransportServerExplorer;
256
- webTransportGatewayExplorer;
257
- logger = new Logger(WebTransportExplorer.name);
258
- constructor(webTransportServerExplorer, webTransportGatewayExplorer) {
259
- this.webTransportServerExplorer = webTransportServerExplorer;
260
- this.webTransportGatewayExplorer = webTransportGatewayExplorer;
261
- }
262
- discover() {
263
- const serversByName = this.webTransportServerExplorer.discover();
264
- const gatewaysByNameByPath = this.webTransportGatewayExplorer.discover(Array.from(serversByName.keys()));
265
- const serverBindings = [];
266
- serversByName.forEach((server, name) => {
267
- const gatewaysByPath = gatewaysByNameByPath.get(name);
268
- if (gatewaysByPath === void 0) this.logger.warn(`WebTransport server "${name}" has no gateways, skipping`);
269
- else serverBindings.push({
270
- server,
271
- name,
272
- gatewaysByPath
273
- });
274
- });
275
- return serverBindings;
276
- }
277
- };
278
- //#endregion
279
- //#region src/lib/bootstraps/consumers/abstract-stream.consumer.ts
280
- var AbstractStreamConsumer = class {
281
- async consume(gateway, webTransportSession, session) {
282
- for await (const webTransportStream of this.generateStream(webTransportSession)) this.handleStream(gateway, session, webTransportStream);
283
- }
284
- handleStream(gateway, session, webTransportStream) {
285
- const stream = this.createStream(session, webTransportStream);
286
- this.consumeHook(gateway, stream);
287
- }
288
- };
289
- //#endregion
290
- //#region src/lib/bootstraps/consumers/stream-ro.consumer.ts
291
- var StreamROConsumer = @Injectable() class StreamROConsumer extends AbstractStreamConsumer {
292
- logger = new Logger(StreamROConsumer.name);
293
- generateStream(session) {
294
- return generateUStream(session);
295
- }
296
- createStream(session, webTransportStream) {
297
- return new WtStreamRO(session, webTransportStream);
298
- }
299
- consumeHook(gateway, stream) {
300
- fireAndForget(() => gateway.onStreamRO?.(stream), this.logger);
301
- }
302
- };
303
- //#endregion
304
- //#region src/lib/bootstraps/consumers/stream-rw.consumer.ts
305
- var StreamRWConsumer = @Injectable() class StreamRWConsumer extends AbstractStreamConsumer {
306
- logger = new Logger(StreamRWConsumer.name);
307
- generateStream(session) {
308
- return generateBStream(session);
309
- }
310
- createStream(session, webTransportStream) {
311
- return new WtStreamRW(session, webTransportStream);
312
- }
313
- consumeHook(gateway, stream) {
314
- fireAndForget(() => gateway.onStreamRW?.(stream), this.logger);
315
- }
316
- };
317
- //#endregion
318
- //#region src/lib/classes/wt-session.ts
319
- var WtSession = class {
320
- webTransportSession;
321
- constructor(webTransportSession) {
322
- this.webTransportSession = webTransportSession;
323
- }
324
- async createStreamWO() {
325
- const stream = await this.webTransportSession.createUnidirectionalStream();
326
- return new WtStreamWO(this, stream);
327
- }
328
- async createStreamRW() {
329
- const stream = await this.webTransportSession.createBidirectionalStream();
330
- return new WtStreamRW(this, stream);
331
- }
332
- };
333
- //#endregion
334
- //#region src/lib/bootstraps/server-factory.ts
335
- let webtransport;
336
- const getLib = eval(`import('@fails-components/webtransport')`).then((module) => {
337
- webtransport = module;
338
- });
339
- function createHandshakeResponse(request, host, status) {
340
- const url = new URL(request.header[":path"], `https://${host}`);
341
- return {
342
- ...request,
343
- path: url.pathname,
344
- header: {
345
- ...request.header,
346
- ":path": url.pathname
347
- },
348
- status
349
- };
350
- }
351
- var ServerFactory = @Injectable() class ServerFactory {
352
- streamRWConsumer;
353
- streamROConsumer;
354
- logger = new Logger(ServerFactory.name);
355
- constructor(streamRWConsumer, streamROConsumer) {
356
- this.streamRWConsumer = streamRWConsumer;
357
- this.streamROConsumer = streamROConsumer;
358
- }
359
- async createH3Servers(serverBindings) {
360
- await getLib;
361
- const h3ServersWithInfo = [];
362
- for (const { server, name, gatewaysByPath } of serverBindings) {
363
- const options = await server.options();
364
- const h3Server = this.createH3Server(options, gatewaysByPath);
365
- h3ServersWithInfo.push({
366
- options,
367
- h3Server,
368
- name
369
- });
370
- }
371
- return h3ServersWithInfo;
372
- }
373
- createH3Server(options, gatewaysByPath) {
374
- const h3Server = new webtransport.Http3Server(options);
375
- h3Server.setRequestCallback(async (request) => {
376
- const status = await this.getRequestStatus(request, gatewaysByPath);
377
- return createHandshakeResponse(request, options.host, status);
378
- });
379
- for (const [path, gateway] of gatewaysByPath.entries()) this.consumeSession(gateway, h3Server, path).catch((error) => this.logger.error(error));
380
- return h3Server;
381
- }
382
- async getRequestStatus(request, gatewaysByPath) {
383
- const { pathname } = new URL(request.header[":path"], "https://0.0.0.0");
384
- const gateway = gatewaysByPath.get(pathname);
385
- if (gateway === void 0) return 404;
386
- try {
387
- await gateway.allowRequest?.(request);
388
- return 200;
389
- } catch (e) {
390
- if (e instanceof HttpException) return e.getStatus();
391
- return 400;
392
- }
393
- }
394
- async consumeSession(gateway, h3Server, path) {
395
- const stream = h3Server.sessionStream(path);
396
- for await (const session of generateSession(stream)) this.handleSession(gateway, session);
397
- }
398
- handleSession(gateway, webTransportSession) {
399
- const session = new WtSession(webTransportSession);
400
- fireAndForget(() => gateway.onSession?.(session), this.logger);
401
- webTransportSession.closed.catch((error) => this.logger.error(error)).finally(() => {
402
- fireAndForget(() => gateway.onSessionClosed?.(session), this.logger);
403
- });
404
- this.streamRWConsumer.consume(gateway, webTransportSession, session).catch((error) => this.logger.error(error));
405
- this.streamROConsumer.consume(gateway, webTransportSession, session).catch((error) => this.logger.error(error));
406
- }
407
- };
408
- //#endregion
409
- //#region src/lib/webtransport.module.ts
410
- var WebTransportModule = @Module({
411
- imports: [DiscoveryModule],
412
- providers: [
413
- ServerFactory,
414
- StreamRWConsumer,
415
- StreamROConsumer,
416
- WebTransportExplorer,
417
- WebTransportBootstrapper,
418
- WebTransportServerExplorer,
419
- WebTransportGatewayExplorer
420
- ]
421
- }) class {};
422
- //#endregion
423
- export { WebTransportGateway, WebTransportModule, WebTransportServer, WtSession, WtStreamRO, WtStreamRW, WtStreamWO };