navigation-stack 0.5.2 → 0.6.0
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/CHANGELOG.md +16 -0
- package/README.md +144 -282
- package/karma.conf.cjs +1 -1
- package/lib/cjs/NavigationStack.js +138 -49
- package/lib/cjs/data-storage/DataStorage.js +7 -6
- package/lib/cjs/environment/InMemoryEnvironment.js +6 -0
- package/lib/cjs/{session/ServerSideRenderSession.js → environment/ServerSideRenderEnvironment.js} +5 -6
- package/lib/cjs/environment/WebBrowserEnvironment.js +6 -0
- package/lib/cjs/environment/log/InMemoryLog.js +23 -0
- package/lib/cjs/environment/log/WebBrowserLog.js +22 -0
- package/lib/cjs/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/lib/cjs/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/lib/cjs/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/lib/cjs/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +2 -2
- package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/lib/cjs/getLocationBaseFromLocation.js +14 -0
- package/lib/cjs/getLocationUrl.js +3 -5
- package/lib/cjs/index.js +10 -16
- package/lib/cjs/navigationBlockers.js +34 -32
- package/lib/cjs/navigationBlockersEvaluation.js +150 -0
- package/lib/cjs/parseInputLocation.js +10 -3
- package/lib/cjs/parseQueryFromSearch.js +3 -6
- package/lib/cjs/parseQueryString.js +77 -0
- package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +7 -6
- package/lib/cjs/scroll-position/ScrollPositionRestoration.js +31 -27
- package/lib/cjs/scroll-position/ScrollPositionSaver.js +6 -4
- package/lib/cjs/session/Session.js +61 -26
- package/lib/cjs/session/subscription/Subscription.js +36 -18
- package/lib/cjs/stringifyQuery.js +66 -0
- package/lib/cjs/stringifyQueryAsSearch.js +14 -0
- package/lib/esm/NavigationStack.js +138 -49
- package/lib/esm/data-storage/DataStorage.js +7 -6
- package/lib/esm/environment/InMemoryEnvironment.js +6 -0
- package/lib/esm/environment/ServerSideRenderEnvironment.js +10 -0
- package/lib/esm/environment/WebBrowserEnvironment.js +6 -0
- package/lib/esm/environment/log/InMemoryLog.js +17 -0
- package/lib/esm/environment/log/WebBrowserLog.js +16 -0
- package/lib/esm/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/lib/esm/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/lib/esm/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/lib/esm/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
- package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/lib/esm/getLocationBaseFromLocation.js +9 -0
- package/lib/esm/getLocationUrl.js +2 -5
- package/lib/esm/index.js +5 -8
- package/lib/esm/navigationBlockers.js +34 -32
- package/lib/esm/navigationBlockersEvaluation.js +145 -0
- package/lib/esm/parseInputLocation.js +9 -3
- package/lib/esm/parseQueryFromSearch.js +2 -6
- package/lib/esm/parseQueryString.js +72 -0
- package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +7 -6
- package/lib/esm/scroll-position/ScrollPositionRestoration.js +31 -27
- package/lib/esm/scroll-position/ScrollPositionSaver.js +6 -4
- package/lib/esm/session/Session.js +61 -26
- package/lib/esm/session/subscription/Subscription.js +36 -18
- package/lib/esm/stringifyQuery.js +61 -0
- package/lib/esm/stringifyQueryAsSearch.js +8 -0
- package/lib/index.d.ts +180 -34
- package/package.json +4 -7
- package/src/NavigationStack.js +166 -56
- package/src/data-storage/DataStorage.js +9 -6
- package/src/environment/InMemoryEnvironment.js +6 -0
- package/src/environment/ServerSideRenderEnvironment.js +10 -0
- package/src/environment/WebBrowserEnvironment.js +6 -0
- package/src/environment/log/InMemoryLog.js +20 -0
- package/src/environment/log/WebBrowserLog.js +18 -0
- package/src/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/src/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/src/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/src/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
- package/src/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/src/getLocationBaseFromLocation.js +7 -0
- package/src/getLocationUrl.js +2 -5
- package/src/index.js +10 -13
- package/src/navigationBlockers.js +55 -34
- package/src/navigationBlockersEvaluation.js +161 -0
- package/src/parseInputLocation.js +10 -3
- package/src/parseQueryFromSearch.js +2 -6
- package/src/parseQueryString.js +81 -0
- package/src/scroll-position/ScrollPositionAutoSaver.js +10 -6
- package/src/scroll-position/ScrollPositionRestoration.js +36 -30
- package/src/scroll-position/ScrollPositionSaver.js +6 -4
- package/src/scroll-position/index.js +1 -1
- package/src/session/Session.js +68 -24
- package/src/session/subscription/Subscription.js +36 -11
- package/src/stringifyQuery.js +71 -0
- package/src/stringifyQueryAsSearch.js +9 -0
- package/test/NavigationStack.addBasePath.test.js +50 -0
- package/test/{redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockNonProgrammaticNavigationIfRequired.test.js} +51 -63
- package/test/{redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockProgrammaticNavigationIfRequired.test.js} +98 -78
- package/test/NavigationStack.general.test.js +68 -0
- package/test/NavigationStack.parseInputLocation.test.js +52 -0
- package/test/NavigationStack.removeBasePath.test.js +69 -0
- package/test/NavigationStack.test.js +97 -29
- package/test/data-storage/LocationDataStorage.test.js +3 -2
- package/test/index.js +7 -31
- package/test/index.test.js +4 -5
- package/test/parseQueryFromSearch.test.js +19 -0
- package/test/parseQueryString.test.js +18 -0
- package/test/scroll-position/ScrollPositionRestoration.test.js +34 -13
- package/test/scroll-position/createApp.js +8 -8
- package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +4 -4
- package/test/session/{InMemorySession.test.js → Session.InMemoryEnvironment.test.js} +10 -9
- package/test/session/{ServerSession.test.js → Session.ServerSideRenderEnvironment.test.js} +5 -4
- package/test/session/{WebBrowserSession.test.js → Session.WebBrowserEnvironment.test.js} +63 -13
- package/test/shouldWarn.js +44 -0
- package/test/stringifyQuery.test.js +65 -0
- package/types/index.d.ts +180 -34
- package/types/tsconfig.json +0 -1
- package/data-storage/package.json +0 -7
- package/lib/cjs/createSearchFromQuery.js +0 -13
- package/lib/cjs/debug.js +0 -12
- package/lib/cjs/redux/ActionTypes.js +0 -14
- package/lib/cjs/redux/ActionTypesInternal.js +0 -8
- package/lib/cjs/redux/Actions.js +0 -28
- package/lib/cjs/redux/createMiddlewares.js +0 -60
- package/lib/cjs/redux/index.js +0 -13
- package/lib/cjs/redux/internalLocationReducer.js +0 -14
- package/lib/cjs/redux/locationReducer.js +0 -13
- package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -32
- package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -113
- package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
- package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -30
- package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -73
- package/lib/cjs/redux/middleware/navigationOperationMiddleware.js +0 -40
- package/lib/cjs/redux/middleware/parseInputLocationMiddleware.js +0 -29
- package/lib/cjs/redux/middleware/updateLocationMiddleware.js +0 -34
- package/lib/cjs/session/InMemorySession.js +0 -22
- package/lib/cjs/session/WebBrowserSession.js +0 -20
- package/lib/data-storage/index.d.ts +0 -35
- package/lib/esm/createSearchFromQuery.js +0 -8
- package/lib/esm/debug.js +0 -7
- package/lib/esm/redux/ActionTypes.js +0 -9
- package/lib/esm/redux/ActionTypesInternal.js +0 -3
- package/lib/esm/redux/Actions.js +0 -22
- package/lib/esm/redux/createMiddlewares.js +0 -54
- package/lib/esm/redux/index.js +0 -4
- package/lib/esm/redux/internalLocationReducer.js +0 -8
- package/lib/esm/redux/locationReducer.js +0 -7
- package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
- package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -108
- package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -88
- package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -25
- package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -68
- package/lib/esm/redux/middleware/navigationOperationMiddleware.js +0 -35
- package/lib/esm/redux/middleware/parseInputLocationMiddleware.js +0 -24
- package/lib/esm/redux/middleware/updateLocationMiddleware.js +0 -28
- package/lib/esm/session/InMemorySession.js +0 -15
- package/lib/esm/session/ServerSideRenderSession.js +0 -11
- package/lib/esm/session/WebBrowserSession.js +0 -13
- package/lib/redux/index.d.ts +0 -90
- package/lib/scroll-position/index.d.ts +0 -107
- package/redux/package.json +0 -7
- package/scroll-position/package.json +0 -7
- package/src/createSearchFromQuery.js +0 -9
- package/src/debug.js +0 -8
- package/src/redux/ActionTypes.js +0 -9
- package/src/redux/ActionTypesInternal.js +0 -3
- package/src/redux/Actions.js +0 -27
- package/src/redux/createMiddlewares.js +0 -65
- package/src/redux/index.js +0 -4
- package/src/redux/internalLocationReducer.js +0 -9
- package/src/redux/locationReducer.js +0 -8
- package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
- package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -119
- package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
- package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -26
- package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -72
- package/src/redux/middleware/navigationOperationMiddleware.js +0 -34
- package/src/redux/middleware/parseInputLocationMiddleware.js +0 -23
- package/src/redux/middleware/updateLocationMiddleware.js +0 -28
- package/src/session/InMemorySession.js +0 -13
- package/src/session/ServerSideRenderSession.js +0 -9
- package/src/session/WebBrowserSession.js +0 -13
- package/test/middlewareTestUtil.js +0 -31
- package/test/redux/Action.test.js +0 -73
- package/test/redux/ActionTypes.test.js +0 -13
- package/test/redux/createMiddlewares.test.js +0 -96
- package/test/redux/index.test.js +0 -10
- package/test/redux/locationReducer.test.js +0 -39
- package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +0 -40
- package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +0 -51
- package/test/redux/middleware/navigationOperationMiddleware.test.js +0 -78
- package/test/redux/middleware/parseInputLocationMiddleware.test.js +0 -62
- package/test/testUtil.js +0 -3
- package/types/data-storage/index.d.ts +0 -35
- package/types/redux/index.d.ts +0 -90
- package/types/scroll-position/index.d.ts +0 -107
- /package/lib/cjs/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/lib/cjs/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/lib/cjs/{session → environment}/navigation/operation/operations.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/lib/esm/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/lib/esm/{session → environment}/navigation/operation/operations.js +0 -0
- /package/src/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/src/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/src/{session → environment}/navigation/operation/operations.js +0 -0
- /package/test/{parseInputLocationMiddleware.test.js → parseInputLocation.test.js} +0 -0
|
@@ -1,81 +1,170 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
1
|
+
import { addBasePath, removeBasePath } from './basePath';
|
|
2
|
+
import LocationDataStorage from './data-storage/LocationDataStorage';
|
|
3
|
+
import getLocationFromInternalLocation from './getLocationFromInternalLocation';
|
|
4
|
+
import isPromise from './isPromise';
|
|
5
|
+
import { addNavigationBlocker, removeAllNavigationBlockers } from './navigationBlockers';
|
|
6
|
+
import { blockNonProgrammaticNavigationIfRequired, blockProgrammaticNavigationIfRequired } from './navigationBlockersEvaluation';
|
|
7
|
+
import parseInputLocation from './parseInputLocation';
|
|
7
8
|
import ScrollPositionRestoration from './scroll-position/ScrollPositionRestoration';
|
|
8
|
-
|
|
9
|
-
if (!navigationStackOptions) {
|
|
10
|
-
return undefined;
|
|
11
|
-
}
|
|
12
|
-
// eslint-disable-next-line no-unused-vars
|
|
13
|
-
const restOptions = _objectWithoutPropertiesLoose(navigationStackOptions, _excluded);
|
|
14
|
-
return restOptions;
|
|
15
|
-
}
|
|
9
|
+
import Session from './session/Session';
|
|
16
10
|
export default class NavigationStack {
|
|
17
|
-
constructor(
|
|
18
|
-
|
|
11
|
+
constructor(Environment, {
|
|
12
|
+
basePath,
|
|
13
|
+
manageScrollPosition,
|
|
14
|
+
scrollPositionSetter
|
|
15
|
+
} = {}) {
|
|
16
|
+
// Allows temporarily ignoring location update events.
|
|
17
|
+
this._doAndIgnoreLocationUpdates = func => {
|
|
18
|
+
this._ignoreLocationUpdates = true;
|
|
19
|
+
func();
|
|
20
|
+
this._ignoreLocationUpdates = false;
|
|
21
|
+
};
|
|
22
|
+
// Create a session.
|
|
23
|
+
this._session = new Session(Environment);
|
|
24
|
+
|
|
25
|
+
// Base path, if used.
|
|
26
|
+
this._basePath = basePath;
|
|
27
|
+
|
|
28
|
+
// Create location data storage.
|
|
29
|
+
this.dataStorage = new LocationDataStorage(this._session, {
|
|
30
|
+
namespace: 'navigation-stack'
|
|
31
|
+
});
|
|
19
32
|
|
|
20
|
-
//
|
|
21
|
-
this.
|
|
33
|
+
// Allows temporarily ignoring location update events when set to `true`.
|
|
34
|
+
this._ignoreLocationUpdates = false;
|
|
35
|
+
|
|
36
|
+
// Subscribe to location updates.
|
|
37
|
+
// * Ignores location updates if `_ignoreLocationUpdates` flag is temporarily set.
|
|
38
|
+
// * Runs navigation blockers to see if the location update should be reverted.
|
|
39
|
+
// * Updates `this._location` if the update wasn't ignored or blocked.
|
|
40
|
+
this._unsubscribe = this._session.subscribe(location => {
|
|
41
|
+
// If this location update shouldn't be temporarily ignored.
|
|
42
|
+
if (!this._ignoreLocationUpdates) {
|
|
43
|
+
// Remove `basePath` from `location`.
|
|
44
|
+
location = removeBasePath(location, this._basePath);
|
|
45
|
+
|
|
46
|
+
// See if the location update should've been blocked.
|
|
47
|
+
// If it should've, it will automatically "rewind" it.
|
|
48
|
+
const result = blockNonProgrammaticNavigationIfRequired(location, this._session, this._doAndIgnoreLocationUpdates);
|
|
49
|
+
const onResult = blocked => {
|
|
50
|
+
if (!blocked) {
|
|
51
|
+
// Update `this._location`.
|
|
52
|
+
// Since it's gonna be returned from the public `this.current()` method,
|
|
53
|
+
// convert it from `LocationInternal` to `Location`.
|
|
54
|
+
this._location = getLocationFromInternalLocation(location);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
if (isPromise(result)) {
|
|
58
|
+
result.then(onResult);
|
|
59
|
+
} else {
|
|
60
|
+
onResult(result);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
22
64
|
|
|
23
65
|
// Create `ScrollPositionRestoration`.
|
|
24
|
-
if (
|
|
25
|
-
this._scrollPositionRestoration = new ScrollPositionRestoration(
|
|
66
|
+
if (manageScrollPosition) {
|
|
67
|
+
this._scrollPositionRestoration = new ScrollPositionRestoration(this._session,
|
|
68
|
+
// Custom `ScrollPositionSetter`.
|
|
69
|
+
{
|
|
70
|
+
scrollPositionSetter
|
|
71
|
+
});
|
|
26
72
|
}
|
|
27
73
|
}
|
|
74
|
+
|
|
75
|
+
// Subscribes to any changes of the current location.
|
|
76
|
+
// The first subscriber is always the `NavigationStack` itself
|
|
77
|
+
// because its listener is what drives the actual navigation.
|
|
78
|
+
// Any additional application-specific listeners could be added, if required.
|
|
79
|
+
subscribe(listener) {
|
|
80
|
+
// `NavigationStack.subscribe()` is simply a proxy to `Session.subscribe()`
|
|
81
|
+
// with the only convenience feature that it "normalizes" the `location` argument.
|
|
82
|
+
return this._session.subscribe(locationInternal => {
|
|
83
|
+
listener(getLocationFromInternalLocation(locationInternal));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
addNavigationBlocker(blocker) {
|
|
87
|
+
return addNavigationBlocker(this._session, blocker);
|
|
88
|
+
}
|
|
28
89
|
addScrollableContainer(scrollableContainerKey, scrollableContainer) {
|
|
29
90
|
if (!this._scrollPositionRestoration) {
|
|
30
|
-
throw new Error('`
|
|
91
|
+
throw new Error('`manageScrollPosition: true` option not passed');
|
|
31
92
|
}
|
|
32
93
|
return this._scrollPositionRestoration.addScrollableContainer(scrollableContainerKey, scrollableContainer);
|
|
33
94
|
}
|
|
34
|
-
subscribe(listener) {
|
|
35
|
-
// Subscribe to any potential Redux state changes.
|
|
36
|
-
return this._store.subscribe(() => {
|
|
37
|
-
// Initially, calls the listener when setting the initial location.
|
|
38
|
-
// After that, calls it on any location change.
|
|
39
|
-
const location = this.current();
|
|
40
|
-
if (!this._latestLocation || location !== this._latestLocation) {
|
|
41
|
-
this._latestLocation = location;
|
|
42
|
-
listener(location);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
95
|
init(initialLocation) {
|
|
47
|
-
if (this.
|
|
96
|
+
if (this._location) {
|
|
48
97
|
throw new Error('Already initialized');
|
|
49
98
|
}
|
|
50
|
-
this.
|
|
51
|
-
|
|
99
|
+
this._session.start(initialLocation && this._parseInputLocation(initialLocation));
|
|
100
|
+
if (this._scrollPositionRestoration) {
|
|
101
|
+
this._scrollPositionRestoration.start();
|
|
102
|
+
}
|
|
52
103
|
}
|
|
53
104
|
current() {
|
|
54
|
-
|
|
105
|
+
// TypeScript definition of the `.current()` method tells that it always returns
|
|
106
|
+
// some non-`undefined` location.
|
|
107
|
+
// But `this._location` is `undefined` until `.init(initialLocation?)` is called.
|
|
108
|
+
// To work around that limitation, it simply throws if `.current()` is called before `.init()`.
|
|
109
|
+
if (!this._location) {
|
|
110
|
+
throw new Error('Not initialized');
|
|
111
|
+
}
|
|
112
|
+
return this._location;
|
|
55
113
|
}
|
|
56
114
|
push(location) {
|
|
57
|
-
this.
|
|
115
|
+
this._navigate('push', location);
|
|
58
116
|
}
|
|
59
117
|
replace(location) {
|
|
60
|
-
this.
|
|
118
|
+
this._navigate('replace', location);
|
|
119
|
+
}
|
|
120
|
+
_navigate(operation, location) {
|
|
121
|
+
const toLocation = this._parseInputLocation(location);
|
|
122
|
+
const result = blockProgrammaticNavigationIfRequired(toLocation, this._session);
|
|
123
|
+
const onResult = blocked => {
|
|
124
|
+
if (!blocked) {
|
|
125
|
+
this._session.navigate(operation, toLocation);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
if (isPromise(result)) {
|
|
129
|
+
result.then(onResult);
|
|
130
|
+
} else {
|
|
131
|
+
onResult(result);
|
|
132
|
+
}
|
|
61
133
|
}
|
|
62
134
|
shift(delta) {
|
|
63
|
-
this.
|
|
135
|
+
this._session.shift(delta);
|
|
64
136
|
}
|
|
65
137
|
stop() {
|
|
138
|
+
if (!this._unsubscribe) {
|
|
139
|
+
throw new Error('Already stopped');
|
|
140
|
+
}
|
|
141
|
+
this._unsubscribe();
|
|
142
|
+
this._unsubscribe = undefined;
|
|
143
|
+
|
|
144
|
+
// Even if it calls `unsubscribe()` function above, any other subscriptions
|
|
145
|
+
// would still stay. We're not talking about `navigationStack.subscribe()`
|
|
146
|
+
// subscriptions because those don't really matter in terms of cleaning them up:
|
|
147
|
+
// those're just Redux store subscriptions that don't have any side effects.
|
|
148
|
+
// Subscriptions we're talking here are `Session`'s own subscription
|
|
149
|
+
// via `session.subscribe()` and any hypothetical manual `session.subscribe()`
|
|
150
|
+
// calls that could be made by the application code for whatever purpose.
|
|
151
|
+
// Both of those should be cleared.
|
|
152
|
+
// To work around that, `.stop()` function removes all subscriptions.
|
|
153
|
+
this._session.stop();
|
|
154
|
+
removeAllNavigationBlockers(this._session);
|
|
66
155
|
if (this._scrollPositionRestoration) {
|
|
67
156
|
this._scrollPositionRestoration.stop();
|
|
68
157
|
}
|
|
69
|
-
this._store.dispatch(Actions.stop());
|
|
70
158
|
}
|
|
71
|
-
locationRendered() {
|
|
72
|
-
if (this._scrollPositionRestoration) {
|
|
73
|
-
|
|
74
|
-
if (!location) {
|
|
75
|
-
throw new Error('Not initialized');
|
|
76
|
-
}
|
|
77
|
-
return this._scrollPositionRestoration.locationRendered(location);
|
|
159
|
+
locationRendered(location) {
|
|
160
|
+
if (!this._scrollPositionRestoration) {
|
|
161
|
+
throw new Error('`manageScrollPosition: true` option not passed');
|
|
78
162
|
}
|
|
79
|
-
return
|
|
163
|
+
return this._scrollPositionRestoration.locationRendered(location);
|
|
164
|
+
}
|
|
165
|
+
_parseInputLocation(inputLocation) {
|
|
166
|
+
// Parse input location (string or incomplete object) to a proper `location` object.
|
|
167
|
+
// Add `basePath` to `location`.
|
|
168
|
+
return addBasePath(parseInputLocation(inputLocation), this._basePath);
|
|
80
169
|
}
|
|
81
170
|
}
|
|
@@ -6,6 +6,7 @@ export default class DataStorage {
|
|
|
6
6
|
throw new Error('`DataStorage` requires a `session.key`');
|
|
7
7
|
}
|
|
8
8
|
this._sessionKey = session.key;
|
|
9
|
+
this._log = session.environment.log;
|
|
9
10
|
this._dataStorage = session.environment.dataStorage;
|
|
10
11
|
this._namespace = namespace;
|
|
11
12
|
}
|
|
@@ -22,8 +23,8 @@ export default class DataStorage {
|
|
|
22
23
|
// junk into sessionStorage under our namespace.
|
|
23
24
|
return JSON.parse(value);
|
|
24
25
|
} catch (error) {
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
this._log.error('[navigation-stack] Could not read data from storage');
|
|
27
|
+
this._log.error(error);
|
|
27
28
|
|
|
28
29
|
// Pretend that the entry doesn't exist.
|
|
29
30
|
return undefined;
|
|
@@ -36,8 +37,8 @@ export default class DataStorage {
|
|
|
36
37
|
this._dataStorage.remove(storageKey);
|
|
37
38
|
} catch (error) {
|
|
38
39
|
// No need to handle errors here.
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
this._log.error('[navigation-stack] Could not delete data from storage');
|
|
41
|
+
this._log.error(error);
|
|
41
42
|
}
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
@@ -50,8 +51,8 @@ export default class DataStorage {
|
|
|
50
51
|
} catch (error) {
|
|
51
52
|
// No need to handle errors here either. If it didn't work, it didn't
|
|
52
53
|
// work. We make no guarantees about actually saving the value.
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
this._log.error('[navigation-stack] Could not save data in storage');
|
|
55
|
+
this._log.error(error);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import InMemoryDataStorage from './data-storage/InMemoryDataStorage';
|
|
2
|
+
import InMemorySessionLifecycle from './lifecycle/InMemorySessionLifecycle';
|
|
3
|
+
import InMemoryLog from './log/InMemoryLog';
|
|
4
|
+
import InMemoryNavigation from './navigation/InMemoryNavigation';
|
|
2
5
|
import InMemoryScrollPosition from './scroll-position/InMemoryScrollPosition';
|
|
3
6
|
export default class InMemoryEnvironment {
|
|
4
7
|
constructor() {
|
|
5
8
|
this.dataStorage = new InMemoryDataStorage();
|
|
9
|
+
this.log = new InMemoryLog();
|
|
10
|
+
this.lifecycle = new InMemorySessionLifecycle();
|
|
11
|
+
this.navigation = new InMemoryNavigation();
|
|
6
12
|
this.scrollPosition = new InMemoryScrollPosition();
|
|
7
13
|
}
|
|
8
14
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import InMemoryEnvironment from './InMemoryEnvironment';
|
|
2
|
+
import ServerSideNavigation from './navigation/ServerSideNavigation';
|
|
3
|
+
|
|
4
|
+
// `ServerSideRenderSession` is just a `InMemorySession` that specifically prohibits any navigation.
|
|
5
|
+
export default class ServerSideRenderEnvironment extends InMemoryEnvironment {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.navigation = new ServerSideNavigation();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import WebBrowserDataStorage from './data-storage/WebBrowserDataStorage';
|
|
2
|
+
import WebBrowserSessionLifecycle from './lifecycle/WebBrowserSessionLifecycle';
|
|
3
|
+
import WebBrowserLog from './log/WebBrowserLog';
|
|
4
|
+
import WebBrowserNavigation from './navigation/WebBrowserNavigation';
|
|
2
5
|
import WebBrowserScrollPosition from './scroll-position/WebBrowserScrollPosition';
|
|
3
6
|
export default class WebBrowserEnvironment {
|
|
4
7
|
constructor() {
|
|
5
8
|
this.dataStorage = new WebBrowserDataStorage();
|
|
6
9
|
this.scrollPosition = new WebBrowserScrollPosition();
|
|
10
|
+
this.lifecycle = new WebBrowserSessionLifecycle();
|
|
11
|
+
this.log = new WebBrowserLog();
|
|
12
|
+
this.navigation = new WebBrowserNavigation();
|
|
7
13
|
}
|
|
8
14
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const DEBUG_ENABLED = false;
|
|
2
|
+
export default class InMemoryLog {
|
|
3
|
+
debug(...args) {
|
|
4
|
+
if (DEBUG_ENABLED) {
|
|
5
|
+
// eslint-disable-next-line no-console
|
|
6
|
+
console.log(...args);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
warn(...args) {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console.warn(...args);
|
|
12
|
+
}
|
|
13
|
+
error(...args) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.error(...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default class WebBrowserLog {
|
|
2
|
+
debug(...args) {
|
|
3
|
+
if (window.NAVIGATION_STACK_DEBUG_ENABLED) {
|
|
4
|
+
// eslint-disable-next-line no-console
|
|
5
|
+
console.log(...args);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
warn(...args) {
|
|
9
|
+
// eslint-disable-next-line no-console
|
|
10
|
+
console.warn(...args);
|
|
11
|
+
}
|
|
12
|
+
error(...args) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.error(...args);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -5,12 +5,23 @@ export default class InMemoryNavigation {
|
|
|
5
5
|
this._stack = [];
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
// Subscribes to any "asynchronous" changes of the current location,
|
|
9
|
+
// "asynchronous" changes being ones that happen out-of-sync with the code
|
|
10
|
+
// that might have potentially triggered those changes.
|
|
11
|
+
//
|
|
12
|
+
// For example, in a web browser, "Back"/"Forward" navigation happens out-of-sync
|
|
13
|
+
// with the code that calls `window.pushState()` or `window.replaceState()` function.
|
|
14
|
+
//
|
|
15
|
+
// Additionally, in a web browser, "Back"/"Forward" navigation could be triggered
|
|
16
|
+
// outside of the application code by user clicking those "Back"/"Forward" buttons manually
|
|
17
|
+
// in their web browser.
|
|
18
|
+
//
|
|
8
19
|
// eslint-disable-next-line no-unused-vars
|
|
9
|
-
|
|
10
|
-
// `InMemoryNavigation`
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
20
|
+
subscribeToAsyncrhonousLocationUpdates(listener) {
|
|
21
|
+
// `InMemoryNavigation` location changes are always "synchronous"
|
|
22
|
+
// with the code that initiated such changes, i.e. it always performs
|
|
23
|
+
// any navigation immediately at the time such navigation is triggered in code.
|
|
24
|
+
// Hence, this function doesn't have to "subscribe" to anything, so it's a "no op".
|
|
14
25
|
return () => {};
|
|
15
26
|
}
|
|
16
27
|
init(initialLocation, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle, max-classes-per-file */
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import ServerSideRedirectError from './error/ServerSideRedirectError';
|
|
4
4
|
export default class ServerSideNavigation {
|
|
5
5
|
init(initialLocation, {
|
|
6
6
|
operation,
|
|
@@ -16,12 +16,21 @@ export default class ServerSideNavigation {
|
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Subscribes to any "asynchronous" changes of the current location,
|
|
20
|
+
// "asynchronous" changes being ones that happen out-of-sync with the code
|
|
21
|
+
// that might have potentially triggered those changes.
|
|
22
|
+
//
|
|
23
|
+
// For example, in a web browser, "Back"/"Forward" navigation happens out-of-sync
|
|
24
|
+
// with the code that calls `window.pushState()` or `window.replaceState()` function.
|
|
25
|
+
//
|
|
26
|
+
// Additionally, in a web browser, "Back"/"Forward" navigation could be triggered
|
|
27
|
+
// outside of the application code by user clicking those "Back"/"Forward" buttons manually
|
|
28
|
+
// in their web browser.
|
|
29
|
+
//
|
|
19
30
|
// eslint-disable-next-line no-unused-vars
|
|
20
|
-
|
|
21
|
-
// `ServerSideNavigation`
|
|
22
|
-
//
|
|
23
|
-
// So no asynchronous listener would ever be called
|
|
24
|
-
// due to no asynchronous events being dispatched.
|
|
31
|
+
subscribeToAsyncrhonousLocationUpdates(listener) {
|
|
32
|
+
// `ServerSideNavigation` location changes are prohibited, so they couldn't happen.
|
|
33
|
+
// Hence, this function doesn't have to "subscribe" to anything, so it's a "no op".
|
|
25
34
|
return () => {};
|
|
26
35
|
}
|
|
27
36
|
|
|
@@ -32,7 +41,7 @@ export default class ServerSideNavigation {
|
|
|
32
41
|
index,
|
|
33
42
|
delta
|
|
34
43
|
}) {
|
|
35
|
-
throw new
|
|
44
|
+
throw new ServerSideRedirectError(location);
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -47,15 +47,28 @@ export default class WebBrowserNavigation {
|
|
|
47
47
|
this._currentLocationIndex = NO_LOCATION_INDEX;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// Subscribes to changes
|
|
50
|
+
// Subscribes to any "asynchronous" changes of the current location,
|
|
51
|
+
// "asynchronous" changes being ones that happen out-of-sync with the code
|
|
52
|
+
// that might have potentially triggered those changes.
|
|
53
|
+
//
|
|
54
|
+
// For example, in a web browser, "Back"/"Forward" navigation happens out-of-sync
|
|
55
|
+
// with the code that calls `window.pushState()` or `window.replaceState()` function.
|
|
56
|
+
//
|
|
57
|
+
// Additionally, in a web browser, "Back"/"Forward" navigation could be triggered
|
|
58
|
+
// outside of the application code by user clicking those "Back"/"Forward" buttons manually
|
|
59
|
+
// in their web browser.
|
|
60
|
+
//
|
|
51
61
|
// Returns an `unsubscribe()` function which is "idempotent", i.e. it can be called multiple times.
|
|
52
|
-
|
|
62
|
+
//
|
|
63
|
+
subscribeToAsyncrhonousLocationUpdates(listener) {
|
|
53
64
|
const onPopState = () => {
|
|
54
65
|
// If "popstate" event is received before navigation is initialized,
|
|
55
|
-
// ignore such "popstate" event.
|
|
66
|
+
// ignore such "popstate" event. Such "ignore" behavior is logical from
|
|
67
|
+
// the application code's point of view: it doesn't expect any navigation events
|
|
68
|
+
// to be recorded before it has initialized the navigation.
|
|
56
69
|
// And besides, `this._currentLocationIndex` is not defined in such conditions.
|
|
57
70
|
if (this._currentLocationIndex === NO_LOCATION_INDEX) {
|
|
58
|
-
throw new Error('Received a "popstate" event before
|
|
71
|
+
throw new Error('Received a "popstate" event before finished initializing navigation');
|
|
59
72
|
}
|
|
60
73
|
const prevIndex = this._currentLocationIndex;
|
|
61
74
|
const {
|
|
@@ -86,6 +99,16 @@ export default class WebBrowserNavigation {
|
|
|
86
99
|
this._subscribed = false;
|
|
87
100
|
};
|
|
88
101
|
}
|
|
102
|
+
|
|
103
|
+
// When run in a web browser, it could not only "start" a new navigation session
|
|
104
|
+
// but also "resume" a previously-started navigation session. That could happen
|
|
105
|
+
// when the user refreshes a page in a web browser which still retains
|
|
106
|
+
// the previous navigation session's data but at the same time restarts
|
|
107
|
+
// the javascript code from scratch.
|
|
108
|
+
//
|
|
109
|
+
// So this `init()` method handles both cases: when there's previous navigation session's data
|
|
110
|
+
// that should be restored and when there's no previous navigation session's data.
|
|
111
|
+
//
|
|
89
112
|
init(initialLocation, {
|
|
90
113
|
operation,
|
|
91
114
|
key,
|
|
@@ -102,7 +125,8 @@ export default class WebBrowserNavigation {
|
|
|
102
125
|
// by calling `window.history.replaceState()` on page load.
|
|
103
126
|
// Otherwise, `window.history.state` would be `null` for the initial location
|
|
104
127
|
// and there'd be no place to store the additional properties of the initial location
|
|
105
|
-
// such as `location.key`.
|
|
128
|
+
// such as `location.key`. Without `location.key` always being present the initial location object,
|
|
129
|
+
// there'd be a bug of incorrect scroll position being set on the initial location URL in some scenarios:
|
|
106
130
|
// https://github.com/taion/scroll-behavior/issues/215
|
|
107
131
|
//
|
|
108
132
|
// If the user opens the initial page for the first time, `window.history.state` will be `null`.
|
|
@@ -116,7 +140,7 @@ export default class WebBrowserNavigation {
|
|
|
116
140
|
index
|
|
117
141
|
};
|
|
118
142
|
// Call `history.replaceState()`.
|
|
119
|
-
this.
|
|
143
|
+
this._navigateToLocationAndKeepItsAdditionalPropertiesInHistory(initialLocation, additionalProperties, delta);
|
|
120
144
|
}
|
|
121
145
|
this._currentLocationIndex = index;
|
|
122
146
|
|
|
@@ -136,7 +160,7 @@ export default class WebBrowserNavigation {
|
|
|
136
160
|
key,
|
|
137
161
|
index
|
|
138
162
|
};
|
|
139
|
-
this.
|
|
163
|
+
this._navigateToLocationAndKeepItsAdditionalPropertiesInHistory(location, additionalProperties, delta);
|
|
140
164
|
this._currentLocationIndex = index;
|
|
141
165
|
|
|
142
166
|
// Call the listeners.
|
|
@@ -198,7 +222,23 @@ export default class WebBrowserNavigation {
|
|
|
198
222
|
delta
|
|
199
223
|
};
|
|
200
224
|
}
|
|
201
|
-
|
|
225
|
+
|
|
226
|
+
// Stores "additional" properties associated with `location` in web browser's history storage.
|
|
227
|
+
// Web browser's history storage is not intended for large datasets and should only be used
|
|
228
|
+
// to store small bits of data.
|
|
229
|
+
//
|
|
230
|
+
// "Some browsers save state objects to the user's disk so they can be restored after the user restarts
|
|
231
|
+
// the browser, and impose a size limit on the serialized representation of a state object, and will throw
|
|
232
|
+
// an exception if you pass a state object whose serialized representation is larger than that size limit.
|
|
233
|
+
// So in cases where you want to ensure you have more space than what some browsers might impose,
|
|
234
|
+
// you're encouraged to use sessionStorage and/or localStorage."
|
|
235
|
+
//
|
|
236
|
+
// Source: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
|
|
237
|
+
//
|
|
238
|
+
// To store large amounts of data, one could use `window.sessionStorage` instead.
|
|
239
|
+
// It is accessible via `DataStorage(session)` class.
|
|
240
|
+
//
|
|
241
|
+
_navigateToLocationAndKeepItsAdditionalPropertiesInHistory(location, additionalProperties, delta) {
|
|
202
242
|
const url = getLocationUrl(location);
|
|
203
243
|
// `delta` property is not stored in `window.history.state`
|
|
204
244
|
// because it is supposed to be recalculated every time when reading from `window.history.state`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _excluded = ["operation"];
|
|
2
2
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
3
3
|
import getLocationUrl from '../../../getLocationUrl';
|
|
4
|
-
export default class
|
|
4
|
+
export default class ServerSideRedirectError extends Error {
|
|
5
5
|
constructor(location) {
|
|
6
6
|
super(location ? `Navigate to ${getLocationUrl(location)}` : 'Navigate to previous or next location');
|
|
7
7
|
if (location) {
|
|
@@ -45,9 +45,24 @@ export default class WebBrowserScrollPosition {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
enableAutomaticScrollRestoration() {
|
|
48
|
+
// The default "auto" behavior seems to work in the following way:
|
|
49
|
+
//
|
|
50
|
+
// * It doesn't scroll to top on `window.history.pushState()` or `window.history.replaceState()`.
|
|
51
|
+
//
|
|
52
|
+
// * It does restore scroll position on "popstate" event
|
|
53
|
+
// (they say, in Firefox it happens before the event is dispatched,
|
|
54
|
+
// while in Chrome it happens after the event is dispatched)
|
|
55
|
+
//
|
|
56
|
+
// https://v5.reactrouter.com/web/guides/scroll-restoration
|
|
57
|
+
//
|
|
48
58
|
window.history.scrollRestoration = 'auto';
|
|
49
59
|
}
|
|
50
60
|
disableAutomaticScrollRestoration() {
|
|
61
|
+
// Setting `window.history.scrollRestoration` value updates it in the current history entry
|
|
62
|
+
// and any subsequent history entries.
|
|
63
|
+
// This means that it should be set at application initialization stage,
|
|
64
|
+
// that is before any navigation.
|
|
65
|
+
// https://majido.github.io/scroll-restoration-proposal/history-based-api.html
|
|
51
66
|
window.history.scrollRestoration = 'manual';
|
|
52
67
|
}
|
|
53
68
|
init() {}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const _excluded = ["key", "index"];
|
|
2
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
3
|
+
// Converts `Location` object to a `LocationBase` object.
|
|
4
|
+
// It hides properties of location such as `key` or `index`.
|
|
5
|
+
export default function getLocationBaseFromLocation(location) {
|
|
6
|
+
// eslint-disable-next-line no-unused-vars
|
|
7
|
+
const locationBase = _objectWithoutPropertiesLoose(location, _excluded);
|
|
8
|
+
return locationBase;
|
|
9
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import stringifyQueryAsSearch from './stringifyQueryAsSearch';
|
|
2
2
|
export default function getLocationUrl({
|
|
3
3
|
pathname,
|
|
4
4
|
search,
|
|
@@ -6,10 +6,7 @@ export default function getLocationUrl({
|
|
|
6
6
|
hash
|
|
7
7
|
}) {
|
|
8
8
|
if (!search && query) {
|
|
9
|
-
|
|
10
|
-
if (queryString) {
|
|
11
|
-
search = `?${queryString}`;
|
|
12
|
-
}
|
|
9
|
+
search = stringifyQueryAsSearch(query);
|
|
13
10
|
}
|
|
14
11
|
return `${pathname}${search || ''}${hash || ''}`;
|
|
15
12
|
}
|
package/lib/esm/index.js
CHANGED
|
@@ -4,11 +4,8 @@ export { default as getLocationUrl } from './getLocationUrl';
|
|
|
4
4
|
export { default as parseLocationUrl } from './parseLocationUrl';
|
|
5
5
|
export { default as parseInputLocation } from './parseInputLocation';
|
|
6
6
|
export { default as NavigationStack } from './NavigationStack';
|
|
7
|
-
export { default as
|
|
8
|
-
export { default as
|
|
9
|
-
export { default as
|
|
10
|
-
export { default as
|
|
11
|
-
export { default as
|
|
12
|
-
export { default as ServerSideRenderSession } from './session/ServerSideRenderSession';
|
|
13
|
-
export { default as ServerSideNavigationError } from './session/navigation/error/ServerSideNavigationError';
|
|
14
|
-
export { default as NavigationOutOfBoundsError } from './session/navigation/error/NavigationOutOfBoundsError';
|
|
7
|
+
export { default as InMemoryEnvironment } from './environment/InMemoryEnvironment';
|
|
8
|
+
export { default as WebBrowserEnvironment } from './environment/WebBrowserEnvironment';
|
|
9
|
+
export { default as ServerSideRenderEnvironment } from './environment/ServerSideRenderEnvironment';
|
|
10
|
+
export { default as ServerSideRedirectError } from './environment/navigation/error/ServerSideRedirectError';
|
|
11
|
+
export { default as NavigationOutOfBoundsError } from './environment/navigation/error/NavigationOutOfBoundsError';
|