itty-router 4.0.10 → 4.0.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/Router.js CHANGED
@@ -1 +1,37 @@
1
- const e=({base:e="",routes:r=[]}={})=>({__proto__:new Proxy({},{get:(o,a,t,p)=>(o,...l)=>r.push([a.toUpperCase(),RegExp(`^${(p=(e+"/"+o).replace(/\/+(\/|$)/g,"$1")).replace(/(\/?\.?):(\w+)\+/g,"($1(?<$2>*))").replace(/(\/?\.?):(\w+)/g,"($1(?<$2>[^$1/]+?))").replace(/\./g,"\\.").replace(/(\/?)\*/g,"($1.*)?")}/*$`),l,p])&&t}),routes:r,async handle(e,...o){let a,t,p=new URL(e.url),l=e.query={__proto__:null};for(let[e,r]of p.searchParams)l[e]=void 0===l[e]?r:[l[e],r].flat();for(let[l,s,$,c]of r)if((l===e.method||"ALL"===l)&&(t=p.pathname.match(s))){e.params=t.groups||{},e.route=c;for(let r of $)if(void 0!==(a=await r(e.proxy||e,...o)))return a}}});export{e as Router};
1
+ const Router = ({ base = '', routes = [] } = {}) =>
2
+ // @ts-expect-error TypeScript doesn't know that Proxy makes this work
3
+ ({
4
+ __proto__: new Proxy({}, {
5
+ // @ts-expect-error (we're adding an expected prop "path" to the get)
6
+ get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([
7
+ prop.toUpperCase(),
8
+ RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash
9
+ .replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params
10
+ .replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format
11
+ .replace(/\./g, '\\.') // dot in path
12
+ .replace(/(\/?)\*/g, '($1.*)?') // wildcard
13
+ }/*$`),
14
+ handlers,
15
+ path, // embed clean route path
16
+ ]) && receiver
17
+ }),
18
+ routes,
19
+ async handle(request, ...args) {
20
+ let response, match, url = new URL(request.url), query = request.query = { __proto__: null };
21
+ for (let [k, v] of url.searchParams) {
22
+ query[k] = query[k] === undefined ? v : [query[k], v].flat();
23
+ }
24
+ for (let [method, regex, handlers, path] of routes) {
25
+ if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) {
26
+ request.params = match.groups || {}; // embed params in request
27
+ request.route = path; // embed route path in request
28
+ for (let handler of handlers) {
29
+ if ((response = await handler(request.proxy || request, ...args)) !== undefined)
30
+ return response;
31
+ }
32
+ }
33
+ }
34
+ }
35
+ });
36
+
37
+ export { Router };
package/StatusError.js CHANGED
@@ -1 +1,10 @@
1
- class t extends Error{status;constructor(t=500,s){super("object"==typeof s?s.error:s),"object"==typeof s&&Object.assign(this,s),this.status=t}}export{t as StatusError};
1
+ class StatusError extends Error {
2
+ status;
3
+ constructor(status = 500, body) {
4
+ super(typeof body === 'object' ? body.error : body);
5
+ typeof body === 'object' && Object.assign(this, body);
6
+ this.status = status;
7
+ }
8
+ }
9
+
10
+ export { StatusError };
package/cjs/Router.js CHANGED
@@ -1 +1,39 @@
1
- "use strict";exports.Router=({base:e="",routes:r=[]}={})=>({__proto__:new Proxy({},{get:(o,t,a,p)=>(o,...l)=>r.push([t.toUpperCase(),RegExp(`^${(p=(e+"/"+o).replace(/\/+(\/|$)/g,"$1")).replace(/(\/?\.?):(\w+)\+/g,"($1(?<$2>*))").replace(/(\/?\.?):(\w+)/g,"($1(?<$2>[^$1/]+?))").replace(/\./g,"\\.").replace(/(\/?)\*/g,"($1.*)?")}/*$`),l,p])&&a}),routes:r,async handle(e,...o){let t,a,p=new URL(e.url),l=e.query={__proto__:null};for(let[e,r]of p.searchParams)l[e]=void 0===l[e]?r:[l[e],r].flat();for(let[l,s,u,$]of r)if((l===e.method||"ALL"===l)&&(a=p.pathname.match(s))){e.params=a.groups||{},e.route=$;for(let r of u)if(void 0!==(t=await r(e.proxy||e,...o)))return t}}});
1
+ 'use strict';
2
+
3
+ const Router = ({ base = '', routes = [] } = {}) =>
4
+ // @ts-expect-error TypeScript doesn't know that Proxy makes this work
5
+ ({
6
+ __proto__: new Proxy({}, {
7
+ // @ts-expect-error (we're adding an expected prop "path" to the get)
8
+ get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([
9
+ prop.toUpperCase(),
10
+ RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash
11
+ .replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params
12
+ .replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format
13
+ .replace(/\./g, '\\.') // dot in path
14
+ .replace(/(\/?)\*/g, '($1.*)?') // wildcard
15
+ }/*$`),
16
+ handlers,
17
+ path, // embed clean route path
18
+ ]) && receiver
19
+ }),
20
+ routes,
21
+ async handle(request, ...args) {
22
+ let response, match, url = new URL(request.url), query = request.query = { __proto__: null };
23
+ for (let [k, v] of url.searchParams) {
24
+ query[k] = query[k] === undefined ? v : [query[k], v].flat();
25
+ }
26
+ for (let [method, regex, handlers, path] of routes) {
27
+ if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) {
28
+ request.params = match.groups || {}; // embed params in request
29
+ request.route = path; // embed route path in request
30
+ for (let handler of handlers) {
31
+ if ((response = await handler(request.proxy || request, ...args)) !== undefined)
32
+ return response;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ });
38
+
39
+ exports.Router = Router;
@@ -1 +1,12 @@
1
- "use strict";class t extends Error{status;constructor(t=500,s){super("object"==typeof s?s.error:s),"object"==typeof s&&Object.assign(this,s),this.status=t}}exports.StatusError=t;
1
+ 'use strict';
2
+
3
+ class StatusError extends Error {
4
+ status;
5
+ constructor(status = 500, body) {
6
+ super(typeof body === 'object' ? body.error : body);
7
+ typeof body === 'object' && Object.assign(this, body);
8
+ this.status = status;
9
+ }
10
+ }
11
+
12
+ exports.StatusError = StatusError;
package/cjs/createCors.js CHANGED
@@ -1 +1,68 @@
1
- "use strict";exports.createCors=(e={})=>{const{origins:s=["*"],maxAge:o,methods:t=["GET"],headers:n={}}=e;let r;const c={"content-type":"application/json","Access-Control-Allow-Methods":t.join(", "),...n};o&&(c["Access-Control-Max-Age"]=o);return{corsify:e=>{if(!e)throw new Error("No fetch handler responded and no upstream to proxy to specified.");const{headers:s,status:o,body:t}=e;return[101,301,302,308].includes(o)||s.get("access-control-allow-origin")?e:new Response(t,{status:o,headers:{...Object.fromEntries(s),...c,...r,"content-type":s.get("content-type")}})},preflight:e=>{const o=[...new Set(["OPTIONS",...t])],n=e.headers.get("origin")||"";if(r=(s.includes(n)||s.includes("*"))&&{"Access-Control-Allow-Origin":n},"OPTIONS"===e.method){const s={...c,"Access-Control-Allow-Methods":o.join(", "),"Access-Control-Allow-Headers":e.headers.get("Access-Control-Request-Headers"),...r};return new Response(null,{headers:e.headers.get("Origin")&&e.headers.get("Access-Control-Request-Method")&&e.headers.get("Access-Control-Request-Headers")?s:{Allow:o.join(", ")}})}}}};
1
+ 'use strict';
2
+
3
+ // Create CORS function with default options.
4
+ const createCors = (options = {}) => {
5
+ // Destructure and set defaults for options.
6
+ const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options;
7
+ let allowOrigin;
8
+ // Initial response headers.
9
+ const rHeaders = {
10
+ 'content-type': 'application/json',
11
+ 'Access-Control-Allow-Methods': methods.join(', '),
12
+ ...headers,
13
+ };
14
+ // Set max age if provided.
15
+ if (maxAge)
16
+ rHeaders['Access-Control-Max-Age'] = maxAge;
17
+ // Pre-flight function.
18
+ const preflight = (r) => {
19
+ // Use methods set.
20
+ const useMethods = [...new Set(['OPTIONS', ...methods])];
21
+ const origin = r.headers.get('origin') || '';
22
+ // Set allowOrigin globally.
23
+ allowOrigin = (origins.includes(origin) || origins.includes('*')) && {
24
+ 'Access-Control-Allow-Origin': origin,
25
+ };
26
+ // Check if method is OPTIONS.
27
+ if (r.method === 'OPTIONS') {
28
+ const reqHeaders = {
29
+ ...rHeaders,
30
+ 'Access-Control-Allow-Methods': useMethods.join(', '),
31
+ 'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'),
32
+ ...allowOrigin,
33
+ };
34
+ // Handle CORS pre-flight request.
35
+ return new Response(null, {
36
+ headers: r.headers.get('Origin') &&
37
+ r.headers.get('Access-Control-Request-Method') &&
38
+ r.headers.get('Access-Control-Request-Headers')
39
+ ? reqHeaders
40
+ : { Allow: useMethods.join(', ') },
41
+ });
42
+ }
43
+ };
44
+ // Corsify function.
45
+ const corsify = (response) => {
46
+ if (!response)
47
+ throw new Error('No fetch handler responded and no upstream to proxy to specified.');
48
+ const { headers, status, body } = response;
49
+ // Bypass for protocol shifts or redirects, or if CORS is already set.
50
+ if ([101, 301, 302, 308].includes(status) ||
51
+ headers.get('access-control-allow-origin'))
52
+ return response;
53
+ // Return new response with CORS headers.
54
+ return new Response(body, {
55
+ status,
56
+ headers: {
57
+ ...Object.fromEntries(headers),
58
+ ...rHeaders,
59
+ ...allowOrigin,
60
+ 'content-type': headers.get('content-type'),
61
+ },
62
+ });
63
+ };
64
+ // Return corsify and preflight methods.
65
+ return { corsify, preflight };
66
+ };
67
+
68
+ exports.createCors = createCors;
@@ -1 +1,16 @@
1
- "use strict";exports.createResponse=(e="text/plain; charset=utf-8",t)=>(s,n={})=>{const{headers:r={},...o}=n;return"Response"===s?.constructor.name?s:new Response(t?t(s):s,{headers:{"content-type":e,...r},...o})};
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ exports.createResponse = createResponse;
package/cjs/error.js CHANGED
@@ -1 +1,44 @@
1
- "use strict";const r=((r="text/plain; charset=utf-8",e)=>(t,s={})=>{const{headers:n={},...o}=s;return"Response"===t?.constructor.name?t:new Response(e?e(t):t,{headers:{"content-type":r,...n},...o})})("application/json; charset=utf-8",JSON.stringify),e=r=>({400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",500:"Internal Server Error"}[r]||"Unknown Error");exports.error=(t=500,s)=>{if(t instanceof Error){const{message:r,...n}=t;t=t.status||500,s={error:r||e(t),...n}}return s={status:t,..."object"==typeof s?s:{error:s||e(t)}},r(s,{status:t})};
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
17
+
18
+ const getMessage = (code) => {
19
+ return ({
20
+ 400: 'Bad Request',
21
+ 401: 'Unauthorized',
22
+ 403: 'Forbidden',
23
+ 404: 'Not Found',
24
+ 500: 'Internal Server Error',
25
+ }[code] || 'Unknown Error');
26
+ };
27
+ const error = (a = 500, b) => {
28
+ // handle passing an Error | StatusError directly in
29
+ if (a instanceof Error) {
30
+ const { message, ...err } = a;
31
+ a = a.status || 500;
32
+ b = {
33
+ error: message || getMessage(a),
34
+ ...err,
35
+ };
36
+ }
37
+ b = {
38
+ status: a,
39
+ ...(typeof b === 'object' ? b : { error: b || getMessage(a) }),
40
+ };
41
+ return json(b, { status: a });
42
+ };
43
+
44
+ exports.error = error;
package/cjs/html.js CHANGED
@@ -1 +1,18 @@
1
- "use strict";const t=((t="text/plain; charset=utf-8",e)=>(s,n={})=>{const{headers:r={},...o}=n;return"Response"===s?.constructor.name?s:new Response(e?e(s):s,{headers:{"content-type":t,...r},...o})})("text/html");exports.html=t;
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const html = createResponse('text/html');
17
+
18
+ exports.html = html;
package/cjs/index.js CHANGED
@@ -1 +1,205 @@
1
- "use strict";class e extends Error{status;constructor(e=500,t){super("object"==typeof t?t.error:t),"object"==typeof t&&Object.assign(this,t),this.status=e}}const t=(e="text/plain; charset=utf-8",t)=>(s,o={})=>{const{headers:r={},...n}=o;return"Response"===s?.constructor.name?s:new Response(t?t(s):s,{headers:{"content-type":e,...r},...n})},s=t("application/json; charset=utf-8",JSON.stringify),o=e=>({400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",500:"Internal Server Error"}[e]||"Unknown Error"),r=t("text/html"),n=t("image/jpeg"),a=t("image/png"),c=t("image/webp");exports.Router=({base:e="",routes:t=[]}={})=>({__proto__:new Proxy({},{get:(s,o,r,n)=>(s,...a)=>t.push([o.toUpperCase(),RegExp(`^${(n=(e+"/"+s).replace(/\/+(\/|$)/g,"$1")).replace(/(\/?\.?):(\w+)\+/g,"($1(?<$2>*))").replace(/(\/?\.?):(\w+)/g,"($1(?<$2>[^$1/]+?))").replace(/\./g,"\\.").replace(/(\/?)\*/g,"($1.*)?")}/*$`),a,n])&&r}),routes:t,async handle(e,...s){let o,r,n=new URL(e.url),a=e.query={__proto__:null};for(let[e,t]of n.searchParams)a[e]=void 0===a[e]?t:[a[e],t].flat();for(let[a,c,p,i]of t)if((a===e.method||"ALL"===a)&&(r=n.pathname.match(c))){e.params=r.groups||{},e.route=i;for(let t of p)if(void 0!==(o=await t(e.proxy||e,...s)))return o}}}),exports.StatusError=e,exports.createCors=(e={})=>{const{origins:t=["*"],maxAge:s,methods:o=["GET"],headers:r={}}=e;let n;const a={"content-type":"application/json","Access-Control-Allow-Methods":o.join(", "),...r};s&&(a["Access-Control-Max-Age"]=s);return{corsify:e=>{if(!e)throw new Error("No fetch handler responded and no upstream to proxy to specified.");const{headers:t,status:s,body:o}=e;return[101,301,302,308].includes(s)||t.get("access-control-allow-origin")?e:new Response(o,{status:s,headers:{...Object.fromEntries(t),...a,...n,"content-type":t.get("content-type")}})},preflight:e=>{const s=[...new Set(["OPTIONS",...o])],r=e.headers.get("origin")||"";if(n=(t.includes(r)||t.includes("*"))&&{"Access-Control-Allow-Origin":r},"OPTIONS"===e.method){const t={...a,"Access-Control-Allow-Methods":s.join(", "),"Access-Control-Allow-Headers":e.headers.get("Access-Control-Request-Headers"),...n};return new Response(null,{headers:e.headers.get("Origin")&&e.headers.get("Access-Control-Request-Method")&&e.headers.get("Access-Control-Request-Headers")?t:{Allow:s.join(", ")}})}}}},exports.createResponse=t,exports.error=(e=500,t)=>{if(e instanceof Error){const{message:s,...r}=e;e=e.status||500,t={error:s||o(e),...r}}return t={status:e,..."object"==typeof t?t:{error:t||o(e)}},s(t,{status:e})},exports.html=r,exports.jpeg=n,exports.json=s,exports.png=a,exports.status=e=>new Response(null,{status:e}),exports.text=(e,t)=>new Response(e,t),exports.webp=c,exports.withContent=async e=>{e.headers.get("content-type")?.includes("json")&&(e.content=await e.json())},exports.withCookies=e=>{e.cookies=(e.headers.get("Cookie")||"").split(/;\s*/).map((e=>e.split(/=(.+)/))).reduce(((e,[t,s])=>s?(e[t]=s,e):e),{})},exports.withParams=e=>{e.proxy=new Proxy(e.proxy||e,{get:(t,s)=>{let o;return void 0!==(o=t[s])?o.bind?.(e)||o:t?.params?.[s]}})};
1
+ 'use strict';
2
+
3
+ const Router = ({ base = '', routes = [] } = {}) =>
4
+ // @ts-expect-error TypeScript doesn't know that Proxy makes this work
5
+ ({
6
+ __proto__: new Proxy({}, {
7
+ // @ts-expect-error (we're adding an expected prop "path" to the get)
8
+ get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([
9
+ prop.toUpperCase(),
10
+ RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash
11
+ .replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params
12
+ .replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format
13
+ .replace(/\./g, '\\.') // dot in path
14
+ .replace(/(\/?)\*/g, '($1.*)?') // wildcard
15
+ }/*$`),
16
+ handlers,
17
+ path, // embed clean route path
18
+ ]) && receiver
19
+ }),
20
+ routes,
21
+ async handle(request, ...args) {
22
+ let response, match, url = new URL(request.url), query = request.query = { __proto__: null };
23
+ for (let [k, v] of url.searchParams) {
24
+ query[k] = query[k] === undefined ? v : [query[k], v].flat();
25
+ }
26
+ for (let [method, regex, handlers, path] of routes) {
27
+ if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) {
28
+ request.params = match.groups || {}; // embed params in request
29
+ request.route = path; // embed route path in request
30
+ for (let handler of handlers) {
31
+ if ((response = await handler(request.proxy || request, ...args)) !== undefined)
32
+ return response;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ });
38
+
39
+ class StatusError extends Error {
40
+ status;
41
+ constructor(status = 500, body) {
42
+ super(typeof body === 'object' ? body.error : body);
43
+ typeof body === 'object' && Object.assign(this, body);
44
+ this.status = status;
45
+ }
46
+ }
47
+
48
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
49
+ const { headers = {}, ...rest } = options;
50
+ if (body?.constructor.name === 'Response')
51
+ return body;
52
+ return new Response(transform ? transform(body) : body, {
53
+ headers: {
54
+ 'content-type': format,
55
+ ...headers,
56
+ },
57
+ ...rest,
58
+ });
59
+ };
60
+
61
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
62
+
63
+ const getMessage = (code) => {
64
+ return ({
65
+ 400: 'Bad Request',
66
+ 401: 'Unauthorized',
67
+ 403: 'Forbidden',
68
+ 404: 'Not Found',
69
+ 500: 'Internal Server Error',
70
+ }[code] || 'Unknown Error');
71
+ };
72
+ const error = (a = 500, b) => {
73
+ // handle passing an Error | StatusError directly in
74
+ if (a instanceof Error) {
75
+ const { message, ...err } = a;
76
+ a = a.status || 500;
77
+ b = {
78
+ error: message || getMessage(a),
79
+ ...err,
80
+ };
81
+ }
82
+ b = {
83
+ status: a,
84
+ ...(typeof b === 'object' ? b : { error: b || getMessage(a) }),
85
+ };
86
+ return json(b, { status: a });
87
+ };
88
+
89
+ const status = (status) => new Response(null, { status });
90
+
91
+ const text = (message, options) => new Response(message, options);
92
+
93
+ const html = createResponse('text/html');
94
+
95
+ const jpeg = createResponse('image/jpeg');
96
+
97
+ const png = createResponse('image/png');
98
+
99
+ const webp = createResponse('image/webp');
100
+
101
+ // withContent - embeds any request body as request.content
102
+ const withContent = async (request) => {
103
+ if (request.headers.get('content-type')?.includes('json'))
104
+ request.content = await request.json();
105
+ };
106
+
107
+ // withCookies - embeds cookies object into the request
108
+ const withCookies = (r) => {
109
+ r.cookies = (r.headers.get('Cookie') || '')
110
+ .split(/;\s*/)
111
+ .map((p) => p.split(/=(.+)/))
112
+ .reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {});
113
+ };
114
+
115
+ const withParams = (request) => {
116
+ request.proxy = new Proxy(request.proxy || request, {
117
+ get: (obj, prop) => {
118
+ let p;
119
+ if ((p = obj[prop]) !== undefined)
120
+ return p.bind?.(request) || p;
121
+ return obj?.params?.[prop];
122
+ },
123
+ });
124
+ };
125
+
126
+ // Create CORS function with default options.
127
+ const createCors = (options = {}) => {
128
+ // Destructure and set defaults for options.
129
+ const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options;
130
+ let allowOrigin;
131
+ // Initial response headers.
132
+ const rHeaders = {
133
+ 'content-type': 'application/json',
134
+ 'Access-Control-Allow-Methods': methods.join(', '),
135
+ ...headers,
136
+ };
137
+ // Set max age if provided.
138
+ if (maxAge)
139
+ rHeaders['Access-Control-Max-Age'] = maxAge;
140
+ // Pre-flight function.
141
+ const preflight = (r) => {
142
+ // Use methods set.
143
+ const useMethods = [...new Set(['OPTIONS', ...methods])];
144
+ const origin = r.headers.get('origin') || '';
145
+ // Set allowOrigin globally.
146
+ allowOrigin = (origins.includes(origin) || origins.includes('*')) && {
147
+ 'Access-Control-Allow-Origin': origin,
148
+ };
149
+ // Check if method is OPTIONS.
150
+ if (r.method === 'OPTIONS') {
151
+ const reqHeaders = {
152
+ ...rHeaders,
153
+ 'Access-Control-Allow-Methods': useMethods.join(', '),
154
+ 'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'),
155
+ ...allowOrigin,
156
+ };
157
+ // Handle CORS pre-flight request.
158
+ return new Response(null, {
159
+ headers: r.headers.get('Origin') &&
160
+ r.headers.get('Access-Control-Request-Method') &&
161
+ r.headers.get('Access-Control-Request-Headers')
162
+ ? reqHeaders
163
+ : { Allow: useMethods.join(', ') },
164
+ });
165
+ }
166
+ };
167
+ // Corsify function.
168
+ const corsify = (response) => {
169
+ if (!response)
170
+ throw new Error('No fetch handler responded and no upstream to proxy to specified.');
171
+ const { headers, status, body } = response;
172
+ // Bypass for protocol shifts or redirects, or if CORS is already set.
173
+ if ([101, 301, 302, 308].includes(status) ||
174
+ headers.get('access-control-allow-origin'))
175
+ return response;
176
+ // Return new response with CORS headers.
177
+ return new Response(body, {
178
+ status,
179
+ headers: {
180
+ ...Object.fromEntries(headers),
181
+ ...rHeaders,
182
+ ...allowOrigin,
183
+ 'content-type': headers.get('content-type'),
184
+ },
185
+ });
186
+ };
187
+ // Return corsify and preflight methods.
188
+ return { corsify, preflight };
189
+ };
190
+
191
+ exports.Router = Router;
192
+ exports.StatusError = StatusError;
193
+ exports.createCors = createCors;
194
+ exports.createResponse = createResponse;
195
+ exports.error = error;
196
+ exports.html = html;
197
+ exports.jpeg = jpeg;
198
+ exports.json = json;
199
+ exports.png = png;
200
+ exports.status = status;
201
+ exports.text = text;
202
+ exports.webp = webp;
203
+ exports.withContent = withContent;
204
+ exports.withCookies = withCookies;
205
+ exports.withParams = withParams;
package/cjs/jpeg.js CHANGED
@@ -1 +1,18 @@
1
- "use strict";const e=((e="text/plain; charset=utf-8",t)=>(s,n={})=>{const{headers:r={},...o}=n;return"Response"===s?.constructor.name?s:new Response(t?t(s):s,{headers:{"content-type":e,...r},...o})})("image/jpeg");exports.jpeg=e;
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const jpeg = createResponse('image/jpeg');
17
+
18
+ exports.jpeg = jpeg;
package/cjs/json.js CHANGED
@@ -1 +1,18 @@
1
- "use strict";const t=((t="text/plain; charset=utf-8",e)=>(s,n={})=>{const{headers:o={},...r}=n;return"Response"===s?.constructor.name?s:new Response(e?e(s):s,{headers:{"content-type":t,...o},...r})})("application/json; charset=utf-8",JSON.stringify);exports.json=t;
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
17
+
18
+ exports.json = json;
package/cjs/png.js CHANGED
@@ -1 +1,18 @@
1
- "use strict";const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:r={},...o}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...r},...o})})("image/png");exports.png=e;
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const png = createResponse('image/png');
17
+
18
+ exports.png = png;
package/cjs/status.js CHANGED
@@ -1 +1,5 @@
1
- "use strict";exports.status=s=>new Response(null,{status:s});
1
+ 'use strict';
2
+
3
+ const status = (status) => new Response(null, { status });
4
+
5
+ exports.status = status;
package/cjs/text.js CHANGED
@@ -1 +1,5 @@
1
- "use strict";exports.text=(e,s)=>new Response(e,s);
1
+ 'use strict';
2
+
3
+ const text = (message, options) => new Response(message, options);
4
+
5
+ exports.text = text;
package/cjs/webp.js CHANGED
@@ -1 +1,18 @@
1
- "use strict";const e=((e="text/plain; charset=utf-8",t)=>(s,n={})=>{const{headers:r={},...o}=n;return"Response"===s?.constructor.name?s:new Response(t?t(s):s,{headers:{"content-type":e,...r},...o})})("image/webp");exports.webp=e;
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const webp = createResponse('image/webp');
17
+
18
+ exports.webp = webp;
package/cjs/websocket.js CHANGED
@@ -1 +1,22 @@
1
- "use strict";exports.websocket=(e,t={})=>((e="text/plain; charset=utf-8",t)=>(s,n={})=>{const{headers:o={},...r}=n;return"Response"===s?.constructor.name?s:new Response(t?t(s):s,{headers:{"content-type":e,...o},...r})})()(null,{status:101,webSocket:e,...t});
1
+ 'use strict';
2
+
3
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
4
+ const { headers = {}, ...rest } = options;
5
+ if (body?.constructor.name === 'Response')
6
+ return body;
7
+ return new Response(transform ? transform(body) : body, {
8
+ headers: {
9
+ 'content-type': format,
10
+ ...headers,
11
+ },
12
+ ...rest,
13
+ });
14
+ };
15
+
16
+ const websocket = (client, options = {}) => createResponse()(null, {
17
+ status: 101,
18
+ webSocket: client,
19
+ ...options,
20
+ });
21
+
22
+ exports.websocket = websocket;
@@ -1 +1,9 @@
1
- "use strict";exports.withContent=async t=>{t.headers.get("content-type")?.includes("json")&&(t.content=await t.json())};
1
+ 'use strict';
2
+
3
+ // withContent - embeds any request body as request.content
4
+ const withContent = async (request) => {
5
+ if (request.headers.get('content-type')?.includes('json'))
6
+ request.content = await request.json();
7
+ };
8
+
9
+ exports.withContent = withContent;
@@ -1 +1,11 @@
1
- "use strict";exports.withCookies=e=>{e.cookies=(e.headers.get("Cookie")||"").split(/;\s*/).map((e=>e.split(/=(.+)/))).reduce(((e,[s,i])=>i?(e[s]=i,e):e),{})};
1
+ 'use strict';
2
+
3
+ // withCookies - embeds cookies object into the request
4
+ const withCookies = (r) => {
5
+ r.cookies = (r.headers.get('Cookie') || '')
6
+ .split(/;\s*/)
7
+ .map((p) => p.split(/=(.+)/))
8
+ .reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {});
9
+ };
10
+
11
+ exports.withCookies = withCookies;
package/cjs/withParams.js CHANGED
@@ -1 +1,14 @@
1
- "use strict";exports.withParams=r=>{r.proxy=new Proxy(r.proxy||r,{get:(t,e)=>{let o;return void 0!==(o=t[e])?o.bind?.(r)||o:t?.params?.[e]}})};
1
+ 'use strict';
2
+
3
+ const withParams = (request) => {
4
+ request.proxy = new Proxy(request.proxy || request, {
5
+ get: (obj, prop) => {
6
+ let p;
7
+ if ((p = obj[prop]) !== undefined)
8
+ return p.bind?.(request) || p;
9
+ return obj?.params?.[prop];
10
+ },
11
+ });
12
+ };
13
+
14
+ exports.withParams = withParams;
package/createCors.js CHANGED
@@ -1 +1,66 @@
1
- const e=(e={})=>{const{origins:o=["*"],maxAge:s,methods:t=["GET"],headers:n={}}=e;let r;const c={"content-type":"application/json","Access-Control-Allow-Methods":t.join(", "),...n};s&&(c["Access-Control-Max-Age"]=s);return{corsify:e=>{if(!e)throw new Error("No fetch handler responded and no upstream to proxy to specified.");const{headers:o,status:s,body:t}=e;return[101,301,302,308].includes(s)||o.get("access-control-allow-origin")?e:new Response(t,{status:s,headers:{...Object.fromEntries(o),...c,...r,"content-type":o.get("content-type")}})},preflight:e=>{const s=[...new Set(["OPTIONS",...t])],n=e.headers.get("origin")||"";if(r=(o.includes(n)||o.includes("*"))&&{"Access-Control-Allow-Origin":n},"OPTIONS"===e.method){const o={...c,"Access-Control-Allow-Methods":s.join(", "),"Access-Control-Allow-Headers":e.headers.get("Access-Control-Request-Headers"),...r};return new Response(null,{headers:e.headers.get("Origin")&&e.headers.get("Access-Control-Request-Method")&&e.headers.get("Access-Control-Request-Headers")?o:{Allow:s.join(", ")}})}}}};export{e as createCors};
1
+ // Create CORS function with default options.
2
+ const createCors = (options = {}) => {
3
+ // Destructure and set defaults for options.
4
+ const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options;
5
+ let allowOrigin;
6
+ // Initial response headers.
7
+ const rHeaders = {
8
+ 'content-type': 'application/json',
9
+ 'Access-Control-Allow-Methods': methods.join(', '),
10
+ ...headers,
11
+ };
12
+ // Set max age if provided.
13
+ if (maxAge)
14
+ rHeaders['Access-Control-Max-Age'] = maxAge;
15
+ // Pre-flight function.
16
+ const preflight = (r) => {
17
+ // Use methods set.
18
+ const useMethods = [...new Set(['OPTIONS', ...methods])];
19
+ const origin = r.headers.get('origin') || '';
20
+ // Set allowOrigin globally.
21
+ allowOrigin = (origins.includes(origin) || origins.includes('*')) && {
22
+ 'Access-Control-Allow-Origin': origin,
23
+ };
24
+ // Check if method is OPTIONS.
25
+ if (r.method === 'OPTIONS') {
26
+ const reqHeaders = {
27
+ ...rHeaders,
28
+ 'Access-Control-Allow-Methods': useMethods.join(', '),
29
+ 'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'),
30
+ ...allowOrigin,
31
+ };
32
+ // Handle CORS pre-flight request.
33
+ return new Response(null, {
34
+ headers: r.headers.get('Origin') &&
35
+ r.headers.get('Access-Control-Request-Method') &&
36
+ r.headers.get('Access-Control-Request-Headers')
37
+ ? reqHeaders
38
+ : { Allow: useMethods.join(', ') },
39
+ });
40
+ }
41
+ };
42
+ // Corsify function.
43
+ const corsify = (response) => {
44
+ if (!response)
45
+ throw new Error('No fetch handler responded and no upstream to proxy to specified.');
46
+ const { headers, status, body } = response;
47
+ // Bypass for protocol shifts or redirects, or if CORS is already set.
48
+ if ([101, 301, 302, 308].includes(status) ||
49
+ headers.get('access-control-allow-origin'))
50
+ return response;
51
+ // Return new response with CORS headers.
52
+ return new Response(body, {
53
+ status,
54
+ headers: {
55
+ ...Object.fromEntries(headers),
56
+ ...rHeaders,
57
+ ...allowOrigin,
58
+ 'content-type': headers.get('content-type'),
59
+ },
60
+ });
61
+ };
62
+ // Return corsify and preflight methods.
63
+ return { corsify, preflight };
64
+ };
65
+
66
+ export { createCors };
package/createResponse.js CHANGED
@@ -1 +1,14 @@
1
- const e=(e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})};export{e as createResponse};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ export { createResponse };
package/error.js CHANGED
@@ -1 +1,42 @@
1
- const e=((e="text/plain; charset=utf-8",r)=>(t,n={})=>{const{headers:o={},...s}=n;return"Response"===t?.constructor.name?t:new Response(r?r(t):t,{headers:{"content-type":e,...o},...s})})("application/json; charset=utf-8",JSON.stringify),r=e=>({400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",500:"Internal Server Error"}[e]||"Unknown Error"),t=(t=500,n)=>{if(t instanceof Error){const{message:e,...o}=t;t=t.status||500,n={error:e||r(t),...o}}return n={status:t,..."object"==typeof n?n:{error:n||r(t)}},e(n,{status:t})};export{t as error};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
15
+
16
+ const getMessage = (code) => {
17
+ return ({
18
+ 400: 'Bad Request',
19
+ 401: 'Unauthorized',
20
+ 403: 'Forbidden',
21
+ 404: 'Not Found',
22
+ 500: 'Internal Server Error',
23
+ }[code] || 'Unknown Error');
24
+ };
25
+ const error = (a = 500, b) => {
26
+ // handle passing an Error | StatusError directly in
27
+ if (a instanceof Error) {
28
+ const { message, ...err } = a;
29
+ a = a.status || 500;
30
+ b = {
31
+ error: message || getMessage(a),
32
+ ...err,
33
+ };
34
+ }
35
+ b = {
36
+ status: a,
37
+ ...(typeof b === 'object' ? b : { error: b || getMessage(a) }),
38
+ };
39
+ return json(b, { status: a });
40
+ };
41
+
42
+ export { error };
package/html.js CHANGED
@@ -1 +1,16 @@
1
- const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})("text/html");export{e as html};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const html = createResponse('text/html');
15
+
16
+ export { html };
package/index.js CHANGED
@@ -1 +1,189 @@
1
- const e=({base:e="",routes:t=[]}={})=>({__proto__:new Proxy({},{get:(o,s,r,n)=>(o,...a)=>t.push([s.toUpperCase(),RegExp(`^${(n=(e+"/"+o).replace(/\/+(\/|$)/g,"$1")).replace(/(\/?\.?):(\w+)\+/g,"($1(?<$2>*))").replace(/(\/?\.?):(\w+)/g,"($1(?<$2>[^$1/]+?))").replace(/\./g,"\\.").replace(/(\/?)\*/g,"($1.*)?")}/*$`),a,n])&&r}),routes:t,async handle(e,...o){let s,r,n=new URL(e.url),a=e.query={__proto__:null};for(let[e,t]of n.searchParams)a[e]=void 0===a[e]?t:[a[e],t].flat();for(let[a,c,l,i]of t)if((a===e.method||"ALL"===a)&&(r=n.pathname.match(c))){e.params=r.groups||{},e.route=i;for(let t of l)if(void 0!==(s=await t(e.proxy||e,...o)))return s}}});class t extends Error{status;constructor(e=500,t){super("object"==typeof t?t.error:t),"object"==typeof t&&Object.assign(this,t),this.status=e}}const o=(e="text/plain; charset=utf-8",t)=>(o,s={})=>{const{headers:r={},...n}=s;return"Response"===o?.constructor.name?o:new Response(t?t(o):o,{headers:{"content-type":e,...r},...n})},s=o("application/json; charset=utf-8",JSON.stringify),r=e=>({400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",500:"Internal Server Error"}[e]||"Unknown Error"),n=(e=500,t)=>{if(e instanceof Error){const{message:o,...s}=e;e=e.status||500,t={error:o||r(e),...s}}return t={status:e,..."object"==typeof t?t:{error:t||r(e)}},s(t,{status:e})},a=e=>new Response(null,{status:e}),c=(e,t)=>new Response(e,t),l=o("text/html"),i=o("image/jpeg"),p=o("image/png"),d=o("image/webp"),u=async e=>{e.headers.get("content-type")?.includes("json")&&(e.content=await e.json())},g=e=>{e.cookies=(e.headers.get("Cookie")||"").split(/;\s*/).map((e=>e.split(/=(.+)/))).reduce(((e,[t,o])=>o?(e[t]=o,e):e),{})},h=e=>{e.proxy=new Proxy(e.proxy||e,{get:(t,o)=>{let s;return void 0!==(s=t[o])?s.bind?.(e)||s:t?.params?.[o]}})},f=(e={})=>{const{origins:t=["*"],maxAge:o,methods:s=["GET"],headers:r={}}=e;let n;const a={"content-type":"application/json","Access-Control-Allow-Methods":s.join(", "),...r};o&&(a["Access-Control-Max-Age"]=o);return{corsify:e=>{if(!e)throw new Error("No fetch handler responded and no upstream to proxy to specified.");const{headers:t,status:o,body:s}=e;return[101,301,302,308].includes(o)||t.get("access-control-allow-origin")?e:new Response(s,{status:o,headers:{...Object.fromEntries(t),...a,...n,"content-type":t.get("content-type")}})},preflight:e=>{const o=[...new Set(["OPTIONS",...s])],r=e.headers.get("origin")||"";if(n=(t.includes(r)||t.includes("*"))&&{"Access-Control-Allow-Origin":r},"OPTIONS"===e.method){const t={...a,"Access-Control-Allow-Methods":o.join(", "),"Access-Control-Allow-Headers":e.headers.get("Access-Control-Request-Headers"),...n};return new Response(null,{headers:e.headers.get("Origin")&&e.headers.get("Access-Control-Request-Method")&&e.headers.get("Access-Control-Request-Headers")?t:{Allow:o.join(", ")}})}}}};export{e as Router,t as StatusError,f as createCors,o as createResponse,n as error,l as html,i as jpeg,s as json,p as png,a as status,c as text,d as webp,u as withContent,g as withCookies,h as withParams};
1
+ const Router = ({ base = '', routes = [] } = {}) =>
2
+ // @ts-expect-error TypeScript doesn't know that Proxy makes this work
3
+ ({
4
+ __proto__: new Proxy({}, {
5
+ // @ts-expect-error (we're adding an expected prop "path" to the get)
6
+ get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([
7
+ prop.toUpperCase(),
8
+ RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash
9
+ .replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params
10
+ .replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format
11
+ .replace(/\./g, '\\.') // dot in path
12
+ .replace(/(\/?)\*/g, '($1.*)?') // wildcard
13
+ }/*$`),
14
+ handlers,
15
+ path, // embed clean route path
16
+ ]) && receiver
17
+ }),
18
+ routes,
19
+ async handle(request, ...args) {
20
+ let response, match, url = new URL(request.url), query = request.query = { __proto__: null };
21
+ for (let [k, v] of url.searchParams) {
22
+ query[k] = query[k] === undefined ? v : [query[k], v].flat();
23
+ }
24
+ for (let [method, regex, handlers, path] of routes) {
25
+ if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) {
26
+ request.params = match.groups || {}; // embed params in request
27
+ request.route = path; // embed route path in request
28
+ for (let handler of handlers) {
29
+ if ((response = await handler(request.proxy || request, ...args)) !== undefined)
30
+ return response;
31
+ }
32
+ }
33
+ }
34
+ }
35
+ });
36
+
37
+ class StatusError extends Error {
38
+ status;
39
+ constructor(status = 500, body) {
40
+ super(typeof body === 'object' ? body.error : body);
41
+ typeof body === 'object' && Object.assign(this, body);
42
+ this.status = status;
43
+ }
44
+ }
45
+
46
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
47
+ const { headers = {}, ...rest } = options;
48
+ if (body?.constructor.name === 'Response')
49
+ return body;
50
+ return new Response(transform ? transform(body) : body, {
51
+ headers: {
52
+ 'content-type': format,
53
+ ...headers,
54
+ },
55
+ ...rest,
56
+ });
57
+ };
58
+
59
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
60
+
61
+ const getMessage = (code) => {
62
+ return ({
63
+ 400: 'Bad Request',
64
+ 401: 'Unauthorized',
65
+ 403: 'Forbidden',
66
+ 404: 'Not Found',
67
+ 500: 'Internal Server Error',
68
+ }[code] || 'Unknown Error');
69
+ };
70
+ const error = (a = 500, b) => {
71
+ // handle passing an Error | StatusError directly in
72
+ if (a instanceof Error) {
73
+ const { message, ...err } = a;
74
+ a = a.status || 500;
75
+ b = {
76
+ error: message || getMessage(a),
77
+ ...err,
78
+ };
79
+ }
80
+ b = {
81
+ status: a,
82
+ ...(typeof b === 'object' ? b : { error: b || getMessage(a) }),
83
+ };
84
+ return json(b, { status: a });
85
+ };
86
+
87
+ const status = (status) => new Response(null, { status });
88
+
89
+ const text = (message, options) => new Response(message, options);
90
+
91
+ const html = createResponse('text/html');
92
+
93
+ const jpeg = createResponse('image/jpeg');
94
+
95
+ const png = createResponse('image/png');
96
+
97
+ const webp = createResponse('image/webp');
98
+
99
+ // withContent - embeds any request body as request.content
100
+ const withContent = async (request) => {
101
+ if (request.headers.get('content-type')?.includes('json'))
102
+ request.content = await request.json();
103
+ };
104
+
105
+ // withCookies - embeds cookies object into the request
106
+ const withCookies = (r) => {
107
+ r.cookies = (r.headers.get('Cookie') || '')
108
+ .split(/;\s*/)
109
+ .map((p) => p.split(/=(.+)/))
110
+ .reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {});
111
+ };
112
+
113
+ const withParams = (request) => {
114
+ request.proxy = new Proxy(request.proxy || request, {
115
+ get: (obj, prop) => {
116
+ let p;
117
+ if ((p = obj[prop]) !== undefined)
118
+ return p.bind?.(request) || p;
119
+ return obj?.params?.[prop];
120
+ },
121
+ });
122
+ };
123
+
124
+ // Create CORS function with default options.
125
+ const createCors = (options = {}) => {
126
+ // Destructure and set defaults for options.
127
+ const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options;
128
+ let allowOrigin;
129
+ // Initial response headers.
130
+ const rHeaders = {
131
+ 'content-type': 'application/json',
132
+ 'Access-Control-Allow-Methods': methods.join(', '),
133
+ ...headers,
134
+ };
135
+ // Set max age if provided.
136
+ if (maxAge)
137
+ rHeaders['Access-Control-Max-Age'] = maxAge;
138
+ // Pre-flight function.
139
+ const preflight = (r) => {
140
+ // Use methods set.
141
+ const useMethods = [...new Set(['OPTIONS', ...methods])];
142
+ const origin = r.headers.get('origin') || '';
143
+ // Set allowOrigin globally.
144
+ allowOrigin = (origins.includes(origin) || origins.includes('*')) && {
145
+ 'Access-Control-Allow-Origin': origin,
146
+ };
147
+ // Check if method is OPTIONS.
148
+ if (r.method === 'OPTIONS') {
149
+ const reqHeaders = {
150
+ ...rHeaders,
151
+ 'Access-Control-Allow-Methods': useMethods.join(', '),
152
+ 'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'),
153
+ ...allowOrigin,
154
+ };
155
+ // Handle CORS pre-flight request.
156
+ return new Response(null, {
157
+ headers: r.headers.get('Origin') &&
158
+ r.headers.get('Access-Control-Request-Method') &&
159
+ r.headers.get('Access-Control-Request-Headers')
160
+ ? reqHeaders
161
+ : { Allow: useMethods.join(', ') },
162
+ });
163
+ }
164
+ };
165
+ // Corsify function.
166
+ const corsify = (response) => {
167
+ if (!response)
168
+ throw new Error('No fetch handler responded and no upstream to proxy to specified.');
169
+ const { headers, status, body } = response;
170
+ // Bypass for protocol shifts or redirects, or if CORS is already set.
171
+ if ([101, 301, 302, 308].includes(status) ||
172
+ headers.get('access-control-allow-origin'))
173
+ return response;
174
+ // Return new response with CORS headers.
175
+ return new Response(body, {
176
+ status,
177
+ headers: {
178
+ ...Object.fromEntries(headers),
179
+ ...rHeaders,
180
+ ...allowOrigin,
181
+ 'content-type': headers.get('content-type'),
182
+ },
183
+ });
184
+ };
185
+ // Return corsify and preflight methods.
186
+ return { corsify, preflight };
187
+ };
188
+
189
+ export { Router, StatusError, createCors, createResponse, error, html, jpeg, json, png, status, text, webp, withContent, withCookies, withParams };
package/jpeg.js CHANGED
@@ -1 +1,16 @@
1
- const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})("image/jpeg");export{e as jpeg};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const jpeg = createResponse('image/jpeg');
15
+
16
+ export { jpeg };
package/json.js CHANGED
@@ -1 +1,16 @@
1
- const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})("application/json; charset=utf-8",JSON.stringify);export{e as json};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const json = createResponse('application/json; charset=utf-8', JSON.stringify);
15
+
16
+ export { json };
package/package.json CHANGED
@@ -1,88 +1,11 @@
1
1
  {
2
2
  "name": "itty-router",
3
- "version": "4.0.10",
3
+ "version": "4.0.11",
4
4
  "description": "A tiny, zero-dependency router, designed to make beautiful APIs in any environment.",
5
5
  "type": "module",
6
- "main": "index.js",
7
- "module": "index.js",
8
- "types": "index.d.ts",
9
- "exports": {
10
- ".": {
11
- "import": "./index.js",
12
- "require": "./cjs/index.js",
13
- "types": "./index.d.ts"
14
- },
15
- "./createCors": {
16
- "import": "./createCors.js",
17
- "require": "./cjs/createCors.js",
18
- "types": "./createCors.d.ts"
19
- },
20
- "./createResponse": {
21
- "import": "./createResponse.js",
22
- "require": "./cjs/createResponse.js",
23
- "types": "./createResponse.d.ts"
24
- },
25
- "./error": {
26
- "import": "./error.js",
27
- "require": "./cjs/error.js",
28
- "types": "./error.d.ts"
29
- },
30
- "./html": {
31
- "import": "./html.js",
32
- "require": "./cjs/html.js",
33
- "types": "./html.d.ts"
34
- },
35
- "./jpeg": {
36
- "import": "./jpeg.js",
37
- "require": "./cjs/jpeg.js",
38
- "types": "./jpeg.d.ts"
39
- },
40
- "./png": {
41
- "import": "./png.js",
42
- "require": "./cjs/png.js",
43
- "types": "./png.d.ts"
44
- },
45
- "./Router": {
46
- "import": "./Router.js",
47
- "require": "./cjs/Router.js",
48
- "types": "./Router.d.ts"
49
- },
50
- "./status": {
51
- "import": "./status.js",
52
- "require": "./cjs/status.js",
53
- "types": "./status.d.ts"
54
- },
55
- "./text": {
56
- "import": "./text.js",
57
- "require": "./cjs/text.js",
58
- "types": "./text.d.ts"
59
- },
60
- "./webp": {
61
- "import": "./webp.js",
62
- "require": "./cjs/webp.js",
63
- "types": "./webp.d.ts"
64
- },
65
- "./websocket": {
66
- "import": "./websocket.js",
67
- "require": "./cjs/websocket.js",
68
- "types": "./websocket.d.ts"
69
- },
70
- "./withContent": {
71
- "import": "./withContent.js",
72
- "require": "./cjs/withContent.js",
73
- "types": "./withContent.d.ts"
74
- },
75
- "./withCookies": {
76
- "import": "./withCookies.js",
77
- "require": "./cjs/withCookies.js",
78
- "types": "./withCookies.d.ts"
79
- },
80
- "./withParams": {
81
- "import": "./withParams.js",
82
- "require": "./cjs/withParams.js",
83
- "types": "./withParams.d.ts"
84
- }
85
- },
6
+ "main": "./cjs/index.js",
7
+ "module": "./index.js",
8
+ "types": "./index.d.ts",
86
9
  "keywords": [
87
10
  "api",
88
11
  "router",
@@ -107,9 +30,10 @@
107
30
  "coveralls": "yarn coverage && cat ./coverage/lcov.info | coveralls",
108
31
  "verify": "echo 'verifying module...' && yarn build && yarn test:once",
109
32
  "prerelease": "yarn verify",
110
- "prebuild": "rimraf dist && mkdir dist && yarn coverage && yarn format",
33
+ "prebuild": "rimraf dist && mkdir dist",
111
34
  "build": "rollup -c",
112
35
  "release": "release --tag --push --patch --src=dist",
36
+ "release:next": "release --tag --push --type=next --src=dist",
113
37
  "runtime:bun": "bun example/bun.ts",
114
38
  "runtime:node": "node example/node.js"
115
39
  },
@@ -144,7 +68,7 @@
144
68
  "gzip-size": "^7.0.0",
145
69
  "http": "^0.0.1-security",
146
70
  "isomorphic-fetch": "^3.0.0",
147
- "itty-router": "^4.0.6",
71
+ "itty-router": "^4.0.10-next.4",
148
72
  "jsdom": "^22.1.0",
149
73
  "npm-run-all": "^4.1.5",
150
74
  "prettier": "^2.8.8",
package/png.js CHANGED
@@ -1 +1,16 @@
1
- const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})("image/png");export{e as png};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const png = createResponse('image/png');
15
+
16
+ export { png };
package/status.js CHANGED
@@ -1 +1,3 @@
1
- const s=s=>new Response(null,{status:s});export{s as status};
1
+ const status = (status) => new Response(null, { status });
2
+
3
+ export { status };
package/text.js CHANGED
@@ -1 +1,3 @@
1
- const e=(e,n)=>new Response(e,n);export{e as text};
1
+ const text = (message, options) => new Response(message, options);
2
+
3
+ export { text };
package/webp.js CHANGED
@@ -1 +1,16 @@
1
- const e=((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})("image/webp");export{e as webp};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const webp = createResponse('image/webp');
15
+
16
+ export { webp };
package/websocket.js CHANGED
@@ -1 +1,20 @@
1
- const e=(e,t={})=>((e="text/plain; charset=utf-8",t)=>(n,s={})=>{const{headers:o={},...r}=s;return"Response"===n?.constructor.name?n:new Response(t?t(n):n,{headers:{"content-type":e,...o},...r})})()(null,{status:101,webSocket:e,...t});export{e as websocket};
1
+ const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => {
2
+ const { headers = {}, ...rest } = options;
3
+ if (body?.constructor.name === 'Response')
4
+ return body;
5
+ return new Response(transform ? transform(body) : body, {
6
+ headers: {
7
+ 'content-type': format,
8
+ ...headers,
9
+ },
10
+ ...rest,
11
+ });
12
+ };
13
+
14
+ const websocket = (client, options = {}) => createResponse()(null, {
15
+ status: 101,
16
+ webSocket: client,
17
+ ...options,
18
+ });
19
+
20
+ export { websocket };
package/withContent.js CHANGED
@@ -1 +1,7 @@
1
- const n=async n=>{n.headers.get("content-type")?.includes("json")&&(n.content=await n.json())};export{n as withContent};
1
+ // withContent - embeds any request body as request.content
2
+ const withContent = async (request) => {
3
+ if (request.headers.get('content-type')?.includes('json'))
4
+ request.content = await request.json();
5
+ };
6
+
7
+ export { withContent };
package/withCookies.js CHANGED
@@ -1 +1,9 @@
1
- const e=e=>{e.cookies=(e.headers.get("Cookie")||"").split(/;\s*/).map((e=>e.split(/=(.+)/))).reduce(((e,[o,s])=>s?(e[o]=s,e):e),{})};export{e as withCookies};
1
+ // withCookies - embeds cookies object into the request
2
+ const withCookies = (r) => {
3
+ r.cookies = (r.headers.get('Cookie') || '')
4
+ .split(/;\s*/)
5
+ .map((p) => p.split(/=(.+)/))
6
+ .reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {});
7
+ };
8
+
9
+ export { withCookies };
package/withParams.js CHANGED
@@ -1 +1,12 @@
1
- const r=r=>{r.proxy=new Proxy(r.proxy||r,{get:(o,e)=>{let t;return void 0!==(t=o[e])?t.bind?.(r)||t:o?.params?.[e]}})};export{r as withParams};
1
+ const withParams = (request) => {
2
+ request.proxy = new Proxy(request.proxy || request, {
3
+ get: (obj, prop) => {
4
+ let p;
5
+ if ((p = obj[prop]) !== undefined)
6
+ return p.bind?.(request) || p;
7
+ return obj?.params?.[prop];
8
+ },
9
+ });
10
+ };
11
+
12
+ export { withParams };