appstage 0.2.21 → 0.2.23
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.cjs +29 -10
- package/dist/index.d.ts +16 -1
- package/dist/index.mjs +29 -11
- package/index.ts +1 -0
- package/package.json +1 -1
- package/src/controllers/files.ts +43 -29
- package/src/controllers/redirect.ts +10 -0
package/dist/index.cjs
CHANGED
|
@@ -79,7 +79,6 @@ function matches(x, matcher) {
|
|
|
79
79
|
}
|
|
80
80
|
const defaultExtensions = ["html", "htm"];
|
|
81
81
|
const defaultPath = (req) => req.path;
|
|
82
|
-
const defaultLanguages = getLanguageList;
|
|
83
82
|
/**
|
|
84
83
|
* Serves files from the specified directory path or paths in a locale-aware
|
|
85
84
|
* fashion after applying optional transforms.
|
|
@@ -91,6 +90,7 @@ const files = (params) => {
|
|
|
91
90
|
let fallthrough = p.fallthrough ?? true;
|
|
92
91
|
return async (req, res, next) => {
|
|
93
92
|
let urlPath = typeof p.path === "string" ? p.path : (p.path ?? defaultPath)(req);
|
|
93
|
+
let defaultLanguage = typeof p.defaultLanguage === "function" ? p.defaultLanguage(req) : p.defaultLanguage ?? "en";
|
|
94
94
|
if (!matches(urlPath, p.matches)) {
|
|
95
95
|
if (fallthrough) next();
|
|
96
96
|
else {
|
|
@@ -113,24 +113,25 @@ const files = (params) => {
|
|
|
113
113
|
}
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
|
-
let langs = (p.languages ?? defaultLanguages)(req);
|
|
117
116
|
let filePath = null;
|
|
118
117
|
let urlExt = (0, node_path.extname)(urlPath);
|
|
118
|
+
let langs = (p.languages ?? getLanguageList)(req);
|
|
119
|
+
let defaultLanguageIndex = langs.indexOf(defaultLanguage);
|
|
120
|
+
let suffixes = langs.map((s) => `.${s}`);
|
|
121
|
+
if (defaultLanguageIndex === -1) suffixes.push("");
|
|
122
|
+
else suffixes.splice(defaultLanguageIndex + 1, 0, "");
|
|
119
123
|
for (let k = 0; k < bases.length && filePath === null; k++) {
|
|
120
124
|
let base = bases[k];
|
|
121
125
|
if (!urlPath.endsWith("/")) {
|
|
122
|
-
for (let i = 0; i <
|
|
126
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) filePath = await resolve(base, `${urlPath}${suffixes[i]}`);
|
|
123
127
|
if (filePath === null && urlExt) {
|
|
124
128
|
let urlPathBase = urlPath.slice(0, -urlExt.length);
|
|
125
|
-
for (let i = 0; i <
|
|
129
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) filePath = await resolve(base, `${urlPathBase}${suffixes[i]}${urlExt}`);
|
|
126
130
|
}
|
|
127
|
-
|
|
128
|
-
for (let i = 0; i < langs.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve(base, `${urlPath}.${langs[i]}.${exts[j]}`);
|
|
129
|
-
for (let i = 0; i < exts.length && filePath === null; i++) filePath = await resolve(base, `${urlPath}.${exts[i]}`);
|
|
131
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve(base, `${urlPath}${suffixes[i]}.${exts[j]}`);
|
|
130
132
|
}
|
|
131
|
-
for (let i = 0; i <
|
|
132
|
-
for (let i = 0; i <
|
|
133
|
-
for (let i = 0; i < exts.length && filePath === null; i++) filePath = await resolve(base, urlPath, `index.${exts[i]}`);
|
|
133
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve(base, `${urlPath}${suffixes[i]}`, `index.${exts[j]}`);
|
|
134
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve(base, urlPath, `index${suffixes[i]}.${exts[j]}`);
|
|
134
135
|
}
|
|
135
136
|
if (filePath === null) {
|
|
136
137
|
if (fallthrough) next();
|
|
@@ -162,6 +163,23 @@ const files = (params) => {
|
|
|
162
163
|
};
|
|
163
164
|
};
|
|
164
165
|
|
|
166
|
+
const redirect = (params) => {
|
|
167
|
+
let { url, status = 302 } = typeof params === "string" ? { url: params } : params;
|
|
168
|
+
return (req, res) => {
|
|
169
|
+
let search = req.originalUrl.split("?")[1] ?? "";
|
|
170
|
+
if (search) search = `${url.includes("?") ? "&" : "?"}${search}`;
|
|
171
|
+
emitLog(req.app, "Redirecting", {
|
|
172
|
+
data: {
|
|
173
|
+
from: req.originalUrl,
|
|
174
|
+
to: `${url}${search}`
|
|
175
|
+
},
|
|
176
|
+
req,
|
|
177
|
+
res
|
|
178
|
+
});
|
|
179
|
+
res.redirect(status, `${url}${search}`);
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
|
|
165
183
|
const unhandledError = () => async (err, req, res) => {
|
|
166
184
|
emitLog(req.app, "Unhandled error", {
|
|
167
185
|
level: "error",
|
|
@@ -690,6 +708,7 @@ exports.init = init;
|
|
|
690
708
|
exports.injectNonce = injectNonce;
|
|
691
709
|
exports.lang = lang;
|
|
692
710
|
exports.log = log;
|
|
711
|
+
exports.redirect = redirect;
|
|
693
712
|
exports.renderStatus = renderStatus;
|
|
694
713
|
exports.requestEvents = requestEvents;
|
|
695
714
|
exports.serializeState = serializeState;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,8 +21,17 @@ type FilesParams = {
|
|
|
21
21
|
base: string | string[];
|
|
22
22
|
path?: string | ((req: Request) => string); /** Specifies which paths should be accepted. */
|
|
23
23
|
matches?: StringMatcher;
|
|
24
|
+
/**
|
|
25
|
+
* @default ["html", "htm"]
|
|
26
|
+
*/
|
|
24
27
|
extensions?: string[];
|
|
25
28
|
languages?: (req: Request) => string[];
|
|
29
|
+
/**
|
|
30
|
+
* Assumed file language if unspecified in the file name.
|
|
31
|
+
*
|
|
32
|
+
* @default "en"
|
|
33
|
+
*/
|
|
34
|
+
defaultLanguage?: string | ((req: Request) => string);
|
|
26
35
|
transform?: TransformContent[];
|
|
27
36
|
fallthrough?: boolean;
|
|
28
37
|
};
|
|
@@ -32,6 +41,12 @@ type FilesParams = {
|
|
|
32
41
|
*/
|
|
33
42
|
declare const files: Controller<string | FilesParams>;
|
|
34
43
|
|
|
44
|
+
type RedirectParams = {
|
|
45
|
+
url: string;
|
|
46
|
+
status?: number;
|
|
47
|
+
};
|
|
48
|
+
declare const redirect: Controller<string | RedirectParams>;
|
|
49
|
+
|
|
35
50
|
type ErrorController<T = void> = (params: T) => ErrorRequestHandler;
|
|
36
51
|
|
|
37
52
|
declare const unhandledError: ErrorController;
|
|
@@ -1123,4 +1138,4 @@ declare function servePipeableStream(req: Request, res: Response): ({
|
|
|
1123
1138
|
pipe
|
|
1124
1139
|
}: PipeableStream, error?: unknown) => Promise<void>;
|
|
1125
1140
|
|
|
1126
|
-
export { Controller, ErrorController, FilesParams, LangParams, LogEventPayload, LogLevel, LogOptions, Middleware, MiddlewareSet, RenderStatus, ReqCtx, TransformContent, build, cli, createApp, cspNonce, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, renderStatus, requestEvents, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
|
|
1141
|
+
export { Controller, ErrorController, FilesParams, LangParams, LogEventPayload, LogLevel, LogOptions, Middleware, MiddlewareSet, RedirectParams, RenderStatus, ReqCtx, TransformContent, build, cli, createApp, cspNonce, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, redirect, renderStatus, requestEvents, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
|
package/dist/index.mjs
CHANGED
|
@@ -53,7 +53,6 @@ function matches(x, matcher) {
|
|
|
53
53
|
}
|
|
54
54
|
const defaultExtensions = ["html", "htm"];
|
|
55
55
|
const defaultPath = (req) => req.path;
|
|
56
|
-
const defaultLanguages = getLanguageList;
|
|
57
56
|
/**
|
|
58
57
|
* Serves files from the specified directory path or paths in a locale-aware
|
|
59
58
|
* fashion after applying optional transforms.
|
|
@@ -65,6 +64,7 @@ const files = (params) => {
|
|
|
65
64
|
let fallthrough = p.fallthrough ?? true;
|
|
66
65
|
return async (req, res, next) => {
|
|
67
66
|
let urlPath = typeof p.path === "string" ? p.path : (p.path ?? defaultPath)(req);
|
|
67
|
+
let defaultLanguage = typeof p.defaultLanguage === "function" ? p.defaultLanguage(req) : p.defaultLanguage ?? "en";
|
|
68
68
|
if (!matches(urlPath, p.matches)) {
|
|
69
69
|
if (fallthrough) next();
|
|
70
70
|
else {
|
|
@@ -87,24 +87,25 @@ const files = (params) => {
|
|
|
87
87
|
}
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
let langs = (p.languages ?? defaultLanguages)(req);
|
|
91
90
|
let filePath = null;
|
|
92
91
|
let urlExt = extname(urlPath);
|
|
92
|
+
let langs = (p.languages ?? getLanguageList)(req);
|
|
93
|
+
let defaultLanguageIndex = langs.indexOf(defaultLanguage);
|
|
94
|
+
let suffixes = langs.map((s) => `.${s}`);
|
|
95
|
+
if (defaultLanguageIndex === -1) suffixes.push("");
|
|
96
|
+
else suffixes.splice(defaultLanguageIndex + 1, 0, "");
|
|
93
97
|
for (let k = 0; k < bases.length && filePath === null; k++) {
|
|
94
98
|
let base = bases[k];
|
|
95
99
|
if (!urlPath.endsWith("/")) {
|
|
96
|
-
for (let i = 0; i <
|
|
100
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) filePath = await resolve$1(base, `${urlPath}${suffixes[i]}`);
|
|
97
101
|
if (filePath === null && urlExt) {
|
|
98
102
|
let urlPathBase = urlPath.slice(0, -urlExt.length);
|
|
99
|
-
for (let i = 0; i <
|
|
103
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) filePath = await resolve$1(base, `${urlPathBase}${suffixes[i]}${urlExt}`);
|
|
100
104
|
}
|
|
101
|
-
|
|
102
|
-
for (let i = 0; i < langs.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve$1(base, `${urlPath}.${langs[i]}.${exts[j]}`);
|
|
103
|
-
for (let i = 0; i < exts.length && filePath === null; i++) filePath = await resolve$1(base, `${urlPath}.${exts[i]}`);
|
|
105
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve$1(base, `${urlPath}${suffixes[i]}.${exts[j]}`);
|
|
104
106
|
}
|
|
105
|
-
for (let i = 0; i <
|
|
106
|
-
for (let i = 0; i <
|
|
107
|
-
for (let i = 0; i < exts.length && filePath === null; i++) filePath = await resolve$1(base, urlPath, `index.${exts[i]}`);
|
|
107
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve$1(base, `${urlPath}${suffixes[i]}`, `index.${exts[j]}`);
|
|
108
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) for (let j = 0; j < exts.length && filePath === null; j++) filePath = await resolve$1(base, urlPath, `index${suffixes[i]}.${exts[j]}`);
|
|
108
109
|
}
|
|
109
110
|
if (filePath === null) {
|
|
110
111
|
if (fallthrough) next();
|
|
@@ -136,6 +137,23 @@ const files = (params) => {
|
|
|
136
137
|
};
|
|
137
138
|
};
|
|
138
139
|
|
|
140
|
+
const redirect = (params) => {
|
|
141
|
+
let { url, status = 302 } = typeof params === "string" ? { url: params } : params;
|
|
142
|
+
return (req, res) => {
|
|
143
|
+
let search = req.originalUrl.split("?")[1] ?? "";
|
|
144
|
+
if (search) search = `${url.includes("?") ? "&" : "?"}${search}`;
|
|
145
|
+
emitLog(req.app, "Redirecting", {
|
|
146
|
+
data: {
|
|
147
|
+
from: req.originalUrl,
|
|
148
|
+
to: `${url}${search}`
|
|
149
|
+
},
|
|
150
|
+
req,
|
|
151
|
+
res
|
|
152
|
+
});
|
|
153
|
+
res.redirect(status, `${url}${search}`);
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
139
157
|
const unhandledError = () => async (err, req, res) => {
|
|
140
158
|
emitLog(req.app, "Unhandled error", {
|
|
141
159
|
level: "error",
|
|
@@ -651,4 +669,4 @@ function servePipeableStream(req, res) {
|
|
|
651
669
|
};
|
|
652
670
|
}
|
|
653
671
|
|
|
654
|
-
export { build, cli, createApp, cspNonce, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, renderStatus, requestEvents, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
|
|
672
|
+
export { build, cli, createApp, cspNonce, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, redirect, renderStatus, requestEvents, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
|
package/index.ts
CHANGED
package/package.json
CHANGED
package/src/controllers/files.ts
CHANGED
|
@@ -66,15 +66,23 @@ export type FilesParams = {
|
|
|
66
66
|
path?: string | ((req: Request) => string);
|
|
67
67
|
/** Specifies which paths should be accepted. */
|
|
68
68
|
matches?: StringMatcher;
|
|
69
|
+
/**
|
|
70
|
+
* @default ["html", "htm"]
|
|
71
|
+
*/
|
|
69
72
|
extensions?: string[];
|
|
70
73
|
languages?: (req: Request) => string[];
|
|
74
|
+
/**
|
|
75
|
+
* Assumed file language if unspecified in the file name.
|
|
76
|
+
*
|
|
77
|
+
* @default "en"
|
|
78
|
+
*/
|
|
79
|
+
defaultLanguage?: string | ((req: Request) => string);
|
|
71
80
|
transform?: TransformContent[];
|
|
72
81
|
fallthrough?: boolean;
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
const defaultExtensions = ["html", "htm"];
|
|
76
85
|
const defaultPath = (req: Request) => req.path;
|
|
77
|
-
const defaultLanguages = getLanguageList;
|
|
78
86
|
|
|
79
87
|
/**
|
|
80
88
|
* Serves files from the specified directory path or paths in a locale-aware
|
|
@@ -91,6 +99,11 @@ export const files: Controller<string | FilesParams> = (params) => {
|
|
|
91
99
|
let urlPath =
|
|
92
100
|
typeof p.path === "string" ? p.path : (p.path ?? defaultPath)(req);
|
|
93
101
|
|
|
102
|
+
let defaultLanguage =
|
|
103
|
+
typeof p.defaultLanguage === "function"
|
|
104
|
+
? p.defaultLanguage(req)
|
|
105
|
+
: (p.defaultLanguage ?? "en");
|
|
106
|
+
|
|
94
107
|
if (!matches(urlPath, p.matches)) {
|
|
95
108
|
if (fallthrough) next();
|
|
96
109
|
else {
|
|
@@ -125,67 +138,68 @@ export const files: Controller<string | FilesParams> = (params) => {
|
|
|
125
138
|
return;
|
|
126
139
|
}
|
|
127
140
|
|
|
128
|
-
let langs = (p.languages ?? defaultLanguages)(req);
|
|
129
141
|
let filePath: string | null = null;
|
|
130
142
|
let urlExt = extname(urlPath);
|
|
131
143
|
|
|
132
|
-
|
|
144
|
+
let langs = (p.languages ?? getLanguageList)(req);
|
|
145
|
+
let defaultLanguageIndex = langs.indexOf(defaultLanguage);
|
|
146
|
+
|
|
147
|
+
let suffixes = langs.map((s) => `.${s}`);
|
|
148
|
+
|
|
149
|
+
// Check the suffixless path right after the default language suffix.
|
|
150
|
+
if (defaultLanguageIndex === -1) suffixes.push("");
|
|
151
|
+
else suffixes.splice(defaultLanguageIndex + 1, 0, "");
|
|
152
|
+
|
|
153
|
+
// Example:
|
|
154
|
+
// path = /x, langs = [en, ru], default lang: en, exts = [html, htm]
|
|
133
155
|
for (let k = 0; k < bases.length && filePath === null; k++) {
|
|
134
156
|
let base = bases[k];
|
|
135
157
|
|
|
136
158
|
if (!urlPath.endsWith("/")) {
|
|
137
|
-
// /x.en /x.ru
|
|
138
|
-
for (let i = 0; i <
|
|
139
|
-
filePath = await resolve(base, `${urlPath}
|
|
159
|
+
// /x.en /x /x.ru
|
|
160
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++)
|
|
161
|
+
filePath = await resolve(base, `${urlPath}${suffixes[i]}`);
|
|
140
162
|
|
|
141
163
|
if (filePath === null && urlExt) {
|
|
142
164
|
let urlPathBase = urlPath.slice(0, -urlExt.length);
|
|
143
165
|
|
|
144
|
-
// /x.en.ext /x.ru.ext
|
|
145
|
-
for (let i = 0; i <
|
|
166
|
+
// /x.en.ext /x.ext /x.ru.ext
|
|
167
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++)
|
|
146
168
|
filePath = await resolve(
|
|
147
169
|
base,
|
|
148
|
-
`${urlPathBase}
|
|
170
|
+
`${urlPathBase}${suffixes[i]}${urlExt}`,
|
|
149
171
|
);
|
|
150
172
|
}
|
|
151
173
|
|
|
152
|
-
// /x
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// /x.en.html /x.en.htm /x.ru.html /x.ru.htm
|
|
156
|
-
for (let i = 0; i < langs.length && filePath === null; i++) {
|
|
174
|
+
// /x.en.html /x.en.htm /x.html /x.htm /x.ru.html /x.ru.htm
|
|
175
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) {
|
|
157
176
|
for (let j = 0; j < exts.length && filePath === null; j++)
|
|
158
|
-
filePath = await resolve(
|
|
177
|
+
filePath = await resolve(
|
|
178
|
+
base,
|
|
179
|
+
`${urlPath}${suffixes[i]}.${exts[j]}`,
|
|
180
|
+
);
|
|
159
181
|
}
|
|
160
|
-
|
|
161
|
-
// /x.html /x.htm
|
|
162
|
-
for (let i = 0; i < exts.length && filePath === null; i++)
|
|
163
|
-
filePath = await resolve(base, `${urlPath}.${exts[i]}`);
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
// /x.en/index.html /x.en/index.htm /x.ru/index.html /x.ru/index.htm
|
|
167
|
-
for (let i = 0; i <
|
|
184
|
+
// /x.en/index.html /x.en/index.htm /x/index.html /x/index.htm /x.ru/index.html /x.ru/index.htm
|
|
185
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) {
|
|
168
186
|
for (let j = 0; j < exts.length && filePath === null; j++)
|
|
169
187
|
filePath = await resolve(
|
|
170
188
|
base,
|
|
171
|
-
`${urlPath}
|
|
189
|
+
`${urlPath}${suffixes[i]}`,
|
|
172
190
|
`index.${exts[j]}`,
|
|
173
191
|
);
|
|
174
192
|
}
|
|
175
193
|
|
|
176
|
-
// /x/index.en.html /x/index.en.htm /x/index.ru.html /x/index.ru.htm
|
|
177
|
-
for (let i = 0; i <
|
|
194
|
+
// /x/index.en.html /x/index.en.htm /x/index.html /x/index.htm /x/index.ru.html /x/index.ru.htm
|
|
195
|
+
for (let i = 0; i < suffixes.length && filePath === null; i++) {
|
|
178
196
|
for (let j = 0; j < exts.length && filePath === null; j++)
|
|
179
197
|
filePath = await resolve(
|
|
180
198
|
base,
|
|
181
199
|
urlPath,
|
|
182
|
-
`index
|
|
200
|
+
`index${suffixes[i]}.${exts[j]}`,
|
|
183
201
|
);
|
|
184
202
|
}
|
|
185
|
-
|
|
186
|
-
// /x/index.html /x/index.htm
|
|
187
|
-
for (let i = 0; i < exts.length && filePath === null; i++)
|
|
188
|
-
filePath = await resolve(base, urlPath, `index.${exts[i]}`);
|
|
189
203
|
}
|
|
190
204
|
|
|
191
205
|
if (filePath === null) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Controller } from "../types/Controller.ts";
|
|
2
|
+
import { emitLog } from "../utils/emitLog.ts";
|
|
2
3
|
|
|
3
4
|
export type RedirectParams = {
|
|
4
5
|
url: string;
|
|
@@ -14,6 +15,15 @@ export const redirect: Controller<string | RedirectParams> = (params) => {
|
|
|
14
15
|
|
|
15
16
|
if (search) search = `${url.includes("?") ? "&" : "?"}${search}`;
|
|
16
17
|
|
|
18
|
+
emitLog(req.app, "Redirecting", {
|
|
19
|
+
data: {
|
|
20
|
+
from: req.originalUrl,
|
|
21
|
+
to: `${url}${search}`,
|
|
22
|
+
},
|
|
23
|
+
req,
|
|
24
|
+
res,
|
|
25
|
+
});
|
|
26
|
+
|
|
17
27
|
res.redirect(status, `${url}${search}`);
|
|
18
28
|
};
|
|
19
29
|
};
|