180 lines
6.0 KiB
JavaScript
180 lines
6.0 KiB
JavaScript
// src/router/trie-router/node.ts
|
|
import { METHOD_NAME_ALL } from "../../router.js";
|
|
import { getPattern, splitPath, splitRoutingPath } from "../../utils/url.js";
|
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
var hasChildren = (children) => {
|
|
for (const _ in children) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var Node = class _Node {
|
|
#methods;
|
|
#children;
|
|
#patterns;
|
|
#order = 0;
|
|
#params = emptyParams;
|
|
constructor(method, handler, children) {
|
|
this.#children = children || /* @__PURE__ */ Object.create(null);
|
|
this.#methods = [];
|
|
if (method && handler) {
|
|
const m = /* @__PURE__ */ 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;
|
|
}
|
|
#pushHandlerSets(handlerSets, node, method, nodeParams, params) {
|
|
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 = /* @__PURE__ */ Object.create(null);
|
|
handlerSets.push(handlerSet);
|
|
if (nodeParams !== emptyParams || params && params !== emptyParams) {
|
|
for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {
|
|
const key = handlerSet.possibleKeys[i2];
|
|
const processed = processedSet[handlerSet.score];
|
|
handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];
|
|
processedSet[handlerSet.score] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
search(method, path) {
|
|
const handlerSets = [];
|
|
this.#params = emptyParams;
|
|
const curNode = this;
|
|
let curNodes = [curNode];
|
|
const parts = splitPath(path);
|
|
const curNodesQueue = [];
|
|
const len = parts.length;
|
|
let partOffsets = null;
|
|
for (let i = 0; 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["*"]) {
|
|
this.#pushHandlerSets(handlerSets, nextNode.#children["*"], method, node.#params);
|
|
}
|
|
this.#pushHandlerSets(handlerSets, 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) {
|
|
this.#pushHandlerSets(handlerSets, 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];
|
|
if (matcher instanceof RegExp) {
|
|
if (partOffsets === null) {
|
|
partOffsets = new Array(len);
|
|
let offset = path[0] === "/" ? 1 : 0;
|
|
for (let p = 0; p < len; p++) {
|
|
partOffsets[p] = offset;
|
|
offset += parts[p].length + 1;
|
|
}
|
|
}
|
|
const restPathString = path.substring(partOffsets[i]);
|
|
const m = matcher.exec(restPathString);
|
|
if (m) {
|
|
params[name] = m[0];
|
|
this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
|
|
if (hasChildren(child.#children)) {
|
|
child.#params = params;
|
|
const componentCount = m[0].match(/\//)?.length ?? 0;
|
|
const targetCurNodes = curNodesQueue[componentCount] ||= [];
|
|
targetCurNodes.push(child);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (matcher === true || matcher.test(part)) {
|
|
params[name] = part;
|
|
if (isLast) {
|
|
this.#pushHandlerSets(handlerSets, child, method, params, node.#params);
|
|
if (child.#children["*"]) {
|
|
this.#pushHandlerSets(
|
|
handlerSets,
|
|
child.#children["*"],
|
|
method,
|
|
params,
|
|
node.#params
|
|
);
|
|
}
|
|
} else {
|
|
child.#params = params;
|
|
tempNodes.push(child);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const shifted = curNodesQueue.shift();
|
|
curNodes = shifted ? tempNodes.concat(shifted) : tempNodes;
|
|
}
|
|
if (handlerSets.length > 1) {
|
|
handlerSets.sort((a, b) => {
|
|
return a.score - b.score;
|
|
});
|
|
}
|
|
return [handlerSets.map(({ handler, params }) => [handler, params])];
|
|
}
|
|
};
|
|
export {
|
|
Node
|
|
};
|