const NODE_TYPES = { NORMAL: 0, WILDCARD: 1, PLACEHOLDER: 2 }; function createRouter(options = {}) { const ctx = { options, rootNode: createRadixNode(), staticRoutesMap: {} }; const normalizeTrailingSlash = (p) => options.strictTrailingSlash ? p : p.replace(/\/$/, "") || "/"; if (options.routes) { for (const path in options.routes) { insert(ctx, normalizeTrailingSlash(path), options.routes[path]); } } return { ctx, lookup: (path) => lookup(ctx, normalizeTrailingSlash(path)), insert: (path, data) => insert(ctx, normalizeTrailingSlash(path), data), remove: (path) => remove(ctx, normalizeTrailingSlash(path)) }; } function lookup(ctx, path) { const staticPathNode = ctx.staticRoutesMap[path]; if (staticPathNode) { return staticPathNode.data; } const sections = path.split("/"); const params = {}; let paramsFound = false; let wildcardNode = null; let node = ctx.rootNode; let wildCardParam = null; for (let i = 0; i < sections.length; i++) { const section = sections[i]; if (node.wildcardChildNode !== null) { wildcardNode = node.wildcardChildNode; wildCardParam = sections.slice(i).join("/"); } const nextNode = node.children.get(section); if (nextNode !== void 0) { node = nextNode; } else { node = node.placeholderChildNode; if (node !== null) { params[node.paramName] = section; paramsFound = true; } else { break; } } } if ((node === null || node.data === null) && wildcardNode !== null) { node = wildcardNode; params[node.paramName || "_"] = wildCardParam; paramsFound = true; } if (!node) { return null; } if (paramsFound) { return { ...node.data, params: paramsFound ? params : void 0 }; } return node.data; } function insert(ctx, path, data) { let isStaticRoute = true; const sections = path.split("/"); let node = ctx.rootNode; let _unnamedPlaceholderCtr = 0; for (let i = 0; i < sections.length; i++) { const section = sections[i]; let childNode; if (childNode = node.children.get(section)) { node = childNode; } else { const type = getNodeType(section); childNode = createRadixNode({ type, parent: node }); node.children.set(section, childNode); if (type === NODE_TYPES.PLACEHOLDER) { childNode.paramName = section === "*" ? `_${_unnamedPlaceholderCtr++}` : section.slice(1); node.placeholderChildNode = childNode; isStaticRoute = false; } else if (type === NODE_TYPES.WILDCARD) { node.wildcardChildNode = childNode; childNode.paramName = section.substring(3) || "_"; isStaticRoute = false; } node = childNode; } } node.data = data; if (isStaticRoute === true) { ctx.staticRoutesMap[path] = node; } return node; } function remove(ctx, path) { let success = false; const sections = path.split("/"); let node = ctx.rootNode; for (let i = 0; i < sections.length; i++) { const section = sections[i]; node = node.children.get(section); if (!node) { return success; } } if (node.data) { const lastSection = sections[sections.length - 1]; node.data = null; if (Object.keys(node.children).length === 0) { const parentNode = node.parent; delete parentNode[lastSection]; parentNode.wildcardChildNode = null; parentNode.placeholderChildNode = null; } success = true; } return success; } function createRadixNode(options = {}) { return { type: options.type || NODE_TYPES.NORMAL, parent: options.parent || null, children: /* @__PURE__ */ new Map(), data: options.data || null, paramName: options.paramName || null, wildcardChildNode: null, placeholderChildNode: null }; } function getNodeType(str) { if (str.startsWith("**")) { return NODE_TYPES.WILDCARD; } if (str[0] === ":" || str === "*") { return NODE_TYPES.PLACEHOLDER; } return NODE_TYPES.NORMAL; } function toRouteMatcher(router) { const table = _routerNodeToTable("", router.ctx.rootNode); return _createMatcher(table); } function _createMatcher(table) { return { ctx: { table }, matchAll: (path) => _matchRoutes(path, table) }; } function _createRouteTable() { return { static: /* @__PURE__ */ new Map(), wildcard: /* @__PURE__ */ new Map(), dynamic: /* @__PURE__ */ new Map() }; } function _matchRoutes(path, table) { const matches = []; for (const [key, value] of table.wildcard) { if (path.startsWith(key)) { matches.push(value); } } for (const [key, value] of table.dynamic) { if (path.startsWith(key + "/")) { const subPath = "/" + path.substring(key.length).split("/").splice(2).join("/"); matches.push(..._matchRoutes(subPath, value)); } } const staticMatch = table.static.get(path); if (staticMatch) { matches.push(staticMatch); } return matches.filter(Boolean); } function _routerNodeToTable(initialPath, initialNode) { const table = _createRouteTable(); function _addNode(path, node) { if (path) { if (node.type === NODE_TYPES.NORMAL && !(path.includes("*") || path.includes(":"))) { table.static.set(path, node.data); } else if (node.type === NODE_TYPES.WILDCARD) { table.wildcard.set(path.replace("/**", ""), node.data); } else if (node.type === NODE_TYPES.PLACEHOLDER) { const subTable = _routerNodeToTable("", node); if (node.data) { subTable.static.set("/", node.data); } table.dynamic.set(path.replace(/\/\*|\/:\w+/, ""), subTable); return; } } for (const [childPath, child] of node.children.entries()) { _addNode(`${path}/${childPath}`.replace("//", "/"), child); } } _addNode(initialPath, initialNode); return table; } export { NODE_TYPES, createRouter, toRouteMatcher };