itty-router
Advanced tools
Comparing version 4.0.10 to 4.0.11-next.0
@@ -1,1 +0,68 @@ | ||
"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(", ")}})}}}}; | ||
'use strict'; | ||
// Create CORS function with default options. | ||
const createCors = (options = {}) => { | ||
// Destructure and set defaults for options. | ||
const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options; | ||
let allowOrigin; | ||
// Initial response headers. | ||
const rHeaders = { | ||
'content-type': 'application/json', | ||
'Access-Control-Allow-Methods': methods.join(', '), | ||
...headers, | ||
}; | ||
// Set max age if provided. | ||
if (maxAge) | ||
rHeaders['Access-Control-Max-Age'] = maxAge; | ||
// Pre-flight function. | ||
const preflight = (r) => { | ||
// Use methods set. | ||
const useMethods = [...new Set(['OPTIONS', ...methods])]; | ||
const origin = r.headers.get('origin') || ''; | ||
// Set allowOrigin globally. | ||
allowOrigin = (origins.includes(origin) || origins.includes('*')) && { | ||
'Access-Control-Allow-Origin': origin, | ||
}; | ||
// Check if method is OPTIONS. | ||
if (r.method === 'OPTIONS') { | ||
const reqHeaders = { | ||
...rHeaders, | ||
'Access-Control-Allow-Methods': useMethods.join(', '), | ||
'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'), | ||
...allowOrigin, | ||
}; | ||
// Handle CORS pre-flight request. | ||
return new Response(null, { | ||
headers: r.headers.get('Origin') && | ||
r.headers.get('Access-Control-Request-Method') && | ||
r.headers.get('Access-Control-Request-Headers') | ||
? reqHeaders | ||
: { Allow: useMethods.join(', ') }, | ||
}); | ||
} | ||
}; | ||
// Corsify function. | ||
const corsify = (response) => { | ||
if (!response) | ||
throw new Error('No fetch handler responded and no upstream to proxy to specified.'); | ||
const { headers, status, body } = response; | ||
// Bypass for protocol shifts or redirects, or if CORS is already set. | ||
if ([101, 301, 302, 308].includes(status) || | ||
headers.get('access-control-allow-origin')) | ||
return response; | ||
// Return new response with CORS headers. | ||
return new Response(body, { | ||
status, | ||
headers: { | ||
...Object.fromEntries(headers), | ||
...rHeaders, | ||
...allowOrigin, | ||
'content-type': headers.get('content-type'), | ||
}, | ||
}); | ||
}; | ||
// Return corsify and preflight methods. | ||
return { corsify, preflight }; | ||
}; | ||
exports.createCors = createCors; |
@@ -1,1 +0,16 @@ | ||
"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})}; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
exports.createResponse = createResponse; |
@@ -1,1 +0,44 @@ | ||
"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})}; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
const getMessage = (code) => { | ||
return ({ | ||
400: 'Bad Request', | ||
401: 'Unauthorized', | ||
403: 'Forbidden', | ||
404: 'Not Found', | ||
500: 'Internal Server Error', | ||
}[code] || 'Unknown Error'); | ||
}; | ||
const error = (a = 500, b) => { | ||
// handle passing an Error | StatusError directly in | ||
if (a instanceof Error) { | ||
const { message, ...err } = a; | ||
a = a.status || 500; | ||
b = { | ||
error: message || getMessage(a), | ||
...err, | ||
}; | ||
} | ||
b = { | ||
status: a, | ||
...(typeof b === 'object' ? b : { error: b || getMessage(a) }), | ||
}; | ||
return json(b, { status: a }); | ||
}; | ||
exports.error = error; |
@@ -1,1 +0,18 @@ | ||
"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; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const html = createResponse('text/html'); | ||
exports.html = html; |
206
cjs/index.js
@@ -1,1 +0,205 @@ | ||
"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]}})}; | ||
'use strict'; | ||
const Router = ({ base = '', routes = [] } = {}) => | ||
// @ts-expect-error TypeScript doesn't know that Proxy makes this work | ||
({ | ||
__proto__: new Proxy({}, { | ||
// @ts-expect-error (we're adding an expected prop "path" to the get) | ||
get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([ | ||
prop.toUpperCase(), | ||
RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash | ||
.replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params | ||
.replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format | ||
.replace(/\./g, '\\.') // dot in path | ||
.replace(/(\/?)\*/g, '($1.*)?') // wildcard | ||
}/*$`), | ||
handlers, | ||
path, // embed clean route path | ||
]) && receiver | ||
}), | ||
routes, | ||
async handle(request, ...args) { | ||
let response, match, url = new URL(request.url), query = request.query = { __proto__: null }; | ||
for (let [k, v] of url.searchParams) { | ||
query[k] = query[k] === undefined ? v : [query[k], v].flat(); | ||
} | ||
for (let [method, regex, handlers, path] of routes) { | ||
if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) { | ||
request.params = match.groups || {}; // embed params in request | ||
request.route = path; // embed route path in request | ||
for (let handler of handlers) { | ||
if ((response = await handler(request.proxy || request, ...args)) !== undefined) | ||
return response; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
class StatusError extends Error { | ||
status; | ||
constructor(status = 500, body) { | ||
super(typeof body === 'object' ? body.error : body); | ||
typeof body === 'object' && Object.assign(this, body); | ||
this.status = status; | ||
} | ||
} | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
const getMessage = (code) => { | ||
return ({ | ||
400: 'Bad Request', | ||
401: 'Unauthorized', | ||
403: 'Forbidden', | ||
404: 'Not Found', | ||
500: 'Internal Server Error', | ||
}[code] || 'Unknown Error'); | ||
}; | ||
const error = (a = 500, b) => { | ||
// handle passing an Error | StatusError directly in | ||
if (a instanceof Error) { | ||
const { message, ...err } = a; | ||
a = a.status || 500; | ||
b = { | ||
error: message || getMessage(a), | ||
...err, | ||
}; | ||
} | ||
b = { | ||
status: a, | ||
...(typeof b === 'object' ? b : { error: b || getMessage(a) }), | ||
}; | ||
return json(b, { status: a }); | ||
}; | ||
const status = (status) => new Response(null, { status }); | ||
const text = (message, options) => new Response(message, options); | ||
const html = createResponse('text/html'); | ||
const jpeg = createResponse('image/jpeg'); | ||
const png = createResponse('image/png'); | ||
const webp = createResponse('image/webp'); | ||
// withContent - embeds any request body as request.content | ||
const withContent = async (request) => { | ||
if (request.headers.get('content-type')?.includes('json')) | ||
request.content = await request.json(); | ||
}; | ||
// withCookies - embeds cookies object into the request | ||
const withCookies = (r) => { | ||
r.cookies = (r.headers.get('Cookie') || '') | ||
.split(/;\s*/) | ||
.map((p) => p.split(/=(.+)/)) | ||
.reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {}); | ||
}; | ||
const withParams = (request) => { | ||
request.proxy = new Proxy(request.proxy || request, { | ||
get: (obj, prop) => { | ||
let p; | ||
if ((p = obj[prop]) !== undefined) | ||
return p.bind?.(request) || p; | ||
return obj?.params?.[prop]; | ||
}, | ||
}); | ||
}; | ||
// Create CORS function with default options. | ||
const createCors = (options = {}) => { | ||
// Destructure and set defaults for options. | ||
const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options; | ||
let allowOrigin; | ||
// Initial response headers. | ||
const rHeaders = { | ||
'content-type': 'application/json', | ||
'Access-Control-Allow-Methods': methods.join(', '), | ||
...headers, | ||
}; | ||
// Set max age if provided. | ||
if (maxAge) | ||
rHeaders['Access-Control-Max-Age'] = maxAge; | ||
// Pre-flight function. | ||
const preflight = (r) => { | ||
// Use methods set. | ||
const useMethods = [...new Set(['OPTIONS', ...methods])]; | ||
const origin = r.headers.get('origin') || ''; | ||
// Set allowOrigin globally. | ||
allowOrigin = (origins.includes(origin) || origins.includes('*')) && { | ||
'Access-Control-Allow-Origin': origin, | ||
}; | ||
// Check if method is OPTIONS. | ||
if (r.method === 'OPTIONS') { | ||
const reqHeaders = { | ||
...rHeaders, | ||
'Access-Control-Allow-Methods': useMethods.join(', '), | ||
'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'), | ||
...allowOrigin, | ||
}; | ||
// Handle CORS pre-flight request. | ||
return new Response(null, { | ||
headers: r.headers.get('Origin') && | ||
r.headers.get('Access-Control-Request-Method') && | ||
r.headers.get('Access-Control-Request-Headers') | ||
? reqHeaders | ||
: { Allow: useMethods.join(', ') }, | ||
}); | ||
} | ||
}; | ||
// Corsify function. | ||
const corsify = (response) => { | ||
if (!response) | ||
throw new Error('No fetch handler responded and no upstream to proxy to specified.'); | ||
const { headers, status, body } = response; | ||
// Bypass for protocol shifts or redirects, or if CORS is already set. | ||
if ([101, 301, 302, 308].includes(status) || | ||
headers.get('access-control-allow-origin')) | ||
return response; | ||
// Return new response with CORS headers. | ||
return new Response(body, { | ||
status, | ||
headers: { | ||
...Object.fromEntries(headers), | ||
...rHeaders, | ||
...allowOrigin, | ||
'content-type': headers.get('content-type'), | ||
}, | ||
}); | ||
}; | ||
// Return corsify and preflight methods. | ||
return { corsify, preflight }; | ||
}; | ||
exports.Router = Router; | ||
exports.StatusError = StatusError; | ||
exports.createCors = createCors; | ||
exports.createResponse = createResponse; | ||
exports.error = error; | ||
exports.html = html; | ||
exports.jpeg = jpeg; | ||
exports.json = json; | ||
exports.png = png; | ||
exports.status = status; | ||
exports.text = text; | ||
exports.webp = webp; | ||
exports.withContent = withContent; | ||
exports.withCookies = withCookies; | ||
exports.withParams = withParams; |
@@ -1,1 +0,18 @@ | ||
"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; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const jpeg = createResponse('image/jpeg'); | ||
exports.jpeg = jpeg; |
@@ -1,1 +0,18 @@ | ||
"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; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
exports.json = json; |
@@ -1,1 +0,18 @@ | ||
"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; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const png = createResponse('image/png'); | ||
exports.png = png; |
@@ -1,1 +0,39 @@ | ||
"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}}}); | ||
'use strict'; | ||
const Router = ({ base = '', routes = [] } = {}) => | ||
// @ts-expect-error TypeScript doesn't know that Proxy makes this work | ||
({ | ||
__proto__: new Proxy({}, { | ||
// @ts-expect-error (we're adding an expected prop "path" to the get) | ||
get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([ | ||
prop.toUpperCase(), | ||
RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash | ||
.replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params | ||
.replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format | ||
.replace(/\./g, '\\.') // dot in path | ||
.replace(/(\/?)\*/g, '($1.*)?') // wildcard | ||
}/*$`), | ||
handlers, | ||
path, // embed clean route path | ||
]) && receiver | ||
}), | ||
routes, | ||
async handle(request, ...args) { | ||
let response, match, url = new URL(request.url), query = request.query = { __proto__: null }; | ||
for (let [k, v] of url.searchParams) { | ||
query[k] = query[k] === undefined ? v : [query[k], v].flat(); | ||
} | ||
for (let [method, regex, handlers, path] of routes) { | ||
if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) { | ||
request.params = match.groups || {}; // embed params in request | ||
request.route = path; // embed route path in request | ||
for (let handler of handlers) { | ||
if ((response = await handler(request.proxy || request, ...args)) !== undefined) | ||
return response; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
exports.Router = Router; |
@@ -1,1 +0,5 @@ | ||
"use strict";exports.status=s=>new Response(null,{status:s}); | ||
'use strict'; | ||
const status = (status) => new Response(null, { status }); | ||
exports.status = status; |
@@ -1,1 +0,12 @@ | ||
"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; | ||
'use strict'; | ||
class StatusError extends Error { | ||
status; | ||
constructor(status = 500, body) { | ||
super(typeof body === 'object' ? body.error : body); | ||
typeof body === 'object' && Object.assign(this, body); | ||
this.status = status; | ||
} | ||
} | ||
exports.StatusError = StatusError; |
@@ -1,1 +0,5 @@ | ||
"use strict";exports.text=(e,s)=>new Response(e,s); | ||
'use strict'; | ||
const text = (message, options) => new Response(message, options); | ||
exports.text = text; |
@@ -1,1 +0,18 @@ | ||
"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; | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const webp = createResponse('image/webp'); | ||
exports.webp = webp; |
@@ -1,1 +0,22 @@ | ||
"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}); | ||
'use strict'; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const websocket = (client, options = {}) => createResponse()(null, { | ||
status: 101, | ||
webSocket: client, | ||
...options, | ||
}); | ||
exports.websocket = websocket; |
@@ -1,1 +0,9 @@ | ||
"use strict";exports.withContent=async t=>{t.headers.get("content-type")?.includes("json")&&(t.content=await t.json())}; | ||
'use strict'; | ||
// withContent - embeds any request body as request.content | ||
const withContent = async (request) => { | ||
if (request.headers.get('content-type')?.includes('json')) | ||
request.content = await request.json(); | ||
}; | ||
exports.withContent = withContent; |
@@ -1,1 +0,11 @@ | ||
"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),{})}; | ||
'use strict'; | ||
// withCookies - embeds cookies object into the request | ||
const withCookies = (r) => { | ||
r.cookies = (r.headers.get('Cookie') || '') | ||
.split(/;\s*/) | ||
.map((p) => p.split(/=(.+)/)) | ||
.reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {}); | ||
}; | ||
exports.withCookies = withCookies; |
@@ -1,1 +0,14 @@ | ||
"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]}})}; | ||
'use strict'; | ||
const withParams = (request) => { | ||
request.proxy = new Proxy(request.proxy || request, { | ||
get: (obj, prop) => { | ||
let p; | ||
if ((p = obj[prop]) !== undefined) | ||
return p.bind?.(request) || p; | ||
return obj?.params?.[prop]; | ||
}, | ||
}); | ||
}; | ||
exports.withParams = withParams; |
@@ -1,1 +0,66 @@ | ||
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}; | ||
// Create CORS function with default options. | ||
const createCors = (options = {}) => { | ||
// Destructure and set defaults for options. | ||
const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options; | ||
let allowOrigin; | ||
// Initial response headers. | ||
const rHeaders = { | ||
'content-type': 'application/json', | ||
'Access-Control-Allow-Methods': methods.join(', '), | ||
...headers, | ||
}; | ||
// Set max age if provided. | ||
if (maxAge) | ||
rHeaders['Access-Control-Max-Age'] = maxAge; | ||
// Pre-flight function. | ||
const preflight = (r) => { | ||
// Use methods set. | ||
const useMethods = [...new Set(['OPTIONS', ...methods])]; | ||
const origin = r.headers.get('origin') || ''; | ||
// Set allowOrigin globally. | ||
allowOrigin = (origins.includes(origin) || origins.includes('*')) && { | ||
'Access-Control-Allow-Origin': origin, | ||
}; | ||
// Check if method is OPTIONS. | ||
if (r.method === 'OPTIONS') { | ||
const reqHeaders = { | ||
...rHeaders, | ||
'Access-Control-Allow-Methods': useMethods.join(', '), | ||
'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'), | ||
...allowOrigin, | ||
}; | ||
// Handle CORS pre-flight request. | ||
return new Response(null, { | ||
headers: r.headers.get('Origin') && | ||
r.headers.get('Access-Control-Request-Method') && | ||
r.headers.get('Access-Control-Request-Headers') | ||
? reqHeaders | ||
: { Allow: useMethods.join(', ') }, | ||
}); | ||
} | ||
}; | ||
// Corsify function. | ||
const corsify = (response) => { | ||
if (!response) | ||
throw new Error('No fetch handler responded and no upstream to proxy to specified.'); | ||
const { headers, status, body } = response; | ||
// Bypass for protocol shifts or redirects, or if CORS is already set. | ||
if ([101, 301, 302, 308].includes(status) || | ||
headers.get('access-control-allow-origin')) | ||
return response; | ||
// Return new response with CORS headers. | ||
return new Response(body, { | ||
status, | ||
headers: { | ||
...Object.fromEntries(headers), | ||
...rHeaders, | ||
...allowOrigin, | ||
'content-type': headers.get('content-type'), | ||
}, | ||
}); | ||
}; | ||
// Return corsify and preflight methods. | ||
return { corsify, preflight }; | ||
}; | ||
export { createCors }; |
@@ -1,1 +0,14 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
export { createResponse }; |
43
error.js
@@ -1,1 +0,42 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
const getMessage = (code) => { | ||
return ({ | ||
400: 'Bad Request', | ||
401: 'Unauthorized', | ||
403: 'Forbidden', | ||
404: 'Not Found', | ||
500: 'Internal Server Error', | ||
}[code] || 'Unknown Error'); | ||
}; | ||
const error = (a = 500, b) => { | ||
// handle passing an Error | StatusError directly in | ||
if (a instanceof Error) { | ||
const { message, ...err } = a; | ||
a = a.status || 500; | ||
b = { | ||
error: message || getMessage(a), | ||
...err, | ||
}; | ||
} | ||
b = { | ||
status: a, | ||
...(typeof b === 'object' ? b : { error: b || getMessage(a) }), | ||
}; | ||
return json(b, { status: a }); | ||
}; | ||
export { error }; |
17
html.js
@@ -1,1 +0,16 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const html = createResponse('text/html'); | ||
export { html }; |
190
index.js
@@ -1,1 +0,189 @@ | ||
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}; | ||
const Router = ({ base = '', routes = [] } = {}) => | ||
// @ts-expect-error TypeScript doesn't know that Proxy makes this work | ||
({ | ||
__proto__: new Proxy({}, { | ||
// @ts-expect-error (we're adding an expected prop "path" to the get) | ||
get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([ | ||
prop.toUpperCase(), | ||
RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash | ||
.replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params | ||
.replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format | ||
.replace(/\./g, '\\.') // dot in path | ||
.replace(/(\/?)\*/g, '($1.*)?') // wildcard | ||
}/*$`), | ||
handlers, | ||
path, // embed clean route path | ||
]) && receiver | ||
}), | ||
routes, | ||
async handle(request, ...args) { | ||
let response, match, url = new URL(request.url), query = request.query = { __proto__: null }; | ||
for (let [k, v] of url.searchParams) { | ||
query[k] = query[k] === undefined ? v : [query[k], v].flat(); | ||
} | ||
for (let [method, regex, handlers, path] of routes) { | ||
if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) { | ||
request.params = match.groups || {}; // embed params in request | ||
request.route = path; // embed route path in request | ||
for (let handler of handlers) { | ||
if ((response = await handler(request.proxy || request, ...args)) !== undefined) | ||
return response; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
class StatusError extends Error { | ||
status; | ||
constructor(status = 500, body) { | ||
super(typeof body === 'object' ? body.error : body); | ||
typeof body === 'object' && Object.assign(this, body); | ||
this.status = status; | ||
} | ||
} | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
const getMessage = (code) => { | ||
return ({ | ||
400: 'Bad Request', | ||
401: 'Unauthorized', | ||
403: 'Forbidden', | ||
404: 'Not Found', | ||
500: 'Internal Server Error', | ||
}[code] || 'Unknown Error'); | ||
}; | ||
const error = (a = 500, b) => { | ||
// handle passing an Error | StatusError directly in | ||
if (a instanceof Error) { | ||
const { message, ...err } = a; | ||
a = a.status || 500; | ||
b = { | ||
error: message || getMessage(a), | ||
...err, | ||
}; | ||
} | ||
b = { | ||
status: a, | ||
...(typeof b === 'object' ? b : { error: b || getMessage(a) }), | ||
}; | ||
return json(b, { status: a }); | ||
}; | ||
const status = (status) => new Response(null, { status }); | ||
const text = (message, options) => new Response(message, options); | ||
const html = createResponse('text/html'); | ||
const jpeg = createResponse('image/jpeg'); | ||
const png = createResponse('image/png'); | ||
const webp = createResponse('image/webp'); | ||
// withContent - embeds any request body as request.content | ||
const withContent = async (request) => { | ||
if (request.headers.get('content-type')?.includes('json')) | ||
request.content = await request.json(); | ||
}; | ||
// withCookies - embeds cookies object into the request | ||
const withCookies = (r) => { | ||
r.cookies = (r.headers.get('Cookie') || '') | ||
.split(/;\s*/) | ||
.map((p) => p.split(/=(.+)/)) | ||
.reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {}); | ||
}; | ||
const withParams = (request) => { | ||
request.proxy = new Proxy(request.proxy || request, { | ||
get: (obj, prop) => { | ||
let p; | ||
if ((p = obj[prop]) !== undefined) | ||
return p.bind?.(request) || p; | ||
return obj?.params?.[prop]; | ||
}, | ||
}); | ||
}; | ||
// Create CORS function with default options. | ||
const createCors = (options = {}) => { | ||
// Destructure and set defaults for options. | ||
const { origins = ['*'], maxAge, methods = ['GET'], headers = {} } = options; | ||
let allowOrigin; | ||
// Initial response headers. | ||
const rHeaders = { | ||
'content-type': 'application/json', | ||
'Access-Control-Allow-Methods': methods.join(', '), | ||
...headers, | ||
}; | ||
// Set max age if provided. | ||
if (maxAge) | ||
rHeaders['Access-Control-Max-Age'] = maxAge; | ||
// Pre-flight function. | ||
const preflight = (r) => { | ||
// Use methods set. | ||
const useMethods = [...new Set(['OPTIONS', ...methods])]; | ||
const origin = r.headers.get('origin') || ''; | ||
// Set allowOrigin globally. | ||
allowOrigin = (origins.includes(origin) || origins.includes('*')) && { | ||
'Access-Control-Allow-Origin': origin, | ||
}; | ||
// Check if method is OPTIONS. | ||
if (r.method === 'OPTIONS') { | ||
const reqHeaders = { | ||
...rHeaders, | ||
'Access-Control-Allow-Methods': useMethods.join(', '), | ||
'Access-Control-Allow-Headers': r.headers.get('Access-Control-Request-Headers'), | ||
...allowOrigin, | ||
}; | ||
// Handle CORS pre-flight request. | ||
return new Response(null, { | ||
headers: r.headers.get('Origin') && | ||
r.headers.get('Access-Control-Request-Method') && | ||
r.headers.get('Access-Control-Request-Headers') | ||
? reqHeaders | ||
: { Allow: useMethods.join(', ') }, | ||
}); | ||
} | ||
}; | ||
// Corsify function. | ||
const corsify = (response) => { | ||
if (!response) | ||
throw new Error('No fetch handler responded and no upstream to proxy to specified.'); | ||
const { headers, status, body } = response; | ||
// Bypass for protocol shifts or redirects, or if CORS is already set. | ||
if ([101, 301, 302, 308].includes(status) || | ||
headers.get('access-control-allow-origin')) | ||
return response; | ||
// Return new response with CORS headers. | ||
return new Response(body, { | ||
status, | ||
headers: { | ||
...Object.fromEntries(headers), | ||
...rHeaders, | ||
...allowOrigin, | ||
'content-type': headers.get('content-type'), | ||
}, | ||
}); | ||
}; | ||
// Return corsify and preflight methods. | ||
return { corsify, preflight }; | ||
}; | ||
export { Router, StatusError, createCors, createResponse, error, html, jpeg, json, png, status, text, webp, withContent, withCookies, withParams }; |
17
jpeg.js
@@ -1,1 +0,16 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const jpeg = createResponse('image/jpeg'); | ||
export { jpeg }; |
17
json.js
@@ -1,1 +0,16 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const json = createResponse('application/json; charset=utf-8', JSON.stringify); | ||
export { json }; |
{ | ||
"name": "itty-router", | ||
"version": "4.0.10", | ||
"version": "4.0.11-next.0", | ||
"description": "A tiny, zero-dependency router, designed to make beautiful APIs in any environment.", | ||
"type": "module", | ||
"main": "index.js", | ||
"module": "index.js", | ||
"types": "index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": "./index.js", | ||
"require": "./cjs/index.js", | ||
"types": "./index.d.ts" | ||
}, | ||
"./createCors": { | ||
"import": "./createCors.js", | ||
"require": "./cjs/createCors.js", | ||
"types": "./createCors.d.ts" | ||
}, | ||
"./createResponse": { | ||
"import": "./createResponse.js", | ||
"require": "./cjs/createResponse.js", | ||
"types": "./createResponse.d.ts" | ||
}, | ||
"./error": { | ||
"import": "./error.js", | ||
"require": "./cjs/error.js", | ||
"types": "./error.d.ts" | ||
}, | ||
"./html": { | ||
"import": "./html.js", | ||
"require": "./cjs/html.js", | ||
"types": "./html.d.ts" | ||
}, | ||
"./jpeg": { | ||
"import": "./jpeg.js", | ||
"require": "./cjs/jpeg.js", | ||
"types": "./jpeg.d.ts" | ||
}, | ||
"./png": { | ||
"import": "./png.js", | ||
"require": "./cjs/png.js", | ||
"types": "./png.d.ts" | ||
}, | ||
"./Router": { | ||
"import": "./Router.js", | ||
"require": "./cjs/Router.js", | ||
"types": "./Router.d.ts" | ||
}, | ||
"./status": { | ||
"import": "./status.js", | ||
"require": "./cjs/status.js", | ||
"types": "./status.d.ts" | ||
}, | ||
"./text": { | ||
"import": "./text.js", | ||
"require": "./cjs/text.js", | ||
"types": "./text.d.ts" | ||
}, | ||
"./webp": { | ||
"import": "./webp.js", | ||
"require": "./cjs/webp.js", | ||
"types": "./webp.d.ts" | ||
}, | ||
"./websocket": { | ||
"import": "./websocket.js", | ||
"require": "./cjs/websocket.js", | ||
"types": "./websocket.d.ts" | ||
}, | ||
"./withContent": { | ||
"import": "./withContent.js", | ||
"require": "./cjs/withContent.js", | ||
"types": "./withContent.d.ts" | ||
}, | ||
"./withCookies": { | ||
"import": "./withCookies.js", | ||
"require": "./cjs/withCookies.js", | ||
"types": "./withCookies.d.ts" | ||
}, | ||
"./withParams": { | ||
"import": "./withParams.js", | ||
"require": "./cjs/withParams.js", | ||
"types": "./withParams.d.ts" | ||
} | ||
}, | ||
"main": "./index.js", | ||
"module": "./index.mjs", | ||
"types": "./index.d.ts", | ||
"keywords": [ | ||
@@ -110,5 +32,6 @@ "api", | ||
"prerelease": "yarn verify", | ||
"prebuild": "rimraf dist && mkdir dist && yarn coverage && yarn format", | ||
"prebuild": "rimraf dist && mkdir dist", | ||
"build": "rollup -c", | ||
"release": "release --tag --push --patch --src=dist", | ||
"release:next": "release --tag --push --type=next --src=dist", | ||
"runtime:bun": "bun example/bun.ts", | ||
@@ -148,3 +71,3 @@ "runtime:node": "node example/node.js" | ||
"isomorphic-fetch": "^3.0.0", | ||
"itty-router": "^4.0.6", | ||
"itty-router": "^4.0.10-next.4", | ||
"jsdom": "^22.1.0", | ||
@@ -151,0 +74,0 @@ "npm-run-all": "^4.1.5", |
17
png.js
@@ -1,1 +0,16 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const png = createResponse('image/png'); | ||
export { png }; |
@@ -1,1 +0,37 @@ | ||
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}; | ||
const Router = ({ base = '', routes = [] } = {}) => | ||
// @ts-expect-error TypeScript doesn't know that Proxy makes this work | ||
({ | ||
__proto__: new Proxy({}, { | ||
// @ts-expect-error (we're adding an expected prop "path" to the get) | ||
get: (target, prop, receiver, path) => (route, ...handlers) => routes.push([ | ||
prop.toUpperCase(), | ||
RegExp(`^${(path = (base + '/' + route).replace(/\/+(\/|$)/g, '$1')) // strip double & trailing splash | ||
.replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>*))') // greedy params | ||
.replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))') // named params and image format | ||
.replace(/\./g, '\\.') // dot in path | ||
.replace(/(\/?)\*/g, '($1.*)?') // wildcard | ||
}/*$`), | ||
handlers, | ||
path, // embed clean route path | ||
]) && receiver | ||
}), | ||
routes, | ||
async handle(request, ...args) { | ||
let response, match, url = new URL(request.url), query = request.query = { __proto__: null }; | ||
for (let [k, v] of url.searchParams) { | ||
query[k] = query[k] === undefined ? v : [query[k], v].flat(); | ||
} | ||
for (let [method, regex, handlers, path] of routes) { | ||
if ((method === request.method || method === 'ALL') && (match = url.pathname.match(regex))) { | ||
request.params = match.groups || {}; // embed params in request | ||
request.route = path; // embed route path in request | ||
for (let handler of handlers) { | ||
if ((response = await handler(request.proxy || request, ...args)) !== undefined) | ||
return response; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
export { Router }; |
@@ -1,1 +0,3 @@ | ||
const s=s=>new Response(null,{status:s});export{s as status}; | ||
const status = (status) => new Response(null, { status }); | ||
export { status }; |
@@ -1,1 +0,10 @@ | ||
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}; | ||
class StatusError extends Error { | ||
status; | ||
constructor(status = 500, body) { | ||
super(typeof body === 'object' ? body.error : body); | ||
typeof body === 'object' && Object.assign(this, body); | ||
this.status = status; | ||
} | ||
} | ||
export { StatusError }; |
@@ -1,1 +0,3 @@ | ||
const e=(e,n)=>new Response(e,n);export{e as text}; | ||
const text = (message, options) => new Response(message, options); | ||
export { text }; |
17
webp.js
@@ -1,1 +0,16 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const webp = createResponse('image/webp'); | ||
export { webp }; |
@@ -1,1 +0,20 @@ | ||
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}; | ||
const createResponse = (format = 'text/plain; charset=utf-8', transform) => (body, options = {}) => { | ||
const { headers = {}, ...rest } = options; | ||
if (body?.constructor.name === 'Response') | ||
return body; | ||
return new Response(transform ? transform(body) : body, { | ||
headers: { | ||
'content-type': format, | ||
...headers, | ||
}, | ||
...rest, | ||
}); | ||
}; | ||
const websocket = (client, options = {}) => createResponse()(null, { | ||
status: 101, | ||
webSocket: client, | ||
...options, | ||
}); | ||
export { websocket }; |
@@ -1,1 +0,7 @@ | ||
const n=async n=>{n.headers.get("content-type")?.includes("json")&&(n.content=await n.json())};export{n as withContent}; | ||
// withContent - embeds any request body as request.content | ||
const withContent = async (request) => { | ||
if (request.headers.get('content-type')?.includes('json')) | ||
request.content = await request.json(); | ||
}; | ||
export { withContent }; |
@@ -1,1 +0,9 @@ | ||
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}; | ||
// withCookies - embeds cookies object into the request | ||
const withCookies = (r) => { | ||
r.cookies = (r.headers.get('Cookie') || '') | ||
.split(/;\s*/) | ||
.map((p) => p.split(/=(.+)/)) | ||
.reduce((a, [k, v]) => (v ? ((a[k] = v), a) : a), {}); | ||
}; | ||
export { withCookies }; |
@@ -1,1 +0,12 @@ | ||
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}; | ||
const withParams = (request) => { | ||
request.proxy = new Proxy(request.proxy || request, { | ||
get: (obj, prop) => { | ||
let p; | ||
if ((p = obj[prop]) !== undefined) | ||
return p.bind?.(request) || p; | ||
return obj?.params?.[prop]; | ||
}, | ||
}); | ||
}; | ||
export { withParams }; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54554
1173
1
1
No