@nmtjs/http-transport 0.0.2 → 0.1.1

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/index.js CHANGED
@@ -2,4 +2,4 @@ export * from "./lib/server.js";
2
2
  export * from "./lib/transport.js";
3
3
  export * from "./lib/types.js";
4
4
  export * from "./lib/utils.js";
5
- export * from "./lib/providers.js";
5
+ export * from "./lib/injectables.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../index.ts"],"sourcesContent":["export * from './lib/server.ts'\nexport * from './lib/transport.ts'\nexport * from './lib/types.ts'\nexport * from './lib/utils.ts'\nexport * from './lib/providers.ts'\n"],"names":[],"mappings":"AAAA,cAAc,kBAAiB;AAC/B,cAAc,qBAAoB;AAClC,cAAc,iBAAgB;AAC9B,cAAc,iBAAgB;AAC9B,cAAc,qBAAoB"}
1
+ {"version":3,"sources":["../../index.ts"],"sourcesContent":["export * from './lib/server.ts'\nexport * from './lib/transport.ts'\nexport * from './lib/types.ts'\nexport * from './lib/utils.ts'\nexport * from './lib/injectables.ts'\n"],"names":[],"mappings":"AAAA,cAAc,kBAAiB;AAC/B,cAAc,qBAAoB;AAClC,cAAc,iBAAgB;AAC9B,cAAc,iBAAgB;AAC9B,cAAc,uBAAsB"}
@@ -0,0 +1,2 @@
1
+ import { injectables } from '@nmtjs/application';
2
+ export const connectionData = injectables.connectionData;
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../lib/injectables.ts"],"sourcesContent":["import {\n type LazyInjectable,\n type Scope,\n injectables,\n} from '@nmtjs/application'\nimport type { HttpConnectionData } from './types.ts'\n\nexport const connectionData = injectables.connectionData as LazyInjectable<\n HttpConnectionData,\n Scope.Connection\n>\n"],"names":["injectables","connectionData"],"mappings":"AAAA,SAGEA,WAAW,QACN,qBAAoB;AAG3B,OAAO,MAAMC,iBAAiBD,YAAYC,cAAc,CAGvD"}
@@ -1,7 +1,7 @@
1
- import { ApiError, Scope, providers } from '@nmtjs/application';
1
+ import { ApiError, Scope, injectables } from '@nmtjs/application';
2
2
  import { TransportType } from '@nmtjs/common';
3
3
  import { App, SSLApp } from 'uWebSockets.js';
4
- import { connectionData } from "./providers.js";
4
+ import { connectionData } from "./injectables.js";
5
5
  import { InternalError, getFormat, getRequestData } from "./utils.js";
6
6
  export class HttpTransportServer {
7
7
  application;
@@ -13,19 +13,22 @@ export class HttpTransportServer {
13
13
  this.options = options;
14
14
  this.transportType = TransportType.HTTP;
15
15
  this.server = this.options.tls ? SSLApp(options.tls) : App();
16
- this.server.get('/healthy', (res)=>{
17
- res.cork(()=>{
18
- res.writeHeader('Access-Control-Allow-Origin', '*');
19
- res.writeHeader('Access-Control-Allow-Headers', 'Content-Type');
20
- res.writeHeader('Access-Control-Allow-Methods', 'GET');
21
- res.writeHeader('Content-Type', 'text/plain');
22
- res.end('OK');
23
- });
16
+ this.server.options('/*', (res, req)=>{
17
+ this.applyCors(res, req);
18
+ res.writeStatus('200 OK');
19
+ res.endWithoutBody();
20
+ }).get('/healthy', (res, req)=>{
21
+ this.applyCors(res, req);
22
+ res.writeHeader('Content-Type', 'text/plain');
23
+ res.end('OK');
24
24
  }).post('/api/:service/:procudure', async (res, req)=>{
25
25
  const ac = new AbortController();
26
26
  res.onAborted(()=>ac.abort());
27
27
  const tryEnd = (cb)=>{
28
- if (!ac.signal.aborted) res.cork(cb);
28
+ if (!ac.signal.aborted) res.cork(()=>{
29
+ this.applyCors(res, req);
30
+ return cb();
31
+ });
29
32
  };
30
33
  try {
31
34
  const requestData = getRequestData(req);
@@ -53,8 +56,7 @@ export class HttpTransportServer {
53
56
  remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),
54
57
  responseHeaders
55
58
  });
56
- container.provide(providers.connection, connection);
57
- container.provide(providers.signal, ac.signal);
59
+ container.provide(injectables.connection, connection);
58
60
  const { procedure } = this.api.find(serviceName, procedureName, this.transportType);
59
61
  try {
60
62
  const response = await this.handleRPC({
@@ -123,6 +125,14 @@ export class HttpTransportServer {
123
125
  cause
124
126
  }));
125
127
  }
128
+ applyCors(res, req) {
129
+ const origin = req.getHeader('origin');
130
+ if (!origin) return;
131
+ res.headers.set('Access-Control-Allow-Origin', origin);
132
+ res.headers.set('Access-Control-Allow-Headers', 'Content-Type');
133
+ res.headers.set('Access-Control-Allow-Methods', 'GET, POST');
134
+ res.headers.set('Access-Control-Allow-Credentials', 'true');
135
+ }
126
136
  handleContainerDisposal(container) {
127
137
  container.dispose();
128
138
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../lib/server.ts"],"sourcesContent":["import {\n type AnyProcedure,\n ApiError,\n type ApplicationContext,\n type Connection,\n type Container,\n Scope,\n type Service,\n providers,\n} from '@nmtjs/application'\nimport { TransportType } from '@nmtjs/common'\nimport {\n App,\n type HttpResponse,\n SSLApp,\n type TemplatedApp,\n} from 'uWebSockets.js'\nimport { connectionData } from './providers.ts'\nimport type { HttpTransportOptions } from './types.ts'\nimport { InternalError, getFormat, getRequestData } from './utils.ts'\n\nexport class HttpTransportServer {\n protected server!: TemplatedApp\n protected readonly transportType = TransportType.HTTP\n\n constructor(\n protected readonly application: ApplicationContext,\n protected readonly options: HttpTransportOptions,\n ) {\n this.server = this.options.tls ? SSLApp(options.tls!) : App()\n\n this.server\n .get('/healthy', (res) => {\n res.cork(() => {\n // cors\n res.writeHeader('Access-Control-Allow-Origin', '*')\n res.writeHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.writeHeader('Access-Control-Allow-Methods', 'GET')\n res.writeHeader('Content-Type', 'text/plain')\n res.end('OK')\n })\n })\n .post('/api/:service/:procudure', async (res, req) => {\n const ac = new AbortController()\n res.onAborted(() => ac.abort())\n const tryEnd = (cb) => {\n if (!ac.signal.aborted) res.cork(cb)\n }\n\n try {\n const requestData = getRequestData(req)\n\n const serviceName = req.getParameter(0)!\n const procedureName = req.getParameter(1)!\n\n const service = this.application.registry.services.get(serviceName)\n\n if (!service) throw new Error(`Service ${serviceName} not found`)\n if (this.transportType in service.contract.transports === false)\n throw new Error(`Service ${serviceName} not supported`)\n\n const format = getFormat(requestData, this.application.format)\n const body = await this.getBody(res)\n const container = this.application.container.createScope(Scope.Call)\n const payload = body.byteLength ? format.decoder.decode(body) : null\n const connection = this.application.connections.add({\n services: [serviceName],\n type: this.transportType,\n subscriptions: new Map(),\n })\n\n const responseHeaders = new Headers()\n\n container.provide(connectionData, {\n query: requestData.query,\n headers: requestData.headers,\n proxiedRemoteAddress: Buffer.from(\n res.getProxiedRemoteAddressAsText(),\n ).toString(),\n remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),\n responseHeaders,\n })\n container.provide(providers.connection, connection)\n container.provide(providers.signal, ac.signal)\n\n const { procedure } = this.api.find(\n serviceName,\n procedureName,\n this.transportType,\n )\n\n try {\n const response = await this.handleRPC({\n connection,\n service,\n procedure,\n container,\n signal: ac.signal,\n payload,\n })\n\n tryEnd(() => {\n res\n .writeStatus('200 OK')\n .writeHeader('Content-Type', format.encoder.contentType)\n responseHeaders.forEach((v, k) => res.writeHeader(k, v))\n res.end(format.encoder.encode({ error: null, result: response }))\n })\n } catch (error: any) {\n if (error instanceof ApiError) {\n tryEnd(() =>\n res\n .writeStatus('200 OK')\n .end(format.encoder.encode({ error, result: null })),\n )\n } else {\n tryEnd(() =>\n res.writeStatus('200 OK').end(\n format.encoder.encode({\n error: InternalError(),\n result: null,\n }),\n ),\n )\n }\n this.logError(error)\n } finally {\n this.application.connections.remove(connection)\n this.handleContainerDisposal(container)\n }\n } catch (error: any) {\n this.logError(error)\n tryEnd(() =>\n res.writeStatus('500 Internal Server Error').endWithoutBody(),\n )\n }\n })\n }\n\n async start() {\n return new Promise<void>((resolve, reject) => {\n const hostname = this.options.hostname ?? '127.0.0.1'\n this.server.listen(hostname, this.options.port!, (socket) => {\n if (socket) {\n this.logger.info(\n 'Server started on %s:%s',\n hostname,\n this.options.port!,\n )\n resolve()\n } else {\n reject(new Error('Failed to start server'))\n }\n })\n })\n }\n\n async stop() {\n this.server.close()\n }\n\n protected get api() {\n return this.application.api\n }\n\n protected get logger() {\n return this.application.logger\n }\n\n protected async logError(\n cause: Error,\n message = 'Unknown error while processing request',\n ) {\n this.logger.error(new Error(message, { cause }))\n }\n\n protected handleContainerDisposal(container: Container) {\n container.dispose()\n }\n\n protected async handleRPC(options: {\n connection: Connection\n service: Service\n procedure: AnyProcedure\n container: Container\n signal: AbortSignal\n payload: any\n }) {\n return await this.api.call({\n ...options,\n transport: this.transportType,\n })\n }\n\n protected async getBody(res: HttpResponse) {\n return new Promise<Buffer>((resolve) => {\n const chunks: Buffer[] = []\n res.onData((chunk, isLast) => {\n chunks.push(Buffer.from(chunk))\n if (isLast) {\n resolve(Buffer.concat(chunks))\n }\n })\n })\n }\n}\n"],"names":["ApiError","Scope","providers","TransportType","App","SSLApp","connectionData","InternalError","getFormat","getRequestData","HttpTransportServer","server","transportType","constructor","application","options","HTTP","tls","get","res","cork","writeHeader","end","post","req","ac","AbortController","onAborted","abort","tryEnd","cb","signal","aborted","requestData","serviceName","getParameter","procedureName","service","registry","services","Error","contract","transports","format","body","getBody","container","createScope","Call","payload","byteLength","decoder","decode","connection","connections","add","type","subscriptions","Map","responseHeaders","Headers","provide","query","headers","proxiedRemoteAddress","Buffer","from","getProxiedRemoteAddressAsText","toString","remoteAddress","getRemoteAddressAsText","procedure","api","find","response","handleRPC","writeStatus","encoder","contentType","forEach","v","k","encode","error","result","logError","remove","handleContainerDisposal","endWithoutBody","start","Promise","resolve","reject","hostname","listen","port","socket","logger","info","stop","close","cause","message","dispose","call","transport","chunks","onData","chunk","isLast","push","concat"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../lib/server.ts"],"sourcesContent":["import {\n type AnyProcedure,\n ApiError,\n type ApplicationContext,\n type Connection,\n type Container,\n Scope,\n type Service,\n injectables,\n} from '@nmtjs/application'\nimport { TransportType } from '@nmtjs/common'\nimport {\n App,\n type HttpRequest,\n type HttpResponse,\n SSLApp,\n type TemplatedApp,\n} from 'uWebSockets.js'\nimport { connectionData } from './injectables.ts'\nimport type { HttpTransportOptions } from './types.ts'\nimport { InternalError, getFormat, getRequestData } from './utils.ts'\n\nexport class HttpTransportServer {\n protected server!: TemplatedApp\n protected readonly transportType = TransportType.HTTP\n\n constructor(\n protected readonly application: ApplicationContext,\n protected readonly options: HttpTransportOptions,\n ) {\n this.server = this.options.tls ? SSLApp(options.tls!) : App()\n\n this.server\n .options('/*', (res, req) => {\n this.applyCors(res, req)\n res.writeStatus('200 OK')\n res.endWithoutBody()\n })\n .get('/healthy', (res, req) => {\n this.applyCors(res, req)\n res.writeHeader('Content-Type', 'text/plain')\n res.end('OK')\n })\n .post('/api/:service/:procudure', async (res, req) => {\n const ac = new AbortController()\n res.onAborted(() => ac.abort())\n const tryEnd = (cb) => {\n if (!ac.signal.aborted)\n res.cork(() => {\n this.applyCors(res, req)\n return cb()\n })\n }\n\n try {\n const requestData = getRequestData(req)\n\n const serviceName = req.getParameter(0)!\n const procedureName = req.getParameter(1)!\n\n const service = this.application.registry.services.get(serviceName)\n\n if (!service) throw new Error(`Service ${serviceName} not found`)\n if (this.transportType in service.contract.transports === false)\n throw new Error(`Service ${serviceName} not supported`)\n\n const format = getFormat(requestData, this.application.format)\n const body = await this.getBody(res)\n const container = this.application.container.createScope(Scope.Call)\n const payload = body.byteLength ? format.decoder.decode(body) : null\n const connection = this.application.connections.add({\n services: [serviceName],\n type: this.transportType,\n subscriptions: new Map(),\n })\n\n const responseHeaders = new Headers()\n\n container.provide(connectionData, {\n query: requestData.query,\n headers: requestData.headers,\n proxiedRemoteAddress: Buffer.from(\n res.getProxiedRemoteAddressAsText(),\n ).toString(),\n remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),\n responseHeaders,\n })\n container.provide(injectables.connection, connection)\n\n const { procedure } = this.api.find(\n serviceName,\n procedureName,\n this.transportType,\n )\n\n try {\n const response = await this.handleRPC({\n connection,\n service,\n procedure,\n container,\n signal: ac.signal,\n payload,\n })\n\n tryEnd(() => {\n res\n .writeStatus('200 OK')\n .writeHeader('Content-Type', format.encoder.contentType)\n responseHeaders.forEach((v, k) => res.writeHeader(k, v))\n res.end(format.encoder.encode({ error: null, result: response }))\n })\n } catch (error: any) {\n if (error instanceof ApiError) {\n tryEnd(() =>\n res\n .writeStatus('200 OK')\n .end(format.encoder.encode({ error, result: null })),\n )\n } else {\n tryEnd(() =>\n res.writeStatus('200 OK').end(\n format.encoder.encode({\n error: InternalError(),\n result: null,\n }),\n ),\n )\n }\n this.logError(error)\n } finally {\n this.application.connections.remove(connection)\n this.handleContainerDisposal(container)\n }\n } catch (error: any) {\n this.logError(error)\n tryEnd(() =>\n res.writeStatus('500 Internal Server Error').endWithoutBody(),\n )\n }\n })\n }\n\n async start() {\n return new Promise<void>((resolve, reject) => {\n const hostname = this.options.hostname ?? '127.0.0.1'\n this.server.listen(hostname, this.options.port!, (socket) => {\n if (socket) {\n this.logger.info(\n 'Server started on %s:%s',\n hostname,\n this.options.port!,\n )\n resolve()\n } else {\n reject(new Error('Failed to start server'))\n }\n })\n })\n }\n\n async stop() {\n this.server.close()\n }\n\n protected get api() {\n return this.application.api\n }\n\n protected get logger() {\n return this.application.logger\n }\n\n protected async logError(\n cause: Error,\n message = 'Unknown error while processing request',\n ) {\n this.logger.error(new Error(message, { cause }))\n }\n\n protected applyCors(res: HttpResponse, req: HttpRequest) {\n // TODO: this should be configurable\n const origin = req.getHeader('origin')\n if (!origin) return\n res.headers.set('Access-Control-Allow-Origin', origin)\n res.headers.set('Access-Control-Allow-Headers', 'Content-Type')\n res.headers.set('Access-Control-Allow-Methods', 'GET, POST')\n res.headers.set('Access-Control-Allow-Credentials', 'true')\n }\n\n protected handleContainerDisposal(container: Container) {\n container.dispose()\n }\n\n protected async handleRPC(options: {\n connection: Connection\n service: Service\n procedure: AnyProcedure\n container: Container\n signal: AbortSignal\n payload: any\n }) {\n return await this.api.call({\n ...options,\n transport: this.transportType,\n })\n }\n\n protected async getBody(res: HttpResponse) {\n return new Promise<Buffer>((resolve) => {\n const chunks: Buffer[] = []\n res.onData((chunk, isLast) => {\n chunks.push(Buffer.from(chunk))\n if (isLast) {\n resolve(Buffer.concat(chunks))\n }\n })\n })\n }\n}\n"],"names":["ApiError","Scope","injectables","TransportType","App","SSLApp","connectionData","InternalError","getFormat","getRequestData","HttpTransportServer","server","transportType","constructor","application","options","HTTP","tls","res","req","applyCors","writeStatus","endWithoutBody","get","writeHeader","end","post","ac","AbortController","onAborted","abort","tryEnd","cb","signal","aborted","cork","requestData","serviceName","getParameter","procedureName","service","registry","services","Error","contract","transports","format","body","getBody","container","createScope","Call","payload","byteLength","decoder","decode","connection","connections","add","type","subscriptions","Map","responseHeaders","Headers","provide","query","headers","proxiedRemoteAddress","Buffer","from","getProxiedRemoteAddressAsText","toString","remoteAddress","getRemoteAddressAsText","procedure","api","find","response","handleRPC","encoder","contentType","forEach","v","k","encode","error","result","logError","remove","handleContainerDisposal","start","Promise","resolve","reject","hostname","listen","port","socket","logger","info","stop","close","cause","message","origin","getHeader","set","dispose","call","transport","chunks","onData","chunk","isLast","push","concat"],"mappings":"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"}
package/index.ts CHANGED
@@ -2,4 +2,4 @@ export * from './lib/server.ts'
2
2
  export * from './lib/transport.ts'
3
3
  export * from './lib/types.ts'
4
4
  export * from './lib/utils.ts'
5
- export * from './lib/providers.ts'
5
+ export * from './lib/injectables.ts'
@@ -0,0 +1,11 @@
1
+ import {
2
+ type LazyInjectable,
3
+ type Scope,
4
+ injectables,
5
+ } from '@nmtjs/application'
6
+ import type { HttpConnectionData } from './types.ts'
7
+
8
+ export const connectionData = injectables.connectionData as LazyInjectable<
9
+ HttpConnectionData,
10
+ Scope.Connection
11
+ >
package/lib/server.ts CHANGED
@@ -6,16 +6,17 @@ import {
6
6
  type Container,
7
7
  Scope,
8
8
  type Service,
9
- providers,
9
+ injectables,
10
10
  } from '@nmtjs/application'
11
11
  import { TransportType } from '@nmtjs/common'
12
12
  import {
13
13
  App,
14
+ type HttpRequest,
14
15
  type HttpResponse,
15
16
  SSLApp,
16
17
  type TemplatedApp,
17
18
  } from 'uWebSockets.js'
18
- import { connectionData } from './providers.ts'
19
+ import { connectionData } from './injectables.ts'
19
20
  import type { HttpTransportOptions } from './types.ts'
20
21
  import { InternalError, getFormat, getRequestData } from './utils.ts'
21
22
 
@@ -30,21 +31,25 @@ export class HttpTransportServer {
30
31
  this.server = this.options.tls ? SSLApp(options.tls!) : App()
31
32
 
32
33
  this.server
33
- .get('/healthy', (res) => {
34
- res.cork(() => {
35
- // cors
36
- res.writeHeader('Access-Control-Allow-Origin', '*')
37
- res.writeHeader('Access-Control-Allow-Headers', 'Content-Type')
38
- res.writeHeader('Access-Control-Allow-Methods', 'GET')
39
- res.writeHeader('Content-Type', 'text/plain')
40
- res.end('OK')
41
- })
34
+ .options('/*', (res, req) => {
35
+ this.applyCors(res, req)
36
+ res.writeStatus('200 OK')
37
+ res.endWithoutBody()
38
+ })
39
+ .get('/healthy', (res, req) => {
40
+ this.applyCors(res, req)
41
+ res.writeHeader('Content-Type', 'text/plain')
42
+ res.end('OK')
42
43
  })
43
44
  .post('/api/:service/:procudure', async (res, req) => {
44
45
  const ac = new AbortController()
45
46
  res.onAborted(() => ac.abort())
46
47
  const tryEnd = (cb) => {
47
- if (!ac.signal.aborted) res.cork(cb)
48
+ if (!ac.signal.aborted)
49
+ res.cork(() => {
50
+ this.applyCors(res, req)
51
+ return cb()
52
+ })
48
53
  }
49
54
 
50
55
  try {
@@ -80,8 +85,7 @@ export class HttpTransportServer {
80
85
  remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),
81
86
  responseHeaders,
82
87
  })
83
- container.provide(providers.connection, connection)
84
- container.provide(providers.signal, ac.signal)
88
+ container.provide(injectables.connection, connection)
85
89
 
86
90
  const { procedure } = this.api.find(
87
91
  serviceName,
@@ -174,6 +178,16 @@ export class HttpTransportServer {
174
178
  this.logger.error(new Error(message, { cause }))
175
179
  }
176
180
 
181
+ protected applyCors(res: HttpResponse, req: HttpRequest) {
182
+ // TODO: this should be configurable
183
+ const origin = req.getHeader('origin')
184
+ if (!origin) return
185
+ res.headers.set('Access-Control-Allow-Origin', origin)
186
+ res.headers.set('Access-Control-Allow-Headers', 'Content-Type')
187
+ res.headers.set('Access-Control-Allow-Methods', 'GET, POST')
188
+ res.headers.set('Access-Control-Allow-Credentials', 'true')
189
+ }
190
+
177
191
  protected handleContainerDisposal(container: Container) {
178
192
  container.dispose()
179
193
  }
package/package.json CHANGED
@@ -11,12 +11,13 @@
11
11
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.47.0"
12
12
  },
13
13
  "peerDependencies": {
14
- "@nmtjs/application": "^0.0.1",
15
- "@nmtjs/common": "^0.0.1"
14
+ "@nmtjs/application": "^0.2.1",
15
+ "@nmtjs/common": "^0.2.1"
16
16
  },
17
17
  "devDependencies": {
18
- "@nmtjs/application": "^0.0.1",
19
- "@nmtjs/common": "^0.0.1"
18
+ "@nmtjs/application": "^0.2.1",
19
+ "@nmtjs/common": "^0.2.1",
20
+ "@types/node": "^18.0.0"
20
21
  },
21
22
  "engines": {
22
23
  "node": ">=18.9.0"
@@ -29,7 +30,7 @@
29
30
  "LICENSE.md",
30
31
  "README.md"
31
32
  ],
32
- "version": "0.0.2",
33
+ "version": "0.1.1",
33
34
  "scripts": {
34
35
  "build": "neemata-build ./index.ts './lib/**/*.ts'",
35
36
  "type-check": "tsc --noEmit"
@@ -1,2 +0,0 @@
1
- import { providers } from '@nmtjs/application';
2
- export const connectionData = providers.connectionData.$withType();
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../lib/providers.ts"],"sourcesContent":["import { providers } from '@nmtjs/application'\nimport type { HttpConnectionData } from './types.ts'\n\nexport const connectionData =\n providers.connectionData.$withType<HttpConnectionData>()\n"],"names":["providers","connectionData","$withType"],"mappings":"AAAA,SAASA,SAAS,QAAQ,qBAAoB;AAG9C,OAAO,MAAMC,iBACXD,UAAUC,cAAc,CAACC,SAAS,GAAsB"}
package/lib/providers.ts DELETED
@@ -1,5 +0,0 @@
1
- import { providers } from '@nmtjs/application'
2
- import type { HttpConnectionData } from './types.ts'
3
-
4
- export const connectionData =
5
- providers.connectionData.$withType<HttpConnectionData>()