appstage 0.2.10 → 0.2.11

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 CHANGED
@@ -44,86 +44,6 @@ function emitLog(app, message, payload) {
44
44
  return app.events?.emit("log", normalizedPayload);
45
45
  }
46
46
 
47
- function toLanguage(locale) {
48
- return locale.split(/[-_]/)[0];
49
- }
50
-
51
- async function resolveFilePath({ name, dir = ".", lang, supportedLocales = [], ext, index }) {
52
- let cwd = process.cwd();
53
- let localeSet = new Set(supportedLocales);
54
- let langSet = new Set(supportedLocales.map(toLanguage));
55
- let availableNames = [name, ...[...localeSet, ...langSet].map((item) => `${name}.${item}`)];
56
- let preferredLangNames;
57
- if (lang && (!supportedLocales.length || localeSet.has(lang) || langSet.has(lang))) preferredLangNames = [`${name}.${lang}`, `${name}.${toLanguage(lang)}`];
58
- let names = new Set(preferredLangNames ? [...preferredLangNames, ...availableNames] : availableNames);
59
- let exts = Array.isArray(ext) ? ext : [ext];
60
- for (let item of names) for (let itemExt of exts) {
61
- let path = (0, node_path.join)(cwd, dir, `${item}${itemExt ? `.${itemExt}` : ""}`);
62
- try {
63
- await (0, node_fs_promises.access)(path);
64
- return path;
65
- } catch {}
66
- }
67
- if (index) for (let item of names) for (let itemExt of exts) {
68
- let path = (0, node_path.join)(cwd, dir, item, `index${itemExt ? `.${itemExt}` : ""}`);
69
- try {
70
- await (0, node_fs_promises.access)(path);
71
- return path;
72
- } catch {}
73
- }
74
- }
75
-
76
- const defaultExt = ["html", "htm"];
77
- const defaultName = (req) => req.path.split("/").at(-1);
78
- /**
79
- * Serves files from the specified directory path in a locale-aware
80
- * fashion after applying optional transforms.
81
- *
82
- * A file ending with `.<lang>.<ext>` is picked first if the `<lang>`
83
- * part matches `req.ctx.lang`. If the `supportedLocales` array is
84
- * provided, the `*.<lang>.<ext>` file is picked only if the given
85
- * array contains `req.ctx.lang`. Otherwise, a file without the locale
86
- * in its path (`*.<ext>`) is picked.
87
- */
88
- const dir = ({ path, name = defaultName, ext = defaultExt, transform, supportedLocales, index = true }) => {
89
- if (typeof path !== "string") throw new Error(`'path' is not a string`);
90
- let transformSet = (Array.isArray(transform) ? transform : [transform]).filter((item) => typeof item === "function");
91
- return async (req, res) => {
92
- let fileName = typeof name === "function" ? name(req, res) : name;
93
- emitLog(req.app, `Name: ${JSON.stringify(fileName)}`, {
94
- req,
95
- res
96
- });
97
- if (fileName === void 0) {
98
- res.status(404).send(await req.app.renderStatus?.(req, res));
99
- return;
100
- }
101
- let filePath = await resolveFilePath({
102
- name: fileName,
103
- dir: path,
104
- ext,
105
- supportedLocales,
106
- lang: req.ctx?.lang,
107
- index
108
- });
109
- emitLog(req.app, `Path: ${JSON.stringify(filePath)}`, {
110
- req,
111
- res
112
- });
113
- if (!filePath) {
114
- res.status(404).send(await req.app.renderStatus?.(req, res));
115
- return;
116
- }
117
- let content = (await (0, node_fs_promises.readFile)(filePath)).toString();
118
- for (let transformItem of transformSet) content = await transformItem(req, res, {
119
- content,
120
- path: filePath,
121
- name: (0, node_path.basename)(filePath, (0, node_path.extname)(filePath))
122
- });
123
- res.send(content);
124
- };
125
- };
126
-
127
47
  const maxLanguages = 3;
128
48
  async function resolve(...parts) {
129
49
  let fullPath = (0, node_path.join)(...parts);
@@ -194,16 +114,17 @@ const files = (params) => {
194
114
  return;
195
115
  }
196
116
  let content = (await (0, node_fs_promises.readFile)(filePath)).toString();
197
- let name = (0, node_path.basename)(filePath);
117
+ let ext = (0, node_path.extname)(filePath);
118
+ let name = (0, node_path.basename)(filePath, ext);
198
119
  for (let transform of p.transform) {
199
120
  let result = transform(req, res, {
200
121
  content,
201
- path,
122
+ path: filePath,
202
123
  name
203
124
  });
204
125
  content = result instanceof Promise ? await result : result;
205
126
  }
206
- res.type((0, node_path.extname)(name).slice(1)).send(content);
127
+ res.type(ext.slice(1)).send(content);
207
128
  };
208
129
  };
209
130
 
@@ -226,6 +147,10 @@ const unhandledRoute = () => async (req, res) => {
226
147
  res.status(404).send(await req.app.renderStatus?.(req, res, "unhandled_route"));
227
148
  };
228
149
 
150
+ function toLanguage(locale) {
151
+ return locale.split(/[-_]/)[0];
152
+ }
153
+
229
154
  function getEffectiveLocale(preferredLocales, supportedLocales) {
230
155
  if (!supportedLocales || supportedLocales.length === 0) return void 0;
231
156
  if (!preferredLocales || preferredLocales.length === 0) return supportedLocales[0];
@@ -720,7 +645,6 @@ exports.build = build;
720
645
  exports.cli = cli;
721
646
  exports.createApp = createApp;
722
647
  exports.cspNonce = cspNonce;
723
- exports.dir = dir;
724
648
  exports.emitLog = emitLog;
725
649
  exports.files = files;
726
650
  exports.getEffectiveLocale = getEffectiveLocale;
@@ -732,7 +656,6 @@ exports.lang = lang;
732
656
  exports.log = log;
733
657
  exports.renderStatus = renderStatus;
734
658
  exports.requestEvents = requestEvents;
735
- exports.resolveFilePath = resolveFilePath;
736
659
  exports.serializeState = serializeState;
737
660
  exports.servePipeableStream = servePipeableStream;
738
661
  exports.toLanguage = toLanguage;
package/dist/index.d.ts CHANGED
@@ -16,65 +16,6 @@ type TransformContent = (req: Request, res: Response, params: {
16
16
  name?: string;
17
17
  }) => string | Promise<string>;
18
18
 
19
- type ResolveFilePathParams = {
20
- name: string;
21
- dir?: string;
22
- lang?: string;
23
- supportedLocales?: string[]; /** Allowed file extensions. */
24
- ext?: string | string[];
25
- /**
26
- * Whether an index file should be checked if the resolved file name
27
- * doesn't correspond to an existing file.
28
- *
29
- * @defaultValue `true`
30
- */
31
- index?: boolean;
32
- };
33
- declare function resolveFilePath({
34
- name,
35
- dir,
36
- lang,
37
- supportedLocales,
38
- ext,
39
- index
40
- }: ResolveFilePathParams): Promise<string | undefined>;
41
-
42
- type ZeroTransform = false | null | undefined;
43
- type DirParams = Partial<Pick<ResolveFilePathParams, "supportedLocales" | "index">> & {
44
- /** Directory path to serve files from. */path: string;
45
- /**
46
- * File name.
47
- * By default, the portion of `req.path` after the last slash.
48
- */
49
- name?: string | undefined | ((req: Request, res: Response) => string | undefined);
50
- /**
51
- * Allowed file extensions.
52
- *
53
- * @defaultValue `['html', 'htm']`
54
- */
55
- ext?: ResolveFilePathParams["ext"];
56
- /**
57
- * Custom transforms applied to the file content.
58
- *
59
- * Example: Use `injectNonce` from this package to inject the `nonce`
60
- * value generated for the current request into the `{{nonce}}`
61
- * placeholders in an HTML file.
62
- */
63
- transform?: TransformContent | ZeroTransform | (TransformContent | ZeroTransform)[];
64
- supportedLocales?: string[];
65
- };
66
- /**
67
- * Serves files from the specified directory path in a locale-aware
68
- * fashion after applying optional transforms.
69
- *
70
- * A file ending with `.<lang>.<ext>` is picked first if the `<lang>`
71
- * part matches `req.ctx.lang`. If the `supportedLocales` array is
72
- * provided, the `*.<lang>.<ext>` file is picked only if the given
73
- * array contains `req.ctx.lang`. Otherwise, a file without the locale
74
- * in its path (`*.<ext>`) is picked.
75
- */
76
- declare const dir$1: Controller<DirParams>;
77
-
78
19
  type FilesParams = {
79
20
  base: string | string[];
80
21
  path?: string | ((req: Request) => string);
@@ -136,7 +77,7 @@ type LangParams = {
136
77
  shouldRedirect?: boolean;
137
78
  langCookieOptions?: CookieOptions;
138
79
  };
139
- declare const lang$1: Middleware<LangParams | void>;
80
+ declare const lang: Middleware<LangParams | void>;
140
81
 
141
82
  /**
142
83
  * Adds event handlers, like logging, to essential request phases.
@@ -1179,4 +1120,4 @@ declare function servePipeableStream(req: Request, res: Response): ({
1179
1120
  pipe
1180
1121
  }: PipeableStream, error?: unknown) => Promise<void>;
1181
1122
 
1182
- export { Controller, DirParams, ErrorController, FilesParams, LangParams, LogEventPayload, LogLevel, LogOptions, Middleware, MiddlewareSet, RenderStatus, ReqCtx, ResolveFilePathParams, TransformContent, build, cli, createApp, cspNonce, dir$1 as dir, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang$1 as lang, log, renderStatus, requestEvents, resolveFilePath, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
1123
+ 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 };
package/dist/index.mjs CHANGED
@@ -18,86 +18,6 @@ function emitLog(app, message, payload) {
18
18
  return app.events?.emit("log", normalizedPayload);
19
19
  }
20
20
 
21
- function toLanguage(locale) {
22
- return locale.split(/[-_]/)[0];
23
- }
24
-
25
- async function resolveFilePath({ name, dir = ".", lang, supportedLocales = [], ext, index }) {
26
- let cwd = process.cwd();
27
- let localeSet = new Set(supportedLocales);
28
- let langSet = new Set(supportedLocales.map(toLanguage));
29
- let availableNames = [name, ...[...localeSet, ...langSet].map((item) => `${name}.${item}`)];
30
- let preferredLangNames;
31
- if (lang && (!supportedLocales.length || localeSet.has(lang) || langSet.has(lang))) preferredLangNames = [`${name}.${lang}`, `${name}.${toLanguage(lang)}`];
32
- let names = new Set(preferredLangNames ? [...preferredLangNames, ...availableNames] : availableNames);
33
- let exts = Array.isArray(ext) ? ext : [ext];
34
- for (let item of names) for (let itemExt of exts) {
35
- let path = join(cwd, dir, `${item}${itemExt ? `.${itemExt}` : ""}`);
36
- try {
37
- await access(path);
38
- return path;
39
- } catch {}
40
- }
41
- if (index) for (let item of names) for (let itemExt of exts) {
42
- let path = join(cwd, dir, item, `index${itemExt ? `.${itemExt}` : ""}`);
43
- try {
44
- await access(path);
45
- return path;
46
- } catch {}
47
- }
48
- }
49
-
50
- const defaultExt = ["html", "htm"];
51
- const defaultName = (req) => req.path.split("/").at(-1);
52
- /**
53
- * Serves files from the specified directory path in a locale-aware
54
- * fashion after applying optional transforms.
55
- *
56
- * A file ending with `.<lang>.<ext>` is picked first if the `<lang>`
57
- * part matches `req.ctx.lang`. If the `supportedLocales` array is
58
- * provided, the `*.<lang>.<ext>` file is picked only if the given
59
- * array contains `req.ctx.lang`. Otherwise, a file without the locale
60
- * in its path (`*.<ext>`) is picked.
61
- */
62
- const dir = ({ path, name = defaultName, ext = defaultExt, transform, supportedLocales, index = true }) => {
63
- if (typeof path !== "string") throw new Error(`'path' is not a string`);
64
- let transformSet = (Array.isArray(transform) ? transform : [transform]).filter((item) => typeof item === "function");
65
- return async (req, res) => {
66
- let fileName = typeof name === "function" ? name(req, res) : name;
67
- emitLog(req.app, `Name: ${JSON.stringify(fileName)}`, {
68
- req,
69
- res
70
- });
71
- if (fileName === void 0) {
72
- res.status(404).send(await req.app.renderStatus?.(req, res));
73
- return;
74
- }
75
- let filePath = await resolveFilePath({
76
- name: fileName,
77
- dir: path,
78
- ext,
79
- supportedLocales,
80
- lang: req.ctx?.lang,
81
- index
82
- });
83
- emitLog(req.app, `Path: ${JSON.stringify(filePath)}`, {
84
- req,
85
- res
86
- });
87
- if (!filePath) {
88
- res.status(404).send(await req.app.renderStatus?.(req, res));
89
- return;
90
- }
91
- let content = (await readFile(filePath)).toString();
92
- for (let transformItem of transformSet) content = await transformItem(req, res, {
93
- content,
94
- path: filePath,
95
- name: basename(filePath, extname(filePath))
96
- });
97
- res.send(content);
98
- };
99
- };
100
-
101
21
  const maxLanguages = 3;
102
22
  async function resolve(...parts) {
103
23
  let fullPath = join(...parts);
@@ -168,16 +88,17 @@ const files = (params) => {
168
88
  return;
169
89
  }
170
90
  let content = (await readFile(filePath)).toString();
171
- let name = basename(filePath);
91
+ let ext = extname(filePath);
92
+ let name = basename(filePath, ext);
172
93
  for (let transform of p.transform) {
173
94
  let result = transform(req, res, {
174
95
  content,
175
- path,
96
+ path: filePath,
176
97
  name
177
98
  });
178
99
  content = result instanceof Promise ? await result : result;
179
100
  }
180
- res.type(extname(name).slice(1)).send(content);
101
+ res.type(ext.slice(1)).send(content);
181
102
  };
182
103
  };
183
104
 
@@ -200,6 +121,10 @@ const unhandledRoute = () => async (req, res) => {
200
121
  res.status(404).send(await req.app.renderStatus?.(req, res, "unhandled_route"));
201
122
  };
202
123
 
124
+ function toLanguage(locale) {
125
+ return locale.split(/[-_]/)[0];
126
+ }
127
+
203
128
  function getEffectiveLocale(preferredLocales, supportedLocales) {
204
129
  if (!supportedLocales || supportedLocales.length === 0) return void 0;
205
130
  if (!preferredLocales || preferredLocales.length === 0) return supportedLocales[0];
@@ -690,4 +615,4 @@ function servePipeableStream(req, res) {
690
615
  };
691
616
  }
692
617
 
693
- export { build, cli, createApp, cspNonce, dir, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, renderStatus, requestEvents, resolveFilePath, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
618
+ export { build, cli, createApp, cspNonce, emitLog, files, getEffectiveLocale, getLocales, getStatusMessage, init, injectNonce, lang, log, renderStatus, requestEvents, serializeState, servePipeableStream, toLanguage, unhandledError, unhandledRoute };
package/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./src/controllers/dir.ts";
2
1
  export * from "./src/controllers/files.ts";
3
2
  export * from "./src/controllers/unhandledError.ts";
4
3
  export * from "./src/controllers/unhandledRoute.ts";
@@ -27,6 +26,5 @@ export * from "./src/utils/emitLog.ts";
27
26
  export * from "./src/utils/getStatusMessage.ts";
28
27
  export * from "./src/utils/injectNonce.ts";
29
28
  export * from "./src/utils/renderStatus.ts";
30
- export * from "./src/utils/resolveFilePath.ts";
31
29
  export * from "./src/utils/serializeState.ts";
32
30
  export * from "./src/utils/servePipeableStream.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appstage",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -81,10 +81,10 @@ export const files: Controller<string | FilesParams> = (params) => {
81
81
  return;
82
82
  }
83
83
 
84
- // path: /x
85
- // langs: en, ru
86
84
  let filePath: string | null = null;
87
85
 
86
+ // path: /x
87
+ // langs: en, ru
88
88
  for (let k = 0; k < bases.length && filePath === null; k++) {
89
89
  let base = bases[k];
90
90
 
@@ -145,14 +145,15 @@ export const files: Controller<string | FilesParams> = (params) => {
145
145
  }
146
146
 
147
147
  let content = (await readFile(filePath)).toString();
148
- let name = basename(filePath);
148
+ let ext = extname(filePath);
149
+ let name = basename(filePath, ext);
149
150
 
150
151
  for (let transform of p.transform) {
151
- let result = transform(req, res, { content, path, name });
152
+ let result = transform(req, res, { content, path: filePath, name });
152
153
 
153
154
  content = result instanceof Promise ? await result : result;
154
155
  }
155
156
 
156
- res.type(extname(name).slice(1)).send(content);
157
+ res.type(ext.slice(1)).send(content);
157
158
  };
158
159
  };
@@ -1,119 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { basename, extname } from "node:path";
3
- import type { Request, Response } from "express";
4
- import type { Controller } from "../types/Controller.ts";
5
- import type { TransformContent } from "../types/TransformContent.ts";
6
- import { emitLog } from "../utils/emitLog.ts";
7
- import {
8
- type ResolveFilePathParams,
9
- resolveFilePath,
10
- } from "../utils/resolveFilePath.ts";
11
-
12
- const defaultExt = ["html", "htm"];
13
- const defaultName = (req: Request) => req.path.split("/").at(-1);
14
-
15
- type ZeroTransform = false | null | undefined;
16
-
17
- export type DirParams = Partial<
18
- Pick<ResolveFilePathParams, "supportedLocales" | "index">
19
- > & {
20
- /** Directory path to serve files from. */
21
- path: string;
22
- /**
23
- * File name.
24
- * By default, the portion of `req.path` after the last slash.
25
- */
26
- name?:
27
- | string
28
- | undefined
29
- | ((req: Request, res: Response) => string | undefined);
30
- /**
31
- * Allowed file extensions.
32
- *
33
- * @defaultValue `['html', 'htm']`
34
- */
35
- ext?: ResolveFilePathParams["ext"];
36
- /**
37
- * Custom transforms applied to the file content.
38
- *
39
- * Example: Use `injectNonce` from this package to inject the `nonce`
40
- * value generated for the current request into the `{{nonce}}`
41
- * placeholders in an HTML file.
42
- */
43
- transform?:
44
- | TransformContent
45
- | ZeroTransform
46
- | (TransformContent | ZeroTransform)[];
47
- supportedLocales?: string[];
48
- };
49
-
50
- /**
51
- * Serves files from the specified directory path in a locale-aware
52
- * fashion after applying optional transforms.
53
- *
54
- * A file ending with `.<lang>.<ext>` is picked first if the `<lang>`
55
- * part matches `req.ctx.lang`. If the `supportedLocales` array is
56
- * provided, the `*.<lang>.<ext>` file is picked only if the given
57
- * array contains `req.ctx.lang`. Otherwise, a file without the locale
58
- * in its path (`*.<ext>`) is picked.
59
- */
60
- export const dir: Controller<DirParams> = ({
61
- path,
62
- name = defaultName,
63
- ext = defaultExt,
64
- transform,
65
- supportedLocales,
66
- index = true,
67
- }) => {
68
- if (typeof path !== "string") throw new Error(`'path' is not a string`);
69
-
70
- let transformSet = (
71
- Array.isArray(transform) ? transform : [transform]
72
- ).filter((item) => typeof item === "function");
73
-
74
- return async (req, res) => {
75
- let fileName = typeof name === "function" ? name(req, res) : name;
76
-
77
- emitLog(req.app, `Name: ${JSON.stringify(fileName)}`, {
78
- req,
79
- res,
80
- });
81
-
82
- if (fileName === undefined) {
83
- res.status(404).send(await req.app.renderStatus?.(req, res));
84
-
85
- return;
86
- }
87
-
88
- let filePath = await resolveFilePath({
89
- name: fileName,
90
- dir: path,
91
- ext,
92
- supportedLocales,
93
- lang: req.ctx?.lang,
94
- index,
95
- });
96
-
97
- emitLog(req.app, `Path: ${JSON.stringify(filePath)}`, {
98
- req,
99
- res,
100
- });
101
-
102
- if (!filePath) {
103
- res.status(404).send(await req.app.renderStatus?.(req, res));
104
-
105
- return;
106
- }
107
-
108
- let content = (await readFile(filePath)).toString();
109
-
110
- for (let transformItem of transformSet)
111
- content = await transformItem(req, res, {
112
- content,
113
- path: filePath,
114
- name: basename(filePath, extname(filePath)),
115
- });
116
-
117
- res.send(content);
118
- };
119
- };
@@ -1,78 +0,0 @@
1
- import { access } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { toLanguage } from "../lib/lang/toLanguage.ts";
4
-
5
- export type ResolveFilePathParams = {
6
- name: string;
7
- dir?: string;
8
- lang?: string;
9
- supportedLocales?: string[];
10
- /** Allowed file extensions. */
11
- ext?: string | string[];
12
- /**
13
- * Whether an index file should be checked if the resolved file name
14
- * doesn't correspond to an existing file.
15
- *
16
- * @defaultValue `true`
17
- */
18
- index?: boolean;
19
- };
20
-
21
- export async function resolveFilePath({
22
- name,
23
- dir = ".",
24
- lang,
25
- supportedLocales = [],
26
- ext,
27
- index,
28
- }: ResolveFilePathParams) {
29
- let cwd = process.cwd();
30
-
31
- let localeSet = new Set(supportedLocales);
32
- let langSet = new Set(supportedLocales.map(toLanguage));
33
-
34
- let availableNames = [
35
- name,
36
- ...[...localeSet, ...langSet].map((item) => `${name}.${item}`),
37
- ];
38
-
39
- let preferredLangNames: string[] | undefined;
40
-
41
- if (
42
- lang &&
43
- (!supportedLocales.length || localeSet.has(lang) || langSet.has(lang))
44
- )
45
- preferredLangNames = [`${name}.${lang}`, `${name}.${toLanguage(lang)}`];
46
-
47
- let names = new Set(
48
- preferredLangNames
49
- ? [...preferredLangNames, ...availableNames]
50
- : availableNames,
51
- );
52
-
53
- let exts = Array.isArray(ext) ? ext : [ext];
54
-
55
- for (let item of names) {
56
- for (let itemExt of exts) {
57
- let path = join(cwd, dir, `${item}${itemExt ? `.${itemExt}` : ""}`);
58
-
59
- try {
60
- await access(path);
61
- return path;
62
- } catch {}
63
- }
64
- }
65
-
66
- if (index) {
67
- for (let item of names) {
68
- for (let itemExt of exts) {
69
- let path = join(cwd, dir, item, `index${itemExt ? `.${itemExt}` : ""}`);
70
-
71
- try {
72
- await access(path);
73
- return path;
74
- } catch {}
75
- }
76
- }
77
- }
78
- }