@tanstack/react-router 1.28.7 → 1.28.9
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/RouterProvider.cjs +55 -59
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +4 -0
- package/dist/cjs/link.cjs +22 -7
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +3 -0
- package/dist/cjs/router.cjs +42 -44
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +5 -2
- package/dist/esm/RouterProvider.d.ts +4 -0
- package/dist/esm/RouterProvider.js +55 -59
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/link.d.ts +3 -0
- package/dist/esm/link.js +22 -7
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/router.d.ts +5 -2
- package/dist/esm/router.js +42 -44
- package/dist/esm/router.js.map +1 -1
- package/package.json +2 -2
- package/src/RouterProvider.tsx +82 -71
- package/src/link.tsx +30 -8
- package/src/router.ts +70 -56
package/src/RouterProvider.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { flushSync } from 'react-dom'
|
|
2
3
|
import { Matches } from './Matches'
|
|
3
4
|
import { pick, useLayoutEffect } from './utils'
|
|
4
5
|
import { useRouter } from './useRouter'
|
|
@@ -17,19 +18,13 @@ import type {
|
|
|
17
18
|
|
|
18
19
|
import type { RouteMatch } from './Matches'
|
|
19
20
|
|
|
20
|
-
const useTransition =
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
22
|
-
React.useTransition ||
|
|
23
|
-
(() => [
|
|
24
|
-
false,
|
|
25
|
-
(cb) => {
|
|
26
|
-
cb()
|
|
27
|
-
},
|
|
28
|
-
])
|
|
29
|
-
|
|
30
21
|
export interface CommitLocationOptions {
|
|
31
22
|
replace?: boolean
|
|
32
23
|
resetScroll?: boolean
|
|
24
|
+
viewTransition?: boolean
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated All navigations use React transitions under the hood now
|
|
27
|
+
**/
|
|
33
28
|
startTransition?: boolean
|
|
34
29
|
}
|
|
35
30
|
|
|
@@ -111,37 +106,30 @@ function Transitioner() {
|
|
|
111
106
|
pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
|
|
112
107
|
})
|
|
113
108
|
|
|
114
|
-
const [isTransitioning,
|
|
109
|
+
const [isTransitioning, startReactTransition_] = React.useTransition()
|
|
110
|
+
// Track pending state changes
|
|
111
|
+
const hasPendingMatches = useRouterState({
|
|
112
|
+
select: (s) => s.matches.some((d) => d.status === 'pending'),
|
|
113
|
+
})
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
const previousIsLoading = usePrevious(routerState.isLoading)
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}))
|
|
124
|
-
}
|
|
125
|
-
}, [isTransitioning, router])
|
|
117
|
+
const isAnyPending =
|
|
118
|
+
routerState.isLoading || isTransitioning || hasPendingMatches
|
|
119
|
+
const previousIsAnyPending = usePrevious(isAnyPending)
|
|
120
|
+
|
|
121
|
+
router.startReactTransition = startReactTransition_
|
|
126
122
|
|
|
127
123
|
const tryLoad = () => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
cb()
|
|
133
|
-
}
|
|
124
|
+
try {
|
|
125
|
+
router.load()
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(err)
|
|
134
128
|
}
|
|
135
|
-
|
|
136
|
-
apply(() => {
|
|
137
|
-
try {
|
|
138
|
-
router.load()
|
|
139
|
-
} catch (err) {
|
|
140
|
-
console.error(err)
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
129
|
}
|
|
144
130
|
|
|
131
|
+
// Subscribe to location changes
|
|
132
|
+
// and try to load the new location
|
|
145
133
|
useLayoutEffect(() => {
|
|
146
134
|
const unsub = router.history.subscribe(() => {
|
|
147
135
|
router.latestLocation = router.parseLocation(router.latestLocation)
|
|
@@ -168,57 +156,72 @@ function Transitioner() {
|
|
|
168
156
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
169
157
|
}, [router, router.history])
|
|
170
158
|
|
|
159
|
+
// Try to load the initial location
|
|
171
160
|
useLayoutEffect(() => {
|
|
172
161
|
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
routerState.resolvedLocation !== routerState.location
|
|
162
|
+
window.__TSR_DEHYDRATED__ ||
|
|
163
|
+
(mountLoadForRouter.current.router === router &&
|
|
164
|
+
mountLoadForRouter.current.mounted)
|
|
177
165
|
) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
mountLoadForRouter.current = { router, mounted: true }
|
|
169
|
+
tryLoad()
|
|
170
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
171
|
+
}, [router])
|
|
172
|
+
|
|
173
|
+
useLayoutEffect(() => {
|
|
174
|
+
// The router was loading and now it's not
|
|
175
|
+
if (previousIsLoading && !routerState.isLoading) {
|
|
176
|
+
const toLocation = router.state.location
|
|
177
|
+
const fromLocation = router.state.resolvedLocation
|
|
178
|
+
const pathChanged = fromLocation.href !== toLocation.href
|
|
179
|
+
|
|
180
|
+
router.emit({
|
|
181
|
+
type: 'onLoad',
|
|
182
|
+
fromLocation,
|
|
183
|
+
toLocation,
|
|
184
|
+
pathChanged,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// if (router.viewTransitionPromise) {
|
|
188
|
+
// console.log('resolving view transition promise')
|
|
189
|
+
// }
|
|
190
|
+
|
|
191
|
+
// router.viewTransitionPromise?.resolve(true)
|
|
192
|
+
}
|
|
193
|
+
}, [previousIsLoading, router, routerState.isLoading])
|
|
194
|
+
|
|
195
|
+
useLayoutEffect(() => {
|
|
196
|
+
// The router was pending and now it's not
|
|
197
|
+
if (previousIsAnyPending && !isAnyPending) {
|
|
198
|
+
const toLocation = router.state.location
|
|
199
|
+
const fromLocation = router.state.resolvedLocation
|
|
200
|
+
const pathChanged = fromLocation.href !== toLocation.href
|
|
201
|
+
|
|
178
202
|
router.emit({
|
|
179
203
|
type: 'onResolved',
|
|
180
|
-
fromLocation
|
|
181
|
-
toLocation
|
|
182
|
-
pathChanged
|
|
183
|
-
routerState.location.href !== routerState.resolvedLocation.href,
|
|
204
|
+
fromLocation,
|
|
205
|
+
toLocation,
|
|
206
|
+
pathChanged,
|
|
184
207
|
})
|
|
185
208
|
|
|
209
|
+
router.__store.setState((s) => ({
|
|
210
|
+
...s,
|
|
211
|
+
status: 'idle',
|
|
212
|
+
resolvedLocation: s.location,
|
|
213
|
+
}))
|
|
214
|
+
|
|
186
215
|
if ((document as any).querySelector) {
|
|
187
|
-
if (
|
|
188
|
-
const el = document.getElementById(
|
|
216
|
+
if (router.state.location.hash !== '') {
|
|
217
|
+
const el = document.getElementById(router.state.location.hash)
|
|
189
218
|
if (el) {
|
|
190
219
|
el.scrollIntoView()
|
|
191
220
|
}
|
|
192
221
|
}
|
|
193
222
|
}
|
|
194
|
-
|
|
195
|
-
router.__store.setState((s) => ({
|
|
196
|
-
...s,
|
|
197
|
-
isTransitioning: false,
|
|
198
|
-
resolvedLocation: s.location,
|
|
199
|
-
}))
|
|
200
|
-
}
|
|
201
|
-
}, [
|
|
202
|
-
routerState.isTransitioning,
|
|
203
|
-
isTransitioning,
|
|
204
|
-
routerState.isLoading,
|
|
205
|
-
routerState.resolvedLocation,
|
|
206
|
-
routerState.location,
|
|
207
|
-
router,
|
|
208
|
-
])
|
|
209
|
-
|
|
210
|
-
useLayoutEffect(() => {
|
|
211
|
-
if (
|
|
212
|
-
window.__TSR_DEHYDRATED__ ||
|
|
213
|
-
(mountLoadForRouter.current.router === router &&
|
|
214
|
-
mountLoadForRouter.current.mounted)
|
|
215
|
-
) {
|
|
216
|
-
return
|
|
217
223
|
}
|
|
218
|
-
|
|
219
|
-
tryLoad()
|
|
220
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
221
|
-
}, [router])
|
|
224
|
+
}, [isAnyPending, previousIsAnyPending, router])
|
|
222
225
|
|
|
223
226
|
return null
|
|
224
227
|
}
|
|
@@ -241,3 +244,11 @@ export type RouterProps<
|
|
|
241
244
|
router: Router<TRouteTree>
|
|
242
245
|
context?: Partial<RouterOptions<TRouteTree, TDehydrated>['context']>
|
|
243
246
|
}
|
|
247
|
+
|
|
248
|
+
function usePrevious<T>(value: T) {
|
|
249
|
+
const ref = React.useRef<T>(value)
|
|
250
|
+
React.useEffect(() => {
|
|
251
|
+
ref.current = value
|
|
252
|
+
})
|
|
253
|
+
return ref.current
|
|
254
|
+
}
|
package/src/link.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { flushSync } from 'react-dom'
|
|
2
3
|
import { useMatch } from './Matches'
|
|
3
4
|
import { useRouterState } from './useRouterState'
|
|
4
5
|
import { useRouter } from './useRouter'
|
|
@@ -168,8 +169,10 @@ export type NavigateOptions<
|
|
|
168
169
|
// `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
|
|
169
170
|
replace?: boolean
|
|
170
171
|
resetScroll?: boolean
|
|
171
|
-
|
|
172
|
+
/** @deprecated All navigations now use startTransition under the hood */
|
|
172
173
|
startTransition?: boolean
|
|
174
|
+
// if set to `true`, the router will wrap the resulting navigation in a document.startViewTransition() call.
|
|
175
|
+
viewTransition?: boolean
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
export type ToOptions<
|
|
@@ -494,6 +497,7 @@ export function useLinkProps<
|
|
|
494
497
|
strict: false,
|
|
495
498
|
select: (s) => s.pathname,
|
|
496
499
|
})
|
|
500
|
+
const [isTransitioning, setIsTransitioning] = React.useState(false)
|
|
497
501
|
|
|
498
502
|
const {
|
|
499
503
|
// custom props
|
|
@@ -511,6 +515,7 @@ export function useLinkProps<
|
|
|
511
515
|
replace,
|
|
512
516
|
startTransition,
|
|
513
517
|
resetScroll,
|
|
518
|
+
viewTransition,
|
|
514
519
|
// element props
|
|
515
520
|
children,
|
|
516
521
|
target,
|
|
@@ -602,17 +607,30 @@ export function useLinkProps<
|
|
|
602
607
|
) {
|
|
603
608
|
e.preventDefault()
|
|
604
609
|
|
|
610
|
+
flushSync(() => {
|
|
611
|
+
setIsTransitioning(true)
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
const unsub = router.subscribe('onResolved', () => {
|
|
615
|
+
unsub()
|
|
616
|
+
setIsTransitioning(false)
|
|
617
|
+
})
|
|
618
|
+
|
|
605
619
|
// All is well? Navigate!
|
|
606
|
-
router.commitLocation({
|
|
620
|
+
router.commitLocation({
|
|
621
|
+
...next,
|
|
622
|
+
replace,
|
|
623
|
+
resetScroll,
|
|
624
|
+
startTransition,
|
|
625
|
+
viewTransition,
|
|
626
|
+
})
|
|
607
627
|
}
|
|
608
628
|
}
|
|
609
629
|
|
|
610
630
|
const doPreload = () => {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
console.warn(preloadWarning)
|
|
615
|
-
})
|
|
631
|
+
router.preloadRoute(dest as any).catch((err) => {
|
|
632
|
+
console.warn(err)
|
|
633
|
+
console.warn(preloadWarning)
|
|
616
634
|
})
|
|
617
635
|
}
|
|
618
636
|
|
|
@@ -707,6 +725,7 @@ export function useLinkProps<
|
|
|
707
725
|
'aria-disabled': true,
|
|
708
726
|
}),
|
|
709
727
|
...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),
|
|
728
|
+
...(isTransitioning && { 'data-transitioning': 'transitioning' }),
|
|
710
729
|
}
|
|
711
730
|
}
|
|
712
731
|
|
|
@@ -746,7 +765,10 @@ export type LinkProps<
|
|
|
746
765
|
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
747
766
|
children?:
|
|
748
767
|
| React.ReactNode
|
|
749
|
-
| ((state: {
|
|
768
|
+
| ((state: {
|
|
769
|
+
isActive: boolean
|
|
770
|
+
isTransitioning: boolean
|
|
771
|
+
}) => React.ReactNode)
|
|
750
772
|
}
|
|
751
773
|
|
|
752
774
|
type LinkComponentProps<TComp> = React.PropsWithoutRef<
|
package/src/router.ts
CHANGED
|
@@ -53,6 +53,7 @@ import type {
|
|
|
53
53
|
RoutesByPath,
|
|
54
54
|
} from './routeInfo'
|
|
55
55
|
import type {
|
|
56
|
+
ControlledPromise,
|
|
56
57
|
NonNullableUpdater,
|
|
57
58
|
PickAsRequired,
|
|
58
59
|
Timeout,
|
|
@@ -129,6 +130,7 @@ export interface RouterOptions<
|
|
|
129
130
|
defaultStaleTime?: number
|
|
130
131
|
defaultPreloadStaleTime?: number
|
|
131
132
|
defaultPreloadGcTime?: number
|
|
133
|
+
defaultViewTransition?: boolean
|
|
132
134
|
notFoundMode?: 'root' | 'fuzzy'
|
|
133
135
|
defaultGcTime?: number
|
|
134
136
|
caseSensitive?: boolean
|
|
@@ -270,11 +272,13 @@ export class Router<
|
|
|
270
272
|
Math.random() * 10000000,
|
|
271
273
|
)}`
|
|
272
274
|
resetNextScroll = true
|
|
275
|
+
shouldViewTransition?: true = undefined
|
|
273
276
|
navigateTimeout: Timeout | null = null
|
|
274
277
|
latestLoadPromise: Promise<void> = Promise.resolve()
|
|
275
278
|
subscribers = new Set<RouterListener<RouterEvent>>()
|
|
276
279
|
injectedHtml: Array<InjectedHtmlEntry> = []
|
|
277
280
|
dehydratedData?: TDehydrated
|
|
281
|
+
viewTransitionPromise?: ControlledPromise<true>
|
|
278
282
|
|
|
279
283
|
// Must build in constructor
|
|
280
284
|
__store!: Store<RouterState<TRouteTree>>
|
|
@@ -388,10 +392,6 @@ export class Router<
|
|
|
388
392
|
onUpdate: () => {
|
|
389
393
|
this.__store.state = {
|
|
390
394
|
...this.state,
|
|
391
|
-
status:
|
|
392
|
-
this.state.isTransitioning || this.state.isLoading
|
|
393
|
-
? 'pending'
|
|
394
|
-
: 'idle',
|
|
395
395
|
cachedMatches: this.state.cachedMatches.filter(
|
|
396
396
|
(d) => !['redirected'].includes(d.status),
|
|
397
397
|
),
|
|
@@ -1063,6 +1063,7 @@ export class Router<
|
|
|
1063
1063
|
|
|
1064
1064
|
commitLocation = async ({
|
|
1065
1065
|
startTransition,
|
|
1066
|
+
viewTransition,
|
|
1066
1067
|
...next
|
|
1067
1068
|
}: ParsedLocation & CommitLocationOptions) => {
|
|
1068
1069
|
if (this.navigateTimeout) clearTimeout(this.navigateTimeout)
|
|
@@ -1103,18 +1104,14 @@ export class Router<
|
|
|
1103
1104
|
}
|
|
1104
1105
|
}
|
|
1105
1106
|
|
|
1106
|
-
|
|
1107
|
-
this.
|
|
1108
|
-
nextHistory.href,
|
|
1109
|
-
nextHistory.state,
|
|
1110
|
-
)
|
|
1107
|
+
if (viewTransition) {
|
|
1108
|
+
this.shouldViewTransition = true
|
|
1111
1109
|
}
|
|
1112
1110
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}
|
|
1111
|
+
this.history[next.replace ? 'replace' : 'push'](
|
|
1112
|
+
nextHistory.href,
|
|
1113
|
+
nextHistory.state,
|
|
1114
|
+
)
|
|
1118
1115
|
}
|
|
1119
1116
|
|
|
1120
1117
|
this.resetNextScroll = next.resetScroll ?? true
|
|
@@ -1638,7 +1635,8 @@ export class Router<
|
|
|
1638
1635
|
this.latestLoadPromise = promise
|
|
1639
1636
|
|
|
1640
1637
|
let latestPromise: Promise<void> | undefined | null
|
|
1641
|
-
|
|
1638
|
+
|
|
1639
|
+
this.startReactTransition(async () => {
|
|
1642
1640
|
try {
|
|
1643
1641
|
const next = this.latestLocation
|
|
1644
1642
|
const prevLocation = this.state.resolvedLocation
|
|
@@ -1667,6 +1665,7 @@ export class Router<
|
|
|
1667
1665
|
// If a cached moved to pendingMatches, remove it from cachedMatches
|
|
1668
1666
|
this.__store.setState((s) => ({
|
|
1669
1667
|
...s,
|
|
1668
|
+
status: 'pending',
|
|
1670
1669
|
isLoading: true,
|
|
1671
1670
|
location: next,
|
|
1672
1671
|
pendingMatches,
|
|
@@ -1717,50 +1716,65 @@ export class Router<
|
|
|
1717
1716
|
pendingMatches.find((d) => d.id === match.id),
|
|
1718
1717
|
)
|
|
1719
1718
|
|
|
1720
|
-
//
|
|
1721
|
-
//
|
|
1722
|
-
|
|
1723
|
-
this.
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1719
|
+
// Determine if we should start a view transition from the navigation
|
|
1720
|
+
// or from the router default
|
|
1721
|
+
const shouldViewTransition =
|
|
1722
|
+
this.shouldViewTransition ?? this.options.defaultViewTransition
|
|
1723
|
+
|
|
1724
|
+
// Reset the view transition flag
|
|
1725
|
+
delete this.shouldViewTransition
|
|
1726
|
+
|
|
1727
|
+
const apply = () => {
|
|
1728
|
+
// this.viewTransitionPromise = createControlledPromise<true>()
|
|
1729
|
+
|
|
1730
|
+
// Commit the pending matches. If a previous match was
|
|
1731
|
+
// removed, place it in the cachedMatches
|
|
1732
|
+
this.__store.batch(() => {
|
|
1733
|
+
this.__store.setState((s) => ({
|
|
1734
|
+
...s,
|
|
1735
|
+
isLoading: false,
|
|
1736
|
+
matches: s.pendingMatches!,
|
|
1737
|
+
pendingMatches: undefined,
|
|
1738
|
+
cachedMatches: [
|
|
1739
|
+
...s.cachedMatches,
|
|
1740
|
+
...exitingMatches.filter((d) => d.status !== 'error'),
|
|
1741
|
+
],
|
|
1742
|
+
statusCode:
|
|
1743
|
+
redirect?.statusCode || notFound
|
|
1744
|
+
? 404
|
|
1745
|
+
: s.matches.some((d) => d.status === 'error')
|
|
1746
|
+
? 500
|
|
1747
|
+
: 200,
|
|
1748
|
+
redirect,
|
|
1749
|
+
}))
|
|
1750
|
+
this.cleanCache()
|
|
1751
|
+
})
|
|
1742
1752
|
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
+
//
|
|
1754
|
+
;(
|
|
1755
|
+
[
|
|
1756
|
+
[exitingMatches, 'onLeave'],
|
|
1757
|
+
[enteringMatches, 'onEnter'],
|
|
1758
|
+
[stayingMatches, 'onStay'],
|
|
1759
|
+
] as const
|
|
1760
|
+
).forEach(([matches, hook]) => {
|
|
1761
|
+
matches.forEach((match) => {
|
|
1762
|
+
this.looseRoutesById[match.routeId]!.options[hook]?.(match)
|
|
1763
|
+
})
|
|
1753
1764
|
})
|
|
1754
|
-
})
|
|
1755
1765
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
pathChanged: pathDidChange,
|
|
1761
|
-
})
|
|
1766
|
+
resolveLoad()
|
|
1767
|
+
|
|
1768
|
+
// return this.viewTransitionPromise
|
|
1769
|
+
}
|
|
1762
1770
|
|
|
1763
|
-
|
|
1771
|
+
// Attempt to start a view transition (or just apply the changes if we can't)
|
|
1772
|
+
;(shouldViewTransition && typeof document !== 'undefined'
|
|
1773
|
+
? document
|
|
1774
|
+
: undefined
|
|
1775
|
+
)
|
|
1776
|
+
// @ts-expect-error
|
|
1777
|
+
?.startViewTransition?.(apply) || apply()
|
|
1764
1778
|
} catch (err) {
|
|
1765
1779
|
// Only apply the latest transition
|
|
1766
1780
|
if ((latestPromise = this.checkLatest(promise))) {
|
|
@@ -1771,7 +1785,7 @@ export class Router<
|
|
|
1771
1785
|
|
|
1772
1786
|
rejectLoad(err)
|
|
1773
1787
|
}
|
|
1774
|
-
})
|
|
1788
|
+
})
|
|
1775
1789
|
|
|
1776
1790
|
return this.latestLoadPromise
|
|
1777
1791
|
}
|