open-nest 0.0.0-stage → 0.0.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/express.d.mts +61 -0
- package/dist/express.mjs +46 -0
- package/dist/fastify.d.mts +61 -0
- package/dist/fastify.mjs +46 -0
- package/package.json +60 -4
- package/readme.md +94 -0
- package/README.md +0 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { NestExpressApplication } from "@nestjs/platform-express";
|
|
2
|
+
import { NestApplicationOptions, Type } from "@nestjs/common";
|
|
3
|
+
//#region src/adapters/express/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Options for {@link createFetcher}.
|
|
6
|
+
*/
|
|
7
|
+
export interface CreateHandlerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Root Nest module.
|
|
10
|
+
*/
|
|
11
|
+
module: Type<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Optional hook to configure the app before init (pipes, prefix, cors, ...).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const fetch = createFetcher({
|
|
18
|
+
* module: AppModule,
|
|
19
|
+
* configure: (app) => {
|
|
20
|
+
* app.use(cors());
|
|
21
|
+
* app.use(bodyParser.json());
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
configure?: (app: NestExpressApplication) => void | Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Optional Nest factory options.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const fetch = createFetcher({
|
|
33
|
+
* module: AppModule,
|
|
34
|
+
* opts: { logger: ["error", "warn"] },
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
opts?: NestApplicationOptions;
|
|
39
|
+
}
|
|
40
|
+
export interface Fetcher {
|
|
41
|
+
(request: Request): Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the underlying Nest app instance.
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const app = await fetch.app();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
app(): Promise<NestExpressApplication>;
|
|
50
|
+
/**
|
|
51
|
+
* Closes the underlying Nest app.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* await fetch.close();
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
close(): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export declare function createFetcher({ module, configure, opts }: CreateHandlerOptions): Fetcher;
|
|
61
|
+
//#endregion
|
package/dist/express.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NestFactory } from "@nestjs/core";
|
|
2
|
+
import { ExpressAdapter } from "@nestjs/platform-express";
|
|
3
|
+
import inject from "light-my-request";
|
|
4
|
+
//#region src/adapters/express/index.ts
|
|
5
|
+
function createFetcher({ module, configure, opts }) {
|
|
6
|
+
let pending;
|
|
7
|
+
const instance = () => {
|
|
8
|
+
if (pending) return pending;
|
|
9
|
+
pending = (async () => {
|
|
10
|
+
const app = await NestFactory.create(module, new ExpressAdapter(), opts);
|
|
11
|
+
await configure?.(app);
|
|
12
|
+
await app.init();
|
|
13
|
+
return app;
|
|
14
|
+
})();
|
|
15
|
+
return pending;
|
|
16
|
+
};
|
|
17
|
+
const handler = (async (request) => {
|
|
18
|
+
const app = await instance();
|
|
19
|
+
const url = new URL(request.url);
|
|
20
|
+
const hasBody = !["GET", "HEAD"].includes(request.method);
|
|
21
|
+
const res = await inject(app.getHttpAdapter().getInstance(), {
|
|
22
|
+
method: request.method,
|
|
23
|
+
url: url.pathname + url.search,
|
|
24
|
+
headers: Object.fromEntries(request.headers),
|
|
25
|
+
payload: hasBody ? Buffer.from(await request.arrayBuffer()) : void 0
|
|
26
|
+
});
|
|
27
|
+
const headers = new Headers();
|
|
28
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
29
|
+
if (k === "content-length" || k === "transfer-encoding") continue;
|
|
30
|
+
if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
|
|
31
|
+
else if (v != null) headers.set(k, String(v));
|
|
32
|
+
}
|
|
33
|
+
return new Response(res.rawPayload, {
|
|
34
|
+
status: res.statusCode,
|
|
35
|
+
headers
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
handler.app = instance;
|
|
39
|
+
handler.close = async () => {
|
|
40
|
+
if (pending) await (await pending).close();
|
|
41
|
+
pending = void 0;
|
|
42
|
+
};
|
|
43
|
+
return handler;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { createFetcher };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { NestFastifyApplication } from "@nestjs/platform-fastify";
|
|
2
|
+
import { NestApplicationOptions, Type } from "@nestjs/common";
|
|
3
|
+
//#region src/adapters/fastify/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Options for {@link createFetcher}.
|
|
6
|
+
*/
|
|
7
|
+
export interface CreateHandlerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Root Nest module.
|
|
10
|
+
*/
|
|
11
|
+
module: Type<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Optional hook to configure the app before init (pipes, prefix, cors, ...).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const fetch = createFetcher({
|
|
18
|
+
* module: AppModule,
|
|
19
|
+
* configure: (app) => {
|
|
20
|
+
* app.use(cors());
|
|
21
|
+
* app.use(bodyParser.json());
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
configure?: (app: NestFastifyApplication) => void | Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Optional Nest factory options.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const fetch = createFetcher({
|
|
33
|
+
* module: AppModule,
|
|
34
|
+
* opts: { logger: ["error", "warn"] },
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
opts?: NestApplicationOptions;
|
|
39
|
+
}
|
|
40
|
+
export interface Fetcher {
|
|
41
|
+
(request: Request): Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the underlying Nest app instance.
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const app = await fetch.app();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
app(): Promise<NestFastifyApplication>;
|
|
50
|
+
/**
|
|
51
|
+
* Closes the underlying Nest app.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* await fetch.close();
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
close(): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export declare function createFetcher({ module, configure, opts }: CreateHandlerOptions): Fetcher;
|
|
61
|
+
//#endregion
|
package/dist/fastify.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NestFactory } from "@nestjs/core";
|
|
2
|
+
import { FastifyAdapter } from "@nestjs/platform-fastify";
|
|
3
|
+
//#region src/adapters/fastify/index.ts
|
|
4
|
+
function createFetcher({ module, configure, opts }) {
|
|
5
|
+
let pending;
|
|
6
|
+
const instance = () => {
|
|
7
|
+
if (pending) return pending;
|
|
8
|
+
pending = (async () => {
|
|
9
|
+
const app = await NestFactory.create(module, new FastifyAdapter(), opts);
|
|
10
|
+
await configure?.(app);
|
|
11
|
+
await app.init();
|
|
12
|
+
await app.getHttpAdapter().getInstance().ready();
|
|
13
|
+
return app;
|
|
14
|
+
})();
|
|
15
|
+
return pending;
|
|
16
|
+
};
|
|
17
|
+
const handler = (async (request) => {
|
|
18
|
+
const app = await instance();
|
|
19
|
+
const url = new URL(request.url);
|
|
20
|
+
const hasBody = !["GET", "HEAD"].includes(request.method);
|
|
21
|
+
const res = await app.getHttpAdapter().getInstance().inject({
|
|
22
|
+
method: request.method,
|
|
23
|
+
url: url.pathname + url.search,
|
|
24
|
+
headers: Object.fromEntries(request.headers),
|
|
25
|
+
payload: hasBody ? Buffer.from(await request.arrayBuffer()) : void 0
|
|
26
|
+
});
|
|
27
|
+
const headers = new Headers();
|
|
28
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
29
|
+
if (k === "content-length" || k === "transfer-encoding") continue;
|
|
30
|
+
if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
|
|
31
|
+
else if (v != null) headers.set(k, String(v));
|
|
32
|
+
}
|
|
33
|
+
return new Response(res.rawPayload, {
|
|
34
|
+
status: res.statusCode,
|
|
35
|
+
headers
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
handler.app = instance;
|
|
39
|
+
handler.close = async () => {
|
|
40
|
+
if (pending) await (await pending).close();
|
|
41
|
+
pending = void 0;
|
|
42
|
+
};
|
|
43
|
+
return handler;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { createFetcher };
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-nest",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Makes Nest applications compatible with Web Standard Request/Response handlers (Bun, Cloudflare, ...).",
|
|
5
|
+
"files": [
|
|
6
|
+
"package.json",
|
|
7
|
+
"readme.md",
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
"./fastify": {
|
|
14
|
+
"types": "./dist/fastify.d.mts",
|
|
15
|
+
"import": "./dist/fastify.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./express": {
|
|
18
|
+
"types": "./dist/express.d.mts",
|
|
19
|
+
"import": "./dist/express.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "tsdown --watch",
|
|
25
|
+
"build": "tsdown --clean",
|
|
26
|
+
"typecheck": "bun check",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"lint": "oxlint --type-aware && oxfmt --check",
|
|
29
|
+
"format": "oxlint --fix --fix-suggestions && oxfmt"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"light-my-request": "6.8.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@arethetypeswrong/core": "0.18.5",
|
|
36
|
+
"@types/bun": "latest",
|
|
37
|
+
"@types/node": "26.6.5",
|
|
38
|
+
"oxfmt": "0.66.0",
|
|
39
|
+
"oxlint": "1.81.0",
|
|
40
|
+
"oxlint-tsgolint": "7.0.2001",
|
|
41
|
+
"publint": "0.3.24",
|
|
42
|
+
"tsdown": "0.23.0",
|
|
43
|
+
"tsup": "8.5.1",
|
|
44
|
+
"typescript": "7.0.2"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@nestjs/common": "^12.0.0",
|
|
48
|
+
"@nestjs/core": "^12.0.0",
|
|
49
|
+
"@nestjs/platform-express": "^12.0.0",
|
|
50
|
+
"@nestjs/platform-fastify": "^12.0.0",
|
|
51
|
+
"reflect-metadata": "^0.1.12 || ^0.2.0",
|
|
52
|
+
"rxjs": "^7.1.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@nestjs/platform-express": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"@nestjs/platform-fastify": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# `open-nest`
|
|
2
|
+
|
|
3
|
+
Turn a Nest application into a Web fetch handler. Pass a `Request`, get a `Response`, without calling `listen()`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add open-nest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Nest, `reflect-metadata`, `rxjs`, and your platform package are peer dependencies. An existing Nest app already has them. Import `open-nest/fastify` or `open-nest/express` to match the platform you use.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import "reflect-metadata";
|
|
17
|
+
import { Controller, Get, Module } from "@nestjs/common";
|
|
18
|
+
import { createFetcher } from "open-nest/fastify";
|
|
19
|
+
import { AppModule } from "@/app.module";
|
|
20
|
+
|
|
21
|
+
const fetch = createFetcher({ module: AppModule });
|
|
22
|
+
|
|
23
|
+
await Bun.serve({
|
|
24
|
+
fetch,
|
|
25
|
+
port: 3000,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Express follows the same pattern, imported from `open-nest/express`.
|
|
30
|
+
|
|
31
|
+
The Nest app boots on the first request and its instance is cached for all further requests.
|
|
32
|
+
|
|
33
|
+
You can close the Nest app and clear the cache with `fetch.close()`.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
await fetch.close();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Configure
|
|
40
|
+
|
|
41
|
+
`configure` runs after the app is created and before `init`. Use it for pipes, a global prefix, CORS, and the other app methods.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const fetch = createFetcher({
|
|
45
|
+
module: AppModule,
|
|
46
|
+
configure: (app) => {
|
|
47
|
+
app.setGlobalPrefix("api");
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Nest options
|
|
53
|
+
|
|
54
|
+
`opts` is forwarded to `NestFactory.create` as `NestApplicationOptions`. When omitted, Nest keeps its own defaults.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const fetch = createFetcher({
|
|
58
|
+
module: AppModule,
|
|
59
|
+
opts: { logger: ["error", "warn"] },
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Lifecycle
|
|
64
|
+
|
|
65
|
+
#### Develop
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bun dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Test
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bun test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Build
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bun run build
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Typecheck
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
bun typecheck
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Lint / Format
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bun lint
|
|
93
|
+
bun format
|
|
94
|
+
```
|
package/README.md
DELETED