@zedtech/zedkit-nest 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +82 -2
- package/dist/all-exceptions.filter.d.ts +17 -0
- package/dist/all-exceptions.filter.d.ts.map +1 -0
- package/dist/all-exceptions.filter.js +97 -0
- package/dist/all-exceptions.filter.js.map +1 -0
- package/dist/factory.d.ts +121 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +176 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/request-id.d.ts +3 -0
- package/dist/request-id.d.ts.map +1 -0
- package/dist/request-id.interceptor.d.ts +11 -0
- package/dist/request-id.interceptor.d.ts.map +1 -0
- package/dist/request-id.interceptor.js +31 -0
- package/dist/request-id.interceptor.js.map +1 -0
- package/dist/request-id.js +4 -0
- package/dist/request-id.js.map +1 -0
- package/dist/validate-env.d.ts +11 -0
- package/dist/validate-env.d.ts.map +1 -0
- package/dist/validate-env.js +31 -0
- package/dist/validate-env.js.map +1 -0
- package/package.json +70 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zedtech-fr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zedtech/zedkit-nest
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generic NestJS bootstrap factory. Original work by the same author.
|
|
4
|
+
|
|
5
|
+
`createNestApp({ module, envSchema, docs, ... })` wires only standard
|
|
6
|
+
pieces and returns the app ready to `listen()`:
|
|
7
|
+
|
|
8
|
+
- Fastify platform (`platform-fastify`) with exact raw bodies
|
|
9
|
+
(`{ rawBody: true }`, proven by test) and a configurable body cap.
|
|
10
|
+
- Validated env (`@nestjs/config` + a class-validator schema per app;
|
|
11
|
+
boot fails fast with one readable error).
|
|
12
|
+
- Request-id JSON logs (`nestjs-pino`): honors `x-request-id`,
|
|
13
|
+
generates a UUID otherwise; one id across logs and error bodies.
|
|
14
|
+
`Authorization` and cookies are redacted from logs by default.
|
|
15
|
+
- Strict global `ValidationPipe` (whitelist, forbid non-whitelisted,
|
|
16
|
+
transform; implicit conversion off — see below). Relax per app with
|
|
17
|
+
`validation: { whitelist: false, forbidNonWhitelisted: false }`.
|
|
18
|
+
- One global error shape: `{ code, message, requestId }` (+ status).
|
|
19
|
+
4xx carries the safe message; 5xx never leaks internals. Every response
|
|
20
|
+
echoes `x-request-id` (interceptor on handled routes, filter otherwise).
|
|
21
|
+
Apps with domain errors pass their own filter via `exceptionFilter`.
|
|
22
|
+
- Optional global throttler (`@nestjs/throttler`, default 100/min;
|
|
23
|
+
in-memory, single process — set `trustProxy` before running behind a
|
|
24
|
+
proxy or every client shares one bucket).
|
|
25
|
+
- Generated OpenAPI (`@nestjs/swagger`), served as raw Fastify routes:
|
|
26
|
+
public by design, guards do not apply. Enabled by default except in
|
|
27
|
+
production (`NODE_ENV=production`) — opt back in explicitly if you
|
|
28
|
+
serve docs in prod. `docs.aliases` serves the same JSON from extra
|
|
29
|
+
absolute paths. Shutdown hooks.
|
|
30
|
+
- Custom body parsers per content type (`bodyParsers`), installed
|
|
31
|
+
before Nest's defaults so an `application/json` entry replaces the
|
|
32
|
+
default parser; rawBody capture included.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import "reflect-metadata";
|
|
36
|
+
import { createNestApp } from "@zedtech/zedkit-nest";
|
|
37
|
+
|
|
38
|
+
const app = await createNestApp({
|
|
39
|
+
module: AppModule,
|
|
40
|
+
envSchema: AppEnv,
|
|
41
|
+
docs: { title: "My API", version: "v1" },
|
|
42
|
+
});
|
|
43
|
+
await app.listen(3000);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Framework pieces are peer dependencies (one copy per app — required for
|
|
47
|
+
Nest DI). Install them alongside:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
pnpm add @zedtech/zedkit-nest @fastify/static @nestjs/common @nestjs/config \
|
|
51
|
+
@nestjs/core @nestjs/platform-fastify @nestjs/swagger @nestjs/throttler \
|
|
52
|
+
class-transformer class-validator fastify nestjs-pino pino pino-http \
|
|
53
|
+
reflect-metadata
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Rules (non-negotiable):
|
|
57
|
+
|
|
58
|
+
- Zero product names or logic.
|
|
59
|
+
- Numbers need explicit `@Type(() => Number)` (env ints, nested
|
|
60
|
+
DTOs): implicit conversion relies on decorator metadata, which
|
|
61
|
+
vitest can only emit via SWC — and the day one suite forgets SWC,
|
|
62
|
+
implicit conversion silently diverges from `tsc`.
|
|
63
|
+
- Tests boot the factory with a product-free fixture domain and hit
|
|
64
|
+
it over HTTP (supertest, ephemeral ports): validation, exact raw
|
|
65
|
+
bytes, error shapes, throttling, request ids, docs, env failure.
|
|
66
|
+
|
|
67
|
+
Vitest transforms via SWC (`swc.vite()`, options inferred from
|
|
68
|
+
tsconfig): esbuild cannot emit decorator metadata, which would
|
|
69
|
+
silently disable the global `ValidationPipe` in tests. Never remove
|
|
70
|
+
it. Agent rules: [AGENTS.md](AGENTS.md)
|
|
71
|
+
|
|
72
|
+
Find helpers by need in [CATALOG.md](CATALOG.md).
|
|
73
|
+
|
|
74
|
+
## Install or copy?
|
|
75
|
+
|
|
76
|
+
Install: this package is framework integration (DI, decorators, peer
|
|
77
|
+
dependencies), not suitable for source-copying.
|
|
78
|
+
|
|
79
|
+
## Verify
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm --filter @zedtech/zedkit-nest typecheck && pnpm --filter @zedtech/zedkit-nest lint && pnpm --filter @zedtech/zedkit-nest build && pnpm --filter @zedtech/zedkit-nest test
|
|
83
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ArgumentsHost, ExceptionFilter } from "@nestjs/common";
|
|
2
|
+
/**
|
|
3
|
+
* Global error shape for every Nest app from this bootstrap:
|
|
4
|
+
* `{ code, message, requestId }` with the matching HTTP status.
|
|
5
|
+
* 4xx responses carry the safe client-facing message; 5xx responses never
|
|
6
|
+
* leak internals (the full error goes to the logger only).
|
|
7
|
+
*/
|
|
8
|
+
export interface ErrorBody {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
requestId: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class AllExceptionsFilter implements ExceptionFilter {
|
|
14
|
+
private readonly logger;
|
|
15
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=all-exceptions.filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all-exceptions.filter.d.ts","sourceRoot":"","sources":["../src/all-exceptions.filter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EAEb,eAAe,EAGhB,MAAM,gBAAgB,CAAC;AAIxB;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAwCD,qBACa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAE/D,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;CAuCrD"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var AllExceptionsFilter_1;
|
|
8
|
+
// Original work by the same author.
|
|
9
|
+
import { Catch, HttpException, Logger, } from "@nestjs/common";
|
|
10
|
+
import { REQUEST_ID_HEADER } from "./request-id.js";
|
|
11
|
+
const STATUS_CODES = {
|
|
12
|
+
400: "invalid_input",
|
|
13
|
+
401: "unauthorized",
|
|
14
|
+
403: "forbidden",
|
|
15
|
+
404: "not_found",
|
|
16
|
+
409: "conflict",
|
|
17
|
+
413: "payload_too_large",
|
|
18
|
+
415: "unsupported_media_type",
|
|
19
|
+
429: "rate_limited",
|
|
20
|
+
500: "internal",
|
|
21
|
+
503: "unavailable",
|
|
22
|
+
};
|
|
23
|
+
/** Safe public messages for mapped 5xx; everything else stays "internal error". */
|
|
24
|
+
const STATUS_5XX_MESSAGES = {
|
|
25
|
+
503: "service unavailable",
|
|
26
|
+
};
|
|
27
|
+
function fastifyBodyErrorStatus(exception) {
|
|
28
|
+
if (typeof exception !== "object" || exception === null)
|
|
29
|
+
return null;
|
|
30
|
+
if (!("code" in exception) || typeof exception.code !== "string")
|
|
31
|
+
return null;
|
|
32
|
+
if (exception.code === "FST_ERR_CTP_BODY_TOO_LARGE")
|
|
33
|
+
return 413;
|
|
34
|
+
if (exception.code === "FST_ERR_CTP_INVALID_MEDIA_TYPE")
|
|
35
|
+
return 415;
|
|
36
|
+
if (exception.code.startsWith("FST_ERR_CTP_"))
|
|
37
|
+
return 400;
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function httpMessage(exception) {
|
|
41
|
+
const response = exception.getResponse();
|
|
42
|
+
if (typeof response === "string")
|
|
43
|
+
return response;
|
|
44
|
+
if (typeof response === "object" && response !== null && "message" in response) {
|
|
45
|
+
const message = response.message;
|
|
46
|
+
if (typeof message === "string")
|
|
47
|
+
return message;
|
|
48
|
+
if (Array.isArray(message))
|
|
49
|
+
return message.map(String).join("; ");
|
|
50
|
+
}
|
|
51
|
+
return exception.message;
|
|
52
|
+
}
|
|
53
|
+
let AllExceptionsFilter = AllExceptionsFilter_1 = class AllExceptionsFilter {
|
|
54
|
+
logger = new Logger(AllExceptionsFilter_1.name);
|
|
55
|
+
catch(exception, host) {
|
|
56
|
+
const ctx = host.switchToHttp();
|
|
57
|
+
const req = ctx.getRequest();
|
|
58
|
+
const reply = ctx.getResponse();
|
|
59
|
+
const requestId = typeof req.id === "string" ? req.id : "unknown";
|
|
60
|
+
let status;
|
|
61
|
+
let message;
|
|
62
|
+
if (exception instanceof HttpException) {
|
|
63
|
+
status = exception.getStatus();
|
|
64
|
+
message =
|
|
65
|
+
status >= 500
|
|
66
|
+
? (STATUS_5XX_MESSAGES[status] ?? "internal error")
|
|
67
|
+
: httpMessage(exception);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const bodyStatus = fastifyBodyErrorStatus(exception);
|
|
71
|
+
status = bodyStatus ?? 500;
|
|
72
|
+
message =
|
|
73
|
+
status === 413
|
|
74
|
+
? "body exceeds the configured limit"
|
|
75
|
+
: status === 415
|
|
76
|
+
? "unsupported media type"
|
|
77
|
+
: status === 400
|
|
78
|
+
? "invalid request body"
|
|
79
|
+
: "internal error";
|
|
80
|
+
}
|
|
81
|
+
if (status >= 500) {
|
|
82
|
+
this.logger.error(`Unhandled error on ${req.method} ${req.url}: ${String(exception)}`, exception instanceof Error ? exception.stack : undefined);
|
|
83
|
+
}
|
|
84
|
+
// A streaming handler that throws after the first flush already sent
|
|
85
|
+
// the reply: sending again would raise inside the filter itself.
|
|
86
|
+
if (reply.sent)
|
|
87
|
+
return;
|
|
88
|
+
const code = STATUS_CODES[status] ?? `http_${status}`;
|
|
89
|
+
reply.header(REQUEST_ID_HEADER, requestId);
|
|
90
|
+
reply.status(status).send({ code, message, requestId });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
AllExceptionsFilter = AllExceptionsFilter_1 = __decorate([
|
|
94
|
+
Catch()
|
|
95
|
+
], AllExceptionsFilter);
|
|
96
|
+
export { AllExceptionsFilter };
|
|
97
|
+
//# sourceMappingURL=all-exceptions.filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all-exceptions.filter.js","sourceRoot":"","sources":["../src/all-exceptions.filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { type INestApplication, type Type } from "@nestjs/common";
|
|
3
|
+
import { type NestFastifyApplication } from "@nestjs/platform-fastify";
|
|
4
|
+
import { type ThrottlerStorage } from "@nestjs/throttler";
|
|
5
|
+
import type { FastifyBodyParser, FastifyServerOptions } from "fastify";
|
|
6
|
+
import type { DestinationStream } from "pino";
|
|
7
|
+
/** Whatever `app.enableCors()` accepts (`true` allows any origin). */
|
|
8
|
+
type CorsSetting = Parameters<INestApplication["enableCors"]>[0];
|
|
9
|
+
export interface ThrottleOptions {
|
|
10
|
+
ttlMs: number;
|
|
11
|
+
limit: number;
|
|
12
|
+
/**
|
|
13
|
+
* Custom hit counter (e.g. shared across replicas). Defaults to the
|
|
14
|
+
* in-memory store: correct for a single process only.
|
|
15
|
+
*/
|
|
16
|
+
storage?: ThrottlerStorage | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface DocsOptions {
|
|
19
|
+
title: string;
|
|
20
|
+
version: string;
|
|
21
|
+
description?: string | undefined;
|
|
22
|
+
/** Serve path for the UI and `<path>-json`. Defaults to `docs`. */
|
|
23
|
+
path?: string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Extra absolute paths serving the same JSON document (stable
|
|
26
|
+
* contract URLs beyond the swagger-default `<path>-json`). Served
|
|
27
|
+
* raw (no guards), like the default JSON route.
|
|
28
|
+
*/
|
|
29
|
+
aliases?: string[] | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Request-logging options. The logger is process-wide: when several apps
|
|
33
|
+
* boot in one process (tests), the first app's logger options win.
|
|
34
|
+
*/
|
|
35
|
+
export interface LoggerOptions {
|
|
36
|
+
level?: string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* pino redact paths. Defaults to the Authorization header and cookies:
|
|
39
|
+
* request logs must never carry credentials.
|
|
40
|
+
*/
|
|
41
|
+
redact?: string[] | undefined;
|
|
42
|
+
/** Log destination (pino passthrough). Defaults to stdout. */
|
|
43
|
+
stream?: DestinationStream | undefined;
|
|
44
|
+
}
|
|
45
|
+
export interface ValidationOptions {
|
|
46
|
+
/** Strip properties with no decorators. Defaults to true. */
|
|
47
|
+
whitelist?: boolean | undefined;
|
|
48
|
+
/** Reject (400) unknown properties instead of stripping. Defaults to true. */
|
|
49
|
+
forbidNonWhitelisted?: boolean | undefined;
|
|
50
|
+
}
|
|
51
|
+
export interface NestBootstrapOptions {
|
|
52
|
+
/** Root application module of the product app. */
|
|
53
|
+
module: Type<object>;
|
|
54
|
+
/**
|
|
55
|
+
* class-validator decorated env schema. Validated at boot; boot fails
|
|
56
|
+
* fast with a readable error when a declared variable is invalid.
|
|
57
|
+
* Numbers need an explicit `@Type(() => Number)`: implicit conversion
|
|
58
|
+
* relies on decorator metadata, which vitest/esbuild cannot emit.
|
|
59
|
+
*/
|
|
60
|
+
envSchema?: Type<object> | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* `.env` file lookup (ConfigModule passthrough). Defaults to `[".env"]`
|
|
63
|
+
* (plus `process.env`, which always wins). Missing files are ignored.
|
|
64
|
+
*/
|
|
65
|
+
envFilePath?: string[] | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Generated OpenAPI. Enabled by default except when
|
|
68
|
+
* `NODE_ENV=production` (a public endpoint inventory is a gift to
|
|
69
|
+
* attackers — opt back in explicitly if you serve docs in prod). Pass
|
|
70
|
+
* `false` for apps without HTTP (workers).
|
|
71
|
+
*/
|
|
72
|
+
docs?: DocsOptions | false | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Global IP-based coarse guard. Enabled by default at 100 req/min;
|
|
75
|
+
* product apps add their own credential-scoped limits on top.
|
|
76
|
+
*/
|
|
77
|
+
throttle?: ThrottleOptions | false | undefined;
|
|
78
|
+
/** Populate `request.rawBody` with the exact bytes. Defaults to true. */
|
|
79
|
+
rawBody?: boolean | undefined;
|
|
80
|
+
/** Fastify body cap in bytes. Defaults to 1 MiB. */
|
|
81
|
+
bodyLimit?: number | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Trust proxy headers (Fastify passthrough). Set this before running
|
|
84
|
+
* behind a proxy or every client shares one throttle bucket.
|
|
85
|
+
*/
|
|
86
|
+
trustProxy?: FastifyServerOptions["trustProxy"] | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* `true` allows any origin; pass an object to restrict it. Note: a
|
|
89
|
+
* plain string origin is echoed unconditionally — use an array or
|
|
90
|
+
* function to actually restrict.
|
|
91
|
+
*/
|
|
92
|
+
cors?: CorsSetting | undefined;
|
|
93
|
+
logger?: LoggerOptions | undefined;
|
|
94
|
+
/** Global ValidationPipe shape. Defaults to strict (strip + forbid). */
|
|
95
|
+
validation?: ValidationOptions | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Global error filter. Defaults to AllExceptionsFilter; apps with
|
|
98
|
+
* domain errors pass their own filter class here.
|
|
99
|
+
*/
|
|
100
|
+
exceptionFilter?: Type<object> | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Custom body parsers by content type, installed through the
|
|
103
|
+
* adapter's standard `useBodyParser` (rawBody capture included)
|
|
104
|
+
* before Nest registers its defaults — so a custom
|
|
105
|
+
* `application/json` parser replaces the default instead of
|
|
106
|
+
* conflicting with it. Receives the raw buffer; `done(null, value)`
|
|
107
|
+
* sets `@Body()`, `done(error)` with `statusCode` rejects.
|
|
108
|
+
*/
|
|
109
|
+
bodyParsers?: Record<string, FastifyBodyParser<Buffer>> | undefined;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Generic Nest bootstrap shared by every app. Wires only standard pieces:
|
|
113
|
+
* Fastify platform, validated env (`@nestjs/config`), request-id JSON logs
|
|
114
|
+
* (`nestjs-pino`), strict global `ValidationPipe`, a global error-shape
|
|
115
|
+
* filter, an optional global throttler, generated OpenAPI, and graceful
|
|
116
|
+
* shutdown hooks. Returns the app ready to `listen()`; tests use it with
|
|
117
|
+
* supertest without ever touching product code.
|
|
118
|
+
*/
|
|
119
|
+
export declare function createNestApp(options: NestBootstrapOptions): Promise<NestFastifyApplication>;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
// Original work by the same author. Design notes:
|
|
8
|
+
// - `envFilePath` option (default `[".env"]`) instead of a hardcoded
|
|
9
|
+
// monorepo-relative lookup.
|
|
10
|
+
// - `trustProxy` passthrough so the documented proxy guidance works.
|
|
11
|
+
import "reflect-metadata";
|
|
12
|
+
import { Module, ValidationPipe, } from "@nestjs/common";
|
|
13
|
+
import { ConfigModule } from "@nestjs/config";
|
|
14
|
+
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE, NestFactory } from "@nestjs/core";
|
|
15
|
+
import { FastifyAdapter, } from "@nestjs/platform-fastify";
|
|
16
|
+
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|
17
|
+
import { ThrottlerGuard, ThrottlerModule, } from "@nestjs/throttler";
|
|
18
|
+
import { Logger as PinoLogger, LoggerModule } from "nestjs-pino";
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { AllExceptionsFilter } from "./all-exceptions.filter.js";
|
|
21
|
+
import { REQUEST_ID_HEADER } from "./request-id.js";
|
|
22
|
+
import { RequestIdInterceptor } from "./request-id.interceptor.js";
|
|
23
|
+
import { validateEnv } from "./validate-env.js";
|
|
24
|
+
const DEFAULT_THROTTLE = { ttlMs: 60_000, limit: 100 };
|
|
25
|
+
const DEFAULT_LOG_REDACT = ["req.headers.authorization", "req.headers.cookie"];
|
|
26
|
+
const DEFAULT_BODY_LIMIT = 1024 * 1024;
|
|
27
|
+
const DEFAULT_DOCS = {
|
|
28
|
+
title: "API",
|
|
29
|
+
version: "0.0.0",
|
|
30
|
+
path: "docs",
|
|
31
|
+
};
|
|
32
|
+
function requestIdOf(req) {
|
|
33
|
+
const header = req.headers[REQUEST_ID_HEADER];
|
|
34
|
+
// Node joins repeated HTTP/1 headers with ", "; HTTP/2 may deliver an
|
|
35
|
+
// array. Either way the first value wins.
|
|
36
|
+
const first = Array.isArray(header)
|
|
37
|
+
? header[0]?.trim()
|
|
38
|
+
: header?.split(",")[0]?.trim();
|
|
39
|
+
if (first)
|
|
40
|
+
return first;
|
|
41
|
+
return randomUUID();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Fastify assigns `request.id` (via its own `genReqId`) before pino-http
|
|
45
|
+
* runs, so pino reuses it: logs, error bodies, and the wire id are one
|
|
46
|
+
* value. Generating a second UUID here would split them apart.
|
|
47
|
+
*/
|
|
48
|
+
function pinoReqId(req) {
|
|
49
|
+
const existing = req.id;
|
|
50
|
+
if (typeof existing === "string" && existing.length > 0)
|
|
51
|
+
return existing;
|
|
52
|
+
return requestIdOf(req);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generic Nest bootstrap shared by every app. Wires only standard pieces:
|
|
56
|
+
* Fastify platform, validated env (`@nestjs/config`), request-id JSON logs
|
|
57
|
+
* (`nestjs-pino`), strict global `ValidationPipe`, a global error-shape
|
|
58
|
+
* filter, an optional global throttler, generated OpenAPI, and graceful
|
|
59
|
+
* shutdown hooks. Returns the app ready to `listen()`; tests use it with
|
|
60
|
+
* supertest without ever touching product code.
|
|
61
|
+
*/
|
|
62
|
+
export async function createNestApp(options) {
|
|
63
|
+
const throttle = options.throttle === undefined ? DEFAULT_THROTTLE : options.throttle;
|
|
64
|
+
const schema = options.envSchema;
|
|
65
|
+
let BootstrapRootModule = class BootstrapRootModule {
|
|
66
|
+
};
|
|
67
|
+
BootstrapRootModule = __decorate([
|
|
68
|
+
Module({
|
|
69
|
+
imports: [
|
|
70
|
+
ConfigModule.forRoot({
|
|
71
|
+
isGlobal: true,
|
|
72
|
+
cache: true,
|
|
73
|
+
envFilePath: options.envFilePath ?? [".env"],
|
|
74
|
+
...(schema
|
|
75
|
+
? { validate: (config) => validateEnv(schema, config) }
|
|
76
|
+
: {}),
|
|
77
|
+
}),
|
|
78
|
+
LoggerModule.forRoot({
|
|
79
|
+
// nestjs-pino takes the destination as a tuple, not an option key.
|
|
80
|
+
pinoHttp: options.logger?.stream === undefined
|
|
81
|
+
? {
|
|
82
|
+
level: options.logger?.level ?? "info",
|
|
83
|
+
genReqId: (req) => pinoReqId(req),
|
|
84
|
+
redact: options.logger?.redact ?? DEFAULT_LOG_REDACT,
|
|
85
|
+
}
|
|
86
|
+
: [
|
|
87
|
+
{
|
|
88
|
+
level: options.logger.level ?? "info",
|
|
89
|
+
genReqId: (req) => pinoReqId(req),
|
|
90
|
+
redact: options.logger.redact ?? DEFAULT_LOG_REDACT,
|
|
91
|
+
},
|
|
92
|
+
options.logger.stream,
|
|
93
|
+
],
|
|
94
|
+
}),
|
|
95
|
+
...(throttle
|
|
96
|
+
? [
|
|
97
|
+
ThrottlerModule.forRoot(throttle.storage
|
|
98
|
+
? {
|
|
99
|
+
storage: throttle.storage,
|
|
100
|
+
throttlers: [{ ttl: throttle.ttlMs, limit: throttle.limit }],
|
|
101
|
+
}
|
|
102
|
+
: [{ ttl: throttle.ttlMs, limit: throttle.limit }]),
|
|
103
|
+
]
|
|
104
|
+
: []),
|
|
105
|
+
options.module,
|
|
106
|
+
],
|
|
107
|
+
providers: [
|
|
108
|
+
{
|
|
109
|
+
provide: APP_PIPE,
|
|
110
|
+
useValue: new ValidationPipe({
|
|
111
|
+
whitelist: options.validation?.whitelist ?? true,
|
|
112
|
+
forbidNonWhitelisted: options.validation?.forbidNonWhitelisted ?? true,
|
|
113
|
+
transform: true,
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
{ provide: APP_FILTER, useClass: options.exceptionFilter ?? AllExceptionsFilter },
|
|
117
|
+
{ provide: APP_INTERCEPTOR, useClass: RequestIdInterceptor },
|
|
118
|
+
...(throttle ? [{ provide: APP_GUARD, useClass: ThrottlerGuard }] : []),
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
], BootstrapRootModule);
|
|
122
|
+
const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;
|
|
123
|
+
const adapter = new FastifyAdapter({
|
|
124
|
+
bodyLimit,
|
|
125
|
+
genReqId: (req) => requestIdOf(req),
|
|
126
|
+
// exactOptionalPropertyTypes: only pass the key when set.
|
|
127
|
+
...(options.trustProxy === undefined ? {} : { trustProxy: options.trustProxy }),
|
|
128
|
+
});
|
|
129
|
+
for (const [contentType, parser] of Object.entries(options.bodyParsers ?? {})) {
|
|
130
|
+
adapter.useBodyParser(contentType, options.rawBody ?? true, { bodyLimit }, parser);
|
|
131
|
+
}
|
|
132
|
+
const app = await NestFactory.create(BootstrapRootModule, adapter, {
|
|
133
|
+
rawBody: options.rawBody ?? true,
|
|
134
|
+
bufferLogs: true,
|
|
135
|
+
// Surface boot errors (e.g. invalid env) as rejections instead of
|
|
136
|
+
// `process.abort()`: callers and tests can handle them, and an
|
|
137
|
+
// unhandled rejection still crashes with the error printed.
|
|
138
|
+
abortOnError: false,
|
|
139
|
+
});
|
|
140
|
+
app.useLogger(app.get(PinoLogger));
|
|
141
|
+
app.enableShutdownHooks();
|
|
142
|
+
if (options.cors) {
|
|
143
|
+
app.enableCors(options.cors === true ? undefined : options.cors);
|
|
144
|
+
}
|
|
145
|
+
const docs = options.docs === undefined
|
|
146
|
+
? process.env.NODE_ENV === "production"
|
|
147
|
+
? false
|
|
148
|
+
: DEFAULT_DOCS
|
|
149
|
+
: options.docs;
|
|
150
|
+
if (docs) {
|
|
151
|
+
const builder = new DocumentBuilder()
|
|
152
|
+
.setTitle(docs.title)
|
|
153
|
+
.setVersion(docs.version);
|
|
154
|
+
if (docs.description) {
|
|
155
|
+
builder.setDescription(docs.description);
|
|
156
|
+
}
|
|
157
|
+
// Tolerate "docs/" or "/docs": the JSON route is derived by suffix.
|
|
158
|
+
// Manual trim (no regex): the path is deployer-controlled input.
|
|
159
|
+
const rawDocsPath = docs.path ?? "docs";
|
|
160
|
+
let docsStart = 0;
|
|
161
|
+
let docsEnd = rawDocsPath.length;
|
|
162
|
+
while (docsStart < docsEnd && rawDocsPath[docsStart] === "/")
|
|
163
|
+
docsStart += 1;
|
|
164
|
+
while (docsEnd > docsStart && rawDocsPath[docsEnd - 1] === "/")
|
|
165
|
+
docsEnd -= 1;
|
|
166
|
+
const docsPath = rawDocsPath.slice(docsStart, docsEnd) || "docs";
|
|
167
|
+
const document = SwaggerModule.createDocument(app, builder.build());
|
|
168
|
+
SwaggerModule.setup(docsPath, app, document);
|
|
169
|
+
const fastify = app.getHttpAdapter().getInstance();
|
|
170
|
+
for (const alias of docs.aliases ?? []) {
|
|
171
|
+
fastify.get(alias, (_req, reply) => reply.send(document));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return app;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AllExceptionsFilter, type ErrorBody } from "./all-exceptions.filter.js";
|
|
2
|
+
export { createNestApp, type DocsOptions, type LoggerOptions, type NestBootstrapOptions, type ThrottleOptions, type ValidationOptions, } from "./factory.js";
|
|
3
|
+
export { REQUEST_ID_HEADER } from "./request-id.js";
|
|
4
|
+
export { RequestIdInterceptor } from "./request-id.interceptor.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EACL,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AllExceptionsFilter } from "./all-exceptions.filter.js";
|
|
2
|
+
export { createNestApp, } from "./factory.js";
|
|
3
|
+
export { REQUEST_ID_HEADER } from "./request-id.js";
|
|
4
|
+
export { RequestIdInterceptor } from "./request-id.interceptor.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAkB,MAAM,4BAA4B,CAAC;AACjF,OAAO,EACL,aAAa,GAMd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.d.ts","sourceRoot":"","sources":["../src/request-id.ts"],"names":[],"mappings":"AACA,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CallHandler, type ExecutionContext, type NestInterceptor } from "@nestjs/common";
|
|
2
|
+
/**
|
|
3
|
+
* Echoes the request id as a response header on every handled route.
|
|
4
|
+
* Set before the handler runs, so it survives handler errors; errors
|
|
5
|
+
* thrown earlier (guards, parsers, unknown routes) get the same
|
|
6
|
+
* header from the error filter instead.
|
|
7
|
+
*/
|
|
8
|
+
export declare class RequestIdInterceptor implements NestInterceptor {
|
|
9
|
+
intercept(context: ExecutionContext, next: CallHandler): ReturnType<CallHandler["handle"]>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=request-id.interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.interceptor.d.ts","sourceRoot":"","sources":["../src/request-id.interceptor.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAI3G;;;;;GAKG;AACH,qBACa,oBAAqB,YAAW,eAAe;IAC1D,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;CAS3F"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
// Original work by the same author.
|
|
8
|
+
import { Injectable } from "@nestjs/common";
|
|
9
|
+
import { REQUEST_ID_HEADER } from "./request-id.js";
|
|
10
|
+
/**
|
|
11
|
+
* Echoes the request id as a response header on every handled route.
|
|
12
|
+
* Set before the handler runs, so it survives handler errors; errors
|
|
13
|
+
* thrown earlier (guards, parsers, unknown routes) get the same
|
|
14
|
+
* header from the error filter instead.
|
|
15
|
+
*/
|
|
16
|
+
let RequestIdInterceptor = class RequestIdInterceptor {
|
|
17
|
+
intercept(context, next) {
|
|
18
|
+
const ctx = context.switchToHttp();
|
|
19
|
+
const req = ctx.getRequest();
|
|
20
|
+
const reply = ctx.getResponse();
|
|
21
|
+
if (typeof req.id === "string" && req.id.length > 0) {
|
|
22
|
+
reply.header(REQUEST_ID_HEADER, req.id);
|
|
23
|
+
}
|
|
24
|
+
return next.handle();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
RequestIdInterceptor = __decorate([
|
|
28
|
+
Injectable()
|
|
29
|
+
], RequestIdInterceptor);
|
|
30
|
+
export { RequestIdInterceptor };
|
|
31
|
+
//# sourceMappingURL=request-id.interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.interceptor.js","sourceRoot":"","sources":["../src/request-id.interceptor.ts"],"names":[],"mappings":";;;;;;AAAA,oCAAoC;AACpC,OAAO,EAAE,UAAU,EAAiE,MAAM,gBAAgB,CAAC;AAE3G,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;GAKG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAC/B,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,EAAkB,CAAC;QAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAgB,CAAC;QAC9C,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;CACF,CAAA;AAVY,oBAAoB;IADhC,UAAU,EAAE;GACA,oBAAoB,CAUhC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.js","sourceRoot":"","sources":["../src/request-id.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Type } from "@nestjs/common";
|
|
2
|
+
/**
|
|
3
|
+
* `ConfigModule` validate hook (documented Nest recipe): instantiate the
|
|
4
|
+
* schema class so field defaults and `@Type()` coercions apply, then fail
|
|
5
|
+
* fast with one readable error when a declared variable is invalid.
|
|
6
|
+
* Undeclared variables pass through untouched, so unrelated env (PATH,
|
|
7
|
+
* CI flags, other tools) never breaks boot. Returns the instance, so
|
|
8
|
+
* `ConfigService` serves defaults and coerced numbers.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateEnv(schema: Type<object>, config: Record<string, unknown>): Record<string, unknown>;
|
|
11
|
+
//# sourceMappingURL=validate-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-env.d.ts","sourceRoot":"","sources":["../src/validate-env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAkB3C;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { plainToInstance } from "class-transformer";
|
|
2
|
+
import { validateSync } from "class-validator";
|
|
3
|
+
function flattenErrors(errors, prefix = "") {
|
|
4
|
+
return errors.flatMap((error) => {
|
|
5
|
+
const path = prefix ? `${prefix}.${error.property}` : error.property;
|
|
6
|
+
const reasons = Object.values(error.constraints ?? {}).join(", ");
|
|
7
|
+
const self = reasons ? [`${path}: ${reasons}`] : [];
|
|
8
|
+
// Unreachable via flat process env today (nested objects never arrive
|
|
9
|
+
// from env files), but the walk keeps the message readable if a nested
|
|
10
|
+
// schema ever validates nested input.
|
|
11
|
+
const children = error.children?.length ? flattenErrors(error.children, path) : [];
|
|
12
|
+
return [...self, ...children];
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `ConfigModule` validate hook (documented Nest recipe): instantiate the
|
|
17
|
+
* schema class so field defaults and `@Type()` coercions apply, then fail
|
|
18
|
+
* fast with one readable error when a declared variable is invalid.
|
|
19
|
+
* Undeclared variables pass through untouched, so unrelated env (PATH,
|
|
20
|
+
* CI flags, other tools) never breaks boot. Returns the instance, so
|
|
21
|
+
* `ConfigService` serves defaults and coerced numbers.
|
|
22
|
+
*/
|
|
23
|
+
export function validateEnv(schema, config) {
|
|
24
|
+
const instance = plainToInstance(schema, config);
|
|
25
|
+
const errors = validateSync(instance);
|
|
26
|
+
if (errors.length > 0) {
|
|
27
|
+
throw new Error(`Invalid environment: ${flattenErrors(errors).join("; ")}`);
|
|
28
|
+
}
|
|
29
|
+
return instance;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=validate-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-env.js","sourceRoot":"","sources":["../src/validate-env.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAC;AAErE,SAAS,aAAa,CAAC,MAAyB,EAAE,MAAM,GAAG,EAAE;IAC3D,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;QACrE,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,sEAAsE;QACtE,uEAAuE;QACvE,sCAAsC;QACtC,MAAM,QAAQ,GACZ,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,MAAoB,EACpB,MAA+B;IAE/B,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,wBAAwB,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,QAA8C,CAAC;AACxD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zedtech/zedkit-nest",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"registry": "https://registry.npmjs.org/"
|
|
7
|
+
},
|
|
8
|
+
"description": "Generic NestJS bootstrap factory: platform, config, logging, validation, errors, throttling, docs, shutdown. Original work by the same author.",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/zedtech-fr/zedkit",
|
|
13
|
+
"directory": "packages/nest"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@fastify/static": "^10.1.7",
|
|
30
|
+
"@nestjs/common": "^12.2.0",
|
|
31
|
+
"@nestjs/config": "^12.0.1",
|
|
32
|
+
"@nestjs/core": "^12.2.0",
|
|
33
|
+
"@nestjs/platform-fastify": "^12.2.0",
|
|
34
|
+
"@nestjs/swagger": "^12.0.4",
|
|
35
|
+
"@nestjs/throttler": "^6.7.1",
|
|
36
|
+
"class-transformer": "^0.5.1",
|
|
37
|
+
"class-validator": "^0.15.1",
|
|
38
|
+
"fastify": "^5.12.7",
|
|
39
|
+
"nestjs-pino": "^5.3.1",
|
|
40
|
+
"pino": "^10.4.0",
|
|
41
|
+
"pino-http": "^11.0.0",
|
|
42
|
+
"reflect-metadata": "^0.2.2"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@fastify/static": "10.1.7",
|
|
46
|
+
"@nestjs/common": "12.2.0",
|
|
47
|
+
"@nestjs/config": "12.0.1",
|
|
48
|
+
"@nestjs/core": "12.2.0",
|
|
49
|
+
"@nestjs/platform-fastify": "12.2.0",
|
|
50
|
+
"@nestjs/swagger": "12.0.4",
|
|
51
|
+
"@nestjs/throttler": "6.7.1",
|
|
52
|
+
"@swc/core": "1.16.20",
|
|
53
|
+
"@types/node": "22.20.5",
|
|
54
|
+
"@types/supertest": "7.2.1",
|
|
55
|
+
"class-transformer": "0.5.1",
|
|
56
|
+
"class-validator": "0.15.1",
|
|
57
|
+
"fastify": "5.12.7",
|
|
58
|
+
"nestjs-pino": "5.3.1",
|
|
59
|
+
"pino": "10.4.0",
|
|
60
|
+
"pino-http": "11.0.0",
|
|
61
|
+
"reflect-metadata": "0.2.2",
|
|
62
|
+
"supertest": "7.3.1",
|
|
63
|
+
"typescript": "5.9.3",
|
|
64
|
+
"unplugin-swc": "2.0.0",
|
|
65
|
+
"vitest": "2.1.9"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsc -p tsconfig.build.json",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
71
|
+
"lint": "eslint src tests"
|
|
72
|
+
}
|
|
6
73
|
}
|