@orpc/nest 0.0.0 → 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/index.d.mts +28 -9
- package/dist/index.d.ts +28 -9
- package/dist/index.mjs +82 -149
- package/package.json +15 -10
package/dist/index.d.mts
CHANGED
@@ -1,17 +1,36 @@
|
|
1
|
-
import { AnyContractProcedure, HTTPPath, AnyContractRouter } from '@orpc/contract';
|
2
|
-
import { BuilderConfig, ImplementerInternal } from '@orpc/server';
|
3
|
-
export { ORPCError } from '@orpc/server';
|
4
1
|
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
2
|
+
import { ContractRouter, HTTPPath, AnyContractRouter, ContractProcedure } from '@orpc/contract';
|
3
|
+
import { Router } from '@orpc/server';
|
4
|
+
export { ImplementedProcedure, Implementer, ImplementerInternal, ImplementerInternalWithMiddlewares, ORPCError, ProcedureImplementer, RouterImplementer, RouterImplementerWithMiddlewares, implement } from '@orpc/server';
|
5
|
+
import { Promisable } from '@orpc/shared';
|
5
6
|
import { Observable } from 'rxjs';
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
/**
|
9
|
+
* Decorator in controller handler to implement a oRPC contract.
|
10
|
+
*
|
11
|
+
* @see {@link https://orpc.unnoq.com/docs/openapi/nest/implement-contract#implement-your-contract NestJS Implement Contract Docs}
|
12
|
+
*/
|
13
|
+
declare function Implement<T extends ContractRouter<any>>(contract: T): <U extends Promisable<Router<T, Record<never, never>>>>(target: Record<PropertyKey, any>, propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => U>) => void;
|
9
14
|
declare class ImplementInterceptor implements NestInterceptor {
|
10
15
|
intercept(ctx: ExecutionContext, next: CallHandler<any>): Observable<any>;
|
11
16
|
}
|
12
17
|
|
13
|
-
declare function
|
14
|
-
|
15
|
-
|
18
|
+
declare function toNestPattern(path: HTTPPath): string;
|
19
|
+
type PopulatedContractRouterPaths<T extends AnyContractRouter> = T extends ContractProcedure<infer UInputSchema, infer UOutputSchema, infer UErrors, infer UMeta> ? ContractProcedure<UInputSchema, UOutputSchema, UErrors, UMeta> : {
|
20
|
+
[K in keyof T]: T[K] extends AnyContractRouter ? PopulatedContractRouterPaths<T[K]> : never;
|
21
|
+
};
|
22
|
+
interface PopulateContractRouterPathsOptions {
|
23
|
+
path?: readonly string[];
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* populateContractRouterPaths is completely optional,
|
27
|
+
* because the procedure's path is required for NestJS implementation.
|
28
|
+
* This utility automatically populates any missing paths
|
29
|
+
* Using the router's keys + `/`.
|
30
|
+
*
|
31
|
+
* @see {@link https://orpc.unnoq.com/docs/openapi/nest/implement-contract#define-your-contract NestJS Implement Contract Docs}
|
32
|
+
*/
|
33
|
+
declare function populateContractRouterPaths<T extends AnyContractRouter>(router: T, options?: PopulateContractRouterPathsOptions): PopulatedContractRouterPaths<T>;
|
16
34
|
|
17
|
-
export { Implement, ImplementInterceptor,
|
35
|
+
export { Implement as Impl, Implement, ImplementInterceptor, populateContractRouterPaths, toNestPattern };
|
36
|
+
export type { PopulateContractRouterPathsOptions, PopulatedContractRouterPaths };
|
package/dist/index.d.ts
CHANGED
@@ -1,17 +1,36 @@
|
|
1
|
-
import { AnyContractProcedure, HTTPPath, AnyContractRouter } from '@orpc/contract';
|
2
|
-
import { BuilderConfig, ImplementerInternal } from '@orpc/server';
|
3
|
-
export { ORPCError } from '@orpc/server';
|
4
1
|
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
2
|
+
import { ContractRouter, HTTPPath, AnyContractRouter, ContractProcedure } from '@orpc/contract';
|
3
|
+
import { Router } from '@orpc/server';
|
4
|
+
export { ImplementedProcedure, Implementer, ImplementerInternal, ImplementerInternalWithMiddlewares, ORPCError, ProcedureImplementer, RouterImplementer, RouterImplementerWithMiddlewares, implement } from '@orpc/server';
|
5
|
+
import { Promisable } from '@orpc/shared';
|
5
6
|
import { Observable } from 'rxjs';
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
/**
|
9
|
+
* Decorator in controller handler to implement a oRPC contract.
|
10
|
+
*
|
11
|
+
* @see {@link https://orpc.unnoq.com/docs/openapi/nest/implement-contract#implement-your-contract NestJS Implement Contract Docs}
|
12
|
+
*/
|
13
|
+
declare function Implement<T extends ContractRouter<any>>(contract: T): <U extends Promisable<Router<T, Record<never, never>>>>(target: Record<PropertyKey, any>, propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => U>) => void;
|
9
14
|
declare class ImplementInterceptor implements NestInterceptor {
|
10
15
|
intercept(ctx: ExecutionContext, next: CallHandler<any>): Observable<any>;
|
11
16
|
}
|
12
17
|
|
13
|
-
declare function
|
14
|
-
|
15
|
-
|
18
|
+
declare function toNestPattern(path: HTTPPath): string;
|
19
|
+
type PopulatedContractRouterPaths<T extends AnyContractRouter> = T extends ContractProcedure<infer UInputSchema, infer UOutputSchema, infer UErrors, infer UMeta> ? ContractProcedure<UInputSchema, UOutputSchema, UErrors, UMeta> : {
|
20
|
+
[K in keyof T]: T[K] extends AnyContractRouter ? PopulatedContractRouterPaths<T[K]> : never;
|
21
|
+
};
|
22
|
+
interface PopulateContractRouterPathsOptions {
|
23
|
+
path?: readonly string[];
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* populateContractRouterPaths is completely optional,
|
27
|
+
* because the procedure's path is required for NestJS implementation.
|
28
|
+
* This utility automatically populates any missing paths
|
29
|
+
* Using the router's keys + `/`.
|
30
|
+
*
|
31
|
+
* @see {@link https://orpc.unnoq.com/docs/openapi/nest/implement-contract#define-your-contract NestJS Implement Contract Docs}
|
32
|
+
*/
|
33
|
+
declare function populateContractRouterPaths<T extends AnyContractRouter>(router: T, options?: PopulateContractRouterPathsOptions): PopulatedContractRouterPaths<T>;
|
16
34
|
|
17
|
-
export { Implement, ImplementInterceptor,
|
35
|
+
export { Implement as Impl, Implement, ImplementInterceptor, populateContractRouterPaths, toNestPattern };
|
36
|
+
export type { PopulateContractRouterPathsOptions, PopulatedContractRouterPaths };
|
package/dist/index.mjs
CHANGED
@@ -1,133 +1,86 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
import {
|
4
|
-
import {
|
5
|
-
import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializer, StandardBracketNotationSerializer, standardizeHTTPPath } from '@orpc/openapi-client/standard';
|
1
|
+
import { applyDecorators, Delete, Patch, Put, Post, Get, Head, UseInterceptors } from '@nestjs/common';
|
2
|
+
import { toORPCError } from '@orpc/client';
|
3
|
+
import { isContractProcedure, ContractProcedure, fallbackContractConfig } from '@orpc/contract';
|
4
|
+
import { standardizeHTTPPath, StandardOpenAPISerializer, StandardOpenAPIJsonSerializer, StandardBracketNotationSerializer } from '@orpc/openapi-client/standard';
|
6
5
|
import { StandardOpenAPICodec } from '@orpc/openapi/standard';
|
6
|
+
import { getRouter, unlazy, isProcedure, call, ORPCError } from '@orpc/server';
|
7
|
+
export { ORPCError, implement } from '@orpc/server';
|
8
|
+
import { toArray, get } from '@orpc/shared';
|
7
9
|
import { toStandardLazyRequest, sendStandardResponse } from '@orpc/standard-server-node';
|
8
10
|
import { mergeMap } from 'rxjs';
|
9
|
-
import '@orpc/
|
11
|
+
import { toHttpPath } from '@orpc/client/standard';
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
status: 400,
|
14
|
-
message: "Bad Request"
|
15
|
-
},
|
16
|
-
UNAUTHORIZED: {
|
17
|
-
status: 401,
|
18
|
-
message: "Unauthorized"
|
19
|
-
},
|
20
|
-
FORBIDDEN: {
|
21
|
-
status: 403,
|
22
|
-
message: "Forbidden"
|
23
|
-
},
|
24
|
-
NOT_FOUND: {
|
25
|
-
status: 404,
|
26
|
-
message: "Not Found"
|
27
|
-
},
|
28
|
-
METHOD_NOT_SUPPORTED: {
|
29
|
-
status: 405,
|
30
|
-
message: "Method Not Supported"
|
31
|
-
},
|
32
|
-
NOT_ACCEPTABLE: {
|
33
|
-
status: 406,
|
34
|
-
message: "Not Acceptable"
|
35
|
-
},
|
36
|
-
TIMEOUT: {
|
37
|
-
status: 408,
|
38
|
-
message: "Request Timeout"
|
39
|
-
},
|
40
|
-
CONFLICT: {
|
41
|
-
status: 409,
|
42
|
-
message: "Conflict"
|
43
|
-
},
|
44
|
-
PRECONDITION_FAILED: {
|
45
|
-
status: 412,
|
46
|
-
message: "Precondition Failed"
|
47
|
-
},
|
48
|
-
PAYLOAD_TOO_LARGE: {
|
49
|
-
status: 413,
|
50
|
-
message: "Payload Too Large"
|
51
|
-
},
|
52
|
-
UNSUPPORTED_MEDIA_TYPE: {
|
53
|
-
status: 415,
|
54
|
-
message: "Unsupported Media Type"
|
55
|
-
},
|
56
|
-
UNPROCESSABLE_CONTENT: {
|
57
|
-
status: 422,
|
58
|
-
message: "Unprocessable Content"
|
59
|
-
},
|
60
|
-
TOO_MANY_REQUESTS: {
|
61
|
-
status: 429,
|
62
|
-
message: "Too Many Requests"
|
63
|
-
},
|
64
|
-
CLIENT_CLOSED_REQUEST: {
|
65
|
-
status: 499,
|
66
|
-
message: "Client Closed Request"
|
67
|
-
},
|
68
|
-
INTERNAL_SERVER_ERROR: {
|
69
|
-
status: 500,
|
70
|
-
message: "Internal Server Error"
|
71
|
-
},
|
72
|
-
NOT_IMPLEMENTED: {
|
73
|
-
status: 501,
|
74
|
-
message: "Not Implemented"
|
75
|
-
},
|
76
|
-
BAD_GATEWAY: {
|
77
|
-
status: 502,
|
78
|
-
message: "Bad Gateway"
|
79
|
-
},
|
80
|
-
SERVICE_UNAVAILABLE: {
|
81
|
-
status: 503,
|
82
|
-
message: "Service Unavailable"
|
83
|
-
},
|
84
|
-
GATEWAY_TIMEOUT: {
|
85
|
-
status: 504,
|
86
|
-
message: "Gateway Timeout"
|
87
|
-
}
|
88
|
-
};
|
89
|
-
function fallbackORPCErrorStatus(code, status) {
|
90
|
-
return status ?? COMMON_ORPC_ERROR_DEFS[code]?.status ?? 500;
|
91
|
-
}
|
92
|
-
function fallbackORPCErrorMessage(code, message) {
|
93
|
-
return message || COMMON_ORPC_ERROR_DEFS[code]?.message || code;
|
13
|
+
function toNestPattern(path) {
|
14
|
+
return standardizeHTTPPath(path).replace(/\/\{\+([^}]+)\}/g, "/*$1").replace(/\/\{([^}]+)\}/g, "/:$1");
|
94
15
|
}
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
16
|
+
function populateContractRouterPaths(router, options = {}) {
|
17
|
+
const path = toArray(options.path);
|
18
|
+
if (isContractProcedure(router)) {
|
19
|
+
if (router["~orpc"].route.path === void 0) {
|
20
|
+
return new ContractProcedure({
|
21
|
+
...router["~orpc"],
|
22
|
+
route: {
|
23
|
+
...router["~orpc"].route,
|
24
|
+
path: toHttpPath(path)
|
25
|
+
}
|
26
|
+
});
|
103
27
|
}
|
104
|
-
|
105
|
-
super(message, options);
|
106
|
-
this.code = code;
|
107
|
-
this.status = fallbackORPCErrorStatus(code, options?.status);
|
108
|
-
this.defined = options?.defined ?? false;
|
109
|
-
this.data = options?.data;
|
28
|
+
return router;
|
110
29
|
}
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
code: this.code,
|
115
|
-
status: this.status,
|
116
|
-
message: this.message,
|
117
|
-
data: this.data
|
118
|
-
};
|
30
|
+
const populated = {};
|
31
|
+
for (const key in router) {
|
32
|
+
populated[key] = populateContractRouterPaths(router[key], { ...options, path: [...path, key] });
|
119
33
|
}
|
120
|
-
|
121
|
-
function toORPCError(error) {
|
122
|
-
return error instanceof ORPCError ? error : new ORPCError("INTERNAL_SERVER_ERROR", {
|
123
|
-
message: "Internal server error",
|
124
|
-
cause: error
|
125
|
-
});
|
126
|
-
}
|
127
|
-
function isORPCErrorStatus(status) {
|
128
|
-
return status < 200 || status >= 400;
|
34
|
+
return populated;
|
129
35
|
}
|
130
36
|
|
37
|
+
const MethodDecoratorMap = {
|
38
|
+
HEAD: Head,
|
39
|
+
GET: Get,
|
40
|
+
POST: Post,
|
41
|
+
PUT: Put,
|
42
|
+
PATCH: Patch,
|
43
|
+
DELETE: Delete
|
44
|
+
};
|
45
|
+
function Implement(contract) {
|
46
|
+
if (isContractProcedure(contract)) {
|
47
|
+
const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route.method);
|
48
|
+
const path = contract["~orpc"].route.path;
|
49
|
+
if (path === void 0) {
|
50
|
+
throw new Error(`
|
51
|
+
@Implement decorator requires contract to have a 'path'.
|
52
|
+
Please define one using 'path' property on the '.route' method.
|
53
|
+
Or use "populateContractRouterPaths" utility to automatically fill in any missing paths.
|
54
|
+
`);
|
55
|
+
}
|
56
|
+
return (target, propertyKey, descriptor) => {
|
57
|
+
applyDecorators(
|
58
|
+
MethodDecoratorMap[method](toNestPattern(path)),
|
59
|
+
UseInterceptors(ImplementInterceptor)
|
60
|
+
)(target, propertyKey, descriptor);
|
61
|
+
};
|
62
|
+
}
|
63
|
+
return (target, propertyKey, descriptor) => {
|
64
|
+
for (const key in contract) {
|
65
|
+
let methodName = `${propertyKey}_${key}`;
|
66
|
+
let i = 0;
|
67
|
+
while (methodName in target) {
|
68
|
+
methodName = `${propertyKey}_${key}_${i++}`;
|
69
|
+
}
|
70
|
+
target[methodName] = async function(...args) {
|
71
|
+
const router = await descriptor.value.apply(this, args);
|
72
|
+
return getRouter(router, [key]);
|
73
|
+
};
|
74
|
+
for (const p of Reflect.getOwnMetadataKeys(target, propertyKey)) {
|
75
|
+
Reflect.defineMetadata(p, Reflect.getOwnMetadata(p, target, propertyKey), target, methodName);
|
76
|
+
}
|
77
|
+
for (const p of Reflect.getOwnMetadataKeys(target.constructor, propertyKey)) {
|
78
|
+
Reflect.defineMetadata(p, Reflect.getOwnMetadata(p, target.constructor, propertyKey), target.constructor, methodName);
|
79
|
+
}
|
80
|
+
Implement(get(contract, [key]))(target, methodName, Object.getOwnPropertyDescriptor(target, methodName));
|
81
|
+
}
|
82
|
+
};
|
83
|
+
}
|
131
84
|
const codec = new StandardOpenAPICodec(
|
132
85
|
new StandardOpenAPISerializer(
|
133
86
|
new StandardOpenAPIJsonSerializer(),
|
@@ -137,7 +90,13 @@ const codec = new StandardOpenAPICodec(
|
|
137
90
|
class ImplementInterceptor {
|
138
91
|
intercept(ctx, next) {
|
139
92
|
return next.handle().pipe(
|
140
|
-
mergeMap(async (
|
93
|
+
mergeMap(async (impl) => {
|
94
|
+
const { default: procedure } = await unlazy(impl);
|
95
|
+
if (!isProcedure(procedure)) {
|
96
|
+
throw new Error(`
|
97
|
+
The return value of the @Implement controller handler must be a corresponding implemented router or procedure.
|
98
|
+
`);
|
99
|
+
}
|
141
100
|
const req = ctx.switchToHttp().getRequest();
|
142
101
|
const res = ctx.switchToHttp().getResponse();
|
143
102
|
const nodeReq = "raw" in req ? req.raw : req;
|
@@ -154,7 +113,7 @@ class ImplementInterceptor {
|
|
154
113
|
const output = await call(procedure, input);
|
155
114
|
return codec.encode(output, procedure);
|
156
115
|
} catch (e) {
|
157
|
-
const error = isDecoding && !(e instanceof ORPCError
|
116
|
+
const error = isDecoding && !(e instanceof ORPCError) ? new ORPCError("BAD_REQUEST", {
|
158
117
|
message: `Malformed request. Ensure the request body is properly formatted and the 'Content-Type' header is set correctly.`,
|
159
118
|
cause: e
|
160
119
|
}) : toORPCError(e);
|
@@ -178,30 +137,4 @@ function flattenParams(params) {
|
|
178
137
|
return flatten;
|
179
138
|
}
|
180
139
|
|
181
|
-
|
182
|
-
return standardizeHTTPPath(path).replace(/\/\{\+([^}]+)\}/g, "/*$1").replace(/\/\{([^}]+)\}/g, "/:$1");
|
183
|
-
}
|
184
|
-
|
185
|
-
function Implement(contract) {
|
186
|
-
const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route.method);
|
187
|
-
const path = contract["~orpc"].route.path;
|
188
|
-
if (path === void 0) {
|
189
|
-
throw new Error(`
|
190
|
-
oRPC Fastify integration requires procedure to have a 'path'.
|
191
|
-
Please define one using 'path' property on the '.route' method.
|
192
|
-
`);
|
193
|
-
}
|
194
|
-
return (target, propertyKey, descriptor) => {
|
195
|
-
const MethodDecorator = method === "GET" ? Get : method === "HEAD" ? Head : method === "PUT" ? Put : method === "PATCH" ? Patch : method === "DELETE" ? Delete : Post;
|
196
|
-
applyDecorators(
|
197
|
-
MethodDecorator(toFastifyPattern(path)),
|
198
|
-
UseInterceptors(ImplementInterceptor)
|
199
|
-
)(target, propertyKey, descriptor);
|
200
|
-
};
|
201
|
-
}
|
202
|
-
|
203
|
-
function implement(contract, config = {}) {
|
204
|
-
return implementerInternal(contract, config, []);
|
205
|
-
}
|
206
|
-
|
207
|
-
export { Implement, ImplementInterceptor, implement, toFastifyPattern };
|
140
|
+
export { Implement as Impl, Implement, ImplementInterceptor, populateContractRouterPaths, toNestPattern };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orpc/nest",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.0.
|
4
|
+
"version": "0.0.1",
|
5
5
|
"license": "MIT",
|
6
6
|
"homepage": "https://orpc.unnoq.com",
|
7
7
|
"repository": {
|
@@ -40,27 +40,32 @@
|
|
40
40
|
}
|
41
41
|
},
|
42
42
|
"dependencies": {
|
43
|
-
"@orpc/openapi-client": "1.2.0",
|
44
|
-
"@orpc/server": "1.2.0",
|
45
43
|
"@orpc/client": "1.2.0",
|
44
|
+
"@orpc/openapi": "1.2.0",
|
45
|
+
"@orpc/server": "1.2.0",
|
46
|
+
"@orpc/openapi-client": "1.2.0",
|
46
47
|
"@orpc/shared": "1.2.0",
|
47
48
|
"@orpc/standard-server": "1.2.0",
|
48
|
-
"@orpc/standard-server-node": "1.2.0"
|
49
|
-
"@orpc/openapi": "1.2.0"
|
49
|
+
"@orpc/standard-server-node": "1.2.0"
|
50
50
|
},
|
51
51
|
"devDependencies": {
|
52
|
-
"@nestjs/common": "^11.
|
52
|
+
"@nestjs/common": "^11.1.0",
|
53
53
|
"@nestjs/core": "^11.0.0",
|
54
|
-
"@nestjs/platform-express": "^11.
|
55
|
-
"@nestjs/platform-fastify": "^11.
|
54
|
+
"@nestjs/platform-express": "^11.1.0",
|
55
|
+
"@nestjs/platform-fastify": "^11.1.0",
|
56
|
+
"@nestjs/testing": "^11.1.0",
|
57
|
+
"@ts-rest/core": "^3.52.1",
|
56
58
|
"@types/express": "^5.0.1",
|
57
59
|
"express": "^5.0.0",
|
58
60
|
"fastify": "^5.0.0",
|
59
|
-
"rxjs": "^7.0.0"
|
61
|
+
"rxjs": "^7.0.0",
|
62
|
+
"supertest": "^7.1.0",
|
63
|
+
"zod": "^3.24.4"
|
60
64
|
},
|
61
65
|
"scripts": {
|
62
66
|
"build": "unbuild",
|
63
67
|
"build:watch": "pnpm run build --watch",
|
64
|
-
"type:check": "tsc -b"
|
68
|
+
"type:check": "tsc -b",
|
69
|
+
"type:check:test": "tsc -p tsconfig.test.json --noEmit"
|
65
70
|
}
|
66
71
|
}
|