@tanstack/router-core
Version:
Modern and scalable routing for React applications
753 lines (752 loc) • 24.8 kB
JavaScript
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
const require_utils = require("./utils.cjs");
const require_lru_cache = require("./lru-cache.cjs");
let tiny_invariant = require("tiny-invariant");
tiny_invariant = require_runtime.__toESM(tiny_invariant);
var SEGMENT_TYPE_INDEX = 4;
var SEGMENT_TYPE_PATHLESS = 5;
function getOpenAndCloseBraces(part) {
const openBrace = part.indexOf("{");
if (openBrace === -1) return null;
const closeBrace = part.indexOf("}", openBrace);
if (closeBrace === -1) return null;
if (openBrace + 1 >= part.length) return null;
return [openBrace, closeBrace];
}
/**
* Populates the `output` array with the parsed representation of the given `segment` string.
*
* Usage:
* ```ts
* let output
* let cursor = 0
* while (cursor < path.length) {
* output = parseSegment(path, cursor, output)
* const end = output[5]
* cursor = end + 1
* ```
*
* `output` is stored outside to avoid allocations during repeated calls. It doesn't need to be typed
* or initialized, it will be done automatically.
*/
function parseSegment(path, start, output = new Uint16Array(6)) {
const next = path.indexOf("/", start);
const end = next === -1 ? path.length : next;
const part = path.substring(start, end);
if (!part || !part.includes("$")) {
output[0] = 0;
output[1] = start;
output[2] = start;
output[3] = end;
output[4] = end;
output[5] = end;
return output;
}
if (part === "$") {
const total = path.length;
output[0] = 2;
output[1] = start;
output[2] = start;
output[3] = total;
output[4] = total;
output[5] = total;
return output;
}
if (part.charCodeAt(0) === 36) {
output[0] = 1;
output[1] = start;
output[2] = start + 1;
output[3] = end;
output[4] = end;
output[5] = end;
return output;
}
const braces = getOpenAndCloseBraces(part);
if (braces) {
const [openBrace, closeBrace] = braces;
const firstChar = part.charCodeAt(openBrace + 1);
if (firstChar === 45) {
if (openBrace + 2 < part.length && part.charCodeAt(openBrace + 2) === 36) {
const paramStart = openBrace + 3;
const paramEnd = closeBrace;
if (paramStart < paramEnd) {
output[0] = 3;
output[1] = start + openBrace;
output[2] = start + paramStart;
output[3] = start + paramEnd;
output[4] = start + closeBrace + 1;
output[5] = end;
return output;
}
}
} else if (firstChar === 36) {
const dollarPos = openBrace + 1;
const afterDollar = openBrace + 2;
if (afterDollar === closeBrace) {
output[0] = 2;
output[1] = start + openBrace;
output[2] = start + dollarPos;
output[3] = start + afterDollar;
output[4] = start + closeBrace + 1;
output[5] = path.length;
return output;
}
output[0] = 1;
output[1] = start + openBrace;
output[2] = start + afterDollar;
output[3] = start + closeBrace;
output[4] = start + closeBrace + 1;
output[5] = end;
return output;
}
}
output[0] = 0;
output[1] = start;
output[2] = start;
output[3] = end;
output[4] = end;
output[5] = end;
return output;
}
/**
* Recursively parses the segments of the given route tree and populates a segment trie.
*
* @param data A reusable Uint16Array for parsing segments. (non important, we're just avoiding allocations)
* @param route The current route to parse.
* @param start The starting index for parsing within the route's full path.
* @param node The current segment node in the trie to populate.
* @param onRoute Callback invoked for each route processed.
*/
function parseSegments(defaultCaseSensitive, data, route, start, node, depth, onRoute) {
onRoute?.(route);
let cursor = start;
{
const path = route.fullPath ?? route.from;
const length = path.length;
const caseSensitive = route.options?.caseSensitive ?? defaultCaseSensitive;
const skipOnParamError = !!(route.options?.params?.parse && route.options?.skipRouteOnParseError?.params);
while (cursor < length) {
const segment = parseSegment(path, cursor, data);
let nextNode;
const start = cursor;
const end = segment[5];
cursor = end + 1;
depth++;
switch (segment[0]) {
case 0: {
const value = path.substring(segment[2], segment[3]);
if (caseSensitive) {
const existingNode = node.static?.get(value);
if (existingNode) nextNode = existingNode;
else {
node.static ??= /* @__PURE__ */ new Map();
const next = createStaticNode(route.fullPath ?? route.from);
next.parent = node;
next.depth = depth;
nextNode = next;
node.static.set(value, next);
}
} else {
const name = value.toLowerCase();
const existingNode = node.staticInsensitive?.get(name);
if (existingNode) nextNode = existingNode;
else {
node.staticInsensitive ??= /* @__PURE__ */ new Map();
const next = createStaticNode(route.fullPath ?? route.from);
next.parent = node;
next.depth = depth;
nextNode = next;
node.staticInsensitive.set(name, next);
}
}
break;
}
case 1: {
const prefix_raw = path.substring(start, segment[1]);
const suffix_raw = path.substring(segment[4], end);
const actuallyCaseSensitive = caseSensitive && !!(prefix_raw || suffix_raw);
const prefix = !prefix_raw ? void 0 : actuallyCaseSensitive ? prefix_raw : prefix_raw.toLowerCase();
const suffix = !suffix_raw ? void 0 : actuallyCaseSensitive ? suffix_raw : suffix_raw.toLowerCase();
const existingNode = !skipOnParamError && node.dynamic?.find((s) => !s.skipOnParamError && s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix);
if (existingNode) nextNode = existingNode;
else {
const next = createDynamicNode(1, route.fullPath ?? route.from, actuallyCaseSensitive, prefix, suffix);
nextNode = next;
next.depth = depth;
next.parent = node;
node.dynamic ??= [];
node.dynamic.push(next);
}
break;
}
case 3: {
const prefix_raw = path.substring(start, segment[1]);
const suffix_raw = path.substring(segment[4], end);
const actuallyCaseSensitive = caseSensitive && !!(prefix_raw || suffix_raw);
const prefix = !prefix_raw ? void 0 : actuallyCaseSensitive ? prefix_raw : prefix_raw.toLowerCase();
const suffix = !suffix_raw ? void 0 : actuallyCaseSensitive ? suffix_raw : suffix_raw.toLowerCase();
const existingNode = !skipOnParamError && node.optional?.find((s) => !s.skipOnParamError && s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix);
if (existingNode) nextNode = existingNode;
else {
const next = createDynamicNode(3, route.fullPath ?? route.from, actuallyCaseSensitive, prefix, suffix);
nextNode = next;
next.parent = node;
next.depth = depth;
node.optional ??= [];
node.optional.push(next);
}
break;
}
case 2: {
const prefix_raw = path.substring(start, segment[1]);
const suffix_raw = path.substring(segment[4], end);
const actuallyCaseSensitive = caseSensitive && !!(prefix_raw || suffix_raw);
const prefix = !prefix_raw ? void 0 : actuallyCaseSensitive ? prefix_raw : prefix_raw.toLowerCase();
const suffix = !suffix_raw ? void 0 : actuallyCaseSensitive ? suffix_raw : suffix_raw.toLowerCase();
const next = createDynamicNode(2, route.fullPath ?? route.from, actuallyCaseSensitive, prefix, suffix);
nextNode = next;
next.parent = node;
next.depth = depth;
node.wildcard ??= [];
node.wildcard.push(next);
}
}
node = nextNode;
}
if (skipOnParamError && route.children && !route.isRoot && route.id && route.id.charCodeAt(route.id.lastIndexOf("/") + 1) === 95) {
const pathlessNode = createStaticNode(route.fullPath ?? route.from);
pathlessNode.kind = SEGMENT_TYPE_PATHLESS;
pathlessNode.parent = node;
depth++;
pathlessNode.depth = depth;
node.pathless ??= [];
node.pathless.push(pathlessNode);
node = pathlessNode;
}
const isLeaf = (route.path || !route.children) && !route.isRoot;
if (isLeaf && path.endsWith("/")) {
const indexNode = createStaticNode(route.fullPath ?? route.from);
indexNode.kind = SEGMENT_TYPE_INDEX;
indexNode.parent = node;
depth++;
indexNode.depth = depth;
node.index = indexNode;
node = indexNode;
}
node.parse = route.options?.params?.parse ?? null;
node.skipOnParamError = skipOnParamError;
node.parsingPriority = route.options?.skipRouteOnParseError?.priority ?? 0;
if (isLeaf && !node.route) {
node.route = route;
node.fullPath = route.fullPath ?? route.from;
}
}
if (route.children) for (const child of route.children) parseSegments(defaultCaseSensitive, data, child, cursor, node, depth, onRoute);
}
function sortDynamic(a, b) {
if (a.skipOnParamError && !b.skipOnParamError) return -1;
if (!a.skipOnParamError && b.skipOnParamError) return 1;
if (a.skipOnParamError && b.skipOnParamError && (a.parsingPriority || b.parsingPriority)) return b.parsingPriority - a.parsingPriority;
if (a.prefix && b.prefix && a.prefix !== b.prefix) {
if (a.prefix.startsWith(b.prefix)) return -1;
if (b.prefix.startsWith(a.prefix)) return 1;
}
if (a.suffix && b.suffix && a.suffix !== b.suffix) {
if (a.suffix.endsWith(b.suffix)) return -1;
if (b.suffix.endsWith(a.suffix)) return 1;
}
if (a.prefix && !b.prefix) return -1;
if (!a.prefix && b.prefix) return 1;
if (a.suffix && !b.suffix) return -1;
if (!a.suffix && b.suffix) return 1;
if (a.caseSensitive && !b.caseSensitive) return -1;
if (!a.caseSensitive && b.caseSensitive) return 1;
return 0;
}
function sortTreeNodes(node) {
if (node.pathless) for (const child of node.pathless) sortTreeNodes(child);
if (node.static) for (const child of node.static.values()) sortTreeNodes(child);
if (node.staticInsensitive) for (const child of node.staticInsensitive.values()) sortTreeNodes(child);
if (node.dynamic?.length) {
node.dynamic.sort(sortDynamic);
for (const child of node.dynamic) sortTreeNodes(child);
}
if (node.optional?.length) {
node.optional.sort(sortDynamic);
for (const child of node.optional) sortTreeNodes(child);
}
if (node.wildcard?.length) {
node.wildcard.sort(sortDynamic);
for (const child of node.wildcard) sortTreeNodes(child);
}
}
function createStaticNode(fullPath) {
return {
kind: 0,
depth: 0,
pathless: null,
index: null,
static: null,
staticInsensitive: null,
dynamic: null,
optional: null,
wildcard: null,
route: null,
fullPath,
parent: null,
parse: null,
skipOnParamError: false,
parsingPriority: 0
};
}
/**
* Keys must be declared in the same order as in `SegmentNode` type,
* to ensure they are represented as the same object class in the engine.
*/
function createDynamicNode(kind, fullPath, caseSensitive, prefix, suffix) {
return {
kind,
depth: 0,
pathless: null,
index: null,
static: null,
staticInsensitive: null,
dynamic: null,
optional: null,
wildcard: null,
route: null,
fullPath,
parent: null,
parse: null,
skipOnParamError: false,
parsingPriority: 0,
caseSensitive,
prefix,
suffix
};
}
function processRouteMasks(routeList, processedTree) {
const segmentTree = createStaticNode("/");
const data = new Uint16Array(6);
for (const route of routeList) parseSegments(false, data, route, 1, segmentTree, 0);
sortTreeNodes(segmentTree);
processedTree.masksTree = segmentTree;
processedTree.flatCache = require_lru_cache.createLRUCache(1e3);
}
/**
* Take an arbitrary list of routes, create a tree from them (if it hasn't been created already), and match a path against it.
*/
function findFlatMatch(path, processedTree) {
path ||= "/";
const cached = processedTree.flatCache.get(path);
if (cached) return cached;
const result = findMatch(path, processedTree.masksTree);
processedTree.flatCache.set(path, result);
return result;
}
/**
* @deprecated keep until v2 so that `router.matchRoute` can keep not caring about the actual route tree
*/
function findSingleMatch(from, caseSensitive, fuzzy, path, processedTree) {
from ||= "/";
path ||= "/";
const key = caseSensitive ? `case\0${from}` : from;
let tree = processedTree.singleCache.get(key);
if (!tree) {
tree = createStaticNode("/");
parseSegments(caseSensitive, new Uint16Array(6), { from }, 1, tree, 0);
processedTree.singleCache.set(key, tree);
}
return findMatch(path, tree, fuzzy);
}
function findRouteMatch(path, processedTree, fuzzy = false) {
const key = fuzzy ? path : `nofuzz\0${path}`;
const cached = processedTree.matchCache.get(key);
if (cached !== void 0) return cached;
path ||= "/";
let result;
try {
result = findMatch(path, processedTree.segmentTree, fuzzy);
} catch (err) {
if (err instanceof URIError) result = null;
else throw err;
}
if (result) result.branch = buildRouteBranch(result.route);
processedTree.matchCache.set(key, result);
return result;
}
/** Trim trailing slashes (except preserving root '/'). */
function trimPathRight(path) {
return path === "/" ? path : path.replace(/\/{1,}$/, "");
}
/**
* Processes a route tree into a segment trie for efficient path matching.
* Also builds lookup maps for routes by ID and by trimmed full path.
*/
function processRouteTree(routeTree, caseSensitive = false, initRoute) {
const segmentTree = createStaticNode(routeTree.fullPath);
const data = new Uint16Array(6);
const routesById = {};
const routesByPath = {};
let index = 0;
parseSegments(caseSensitive, data, routeTree, 1, segmentTree, 0, (route) => {
initRoute?.(route, index);
(0, tiny_invariant.default)(!(route.id in routesById), `Duplicate routes found with id: ${String(route.id)}`);
routesById[route.id] = route;
if (index !== 0 && route.path) {
const trimmedFullPath = trimPathRight(route.fullPath);
if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith("/")) routesByPath[trimmedFullPath] = route;
}
index++;
});
sortTreeNodes(segmentTree);
return {
processedTree: {
segmentTree,
singleCache: require_lru_cache.createLRUCache(1e3),
matchCache: require_lru_cache.createLRUCache(1e3),
flatCache: null,
masksTree: null
},
routesById,
routesByPath
};
}
function findMatch(path, segmentTree, fuzzy = false) {
const parts = path.split("/");
const leaf = getNodeMatch(path, parts, segmentTree, fuzzy);
if (!leaf) return null;
const [rawParams] = extractParams(path, parts, leaf);
return {
route: leaf.node.route,
rawParams,
parsedParams: leaf.parsedParams
};
}
/**
* This function is "resumable":
* - the `leaf` input can contain `extract` and `rawParams` properties from a previous `extractParams` call
* - the returned `state` can be passed back as `extract` in a future call to continue extracting params from where we left off
*
* Inputs are *not* mutated.
*/
function extractParams(path, parts, leaf) {
const list = buildBranch(leaf.node);
let nodeParts = null;
const rawParams = Object.create(null);
/** which segment of the path we're currently processing */
let partIndex = leaf.extract?.part ?? 0;
/** which node of the route tree branch we're currently processing */
let nodeIndex = leaf.extract?.node ?? 0;
/** index of the 1st character of the segment we're processing in the path string */
let pathIndex = leaf.extract?.path ?? 0;
/** which fullPath segment we're currently processing */
let segmentCount = leaf.extract?.segment ?? 0;
for (; nodeIndex < list.length; partIndex++, nodeIndex++, pathIndex++, segmentCount++) {
const node = list[nodeIndex];
if (node.kind === SEGMENT_TYPE_INDEX) break;
if (node.kind === SEGMENT_TYPE_PATHLESS) {
segmentCount--;
partIndex--;
pathIndex--;
continue;
}
const part = parts[partIndex];
const currentPathIndex = pathIndex;
if (part) pathIndex += part.length;
if (node.kind === 1) {
nodeParts ??= leaf.node.fullPath.split("/");
const nodePart = nodeParts[segmentCount];
const preLength = node.prefix?.length ?? 0;
if (nodePart.charCodeAt(preLength) === 123) {
const sufLength = node.suffix?.length ?? 0;
const name = nodePart.substring(preLength + 2, nodePart.length - sufLength - 1);
const value = part.substring(preLength, part.length - sufLength);
rawParams[name] = decodeURIComponent(value);
} else {
const name = nodePart.substring(1);
rawParams[name] = decodeURIComponent(part);
}
} else if (node.kind === 3) {
if (leaf.skipped & 1 << nodeIndex) {
partIndex--;
pathIndex = currentPathIndex - 1;
continue;
}
nodeParts ??= leaf.node.fullPath.split("/");
const nodePart = nodeParts[segmentCount];
const preLength = node.prefix?.length ?? 0;
const sufLength = node.suffix?.length ?? 0;
const name = nodePart.substring(preLength + 3, nodePart.length - sufLength - 1);
const value = node.suffix || node.prefix ? part.substring(preLength, part.length - sufLength) : part;
if (value) rawParams[name] = decodeURIComponent(value);
} else if (node.kind === 2) {
const n = node;
const value = path.substring(currentPathIndex + (n.prefix?.length ?? 0), path.length - (n.suffix?.length ?? 0));
const splat = decodeURIComponent(value);
rawParams["*"] = splat;
rawParams._splat = splat;
break;
}
}
if (leaf.rawParams) Object.assign(rawParams, leaf.rawParams);
return [rawParams, {
part: partIndex,
node: nodeIndex,
path: pathIndex,
segment: segmentCount
}];
}
function buildRouteBranch(route) {
const list = [route];
while (route.parentRoute) {
route = route.parentRoute;
list.push(route);
}
list.reverse();
return list;
}
function buildBranch(node) {
const list = Array(node.depth + 1);
do {
list[node.depth] = node;
node = node.parent;
} while (node);
return list;
}
function getNodeMatch(path, parts, segmentTree, fuzzy) {
if (path === "/" && segmentTree.index) return {
node: segmentTree.index,
skipped: 0
};
const trailingSlash = !require_utils.last(parts);
const pathIsIndex = trailingSlash && path !== "/";
const partsLength = parts.length - (trailingSlash ? 1 : 0);
const stack = [{
node: segmentTree,
index: 1,
skipped: 0,
depth: 1,
statics: 1,
dynamics: 0,
optionals: 0
}];
let wildcardMatch = null;
let bestFuzzy = null;
let bestMatch = null;
while (stack.length) {
const frame = stack.pop();
const { node, index, skipped, depth, statics, dynamics, optionals } = frame;
let { extract, rawParams, parsedParams } = frame;
if (node.skipOnParamError) {
if (!validateMatchParams(path, parts, frame)) continue;
rawParams = frame.rawParams;
extract = frame.extract;
parsedParams = frame.parsedParams;
}
if (fuzzy && node.route && node.kind !== SEGMENT_TYPE_INDEX && isFrameMoreSpecific(bestFuzzy, frame)) bestFuzzy = frame;
const isBeyondPath = index === partsLength;
if (isBeyondPath) {
if (node.route && !pathIsIndex && isFrameMoreSpecific(bestMatch, frame)) bestMatch = frame;
if (!node.optional && !node.wildcard && !node.index && !node.pathless) continue;
}
const part = isBeyondPath ? void 0 : parts[index];
let lowerPart;
if (isBeyondPath && node.index) {
const indexFrame = {
node: node.index,
index,
skipped,
depth: depth + 1,
statics,
dynamics,
optionals,
extract,
rawParams,
parsedParams
};
let indexValid = true;
if (node.index.skipOnParamError) {
if (!validateMatchParams(path, parts, indexFrame)) indexValid = false;
}
if (indexValid) {
if (statics === partsLength && !dynamics && !optionals && !skipped) return indexFrame;
if (isFrameMoreSpecific(bestMatch, indexFrame)) bestMatch = indexFrame;
}
}
if (node.wildcard && isFrameMoreSpecific(wildcardMatch, frame)) for (const segment of node.wildcard) {
const { prefix, suffix } = segment;
if (prefix) {
if (isBeyondPath) continue;
if (!(segment.caseSensitive ? part : lowerPart ??= part.toLowerCase()).startsWith(prefix)) continue;
}
if (suffix) {
if (isBeyondPath) continue;
const end = parts.slice(index).join("/").slice(-suffix.length);
if ((segment.caseSensitive ? end : end.toLowerCase()) !== suffix) continue;
}
const frame = {
node: segment,
index: partsLength,
skipped,
depth,
statics,
dynamics,
optionals,
extract,
rawParams,
parsedParams
};
if (segment.skipOnParamError) {
if (!validateMatchParams(path, parts, frame)) continue;
}
wildcardMatch = frame;
break;
}
if (node.optional) {
const nextSkipped = skipped | 1 << depth;
const nextDepth = depth + 1;
for (let i = node.optional.length - 1; i >= 0; i--) {
const segment = node.optional[i];
stack.push({
node: segment,
index,
skipped: nextSkipped,
depth: nextDepth,
statics,
dynamics,
optionals,
extract,
rawParams,
parsedParams
});
}
if (!isBeyondPath) for (let i = node.optional.length - 1; i >= 0; i--) {
const segment = node.optional[i];
const { prefix, suffix } = segment;
if (prefix || suffix) {
const casePart = segment.caseSensitive ? part : lowerPart ??= part.toLowerCase();
if (prefix && !casePart.startsWith(prefix)) continue;
if (suffix && !casePart.endsWith(suffix)) continue;
}
stack.push({
node: segment,
index: index + 1,
skipped,
depth: nextDepth,
statics,
dynamics,
optionals: optionals + 1,
extract,
rawParams,
parsedParams
});
}
}
if (!isBeyondPath && node.dynamic && part) for (let i = node.dynamic.length - 1; i >= 0; i--) {
const segment = node.dynamic[i];
const { prefix, suffix } = segment;
if (prefix || suffix) {
const casePart = segment.caseSensitive ? part : lowerPart ??= part.toLowerCase();
if (prefix && !casePart.startsWith(prefix)) continue;
if (suffix && !casePart.endsWith(suffix)) continue;
}
stack.push({
node: segment,
index: index + 1,
skipped,
depth: depth + 1,
statics,
dynamics: dynamics + 1,
optionals,
extract,
rawParams,
parsedParams
});
}
if (!isBeyondPath && node.staticInsensitive) {
const match = node.staticInsensitive.get(lowerPart ??= part.toLowerCase());
if (match) stack.push({
node: match,
index: index + 1,
skipped,
depth: depth + 1,
statics: statics + 1,
dynamics,
optionals,
extract,
rawParams,
parsedParams
});
}
if (!isBeyondPath && node.static) {
const match = node.static.get(part);
if (match) stack.push({
node: match,
index: index + 1,
skipped,
depth: depth + 1,
statics: statics + 1,
dynamics,
optionals,
extract,
rawParams,
parsedParams
});
}
if (node.pathless) {
const nextDepth = depth + 1;
for (let i = node.pathless.length - 1; i >= 0; i--) {
const segment = node.pathless[i];
stack.push({
node: segment,
index,
skipped,
depth: nextDepth,
statics,
dynamics,
optionals,
extract,
rawParams,
parsedParams
});
}
}
}
if (bestMatch && wildcardMatch) return isFrameMoreSpecific(wildcardMatch, bestMatch) ? bestMatch : wildcardMatch;
if (bestMatch) return bestMatch;
if (wildcardMatch) return wildcardMatch;
if (fuzzy && bestFuzzy) {
let sliceIndex = bestFuzzy.index;
for (let i = 0; i < bestFuzzy.index; i++) sliceIndex += parts[i].length;
const splat = sliceIndex === path.length ? "/" : path.slice(sliceIndex);
bestFuzzy.rawParams ??= Object.create(null);
bestFuzzy.rawParams["**"] = decodeURIComponent(splat);
return bestFuzzy;
}
return null;
}
function validateMatchParams(path, parts, frame) {
try {
const [rawParams, state] = extractParams(path, parts, frame);
frame.rawParams = rawParams;
frame.extract = state;
const parsed = frame.node.parse(rawParams);
frame.parsedParams = Object.assign(Object.create(null), frame.parsedParams, parsed);
return true;
} catch {
return null;
}
}
function isFrameMoreSpecific(prev, next) {
if (!prev) return true;
return next.statics > prev.statics || next.statics === prev.statics && (next.dynamics > prev.dynamics || next.dynamics === prev.dynamics && (next.optionals > prev.optionals || next.optionals === prev.optionals && ((next.node.kind === SEGMENT_TYPE_INDEX) > (prev.node.kind === SEGMENT_TYPE_INDEX) || next.node.kind === SEGMENT_TYPE_INDEX === (prev.node.kind === SEGMENT_TYPE_INDEX) && next.depth > prev.depth)));
}
//#endregion
exports.findFlatMatch = findFlatMatch;
exports.findRouteMatch = findRouteMatch;
exports.findSingleMatch = findSingleMatch;
exports.parseSegment = parseSegment;
exports.processRouteMasks = processRouteMasks;
exports.processRouteTree = processRouteTree;
//# sourceMappingURL=new-process-route-tree.cjs.map