hoa 0.0.1 → 0.1.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.
- package/CHANGELOG.md +3 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/cjs/application.js +245 -0
- package/dist/cjs/context.js +111 -0
- package/dist/cjs/lib/compose.js +40 -0
- package/dist/cjs/lib/http-error.js +61 -0
- package/dist/cjs/lib/utils.js +190 -0
- package/dist/cjs/request.js +546 -0
- package/dist/cjs/response.js +380 -0
- package/dist/esm/application.js +207 -0
- package/dist/esm/context.js +82 -0
- package/dist/esm/lib/compose.js +21 -0
- package/dist/esm/lib/http-error.js +42 -0
- package/dist/esm/lib/utils.js +161 -0
- package/dist/esm/request.js +527 -0
- package/dist/esm/response.js +361 -0
- package/package.json +60 -7
- package/types/index.d.ts +233 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 - present, Hoa contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## Hoa
|
|
2
|
+
|
|
3
|
+
Hoa is a minimal Web framework inspired by [Koa](https://github.com/koajs/koa) and [Hono](https://github.com/honojs/hono), built entirely on Web Standards. It runs seamlessly on any modern JavaScript runtime: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, AWS Lambda, Lambda@Edge, and Node.js.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ⚡ Minimal - Only ~4.4KB (gzipped).
|
|
8
|
+
- 🚫 Zero Dependencies - Built on modern Web Standards with no external dependencies.
|
|
9
|
+
- 🛠️ Highly Extensible - Features a flexible extension and middleware system.
|
|
10
|
+
- 😊 Standards-Based - Designed entirely around modern Web Standard APIs.
|
|
11
|
+
- 🌐 Multi-Runtime - The same code runs on Cloudflare Workers, Deno, Bun, Node.js, and more.
|
|
12
|
+
- ✅ 100% Tested – Backed by a full-coverage automated test suite.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm i hoa --save
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import { Hoa } from 'hoa'
|
|
24
|
+
const app = new Hoa()
|
|
25
|
+
|
|
26
|
+
app.use(async (ctx, next) => {
|
|
27
|
+
ctx.res.body = 'Hello, Hoa!'
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export default app
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Documentation
|
|
34
|
+
|
|
35
|
+
The documentation is available on [hoa-js.com](https://hoa-js.com)
|
|
36
|
+
|
|
37
|
+
## Contributing
|
|
38
|
+
|
|
39
|
+
Contributions Welcome! You can contribute in the following ways.
|
|
40
|
+
|
|
41
|
+
- Create an Issue - Propose a new feature. Report a bug.
|
|
42
|
+
- Pull Request - Fix a bug and typo. Refactor the code.
|
|
43
|
+
- Create third-party middleware.
|
|
44
|
+
- Share your thoughts on the Blog, X, and others.
|
|
45
|
+
- Make your application.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
Distributed under the MIT License.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var application_exports = {};
|
|
29
|
+
__export(application_exports, {
|
|
30
|
+
Hoa: () => Application,
|
|
31
|
+
HoaContext: () => import_context.default,
|
|
32
|
+
HoaRequest: () => import_request.default,
|
|
33
|
+
HoaResponse: () => import_response.default,
|
|
34
|
+
HttpError: () => import_http_error.default,
|
|
35
|
+
compose: () => import_compose.default,
|
|
36
|
+
default: () => Application
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(application_exports);
|
|
39
|
+
var import_compose = __toESM(require("./lib/compose.js"), 1);
|
|
40
|
+
var import_http_error = __toESM(require("./lib/http-error.js"), 1);
|
|
41
|
+
var import_utils = require("./lib/utils.js");
|
|
42
|
+
var import_context = __toESM(require("./context.js"), 1);
|
|
43
|
+
var import_request = __toESM(require("./request.js"), 1);
|
|
44
|
+
var import_response = __toESM(require("./response.js"), 1);
|
|
45
|
+
class Application {
|
|
46
|
+
/**
|
|
47
|
+
* Create an Application instance.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} [options={}] - Application options
|
|
50
|
+
* @param {string} [options.name='Hoa'] - Application name for identification
|
|
51
|
+
*/
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.name = options.name || "Hoa";
|
|
54
|
+
this.HoaContext = import_context.default;
|
|
55
|
+
this.HoaRequest = import_request.default;
|
|
56
|
+
this.HoaResponse = import_response.default;
|
|
57
|
+
this.middlewares = [];
|
|
58
|
+
this.fetch = this.fetch.bind(this);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extend the application with a plugin initializer.
|
|
62
|
+
* @param {(app: Application) => void} fn - Plugin function that receives the app instance
|
|
63
|
+
* @returns {Application}
|
|
64
|
+
* @throws {TypeError} If fn is not a function
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
extend(fn) {
|
|
68
|
+
if (typeof fn !== "function") {
|
|
69
|
+
throw new TypeError("extend() must receive a function!");
|
|
70
|
+
}
|
|
71
|
+
fn(this);
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Register a middleware. Executed in registration order.
|
|
76
|
+
* @param {(ctx: HoaContext, next: () => Promise<void>) => Promise<any | void>} fn - Middleware function
|
|
77
|
+
* @returns {Application}
|
|
78
|
+
* @throws {TypeError} If fn is not a function
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
use(fn) {
|
|
82
|
+
if (typeof fn !== "function") {
|
|
83
|
+
throw new TypeError("use() must receive a function!");
|
|
84
|
+
}
|
|
85
|
+
this.middlewares.push(fn);
|
|
86
|
+
this._composedMiddleware = null;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Web Standards fetch handler - main entry point for HTTP requests.
|
|
91
|
+
* Compatible with Cloudflare Workers, Deno, and other Web Standards environments.
|
|
92
|
+
*
|
|
93
|
+
* @param {Request} request - Web Standard Request object
|
|
94
|
+
* @param {any} [env] - Environment variables (platform-specific)
|
|
95
|
+
* @param {any} [executionCtx] - Execution context (platform-specific)
|
|
96
|
+
* @returns {Promise<Response>}
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
fetch(request, env, executionCtx) {
|
|
100
|
+
const ctx = this.createContext(request, env, executionCtx);
|
|
101
|
+
if (!this._composedMiddleware) this._composedMiddleware = (0, import_compose.default)(this.middlewares);
|
|
102
|
+
return this.handleRequest(ctx, this._composedMiddleware);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Handle incoming request through the middleware stack.
|
|
106
|
+
* Manages error handling and response building.
|
|
107
|
+
*
|
|
108
|
+
* @param {HoaContext} ctx - Request context
|
|
109
|
+
* @param {(ctx: HoaContext) => Promise<void>} middlewareFn - Composed middleware function
|
|
110
|
+
* @returns {Promise<Response>}
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
handleRequest(ctx, middlewareFn) {
|
|
114
|
+
const onerror = (err) => ctx.onerror(err);
|
|
115
|
+
const handleResponse = () => respond(ctx);
|
|
116
|
+
return middlewareFn(ctx).then(handleResponse).catch(onerror);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create context for incoming request with linked request/response objects.
|
|
120
|
+
* Establishes the context chain: ctx ↔ req ↔ res ↔ app
|
|
121
|
+
*
|
|
122
|
+
* @param {Request} request - Web Standard Request object
|
|
123
|
+
* @param {any} [env] - Environment variables
|
|
124
|
+
* @param {any} [executionCtx] - Execution context
|
|
125
|
+
* @returns {HoaContext}
|
|
126
|
+
* @private
|
|
127
|
+
*/
|
|
128
|
+
createContext(request, env, executionCtx) {
|
|
129
|
+
const ctx = new this.HoaContext({ request, env, executionCtx });
|
|
130
|
+
const req = ctx.req = new this.HoaRequest();
|
|
131
|
+
const res = ctx.res = new this.HoaResponse();
|
|
132
|
+
ctx.app = req.app = res.app = this;
|
|
133
|
+
req.ctx = res.ctx = ctx;
|
|
134
|
+
req.res = res;
|
|
135
|
+
res.req = req;
|
|
136
|
+
return ctx;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Default error handler for unhandled application errors.
|
|
140
|
+
* Logs errors to console unless they're client errors (4xx) or explicitly exposed.
|
|
141
|
+
*
|
|
142
|
+
* @param {Error} err - Error to handle
|
|
143
|
+
* @param {HoaContext} [ctx] - Request context (optional)
|
|
144
|
+
* @throws {TypeError} When err is not a proper Error object
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
onerror(err, ctx) {
|
|
148
|
+
const isNativeError = Object.prototype.toString.call(err) === "[object Error]" || err instanceof Error;
|
|
149
|
+
if (!isNativeError) {
|
|
150
|
+
throw new TypeError(`non-error thrown: ${JSON.stringify(err)}`);
|
|
151
|
+
}
|
|
152
|
+
if (err.status === 404 || err.expose) return;
|
|
153
|
+
if (this.silent) return;
|
|
154
|
+
const msg = err.stack || err.toString();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* ESM/CJS interop helper for default exports.
|
|
158
|
+
*
|
|
159
|
+
* @returns {typeof Application}
|
|
160
|
+
* @static
|
|
161
|
+
*/
|
|
162
|
+
static get default() {
|
|
163
|
+
return Application;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Return JSON representation of the app.
|
|
167
|
+
*
|
|
168
|
+
* @returns {AppJSON}
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
toJSON() {
|
|
172
|
+
return {
|
|
173
|
+
name: this.name
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function respond(ctx) {
|
|
178
|
+
const { res, req } = ctx;
|
|
179
|
+
let body = res.body;
|
|
180
|
+
if (req.method === "HEAD") {
|
|
181
|
+
if (!res.has("Content-Length")) {
|
|
182
|
+
const contentLength = res.length;
|
|
183
|
+
if (Number.isInteger(contentLength)) {
|
|
184
|
+
res.length = contentLength;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return new Response(null, {
|
|
188
|
+
status: res.status,
|
|
189
|
+
statusText: res.statusText,
|
|
190
|
+
headers: res._headers
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (import_utils.statusEmptyMapping[res.status]) {
|
|
194
|
+
res.body = null;
|
|
195
|
+
return new Response(null, {
|
|
196
|
+
status: res.status,
|
|
197
|
+
statusText: res.statusText,
|
|
198
|
+
headers: res._headers
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (body == null) {
|
|
202
|
+
if (res._explicitNullBody) {
|
|
203
|
+
res.delete("Content-Type");
|
|
204
|
+
res.delete("Transfer-Encoding");
|
|
205
|
+
res.set("Content-Length", "0");
|
|
206
|
+
}
|
|
207
|
+
if (!res._explicitStatus) {
|
|
208
|
+
res.status = 404;
|
|
209
|
+
}
|
|
210
|
+
return new Response(null, {
|
|
211
|
+
status: res.status,
|
|
212
|
+
statusText: res.statusText,
|
|
213
|
+
headers: res._headers
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (typeof body === "string" || body instanceof Blob || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof ReadableStream || body instanceof FormData || body instanceof URLSearchParams) {
|
|
217
|
+
return new Response(body, {
|
|
218
|
+
status: res.status,
|
|
219
|
+
statusText: res.statusText,
|
|
220
|
+
headers: res._headers
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (body instanceof Response) {
|
|
224
|
+
return new Response(body.body, {
|
|
225
|
+
status: res.status,
|
|
226
|
+
statusText: res.statusText,
|
|
227
|
+
headers: res._headers
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
body = JSON.stringify(body);
|
|
231
|
+
return new Response(body, {
|
|
232
|
+
status: res.status,
|
|
233
|
+
statusText: res.statusText,
|
|
234
|
+
headers: res._headers
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
+
0 && (module.exports = {
|
|
239
|
+
Hoa,
|
|
240
|
+
HoaContext,
|
|
241
|
+
HoaRequest,
|
|
242
|
+
HoaResponse,
|
|
243
|
+
HttpError,
|
|
244
|
+
compose
|
|
245
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var context_exports = {};
|
|
29
|
+
__export(context_exports, {
|
|
30
|
+
default: () => HoaContext
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(context_exports);
|
|
33
|
+
var import_http_error = __toESM(require("./lib/http-error.js"), 1);
|
|
34
|
+
var import_utils = require("./lib/utils.js");
|
|
35
|
+
class HoaContext {
|
|
36
|
+
/**
|
|
37
|
+
* Create a context for a single HTTP request.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} [options={}]
|
|
40
|
+
* @param {Request} [options.request] - Web Standard Request
|
|
41
|
+
* @param {any} [options.env] - Environment (platform-specific)
|
|
42
|
+
* @param {any} [options.executionCtx] - Execution context (platform-specific)
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this.request = options.request;
|
|
47
|
+
this.env = options.env;
|
|
48
|
+
this.executionCtx = options.executionCtx;
|
|
49
|
+
this.state = /* @__PURE__ */ Object.create(null);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Throw an HttpError.
|
|
53
|
+
*
|
|
54
|
+
* @param {number} status
|
|
55
|
+
* @param {string|{message?: string, cause?: any, headers?: HeadersInit}} [messageOrOptions]
|
|
56
|
+
* @throws {HttpError}
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
throw(...args) {
|
|
60
|
+
throw new import_http_error.default(...args);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Assert condition or throw an HttpError.
|
|
64
|
+
*
|
|
65
|
+
* @param {any} value - Condition to assert
|
|
66
|
+
* @param {...any} args - Arguments passed to HttpError constructor
|
|
67
|
+
* @throws {HttpError}
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
assert(value, ...args) {
|
|
71
|
+
if (value) return;
|
|
72
|
+
throw new import_http_error.default(...args);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Default error handling and response builder.
|
|
76
|
+
* @param {Error} err
|
|
77
|
+
* @returns {Response}
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
onerror(err) {
|
|
81
|
+
const { res } = this;
|
|
82
|
+
const isNativeError = Object.prototype.toString.call(err) === "[object Error]" || err instanceof Error;
|
|
83
|
+
if (!isNativeError) err = new Error(`non-error thrown: ${JSON.stringify(err)}`);
|
|
84
|
+
this.app.onerror(err, this);
|
|
85
|
+
res.headers = new Headers();
|
|
86
|
+
res.set(err.headers);
|
|
87
|
+
res.type = "text";
|
|
88
|
+
let status = err.status || err.statusCode;
|
|
89
|
+
if (typeof status !== "number" || !import_utils.statusTextMapping[status]) status = 500;
|
|
90
|
+
const message = import_utils.statusTextMapping[status];
|
|
91
|
+
const msg = err.expose ? err.message : message;
|
|
92
|
+
res.status = status;
|
|
93
|
+
res.body = msg;
|
|
94
|
+
return new Response(res.body, {
|
|
95
|
+
status: res.status,
|
|
96
|
+
headers: res._headers
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Return JSON representation of the context.
|
|
101
|
+
* @returns {CtxJSON}
|
|
102
|
+
* @public
|
|
103
|
+
*/
|
|
104
|
+
toJSON() {
|
|
105
|
+
return {
|
|
106
|
+
app: this.app.toJSON(),
|
|
107
|
+
req: this.req.toJSON(),
|
|
108
|
+
res: this.res.toJSON()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var compose_exports = {};
|
|
19
|
+
__export(compose_exports, {
|
|
20
|
+
default: () => compose
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(compose_exports);
|
|
23
|
+
const composeSlim = (middlewares) => async (ctx, next) => {
|
|
24
|
+
const dispatch = (i) => async () => {
|
|
25
|
+
const fn = i === middlewares.length ? next : middlewares[i];
|
|
26
|
+
if (!fn) return;
|
|
27
|
+
return await fn(ctx, dispatch(i + 1));
|
|
28
|
+
};
|
|
29
|
+
return dispatch(0)();
|
|
30
|
+
};
|
|
31
|
+
function compose(middlewares) {
|
|
32
|
+
if (!Array.isArray(middlewares)) {
|
|
33
|
+
throw new TypeError("compose() must receive an array of middleware functions!");
|
|
34
|
+
}
|
|
35
|
+
middlewares = middlewares.flat();
|
|
36
|
+
for (const middleware of middlewares) {
|
|
37
|
+
if (typeof middleware !== "function") throw new TypeError("Middleware must be composed of functions!");
|
|
38
|
+
}
|
|
39
|
+
return composeSlim(middlewares);
|
|
40
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var http_error_exports = {};
|
|
19
|
+
__export(http_error_exports, {
|
|
20
|
+
default: () => HttpError
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(http_error_exports);
|
|
23
|
+
var import_utils = require("./utils.js");
|
|
24
|
+
class HttpError extends Error {
|
|
25
|
+
/**
|
|
26
|
+
* Create a new HttpError instance.
|
|
27
|
+
*
|
|
28
|
+
* @param {number} status - HTTP status code (400-599, invalid codes become 500)
|
|
29
|
+
* @param {string|HttpErrorOptions} [message] - Error message or options object
|
|
30
|
+
* @param {HttpErrorOptions} [options] - Additional options when second param is string
|
|
31
|
+
* @throws {TypeError} When status is not an integer
|
|
32
|
+
*/
|
|
33
|
+
constructor(status, message, options) {
|
|
34
|
+
if (!Number.isInteger(status)) {
|
|
35
|
+
throw new TypeError("status code must be an integer");
|
|
36
|
+
}
|
|
37
|
+
if (status < 400 || status >= 600) {
|
|
38
|
+
status = 500;
|
|
39
|
+
}
|
|
40
|
+
let finalOptions = {};
|
|
41
|
+
if (typeof message === "string") {
|
|
42
|
+
finalOptions.message = message;
|
|
43
|
+
if (options && typeof options === "object") {
|
|
44
|
+
finalOptions = { ...finalOptions, ...options };
|
|
45
|
+
}
|
|
46
|
+
} else if (message && typeof message === "object") {
|
|
47
|
+
finalOptions = message;
|
|
48
|
+
}
|
|
49
|
+
message = finalOptions.message ?? import_utils.statusTextMapping[status] ?? "Unknown error";
|
|
50
|
+
super(message, { cause: finalOptions.cause });
|
|
51
|
+
this.name = "HttpError";
|
|
52
|
+
this.status = this.statusCode = status;
|
|
53
|
+
this.expose = finalOptions.expose ?? status < 500;
|
|
54
|
+
if (finalOptions.headers) {
|
|
55
|
+
this.headers = Object.fromEntries(new Headers(finalOptions.headers).entries());
|
|
56
|
+
}
|
|
57
|
+
if (Error.captureStackTrace) {
|
|
58
|
+
Error.captureStackTrace(this, HttpError);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|