hoa 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## v0.3.5 / 2026-02-03
2
+
3
+ - hotfix: package.json exports
4
+
5
+ ## v0.3.4 / 2026-01-26
6
+
7
+ - feat: use tsdown instead of tsup
8
+ - fix: update devDependencies
9
+
1
10
  ## v0.3.3 / 2025-12-03
2
11
 
3
12
  - fix: types improvements
@@ -0,0 +1,152 @@
1
+ const require_lib_utils = require('./lib/utils.cjs');
2
+ const require_lib_http_error = require('./lib/http-error.cjs');
3
+
4
+ //#region src/context.js
5
+ /**
6
+ * @typedef {Object} CtxJSON
7
+ * @property {ReturnType<import('./hoa.js').default.prototype.toJSON>} app
8
+ * @property {ReturnType<import('./request.js').default.prototype.toJSON>} req
9
+ * @property {ReturnType<import('./response.js').default.prototype.toJSON>} res
10
+ */
11
+ /**
12
+ * @class HoaContext
13
+ */
14
+ var HoaContext = class {
15
+ /**
16
+ * Create a context for a single HTTP request.
17
+ *
18
+ * @param {Object} [options={}]
19
+ * @param {Request} [options.request] - Web Standard Request
20
+ * @param {any} [options.env] - Environment (platform-specific)
21
+ * @param {any} [options.executionCtx] - Execution context (platform-specific)
22
+ * @public
23
+ */
24
+ constructor(options = {}) {
25
+ this.request = options.request;
26
+ this.env = options.env;
27
+ this.executionCtx = options.executionCtx;
28
+ this.state = Object.create(null);
29
+ }
30
+ /**
31
+ * Throw an HttpError.
32
+ *
33
+ * @param {number} status - HTTP status code
34
+ * @param {string|{message?: string, cause?: any, headers?: HeadersInit}} [messageOrOptions] - Error message or options object
35
+ * @throws {HttpError}
36
+ * @public
37
+ */
38
+ throw(...args) {
39
+ throw new require_lib_http_error(...args);
40
+ }
41
+ /**
42
+ * Assert condition or throw an HttpError.
43
+ *
44
+ * @param {any} value - Condition to assert
45
+ * @param {...any} args - Arguments passed to HttpError constructor
46
+ * @throws {HttpError}
47
+ * @public
48
+ */
49
+ assert(value, ...args) {
50
+ if (value) return;
51
+ throw new require_lib_http_error(...args);
52
+ }
53
+ /**
54
+ * Default error handling and response builder.
55
+ *
56
+ * @param {Error} err - Error to handle
57
+ * @returns {Response} Web Standard Response object
58
+ * @private
59
+ */
60
+ onerror(err) {
61
+ const { res } = this;
62
+ if (!(Object.prototype.toString.call(err) === "[object Error]" || err instanceof Error)) err = /* @__PURE__ */ new Error(`non-error thrown: ${JSON.stringify(err)}`);
63
+ this.app.onerror(err, this);
64
+ res.headers = new Headers();
65
+ res.set(err.headers);
66
+ res.type = "text";
67
+ let status = err.status || err.statusCode;
68
+ if (typeof status !== "number" || !require_lib_utils.statusTextMapping[status]) status = 500;
69
+ const message = require_lib_utils.statusTextMapping[status];
70
+ const msg = err.expose ? err.message : message;
71
+ res.status = status;
72
+ res.body = msg;
73
+ return new Response(res.body, {
74
+ status: res.status,
75
+ headers: res._headers
76
+ });
77
+ }
78
+ /**
79
+ * Build Web Standard Response from current context state.
80
+ * Handles various body types, HEAD requests, and status-specific behaviors.
81
+ *
82
+ * @returns {Response} Web Standard Response object
83
+ * @public
84
+ */
85
+ get response() {
86
+ const { res, req } = this;
87
+ let body = res.body;
88
+ if (req.method === "HEAD") {
89
+ if (!res.has("Content-Length")) {
90
+ const contentLength = res.length;
91
+ if (Number.isInteger(contentLength)) res.length = contentLength;
92
+ }
93
+ return new Response(null, {
94
+ status: res.status,
95
+ statusText: res.statusText,
96
+ headers: res._headers
97
+ });
98
+ }
99
+ if (require_lib_utils.statusEmptyMapping[res.status]) {
100
+ res.body = null;
101
+ return new Response(null, {
102
+ status: res.status,
103
+ statusText: res.statusText,
104
+ headers: res._headers
105
+ });
106
+ }
107
+ if (body == null) {
108
+ if (res._explicitNullBody) {
109
+ res.delete("Content-Type");
110
+ res.delete("Transfer-Encoding");
111
+ res.set("Content-Length", "0");
112
+ }
113
+ return new Response(null, {
114
+ status: res.status,
115
+ statusText: res.statusText,
116
+ headers: res._headers
117
+ });
118
+ }
119
+ if (typeof body === "string" || body instanceof Blob || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof ReadableStream || body instanceof FormData || body instanceof URLSearchParams) return new Response(body, {
120
+ status: res.status,
121
+ statusText: res.statusText,
122
+ headers: res._headers
123
+ });
124
+ if (body instanceof Response) return new Response(body.body, {
125
+ status: res.status,
126
+ statusText: res.statusText,
127
+ headers: res._headers
128
+ });
129
+ body = JSON.stringify(body);
130
+ return new Response(body, {
131
+ status: res.status,
132
+ statusText: res.statusText,
133
+ headers: res._headers
134
+ });
135
+ }
136
+ /**
137
+ * Return JSON representation of the context.
138
+ *
139
+ * @returns {CtxJSON} JSON representation of context
140
+ * @public
141
+ */
142
+ toJSON() {
143
+ return {
144
+ app: this.app.toJSON(),
145
+ req: this.req.toJSON(),
146
+ res: this.res.toJSON()
147
+ };
148
+ }
149
+ };
150
+
151
+ //#endregion
152
+ module.exports = HoaContext;
@@ -0,0 +1,158 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_lib_utils = require('./lib/utils.cjs');
3
+ const require_lib_http_error = require('./lib/http-error.cjs');
4
+ const require_context = require('./context.cjs');
5
+ const require_lib_compose = require('./lib/compose.cjs');
6
+ const require_request = require('./request.cjs');
7
+ const require_response = require('./response.cjs');
8
+
9
+ //#region src/hoa.js
10
+ /**
11
+ * @typedef {Object} AppJSON
12
+ * @property {string} name - Application name
13
+ */
14
+ /**
15
+ * @class Hoa
16
+ * @property {string} name - Application name
17
+ * @property {boolean} [silent] - Suppress error console output when true
18
+ */
19
+ var Hoa = class Hoa {
20
+ /**
21
+ * Create an Hoa instance.
22
+ *
23
+ * @param {Object} [options={}] - Application options
24
+ * @param {string} [options.name='Hoa'] - Application name for identification
25
+ */
26
+ constructor(options = {}) {
27
+ this.name = options.name || "Hoa";
28
+ this.HoaContext = require_context;
29
+ this.HoaRequest = require_request;
30
+ this.HoaResponse = require_response;
31
+ this.middlewares = [];
32
+ this.fetch = this.fetch.bind(this);
33
+ }
34
+ /**
35
+ * Extend the application with a plugin initializer.
36
+ *
37
+ * @param {HoaExtension} fn - Plugin function that receives the app instance
38
+ * @returns {Hoa} The Hoa instance for method chaining
39
+ * @throws {TypeError}
40
+ * @public
41
+ */
42
+ extend(fn) {
43
+ if (typeof fn !== "function") throw new TypeError("extend() must receive a function!");
44
+ fn(this);
45
+ return this;
46
+ }
47
+ /**
48
+ * Register a middleware. Executed in registration order.
49
+ *
50
+ * @param {HoaMiddleware} fn - Middleware function
51
+ * @returns {Hoa} The Hoa instance for method chaining
52
+ * @throws {TypeError}
53
+ * @public
54
+ */
55
+ use(fn) {
56
+ if (typeof fn !== "function") throw new TypeError("use() must receive a function!");
57
+ this.middlewares.push(fn);
58
+ this._composedMiddleware = null;
59
+ return this;
60
+ }
61
+ /**
62
+ * Web Standards fetch handler - main entry point for HTTP requests.
63
+ * Compatible with Cloudflare Workers, Deno, and other Web Standards environments.
64
+ *
65
+ * @param {Request} request - Web Standard Request object
66
+ * @param {any} [env] - Environment variables (platform-specific)
67
+ * @param {any} [executionCtx] - Execution context (platform-specific)
68
+ * @returns {Promise<Response>} Web Standard Response object
69
+ * @public
70
+ */
71
+ fetch(request, env, executionCtx) {
72
+ const ctx = this.createContext(request, env, executionCtx);
73
+ if (!this._composedMiddleware) this._composedMiddleware = require_lib_compose(this.middlewares);
74
+ return this.handleRequest(ctx, this._composedMiddleware);
75
+ }
76
+ /**
77
+ * Handle incoming request through the middleware stack.
78
+ * Manages error handling and response building.
79
+ *
80
+ * @param {HoaContext} ctx - Request context
81
+ * @param {HoaMiddleware} middlewareFn - Composed middleware function
82
+ * @returns {Promise<Response>} Web Standard Response object
83
+ * @private
84
+ */
85
+ handleRequest(ctx, middlewareFn) {
86
+ return middlewareFn(ctx).then(() => ctx.response).catch((err) => ctx.onerror(err));
87
+ }
88
+ /**
89
+ * Create context for incoming request with linked request/response objects.
90
+ * Establishes the context chain: ctx ↔ req ↔ res ↔ app
91
+ *
92
+ * @param {Request} request - Web Standard Request object
93
+ * @param {any} [env] - Environment variables
94
+ * @param {any} [executionCtx] - Execution context
95
+ * @returns {HoaContext} Created context instance
96
+ * @private
97
+ */
98
+ createContext(request, env, executionCtx) {
99
+ const ctx = new this.HoaContext({
100
+ request,
101
+ env,
102
+ executionCtx
103
+ });
104
+ const req = ctx.req = new this.HoaRequest();
105
+ const res = ctx.res = new this.HoaResponse();
106
+ ctx.app = req.app = res.app = this;
107
+ req.ctx = res.ctx = ctx;
108
+ req.res = res;
109
+ res.req = req;
110
+ return ctx;
111
+ }
112
+ /**
113
+ * Default error handler for unhandled application errors.
114
+ * Logs errors to console unless they're client errors (4xx) or explicitly exposed.
115
+ *
116
+ * @param {Error} err - Error to handle
117
+ * @param {HoaContext} [ctx] - Request context (optional)
118
+ * @returns {void}
119
+ * @throws {TypeError}
120
+ * @private
121
+ */
122
+ onerror(err, ctx) {
123
+ if (!(Object.prototype.toString.call(err) === "[object Error]" || err instanceof Error)) throw new TypeError(`non-error thrown: ${JSON.stringify(err)}`);
124
+ if (err.status === 404 || err.expose) return;
125
+ if (this.silent) return;
126
+ console.error(err);
127
+ }
128
+ /**
129
+ * ESM/CJS interop helper for default exports.
130
+ *
131
+ * @returns {typeof Hoa} The Hoa class
132
+ * @static
133
+ */
134
+ static get default() {
135
+ return Hoa;
136
+ }
137
+ /**
138
+ * Return JSON representation of the app.
139
+ *
140
+ * @returns {AppJSON} JSON representation of application
141
+ * @public
142
+ */
143
+ toJSON() {
144
+ return { name: this.name };
145
+ }
146
+ };
147
+
148
+ //#endregion
149
+ exports.Hoa = Hoa;
150
+ exports.default = Hoa;
151
+ exports.HoaContext = require_context;
152
+ exports.HoaRequest = require_request;
153
+ exports.HoaResponse = require_response;
154
+ exports.HttpError = require_lib_http_error;
155
+ exports.compose = require_lib_compose;
156
+ exports.statusEmptyMapping = require_lib_utils.statusEmptyMapping;
157
+ exports.statusRedirectMapping = require_lib_utils.statusRedirectMapping;
158
+ exports.statusTextMapping = require_lib_utils.statusTextMapping;
@@ -0,0 +1,35 @@
1
+
2
+ //#region src/lib/compose.js
3
+ /**
4
+ * Compose middleware functions into a single dispatcher.
5
+ *
6
+ * @param {HoaMiddleware[]} middlewares - Array of middleware functions
7
+ * @returns {HoaMiddleware} Composed middleware function
8
+ * @private
9
+ */
10
+ const composeSlim = (middlewares) => async (ctx, next) => {
11
+ const dispatch = (i) => async () => {
12
+ const fn = i === middlewares.length ? next : middlewares[i];
13
+ if (!fn) return;
14
+ return await fn(ctx, dispatch(i + 1));
15
+ };
16
+ return dispatch(0)();
17
+ };
18
+ /**
19
+ * Compose multiple middleware functions into one.
20
+ * Validates input, flattens nested arrays, and returns a composed dispatcher.
21
+ *
22
+ * @param {HoaMiddleware[]|HoaMiddleware[][]} middlewares - Array of middleware functions or nested arrays
23
+ * @returns {HoaMiddleware} Composed middleware function
24
+ * @throws {TypeError}
25
+ * @public
26
+ */
27
+ function compose(middlewares) {
28
+ if (!Array.isArray(middlewares)) throw new TypeError("compose() must receive an array of middleware functions!");
29
+ middlewares = middlewares.flat();
30
+ for (const middleware of middlewares) if (typeof middleware !== "function") throw new TypeError("Middleware must be composed of functions!");
31
+ return composeSlim(middlewares);
32
+ }
33
+
34
+ //#endregion
35
+ module.exports = compose;
@@ -0,0 +1,42 @@
1
+ const require_lib_utils = require('./utils.cjs');
2
+
3
+ //#region src/lib/http-error.js
4
+ /**
5
+ * @typedef {Object} HttpErrorOptions
6
+ * @property {string} [message] - Custom error message
7
+ * @property {Error} [cause] - The underlying cause of this error
8
+ * @property {boolean} [expose] - Whether to expose the error message to clients (defaults based on status)
9
+ * @property {HeadersInit} [headers] - Additional response headers
10
+ */
11
+ var HttpError = class HttpError extends Error {
12
+ /**
13
+ * Create a new HttpError instance.
14
+ *
15
+ * @param {number} status - HTTP status code (400-599, invalid codes become 500)
16
+ * @param {string|HttpErrorOptions} [message] - Error message or options object
17
+ * @param {HttpErrorOptions} [options] - Additional options when second param is string
18
+ * @throws {TypeError}
19
+ */
20
+ constructor(status, message, options) {
21
+ if (!Number.isInteger(status)) throw new TypeError("status code must be an integer");
22
+ if (status < 400 || status >= 600) status = 500;
23
+ let finalOptions = {};
24
+ if (typeof message === "string") {
25
+ finalOptions.message = message;
26
+ if (options && typeof options === "object") finalOptions = {
27
+ ...finalOptions,
28
+ ...options
29
+ };
30
+ } else if (message && typeof message === "object") finalOptions = message;
31
+ message = finalOptions.message ?? require_lib_utils.statusTextMapping[status] ?? "Unknown error";
32
+ super(message, { cause: finalOptions.cause });
33
+ this.name = "HttpError";
34
+ this.status = this.statusCode = status;
35
+ this.expose = finalOptions.expose ?? status < 500;
36
+ if (finalOptions.headers) this.headers = Object.fromEntries(new Headers(finalOptions.headers).entries());
37
+ if (Error.captureStackTrace) Error.captureStackTrace(this, HttpError);
38
+ }
39
+ };
40
+
41
+ //#endregion
42
+ module.exports = HttpError;
@@ -0,0 +1,214 @@
1
+
2
+ //#region src/lib/utils.js
3
+ /**
4
+ * Parse URLSearchParams into a query object, handling multiple values for the same key.
5
+ * When a key appears multiple times, values are collected into an array.
6
+ *
7
+ * @param {URLSearchParams} searchParams - The URLSearchParams object to parse
8
+ * @returns {Record<string, string|string[]>} Query object with string values or arrays for multiple values
9
+ * @public
10
+ */
11
+ function parseSearchParamsToQuery(searchParams) {
12
+ const query = {};
13
+ for (const [key, value] of searchParams) if (query[key] !== void 0) query[key] = [].concat(query[key], value);
14
+ else query[key] = value;
15
+ return query;
16
+ }
17
+ /**
18
+ * Convert a query object to a URL query string.
19
+ * Handles arrays by appending multiple parameters with the same key.
20
+ *
21
+ * @param {Record<string, string|string[]|undefined|null>} query - Query object to stringify
22
+ * @returns {string} URL-encoded query string (without leading '?')
23
+ * @public
24
+ */
25
+ function stringifyQueryToString(query) {
26
+ if (!query) return "";
27
+ const params = new URLSearchParams();
28
+ for (const [key, value] of Object.entries(query)) if (Array.isArray(value)) value.forEach((v) => params.append(key, v ?? ""));
29
+ else params.append(key, value ?? "");
30
+ return params.toString();
31
+ }
32
+ /**
33
+ * Mapping of HTTP status codes to their standard reason phrases.
34
+ *
35
+ * @type {Record<number, string>}
36
+ * @public
37
+ */
38
+ const statusTextMapping = {
39
+ 100: "Continue",
40
+ 101: "Switching Protocols",
41
+ 102: "Processing",
42
+ 103: "Early Hints",
43
+ 200: "OK",
44
+ 201: "Created",
45
+ 202: "Accepted",
46
+ 203: "Non-Authoritative Information",
47
+ 204: "No Content",
48
+ 205: "Reset Content",
49
+ 206: "Partial Content",
50
+ 207: "Multi-Status",
51
+ 208: "Already Reported",
52
+ 226: "IM Used",
53
+ 300: "Multiple Choices",
54
+ 301: "Moved Permanently",
55
+ 302: "Found",
56
+ 303: "See Other",
57
+ 304: "Not Modified",
58
+ 305: "Use Proxy",
59
+ 307: "Temporary Redirect",
60
+ 308: "Permanent Redirect",
61
+ 400: "Bad Request",
62
+ 401: "Unauthorized",
63
+ 402: "Payment Required",
64
+ 403: "Forbidden",
65
+ 404: "Not Found",
66
+ 405: "Method Not Allowed",
67
+ 406: "Not Acceptable",
68
+ 407: "Proxy Authentication Required",
69
+ 408: "Request Timeout",
70
+ 409: "Conflict",
71
+ 410: "Gone",
72
+ 411: "Length Required",
73
+ 412: "Precondition Failed",
74
+ 413: "Payload Too Large",
75
+ 414: "URI Too Long",
76
+ 415: "Unsupported Media Type",
77
+ 416: "Range Not Satisfiable",
78
+ 417: "Expectation Failed",
79
+ 418: "I'm a Teapot",
80
+ 421: "Misdirected Request",
81
+ 422: "Unprocessable Entity",
82
+ 423: "Locked",
83
+ 424: "Failed Dependency",
84
+ 425: "Too Early",
85
+ 426: "Upgrade Required",
86
+ 428: "Precondition Required",
87
+ 429: "Too Many Requests",
88
+ 431: "Request Header Fields Too Large",
89
+ 451: "Unavailable For Legal Reasons",
90
+ 500: "Internal Server Error",
91
+ 501: "Not Implemented",
92
+ 502: "Bad Gateway",
93
+ 503: "Service Unavailable",
94
+ 504: "Gateway Timeout",
95
+ 505: "HTTP Version Not Supported",
96
+ 506: "Variant Also Negotiates",
97
+ 507: "Insufficient Storage",
98
+ 508: "Loop Detected",
99
+ 509: "Bandwidth Limit Exceeded",
100
+ 510: "Not Extended",
101
+ 511: "Network Authentication Required"
102
+ };
103
+ /**
104
+ * Mapping of HTTP status codes that indicate redirects.
105
+ * Used to determine if a response should trigger a redirect.
106
+ *
107
+ * @type {Record<number, boolean>}
108
+ * @readonly
109
+ * @public
110
+ */
111
+ const statusRedirectMapping = {
112
+ 300: true,
113
+ 301: true,
114
+ 302: true,
115
+ 303: true,
116
+ 305: true,
117
+ 307: true,
118
+ 308: true
119
+ };
120
+ /**
121
+ * Mapping of HTTP status codes that should have empty response bodies.
122
+ * These status codes by specification should not include a message body.
123
+ *
124
+ * @type {Record<number, boolean>}
125
+ * @readonly
126
+ * @public
127
+ */
128
+ const statusEmptyMapping = {
129
+ 204: true,
130
+ 205: true,
131
+ 304: true
132
+ };
133
+ /**
134
+ * Mapping of common content type aliases to their full MIME types.
135
+ * Provides convenient shortcuts for setting response content types.
136
+ *
137
+ * @type {Record<string, string>}
138
+ * @readonly
139
+ * @public
140
+ */
141
+ const commonTypeMapping = {
142
+ html: "text/html;charset=UTF-8",
143
+ text: "text/plain;charset=UTF-8",
144
+ xml: "text/xml;charset=UTF-8",
145
+ md: "text/markdown;charset=UTF-8",
146
+ json: "application/json",
147
+ form: "application/x-www-form-urlencoded;charset=UTF-8",
148
+ pdf: "application/pdf",
149
+ zip: "application/zip",
150
+ wasm: "application/wasm",
151
+ webmanifest: "application/manifest+json",
152
+ js: "application/javascript;charset=UTF-8",
153
+ ts: "application/typescript;charset=UTF-8",
154
+ png: "image/png",
155
+ jpg: "image/jpeg",
156
+ jpeg: "image/jpeg",
157
+ gif: "image/gif",
158
+ svg: "image/svg+xml",
159
+ webp: "image/webp",
160
+ avif: "image/avif",
161
+ ico: "image/x-icon",
162
+ mp3: "audio/mpeg",
163
+ wav: "audio/wav",
164
+ ogg: "audio/ogg",
165
+ mp4: "video/mp4",
166
+ webm: "video/webm",
167
+ avi: "video/x-msvideo",
168
+ mov: "video/quicktime",
169
+ woff: "font/woff",
170
+ woff2: "font/woff2",
171
+ ttf: "font/ttf",
172
+ otf: "font/otf",
173
+ bin: "application/octet-stream"
174
+ };
175
+ const ENCODE_CHARS_REGEXP = /(?:[^\x21\x23-\x3B\x3D\x3F-\x5F\x61-\x7A\x7C\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g;
176
+ /**
177
+ * RegExp to match unmatched surrogate pair.
178
+ * @private
179
+ */
180
+ const UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g;
181
+ /**
182
+ * String to replace unmatched surrogate pair with.
183
+ * @private
184
+ */
185
+ const UNMATCHED_SURROGATE_PAIR_REPLACE = "$1�$2";
186
+ /**
187
+ * Encode a URL to a percent-encoded form, excluding already-encoded sequences.
188
+ *
189
+ * This function will take an already-encoded URL and encode all the non-URL
190
+ * code points. This function will not encode the "%" character unless it is
191
+ * not part of a valid sequence (`%20` will be left as-is, but `%foo` will
192
+ * be encoded as `%25foo`).
193
+ *
194
+ * This encode is meant to be "safe" and does not throw errors. It will try as
195
+ * hard as it can to properly encode the given URL, including replacing any raw,
196
+ * unpaired surrogate pairs with the Unicode replacement character prior to
197
+ * encoding.
198
+ *
199
+ * @param {string} url - URL string to encode
200
+ * @return {string} Encoded URL string
201
+ * @public
202
+ */
203
+ function encodeUrl(url) {
204
+ return String(url).replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE).replace(ENCODE_CHARS_REGEXP, encodeURI);
205
+ }
206
+
207
+ //#endregion
208
+ exports.commonTypeMapping = commonTypeMapping;
209
+ exports.encodeUrl = encodeUrl;
210
+ exports.parseSearchParamsToQuery = parseSearchParamsToQuery;
211
+ exports.statusEmptyMapping = statusEmptyMapping;
212
+ exports.statusRedirectMapping = statusRedirectMapping;
213
+ exports.statusTextMapping = statusTextMapping;
214
+ exports.stringifyQueryToString = stringifyQueryToString;