import type { RouteIds } from './routeInfo' import type { AnyRouter } from './router' export type NoInfer = [T][T extends any ? 0 : never] export type IsAny = 1 extends 0 & TValue ? TYesResult : TNoResult export type PickAsRequired = Omit< TValue, TKey > & Required> export type PickRequired = { [K in keyof T as undefined extends T[K] ? never : K]: T[K] } export type PickOptional = { [K in keyof T as undefined extends T[K] ? K : never]: T[K] } // from https://stackoverflow.com/a/76458160 export type WithoutEmpty = T extends any ? ({} extends T ? never : T) : never export type Expand = T extends object ? T extends infer O ? O extends Function ? O : { [K in keyof O]: O[K] } : never : T export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial } : T export type MakeDifferenceOptional = Omit< TRight, keyof TLeft > & { [K in keyof TLeft & keyof TRight]?: TRight[K] } // from https://stackoverflow.com/a/53955431 // eslint-disable-next-line @typescript-eslint/naming-convention export type IsUnion = ( T extends any ? (U extends T ? false : true) : never ) extends false ? false : true export type IsNonEmptyObject = T extends object ? keyof T extends never ? false : true : false export type Assign = TLeft extends any ? TRight extends any ? IsNonEmptyObject extends false ? TRight : IsNonEmptyObject extends false ? TLeft : keyof TLeft & keyof TRight extends never ? TLeft & TRight : Omit & TRight : never : never export type IntersectAssign = TLeft extends any ? TRight extends any ? IsNonEmptyObject extends false ? TRight : IsNonEmptyObject extends false ? TLeft : TRight & TLeft : never : never export type Timeout = ReturnType export type Updater = | TResult | ((prev?: TPrevious) => TResult) export type NonNullableUpdater = | TResult | ((prev: TPrevious) => TResult) export type ExtractObjects = TUnion extends MergeAllPrimitive ? never : TUnion export type PartialMergeAllObject = ExtractObjects extends infer TObj ? [TObj] extends [never] ? never : { [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any ? TKey extends keyof TObj ? TObj[TKey] : never : never } : never export type MergeAllPrimitive = | ReadonlyArray | number | string | bigint | boolean | symbol | undefined | null export type ExtractPrimitives = TUnion extends MergeAllPrimitive ? TUnion : TUnion extends object ? never : TUnion export type PartialMergeAll = | ExtractPrimitives | PartialMergeAllObject export type Constrain = | (T extends TConstraint ? T : never) | TDefault export type ConstrainLiteral = | (T & TConstraint) | TDefault /** * To be added to router types */ export type UnionToIntersection = ( T extends any ? (arg: T) => any : never ) extends (arg: infer T) => any ? T : never /** * Merges everything in a union into one object. * This mapped type is homomorphic which means it preserves stuff! :) */ export type MergeAllObjects< TUnion, TIntersected = UnionToIntersection>, > = [keyof TIntersected] extends [never] ? never : { [TKey in keyof TIntersected]: TUnion extends any ? TUnion[TKey & keyof TUnion] : never } export type MergeAll = | MergeAllObjects | ExtractPrimitives export type ValidateJSON = ((...args: Array) => any) extends T ? unknown extends T ? never : 'Function is not serializable' : { [K in keyof T]: ValidateJSON } export function last(arr: Array) { return arr[arr.length - 1] } function isFunction(d: any): d is Function { return typeof d === 'function' } export function functionalUpdate( updater: Updater | NonNullableUpdater, previous: TPrevious, ): TResult { if (isFunction(updater)) { return updater(previous) } return updater } export function pick( parent: TValue, keys: Array, ): Pick { return keys.reduce((obj: any, key: TKey) => { obj[key] = parent[key] return obj }, {} as any) } /** * This function returns `prev` if `_next` is deeply equal. * If not, it will replace any deeply equal children of `b` with those of `a`. * This can be used for structural sharing between immutable JSON values for example. * Do not use this with signals */ export function replaceEqualDeep(prev: any, _next: T): T { if (prev === _next) { return prev } const next = _next as any const array = isPlainArray(prev) && isPlainArray(next) if (array || (isPlainObject(prev) && isPlainObject(next))) { const prevItems = array ? prev : Object.keys(prev) const prevSize = prevItems.length const nextItems = array ? next : Object.keys(next) const nextSize = nextItems.length const copy: any = array ? [] : {} let equalItems = 0 for (let i = 0; i < nextSize; i++) { const key = array ? i : (nextItems[i] as any) if ( ((!array && prevItems.includes(key)) || array) && prev[key] === undefined && next[key] === undefined ) { copy[key] = undefined equalItems++ } else { copy[key] = replaceEqualDeep(prev[key], next[key]) if (copy[key] === prev[key] && prev[key] !== undefined) { equalItems++ } } } return prevSize === nextSize && equalItems === prevSize ? prev : copy } return next } // Copied from: https://github.com/jonschlinkert/is-plain-object export function isPlainObject(o: any) { if (!hasObjectPrototype(o)) { return false } // If has modified constructor const ctor = o.constructor if (typeof ctor === 'undefined') { return true } // If has modified prototype const prot = ctor.prototype if (!hasObjectPrototype(prot)) { return false } // If constructor does not have an Object-specific method if (!prot.hasOwnProperty('isPrototypeOf')) { return false } // Most likely a plain Object return true } function hasObjectPrototype(o: any) { return Object.prototype.toString.call(o) === '[object Object]' } export function isPlainArray(value: unknown): value is Array { return Array.isArray(value) && value.length === Object.keys(value).length } function getObjectKeys(obj: any, ignoreUndefined: boolean) { let keys = Object.keys(obj) if (ignoreUndefined) { keys = keys.filter((key) => obj[key] !== undefined) } return keys } export function deepEqual( a: any, b: any, opts?: { partial?: boolean; ignoreUndefined?: boolean }, ): boolean { if (a === b) { return true } if (typeof a !== typeof b) { return false } if (isPlainObject(a) && isPlainObject(b)) { const ignoreUndefined = opts?.ignoreUndefined ?? true const aKeys = getObjectKeys(a, ignoreUndefined) const bKeys = getObjectKeys(b, ignoreUndefined) if (!opts?.partial && aKeys.length !== bKeys.length) { return false } return bKeys.every((key) => deepEqual(a[key], b[key], opts)) } if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) { return false } return !a.some((item, index) => !deepEqual(item, b[index], opts)) } return false } export type StringLiteral = T extends string ? string extends T ? string : T : never export type ThrowOrOptional = TThrow extends true ? T : T | undefined export type StrictOrFrom< TRouter extends AnyRouter, TFrom, TStrict extends boolean = true, > = TStrict extends false ? { from?: never strict: TStrict } : { from: ConstrainLiteral> strict?: TStrict } export type ThrowConstraint< TStrict extends boolean, TThrow extends boolean, > = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow export type ControlledPromise = Promise & { resolve: (value: T) => void reject: (value: any) => void status: 'pending' | 'resolved' | 'rejected' value?: T } export function createControlledPromise(onResolve?: (value: T) => void) { let resolveLoadPromise!: (value: T) => void let rejectLoadPromise!: (value: any) => void const controlledPromise = new Promise((resolve, reject) => { resolveLoadPromise = resolve rejectLoadPromise = reject }) as ControlledPromise controlledPromise.status = 'pending' controlledPromise.resolve = (value: T) => { controlledPromise.status = 'resolved' controlledPromise.value = value resolveLoadPromise(value) onResolve?.(value) } controlledPromise.reject = (e) => { controlledPromise.status = 'rejected' rejectLoadPromise(e) } return controlledPromise } /** * * @deprecated use `jsesc` instead */ export function escapeJSON(jsonString: string) { return jsonString .replace(/\\/g, '\\\\') // Escape backslashes .replace(/'/g, "\\'") // Escape single quotes .replace(/"/g, '\\"') // Escape double quotes } export function shallow(objA: T, objB: T) { if (Object.is(objA, objB)) { return true } if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false } const keysA = Object.keys(objA) if (keysA.length !== Object.keys(objB).length) { return false } for (const item of keysA) { if ( !Object.prototype.hasOwnProperty.call(objB, item) || !Object.is(objA[item as keyof T], objB[item as keyof T]) ) { return false } } return true } /** * Checks if a string contains URI-encoded special characters (e.g., %3F, %20). * * @param {string} inputString The string to check. * @returns {boolean} True if the string contains URI-encoded characters, false otherwise. * @example * ```typescript * const str1 = "foo%3Fbar"; * const hasEncodedChars = hasUriEncodedChars(str1); // returns true * ``` */ export function hasUriEncodedChars(inputString: string): boolean { // This regex looks for a percent sign followed by two hexadecimal digits const pattern = /%[0-9A-Fa-f]{2}/ return pattern.test(inputString) }