@tanstack/router-core 1.135.2 → 1.136.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/path.cjs +0 -9
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/path.d.cts +1 -3
- package/dist/cjs/router.cjs +5 -4
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/utils.cjs +29 -28
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/path.d.ts +1 -3
- package/dist/esm/path.js +0 -9
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/router.js +5 -4
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.js +29 -28
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/path.ts +2 -11
- package/src/router.ts +11 -10
- package/src/utils.ts +49 -50
package/dist/esm/utils.js
CHANGED
|
@@ -161,37 +161,38 @@ function findLast(array, predicate) {
|
|
|
161
161
|
}
|
|
162
162
|
return void 0;
|
|
163
163
|
}
|
|
164
|
-
const DECODE_IGNORE_LIST =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
return partsToJoin.join(char);
|
|
164
|
+
const DECODE_IGNORE_LIST = [
|
|
165
|
+
"%25",
|
|
166
|
+
// %
|
|
167
|
+
"%5C"
|
|
168
|
+
// \
|
|
169
|
+
];
|
|
170
|
+
function splitAndDecode(part, decodeIgnore, startIndex = 0) {
|
|
171
|
+
for (let i = startIndex; i < decodeIgnore.length; i++) {
|
|
172
|
+
const char = decodeIgnore[i].toUpperCase();
|
|
173
|
+
if (part.includes(char)) {
|
|
174
|
+
const partsToDecode = part.split(char);
|
|
175
|
+
const partsToJoin = new Array(partsToDecode.length);
|
|
176
|
+
for (let j = 0; j < partsToDecode.length; j++) {
|
|
177
|
+
const partToDecode = partsToDecode[j];
|
|
178
|
+
partsToJoin[j] = splitAndDecode(partToDecode, decodeIgnore, i + 1);
|
|
181
179
|
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
return decodeURI(part2);
|
|
185
|
-
} catch {
|
|
186
|
-
return part2.replaceAll(/%[0-9A-F]{2}/g, (match) => {
|
|
187
|
-
try {
|
|
188
|
-
return decodeURI(match);
|
|
189
|
-
} catch {
|
|
190
|
-
return match;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
180
|
+
return partsToJoin.join(char);
|
|
193
181
|
}
|
|
194
182
|
}
|
|
183
|
+
try {
|
|
184
|
+
return decodeURI(part);
|
|
185
|
+
} catch {
|
|
186
|
+
return part.replaceAll(/%[0-9A-F]{2}/g, (match) => {
|
|
187
|
+
try {
|
|
188
|
+
return decodeURI(match);
|
|
189
|
+
} catch {
|
|
190
|
+
return match;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function decodePath(part, decodeIgnore = DECODE_IGNORE_LIST) {
|
|
195
196
|
if (part === "" || !/%[0-9A-Fa-f]{2}/g.test(part)) return part;
|
|
196
197
|
const normalizedPart = part.replaceAll(
|
|
197
198
|
/%[0-9a-f]{2}/g,
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\n/**\n * Return the last element of an array.\n * Intended for non-empty arrays used within router internals.\n */\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\n/**\n * Apply a value-or-updater to a previous value.\n * Accepts either a literal value or a function of the previous value.\n */\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next\n\n const prevItems = array ? prev : getEnumerableOwnKeys(prev)\n if (!prevItems) return next\n const nextItems = array ? next : getEnumerableOwnKeys(next)\n if (!nextItems) return next\n const prevSize = prevItems.length\n const nextSize = nextItems.length\n const copy: any = array ? new Array(nextSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n const p = prev[key]\n const n = next[key]\n\n if (p === n) {\n copy[key] = p\n if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++\n continue\n }\n\n if (\n p === null ||\n n === null ||\n typeof p !== 'object' ||\n typeof n !== 'object'\n ) {\n copy[key] = n\n continue\n }\n\n const v = replaceEqualDeep(p, n)\n copy[key] = v\n if (v === p) equalItems++\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n}\n\n/**\n * Equivalent to `Reflect.ownKeys`, but ensures that objects are \"clone-friendly\":\n * will return false if object has any non-enumerable properties.\n */\nfunction getEnumerableOwnKeys(o: object) {\n const keys = []\n const names = Object.getOwnPropertyNames(o)\n for (const name of names) {\n if (!Object.prototype.propertyIsEnumerable.call(o, name)) return false\n keys.push(name)\n }\n const symbols = Object.getOwnPropertySymbols(o)\n for (const symbol of symbols) {\n if (!Object.prototype.propertyIsEnumerable.call(o, symbol)) return false\n keys.push(symbol)\n }\n return keys\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\n/**\n * Check if a value is a \"plain\" array (no extra enumerable keys).\n */\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n/**\n * Perform a deep equality check with options for partial comparison and\n * ignoring `undefined` values. Optimized for router state comparisons.\n */\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0, l = a.length; i < l; i++) {\n if (!deepEqual(a[i], b[i], opts)) return false\n }\n return true\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n\n if (opts?.partial) {\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n if (!deepEqual(a[k], b[k], opts)) return false\n }\n }\n return true\n }\n\n let aCount = 0\n if (!ignoreUndefined) {\n aCount = Object.keys(a).length\n } else {\n for (const k in a) {\n if (a[k] !== undefined) aCount++\n }\n }\n\n let bCount = 0\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n bCount++\n if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false\n }\n }\n\n return aCount === bCount\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\n/**\n * Create a promise with exposed resolve/reject and status fields.\n * Useful for coordinating async router lifecycle operations.\n */\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Heuristically detect dynamic import \"module not found\" errors\n * across major browsers for lazy route component handling.\n */\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n\nexport function findLast<T>(\n array: ReadonlyArray<T>,\n predicate: (item: T) => boolean,\n): T | undefined {\n for (let i = array.length - 1; i >= 0; i--) {\n const item = array[i]!\n if (predicate(item)) return item\n }\n return undefined\n}\n\nconst DECODE_IGNORE_LIST = Array.from(\n new Map([\n ['%', '%25'],\n ['\\\\', '%5C'],\n ]).values(),\n)\n\nexport function decodePath(\n part: string,\n decodeIgnore: Array<string> = DECODE_IGNORE_LIST,\n): string {\n function splitAndDecode(\n part: string,\n decodeIgnore: Array<string>,\n startIndex = 0,\n ): string {\n // decode the path / path segment by splitting it into parts defined by the ignore list.\n // once these pieces have been decoded, join them back together to form the final decoded path segment with the ignored character in place.\n // we walk through the ignore list linearly, breaking the segment up into pieces and decoding each piece individually.\n // use index traversal to avoid making unnecessary copies of the array.\n for (let i = startIndex; i < decodeIgnore.length; i++) {\n const char = decodeIgnore[i]!.toUpperCase()\n\n // check if the part includes the current ignore character\n // if it doesn't continue to the next ignore character\n if (part.includes(char)) {\n // split the part into pieces that needs to be checked and decoded\n const partsToDecode = part.split(char)\n const partsToJoin: Array<string> = []\n\n // now check and decode each piece individually taking into consideration the remaining ignored characters.\n // since we are walking through the list linearly, we only need to consider ignore items not yet traversed.\n for (const partToDecode of partsToDecode) {\n // once we have traversed the entire ignore list, each decoded part is returned.\n partsToJoin.push(splitAndDecode(partToDecode, decodeIgnore, i + 1))\n }\n\n // and join them back together to form the final decoded path segment with the ignored character in place.\n return partsToJoin.join(char)\n }\n }\n\n // once we have reached the end of the ignore list, we start walking back returning each decoded part.\n // should there be no matching characters, the path segment as a whole will be decoded.\n try {\n return decodeURI(part)\n } catch {\n // if the decoding fails, try to decode the various parts leaving the malformed tags in place\n return part.replaceAll(/%[0-9A-F]{2}/g, (match) => {\n try {\n return decodeURI(match)\n } catch {\n return match\n }\n })\n }\n }\n\n // if the path segment does not contain any encoded uri components return the path as is\n if (part === '' || !/%[0-9A-Fa-f]{2}/g.test(part)) return part\n\n // ensure all encoded characters are uppercase\n const normalizedPart = part.replaceAll(/%[0-9a-f]{2}/g, (match) =>\n match.toUpperCase(),\n )\n\n return splitAndDecode(normalizedPart, decodeIgnore)\n}\n"],"names":["part","decodeIgnore"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\n/**\n * Return the last element of an array.\n * Intended for non-empty arrays used within router internals.\n */\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\n/**\n * Apply a value-or-updater to a previous value.\n * Accepts either a literal value or a function of the previous value.\n */\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next\n\n const prevItems = array ? prev : getEnumerableOwnKeys(prev)\n if (!prevItems) return next\n const nextItems = array ? next : getEnumerableOwnKeys(next)\n if (!nextItems) return next\n const prevSize = prevItems.length\n const nextSize = nextItems.length\n const copy: any = array ? new Array(nextSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n const p = prev[key]\n const n = next[key]\n\n if (p === n) {\n copy[key] = p\n if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++\n continue\n }\n\n if (\n p === null ||\n n === null ||\n typeof p !== 'object' ||\n typeof n !== 'object'\n ) {\n copy[key] = n\n continue\n }\n\n const v = replaceEqualDeep(p, n)\n copy[key] = v\n if (v === p) equalItems++\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n}\n\n/**\n * Equivalent to `Reflect.ownKeys`, but ensures that objects are \"clone-friendly\":\n * will return false if object has any non-enumerable properties.\n */\nfunction getEnumerableOwnKeys(o: object) {\n const keys = []\n const names = Object.getOwnPropertyNames(o)\n for (const name of names) {\n if (!Object.prototype.propertyIsEnumerable.call(o, name)) return false\n keys.push(name)\n }\n const symbols = Object.getOwnPropertySymbols(o)\n for (const symbol of symbols) {\n if (!Object.prototype.propertyIsEnumerable.call(o, symbol)) return false\n keys.push(symbol)\n }\n return keys\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\n/**\n * Check if a value is a \"plain\" array (no extra enumerable keys).\n */\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n/**\n * Perform a deep equality check with options for partial comparison and\n * ignoring `undefined` values. Optimized for router state comparisons.\n */\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0, l = a.length; i < l; i++) {\n if (!deepEqual(a[i], b[i], opts)) return false\n }\n return true\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n\n if (opts?.partial) {\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n if (!deepEqual(a[k], b[k], opts)) return false\n }\n }\n return true\n }\n\n let aCount = 0\n if (!ignoreUndefined) {\n aCount = Object.keys(a).length\n } else {\n for (const k in a) {\n if (a[k] !== undefined) aCount++\n }\n }\n\n let bCount = 0\n for (const k in b) {\n if (!ignoreUndefined || b[k] !== undefined) {\n bCount++\n if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false\n }\n }\n\n return aCount === bCount\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\n/**\n * Create a promise with exposed resolve/reject and status fields.\n * Useful for coordinating async router lifecycle operations.\n */\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Heuristically detect dynamic import \"module not found\" errors\n * across major browsers for lazy route component handling.\n */\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n\nexport function findLast<T>(\n array: ReadonlyArray<T>,\n predicate: (item: T) => boolean,\n): T | undefined {\n for (let i = array.length - 1; i >= 0; i--) {\n const item = array[i]!\n if (predicate(item)) return item\n }\n return undefined\n}\n\nconst DECODE_IGNORE_LIST = [\n '%25', // %\n '%5C', // \\\n]\n\nfunction splitAndDecode(\n part: string,\n decodeIgnore: Array<string>,\n startIndex = 0,\n): string {\n // decode the path / path segment by splitting it into parts defined by the ignore list.\n // once these pieces have been decoded, join them back together to form the final decoded path segment with the ignored character in place.\n // we walk through the ignore list linearly, breaking the segment up into pieces and decoding each piece individually.\n // use index traversal to avoid making unnecessary copies of the array.\n for (let i = startIndex; i < decodeIgnore.length; i++) {\n const char = decodeIgnore[i]!.toUpperCase()\n\n // check if the part includes the current ignore character\n // if it doesn't continue to the next ignore character\n if (part.includes(char)) {\n // split the part into pieces that needs to be checked and decoded\n const partsToDecode = part.split(char)\n const partsToJoin = new Array<string>(partsToDecode.length)\n\n // now check and decode each piece individually taking into consideration the remaining ignored characters.\n // since we are walking through the list linearly, we only need to consider ignore items not yet traversed.\n for (let j = 0; j < partsToDecode.length; j++) {\n const partToDecode = partsToDecode[j]!\n // once we have traversed the entire ignore list, each decoded part is returned.\n partsToJoin[j] = splitAndDecode(partToDecode, decodeIgnore, i + 1)\n }\n\n // and join them back together to form the final decoded path segment with the ignored character in place.\n return partsToJoin.join(char)\n }\n }\n\n // once we have reached the end of the ignore list, we start walking back returning each decoded part.\n // should there be no matching characters, the path segment as a whole will be decoded.\n try {\n return decodeURI(part)\n } catch {\n // if the decoding fails, try to decode the various parts leaving the malformed tags in place\n return part.replaceAll(/%[0-9A-F]{2}/g, (match) => {\n try {\n return decodeURI(match)\n } catch {\n return match\n }\n })\n }\n}\n\nexport function decodePath(\n part: string,\n decodeIgnore: Array<string> = DECODE_IGNORE_LIST,\n): string {\n // if the path segment does not contain any encoded uri components return the path as is\n if (part === '' || !/%[0-9A-Fa-f]{2}/g.test(part)) return part\n\n // ensure all encoded characters are uppercase\n const normalizedPart = part.replaceAll(/%[0-9a-f]{2}/g, (match) =>\n match.toUpperCase(),\n )\n\n return splitAndDecode(normalizedPart, decodeIgnore)\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/path.ts
CHANGED
|
@@ -377,7 +377,6 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
|
|
|
377
377
|
interface InterpolatePathOptions {
|
|
378
378
|
path?: string
|
|
379
379
|
params: Record<string, unknown>
|
|
380
|
-
leaveParams?: boolean
|
|
381
380
|
// Map of encoded chars to decoded chars (e.g. '%40' -> '@') that should remain decoded in path params
|
|
382
381
|
decodeCharMap?: Map<string, string>
|
|
383
382
|
parseCache?: ParsePathnameCache
|
|
@@ -393,7 +392,6 @@ type InterPolatePathResult = {
|
|
|
393
392
|
*
|
|
394
393
|
* - Encodes params safely (configurable allowed characters)
|
|
395
394
|
* - Supports `{-$optional}` segments, `{prefix{$id}suffix}` and `{$}` wildcards
|
|
396
|
-
* - Optionally leaves placeholders or wildcards in place
|
|
397
395
|
*/
|
|
398
396
|
/**
|
|
399
397
|
* Interpolate params and wildcards into a route path template.
|
|
@@ -402,7 +400,6 @@ type InterPolatePathResult = {
|
|
|
402
400
|
export function interpolatePath({
|
|
403
401
|
path,
|
|
404
402
|
params,
|
|
405
|
-
leaveParams,
|
|
406
403
|
decodeCharMap,
|
|
407
404
|
parseCache,
|
|
408
405
|
}: InterpolatePathOptions): InterPolatePathResult {
|
|
@@ -452,6 +449,7 @@ export function interpolatePath({
|
|
|
452
449
|
}
|
|
453
450
|
|
|
454
451
|
const value = encodeParam('_splat')
|
|
452
|
+
|
|
455
453
|
return `${segmentPrefix}${value}${segmentSuffix}`
|
|
456
454
|
}
|
|
457
455
|
|
|
@@ -464,10 +462,7 @@ export function interpolatePath({
|
|
|
464
462
|
|
|
465
463
|
const segmentPrefix = segment.prefixSegment || ''
|
|
466
464
|
const segmentSuffix = segment.suffixSegment || ''
|
|
467
|
-
|
|
468
|
-
const value = encodeParam(segment.value)
|
|
469
|
-
return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}`
|
|
470
|
-
}
|
|
465
|
+
|
|
471
466
|
return `${segmentPrefix}${encodeParam(key) ?? 'undefined'}${segmentSuffix}`
|
|
472
467
|
}
|
|
473
468
|
|
|
@@ -489,10 +484,6 @@ export function interpolatePath({
|
|
|
489
484
|
|
|
490
485
|
usedParams[key] = params[key]
|
|
491
486
|
|
|
492
|
-
if (leaveParams) {
|
|
493
|
-
const value = encodeParam(segment.value)
|
|
494
|
-
return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}`
|
|
495
|
-
}
|
|
496
487
|
return `${segmentPrefix}${encodeParam(key) ?? ''}${segmentSuffix}`
|
|
497
488
|
}
|
|
498
489
|
|
package/src/router.ts
CHANGED
|
@@ -1683,17 +1683,18 @@ export class RouterCore<
|
|
|
1683
1683
|
}
|
|
1684
1684
|
}
|
|
1685
1685
|
|
|
1686
|
-
const nextPathname =
|
|
1687
|
-
|
|
1688
|
-
// Use the original template path for interpolation
|
|
1686
|
+
const nextPathname = opts.leaveParams
|
|
1687
|
+
? // Use the original template path for interpolation
|
|
1689
1688
|
// This preserves the original parameter syntax including optional parameters
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1689
|
+
nextTo
|
|
1690
|
+
: decodePath(
|
|
1691
|
+
interpolatePath({
|
|
1692
|
+
path: nextTo,
|
|
1693
|
+
params: nextParams,
|
|
1694
|
+
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
1695
|
+
parseCache: this.parsePathnameCache,
|
|
1696
|
+
}).interpolatedPath,
|
|
1697
|
+
)
|
|
1697
1698
|
|
|
1698
1699
|
// Resolve the next search
|
|
1699
1700
|
let nextSearch = fromSearch
|
package/src/utils.ts
CHANGED
|
@@ -489,64 +489,63 @@ export function findLast<T>(
|
|
|
489
489
|
return undefined
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
-
const DECODE_IGNORE_LIST =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
]).values(),
|
|
497
|
-
)
|
|
492
|
+
const DECODE_IGNORE_LIST = [
|
|
493
|
+
'%25', // %
|
|
494
|
+
'%5C', // \
|
|
495
|
+
]
|
|
498
496
|
|
|
499
|
-
|
|
497
|
+
function splitAndDecode(
|
|
500
498
|
part: string,
|
|
501
|
-
decodeIgnore: Array<string
|
|
499
|
+
decodeIgnore: Array<string>,
|
|
500
|
+
startIndex = 0,
|
|
502
501
|
): string {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
)
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
// since we are walking through the list linearly, we only need to consider ignore items not yet traversed.
|
|
524
|
-
for (const partToDecode of partsToDecode) {
|
|
525
|
-
// once we have traversed the entire ignore list, each decoded part is returned.
|
|
526
|
-
partsToJoin.push(splitAndDecode(partToDecode, decodeIgnore, i + 1))
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// and join them back together to form the final decoded path segment with the ignored character in place.
|
|
530
|
-
return partsToJoin.join(char)
|
|
502
|
+
// decode the path / path segment by splitting it into parts defined by the ignore list.
|
|
503
|
+
// once these pieces have been decoded, join them back together to form the final decoded path segment with the ignored character in place.
|
|
504
|
+
// we walk through the ignore list linearly, breaking the segment up into pieces and decoding each piece individually.
|
|
505
|
+
// use index traversal to avoid making unnecessary copies of the array.
|
|
506
|
+
for (let i = startIndex; i < decodeIgnore.length; i++) {
|
|
507
|
+
const char = decodeIgnore[i]!.toUpperCase()
|
|
508
|
+
|
|
509
|
+
// check if the part includes the current ignore character
|
|
510
|
+
// if it doesn't continue to the next ignore character
|
|
511
|
+
if (part.includes(char)) {
|
|
512
|
+
// split the part into pieces that needs to be checked and decoded
|
|
513
|
+
const partsToDecode = part.split(char)
|
|
514
|
+
const partsToJoin = new Array<string>(partsToDecode.length)
|
|
515
|
+
|
|
516
|
+
// now check and decode each piece individually taking into consideration the remaining ignored characters.
|
|
517
|
+
// since we are walking through the list linearly, we only need to consider ignore items not yet traversed.
|
|
518
|
+
for (let j = 0; j < partsToDecode.length; j++) {
|
|
519
|
+
const partToDecode = partsToDecode[j]!
|
|
520
|
+
// once we have traversed the entire ignore list, each decoded part is returned.
|
|
521
|
+
partsToJoin[j] = splitAndDecode(partToDecode, decodeIgnore, i + 1)
|
|
531
522
|
}
|
|
532
|
-
}
|
|
533
523
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
try {
|
|
537
|
-
return decodeURI(part)
|
|
538
|
-
} catch {
|
|
539
|
-
// if the decoding fails, try to decode the various parts leaving the malformed tags in place
|
|
540
|
-
return part.replaceAll(/%[0-9A-F]{2}/g, (match) => {
|
|
541
|
-
try {
|
|
542
|
-
return decodeURI(match)
|
|
543
|
-
} catch {
|
|
544
|
-
return match
|
|
545
|
-
}
|
|
546
|
-
})
|
|
524
|
+
// and join them back together to form the final decoded path segment with the ignored character in place.
|
|
525
|
+
return partsToJoin.join(char)
|
|
547
526
|
}
|
|
548
527
|
}
|
|
549
528
|
|
|
529
|
+
// once we have reached the end of the ignore list, we start walking back returning each decoded part.
|
|
530
|
+
// should there be no matching characters, the path segment as a whole will be decoded.
|
|
531
|
+
try {
|
|
532
|
+
return decodeURI(part)
|
|
533
|
+
} catch {
|
|
534
|
+
// if the decoding fails, try to decode the various parts leaving the malformed tags in place
|
|
535
|
+
return part.replaceAll(/%[0-9A-F]{2}/g, (match) => {
|
|
536
|
+
try {
|
|
537
|
+
return decodeURI(match)
|
|
538
|
+
} catch {
|
|
539
|
+
return match
|
|
540
|
+
}
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function decodePath(
|
|
546
|
+
part: string,
|
|
547
|
+
decodeIgnore: Array<string> = DECODE_IGNORE_LIST,
|
|
548
|
+
): string {
|
|
550
549
|
// if the path segment does not contain any encoded uri components return the path as is
|
|
551
550
|
if (part === '' || !/%[0-9A-Fa-f]{2}/g.test(part)) return part
|
|
552
551
|
|