@tsed/platform-express 8.19.3 → 8.19.5
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/lib/esm/components/PlatformExpress.js +11 -1
- package/lib/esm/services/PlatformExpressResponse.js +10 -4
- package/lib/esm/utils/convertPath.js +92 -60
- package/lib/types/interfaces/PlatformExpressSettings.d.ts +4 -0
- package/lib/types/services/PlatformExpressResponse.d.ts +2 -1
- package/package.json +10 -10
|
@@ -2,7 +2,7 @@ import "@tsed/platform-multer/express";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { catchAsyncError, Env, isFunction } from "@tsed/core";
|
|
5
|
+
import { catchAsyncError, Env, isArray, isFunction } from "@tsed/core";
|
|
6
6
|
import { constant, inject, logger, runInContext } from "@tsed/di";
|
|
7
7
|
import { PlatformExceptions } from "@tsed/platform-exceptions";
|
|
8
8
|
import { adapter, application, createContext, PlatformAdapter, PlatformBuilder, PlatformHandler, PlatformResponse } from "@tsed/platform-http";
|
|
@@ -19,6 +19,10 @@ function callNext(next, metadata, $ctx) {
|
|
|
19
19
|
}
|
|
20
20
|
function getVersion() {
|
|
21
21
|
try {
|
|
22
|
+
const v = constant("express.version");
|
|
23
|
+
if (v) {
|
|
24
|
+
return v;
|
|
25
|
+
}
|
|
22
26
|
const { version } = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.resolve("express"))), "package.json"), "utf8"));
|
|
23
27
|
return `v${version.split(".")[0]}`;
|
|
24
28
|
}
|
|
@@ -94,6 +98,12 @@ export class PlatformExpress extends PlatformAdapter {
|
|
|
94
98
|
if (req.params["0"] && !req.params[wildcard]) {
|
|
95
99
|
req.params[wildcard] = req.params["0"];
|
|
96
100
|
}
|
|
101
|
+
if (isArray(req.params[wildcard])) {
|
|
102
|
+
req.params[wildcard] = req.params[wildcard].join("/");
|
|
103
|
+
}
|
|
104
|
+
if (wildcard === "wildcard") {
|
|
105
|
+
req.params["*"] ||= req.params[wildcard];
|
|
106
|
+
}
|
|
97
107
|
next();
|
|
98
108
|
}));
|
|
99
109
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { isNumber } from "@tsed/core";
|
|
1
|
+
import { isArray, isNumber } from "@tsed/core";
|
|
2
2
|
import { PlatformResponse } from "@tsed/platform-http";
|
|
3
3
|
export class PlatformExpressResponse extends PlatformResponse {
|
|
4
|
+
cookie(name, value, opts) {
|
|
5
|
+
super.cookie(name, value, opts);
|
|
6
|
+
const cookie = this.get("set-cookie");
|
|
7
|
+
if (cookie && !isArray(value)) {
|
|
8
|
+
this.setHeader("set-cookie", [].concat(cookie));
|
|
9
|
+
}
|
|
10
|
+
return this;
|
|
11
|
+
}
|
|
4
12
|
end(data) {
|
|
5
13
|
if (isNumber(data)) {
|
|
6
|
-
|
|
7
|
-
// @ts-ignore
|
|
8
|
-
this.raw.send(this.statusCode, data);
|
|
14
|
+
this.raw.send(String(data));
|
|
9
15
|
}
|
|
10
16
|
else {
|
|
11
17
|
this.raw.send(data);
|
|
@@ -3,77 +3,109 @@ import { isString } from "@tsed/core";
|
|
|
3
3
|
* Converts a path to v4 format
|
|
4
4
|
*/
|
|
5
5
|
function convertPathToV4(path) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
// Preserve unsupported/complex patterns
|
|
7
|
+
// Keep paths with bracket choices or middle wildcards intact
|
|
8
|
+
if (path.includes("[") || (path.includes("(.*)") && !path.endsWith("/(.*)") && path !== "/(.*)")) {
|
|
9
|
+
return { path };
|
|
10
|
+
}
|
|
11
|
+
let wildcard;
|
|
12
|
+
let result = path;
|
|
13
|
+
// v5 optional segment syntax -> v4 optional param
|
|
14
|
+
// e.g. "/test{/:foo}{/:param}" -> "/test/:foo?/:param?"
|
|
15
|
+
result = result.replace(/\{\/:([A-Za-z0-9_]+)\}/g, "/:$1?");
|
|
16
|
+
// e.g. "/test/:foo/{:param}" -> "/test/:foo/:param"
|
|
17
|
+
result = result.replace(/\{:([A-Za-z0-9_]+)\}/g, ":$1");
|
|
18
|
+
// Trailing v5 wildcard segment -> v4
|
|
19
|
+
result = result.replace(/\/{\*([A-Za-z0-9_]+)\}$/g, (_m, name) => {
|
|
20
|
+
wildcard = name;
|
|
21
|
+
return "/*";
|
|
22
|
+
});
|
|
23
|
+
const segments = result.split("/");
|
|
24
|
+
const last = segments[segments.length - 1];
|
|
25
|
+
if (last === "*" || last === "(.*)") {
|
|
26
|
+
wildcard = "*";
|
|
27
|
+
if (last === "(.*)") {
|
|
28
|
+
segments[segments.length - 1] = "*";
|
|
29
|
+
result = segments.join("/");
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
return { path: result, wildcard };
|
|
32
|
+
}
|
|
33
|
+
// Trailing *name -> wildcard name for v4
|
|
34
|
+
if (last && last.startsWith("*") && last.length > 1) {
|
|
35
|
+
wildcard = last.substring(1);
|
|
36
|
+
segments[segments.length - 1] = "*";
|
|
37
|
+
result = segments.join("/");
|
|
38
|
+
return { path: result, wildcard };
|
|
39
|
+
}
|
|
40
|
+
// Ts.ED syntax :param* -> v4 "*"
|
|
41
|
+
if (last && last.startsWith(":") && last.endsWith("*")) {
|
|
42
|
+
wildcard = last.substring(1, last.length - 1);
|
|
43
|
+
segments[segments.length - 1] = "*";
|
|
44
|
+
result = segments.join("/");
|
|
45
|
+
return { path: result, wildcard };
|
|
46
|
+
}
|
|
47
|
+
return { path: result, wildcard };
|
|
37
48
|
}
|
|
38
49
|
/**
|
|
39
50
|
* Converts a path to v5 format
|
|
40
51
|
*/
|
|
41
52
|
function convertPathToV5(path) {
|
|
53
|
+
// Preserve unsupported/complex patterns
|
|
54
|
+
if (path.includes("[") || (path.includes("(.*)") && !path.endsWith("/(.*)") && path !== "/(.*)")) {
|
|
55
|
+
return { path };
|
|
56
|
+
}
|
|
57
|
+
// Already v5-style specific constructs should be preserved
|
|
58
|
+
if (/\/{\*[^}]+\}/.test(path) || /\{:[^}]+\}/.test(path) || /\{\/:[^}]+\}/.test(path)) {
|
|
59
|
+
const m = path.match(/\/{\*([^}]+)\}$/);
|
|
60
|
+
return { path, wildcard: m ? m[1] : undefined };
|
|
61
|
+
}
|
|
42
62
|
const segments = path.split("/");
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
63
|
+
const base = [];
|
|
64
|
+
const optionals = [];
|
|
65
|
+
let wildcard;
|
|
66
|
+
segments.forEach((seg, idx) => {
|
|
67
|
+
if (idx === 0)
|
|
68
|
+
return; // first split is empty because path starts with '/'
|
|
69
|
+
const isLast = idx === segments.length - 1;
|
|
70
|
+
// Trailing raw wildcard
|
|
71
|
+
if (isLast && (seg === "*" || seg === "(.*)")) {
|
|
72
|
+
wildcard = "wildcard";
|
|
73
|
+
optionals.push("/{*wildcard}");
|
|
74
|
+
return;
|
|
49
75
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
76
|
+
// Trailing named wildcard forms
|
|
77
|
+
if (isLast && seg.startsWith(":") && seg.endsWith("*")) {
|
|
78
|
+
wildcard = seg.substring(1, seg.length - 1);
|
|
79
|
+
optionals.push(`/{*${wildcard}}`);
|
|
80
|
+
return;
|
|
54
81
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
if (isLastSegment && segment.endsWith("*")) {
|
|
61
|
-
options.wildcard = segment.substring(1, segment.length - 1);
|
|
62
|
-
options.path.push(`{*${options.wildcard}}`);
|
|
63
|
-
return options;
|
|
64
|
-
}
|
|
82
|
+
if (isLast && seg.startsWith("*") && seg.length > 1) {
|
|
83
|
+
wildcard = seg.substring(1);
|
|
84
|
+
base.push(seg); // keep as-is according to spec for v5
|
|
85
|
+
return;
|
|
65
86
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
// Optional parameter -> v5 optional segment
|
|
88
|
+
if (seg.startsWith(":") && seg.endsWith("?")) {
|
|
89
|
+
const name = seg.substring(1, seg.length - 1);
|
|
90
|
+
optionals.push(`{/:${name}}`);
|
|
91
|
+
return;
|
|
69
92
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
// Keep everything else (including non-optional params)
|
|
94
|
+
base.push(seg);
|
|
95
|
+
});
|
|
96
|
+
let result = "/" + base.join("/");
|
|
97
|
+
// Collapse double slash when base is empty
|
|
98
|
+
if (result === "/") {
|
|
99
|
+
result = "";
|
|
100
|
+
}
|
|
101
|
+
result += optionals.join("");
|
|
102
|
+
// If named wildcard kept as segment like '/*splat', leave as-is (handled above)
|
|
103
|
+
if (!result) {
|
|
104
|
+
// Edge case when path was only optional param like "/:param?"
|
|
105
|
+
// base is empty -> result becomes optionals only
|
|
106
|
+
result = optionals.join("");
|
|
107
|
+
}
|
|
108
|
+
return { path: result || "/", wildcard };
|
|
77
109
|
}
|
|
78
110
|
/**
|
|
79
111
|
* Converts a path between v4 and v5 formats
|
|
@@ -2,6 +2,10 @@ import type { Options, OptionsJson, OptionsText, OptionsUrlencoded } from "body-
|
|
|
2
2
|
import type { NextHandleFunction } from "connect";
|
|
3
3
|
import type { RouterOptions } from "express";
|
|
4
4
|
export interface PlatformExpressSettings {
|
|
5
|
+
/**
|
|
6
|
+
* Force Express version detection. Use when Ts.ED doesn't detect the version automatically.
|
|
7
|
+
*/
|
|
8
|
+
version?: "v4" | "v5";
|
|
5
9
|
/**
|
|
6
10
|
* Global configuration for the Express.Router. See express [documentation](http://expressjs.com/en/api.html#express.router).
|
|
7
11
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PlatformResponse } from "@tsed/platform-http";
|
|
2
2
|
export declare class PlatformExpressResponse extends PlatformResponse<TsED.Response> {
|
|
3
|
-
|
|
3
|
+
cookie(name: string, value: string | null, opts?: TsED.SetCookieOpts): this;
|
|
4
|
+
protected end(data?: number | string | Buffer): void;
|
|
4
5
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tsed/platform-express",
|
|
3
3
|
"description": "A TypeScript Framework on top of Express",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.19.
|
|
5
|
+
"version": "8.19.5",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
@@ -58,17 +58,17 @@
|
|
|
58
58
|
"tslib": "2.7.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@tsed/barrels": "8.19.
|
|
62
|
-
"@tsed/core": "8.19.
|
|
63
|
-
"@tsed/di": "8.19.
|
|
64
|
-
"@tsed/platform-http": "8.19.
|
|
65
|
-
"@tsed/platform-test-sdk": "8.19.
|
|
66
|
-
"@tsed/platform-views": "8.19.
|
|
67
|
-
"@tsed/typescript": "8.19.
|
|
61
|
+
"@tsed/barrels": "8.19.5",
|
|
62
|
+
"@tsed/core": "8.19.5",
|
|
63
|
+
"@tsed/di": "8.19.5",
|
|
64
|
+
"@tsed/platform-http": "8.19.5",
|
|
65
|
+
"@tsed/platform-test-sdk": "8.19.5",
|
|
66
|
+
"@tsed/platform-views": "8.19.5",
|
|
67
|
+
"@tsed/typescript": "8.19.5",
|
|
68
68
|
"@types/body-parser": "1.19.5",
|
|
69
69
|
"@types/compression": "1.7.5",
|
|
70
70
|
"@types/cookie-parser": "1.4.7",
|
|
71
|
-
"@types/express": "^
|
|
71
|
+
"@types/express": "^5.0.6",
|
|
72
72
|
"@types/express-session": "1.18.1",
|
|
73
73
|
"@types/method-override": "0.0.35",
|
|
74
74
|
"body-parser": "2.2.0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"cors": "2.8.5",
|
|
78
78
|
"cross-env": "7.0.3",
|
|
79
79
|
"eslint": "9.12.0",
|
|
80
|
-
"express": "
|
|
80
|
+
"express": "5.2.1",
|
|
81
81
|
"express-session": "1.18.1",
|
|
82
82
|
"method-override": "3.0.0",
|
|
83
83
|
"typescript": "5.9.3",
|