// src/router/reg-exp-router/router.ts import { MESSAGE_MATCHER_IS_ALREADY_BUILT, METHOD_NAME_ALL, UnsupportedPathError } from "../../router.js"; import { checkOptionalParameter } from "../../utils/url.js"; import { match, emptyParam } from "./matcher.js"; import { PATH_ERROR } from "./node.js"; import { Trie } from "./trie.js"; var wildcardRegExpCache = /* @__PURE__ */ Object.create(null); function buildWildcardRegExp(path) { return wildcardRegExpCache[path] ??= new RegExp( path === "*" ? "" : `^${path.replace( /\/\*$|([.\\+*[^\]$()])/g, (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)" )}$` ); } function clearWildcardRegExpCache() { wildcardRegExpCache = /* @__PURE__ */ Object.create(null); } function findMiddleware(middleware, path) { if (!middleware) { return void 0; } for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) { if (buildWildcardRegExp(k).test(path)) { return [...middleware[k]]; } } return void 0; } var RegExpRouter = class { name = "RegExpRouter"; #middleware; #routes; #tries; constructor() { this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; this.#tries = { [METHOD_NAME_ALL]: new Trie() }; } #insertPath(method, path) { try { this.#tries[method].insert(path, !/\*|\/:/.test(path)); } catch (e) { throw e === PATH_ERROR ? new UnsupportedPathError(path) : e; } } add(method, path, handler) { const middleware = this.#middleware; const routes = this.#routes; if (!middleware || !routes) { throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); } if (!middleware[method]) { this.#tries[method] = new Trie(); [middleware, routes].forEach((handlerMap) => { handlerMap[method] = /* @__PURE__ */ Object.create(null); Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => { handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]]; this.#insertPath(method, p); }); }); } if (path === "/*") { path = "*"; } const paramCount = (path.match(/\/:/g) || []).length; if (/\*$/.test(path)) { const re = buildWildcardRegExp(path); Object.keys(middleware).forEach((m) => { if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) { this.#insertPath(m, path); middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || []; } }); Object.keys(middleware).forEach((m) => { if (method === METHOD_NAME_ALL || method === m) { Object.keys(middleware[m]).forEach((p) => { re.test(p) && middleware[m][p].push([handler, paramCount]); }); } }); Object.keys(routes).forEach((m) => { if (method === METHOD_NAME_ALL || method === m) { Object.keys(routes[m]).forEach( (p) => re.test(p) && routes[m][p].push([handler, paramCount]) ); } }); return; } const paths = checkOptionalParameter(path) || [path]; for (let i = 0, len = paths.length; i < len; i++) { const path2 = paths[i]; Object.keys(routes).forEach((m) => { if (method === METHOD_NAME_ALL || method === m) { if (!routes[m][path2]) { this.#insertPath(m, path2); routes[m][path2] = [ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || [] ]; } routes[m][path2].push([handler, paramCount - len + i + 1]); } }); } } match = match; buildAllMatchers() { const matchers = /* @__PURE__ */ Object.create(null); Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => { matchers[method] ||= this.#buildMatcher(method); }); this.#middleware = this.#routes = this.#tries = void 0; clearWildcardRegExpCache(); return matchers; } #buildMatcher(method) { const middleware = this.#middleware[method]; const routes = this.#routes[method]; const trie = this.#tries[method]; const staticMap = /* @__PURE__ */ Object.create(null); const handlerData = []; [middleware, routes].forEach((r) => { for (const path in r) { const handlers = r[path]; const pathData = trie.paths[path]; if (!pathData) { staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam]; continue; } const paramAssoc = pathData[1]; handlerData[pathData[0]] = handlers.map(([h, paramCount]) => { const paramIndexMap = /* @__PURE__ */ Object.create(null); paramCount -= 1; for (; paramCount >= 0; paramCount--) { const [key, value] = paramAssoc[paramCount]; paramIndexMap[key] = value; } return [h, paramIndexMap]; }); } }); const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); for (let i = 0, len = handlerData.length; i < len; i++) { for (let j = 0, len2 = handlerData[i].length; j < len2; j++) { const map = handlerData[i][j]?.[1]; if (!map) { continue; } const keys = Object.keys(map); for (let k = 0, len3 = keys.length; k < len3; k++) { map[keys[k]] = paramReplacementMap[map[keys[k]]]; } } } const handlerMap = []; for (const i in indexReplacementMap) { handlerMap[i] = handlerData[indexReplacementMap[i]]; } return [regexp, handlerMap, staticMap]; } }; export { RegExpRouter };