Comparing version 0.1.0-rc.6 to 0.1.0-rc.7
@@ -40,3 +40,3 @@ "use strict"; | ||
}); | ||
if (set.headers['Content-Type'] !== null) | ||
if (!set.headers['Content-Type']) | ||
set.headers['Content-Type'] = 'application/json'; | ||
@@ -116,3 +116,3 @@ return new Response(JSON.stringify(response), { | ||
}); | ||
if (set.headers['Content-Type'] !== null) | ||
if (!set.headers['Content-Type']) | ||
set.headers['Content-Type'] = 'application/json'; | ||
@@ -119,0 +119,0 @@ return new Response(JSON.stringify(response), { |
@@ -28,2 +28,3 @@ "use strict"; | ||
}; | ||
// Will be applied to Context | ||
this.decorators = null; | ||
@@ -33,12 +34,3 @@ this.event = { | ||
request: [], | ||
parse: [ | ||
(request, contentType) => { | ||
switch (contentType) { | ||
case 'application/json': | ||
return request.json(); | ||
case 'text/plain': | ||
return request.text(); | ||
} | ||
} | ||
], | ||
parse: [], | ||
transform: [], | ||
@@ -54,3 +46,3 @@ beforeHandle: [], | ||
// This router is fallback for catch all route | ||
this.fallbackRoute = new Map(); | ||
this.fallbackRoute = {}; | ||
this.routes = []; | ||
@@ -76,7 +68,4 @@ /** | ||
this.server.stop(); | ||
for (let i = 0; i < this.event.stop.length; i++) { | ||
const process = this.event.stop[i](this); | ||
if (process instanceof Promise) | ||
await process; | ||
} | ||
for (let i = 0; i < this.event.stop.length; i++) | ||
await this.event.stop[i](this); | ||
}; | ||
@@ -109,3 +98,3 @@ this.config = { | ||
if (path === '/*') | ||
this.fallbackRoute.set(method, { | ||
this.fallbackRoute[method] = { | ||
handle: handler, | ||
@@ -122,3 +111,3 @@ hooks: (0, utils_1.mergeHook)((0, utils_1.clone)(this.event), hook), | ||
: undefined | ||
}); | ||
}; | ||
this.router.register(path)[method] = { | ||
@@ -763,3 +752,4 @@ handle: handler, | ||
} | ||
const route = this.router.find((0, utils_1.getPath)(request.url)); | ||
const index = request.url.indexOf('?'); | ||
const route = this.router.find((0, utils_1.getPath)(request.url, index)); | ||
if (!route) | ||
@@ -769,9 +759,9 @@ throw new Error('NOT_FOUND'); | ||
route.store.ALL ?? | ||
this.fallbackRoute.get(request.method); | ||
this.fallbackRoute[request.method]; | ||
if (!handler) | ||
throw new Error('NOT_FOUND'); | ||
let body; | ||
if (request.method !== 'GET' && request.method !== 'HEAD') { | ||
if (request.method !== 'GET') { | ||
const contentType = request.headers.get('content-type'); | ||
if (contentType) | ||
if (contentType) { | ||
for (let i = 0; i < this.event.parse.length; i++) { | ||
@@ -786,2 +776,12 @@ let temp = this.event.parse[i](request, contentType); | ||
} | ||
if (!body) | ||
switch (contentType) { | ||
case 'application/json': | ||
body = await request.json(); | ||
break; | ||
case 'text/plain': | ||
body = await request.text(); | ||
break; | ||
} | ||
} | ||
} | ||
@@ -796,3 +796,3 @@ const context = (this.decorators | ||
params: route?.params ?? {}, | ||
query: (0, utils_1.mapQuery)(request.url) | ||
query: (0, utils_1.mapQuery)(request.url, index) | ||
}); | ||
@@ -805,3 +805,3 @@ if (this.decorators) { | ||
context.params = route?.params ?? {}; | ||
context.query = (0, utils_1.mapQuery)(request.url); | ||
context.query = (0, utils_1.mapQuery)(request.url, index); | ||
} | ||
@@ -814,18 +814,16 @@ for (let i = 0; i < handler.hooks.transform.length; i++) { | ||
if (handler.validator) { | ||
const validator = handler.validator; | ||
if (validator.headers) { | ||
if (handler.validator.headers) { | ||
const _header = {}; | ||
for (const v of request.headers.entries()) | ||
_header[v[0]] = v[1]; | ||
if (!validator.headers.Check(_header)) | ||
throw (0, utils_1.createValidationError)('header', validator.headers, _header); | ||
for (const key in request.headers) | ||
_header[key] = request.headers.get(key); | ||
if (handler.validator.headers.Check(_header) === false) | ||
throw (0, utils_1.createValidationError)('header', handler.validator.headers, _header); | ||
} | ||
if (validator.params && | ||
!validator.params?.Check(context.params)) | ||
throw (0, utils_1.createValidationError)('params', validator.params, context.params); | ||
if (validator.query && !validator.query.Check(context.query)) { | ||
throw (0, utils_1.createValidationError)('query', validator.query, context.query); | ||
if (handler.validator.params?.Check(context.params) === false) | ||
throw (0, utils_1.createValidationError)('params', handler.validator.params, context.params); | ||
if (handler.validator.query?.Check(context.query) === false) { | ||
throw (0, utils_1.createValidationError)('query', handler.validator.query, context.query); | ||
} | ||
if (validator.body && !validator.body.Check(body)) | ||
throw (0, utils_1.createValidationError)('body', validator.body, body); | ||
if (handler.validator.body?.Check(body) === false) | ||
throw (0, utils_1.createValidationError)('body', handler.validator.body, body); | ||
} | ||
@@ -853,4 +851,3 @@ for (let i = 0; i < handler.hooks.beforeHandle.length; i++) { | ||
response = await response; | ||
if (handler.validator?.response && | ||
!handler.validator.response.Check(response)) | ||
if (handler.validator?.response?.Check(response) === false) | ||
throw (0, utils_1.createValidationError)('response', handler.validator.response, response); | ||
@@ -861,5 +858,3 @@ for (let i = 0; i < handler.hooks.afterHandle.length; i++) { | ||
newResponse = await newResponse; | ||
if (newResponse) | ||
response = newResponse; | ||
const result = (0, handler_1.mapEarlyResponse)(response, context.set); | ||
const result = (0, handler_1.mapEarlyResponse)(newResponse, context.set); | ||
if (result) | ||
@@ -866,0 +861,0 @@ return result; |
@@ -45,4 +45,4 @@ /** | ||
private _root; | ||
register(path: string): any; | ||
register(path: string): FindResult['store']; | ||
find(url: string): FindResult | null; | ||
} |
@@ -60,5 +60,2 @@ "use strict"; | ||
} | ||
function defaultStoreFactory() { | ||
return Object.create(null); | ||
} | ||
class Router { | ||
@@ -69,17 +66,13 @@ constructor() { | ||
register(path) { | ||
if (typeof path !== 'string') { | ||
if (typeof path !== 'string') | ||
throw new TypeError('Route path must be a string'); | ||
} | ||
if (path === '' || path[0] !== '/') { | ||
if (path === '' || path[0] !== '/') | ||
throw new Error(`Invalid route: ${path}\nRoute path must begin with a "/"`); | ||
} | ||
const endsWithWildcard = path.endsWith('*'); | ||
if (endsWithWildcard) { | ||
if (endsWithWildcard) | ||
path = path.slice(0, -1); // Slice off trailing '*' | ||
} | ||
const staticParts = path.split(/:.+?(?=\/|$)/); | ||
const paramParts = path.match(/:.+?(?=\/|$)/g) || []; | ||
if (staticParts[staticParts.length - 1] === '') { | ||
if (staticParts[staticParts.length - 1] === '') | ||
staticParts.pop(); | ||
} | ||
let node = this._root; | ||
@@ -92,10 +85,8 @@ let paramPartsIndex = 0; | ||
const paramName = paramParts[paramPartsIndex++].slice(1); | ||
if (node.parametricChild === null) { | ||
if (node.parametricChild === null) | ||
node.parametricChild = createParametricNode(paramName); | ||
} | ||
else if (node.parametricChild.paramName !== paramName) { | ||
else if (node.parametricChild.paramName !== paramName) | ||
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` + | ||
'because a route already exists with a different parameter name ' + | ||
`("${node.parametricChild.paramName}") in the same location`); | ||
} | ||
const { parametricChild } = node; | ||
@@ -119,5 +110,4 @@ if (parametricChild.staticChild === null) { | ||
// Add static child | ||
if (node.staticChildren === null) { | ||
if (node.staticChildren === null) | ||
node.staticChildren = new Map(); | ||
} | ||
else if (node.staticChildren.has(pathPart.charCodeAt(j))) { | ||
@@ -154,13 +144,10 @@ // Re-run loop with existing static node | ||
const paramName = param.slice(1); | ||
if (node.parametricChild === null) { | ||
if (node.parametricChild === null) | ||
node.parametricChild = createParametricNode(paramName); | ||
} | ||
else if (node.parametricChild.paramName !== paramName) { | ||
else if (node.parametricChild.paramName !== paramName) | ||
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` + | ||
'because a route already exists with a different parameter name ' + | ||
`("${node.parametricChild.paramName}") in the same location`); | ||
} | ||
if (node.parametricChild.store === null) { | ||
if (node.parametricChild.store === null) | ||
node.parametricChild.store = Object.create(null); | ||
} | ||
return node.parametricChild.store; | ||
@@ -170,11 +157,9 @@ } | ||
// The final part is a wildcard | ||
if (node.wildcardStore === null) { | ||
if (node.wildcardStore === null) | ||
node.wildcardStore = Object.create(null); | ||
} | ||
return node.wildcardStore; | ||
} | ||
// The final part is static | ||
if (node.store === null) { | ||
if (node.store === null) | ||
node.store = Object.create(null); | ||
} | ||
return node.store; | ||
@@ -188,26 +173,21 @@ } | ||
function matchRoute(url, urlLength, node, startIndex) { | ||
const pathPart = node.pathPart; | ||
const pathPartEndIndex = startIndex + pathPart.length; | ||
const pathPartEndIndex = startIndex + node.pathPart.length; | ||
// Only check the pathPart if its length is > 1 since the parent has | ||
// already checked that the url matches the first character | ||
if (pathPart.length > 1) { | ||
if (pathPartEndIndex > urlLength) { | ||
if (node.pathPart.length > 1) { | ||
if (pathPartEndIndex > urlLength) | ||
return null; | ||
} | ||
if (pathPart.length < 15) { | ||
if (node.pathPart.length < 15) | ||
// Using a loop is faster for short strings | ||
for (let i = 1, j = startIndex + 1; i < pathPart.length; ++i, ++j) { | ||
if (pathPart[i] !== url[j]) { | ||
for (let i = 1, j = startIndex + 1; i < node.pathPart.length; ++i, ++j) { | ||
if (node.pathPart[i] !== url[j]) { | ||
return null; | ||
} | ||
} | ||
} | ||
else if (url.slice(startIndex, pathPartEndIndex) !== pathPart) { | ||
else if (url.slice(startIndex, pathPartEndIndex) !== node.pathPart) | ||
return null; | ||
} | ||
} | ||
startIndex = pathPartEndIndex; | ||
if (startIndex === urlLength) { | ||
if (pathPartEndIndex === urlLength) { | ||
// Reached the end of the URL | ||
if (node.store !== null) { | ||
if (node.store) | ||
return { | ||
@@ -217,4 +197,3 @@ store: node.store, | ||
}; | ||
} | ||
if (node.wildcardStore !== null) { | ||
if (node.wildcardStore) | ||
return { | ||
@@ -224,25 +203,22 @@ store: node.wildcardStore, | ||
}; | ||
} | ||
return null; | ||
} | ||
if (node.staticChildren !== null) { | ||
const staticChild = node.staticChildren.get(url.charCodeAt(startIndex)); | ||
if (node.staticChildren) { | ||
const staticChild = node.staticChildren.get(url.charCodeAt(pathPartEndIndex)); | ||
if (staticChild !== undefined) { | ||
const route = matchRoute(url, urlLength, staticChild, startIndex); | ||
if (route !== null) { | ||
const route = matchRoute(url, urlLength, staticChild, pathPartEndIndex); | ||
if (route) | ||
return route; | ||
} | ||
} | ||
} | ||
if (node.parametricChild !== null) { | ||
const parametricNode = node.parametricChild; | ||
const slashIndex = url.indexOf('/', startIndex); | ||
if (slashIndex !== startIndex) { | ||
if (node.parametricChild) { | ||
const slashIndex = url.indexOf('/', pathPartEndIndex); | ||
if (slashIndex !== pathPartEndIndex) { | ||
// Params cannot be empty | ||
if (slashIndex === -1 || slashIndex >= urlLength) { | ||
if (parametricNode.store !== null) { | ||
if (node.parametricChild.store) { | ||
const params = {}; // This is much faster than using a computed property | ||
params[parametricNode.paramName] = url.slice(startIndex, urlLength); | ||
params[node.parametricChild.paramName] = url.slice(pathPartEndIndex, urlLength); | ||
return { | ||
store: parametricNode.store, | ||
store: node.parametricChild.store, | ||
params | ||
@@ -252,6 +228,6 @@ }; | ||
} | ||
else if (parametricNode.staticChild !== null) { | ||
const route = matchRoute(url, urlLength, parametricNode.staticChild, slashIndex); | ||
if (route !== null) { | ||
route.params[parametricNode.paramName] = url.slice(startIndex, slashIndex); | ||
else if (node.parametricChild.staticChild) { | ||
const route = matchRoute(url, urlLength, node.parametricChild.staticChild, slashIndex); | ||
if (route) { | ||
route.params[node.parametricChild.paramName] = url.slice(pathPartEndIndex, slashIndex); | ||
return route; | ||
@@ -262,11 +238,10 @@ } | ||
} | ||
if (node.wildcardStore !== null) { | ||
if (node.wildcardStore) | ||
return { | ||
store: node.wildcardStore, | ||
params: { | ||
'*': url.slice(startIndex, urlLength) | ||
'*': url.slice(pathPartEndIndex, urlLength) | ||
} | ||
}; | ||
} | ||
return null; | ||
} |
@@ -8,6 +8,6 @@ import { TypeCheck } from '@sinclair/typebox/compiler'; | ||
export declare const clone: <T extends Object | any[] = Object | any[]>(value: T) => T; | ||
export declare const getPath: (url: string) => string; | ||
export declare const mapQuery: (url: string) => Record<string, string>; | ||
export declare const getPath: (url: string, queryIndex: number) => string; | ||
export declare const mapQuery: (url: string, queryIndex: number) => Record<string, string>; | ||
export declare const mergeDeep: <A extends Object = Object, B extends Object = Object>(target: A, source: B) => DeepMergeTwoTypes<A, B>; | ||
export declare const createValidationError: (type: string, validator: TypeCheck<any>, value: any) => Error; | ||
export declare const getSchemaValidator: <Schema extends TSchema | undefined = undefined>(schema: Schema, additionalProperties?: boolean) => TypeCheck<NonNullable<Schema>> | undefined; |
@@ -32,14 +32,7 @@ "use strict"; | ||
exports.mergeHook = mergeHook; | ||
// export const isPromise = <T>( | ||
// response: T | Promise<T> | ||
// ): response is Promise<T> => response instanceof Promise | ||
const clone = (value) => [value][0]; | ||
exports.clone = clone; | ||
const getPath = (url) => { | ||
const queryIndex = url.indexOf('?'); | ||
return url.substring(url.charCodeAt(0) === 47 ? 0 : url.indexOf('/', 11), queryIndex === -1 ? url.length : queryIndex); | ||
}; | ||
const getPath = (url, queryIndex) => url.substring(url.charCodeAt(0) === 47 ? 0 : url.indexOf('/', 11), queryIndex === -1 ? url.length : queryIndex); | ||
exports.getPath = getPath; | ||
const mapQuery = (url) => { | ||
const queryIndex = url.indexOf('?'); | ||
const mapQuery = (url, queryIndex) => { | ||
if (queryIndex === -1) | ||
@@ -93,3 +86,3 @@ return {}; | ||
return new Error('VALIDATION', { | ||
cause: `Invalid ${type}: '${error?.path?.slice(1) || "root"}'. ${error.message}` | ||
cause: `Invalid ${type}: '${error?.path?.slice(1) || 'root'}'. ${error.message}` | ||
}); | ||
@@ -96,0 +89,0 @@ }; |
@@ -37,3 +37,3 @@ const jsonHeader = { | ||
}); | ||
if (set.headers['Content-Type'] !== null) | ||
if (!set.headers['Content-Type']) | ||
set.headers['Content-Type'] = 'application/json'; | ||
@@ -112,3 +112,3 @@ return new Response(JSON.stringify(response), { | ||
}); | ||
if (set.headers['Content-Type'] !== null) | ||
if (!set.headers['Content-Type']) | ||
set.headers['Content-Type'] = 'application/json'; | ||
@@ -115,0 +115,0 @@ return new Response(JSON.stringify(response), { |
@@ -25,2 +25,3 @@ import { Router } from './router'; | ||
}; | ||
// Will be applied to Context | ||
this.decorators = null; | ||
@@ -30,12 +31,3 @@ this.event = { | ||
request: [], | ||
parse: [ | ||
(request, contentType) => { | ||
switch (contentType) { | ||
case 'application/json': | ||
return request.json(); | ||
case 'text/plain': | ||
return request.text(); | ||
} | ||
} | ||
], | ||
parse: [], | ||
transform: [], | ||
@@ -51,3 +43,3 @@ beforeHandle: [], | ||
// This router is fallback for catch all route | ||
this.fallbackRoute = new Map(); | ||
this.fallbackRoute = {}; | ||
this.routes = []; | ||
@@ -73,7 +65,4 @@ /** | ||
this.server.stop(); | ||
for (let i = 0; i < this.event.stop.length; i++) { | ||
const process = this.event.stop[i](this); | ||
if (process instanceof Promise) | ||
await process; | ||
} | ||
for (let i = 0; i < this.event.stop.length; i++) | ||
await this.event.stop[i](this); | ||
}; | ||
@@ -106,3 +95,3 @@ this.config = { | ||
if (path === '/*') | ||
this.fallbackRoute.set(method, { | ||
this.fallbackRoute[method] = { | ||
handle: handler, | ||
@@ -119,3 +108,3 @@ hooks: mergeHook(clone(this.event), hook), | ||
: undefined | ||
}); | ||
}; | ||
this.router.register(path)[method] = { | ||
@@ -760,3 +749,4 @@ handle: handler, | ||
} | ||
const route = this.router.find(getPath(request.url)); | ||
const index = request.url.indexOf('?'); | ||
const route = this.router.find(getPath(request.url, index)); | ||
if (!route) | ||
@@ -766,9 +756,9 @@ throw new Error('NOT_FOUND'); | ||
route.store.ALL ?? | ||
this.fallbackRoute.get(request.method); | ||
this.fallbackRoute[request.method]; | ||
if (!handler) | ||
throw new Error('NOT_FOUND'); | ||
let body; | ||
if (request.method !== 'GET' && request.method !== 'HEAD') { | ||
if (request.method !== 'GET') { | ||
const contentType = request.headers.get('content-type'); | ||
if (contentType) | ||
if (contentType) { | ||
for (let i = 0; i < this.event.parse.length; i++) { | ||
@@ -783,2 +773,12 @@ let temp = this.event.parse[i](request, contentType); | ||
} | ||
if (!body) | ||
switch (contentType) { | ||
case 'application/json': | ||
body = await request.json(); | ||
break; | ||
case 'text/plain': | ||
body = await request.text(); | ||
break; | ||
} | ||
} | ||
} | ||
@@ -793,3 +793,3 @@ const context = (this.decorators | ||
params: route?.params ?? {}, | ||
query: mapQuery(request.url) | ||
query: mapQuery(request.url, index) | ||
}); | ||
@@ -802,3 +802,3 @@ if (this.decorators) { | ||
context.params = route?.params ?? {}; | ||
context.query = mapQuery(request.url); | ||
context.query = mapQuery(request.url, index); | ||
} | ||
@@ -811,18 +811,16 @@ for (let i = 0; i < handler.hooks.transform.length; i++) { | ||
if (handler.validator) { | ||
const validator = handler.validator; | ||
if (validator.headers) { | ||
if (handler.validator.headers) { | ||
const _header = {}; | ||
for (const v of request.headers.entries()) | ||
_header[v[0]] = v[1]; | ||
if (!validator.headers.Check(_header)) | ||
throw createValidationError('header', validator.headers, _header); | ||
for (const key in request.headers) | ||
_header[key] = request.headers.get(key); | ||
if (handler.validator.headers.Check(_header) === false) | ||
throw createValidationError('header', handler.validator.headers, _header); | ||
} | ||
if (validator.params && | ||
!validator.params?.Check(context.params)) | ||
throw createValidationError('params', validator.params, context.params); | ||
if (validator.query && !validator.query.Check(context.query)) { | ||
throw createValidationError('query', validator.query, context.query); | ||
if (handler.validator.params?.Check(context.params) === false) | ||
throw createValidationError('params', handler.validator.params, context.params); | ||
if (handler.validator.query?.Check(context.query) === false) { | ||
throw createValidationError('query', handler.validator.query, context.query); | ||
} | ||
if (validator.body && !validator.body.Check(body)) | ||
throw createValidationError('body', validator.body, body); | ||
if (handler.validator.body?.Check(body) === false) | ||
throw createValidationError('body', handler.validator.body, body); | ||
} | ||
@@ -850,4 +848,3 @@ for (let i = 0; i < handler.hooks.beforeHandle.length; i++) { | ||
response = await response; | ||
if (handler.validator?.response && | ||
!handler.validator.response.Check(response)) | ||
if (handler.validator?.response?.Check(response) === false) | ||
throw createValidationError('response', handler.validator.response, response); | ||
@@ -858,5 +855,3 @@ for (let i = 0; i < handler.hooks.afterHandle.length; i++) { | ||
newResponse = await newResponse; | ||
if (newResponse) | ||
response = newResponse; | ||
const result = mapEarlyResponse(response, context.set); | ||
const result = mapEarlyResponse(newResponse, context.set); | ||
if (result) | ||
@@ -863,0 +858,0 @@ return result; |
@@ -45,4 +45,4 @@ /** | ||
private _root; | ||
register(path: string): any; | ||
register(path: string): FindResult['store']; | ||
find(url: string): FindResult | null; | ||
} |
@@ -57,5 +57,2 @@ /** | ||
} | ||
function defaultStoreFactory() { | ||
return Object.create(null); | ||
} | ||
export class Router { | ||
@@ -66,17 +63,13 @@ constructor() { | ||
register(path) { | ||
if (typeof path !== 'string') { | ||
if (typeof path !== 'string') | ||
throw new TypeError('Route path must be a string'); | ||
} | ||
if (path === '' || path[0] !== '/') { | ||
if (path === '' || path[0] !== '/') | ||
throw new Error(`Invalid route: ${path}\nRoute path must begin with a "/"`); | ||
} | ||
const endsWithWildcard = path.endsWith('*'); | ||
if (endsWithWildcard) { | ||
if (endsWithWildcard) | ||
path = path.slice(0, -1); // Slice off trailing '*' | ||
} | ||
const staticParts = path.split(/:.+?(?=\/|$)/); | ||
const paramParts = path.match(/:.+?(?=\/|$)/g) || []; | ||
if (staticParts[staticParts.length - 1] === '') { | ||
if (staticParts[staticParts.length - 1] === '') | ||
staticParts.pop(); | ||
} | ||
let node = this._root; | ||
@@ -89,10 +82,8 @@ let paramPartsIndex = 0; | ||
const paramName = paramParts[paramPartsIndex++].slice(1); | ||
if (node.parametricChild === null) { | ||
if (node.parametricChild === null) | ||
node.parametricChild = createParametricNode(paramName); | ||
} | ||
else if (node.parametricChild.paramName !== paramName) { | ||
else if (node.parametricChild.paramName !== paramName) | ||
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` + | ||
'because a route already exists with a different parameter name ' + | ||
`("${node.parametricChild.paramName}") in the same location`); | ||
} | ||
const { parametricChild } = node; | ||
@@ -116,5 +107,4 @@ if (parametricChild.staticChild === null) { | ||
// Add static child | ||
if (node.staticChildren === null) { | ||
if (node.staticChildren === null) | ||
node.staticChildren = new Map(); | ||
} | ||
else if (node.staticChildren.has(pathPart.charCodeAt(j))) { | ||
@@ -151,13 +141,10 @@ // Re-run loop with existing static node | ||
const paramName = param.slice(1); | ||
if (node.parametricChild === null) { | ||
if (node.parametricChild === null) | ||
node.parametricChild = createParametricNode(paramName); | ||
} | ||
else if (node.parametricChild.paramName !== paramName) { | ||
else if (node.parametricChild.paramName !== paramName) | ||
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` + | ||
'because a route already exists with a different parameter name ' + | ||
`("${node.parametricChild.paramName}") in the same location`); | ||
} | ||
if (node.parametricChild.store === null) { | ||
if (node.parametricChild.store === null) | ||
node.parametricChild.store = Object.create(null); | ||
} | ||
return node.parametricChild.store; | ||
@@ -167,11 +154,9 @@ } | ||
// The final part is a wildcard | ||
if (node.wildcardStore === null) { | ||
if (node.wildcardStore === null) | ||
node.wildcardStore = Object.create(null); | ||
} | ||
return node.wildcardStore; | ||
} | ||
// The final part is static | ||
if (node.store === null) { | ||
if (node.store === null) | ||
node.store = Object.create(null); | ||
} | ||
return node.store; | ||
@@ -184,26 +169,21 @@ } | ||
function matchRoute(url, urlLength, node, startIndex) { | ||
const pathPart = node.pathPart; | ||
const pathPartEndIndex = startIndex + pathPart.length; | ||
const pathPartEndIndex = startIndex + node.pathPart.length; | ||
// Only check the pathPart if its length is > 1 since the parent has | ||
// already checked that the url matches the first character | ||
if (pathPart.length > 1) { | ||
if (pathPartEndIndex > urlLength) { | ||
if (node.pathPart.length > 1) { | ||
if (pathPartEndIndex > urlLength) | ||
return null; | ||
} | ||
if (pathPart.length < 15) { | ||
if (node.pathPart.length < 15) | ||
// Using a loop is faster for short strings | ||
for (let i = 1, j = startIndex + 1; i < pathPart.length; ++i, ++j) { | ||
if (pathPart[i] !== url[j]) { | ||
for (let i = 1, j = startIndex + 1; i < node.pathPart.length; ++i, ++j) { | ||
if (node.pathPart[i] !== url[j]) { | ||
return null; | ||
} | ||
} | ||
} | ||
else if (url.slice(startIndex, pathPartEndIndex) !== pathPart) { | ||
else if (url.slice(startIndex, pathPartEndIndex) !== node.pathPart) | ||
return null; | ||
} | ||
} | ||
startIndex = pathPartEndIndex; | ||
if (startIndex === urlLength) { | ||
if (pathPartEndIndex === urlLength) { | ||
// Reached the end of the URL | ||
if (node.store !== null) { | ||
if (node.store) | ||
return { | ||
@@ -213,4 +193,3 @@ store: node.store, | ||
}; | ||
} | ||
if (node.wildcardStore !== null) { | ||
if (node.wildcardStore) | ||
return { | ||
@@ -220,25 +199,22 @@ store: node.wildcardStore, | ||
}; | ||
} | ||
return null; | ||
} | ||
if (node.staticChildren !== null) { | ||
const staticChild = node.staticChildren.get(url.charCodeAt(startIndex)); | ||
if (node.staticChildren) { | ||
const staticChild = node.staticChildren.get(url.charCodeAt(pathPartEndIndex)); | ||
if (staticChild !== undefined) { | ||
const route = matchRoute(url, urlLength, staticChild, startIndex); | ||
if (route !== null) { | ||
const route = matchRoute(url, urlLength, staticChild, pathPartEndIndex); | ||
if (route) | ||
return route; | ||
} | ||
} | ||
} | ||
if (node.parametricChild !== null) { | ||
const parametricNode = node.parametricChild; | ||
const slashIndex = url.indexOf('/', startIndex); | ||
if (slashIndex !== startIndex) { | ||
if (node.parametricChild) { | ||
const slashIndex = url.indexOf('/', pathPartEndIndex); | ||
if (slashIndex !== pathPartEndIndex) { | ||
// Params cannot be empty | ||
if (slashIndex === -1 || slashIndex >= urlLength) { | ||
if (parametricNode.store !== null) { | ||
if (node.parametricChild.store) { | ||
const params = {}; // This is much faster than using a computed property | ||
params[parametricNode.paramName] = url.slice(startIndex, urlLength); | ||
params[node.parametricChild.paramName] = url.slice(pathPartEndIndex, urlLength); | ||
return { | ||
store: parametricNode.store, | ||
store: node.parametricChild.store, | ||
params | ||
@@ -248,6 +224,6 @@ }; | ||
} | ||
else if (parametricNode.staticChild !== null) { | ||
const route = matchRoute(url, urlLength, parametricNode.staticChild, slashIndex); | ||
if (route !== null) { | ||
route.params[parametricNode.paramName] = url.slice(startIndex, slashIndex); | ||
else if (node.parametricChild.staticChild) { | ||
const route = matchRoute(url, urlLength, node.parametricChild.staticChild, slashIndex); | ||
if (route) { | ||
route.params[node.parametricChild.paramName] = url.slice(pathPartEndIndex, slashIndex); | ||
return route; | ||
@@ -258,11 +234,10 @@ } | ||
} | ||
if (node.wildcardStore !== null) { | ||
if (node.wildcardStore) | ||
return { | ||
store: node.wildcardStore, | ||
params: { | ||
'*': url.slice(startIndex, urlLength) | ||
'*': url.slice(pathPartEndIndex, urlLength) | ||
} | ||
}; | ||
} | ||
return null; | ||
} |
@@ -8,6 +8,6 @@ import { TypeCheck } from '@sinclair/typebox/compiler'; | ||
export declare const clone: <T extends Object | any[] = Object | any[]>(value: T) => T; | ||
export declare const getPath: (url: string) => string; | ||
export declare const mapQuery: (url: string) => Record<string, string>; | ||
export declare const getPath: (url: string, queryIndex: number) => string; | ||
export declare const mapQuery: (url: string, queryIndex: number) => Record<string, string>; | ||
export declare const mergeDeep: <A extends Object = Object, B extends Object = Object>(target: A, source: B) => DeepMergeTwoTypes<A, B>; | ||
export declare const createValidationError: (type: string, validator: TypeCheck<any>, value: any) => Error; | ||
export declare const getSchemaValidator: <Schema extends TSchema | undefined = undefined>(schema: Schema, additionalProperties?: boolean) => TypeCheck<NonNullable<Schema>> | undefined; |
@@ -27,12 +27,5 @@ import { TypeCompiler } from '@sinclair/typebox/compiler'; | ||
}; | ||
// export const isPromise = <T>( | ||
// response: T | Promise<T> | ||
// ): response is Promise<T> => response instanceof Promise | ||
export const clone = (value) => [value][0]; | ||
export const getPath = (url) => { | ||
const queryIndex = url.indexOf('?'); | ||
return url.substring(url.charCodeAt(0) === 47 ? 0 : url.indexOf('/', 11), queryIndex === -1 ? url.length : queryIndex); | ||
}; | ||
export const mapQuery = (url) => { | ||
const queryIndex = url.indexOf('?'); | ||
export const getPath = (url, queryIndex) => url.substring(url.charCodeAt(0) === 47 ? 0 : url.indexOf('/', 11), queryIndex === -1 ? url.length : queryIndex); | ||
export const mapQuery = (url, queryIndex) => { | ||
if (queryIndex === -1) | ||
@@ -84,3 +77,3 @@ return {}; | ||
return new Error('VALIDATION', { | ||
cause: `Invalid ${type}: '${error?.path?.slice(1) || "root"}'. ${error.message}` | ||
cause: `Invalid ${type}: '${error?.path?.slice(1) || 'root'}'. ${error.message}` | ||
}); | ||
@@ -87,0 +80,0 @@ }; |
{ | ||
"name": "elysia", | ||
"description": "Fast, and friendly Bun web framework", | ||
"version": "0.1.0-rc.6", | ||
"version": "0.1.0-rc.7", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "saltyAom", |
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
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
170712
4750