ufiber
Version:
Next-gen webserver for node-js developer
145 lines (143 loc) • 4.81 kB
JavaScript
import { METHOD_NAME_ALL } from "../../consts.js";
import { getPattern, splitPath, splitRoutingPath } from "./utils.js";
//#region src/router/trie-tree/node.ts
const emptyParams = Object.create(null);
var Node = class Node {
#methods;
#children;
#patterns;
#order = 0;
#params = emptyParams;
constructor(method, handler, children) {
this.#children = children || Object.create(null);
this.#methods = [];
if (method && handler) {
const m = Object.create(null);
m[method] = {
handler,
possibleKeys: [],
score: 0
};
this.#methods = [m];
}
this.#patterns = [];
}
insert(method, path, handler) {
this.#order = ++this.#order;
let curNode = this;
const parts = splitRoutingPath(path);
const possibleKeys = [];
for (let i = 0, len = parts.length; i < len; i++) {
const p = parts[i];
const nextP = parts[i + 1];
const pattern = getPattern(p, nextP);
const key = Array.isArray(pattern) ? pattern[0] : p;
if (key in curNode.#children) {
curNode = curNode.#children[key];
if (pattern) possibleKeys.push(pattern[1]);
continue;
}
curNode.#children[key] = new Node();
if (pattern) {
curNode.#patterns.push(pattern);
possibleKeys.push(pattern[1]);
}
curNode = curNode.#children[key];
}
curNode.#methods.push({ [method]: {
handler,
possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),
score: this.#order
} });
return curNode;
}
#getHandlerSets(node, method, nodeParams, params) {
const handlerSets = [];
for (let i = 0, len = node.#methods.length; i < len; i++) {
const m = node.#methods[i];
const handlerSet = m[method] || m[METHOD_NAME_ALL];
const processedSet = {};
if (handlerSet !== void 0) {
handlerSet.params = Object.create(null);
handlerSets.push(handlerSet);
if (nodeParams !== emptyParams || params && params !== emptyParams) for (let i$1 = 0, len$1 = handlerSet.possibleKeys.length; i$1 < len$1; i$1++) {
const key = handlerSet.possibleKeys[i$1];
const processed = processedSet[handlerSet.score];
handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];
processedSet[handlerSet.score] = true;
}
}
}
return handlerSets;
}
search(method, path) {
const handlerSets = [];
this.#params = emptyParams;
let curNodes = [this];
const parts = splitPath(path);
const curNodesQueue = [];
for (let i = 0, len = parts.length; i < len; i++) {
const part = parts[i];
const isLast = i === len - 1;
const tempNodes = [];
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
const node = curNodes[j];
const nextNode = node.#children[part];
if (nextNode) {
nextNode.#params = node.#params;
if (isLast) {
if (nextNode.#children["*"]) handlerSets.push(...this.#getHandlerSets(nextNode.#children["*"], method, node.#params));
handlerSets.push(...this.#getHandlerSets(nextNode, method, node.#params));
} else tempNodes.push(nextNode);
}
for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {
const pattern = node.#patterns[k];
const params = node.#params === emptyParams ? {} : { ...node.#params };
if (pattern === "*") {
const astNode = node.#children["*"];
if (astNode) {
handlerSets.push(...this.#getHandlerSets(astNode, method, node.#params));
astNode.#params = params;
tempNodes.push(astNode);
}
continue;
}
const [key, name, matcher] = pattern;
if (!part && !(matcher instanceof RegExp)) continue;
const child = node.#children[key];
const restPathString = parts.slice(i).join("/");
if (matcher instanceof RegExp) {
const m = matcher.exec(restPathString);
if (m) {
params[name] = m[0];
handlerSets.push(...this.#getHandlerSets(child, method, node.#params, params));
if (Object.keys(child.#children).length) {
child.#params = params;
const componentCount = m[0].match(/\//)?.length ?? 0;
(curNodesQueue[componentCount] ||= []).push(child);
}
continue;
}
}
if (matcher === true || matcher.test(part)) {
params[name] = part;
if (isLast) {
handlerSets.push(...this.#getHandlerSets(child, method, params, node.#params));
if (child.#children["*"]) handlerSets.push(...this.#getHandlerSets(child.#children["*"], method, params, node.#params));
} else {
child.#params = params;
tempNodes.push(child);
}
}
}
}
curNodes = tempNodes.concat(curNodesQueue.shift() ?? []);
}
if (handlerSets.length > 1) handlerSets.sort((a, b) => {
return a.score - b.score;
});
return [handlerSets.map(({ handler, params }) => [handler, params])];
}
};
//#endregion
export { Node };