Skip to content

Commit 9fb1683

Browse files
committed
Merge origin/production in beta
2 parents 3591772 + 1486e31 commit 9fb1683

33 files changed

+269
-184
lines changed

.github/ISSUE_TEMPLATE/01_bug-report.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: "\U0001F41B Bug report"
22
description: Create a report to help us improve
3-
title: "[BUG] Title"
3+
title: "Title"
44
labels: [":bug: bug"]
5+
type: bug
56
assignees: ["Romakita"]
67
body:
78
- type: markdown

.github/ISSUE_TEMPLATE/02_feature-request.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: "\U0001F680 Feature request"
22
description: Suggest an idea for this library
33
labels: [":sparkles: enhancement"]
4+
type: feature
45
assignees: ["Romakita"]
56
body:
67
- type: markdown

docs/.vitepress/config.mts

+16
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ export default defineConfig({
158158
text: "Platform API",
159159
link: `/docs/platform-api`
160160
},
161+
{
162+
text: "Platform Serverless",
163+
link: `/docs/platform-serverless`
164+
},
165+
{
166+
text: "Platform Serverless HTTP",
167+
link: `/docs/platform-serverless-http`
168+
},
161169
{
162170
text: "Command",
163171
link: `/docs/command`
@@ -434,6 +442,14 @@ export default defineConfig({
434442
text: "Platform API",
435443
link: `/docs/platform-api`
436444
},
445+
{
446+
text: "Platform Serverless",
447+
link: `/docs/platform-serverless`
448+
},
449+
{
450+
text: "Platform Serverless HTTP",
451+
link: `/docs/platform-serverless-http`
452+
},
437453
{
438454
text: "Command",
439455
link: `/docs/command`

docs/docs/controllers.md

+20
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,25 @@ You have to add extra parameter to enable it:
266266

267267
:::
268268

269+
### Accept Mime
270+
271+
@@AcceptMime@@ decorator provides you quick access to the `request.accepts()` and add given mime type to the consumable data types.
272+
273+
```typescript
274+
import {BodyParams} from "@tsed/platform-params";
275+
import {Get, AcceptMime} from "@tsed/schema";
276+
import {Controller} from "@tsed/di";
277+
278+
@Controller("/")
279+
class ExampleCtrl {
280+
@Get("/")
281+
@AcceptMime("application/x-www-form-urlencoded")
282+
async get(@BodyParams() model: Model) {
283+
return {message: "Hello world"};
284+
}
285+
}
286+
```
287+
269288
### Session/Cookies/Locals/Context
270289

271290
For the session, cookies, locals or context data attached on the request, it works the same way as seen before. Use the
@@ -275,6 +294,7 @@ following decorators to get the data:
275294
- @@Cookies@@
276295
- @@Locals@@
277296
- @@Context@@
297+
-
278298

279299
#### Locals
280300

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {methodsOf} from "./methodsOf.js";
2+
3+
describe("methodsOf()", () => {
4+
it("should return methods", () => {
5+
class TestClass {
6+
method() {}
7+
8+
property: string;
9+
}
10+
11+
const methods = methodsOf(TestClass);
12+
expect(methods).toHaveLength(1);
13+
expect(methods[0]).toMatchObject({
14+
propertyKey: "method"
15+
});
16+
});
17+
it("should not return getters", () => {
18+
class TestClass {
19+
method() {}
20+
21+
get property() {
22+
return "";
23+
}
24+
}
25+
26+
const methods = methodsOf(TestClass);
27+
28+
expect(methods).toHaveLength(1);
29+
expect(methods[0]).toMatchObject({
30+
propertyKey: "method"
31+
});
32+
});
33+
});

packages/core/src/utils/objects/methodsOf.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Type} from "../../domain/Type.js";
22
import {ancestorsOf} from "./ancestorsOf.js";
33
import {classOf} from "./classOf.js";
4+
import {isSymbol} from "./isSymbol.js";
45
import {prototypeOf} from "./prototypeOf.js";
56

67
/**
@@ -15,9 +16,9 @@ export function methodsOf(target: any): {target: Type; propertyKey: string}[] {
1516
const keys = Reflect.ownKeys(prototypeOf(target));
1617

1718
keys.forEach((propertyKey: string) => {
18-
if (propertyKey !== "constructor") {
19-
methods.set(propertyKey, {target, propertyKey});
20-
}
19+
if (isSymbol(propertyKey) || propertyKey === "constructor" || Object.getOwnPropertyDescriptor(prototypeOf(target), propertyKey)?.get)
20+
return;
21+
methods.set(propertyKey, {target, propertyKey});
2122
});
2223
});
2324

packages/platform/platform-express/src/components/PlatformExpress.ts

+6-12
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,6 @@ declare global {
6464
export class PlatformExpress extends PlatformAdapter<Express.Application> {
6565
readonly NAME = "express";
6666

67-
readonly providers = [
68-
{
69-
token: PlatformHandler,
70-
useClass: PlatformExpressHandler
71-
},
72-
{token: PlatformResponse},
73-
{token: PlatformRequest},
74-
{token: PlatformApplication},
75-
{token: Platform}
76-
];
77-
7867
#multer: typeof multer;
7968

8069
constructor() {
@@ -286,4 +275,9 @@ export class PlatformExpress extends PlatformAdapter<Express.Application> {
286275
}
287276
}
288277

289-
adapter(PlatformExpress);
278+
adapter(PlatformExpress, [
279+
{
280+
token: PlatformHandler,
281+
useClass: PlatformExpressHandler
282+
}
283+
]);

packages/platform/platform-express/vitest.config.mts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ export default defineConfig(
1010
coverage: {
1111
...presets.test.coverage,
1212
thresholds: {
13-
statements: 96.63,
13+
statements: 96.61,
1414
branches: 95,
1515
functions: 100,
16-
lines: 96.63
16+
lines: 96.61
1717
}
1818
}
1919
}
2020
}
21-
);
21+
);

packages/platform/platform-http/src/common/fn/adapter.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,39 @@
11
import {Type} from "@tsed/core";
2-
import {refValue} from "@tsed/di";
2+
import {GlobalProviders, injector, ProviderOpts, refValue} from "@tsed/di";
33

4-
import type {PlatformAdapter} from "../services/PlatformAdapter.js";
4+
import {PlatformAdapter} from "../services/PlatformAdapter.js";
55

66
const ADAPTER = "platform.adapter";
77

88
let globalAdapter: Type<PlatformAdapter<any>>;
99

1010
/**
11-
* Set the platform adapter
11+
* Set the platform adapter token and his dependencies
1212
*/
13-
export function adapter(adapter?: Type<PlatformAdapter<any>>): Type<PlatformAdapter<any>> {
13+
export function adapter(adapter: Type<PlatformAdapter<any>>, imports?: ProviderOpts[]): Type<PlatformAdapter<any>>;
14+
/**
15+
* Get the platform adapter token
16+
*/
17+
export function adapter(): Type<PlatformAdapter<any>>;
18+
export function adapter(adapter?: Type<PlatformAdapter<any>>, imports: ProviderOpts[] = []): Type<PlatformAdapter<any>> {
1419
const ref = refValue<Type<PlatformAdapter<any>>>(ADAPTER);
1520

1621
if (adapter) {
1722
globalAdapter ||= adapter;
23+
24+
imports?.forEach(({token, useClass}) => {
25+
const provider = GlobalProviders.get(token);
26+
if (useClass && provider) {
27+
provider.useClass = useClass;
28+
injector().set(token, provider);
29+
}
30+
});
31+
32+
injector()
33+
.addProvider(PlatformAdapter, {
34+
useClass: adapter
35+
})
36+
.alias(PlatformAdapter, "PlatformAdapter");
1837
}
1938

2039
ref.value ||= globalAdapter;

packages/platform/platform-http/src/common/middlewares/PlatformMulterMiddleware.spec.ts

+12-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {catchAsyncError} from "@tsed/core";
2+
import {inject} from "@tsed/di";
23
import {Exception} from "@tsed/exceptions";
34
import {MulterError} from "multer";
45

56
import {PlatformTest} from "../../testing/PlatformTest.js";
67
import {MulterOptions} from "../decorators/multer/multerOptions.js";
78
import {MultipartFile} from "../decorators/multer/multipartFile.js";
89
import {EndpointMetadata} from "../domain/EndpointMetadata.js";
10+
import {PlatformAdapter} from "../services/PlatformAdapter.js";
911
import {PlatformApplication} from "../services/PlatformApplication.js";
1012
import {PlatformMulterMiddleware} from "./PlatformMulterMiddleware.js";
1113

@@ -19,20 +21,16 @@ async function build(options = {}) {
1921
const multer = {
2022
fields: vi.fn().mockReturnValue(multerMiddleware)
2123
};
22-
const app = {
23-
multer: vi.fn().mockReturnValue(multer)
24-
};
2524

26-
const middleware = await PlatformTest.invoke<PlatformMulterMiddleware>(PlatformMulterMiddleware, [
27-
{
28-
token: PlatformApplication,
29-
use: app
30-
}
31-
]);
25+
const middleware = inject(PlatformMulterMiddleware);
3226
const ctx: any = PlatformTest.createRequestContext();
3327
ctx.endpoint = EndpointMetadata.get(Test, "upload");
3428

35-
return {middleware, ctx, multer, app, multerMiddleware};
29+
const adapter = inject(PlatformAdapter);
30+
31+
vi.spyOn(adapter, "multipart").mockReturnValue(multer as any);
32+
33+
return {middleware, ctx, multer, adapter, multerMiddleware};
3634
}
3735

3836
describe("PlatformMulterMiddleware", () => {
@@ -45,24 +43,24 @@ describe("PlatformMulterMiddleware", () => {
4543
);
4644
afterEach(() => PlatformTest.reset());
4745
it("should create middleware", async () => {
48-
const {middleware, ctx, multer, app, multerMiddleware} = await build({});
46+
const {middleware, ctx, multer, adapter, multerMiddleware} = await build({});
4947

5048
await middleware.use(ctx);
5149

52-
expect(app.multer).toHaveBeenCalledWith({
50+
expect(adapter.multipart).toHaveBeenCalledWith({
5351
dest: "/dest"
5452
});
5553
expect(multer.fields).toHaveBeenCalledWith([{maxCount: undefined, name: "file1"}]);
5654
expect(multerMiddleware).toHaveBeenCalledWith(ctx.request.raw, ctx.response.raw);
5755
});
5856
it("should create middleware with storage", async () => {
59-
const {middleware, ctx, multer, app, multerMiddleware} = await build({
57+
const {middleware, ctx, multer, adapter, multerMiddleware} = await build({
6058
storage: "storage"
6159
});
6260

6361
await middleware.use(ctx);
6462

65-
expect(app.multer).toHaveBeenCalledWith({
63+
expect(adapter.multipart).toHaveBeenCalledWith({
6664
storage: "storage"
6765
});
6866
expect(multer.fields).toHaveBeenCalledWith([{maxCount: undefined, name: "file1"}]);

packages/platform/platform-http/src/common/middlewares/PlatformMulterMiddleware.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {MulterError} from "multer";
66

77
import {PlatformMulterField, PlatformMulterSettings} from "../config/interfaces/PlatformMulterSettings.js";
88
import {PlatformContext} from "../domain/PlatformContext.js";
9+
import {PlatformAdapter} from "../services/PlatformAdapter.js";
910
import {PlatformApplication} from "../services/PlatformApplication.js";
1011

1112
export interface MulterInputOptions {
@@ -24,8 +25,6 @@ export class MulterException extends BadRequest {
2425
* @middleware
2526
*/
2627
export class PlatformMulterMiddleware implements MiddlewareMethods {
27-
protected app = inject(PlatformApplication);
28-
2928
async use(@Context() ctx: PlatformContext) {
3029
try {
3130
const {fields, options = {}} = ctx.endpoint.get(PlatformMulterMiddleware);
@@ -39,7 +38,7 @@ export class PlatformMulterMiddleware implements MiddlewareMethods {
3938
delete settings.dest;
4039
}
4140

42-
const multer = this.app.multer(settings);
41+
const multer = inject(PlatformAdapter).multipart(settings);
4342

4443
if (multer) {
4544
const middleware: any = multer.fields(this.getFields({fields}));

packages/platform/platform-http/src/common/services/PlatformAdapter.ts

+18-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {IncomingMessage, ServerResponse} from "node:http";
22

33
import {Type} from "@tsed/core";
4-
import {injectable, ProviderOpts} from "@tsed/di";
4+
import {configuration, constant, inject, injectable} from "@tsed/di";
5+
import {$on} from "@tsed/hooks";
56
import {PlatformHandlerMetadata, PlatformLayer} from "@tsed/platform-router";
67

78
import {PlatformMulter, PlatformMulterSettings} from "../config/interfaces/PlatformMulterSettings.js";
@@ -10,17 +11,25 @@ import {application} from "../fn/application.js";
1011
import {createHttpServer} from "../utils/createHttpServer.js";
1112
import {createHttpsServer} from "../utils/createHttpsServer.js";
1213
import {CreateServerReturn} from "../utils/createServer.js";
13-
import type {PlatformApplication} from "./PlatformApplication.js";
14+
import {Platform} from "./Platform.js";
15+
import {PlatformApplication} from "./PlatformApplication.js";
16+
import {PlatformHandler} from "./PlatformHandler.js";
1417

1518
export abstract class PlatformAdapter<App = TsED.Application> {
1619
abstract readonly NAME: string;
17-
/**
18-
* Load providers in top priority
19-
*/
20-
providers: ProviderOpts[] = [];
2120

22-
get app(): PlatformApplication<App> {
23-
return application();
21+
readonly app = inject<PlatformApplication<App>>(PlatformApplication);
22+
23+
constructor() {
24+
const platformApp = inject(PlatformApplication);
25+
26+
const {app, callback} = this.createApp();
27+
platformApp.rawApp = app as any;
28+
platformApp.rawCallback = callback;
29+
30+
$on("$afterInvoke", PlatformAdapter, () => {
31+
configuration().set("PLATFORM_NAME", constant("PLATFORM_NAME") || this.NAME);
32+
});
2433
}
2534

2635
getServers(): CreateServerReturn[] {
@@ -87,4 +96,4 @@ export interface PlatformBuilderSettings<App = TsED.Application> extends Partial
8796
adapter?: Type<PlatformAdapter<App>>;
8897
}
8998

90-
injectable(PlatformAdapter).alias("PlatformAdapter");
99+
injectable(PlatformAdapter).imports([PlatformApplication, Platform, PlatformHandler]).alias("PlatformAdapter");

packages/platform/platform-http/src/common/services/PlatformApplication.spec.ts

-12
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ async function getPlatformApp() {
3333
]);
3434
configuration().logger = {};
3535
platformApp.rawApp = createDriver() as any;
36-
vi.spyOn(platformApp, "multer").mockReturnValue({} as never);
3736

3837
return {platformApp, platformHandler};
3938
}
@@ -63,17 +62,6 @@ describe("PlatformApplication", () => {
6362
platformApp.statics("/", {root: "/root"});
6463
});
6564
});
66-
describe("multer()", () => {
67-
it("should call statics", async () => {
68-
// GIVEN
69-
const {platformApp} = await getPlatformApp();
70-
71-
vi.spyOn(console, "warn");
72-
73-
// WHEN
74-
platformApp.multer({});
75-
});
76-
});
7765

7866
describe("callback()", () => {
7967
it("should return the callback", async () => {

0 commit comments

Comments
 (0)