@mojojs/core 1.27.0 → 2.0.0

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.
Files changed (157) hide show
  1. package/README.md +1 -1
  2. package/lib/app.d.ts +24 -16
  3. package/lib/app.js +172 -126
  4. package/lib/body/params.js +0 -1
  5. package/lib/body.d.ts +13 -5
  6. package/lib/body.js +47 -17
  7. package/lib/cache.d.ts +1 -2
  8. package/lib/cache.js +12 -21
  9. package/lib/cgi.d.ts +3 -3
  10. package/lib/cgi.js +6 -5
  11. package/lib/cli/cgi.d.ts +1 -1
  12. package/lib/cli/cgi.js +1 -2
  13. package/lib/cli/create-full-app.d.ts +1 -1
  14. package/lib/cli/create-full-app.js +27 -27
  15. package/lib/cli/create-lite-app.d.ts +1 -1
  16. package/lib/cli/create-lite-app.js +2 -3
  17. package/lib/cli/create-plugin.d.ts +1 -1
  18. package/lib/cli/create-plugin.js +25 -23
  19. package/lib/cli/eval.d.ts +1 -1
  20. package/lib/cli/eval.js +12 -5
  21. package/lib/cli/get.d.ts +1 -1
  22. package/lib/cli/get.js +30 -26
  23. package/lib/cli/repl.d.ts +1 -1
  24. package/lib/cli/repl.js +0 -1
  25. package/lib/cli/routes.d.ts +1 -1
  26. package/lib/cli/routes.js +10 -7
  27. package/lib/cli/server.d.ts +1 -1
  28. package/lib/cli/server.js +41 -24
  29. package/lib/cli/version.js +1 -2
  30. package/lib/cli.d.ts +1 -1
  31. package/lib/cli.js +25 -19
  32. package/lib/context.d.ts +10 -10
  33. package/lib/context.js +94 -34
  34. package/lib/core.d.ts +14 -14
  35. package/lib/core.js +12 -13
  36. package/lib/headers.js +5 -4
  37. package/lib/hooks.d.ts +1 -1
  38. package/lib/hooks.js +1 -5
  39. package/lib/logger.d.ts +2 -2
  40. package/lib/logger.js +28 -16
  41. package/lib/mime.js +10 -13
  42. package/lib/plugins/default-conditions.d.ts +1 -1
  43. package/lib/plugins/default-conditions.js +0 -1
  44. package/lib/plugins/default-helpers.d.ts +1 -1
  45. package/lib/plugins/default-helpers.js +28 -11
  46. package/lib/plugins/json-config.d.ts +1 -1
  47. package/lib/plugins/json-config.js +0 -1
  48. package/lib/plugins/mount.d.ts +1 -1
  49. package/lib/plugins/mount.js +0 -1
  50. package/lib/plugins/tmpl-engine.d.ts +1 -1
  51. package/lib/plugins/tmpl-engine.js +2 -5
  52. package/lib/plugins/yaml-config.d.ts +1 -1
  53. package/lib/plugins/yaml-config.js +1 -2
  54. package/lib/renderer.d.ts +1 -1
  55. package/lib/renderer.js +27 -30
  56. package/lib/router/pattern.d.ts +1 -1
  57. package/lib/router/pattern.js +25 -14
  58. package/lib/router/plan.d.ts +1 -1
  59. package/lib/router/plan.js +12 -15
  60. package/lib/router/route.d.ts +3 -3
  61. package/lib/router/route.js +35 -38
  62. package/lib/router.d.ts +4 -4
  63. package/lib/router.js +25 -26
  64. package/lib/server/cookie.d.ts +1 -1
  65. package/lib/server/cookie.js +1 -2
  66. package/lib/server/request.d.ts +3 -3
  67. package/lib/server/request.js +44 -17
  68. package/lib/server/response.d.ts +3 -3
  69. package/lib/server/response.js +20 -20
  70. package/lib/server.d.ts +7 -2
  71. package/lib/server.js +69 -19
  72. package/lib/session.d.ts +3 -3
  73. package/lib/session.js +46 -34
  74. package/lib/sse.d.ts +1 -1
  75. package/lib/sse.js +0 -1
  76. package/lib/static.d.ts +1 -1
  77. package/lib/static.js +27 -22
  78. package/lib/types.d.ts +6 -5
  79. package/lib/types.js +0 -1
  80. package/lib/user-agent/cookie-jar.d.ts +2 -2
  81. package/lib/user-agent/cookie-jar.js +6 -9
  82. package/lib/user-agent/mock.d.ts +4 -4
  83. package/lib/user-agent/mock.js +6 -7
  84. package/lib/user-agent/response.d.ts +2 -2
  85. package/lib/user-agent/response.js +14 -3
  86. package/lib/user-agent/test.d.ts +5 -5
  87. package/lib/user-agent/test.js +14 -15
  88. package/lib/user-agent/transport/http.d.ts +2 -2
  89. package/lib/user-agent/transport/http.js +7 -10
  90. package/lib/user-agent/transport/https.d.ts +2 -2
  91. package/lib/user-agent/transport/https.js +2 -6
  92. package/lib/user-agent/transport/ws.d.ts +2 -2
  93. package/lib/user-agent/transport/ws.js +2 -3
  94. package/lib/user-agent.d.ts +18 -19
  95. package/lib/user-agent.js +64 -49
  96. package/lib/util.d.ts +13 -0
  97. package/lib/util.js +35 -11
  98. package/lib/validator/result.d.ts +1 -1
  99. package/lib/validator/result.js +8 -1
  100. package/lib/validator.d.ts +2 -2
  101. package/lib/validator.js +3 -6
  102. package/lib/websocket.d.ts +2 -2
  103. package/lib/websocket.js +9 -1
  104. package/package.json +22 -29
  105. package/lib/app.js.map +0 -1
  106. package/lib/body/params.js.map +0 -1
  107. package/lib/body.js.map +0 -1
  108. package/lib/cache.js.map +0 -1
  109. package/lib/cgi.js.map +0 -1
  110. package/lib/cli/cgi.js.map +0 -1
  111. package/lib/cli/create-full-app.js.map +0 -1
  112. package/lib/cli/create-lite-app.js.map +0 -1
  113. package/lib/cli/create-plugin.js.map +0 -1
  114. package/lib/cli/eval.js.map +0 -1
  115. package/lib/cli/get.js.map +0 -1
  116. package/lib/cli/repl.js.map +0 -1
  117. package/lib/cli/routes.js.map +0 -1
  118. package/lib/cli/server.js.map +0 -1
  119. package/lib/cli/version.js.map +0 -1
  120. package/lib/cli.js.map +0 -1
  121. package/lib/context.js.map +0 -1
  122. package/lib/core.js.map +0 -1
  123. package/lib/headers.js.map +0 -1
  124. package/lib/hooks.js.map +0 -1
  125. package/lib/logger.js.map +0 -1
  126. package/lib/mime.js.map +0 -1
  127. package/lib/plugins/default-conditions.js.map +0 -1
  128. package/lib/plugins/default-helpers.js.map +0 -1
  129. package/lib/plugins/json-config.js.map +0 -1
  130. package/lib/plugins/mount.js.map +0 -1
  131. package/lib/plugins/tmpl-engine.js.map +0 -1
  132. package/lib/plugins/yaml-config.js.map +0 -1
  133. package/lib/renderer.js.map +0 -1
  134. package/lib/router/pattern.js.map +0 -1
  135. package/lib/router/plan.js.map +0 -1
  136. package/lib/router/route.js.map +0 -1
  137. package/lib/router.js.map +0 -1
  138. package/lib/server/cookie.js.map +0 -1
  139. package/lib/server/request.js.map +0 -1
  140. package/lib/server/response.js.map +0 -1
  141. package/lib/server.js.map +0 -1
  142. package/lib/session.js.map +0 -1
  143. package/lib/sse.js.map +0 -1
  144. package/lib/static.js.map +0 -1
  145. package/lib/types.js.map +0 -1
  146. package/lib/user-agent/cookie-jar.js.map +0 -1
  147. package/lib/user-agent/mock.js.map +0 -1
  148. package/lib/user-agent/response.js.map +0 -1
  149. package/lib/user-agent/test.js.map +0 -1
  150. package/lib/user-agent/transport/http.js.map +0 -1
  151. package/lib/user-agent/transport/https.js.map +0 -1
  152. package/lib/user-agent/transport/ws.js.map +0 -1
  153. package/lib/user-agent.js.map +0 -1
  154. package/lib/util.js.map +0 -1
  155. package/lib/validator/result.js.map +0 -1
  156. package/lib/validator.js.map +0 -1
  157. package/lib/websocket.js.map +0 -1
package/lib/context.js CHANGED
@@ -1,49 +1,110 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import querystring from 'node:querystring';
3
3
  import { PassThrough } from 'node:stream';
4
- import { Params } from './body/params.js';
5
- import { buildEvent } from './sse.js';
6
- import { SafeString } from './util.js';
4
+ import { Params } from "./body/params.js";
5
+ import { buildEvent } from "./sse.js";
6
+ import { SafeString } from "./util.js";
7
7
  const ABSOLUTE = /^[a-zA-Z][a-zA-Z0-9]*:\/\//;
8
8
  /**
9
9
  * Context class.
10
10
  */
11
11
  class Context extends EventEmitter {
12
+ /**
13
+ * Application this context belongs to.
14
+ */
15
+ app;
16
+ /**
17
+ * Backend information for server that received the request.
18
+ * @example
19
+ * // Backend name (usually "server", "cgi", or "mock")
20
+ * const {name} = ctx.backend;
21
+ *
22
+ * // Use low level Node.js APIs with "server" backend
23
+ * const {res} = ctx.backend;
24
+ * res.writeHead(200);
25
+ * res.end('Hello World!');
26
+ */
27
+ backend;
28
+ /**
29
+ * Partial content. Always returns at least an empty string, even for sections that have not been defined with
30
+ * `ctx.contentFor()`.
31
+ * @example
32
+ * // Get content for "head" section
33
+ * const head = ctx.content.head;
34
+ */
35
+ content = new Proxy({}, {
36
+ get: function (target, name) {
37
+ return new SafeString(target[name] ?? '');
38
+ }
39
+ });
40
+ /**
41
+ * Format for HTTP exceptions ("html", "json", or "txt").
42
+ */
43
+ exceptionFormat;
44
+ /**
45
+ * WebSocket JSON mode.
46
+ */
47
+ jsonMode = false;
48
+ /**
49
+ * Logger with request id.
50
+ * @example
51
+ * // Pass logger with context to model
52
+ * const log = ctx.log;
53
+ * ctx.model.users.create({name: 'kraih'}, log);
54
+ */
55
+ log;
56
+ /**
57
+ * Router dispatch plan.
58
+ */
59
+ plan = null;
60
+ /**
61
+ * HTTP request information.
62
+ * @example
63
+ * // Extract request information
64
+ * const id = ctx.req.requestId;
65
+ * const method = ctx.req.method;
66
+ * const baseURL = ctx.req.baseURL;
67
+ * const url = ctx.req.url;
68
+ * const query = ctx.req.query;
69
+ * const info = ctx.req.userinfo;
70
+ * const agent = ctx.req.get('User-Agent');
71
+ * const buffer = await ctx.req.buffer();
72
+ * const text = await ctx.req.text();
73
+ * const form = await ctx.req.form();
74
+ * const value = await ctx.req.json();
75
+ * const html = await ctx.req.html();
76
+ * const xml = await ctx.req.xml();
77
+ */
78
+ req;
79
+ /**
80
+ * HTTP response information.
81
+ * @example
82
+ * // Force file download by setting a response header
83
+ * ctx.res.set('Content-Disposition', 'attachment; filename=foo.png;');
84
+ *
85
+ * // Make sure response is cached correctly
86
+ * ctx.res.set('Cache-Control', 'public, max-age=300');
87
+ * ctx.res.headers.append('Vary', 'Accept-Encoding');
88
+ */
89
+ res;
90
+ /**
91
+ * Non-persistent data storage and exchange for the current request.
92
+ */
93
+ stash = {};
94
+ _flash = undefined;
95
+ _nestedHelpersCache = {};
96
+ _params = undefined;
97
+ _session = undefined;
98
+ _sse = undefined;
99
+ _ws = null;
12
100
  constructor(app, req, res, backend) {
13
101
  super({ captureRejections: true });
14
- /**
15
- * Partial content. Always returns at least an empty string, even for sections that have not been defined with
16
- * `ctx.contentFor()`.
17
- * @example
18
- * // Get content for "head" section
19
- * const head = ctx.content.head;
20
- */
21
- this.content = new Proxy({}, {
22
- get: function (target, name) {
23
- return new SafeString(target[name] ?? '');
24
- }
25
- });
26
- /**
27
- * WebSocket JSON mode.
28
- */
29
- this.jsonMode = false;
30
- /**
31
- * Router dispatch plan.
32
- */
33
- this.plan = null;
34
- /**
35
- * Non-persistent data storage and exchange for the current request.
36
- */
37
- this.stash = {};
38
- this._flash = undefined;
39
- this._nestedHelpersCache = {};
40
- this._params = undefined;
41
- this._session = undefined;
42
- this._sse = undefined;
43
- this._ws = null;
44
102
  this.app = app;
45
103
  this.exceptionFormat = app.exceptionFormat;
46
104
  this.req = req;
105
+ // Mounted applications share the request, and the limit of the outer one
106
+ if (req.maxMessageSize === undefined)
107
+ req.maxMessageSize = app.maxRequestSize;
47
108
  this.res = res;
48
109
  this.res.bindContext(this);
49
110
  this.backend = backend;
@@ -421,4 +482,3 @@ class Context extends EventEmitter {
421
482
  }
422
483
  }
423
484
  export { Context };
424
- //# sourceMappingURL=context.js.map
package/lib/core.d.ts CHANGED
@@ -3,22 +3,22 @@
3
3
  * Copyright (C) 2021-2024 Sebastian Riedel
4
4
  * MIT Licensed
5
5
  */
6
- import type { AppOptions } from './types.js';
7
- import { App } from './app.js';
6
+ import type { AppOptions } from './types.ts';
7
+ import { App } from './app.ts';
8
8
  export declare const version: any;
9
9
  /**
10
10
  * Create a new mojo.js application.
11
11
  */
12
12
  export default function mojo(options?: AppOptions): App;
13
- export { default as jsonConfigPlugin } from './plugins/json-config.js';
14
- export { default as yamlConfigPlugin } from './plugins/yaml-config.js';
15
- export { default as mountPlugin } from './plugins/mount.js';
16
- export { CGI } from './cgi.js';
17
- export { Logger } from './logger.js';
18
- export { Server } from './server.js';
19
- export { Session } from './session.js';
20
- export { buildEvent, parseEvent } from './sse.js';
21
- export { TestUserAgent } from './user-agent/test.js';
22
- export { UserAgent } from './user-agent.js';
23
- export * as util from './util.js';
24
- export { JSONValue, MojoAction, MojoApp, MojoContext, MojoModels, MojoRenderOptions, MojoRoute, MojoTags, MojoURLOptions, SSEEvent } from './types.js';
13
+ export { default as jsonConfigPlugin } from './plugins/json-config.ts';
14
+ export { default as yamlConfigPlugin } from './plugins/yaml-config.ts';
15
+ export { default as mountPlugin } from './plugins/mount.ts';
16
+ export { CGI } from './cgi.ts';
17
+ export { Logger } from './logger.ts';
18
+ export { Server } from './server.ts';
19
+ export { Session } from './session.ts';
20
+ export { buildEvent, parseEvent } from './sse.ts';
21
+ export { TestUserAgent } from './user-agent/test.ts';
22
+ export { UserAgent } from './user-agent.ts';
23
+ export * as util from './util.ts';
24
+ export type { JSONValue, MojoAction, MojoApp, MojoContext, MojoModels, MojoRenderOptions, MojoRoute, MojoTags, MojoURLOptions, SSEEvent } from './types.ts';
package/lib/core.js CHANGED
@@ -1,4 +1,4 @@
1
- import { App } from './app.js';
1
+ import { App } from "./app.js";
2
2
  import Path from '@mojojs/path';
3
3
  export const version = JSON.parse(Path.currentFile().dirname().sibling('package.json').readFileSync().toString()).version;
4
4
  /**
@@ -22,15 +22,14 @@ export default function mojo(options) {
22
22
  return app;
23
23
  }
24
24
  // "Professor: These old Doomsday devices are dangerously unstable. I'll rest easier not knowing where they are."
25
- export { default as jsonConfigPlugin } from './plugins/json-config.js';
26
- export { default as yamlConfigPlugin } from './plugins/yaml-config.js';
27
- export { default as mountPlugin } from './plugins/mount.js';
28
- export { CGI } from './cgi.js';
29
- export { Logger } from './logger.js';
30
- export { Server } from './server.js';
31
- export { Session } from './session.js';
32
- export { buildEvent, parseEvent } from './sse.js';
33
- export { TestUserAgent } from './user-agent/test.js';
34
- export { UserAgent } from './user-agent.js';
35
- export * as util from './util.js';
36
- //# sourceMappingURL=core.js.map
25
+ export { default as jsonConfigPlugin } from "./plugins/json-config.js";
26
+ export { default as yamlConfigPlugin } from "./plugins/yaml-config.js";
27
+ export { default as mountPlugin } from "./plugins/mount.js";
28
+ export { CGI } from "./cgi.js";
29
+ export { Logger } from "./logger.js";
30
+ export { Server } from "./server.js";
31
+ export { Session } from "./session.js";
32
+ export { buildEvent, parseEvent } from "./sse.js";
33
+ export { TestUserAgent } from "./user-agent/test.js";
34
+ export { UserAgent } from "./user-agent.js";
35
+ export * as util from "./util.js";
package/lib/headers.js CHANGED
@@ -13,6 +13,8 @@ const HOP_BY_HOP = [
13
13
  * HTTP header class.
14
14
  */
15
15
  export class Headers {
16
+ _headers;
17
+ _init;
16
18
  constructor(init = []) {
17
19
  this._init = init;
18
20
  }
@@ -70,7 +72,6 @@ export class Headers {
70
72
  * const {link, title} = headers.getLinks().next;
71
73
  */
72
74
  getLinks() {
73
- var _a;
74
75
  const data = {};
75
76
  let value = this.get('Link') ?? '';
76
77
  while (value.length > 0) {
@@ -81,7 +82,7 @@ export class Headers {
81
82
  const { params, remainder } = headerParams(linkMatch[2]);
82
83
  value = remainder;
83
84
  if (params.rel !== undefined)
84
- data[_a = params.rel] ?? (data[_a] = { ...params, link });
85
+ data[params.rel] ??= { ...params, link };
85
86
  }
86
87
  return data;
87
88
  }
@@ -149,7 +150,8 @@ export class Headers {
149
150
  }
150
151
  _getHeaders() {
151
152
  if (this._headers === undefined) {
152
- const headers = (this._headers = {});
153
+ // No prototype, so names like "constructor" are just regular headers
154
+ const headers = (this._headers = Object.create(null));
153
155
  const init = this._init;
154
156
  for (let i = 0; i < init.length; i += 2) {
155
157
  const name = init[i];
@@ -166,4 +168,3 @@ export class Headers {
166
168
  return this._headers;
167
169
  }
168
170
  }
169
- //# sourceMappingURL=headers.js.map
package/lib/hooks.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { MojoApp } from './types.js';
1
+ import type { MojoApp } from './types.ts';
2
2
  import { AsyncHooks } from '@mojojs/util';
3
3
  /**
4
4
  * Hook class.
package/lib/hooks.js CHANGED
@@ -3,10 +3,7 @@ import { AsyncHooks } from '@mojojs/util';
3
3
  * Hook class.
4
4
  */
5
5
  export class Hooks extends AsyncHooks {
6
- constructor() {
7
- super(...arguments);
8
- this._lifecycleHookScore = 0;
9
- }
6
+ _lifecycleHookScore = 0;
10
7
  /**
11
8
  * Run `command:before` hook with dependencies. Note that this method is EXPERIMENTAL and might change without
12
9
  * warning!
@@ -49,4 +46,3 @@ export class Hooks extends AsyncHooks {
49
46
  await this.runHook('app:stop', app);
50
47
  }
51
48
  }
52
- //# sourceMappingURL=hooks.js.map
package/lib/logger.d.ts CHANGED
@@ -8,7 +8,7 @@ interface LogEvent extends LogContext {
8
8
  time: string;
9
9
  msg: string;
10
10
  }
11
- type LogFormatter = (data: LogEvent) => string;
11
+ type LogFormatter = (data: LogEvent, destination: NodeJS.WritableStream) => string;
12
12
  /**
13
13
  * A simple logger class.
14
14
  */
@@ -45,7 +45,7 @@ export declare class Logger {
45
45
  /**
46
46
  * Log formatter with color highlighting.
47
47
  */
48
- static colorFormatter(data: LogEvent): string;
48
+ static colorFormatter(data: LogEvent, destination?: NodeJS.WritableStream): string;
49
49
  /**
50
50
  * Log `debug` message.
51
51
  */
package/lib/logger.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import assert from 'node:assert';
2
- import chalk from 'chalk';
2
+ import { styleText } from 'node:util';
3
3
  const LEVEL = {
4
4
  fatal: 2,
5
5
  error: 3,
@@ -12,13 +12,22 @@ const LEVEL = {
12
12
  * A simple logger class.
13
13
  */
14
14
  export class Logger {
15
+ /**
16
+ * Log destination stream.
17
+ */
18
+ destination;
19
+ /**
20
+ * Log formatter.
21
+ */
22
+ formatter;
23
+ /**
24
+ * The last few logged messages.
25
+ */
26
+ history = [];
27
+ _capture = undefined;
28
+ _historySize;
29
+ _level = 7;
15
30
  constructor(options = {}) {
16
- /**
17
- * The last few logged messages.
18
- */
19
- this.history = [];
20
- this._capture = undefined;
21
- this._level = 7;
22
31
  this.destination = options.destination ?? process.stderr;
23
32
  this.formatter = options.formatter ?? Logger.colorFormatter;
24
33
  this.level = process.env.MOJO_LOG_LEVEL ?? options.level ?? 'trace';
@@ -47,13 +56,14 @@ export class Logger {
47
56
  /**
48
57
  * Log formatter with color highlighting.
49
58
  */
50
- static colorFormatter(data) {
59
+ static colorFormatter(data, destination = process.stderr) {
51
60
  const formatted = Logger.stringFormatter(data);
52
- if (data.level === 'error' || data.level === 'fatal')
53
- return chalk.red(formatted);
54
- if (data.level === 'warn')
55
- return chalk.yellow(formatted);
56
- return formatted;
61
+ const { level } = data;
62
+ const color = level === 'error' || level === 'fatal' ? 'red' : level === 'warn' ? 'yellow' : null;
63
+ if (color === null)
64
+ return formatted;
65
+ // Keep the trailing newline outside of the color codes, and only use colors if the destination supports them
66
+ return styleText(color, formatted.slice(0, -1), { stream: destination }) + '\n';
57
67
  }
58
68
  /**
59
69
  * Log `debug` message.
@@ -139,7 +149,7 @@ export class Logger {
139
149
  if (historySize > maxHistorySize)
140
150
  history.splice(0, historySize - maxHistorySize);
141
151
  }
142
- const formatted = this.formatter(data);
152
+ const formatted = this.formatter(data, this.destination);
143
153
  if (this._capture === undefined) {
144
154
  this.destination.write(formatted);
145
155
  }
@@ -152,9 +162,10 @@ export class Logger {
152
162
  * Captured log message class.
153
163
  */
154
164
  class CapturedLogs extends Array {
165
+ _cb;
166
+ _stopped = false;
155
167
  constructor(cb) {
156
168
  super();
157
- this._stopped = false;
158
169
  this._cb = cb;
159
170
  }
160
171
  /**
@@ -177,6 +188,8 @@ class CapturedLogs extends Array {
177
188
  * Child logger class.
178
189
  */
179
190
  export class ChildLogger {
191
+ parent;
192
+ context;
180
193
  constructor(parent, context) {
181
194
  this.parent = parent;
182
195
  this.context = context;
@@ -218,4 +231,3 @@ export class ChildLogger {
218
231
  this.parent.warn(msg, this.context);
219
232
  }
220
233
  }
221
- //# sourceMappingURL=logger.js.map
package/lib/mime.js CHANGED
@@ -3,18 +3,16 @@ import mime from 'mime-types';
3
3
  * MIME class.
4
4
  */
5
5
  export class Mime {
6
- constructor() {
7
- /**
8
- * Custom MIME types.
9
- */
10
- this.custom = {
11
- html: 'text/html; charset=utf-8',
12
- json: 'application/json; charset=utf-8',
13
- txt: 'text/plain; charset=utf-8',
14
- yaml: 'text/yaml; charset=utf-8'
15
- };
16
- this._reverseCustom = undefined;
17
- }
6
+ /**
7
+ * Custom MIME types.
8
+ */
9
+ custom = {
10
+ html: 'text/html; charset=utf-8',
11
+ json: 'application/json; charset=utf-8',
12
+ txt: 'text/plain; charset=utf-8',
13
+ yaml: 'text/yaml; charset=utf-8'
14
+ };
15
+ _reverseCustom = undefined;
18
16
  /**
19
17
  * Detect file extensions from `Accept` header value.
20
18
  */
@@ -50,4 +48,3 @@ export class Mime {
50
48
  return this.custom[ext] ?? mime.types[ext] ?? null;
51
49
  }
52
50
  }
53
- //# sourceMappingURL=mime.js.map
@@ -1,4 +1,4 @@
1
- import type { App } from '../app.js';
1
+ import type { App } from '../app.ts';
2
2
  /**
3
3
  * Default condition plugin.
4
4
  */
@@ -19,4 +19,3 @@ async function headerCondition(ctx, requirement) {
19
19
  async function hostCondition(ctx, requirement) {
20
20
  return headerCondition(ctx, { Host: requirement });
21
21
  }
22
- //# sourceMappingURL=default-conditions.js.map
@@ -1,4 +1,4 @@
1
- import type { MojoApp } from '../types.js';
1
+ import type { MojoApp } from '../types.ts';
2
2
  /**
3
3
  * Default helper plugin.
4
4
  */
@@ -1,6 +1,6 @@
1
1
  import { inspect } from 'node:util';
2
- import { Logger } from '../logger.js';
3
- import { exceptionContext, SafeString } from '../util.js';
2
+ import { Logger } from "../logger.js";
3
+ import { exceptionContext, HTTPError, httpStatusMessages, SafeString } from "../util.js";
4
4
  import DOM from '@mojojs/dom';
5
5
  /**
6
6
  * Default helper plugin.
@@ -140,12 +140,13 @@ async function hiddenFieldTag(ctx, name, value, attrs = {}) {
140
140
  async function htmlException(ctx, error) {
141
141
  ctx.stash.exception = error;
142
142
  const mode = ctx.app.mode;
143
- if (await ctx.render({ view: `exception.${mode}`, maybe: true, status: 500 }))
143
+ const status = errorStatus(error);
144
+ if (await ctx.render({ view: `exception.${mode}`, maybe: true, status }))
144
145
  return true;
145
- if (await ctx.render({ view: 'exception', maybe: true, status: 500 }))
146
+ if (await ctx.render({ view: 'exception', maybe: true, status }))
146
147
  return true;
147
148
  const view = mode === 'development' ? 'mojo/debug' : 'mojo/exception';
148
- return await ctx.render({ view, status: 500, stringFormatter: Logger.stringFormatter, exceptionContext });
149
+ return await ctx.render({ view, status, stringFormatter: Logger.stringFormatter, exceptionContext });
149
150
  }
150
151
  async function htmlNotFound(ctx) {
151
152
  ctx.stash.exception = null;
@@ -159,7 +160,15 @@ async function htmlNotFound(ctx) {
159
160
  }
160
161
  async function httpException(ctx, error) {
161
162
  error = ensureError(error);
162
- ctx.log.error(error.stack);
163
+ // Client errors are not worth a stack trace
164
+ const status = errorStatus(error);
165
+ if (status < 500)
166
+ ctx.log.debug(error.message);
167
+ else
168
+ ctx.log.error(error.stack);
169
+ // Do not wait for the rest of a request body that is too large
170
+ if (status === 413)
171
+ ctx.res.set('Connection', 'close');
163
172
  const exceptionFormat = ctx.exceptionFormat;
164
173
  if (exceptionFormat === 'txt')
165
174
  return ctx.txtException(error);
@@ -195,6 +204,7 @@ async function include(ctx, options, stash) {
195
204
  return str === null ? null : new SafeString(str);
196
205
  }
197
206
  async function jsonException(ctx, error) {
207
+ const status = errorStatus(error);
198
208
  if (ctx.app.mode === 'development') {
199
209
  return await ctx.render({
200
210
  json: {
@@ -205,10 +215,10 @@ async function jsonException(ctx, error) {
205
215
  }
206
216
  },
207
217
  pretty: true,
208
- status: 500
218
+ status
209
219
  });
210
220
  }
211
- return await ctx.render({ json: { error: { message: 'Internal Server Error' } }, pretty: true, status: 500 });
221
+ return await ctx.render({ json: { error: { message: httpStatusMessages[status] } }, pretty: true, status });
212
222
  }
213
223
  async function jsonNotFound(ctx) {
214
224
  return await ctx.render({ json: { error: { message: 'Not Found' } }, pretty: true, status: 404 });
@@ -276,10 +286,11 @@ async function textAreaTag(ctx, name, attrs = {}, content) {
276
286
  return ctx.tag('textarea', { ...attrs, name }, content);
277
287
  }
278
288
  async function txtException(ctx, error) {
289
+ const status = errorStatus(error);
279
290
  if (ctx.app.mode === 'development') {
280
- return await ctx.render({ text: error.stack, status: 500 });
291
+ return await ctx.render({ text: error.stack, status });
281
292
  }
282
- return await ctx.render({ text: 'Internal Server Error', status: 500 });
293
+ return await ctx.render({ text: httpStatusMessages[status], status });
283
294
  }
284
295
  async function txtNotFound(ctx) {
285
296
  return await ctx.render({ text: 'Not Found', status: 404 });
@@ -299,4 +310,10 @@ function urlTarget(target) {
299
310
  function ensureError(error) {
300
311
  return error instanceof Error ? error : new Error(error);
301
312
  }
302
- //# sourceMappingURL=default-helpers.js.map
313
+ // Only our own errors, a "status" property from other libraries could hide real server errors
314
+ function errorStatus(error) {
315
+ if (!(error instanceof HTTPError))
316
+ return 500;
317
+ const { status } = error;
318
+ return Number.isInteger(status) && status >= 400 && status <= 599 ? status : 500;
319
+ }
@@ -1,4 +1,4 @@
1
- import type { ConfigOptions, MojoApp } from '../types.js';
1
+ import type { ConfigOptions, MojoApp } from '../types.ts';
2
2
  /**
3
3
  * JSON config plugin.
4
4
  */
@@ -26,4 +26,3 @@ export function loadConfig(app, options, parser) {
26
26
  log.trace(`Mode specific config file "${filePath.toString()}" loaded`);
27
27
  }
28
28
  }
29
- //# sourceMappingURL=json-config.js.map
@@ -1,4 +1,4 @@
1
- import type { MojoApp, MojoRoute, ConfigOptions } from '../types.js';
1
+ import type { MojoApp, MojoRoute, ConfigOptions } from '../types.ts';
2
2
  interface MountOptions extends ConfigOptions {
3
3
  app: MojoApp;
4
4
  host?: RegExp;
@@ -39,4 +39,3 @@ export default function mountPlugin(app, options) {
39
39
  route.requires('host', host);
40
40
  return route;
41
41
  }
42
- //# sourceMappingURL=mount.js.map
@@ -1,4 +1,4 @@
1
- import type { App } from '../app.js';
1
+ import type { App } from '../app.ts';
2
2
  /**
3
3
  * `@mojojs/template` engine plugin.
4
4
  */
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { Cache } from '../cache.js';
2
+ import { Cache } from "../cache.js";
3
3
  import Path from '@mojojs/path';
4
4
  import Template from '@mojojs/template';
5
5
  /**
@@ -9,9 +9,7 @@ export default function tmplEnginePlugin(app) {
9
9
  app.renderer.addEngine('tmpl', new TmplEngine());
10
10
  }
11
11
  class TmplEngine {
12
- constructor() {
13
- this.cache = new Cache();
14
- }
12
+ cache = new Cache();
15
13
  async render(ctx, options) {
16
14
  let template;
17
15
  if (options.inline !== undefined) {
@@ -35,4 +33,3 @@ class TmplEngine {
35
33
  return Buffer.from(await template({ ...ctx.stash, stash: ctx.stash, ctx, view: options, tags: ctx.tags }));
36
34
  }
37
35
  }
38
- //# sourceMappingURL=tmpl-engine.js.map
@@ -1,4 +1,4 @@
1
- import type { MojoApp, ConfigOptions } from '../types.js';
1
+ import type { MojoApp, ConfigOptions } from '../types.ts';
2
2
  /**
3
3
  * YAML config plugin.
4
4
  */
@@ -1,4 +1,4 @@
1
- import { loadConfig } from './json-config.js';
1
+ import { loadConfig } from "./json-config.js";
2
2
  import * as yaml from 'js-yaml';
3
3
  /**
4
4
  * YAML config plugin.
@@ -8,4 +8,3 @@ export default function yamlConfigPlugin(app, options) {
8
8
  options.ext = 'yml';
9
9
  loadConfig(app, options, yaml.load);
10
10
  }
11
- //# sourceMappingURL=yaml-config.js.map
package/lib/renderer.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { MojoContext, MojoRenderOptions } from './types.js';
1
+ import type { MojoContext, MojoRenderOptions } from './types.ts';
2
2
  interface EngineResult {
3
3
  format: string;
4
4
  output: string | Buffer;