@tanstack/router-core 0.0.1-beta.9 → 1.97.21

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.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/Matches.cjs +13 -0
  4. package/dist/cjs/Matches.cjs.map +1 -0
  5. package/dist/cjs/Matches.d.cts +28 -0
  6. package/dist/cjs/RouterProvider.d.cts +18 -0
  7. package/dist/cjs/defer.cjs +25 -0
  8. package/dist/cjs/defer.cjs.map +1 -0
  9. package/dist/cjs/defer.d.cts +20 -0
  10. package/dist/cjs/index.cjs +50 -0
  11. package/dist/cjs/index.cjs.map +1 -0
  12. package/dist/cjs/index.d.cts +25 -0
  13. package/dist/cjs/link.cjs +5 -0
  14. package/dist/cjs/link.cjs.map +1 -0
  15. package/dist/cjs/link.d.cts +51 -0
  16. package/dist/cjs/location.d.cts +12 -0
  17. package/dist/cjs/manifest.d.cts +24 -0
  18. package/dist/cjs/path.cjs +289 -0
  19. package/dist/cjs/path.cjs.map +1 -0
  20. package/dist/cjs/path.d.cts +34 -0
  21. package/dist/cjs/qss.cjs +51 -0
  22. package/dist/cjs/qss.cjs.map +1 -0
  23. package/dist/cjs/qss.d.cts +27 -0
  24. package/dist/cjs/root.cjs +5 -0
  25. package/dist/cjs/root.cjs.map +1 -0
  26. package/dist/cjs/root.d.cts +2 -0
  27. package/dist/cjs/route.d.cts +148 -0
  28. package/dist/cjs/router.cjs +19 -0
  29. package/dist/cjs/router.cjs.map +1 -0
  30. package/dist/cjs/router.d.cts +31 -0
  31. package/dist/cjs/searchMiddleware.cjs +42 -0
  32. package/dist/cjs/searchMiddleware.cjs.map +1 -0
  33. package/dist/cjs/searchMiddleware.d.cts +5 -0
  34. package/dist/cjs/searchParams.cjs +61 -0
  35. package/dist/cjs/searchParams.cjs.map +1 -0
  36. package/dist/cjs/searchParams.d.cts +7 -0
  37. package/dist/cjs/serializer.d.cts +15 -0
  38. package/dist/cjs/structuralSharing.d.cts +4 -0
  39. package/dist/cjs/utils.cjs +155 -0
  40. package/dist/cjs/utils.cjs.map +1 -0
  41. package/dist/cjs/utils.d.cts +81 -0
  42. package/dist/cjs/validators.d.cts +51 -0
  43. package/dist/esm/Matches.d.ts +28 -0
  44. package/dist/esm/Matches.js +13 -0
  45. package/dist/esm/Matches.js.map +1 -0
  46. package/dist/esm/RouterProvider.d.ts +18 -0
  47. package/dist/esm/defer.d.ts +20 -0
  48. package/dist/esm/defer.js +25 -0
  49. package/dist/esm/defer.js.map +1 -0
  50. package/dist/esm/index.d.ts +25 -0
  51. package/dist/esm/index.js +50 -0
  52. package/dist/esm/index.js.map +1 -0
  53. package/dist/esm/link.d.ts +51 -0
  54. package/dist/esm/link.js +5 -0
  55. package/dist/esm/link.js.map +1 -0
  56. package/dist/esm/location.d.ts +12 -0
  57. package/dist/esm/manifest.d.ts +24 -0
  58. package/dist/esm/path.d.ts +34 -0
  59. package/dist/esm/path.js +289 -0
  60. package/dist/esm/path.js.map +1 -0
  61. package/dist/esm/qss.d.ts +27 -0
  62. package/dist/esm/qss.js +51 -0
  63. package/dist/esm/qss.js.map +1 -0
  64. package/dist/esm/root.d.ts +2 -0
  65. package/dist/esm/root.js +5 -0
  66. package/dist/esm/root.js.map +1 -0
  67. package/dist/esm/route.d.ts +148 -0
  68. package/dist/esm/router.d.ts +31 -0
  69. package/dist/esm/router.js +19 -0
  70. package/dist/esm/router.js.map +1 -0
  71. package/dist/esm/searchMiddleware.d.ts +5 -0
  72. package/dist/esm/searchMiddleware.js +42 -0
  73. package/dist/esm/searchMiddleware.js.map +1 -0
  74. package/dist/esm/searchParams.d.ts +7 -0
  75. package/dist/esm/searchParams.js +61 -0
  76. package/dist/esm/searchParams.js.map +1 -0
  77. package/dist/esm/serializer.d.ts +15 -0
  78. package/dist/esm/structuralSharing.d.ts +4 -0
  79. package/dist/esm/utils.d.ts +81 -0
  80. package/dist/esm/utils.js +155 -0
  81. package/dist/esm/utils.js.map +1 -0
  82. package/dist/esm/validators.d.ts +51 -0
  83. package/package.json +35 -32
  84. package/src/Matches.ts +94 -0
  85. package/src/RouterProvider.ts +20 -0
  86. package/src/defer.ts +52 -0
  87. package/src/index.ts +206 -19
  88. package/src/link.ts +122 -300
  89. package/src/location.ts +13 -0
  90. package/src/manifest.ts +32 -0
  91. package/src/path.ts +238 -47
  92. package/src/qss.ts +56 -19
  93. package/src/root.ts +2 -0
  94. package/src/route.ts +296 -223
  95. package/src/router.ts +34 -1281
  96. package/src/searchMiddleware.ts +54 -0
  97. package/src/searchParams.ts +43 -20
  98. package/src/serializer.ts +24 -0
  99. package/src/structuralSharing.ts +7 -0
  100. package/src/utils.ts +314 -75
  101. package/src/validators.ts +121 -0
  102. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  103. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  104. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  105. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  106. package/build/cjs/node_modules/history/index.js +0 -815
  107. package/build/cjs/node_modules/history/index.js.map +0 -1
  108. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  109. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  110. package/build/cjs/packages/router-core/src/index.js +0 -58
  111. package/build/cjs/packages/router-core/src/index.js.map +0 -1
  112. package/build/cjs/packages/router-core/src/path.js +0 -222
  113. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  114. package/build/cjs/packages/router-core/src/qss.js +0 -71
  115. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  116. package/build/cjs/packages/router-core/src/route.js +0 -150
  117. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  118. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  119. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  120. package/build/cjs/packages/router-core/src/routeMatch.js +0 -266
  121. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  122. package/build/cjs/packages/router-core/src/router.js +0 -822
  123. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  124. package/build/cjs/packages/router-core/src/searchParams.js +0 -70
  125. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  126. package/build/cjs/packages/router-core/src/utils.js +0 -125
  127. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  128. package/build/esm/index.js +0 -2481
  129. package/build/esm/index.js.map +0 -1
  130. package/build/stats-html.html +0 -4034
  131. package/build/stats-react.json +0 -493
  132. package/build/types/index.d.ts +0 -618
  133. package/build/umd/index.development.js +0 -2514
  134. package/build/umd/index.development.js.map +0 -1
  135. package/build/umd/index.production.js +0 -12
  136. package/build/umd/index.production.js.map +0 -1
  137. package/src/frameworks.ts +0 -12
  138. package/src/routeConfig.ts +0 -495
  139. package/src/routeInfo.ts +0 -228
  140. package/src/routeMatch.ts +0 -374
@@ -0,0 +1,54 @@
1
+ import { deepEqual } from './utils'
2
+ import type { NoInfer, PickOptional } from './utils'
3
+ import type { SearchMiddleware } from './route'
4
+ import type { IsRequiredParams } from './link'
5
+
6
+ export function retainSearchParams<TSearchSchema extends object>(
7
+ keys: Array<keyof TSearchSchema> | true,
8
+ ): SearchMiddleware<TSearchSchema> {
9
+ return ({ search, next }) => {
10
+ const result = next(search)
11
+ if (keys === true) {
12
+ return { ...search, ...result }
13
+ }
14
+ // add missing keys from search to result
15
+ keys.forEach((key) => {
16
+ if (!(key in result)) {
17
+ result[key] = search[key]
18
+ }
19
+ })
20
+ return result
21
+ }
22
+ }
23
+
24
+ export function stripSearchParams<
25
+ TSearchSchema,
26
+ TOptionalProps = PickOptional<NoInfer<TSearchSchema>>,
27
+ const TValues =
28
+ | Partial<NoInfer<TOptionalProps>>
29
+ | Array<keyof TOptionalProps>,
30
+ const TInput = IsRequiredParams<TSearchSchema> extends never
31
+ ? TValues | true
32
+ : TValues,
33
+ >(input: NoInfer<TInput>): SearchMiddleware<TSearchSchema> {
34
+ return ({ search, next }) => {
35
+ if (input === true) {
36
+ return {}
37
+ }
38
+ const result = next(search) as Record<string, unknown>
39
+ if (Array.isArray(input)) {
40
+ input.forEach((key) => {
41
+ delete result[key]
42
+ })
43
+ } else {
44
+ Object.entries(input as Record<string, unknown>).forEach(
45
+ ([key, value]) => {
46
+ if (deepEqual(result[key], value)) {
47
+ delete result[key]
48
+ }
49
+ },
50
+ )
51
+ }
52
+ return result as any
53
+ }
54
+ }
@@ -1,19 +1,22 @@
1
1
  import { decode, encode } from './qss'
2
- import { AnySearchSchema } from './routeConfig'
2
+ import type { AnySchema } from './validators'
3
3
 
4
4
  export const defaultParseSearch = parseSearchWith(JSON.parse)
5
- export const defaultStringifySearch = stringifySearchWith(JSON.stringify)
5
+ export const defaultStringifySearch = stringifySearchWith(
6
+ JSON.stringify,
7
+ JSON.parse,
8
+ )
6
9
 
7
10
  export function parseSearchWith(parser: (str: string) => any) {
8
- return (searchStr: string): AnySearchSchema => {
11
+ return (searchStr: string): AnySchema => {
9
12
  if (searchStr.substring(0, 1) === '?') {
10
13
  searchStr = searchStr.substring(1)
11
14
  }
12
15
 
13
- let query: Record<string, unknown> = decode(searchStr)
16
+ const query: Record<string, unknown> = decode(searchStr)
14
17
 
15
18
  // Try to parse any query params that might be json
16
- for (let key in query) {
19
+ for (const key in query) {
17
20
  const value = query[key]
18
21
  if (typeof value === 'string') {
19
22
  try {
@@ -28,27 +31,47 @@ export function parseSearchWith(parser: (str: string) => any) {
28
31
  }
29
32
  }
30
33
 
31
- export function stringifySearchWith(stringify: (search: any) => string) {
34
+ export function stringifySearchWith(
35
+ stringify: (search: any) => string,
36
+ parser?: (str: string) => any,
37
+ ) {
38
+ function stringifyValue(val: any) {
39
+ if (typeof val === 'object' && val !== null) {
40
+ try {
41
+ return stringify(val)
42
+ } catch (err) {
43
+ // silent
44
+ }
45
+ } else if (typeof val === 'string' && typeof parser === 'function') {
46
+ try {
47
+ // Check if it's a valid parseable string.
48
+ // If it is, then stringify it again.
49
+ parser(val)
50
+ return stringify(val)
51
+ } catch (err) {
52
+ // silent
53
+ }
54
+ }
55
+ return val
56
+ }
57
+
32
58
  return (search: Record<string, any>) => {
33
59
  search = { ...search }
34
60
 
35
- if (search) {
36
- Object.keys(search).forEach((key) => {
37
- const val = search[key]
38
- if (typeof val === 'undefined' || val === undefined) {
39
- delete search[key]
40
- } else if (val && typeof val === 'object' && val !== null) {
41
- try {
42
- search[key] = stringify(val)
43
- } catch (err) {
44
- // silent
45
- }
46
- }
47
- })
48
- }
61
+ Object.keys(search).forEach((key) => {
62
+ const val = search[key]
63
+ if (typeof val === 'undefined' || val === undefined) {
64
+ delete search[key]
65
+ } else {
66
+ search[key] = stringifyValue(val)
67
+ }
68
+ })
49
69
 
50
70
  const searchStr = encode(search as Record<string, string>).toString()
51
71
 
52
72
  return searchStr ? `?${searchStr}` : ''
53
73
  }
54
74
  }
75
+
76
+ export type SearchSerializer = (searchObj: Record<string, any>) => string
77
+ export type SearchParser = (searchStr: string) => Record<string, any>
@@ -0,0 +1,24 @@
1
+ export interface StartSerializer {
2
+ stringify: (obj: unknown) => string
3
+ parse: (str: string) => unknown
4
+ encode: <T>(value: T) => T
5
+ decode: <T>(value: T) => T
6
+ }
7
+
8
+ export type SerializerStringifyBy<T, TSerializable> = T extends TSerializable
9
+ ? T
10
+ : T extends (...args: Array<any>) => any
11
+ ? 'Function is not serializable'
12
+ : { [K in keyof T]: SerializerStringifyBy<T[K], TSerializable> }
13
+
14
+ export type SerializerParseBy<T, TSerializable> = T extends TSerializable
15
+ ? T
16
+ : T extends React.JSX.Element
17
+ ? ReadableStream
18
+ : { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }
19
+
20
+ export type Serializable = Date | undefined | Error | FormData
21
+
22
+ export type SerializerStringify<T> = SerializerStringifyBy<T, Serializable>
23
+
24
+ export type SerializerParse<T> = SerializerParseBy<T, Serializable>
@@ -0,0 +1,7 @@
1
+ import type { Constrain } from './utils'
2
+
3
+ export interface OptionalStructuralSharing<TStructuralSharing, TConstraint> {
4
+ readonly structuralSharing?:
5
+ | Constrain<TStructuralSharing, TConstraint>
6
+ | undefined
7
+ }
package/src/utils.ts CHANGED
@@ -1,50 +1,81 @@
1
1
  export type NoInfer<T> = [T][T extends any ? 0 : never]
2
- export type IsAny<T, Y, N> = 1 extends 0 & T ? Y : N
3
- export type IsAnyBoolean<T> = 1 extends 0 & T ? true : false
4
- export type IsKnown<T, Y, N> = unknown extends T ? N : Y
5
- export type PickAsRequired<T, K extends keyof T> = Omit<T, K> &
6
- Required<Pick<T, K>>
7
- export type PickAsPartial<T, K extends keyof T> = Omit<T, K> &
8
- Partial<Pick<T, K>>
9
- export type PickUnsafe<T, K> = K extends keyof T ? Pick<T, K> : never
10
- export type PickExtra<T, K> = Expand<{
11
- [TKey in keyof K as string extends TKey
12
- ? never
13
- : TKey extends keyof T
14
- ? never
15
- : TKey]: K[TKey]
16
- }>
2
+ export type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue
3
+ ? TYesResult
4
+ : TNoResult
5
+
6
+ export type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<
7
+ TValue,
8
+ TKey
9
+ > &
10
+ Required<Pick<TValue, TKey>>
11
+
17
12
  export type PickRequired<T> = {
18
13
  [K in keyof T as undefined extends T[K] ? never : K]: T[K]
19
14
  }
20
15
 
16
+ export type PickOptional<T> = {
17
+ [K in keyof T as undefined extends T[K] ? K : never]: T[K]
18
+ }
19
+
20
+ // from https://stackoverflow.com/a/76458160
21
+ export type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never
22
+
21
23
  export type Expand<T> = T extends object
22
24
  ? T extends infer O
23
- ? { [K in keyof O]: O[K] }
25
+ ? O extends Function
26
+ ? O
27
+ : { [K in keyof O]: O[K] }
24
28
  : never
25
29
  : T
26
30
 
27
- // type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
28
- // k: infer I,
29
- // ) => any
30
- // ? I
31
- // : never
31
+ export type DeepPartial<T> = T extends object
32
+ ? {
33
+ [P in keyof T]?: DeepPartial<T[P]>
34
+ }
35
+ : T
36
+
37
+ export type MakeDifferenceOptional<TLeft, TRight> = Omit<
38
+ TRight,
39
+ keyof TLeft
40
+ > & {
41
+ [K in keyof TLeft & keyof TRight]?: TRight[K]
42
+ }
32
43
 
33
- export type Values<O> = O[ValueKeys<O>]
34
- export type ValueKeys<O> = Extract<keyof O, PropertyKey>
44
+ // from https://stackoverflow.com/a/53955431
45
+ // eslint-disable-next-line @typescript-eslint/naming-convention
46
+ export type IsUnion<T, U extends T = T> = (
47
+ T extends any ? (U extends T ? false : true) : never
48
+ ) extends false
49
+ ? false
50
+ : true
35
51
 
36
- export type DeepAwaited<T> = T extends Promise<infer A>
37
- ? DeepAwaited<A>
38
- : T extends Record<infer A, Promise<infer B>>
39
- ? { [K in A]: DeepAwaited<B> }
40
- : T
52
+ export type IsNonEmptyObject<T> = T extends object
53
+ ? keyof T extends never
54
+ ? false
55
+ : true
56
+ : false
41
57
 
42
- export type PathParamMask<TRoutePath extends string> =
43
- TRoutePath extends `${infer L}/:${infer C}/${infer R}`
44
- ? PathParamMask<`${L}/${string}/${R}`>
45
- : TRoutePath extends `${infer L}/:${infer C}`
46
- ? PathParamMask<`${L}/${string}`>
47
- : TRoutePath
58
+ export type Assign<TLeft, TRight> = TLeft extends any
59
+ ? TRight extends any
60
+ ? IsNonEmptyObject<TLeft> extends false
61
+ ? TRight
62
+ : IsNonEmptyObject<TRight> extends false
63
+ ? TLeft
64
+ : keyof TLeft & keyof TRight extends never
65
+ ? TLeft & TRight
66
+ : Omit<TLeft, keyof TRight> & TRight
67
+ : never
68
+ : never
69
+
70
+ export type IntersectAssign<TLeft, TRight> = TLeft extends any
71
+ ? TRight extends any
72
+ ? IsNonEmptyObject<TLeft> extends false
73
+ ? TRight
74
+ : IsNonEmptyObject<TRight> extends false
75
+ ? TLeft
76
+ : TRight & TLeft
77
+ : never
78
+ : never
48
79
 
49
80
  export type Timeout = ReturnType<typeof setTimeout>
50
81
 
@@ -52,50 +83,165 @@ export type Updater<TPrevious, TResult = TPrevious> =
52
83
  | TResult
53
84
  | ((prev?: TPrevious) => TResult)
54
85
 
55
- export type PickExtract<T, U> = {
56
- [K in keyof T as T[K] extends U ? K : never]: T[K]
86
+ export type NonNullableUpdater<TPrevious, TResult = TPrevious> =
87
+ | TResult
88
+ | ((prev: TPrevious) => TResult)
89
+
90
+ export type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive
91
+ ? never
92
+ : TUnion
93
+
94
+ export type PartialMergeAllObject<TUnion> =
95
+ ExtractObjects<TUnion> extends infer TObj
96
+ ? {
97
+ [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any
98
+ ? TKey extends keyof TObj
99
+ ? TObj[TKey]
100
+ : never
101
+ : never
102
+ }
103
+ : never
104
+
105
+ export type MergeAllPrimitive =
106
+ | ReadonlyArray<any>
107
+ | number
108
+ | string
109
+ | bigint
110
+ | boolean
111
+ | symbol
112
+ | undefined
113
+ | null
114
+
115
+ export type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive
116
+ ? TUnion
117
+ : TUnion extends object
118
+ ? never
119
+ : TUnion
120
+
121
+ export type PartialMergeAll<TUnion> =
122
+ | ExtractPrimitives<TUnion>
123
+ | PartialMergeAllObject<TUnion>
124
+
125
+ export type Constrain<T, TConstraint, TDefault = TConstraint> =
126
+ | (T extends TConstraint ? T : never)
127
+ | TDefault
128
+
129
+ export type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =
130
+ | (T & TConstraint)
131
+ | TDefault
132
+
133
+ /**
134
+ * To be added to router types
135
+ */
136
+ export type UnionToIntersection<T> = (
137
+ T extends any ? (arg: T) => any : never
138
+ ) extends (arg: infer T) => any
139
+ ? T
140
+ : never
141
+
142
+ /**
143
+ * Merges everything in a union into one object.
144
+ * This mapped type is homomorphic which means it preserves stuff! :)
145
+ */
146
+ export type MergeAllObjects<
147
+ TUnion,
148
+ TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,
149
+ > = [keyof TIntersected] extends [never]
150
+ ? never
151
+ : {
152
+ [TKey in keyof TIntersected]: TUnion extends any
153
+ ? TUnion[TKey & keyof TUnion]
154
+ : never
155
+ }
156
+
157
+ export type MergeAll<TUnion> =
158
+ | MergeAllObjects<TUnion>
159
+ | ExtractPrimitives<TUnion>
160
+
161
+ export type ValidateJSON<T> = ((...args: Array<any>) => any) extends T
162
+ ? unknown extends T
163
+ ? never
164
+ : 'Function is not serializable'
165
+ : { [K in keyof T]: ValidateJSON<T[K]> }
166
+
167
+ export function last<T>(arr: Array<T>) {
168
+ return arr[arr.length - 1]
169
+ }
170
+
171
+ function isFunction(d: any): d is Function {
172
+ return typeof d === 'function'
173
+ }
174
+
175
+ export function functionalUpdate<TPrevious, TResult = TPrevious>(
176
+ updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,
177
+ previous: TPrevious,
178
+ ): TResult {
179
+ if (isFunction(updater)) {
180
+ return updater(previous)
181
+ }
182
+
183
+ return updater
57
184
  }
58
185
 
59
- export type PickExclude<T, U> = {
60
- [K in keyof T as T[K] extends U ? never : K]: T[K]
186
+ export function pick<TValue, TKey extends keyof TValue>(
187
+ parent: TValue,
188
+ keys: Array<TKey>,
189
+ ): Pick<TValue, TKey> {
190
+ return keys.reduce((obj: any, key: TKey) => {
191
+ obj[key] = parent[key]
192
+ return obj
193
+ }, {} as any)
61
194
  }
62
195
 
63
196
  /**
64
- * This function returns `a` if `b` is deeply equal.
197
+ * This function returns `prev` if `_next` is deeply equal.
65
198
  * If not, it will replace any deeply equal children of `b` with those of `a`.
66
- * This can be used for structural sharing between JSON values for example.
199
+ * This can be used for structural sharing between immutable JSON values for example.
200
+ * Do not use this with signals
67
201
  */
68
- export function replaceEqualDeep(prev: any, next: any) {
69
- if (prev === next) {
202
+ export function replaceEqualDeep<T>(prev: any, _next: T): T {
203
+ if (prev === _next) {
70
204
  return prev
71
205
  }
72
206
 
73
- const array = Array.isArray(prev) && Array.isArray(next)
207
+ const next = _next as any
208
+
209
+ const array = isPlainArray(prev) && isPlainArray(next)
74
210
 
75
211
  if (array || (isPlainObject(prev) && isPlainObject(next))) {
76
- const aSize = array ? prev.length : Object.keys(prev).length
77
- const bItems = array ? next : Object.keys(next)
78
- const bSize = bItems.length
212
+ const prevItems = array ? prev : Object.keys(prev)
213
+ const prevSize = prevItems.length
214
+ const nextItems = array ? next : Object.keys(next)
215
+ const nextSize = nextItems.length
79
216
  const copy: any = array ? [] : {}
80
217
 
81
218
  let equalItems = 0
82
219
 
83
- for (let i = 0; i < bSize; i++) {
84
- const key = array ? i : bItems[i]
85
- copy[key] = replaceEqualDeep(prev[key], next[key])
86
- if (copy[key] === prev[key]) {
220
+ for (let i = 0; i < nextSize; i++) {
221
+ const key = array ? i : (nextItems[i] as any)
222
+ if (
223
+ ((!array && prevItems.includes(key)) || array) &&
224
+ prev[key] === undefined &&
225
+ next[key] === undefined
226
+ ) {
227
+ copy[key] = undefined
87
228
  equalItems++
229
+ } else {
230
+ copy[key] = replaceEqualDeep(prev[key], next[key])
231
+ if (copy[key] === prev[key] && prev[key] !== undefined) {
232
+ equalItems++
233
+ }
88
234
  }
89
235
  }
90
236
 
91
- return aSize === bSize && equalItems === aSize ? prev : copy
237
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy
92
238
  }
93
239
 
94
240
  return next
95
241
  }
96
242
 
97
243
  // Copied from: https://github.com/jonschlinkert/is-plain-object
98
- function isPlainObject(o: any) {
244
+ export function isPlainObject(o: any) {
99
245
  if (!hasObjectPrototype(o)) {
100
246
  return false
101
247
  }
@@ -125,40 +271,133 @@ function hasObjectPrototype(o: any) {
125
271
  return Object.prototype.toString.call(o) === '[object Object]'
126
272
  }
127
273
 
128
- export function last<T>(arr: T[]) {
129
- return arr[arr.length - 1]
274
+ export function isPlainArray(value: unknown): value is Array<unknown> {
275
+ return Array.isArray(value) && value.length === Object.keys(value).length
130
276
  }
131
277
 
132
- export function warning(cond: any, message: string): cond is true {
133
- if (cond) {
134
- if (typeof console !== 'undefined') console.warn(message)
278
+ function getObjectKeys(obj: any, ignoreUndefined: boolean) {
279
+ let keys = Object.keys(obj)
280
+ if (ignoreUndefined) {
281
+ keys = keys.filter((key) => obj[key] !== undefined)
282
+ }
283
+ return keys
284
+ }
135
285
 
136
- try {
137
- throw new Error(message)
138
- } catch {}
286
+ export function deepEqual(
287
+ a: any,
288
+ b: any,
289
+ opts?: { partial?: boolean; ignoreUndefined?: boolean },
290
+ ): boolean {
291
+ if (a === b) {
292
+ return true
139
293
  }
140
294
 
141
- return true
295
+ if (typeof a !== typeof b) {
296
+ return false
297
+ }
298
+
299
+ if (isPlainObject(a) && isPlainObject(b)) {
300
+ const ignoreUndefined = opts?.ignoreUndefined ?? true
301
+ const aKeys = getObjectKeys(a, ignoreUndefined)
302
+ const bKeys = getObjectKeys(b, ignoreUndefined)
303
+
304
+ if (!opts?.partial && aKeys.length !== bKeys.length) {
305
+ return false
306
+ }
307
+
308
+ return bKeys.every((key) => deepEqual(a[key], b[key], opts))
309
+ }
310
+
311
+ if (Array.isArray(a) && Array.isArray(b)) {
312
+ if (a.length !== b.length) {
313
+ return false
314
+ }
315
+ return !a.some((item, index) => !deepEqual(item, b[index], opts))
316
+ }
317
+
318
+ return false
142
319
  }
143
320
 
144
- function isFunction(d: any): d is Function {
145
- return typeof d === 'function'
321
+ export type StringLiteral<T> = T extends string
322
+ ? string extends T
323
+ ? string
324
+ : T
325
+ : never
326
+
327
+ export type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true
328
+ ? T
329
+ : T | undefined
330
+
331
+ export type ControlledPromise<T> = Promise<T> & {
332
+ resolve: (value: T) => void
333
+ reject: (value: any) => void
334
+ status: 'pending' | 'resolved' | 'rejected'
335
+ value?: T
146
336
  }
147
337
 
148
- export function functionalUpdate<TResult>(
149
- updater: Updater<TResult>,
150
- previous: TResult,
151
- ) {
152
- if (isFunction(updater)) {
153
- return updater(previous as TResult)
338
+ export function createControlledPromise<T>(onResolve?: (value: T) => void) {
339
+ let resolveLoadPromise!: (value: T) => void
340
+ let rejectLoadPromise!: (value: any) => void
341
+
342
+ const controlledPromise = new Promise<T>((resolve, reject) => {
343
+ resolveLoadPromise = resolve
344
+ rejectLoadPromise = reject
345
+ }) as ControlledPromise<T>
346
+
347
+ controlledPromise.status = 'pending'
348
+
349
+ controlledPromise.resolve = (value: T) => {
350
+ controlledPromise.status = 'resolved'
351
+ controlledPromise.value = value
352
+ resolveLoadPromise(value)
353
+ onResolve?.(value)
154
354
  }
155
355
 
156
- return updater
356
+ controlledPromise.reject = (e) => {
357
+ controlledPromise.status = 'rejected'
358
+ rejectLoadPromise(e)
359
+ }
360
+
361
+ return controlledPromise
157
362
  }
158
363
 
159
- export function pick<T, K extends keyof T>(parent: T, keys: K[]): Pick<T, K> {
160
- return keys.reduce((obj: any, key: K) => {
161
- obj[key] = parent[key]
162
- return obj
163
- }, {} as any)
364
+ /**
365
+ *
366
+ * @deprecated use `jsesc` instead
367
+ */
368
+ export function escapeJSON(jsonString: string) {
369
+ return jsonString
370
+ .replace(/\\/g, '\\\\') // Escape backslashes
371
+ .replace(/'/g, "\\'") // Escape single quotes
372
+ .replace(/"/g, '\\"') // Escape double quotes
373
+ }
374
+
375
+ export function shallow<T>(objA: T, objB: T) {
376
+ if (Object.is(objA, objB)) {
377
+ return true
378
+ }
379
+
380
+ if (
381
+ typeof objA !== 'object' ||
382
+ objA === null ||
383
+ typeof objB !== 'object' ||
384
+ objB === null
385
+ ) {
386
+ return false
387
+ }
388
+
389
+ const keysA = Object.keys(objA)
390
+ if (keysA.length !== Object.keys(objB).length) {
391
+ return false
392
+ }
393
+
394
+ for (const item of keysA) {
395
+ if (
396
+ !Object.prototype.hasOwnProperty.call(objB, item) ||
397
+ !Object.is(objA[item as keyof T], objB[item as keyof T])
398
+ ) {
399
+ return false
400
+ }
401
+ }
402
+ return true
164
403
  }