@tsgonest/runtime 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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Configuration for tsgonest.
3
+ */
4
+ export interface TsgonestConfig {
5
+ /** Controller file discovery patterns. */
6
+ controllers?: {
7
+ /** Glob patterns for controller files to include. */
8
+ include?: string[];
9
+ /** Glob patterns for files to exclude. */
10
+ exclude?: string[];
11
+ };
12
+ /** Code transformation settings. */
13
+ transforms?: {
14
+ /** Generate validation companion files. */
15
+ validation?: boolean;
16
+ /** Generate serialization companion files. */
17
+ serialization?: boolean;
18
+ };
19
+ /** OpenAPI document generation settings. */
20
+ openapi?: {
21
+ /** Output path for the generated OpenAPI document. */
22
+ output?: string;
23
+ /** API title for the OpenAPI info section. */
24
+ title?: string;
25
+ /** API version for the OpenAPI info section. */
26
+ version?: string;
27
+ /** API description for the OpenAPI info section. */
28
+ description?: string;
29
+ };
30
+ }
31
+ /**
32
+ * Type-safe config helper for tsgonest.config.ts.
33
+ * Provides autocomplete and validation for the config object.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import { defineConfig } from "@tsgonest/runtime";
38
+ *
39
+ * export default defineConfig({
40
+ * controllers: {
41
+ * include: ["src/**\/*.controller.ts"],
42
+ * },
43
+ * transforms: {
44
+ * validation: true,
45
+ * serialization: true,
46
+ * },
47
+ * openapi: {
48
+ * output: "dist/openapi.json",
49
+ * },
50
+ * });
51
+ * ```
52
+ */
53
+ export declare function defineConfig(config: TsgonestConfig): TsgonestConfig;
54
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,WAAW,CAAC,EAAE;QACZ,qDAAqD;QACrD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,0CAA0C;QAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IAEF,oCAAoC;IACpC,UAAU,CAAC,EAAE;QACX,2CAA2C;QAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,8CAA8C;QAC9C,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;IAEF,4CAA4C;IAC5C,OAAO,CAAC,EAAE;QACR,sDAAsD;QACtD,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,8CAA8C;QAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,gDAAgD;QAChD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,oDAAoD;QACpD,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAEnE"}
package/dist/config.js ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineConfig = defineConfig;
4
+ /**
5
+ * Type-safe config helper for tsgonest.config.ts.
6
+ * Provides autocomplete and validation for the config object.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { defineConfig } from "@tsgonest/runtime";
11
+ *
12
+ * export default defineConfig({
13
+ * controllers: {
14
+ * include: ["src/**\/*.controller.ts"],
15
+ * },
16
+ * transforms: {
17
+ * validation: true,
18
+ * serialization: true,
19
+ * },
20
+ * openapi: {
21
+ * output: "dist/openapi.json",
22
+ * },
23
+ * });
24
+ * ```
25
+ */
26
+ function defineConfig(config) {
27
+ return config;
28
+ }
29
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AAuDA,oCAEC;AAxBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,YAAY,CAAC,MAAsB;IACjD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Route mapping entry — maps a controller method to its return type.
3
+ * Key format in the manifest: "ControllerName.methodName"
4
+ */
5
+ export interface RouteMapping {
6
+ /** The DTO type name (e.g., "UserResponse"). */
7
+ returnType: string;
8
+ /** Whether the method returns an array of the return type. */
9
+ isArray?: boolean;
10
+ }
11
+ /**
12
+ * The manifest file generated by tsgonest during compilation.
13
+ * Maps DTO type names to their companion file paths and function names.
14
+ */
15
+ export interface TsgonestManifest {
16
+ validators: Record<string, ManifestEntry>;
17
+ serializers: Record<string, ManifestEntry>;
18
+ routes?: Record<string, RouteMapping>;
19
+ }
20
+ /**
21
+ * A single entry in the manifest, pointing to a companion file and exported function.
22
+ */
23
+ export interface ManifestEntry {
24
+ /** Relative path to the companion JS file. */
25
+ file: string;
26
+ /** Name of the exported function in the companion file. */
27
+ fn: string;
28
+ }
29
+ /**
30
+ * Discovers and loads companion file functions from the tsgonest manifest.
31
+ */
32
+ export declare class CompanionDiscovery {
33
+ private manifest;
34
+ private manifestDir;
35
+ private cache;
36
+ /**
37
+ * Load the manifest from a directory.
38
+ * Searches for __tsgonest_manifest.json in the given directory.
39
+ */
40
+ loadManifest(distDir: string): boolean;
41
+ /**
42
+ * Load the manifest from a JSON string.
43
+ * Used for testing or when the manifest is embedded.
44
+ */
45
+ loadManifestFromJSON(json: string, baseDir: string): boolean;
46
+ /**
47
+ * Get a validator function for a DTO type name.
48
+ * Returns the assert function that throws on invalid input.
49
+ */
50
+ getValidator(typeName: string): ((input: unknown) => unknown) | null;
51
+ /**
52
+ * Get a serializer function for a DTO type name.
53
+ * Returns a function that converts the input to a JSON string.
54
+ */
55
+ getSerializer(typeName: string): ((input: unknown) => string) | null;
56
+ /**
57
+ * Check if a validator exists for a type.
58
+ */
59
+ hasValidator(typeName: string): boolean;
60
+ /**
61
+ * Check if a serializer exists for a type.
62
+ */
63
+ hasSerializer(typeName: string): boolean;
64
+ /**
65
+ * Get all registered validator type names.
66
+ */
67
+ getValidatorTypes(): string[];
68
+ /**
69
+ * Get all registered serializer type names.
70
+ */
71
+ getSerializerTypes(): string[];
72
+ /**
73
+ * Look up the return type for a controller method from the route map.
74
+ * @param controllerName The controller class name (e.g., "UserController").
75
+ * @param methodName The method name (e.g., "findAll").
76
+ * @returns The route mapping or null if not found.
77
+ */
78
+ getRouteMapping(controllerName: string, methodName: string): RouteMapping | null;
79
+ /**
80
+ * Get a serializer function for a controller method, using the route map.
81
+ * This is the primary lookup path for the fast interceptor — no Reflect metadata needed.
82
+ * @returns An object with the serializer function and whether it's an array, or null.
83
+ */
84
+ getSerializerForRoute(controllerName: string, methodName: string): {
85
+ serializer: (input: unknown) => string;
86
+ isArray: boolean;
87
+ } | null;
88
+ }
89
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAC;CACZ;AAUD;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,KAAK,CAGX;IAEF;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAgBtC;;;OAGG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAU5D;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI;IAyBpE;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,GAAG,IAAI;IAyBpE;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIvC;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIxC;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAI7B;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;OAKG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAMhF;;;;OAIG;IACH,qBAAqB,CACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GACjB;QAAE,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CASvE"}
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompanionDiscovery = void 0;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ /**
7
+ * Discovers and loads companion file functions from the tsgonest manifest.
8
+ */
9
+ class CompanionDiscovery {
10
+ manifest = null;
11
+ manifestDir = '';
12
+ cache = {
13
+ validators: new Map(),
14
+ serializers: new Map(),
15
+ };
16
+ /**
17
+ * Load the manifest from a directory.
18
+ * Searches for __tsgonest_manifest.json in the given directory.
19
+ */
20
+ loadManifest(distDir) {
21
+ const manifestPath = (0, path_1.join)(distDir, '__tsgonest_manifest.json');
22
+ if (!(0, fs_1.existsSync)(manifestPath)) {
23
+ return false;
24
+ }
25
+ try {
26
+ const content = (0, fs_1.readFileSync)(manifestPath, 'utf-8');
27
+ this.manifest = JSON.parse(content);
28
+ this.manifestDir = (0, path_1.dirname)(manifestPath);
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Load the manifest from a JSON string.
37
+ * Used for testing or when the manifest is embedded.
38
+ */
39
+ loadManifestFromJSON(json, baseDir) {
40
+ try {
41
+ this.manifest = JSON.parse(json);
42
+ this.manifestDir = baseDir;
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Get a validator function for a DTO type name.
51
+ * Returns the assert function that throws on invalid input.
52
+ */
53
+ getValidator(typeName) {
54
+ // Check cache
55
+ const cached = this.cache.validators.get(typeName);
56
+ if (cached)
57
+ return cached;
58
+ if (!this.manifest)
59
+ return null;
60
+ const entry = this.manifest.validators[typeName];
61
+ if (!entry)
62
+ return null;
63
+ try {
64
+ const filePath = (0, path_1.join)(this.manifestDir, entry.file);
65
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
66
+ const mod = require(filePath);
67
+ const fn = mod[entry.fn];
68
+ if (typeof fn === 'function') {
69
+ this.cache.validators.set(typeName, fn);
70
+ return fn;
71
+ }
72
+ }
73
+ catch {
74
+ // Failed to load companion file
75
+ }
76
+ return null;
77
+ }
78
+ /**
79
+ * Get a serializer function for a DTO type name.
80
+ * Returns a function that converts the input to a JSON string.
81
+ */
82
+ getSerializer(typeName) {
83
+ // Check cache
84
+ const cached = this.cache.serializers.get(typeName);
85
+ if (cached)
86
+ return cached;
87
+ if (!this.manifest)
88
+ return null;
89
+ const entry = this.manifest.serializers[typeName];
90
+ if (!entry)
91
+ return null;
92
+ try {
93
+ const filePath = (0, path_1.join)(this.manifestDir, entry.file);
94
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
95
+ const mod = require(filePath);
96
+ const fn = mod[entry.fn];
97
+ if (typeof fn === 'function') {
98
+ this.cache.serializers.set(typeName, fn);
99
+ return fn;
100
+ }
101
+ }
102
+ catch {
103
+ // Failed to load companion file
104
+ }
105
+ return null;
106
+ }
107
+ /**
108
+ * Check if a validator exists for a type.
109
+ */
110
+ hasValidator(typeName) {
111
+ return this.manifest?.validators[typeName] != null;
112
+ }
113
+ /**
114
+ * Check if a serializer exists for a type.
115
+ */
116
+ hasSerializer(typeName) {
117
+ return this.manifest?.serializers[typeName] != null;
118
+ }
119
+ /**
120
+ * Get all registered validator type names.
121
+ */
122
+ getValidatorTypes() {
123
+ return this.manifest ? Object.keys(this.manifest.validators) : [];
124
+ }
125
+ /**
126
+ * Get all registered serializer type names.
127
+ */
128
+ getSerializerTypes() {
129
+ return this.manifest ? Object.keys(this.manifest.serializers) : [];
130
+ }
131
+ /**
132
+ * Look up the return type for a controller method from the route map.
133
+ * @param controllerName The controller class name (e.g., "UserController").
134
+ * @param methodName The method name (e.g., "findAll").
135
+ * @returns The route mapping or null if not found.
136
+ */
137
+ getRouteMapping(controllerName, methodName) {
138
+ if (!this.manifest?.routes)
139
+ return null;
140
+ const key = `${controllerName}.${methodName}`;
141
+ return this.manifest.routes[key] ?? null;
142
+ }
143
+ /**
144
+ * Get a serializer function for a controller method, using the route map.
145
+ * This is the primary lookup path for the fast interceptor — no Reflect metadata needed.
146
+ * @returns An object with the serializer function and whether it's an array, or null.
147
+ */
148
+ getSerializerForRoute(controllerName, methodName) {
149
+ const mapping = this.getRouteMapping(controllerName, methodName);
150
+ if (!mapping)
151
+ return null;
152
+ const serializer = this.getSerializer(mapping.returnType);
153
+ if (!serializer)
154
+ return null;
155
+ return { serializer, isArray: mapping.isArray ?? false };
156
+ }
157
+ }
158
+ exports.CompanionDiscovery = CompanionDiscovery;
159
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Validation error details for a single field.
3
+ */
4
+ export interface ValidationErrorDetail {
5
+ /** The path to the invalid field (e.g., "input.name"). */
6
+ path: string;
7
+ /** The expected type or constraint. */
8
+ expected: string;
9
+ /** The received type or value description. */
10
+ received: string;
11
+ }
12
+ /**
13
+ * Error thrown when validation fails.
14
+ * Contains structured error details for each invalid field.
15
+ */
16
+ export declare class TsgonestValidationError extends Error {
17
+ readonly errors: ValidationErrorDetail[];
18
+ constructor(errors: ValidationErrorDetail[]);
19
+ }
20
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAgB,MAAM,EAAE,qBAAqB,EAAE,CAAC;gBAEpC,MAAM,EAAE,qBAAqB,EAAE;CAO5C"}
package/dist/errors.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TsgonestValidationError = void 0;
4
+ /**
5
+ * Error thrown when validation fails.
6
+ * Contains structured error details for each invalid field.
7
+ */
8
+ class TsgonestValidationError extends Error {
9
+ errors;
10
+ constructor(errors) {
11
+ const message = `Validation failed: ${errors.length} error(s)\n` +
12
+ errors.map(e => ` - ${e.path}: expected ${e.expected}, received ${e.received}`).join('\n');
13
+ super(message);
14
+ this.name = 'TsgonestValidationError';
15
+ this.errors = errors;
16
+ }
17
+ }
18
+ exports.TsgonestValidationError = TsgonestValidationError;
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAYA;;;GAGG;AACH,MAAa,uBAAwB,SAAQ,KAAK;IAChC,MAAM,CAA0B;IAEhD,YAAY,MAA+B;QACzC,MAAM,OAAO,GAAG,sBAAsB,MAAM,CAAC,MAAM,aAAa;YAC9D,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAVD,0DAUC"}
@@ -0,0 +1,82 @@
1
+ import type { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import type { Observable } from 'rxjs';
3
+ import { CompanionDiscovery } from './discovery';
4
+ /**
5
+ * Options for configuring the TsgonestFastInterceptor.
6
+ */
7
+ export interface FastInterceptorOptions {
8
+ /**
9
+ * Path to the dist directory containing the tsgonest manifest.
10
+ * Defaults to the current working directory's dist/ folder.
11
+ */
12
+ distDir?: string;
13
+ /**
14
+ * Pre-loaded CompanionDiscovery instance.
15
+ * If provided, distDir is ignored.
16
+ */
17
+ discovery?: CompanionDiscovery;
18
+ /**
19
+ * Type name overrides for specific routes.
20
+ * Map of "ControllerName.methodName" to type name.
21
+ * Takes priority over the route map in the manifest.
22
+ */
23
+ typeOverrides?: Record<string, string>;
24
+ /**
25
+ * Whether to set the raw response directly (bypassing NestJS JSON serialization).
26
+ * When true, the interceptor writes the pre-serialized JSON string directly to
27
+ * the response with Content-Type: application/json, skipping JSON.stringify entirely.
28
+ *
29
+ * When false (default), the interceptor returns the pre-serialized string and
30
+ * relies on NestJS/Express to write it (which may still call JSON.stringify on
31
+ * the string — wrapping it in quotes). Use `true` for maximum performance.
32
+ *
33
+ * Defaults to true.
34
+ */
35
+ rawResponse?: boolean;
36
+ }
37
+ /**
38
+ * A high-performance NestJS interceptor that replaces JSON.stringify with
39
+ * tsgonest-generated schema-aware serializers on responses.
40
+ *
41
+ * Unlike `TsgonestSerializationInterceptor`, this interceptor uses the
42
+ * **route map** in the manifest to look up which serializer to use for each
43
+ * controller method — no Reflect.getMetadata or emitDecoratorMetadata needed.
44
+ *
45
+ * The route map is populated at build time by tsgonest's static analysis of
46
+ * NestJS controllers, making this zero-config and guaranteed to work.
47
+ *
48
+ * Performance: 2-5x faster JSON serialization compared to JSON.stringify,
49
+ * because the generated serializers use string concatenation with known
50
+ * property names and types, avoiding the overhead of generic object traversal.
51
+ *
52
+ * Usage:
53
+ * ```ts
54
+ * import { TsgonestFastInterceptor } from '@tsgonest/runtime';
55
+ *
56
+ * // In main.ts (after app creation):
57
+ * app.useGlobalInterceptors(new TsgonestFastInterceptor());
58
+ *
59
+ * // With options:
60
+ * app.useGlobalInterceptors(new TsgonestFastInterceptor({
61
+ * distDir: 'dist',
62
+ * rawResponse: true, // bypass JSON.stringify entirely
63
+ * }));
64
+ * ```
65
+ */
66
+ export declare class TsgonestFastInterceptor implements NestInterceptor {
67
+ private discovery;
68
+ private typeOverrides;
69
+ private rawResponse;
70
+ private initialized;
71
+ private distDir;
72
+ constructor(options?: FastInterceptorOptions);
73
+ /**
74
+ * Ensure the manifest is loaded (lazy init on first request).
75
+ */
76
+ private ensureInitialized;
77
+ /**
78
+ * Intercept the response and apply fast serialization if a route mapping exists.
79
+ */
80
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
81
+ }
82
+ //# sourceMappingURL=fast-interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fast-interceptor.d.ts","sourceRoot":"","sources":["../src/fast-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEvC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAqB;gBAExB,OAAO,GAAE,sBAA2B;IAahD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAyF7E"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TsgonestFastInterceptor = void 0;
4
+ const discovery_1 = require("./discovery");
5
+ /**
6
+ * A high-performance NestJS interceptor that replaces JSON.stringify with
7
+ * tsgonest-generated schema-aware serializers on responses.
8
+ *
9
+ * Unlike `TsgonestSerializationInterceptor`, this interceptor uses the
10
+ * **route map** in the manifest to look up which serializer to use for each
11
+ * controller method — no Reflect.getMetadata or emitDecoratorMetadata needed.
12
+ *
13
+ * The route map is populated at build time by tsgonest's static analysis of
14
+ * NestJS controllers, making this zero-config and guaranteed to work.
15
+ *
16
+ * Performance: 2-5x faster JSON serialization compared to JSON.stringify,
17
+ * because the generated serializers use string concatenation with known
18
+ * property names and types, avoiding the overhead of generic object traversal.
19
+ *
20
+ * Usage:
21
+ * ```ts
22
+ * import { TsgonestFastInterceptor } from '@tsgonest/runtime';
23
+ *
24
+ * // In main.ts (after app creation):
25
+ * app.useGlobalInterceptors(new TsgonestFastInterceptor());
26
+ *
27
+ * // With options:
28
+ * app.useGlobalInterceptors(new TsgonestFastInterceptor({
29
+ * distDir: 'dist',
30
+ * rawResponse: true, // bypass JSON.stringify entirely
31
+ * }));
32
+ * ```
33
+ */
34
+ class TsgonestFastInterceptor {
35
+ discovery;
36
+ typeOverrides;
37
+ rawResponse;
38
+ initialized = false;
39
+ distDir;
40
+ constructor(options = {}) {
41
+ this.typeOverrides = options.typeOverrides ?? {};
42
+ this.rawResponse = options.rawResponse ?? true;
43
+ if (options.discovery) {
44
+ this.discovery = options.discovery;
45
+ this.initialized = true;
46
+ }
47
+ else {
48
+ this.discovery = new discovery_1.CompanionDiscovery();
49
+ this.distDir = options.distDir;
50
+ }
51
+ }
52
+ /**
53
+ * Ensure the manifest is loaded (lazy init on first request).
54
+ */
55
+ ensureInitialized() {
56
+ if (this.initialized)
57
+ return;
58
+ this.initialized = true;
59
+ const distDir = this.distDir || 'dist';
60
+ this.discovery.loadManifest(distDir);
61
+ }
62
+ /**
63
+ * Intercept the response and apply fast serialization if a route mapping exists.
64
+ */
65
+ intercept(context, next) {
66
+ this.ensureInitialized();
67
+ const handler = context.getHandler();
68
+ const controller = context.getClass();
69
+ const controllerName = controller.name;
70
+ const methodName = handler.name;
71
+ // 1. Check explicit type overrides first
72
+ const overrideKey = `${controllerName}.${methodName}`;
73
+ const overrideType = this.typeOverrides[overrideKey];
74
+ // 2. Look up via route map (the primary path — zero-config)
75
+ let serializerFn = null;
76
+ let isArray = false;
77
+ if (overrideType) {
78
+ serializerFn = this.discovery.getSerializer(overrideType);
79
+ }
80
+ else {
81
+ const routeInfo = this.discovery.getSerializerForRoute(controllerName, methodName);
82
+ if (routeInfo) {
83
+ serializerFn = routeInfo.serializer;
84
+ isArray = routeInfo.isArray;
85
+ }
86
+ }
87
+ if (!serializerFn) {
88
+ // No serializer available — pass through to default JSON.stringify
89
+ return next.handle();
90
+ }
91
+ // Capture for closure
92
+ const serializer = serializerFn;
93
+ const useRawResponse = this.rawResponse;
94
+ const isArrayResponse = isArray;
95
+ // Lazy-import rxjs to avoid requiring it at module load time
96
+ try {
97
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
98
+ const { map } = require('rxjs');
99
+ return next.handle().pipe(map((data) => {
100
+ if (data === null || data === undefined) {
101
+ return data;
102
+ }
103
+ let jsonString;
104
+ if (isArrayResponse || Array.isArray(data)) {
105
+ // Serialize array: serialize each element, join with commas
106
+ const items = Array.isArray(data) ? data : [data];
107
+ const parts = items.map(item => serializer(item));
108
+ jsonString = '[' + parts.join(',') + ']';
109
+ }
110
+ else {
111
+ jsonString = serializer(data);
112
+ }
113
+ if (useRawResponse) {
114
+ // Write directly to the response, bypassing NestJS JSON serialization
115
+ const httpCtx = context.switchToHttp();
116
+ const response = httpCtx.getResponse();
117
+ // Works with both Express and Fastify
118
+ if (typeof response.type === 'function') {
119
+ // Fastify
120
+ response.type('application/json');
121
+ response.send(jsonString);
122
+ }
123
+ else if (typeof response.setHeader === 'function') {
124
+ // Express
125
+ response.setHeader('Content-Type', 'application/json');
126
+ response.end(jsonString);
127
+ }
128
+ // Return undefined to signal NestJS not to send anything else.
129
+ // The response is already sent.
130
+ return undefined;
131
+ }
132
+ // Non-raw mode: return the pre-serialized JSON.
133
+ // Note: NestJS will call JSON.stringify on this string,
134
+ // wrapping it in quotes. This mode is less optimal but safer.
135
+ return jsonString;
136
+ }));
137
+ }
138
+ catch {
139
+ // rxjs not available — pass through
140
+ return next.handle();
141
+ }
142
+ }
143
+ }
144
+ exports.TsgonestFastInterceptor = TsgonestFastInterceptor;
145
+ //# sourceMappingURL=fast-interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fast-interceptor.js","sourceRoot":"","sources":["../src/fast-interceptor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export { TsgonestValidationPipe } from './validation-pipe';
2
+ export type { ValidationPipeOptions } from './validation-pipe';
3
+ export { TsgonestSerializationInterceptor } from './serialization';
4
+ export type { SerializationInterceptorOptions } from './serialization';
5
+ export { TsgonestFastInterceptor } from './fast-interceptor';
6
+ export type { FastInterceptorOptions } from './fast-interceptor';
7
+ export { TsgonestValidationError } from './errors';
8
+ export type { ValidationErrorDetail } from './errors';
9
+ export { CompanionDiscovery } from './discovery';
10
+ export type { TsgonestManifest, ManifestEntry, RouteMapping } from './discovery';
11
+ export { defineConfig } from './config';
12
+ export type { TsgonestConfig } from './config';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AACnE,YAAY,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGjF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineConfig = exports.CompanionDiscovery = exports.TsgonestValidationError = exports.TsgonestFastInterceptor = exports.TsgonestSerializationInterceptor = exports.TsgonestValidationPipe = void 0;
4
+ // Validation
5
+ var validation_pipe_1 = require("./validation-pipe");
6
+ Object.defineProperty(exports, "TsgonestValidationPipe", { enumerable: true, get: function () { return validation_pipe_1.TsgonestValidationPipe; } });
7
+ // Serialization (legacy — uses Reflect.getMetadata)
8
+ var serialization_1 = require("./serialization");
9
+ Object.defineProperty(exports, "TsgonestSerializationInterceptor", { enumerable: true, get: function () { return serialization_1.TsgonestSerializationInterceptor; } });
10
+ // Fast Serialization (recommended — uses route map, no Reflect needed)
11
+ var fast_interceptor_1 = require("./fast-interceptor");
12
+ Object.defineProperty(exports, "TsgonestFastInterceptor", { enumerable: true, get: function () { return fast_interceptor_1.TsgonestFastInterceptor; } });
13
+ // Errors
14
+ var errors_1 = require("./errors");
15
+ Object.defineProperty(exports, "TsgonestValidationError", { enumerable: true, get: function () { return errors_1.TsgonestValidationError; } });
16
+ // Discovery
17
+ var discovery_1 = require("./discovery");
18
+ Object.defineProperty(exports, "CompanionDiscovery", { enumerable: true, get: function () { return discovery_1.CompanionDiscovery; } });
19
+ // Config
20
+ var config_1 = require("./config");
21
+ Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return config_1.defineConfig; } });
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,aAAa;AACb,qDAA2D;AAAlD,yHAAA,sBAAsB,OAAA;AAG/B,oDAAoD;AACpD,iDAAmE;AAA1D,iIAAA,gCAAgC,OAAA;AAGzC,uEAAuE;AACvE,uDAA6D;AAApD,2HAAA,uBAAuB,OAAA;AAGhC,SAAS;AACT,mCAAmD;AAA1C,iHAAA,uBAAuB,OAAA;AAGhC,YAAY;AACZ,yCAAiD;AAAxC,+GAAA,kBAAkB,OAAA;AAG3B,SAAS;AACT,mCAAwC;AAA/B,sGAAA,YAAY,OAAA"}
@@ -0,0 +1,65 @@
1
+ import type { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import type { Observable } from 'rxjs';
3
+ import { CompanionDiscovery } from './discovery';
4
+ /**
5
+ * Options for configuring the TsgonestSerializationInterceptor.
6
+ */
7
+ export interface SerializationInterceptorOptions {
8
+ /**
9
+ * Path to the dist directory containing the tsgonest manifest.
10
+ * Defaults to the current working directory's dist/ folder.
11
+ */
12
+ distDir?: string;
13
+ /**
14
+ * Pre-loaded CompanionDiscovery instance.
15
+ * If provided, distDir is ignored.
16
+ */
17
+ discovery?: CompanionDiscovery;
18
+ /**
19
+ * Type name overrides for specific routes.
20
+ * Map of "ControllerName.methodName" → type name.
21
+ */
22
+ typeOverrides?: Record<string, string>;
23
+ }
24
+ /**
25
+ * A NestJS interceptor that uses tsgonest-generated companion serialization
26
+ * functions for fast JSON output.
27
+ *
28
+ * When a serializer companion exists for a controller method's return type,
29
+ * this interceptor replaces the default JSON.stringify with the generated
30
+ * fast serializer (string concatenation approach).
31
+ *
32
+ * Usage:
33
+ * ```ts
34
+ * import { TsgonestSerializationInterceptor } from '@tsgonest/runtime';
35
+ *
36
+ * // In main.ts:
37
+ * app.useGlobalInterceptors(new TsgonestSerializationInterceptor({ distDir: 'dist' }));
38
+ * ```
39
+ */
40
+ export declare class TsgonestSerializationInterceptor implements NestInterceptor {
41
+ private discovery;
42
+ private typeOverrides;
43
+ private initialized;
44
+ private distDir;
45
+ constructor(options?: SerializationInterceptorOptions);
46
+ /**
47
+ * Ensure the manifest is loaded.
48
+ */
49
+ private ensureInitialized;
50
+ /**
51
+ * Intercept the response and apply fast serialization if available.
52
+ */
53
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
54
+ /**
55
+ * Resolve the return type name for a controller method.
56
+ *
57
+ * Strategy:
58
+ * 1. Check typeOverrides for "ControllerName.methodName"
59
+ * 2. Check Reflect metadata (if available) for return type
60
+ * 3. Check the manifest for all registered serializer types
61
+ * and match by method name convention (e.g., "findAll" → look for array type)
62
+ */
63
+ private resolveTypeName;
64
+ }
65
+ //# sourceMappingURL=serialization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../src/serialization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gCAAiC,YAAW,eAAe;IACtE,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAqB;gBAExB,OAAO,GAAE,+BAAoC;IAYzD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IA4C5E;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;CAkCxB"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TsgonestSerializationInterceptor = void 0;
4
+ const discovery_1 = require("./discovery");
5
+ /**
6
+ * A NestJS interceptor that uses tsgonest-generated companion serialization
7
+ * functions for fast JSON output.
8
+ *
9
+ * When a serializer companion exists for a controller method's return type,
10
+ * this interceptor replaces the default JSON.stringify with the generated
11
+ * fast serializer (string concatenation approach).
12
+ *
13
+ * Usage:
14
+ * ```ts
15
+ * import { TsgonestSerializationInterceptor } from '@tsgonest/runtime';
16
+ *
17
+ * // In main.ts:
18
+ * app.useGlobalInterceptors(new TsgonestSerializationInterceptor({ distDir: 'dist' }));
19
+ * ```
20
+ */
21
+ class TsgonestSerializationInterceptor {
22
+ discovery;
23
+ typeOverrides;
24
+ initialized = false;
25
+ distDir;
26
+ constructor(options = {}) {
27
+ this.typeOverrides = options.typeOverrides ?? {};
28
+ if (options.discovery) {
29
+ this.discovery = options.discovery;
30
+ this.initialized = true;
31
+ }
32
+ else {
33
+ this.discovery = new discovery_1.CompanionDiscovery();
34
+ this.distDir = options.distDir;
35
+ }
36
+ }
37
+ /**
38
+ * Ensure the manifest is loaded.
39
+ */
40
+ ensureInitialized() {
41
+ if (this.initialized)
42
+ return;
43
+ this.initialized = true;
44
+ const distDir = this.distDir || 'dist';
45
+ this.discovery.loadManifest(distDir);
46
+ }
47
+ /**
48
+ * Intercept the response and apply fast serialization if available.
49
+ */
50
+ intercept(context, next) {
51
+ this.ensureInitialized();
52
+ const handler = context.getHandler();
53
+ const controller = context.getClass();
54
+ // Determine the return type name
55
+ const typeName = this.resolveTypeName(controller, handler);
56
+ if (!typeName) {
57
+ return next.handle();
58
+ }
59
+ // Look up the serializer for this type
60
+ const serializer = this.discovery.getSerializer(typeName);
61
+ if (!serializer) {
62
+ return next.handle();
63
+ }
64
+ // Lazy-import rxjs map to avoid requiring rxjs at import time
65
+ try {
66
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
67
+ const { map } = require('rxjs');
68
+ return next.handle().pipe(map((data) => {
69
+ if (data === null || data === undefined) {
70
+ return data;
71
+ }
72
+ // If the data is an array, serialize each element
73
+ if (Array.isArray(data)) {
74
+ const parts = data.map(item => serializer(item));
75
+ return '[' + parts.join(',') + ']';
76
+ }
77
+ return serializer(data);
78
+ }));
79
+ }
80
+ catch {
81
+ // rxjs not available — pass through
82
+ return next.handle();
83
+ }
84
+ }
85
+ /**
86
+ * Resolve the return type name for a controller method.
87
+ *
88
+ * Strategy:
89
+ * 1. Check typeOverrides for "ControllerName.methodName"
90
+ * 2. Check Reflect metadata (if available) for return type
91
+ * 3. Check the manifest for all registered serializer types
92
+ * and match by method name convention (e.g., "findAll" → look for array type)
93
+ */
94
+ resolveTypeName(controller, handler) {
95
+ const controllerName = controller.name;
96
+ const methodName = handler.name;
97
+ // 1. Check explicit overrides
98
+ const overrideKey = `${controllerName}.${methodName}`;
99
+ if (this.typeOverrides[overrideKey]) {
100
+ return this.typeOverrides[overrideKey];
101
+ }
102
+ // 2. Check Reflect metadata for return type (if emitDecoratorMetadata is enabled)
103
+ if (typeof Reflect !== 'undefined' && Reflect.getMetadata) {
104
+ const returnType = Reflect.getMetadata('design:returntype', controller.prototype, methodName);
105
+ if (returnType && returnType.name && returnType.name !== 'Promise' && returnType.name !== 'Object') {
106
+ const typeName = returnType.name;
107
+ if (this.discovery.hasSerializer(typeName)) {
108
+ return typeName;
109
+ }
110
+ }
111
+ }
112
+ // 3. Check custom metadata set by tsgonest (via __tsgonest_return_type__)
113
+ if (typeof Reflect !== 'undefined' && Reflect.getMetadata) {
114
+ const tsgonestReturnType = Reflect.getMetadata('__tsgonest_return_type__', controller.prototype, methodName);
115
+ if (tsgonestReturnType && this.discovery.hasSerializer(tsgonestReturnType)) {
116
+ return tsgonestReturnType;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ }
122
+ exports.TsgonestSerializationInterceptor = TsgonestSerializationInterceptor;
123
+ //# sourceMappingURL=serialization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialization.js","sourceRoot":"","sources":["../src/serialization.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { PipeTransform, ArgumentMetadata } from '@nestjs/common';
2
+ import { CompanionDiscovery } from './discovery';
3
+ /**
4
+ * Options for configuring the TsgonestValidationPipe.
5
+ */
6
+ export interface ValidationPipeOptions {
7
+ /**
8
+ * Path to the dist directory containing the tsgonest manifest.
9
+ * Defaults to the current working directory's dist/ folder.
10
+ */
11
+ distDir?: string;
12
+ /**
13
+ * Pre-loaded CompanionDiscovery instance.
14
+ * If provided, distDir is ignored.
15
+ */
16
+ discovery?: CompanionDiscovery;
17
+ /**
18
+ * Whether to throw an error if validation fails.
19
+ * Defaults to true. When false, returns null for invalid input.
20
+ */
21
+ throwOnError?: boolean;
22
+ /**
23
+ * HTTP status code to use when throwing validation errors.
24
+ * Defaults to 400 (Bad Request).
25
+ */
26
+ errorHttpStatusCode?: number;
27
+ }
28
+ /**
29
+ * A NestJS pipe that validates incoming request data using
30
+ * tsgonest-generated companion validation functions.
31
+ *
32
+ * Usage:
33
+ * ```ts
34
+ * import { TsgonestValidationPipe } from '@tsgonest/runtime';
35
+ *
36
+ * // In main.ts:
37
+ * app.useGlobalPipes(new TsgonestValidationPipe({ distDir: 'dist' }));
38
+ *
39
+ * // Or with a pre-loaded discovery:
40
+ * const discovery = new CompanionDiscovery();
41
+ * discovery.loadManifest('dist');
42
+ * app.useGlobalPipes(new TsgonestValidationPipe({ discovery }));
43
+ * ```
44
+ */
45
+ export declare class TsgonestValidationPipe implements PipeTransform {
46
+ private discovery;
47
+ private throwOnError;
48
+ private errorHttpStatusCode;
49
+ private initialized;
50
+ private distDir;
51
+ constructor(options?: ValidationPipeOptions);
52
+ /**
53
+ * Ensure the manifest is loaded.
54
+ */
55
+ private ensureInitialized;
56
+ /**
57
+ * Transform (validate) the incoming value.
58
+ * If a companion validator exists for the metatype, it is called.
59
+ * Otherwise, the value is returned unchanged.
60
+ */
61
+ transform(value: unknown, metadata: ArgumentMetadata): unknown;
62
+ /**
63
+ * Extract the type name from a NestJS metatype.
64
+ * Filters out built-in types (String, Number, Boolean, etc.).
65
+ */
66
+ private getTypeName;
67
+ /**
68
+ * Parse error messages from generated assert functions.
69
+ * The generated code throws errors like:
70
+ * "Validation failed: input.name expected string, received number"
71
+ */
72
+ private parseAssertError;
73
+ /**
74
+ * Create an HTTP exception for validation errors.
75
+ * Uses a dynamic import approach to avoid hard NestJS dependency at import time.
76
+ */
77
+ private createHttpException;
78
+ }
79
+ //# sourceMappingURL=validation-pipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-pipe.d.ts","sourceRoot":"","sources":["../src/validation-pipe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAQ,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,sBAAuB,YAAW,aAAa;IAC1D,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAqB;gBAExB,OAAO,GAAE,qBAA0B;IAa/C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO;IA8C9D;;;OAGG;IACH,OAAO,CAAC,WAAW;IAQnB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAuB5B"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TsgonestValidationPipe = void 0;
4
+ const discovery_1 = require("./discovery");
5
+ const errors_1 = require("./errors");
6
+ /**
7
+ * A NestJS pipe that validates incoming request data using
8
+ * tsgonest-generated companion validation functions.
9
+ *
10
+ * Usage:
11
+ * ```ts
12
+ * import { TsgonestValidationPipe } from '@tsgonest/runtime';
13
+ *
14
+ * // In main.ts:
15
+ * app.useGlobalPipes(new TsgonestValidationPipe({ distDir: 'dist' }));
16
+ *
17
+ * // Or with a pre-loaded discovery:
18
+ * const discovery = new CompanionDiscovery();
19
+ * discovery.loadManifest('dist');
20
+ * app.useGlobalPipes(new TsgonestValidationPipe({ discovery }));
21
+ * ```
22
+ */
23
+ class TsgonestValidationPipe {
24
+ discovery;
25
+ throwOnError;
26
+ errorHttpStatusCode;
27
+ initialized = false;
28
+ distDir;
29
+ constructor(options = {}) {
30
+ this.throwOnError = options.throwOnError ?? true;
31
+ this.errorHttpStatusCode = options.errorHttpStatusCode ?? 400;
32
+ if (options.discovery) {
33
+ this.discovery = options.discovery;
34
+ this.initialized = true;
35
+ }
36
+ else {
37
+ this.discovery = new discovery_1.CompanionDiscovery();
38
+ this.distDir = options.distDir;
39
+ }
40
+ }
41
+ /**
42
+ * Ensure the manifest is loaded.
43
+ */
44
+ ensureInitialized() {
45
+ if (this.initialized)
46
+ return;
47
+ this.initialized = true;
48
+ const distDir = this.distDir || 'dist';
49
+ this.discovery.loadManifest(distDir);
50
+ }
51
+ /**
52
+ * Transform (validate) the incoming value.
53
+ * If a companion validator exists for the metatype, it is called.
54
+ * Otherwise, the value is returned unchanged.
55
+ */
56
+ transform(value, metadata) {
57
+ this.ensureInitialized();
58
+ // Only validate body, query, and param types
59
+ if (!metadata.metatype) {
60
+ return value;
61
+ }
62
+ const typeName = this.getTypeName(metadata.metatype);
63
+ if (!typeName) {
64
+ return value;
65
+ }
66
+ // Look up the validator (assert function) for this type
67
+ const validator = this.discovery.getValidator(typeName);
68
+ if (!validator) {
69
+ // No companion found — pass through
70
+ return value;
71
+ }
72
+ try {
73
+ return validator(value);
74
+ }
75
+ catch (err) {
76
+ if (!this.throwOnError) {
77
+ return null;
78
+ }
79
+ // Re-throw TsgonestValidationError as a structured HTTP error
80
+ if (err instanceof errors_1.TsgonestValidationError) {
81
+ throw this.createHttpException(err.errors);
82
+ }
83
+ // For errors from the generated assert functions that throw plain Error
84
+ if (err instanceof Error && err.message) {
85
+ // The generated assert functions throw errors with structured messages
86
+ // Parse them if possible, otherwise wrap
87
+ const details = this.parseAssertError(err.message);
88
+ if (details.length > 0) {
89
+ throw this.createHttpException(details);
90
+ }
91
+ }
92
+ throw err;
93
+ }
94
+ }
95
+ /**
96
+ * Extract the type name from a NestJS metatype.
97
+ * Filters out built-in types (String, Number, Boolean, etc.).
98
+ */
99
+ getTypeName(metatype) {
100
+ const builtInTypes = [String, Number, Boolean, Array, Object];
101
+ if (builtInTypes.includes(metatype)) {
102
+ return null;
103
+ }
104
+ return metatype.name || null;
105
+ }
106
+ /**
107
+ * Parse error messages from generated assert functions.
108
+ * The generated code throws errors like:
109
+ * "Validation failed: input.name expected string, received number"
110
+ */
111
+ parseAssertError(message) {
112
+ const details = [];
113
+ const lines = message.split('\n');
114
+ for (const line of lines) {
115
+ // Match patterns like " - input.name: expected string, received number"
116
+ const match = line.match(/^\s*-?\s*(.+?):\s*expected\s+(.+?),\s*received\s+(.+)$/);
117
+ if (match) {
118
+ details.push({
119
+ path: match[1].trim(),
120
+ expected: match[2].trim(),
121
+ received: match[3].trim(),
122
+ });
123
+ }
124
+ }
125
+ return details;
126
+ }
127
+ /**
128
+ * Create an HTTP exception for validation errors.
129
+ * Uses a dynamic import approach to avoid hard NestJS dependency at import time.
130
+ */
131
+ createHttpException(errors) {
132
+ // Try to create a NestJS HttpException
133
+ try {
134
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
135
+ const { HttpException } = require('@nestjs/common');
136
+ return new HttpException({
137
+ statusCode: this.errorHttpStatusCode,
138
+ message: 'Validation failed',
139
+ errors: errors.map(e => ({
140
+ property: e.path,
141
+ constraints: { tsgonest: `expected ${e.expected}, received ${e.received}` },
142
+ })),
143
+ }, this.errorHttpStatusCode);
144
+ }
145
+ catch {
146
+ // Fallback if @nestjs/common not available
147
+ const err = new errors_1.TsgonestValidationError(errors);
148
+ err.status = this.errorHttpStatusCode;
149
+ return err;
150
+ }
151
+ }
152
+ }
153
+ exports.TsgonestValidationPipe = TsgonestValidationPipe;
154
+ //# sourceMappingURL=validation-pipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-pipe.js","sourceRoot":"","sources":["../src/validation-pipe.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@tsgonest/runtime",
3
+ "version": "0.0.1",
4
+ "description": "Runtime utilities for tsgonest — validation pipe, serialization interceptor, and companion file discovery",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "peerDependencies": {
18
+ "@nestjs/common": ">=9.0.0",
19
+ "@nestjs/core": ">=9.0.0",
20
+ "rxjs": ">=7.0.0"
21
+ },
22
+ "peerDependenciesMeta": {
23
+ "@nestjs/common": {
24
+ "optional": true
25
+ },
26
+ "@nestjs/core": {
27
+ "optional": true
28
+ },
29
+ "rxjs": {
30
+ "optional": true
31
+ }
32
+ },
33
+ "keywords": [
34
+ "nestjs",
35
+ "typescript",
36
+ "validation",
37
+ "serialization",
38
+ "openapi",
39
+ "tsgonest"
40
+ ],
41
+ "devDependencies": {
42
+ "@nestjs/common": "^10.0.0",
43
+ "@nestjs/core": "^10.0.0",
44
+ "@types/node": "^25.3.0",
45
+ "reflect-metadata": "^0.2.0",
46
+ "rxjs": "^7.8.0",
47
+ "typescript": "^5.7.0"
48
+ },
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/tsgonest/tsgonest"
53
+ },
54
+ "scripts": {
55
+ "build": "tsc",
56
+ "test": "vitest --run"
57
+ }
58
+ }