navigation-stack 0.5.3 → 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.
Files changed (210) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +144 -282
  3. package/karma.conf.cjs +1 -1
  4. package/lib/cjs/NavigationStack.js +138 -49
  5. package/lib/cjs/data-storage/DataStorage.js +7 -6
  6. package/lib/cjs/environment/InMemoryEnvironment.js +6 -0
  7. package/lib/cjs/{session/ServerSideRenderSession.js → environment/ServerSideRenderEnvironment.js} +5 -6
  8. package/lib/cjs/environment/WebBrowserEnvironment.js +6 -0
  9. package/lib/cjs/environment/log/InMemoryLog.js +23 -0
  10. package/lib/cjs/environment/log/WebBrowserLog.js +22 -0
  11. package/lib/cjs/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  12. package/lib/cjs/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  13. package/lib/cjs/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  14. package/lib/cjs/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +2 -2
  15. package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  16. package/lib/cjs/getLocationBaseFromLocation.js +14 -0
  17. package/lib/cjs/getLocationUrl.js +3 -5
  18. package/lib/cjs/index.js +10 -16
  19. package/lib/cjs/navigationBlockers.js +34 -32
  20. package/lib/cjs/navigationBlockersEvaluation.js +150 -0
  21. package/lib/cjs/parseInputLocation.js +2 -2
  22. package/lib/cjs/parseQueryFromSearch.js +3 -6
  23. package/lib/cjs/parseQueryString.js +77 -0
  24. package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +7 -6
  25. package/lib/cjs/scroll-position/ScrollPositionRestoration.js +31 -27
  26. package/lib/cjs/scroll-position/ScrollPositionSaver.js +6 -4
  27. package/lib/cjs/session/Session.js +61 -26
  28. package/lib/cjs/session/subscription/Subscription.js +36 -18
  29. package/lib/cjs/stringifyQuery.js +66 -0
  30. package/lib/cjs/stringifyQueryAsSearch.js +14 -0
  31. package/lib/esm/NavigationStack.js +138 -49
  32. package/lib/esm/data-storage/DataStorage.js +7 -6
  33. package/lib/esm/environment/InMemoryEnvironment.js +6 -0
  34. package/lib/esm/environment/ServerSideRenderEnvironment.js +10 -0
  35. package/lib/esm/environment/WebBrowserEnvironment.js +6 -0
  36. package/lib/esm/environment/log/InMemoryLog.js +17 -0
  37. package/lib/esm/environment/log/WebBrowserLog.js +16 -0
  38. package/lib/esm/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  39. package/lib/esm/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  40. package/lib/esm/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  41. package/lib/esm/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
  42. package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  43. package/lib/esm/getLocationBaseFromLocation.js +9 -0
  44. package/lib/esm/getLocationUrl.js +2 -5
  45. package/lib/esm/index.js +5 -8
  46. package/lib/esm/navigationBlockers.js +34 -32
  47. package/lib/esm/navigationBlockersEvaluation.js +145 -0
  48. package/lib/esm/parseInputLocation.js +2 -2
  49. package/lib/esm/parseQueryFromSearch.js +2 -6
  50. package/lib/esm/parseQueryString.js +72 -0
  51. package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +7 -6
  52. package/lib/esm/scroll-position/ScrollPositionRestoration.js +31 -27
  53. package/lib/esm/scroll-position/ScrollPositionSaver.js +6 -4
  54. package/lib/esm/session/Session.js +61 -26
  55. package/lib/esm/session/subscription/Subscription.js +36 -18
  56. package/lib/esm/stringifyQuery.js +61 -0
  57. package/lib/esm/stringifyQueryAsSearch.js +8 -0
  58. package/lib/index.d.ts +180 -34
  59. package/package.json +4 -7
  60. package/src/NavigationStack.js +166 -56
  61. package/src/data-storage/DataStorage.js +9 -6
  62. package/src/environment/InMemoryEnvironment.js +6 -0
  63. package/src/environment/ServerSideRenderEnvironment.js +10 -0
  64. package/src/environment/WebBrowserEnvironment.js +6 -0
  65. package/src/environment/log/InMemoryLog.js +20 -0
  66. package/src/environment/log/WebBrowserLog.js +18 -0
  67. package/src/{session → environment}/navigation/InMemoryNavigation.js +16 -5
  68. package/src/{session → environment}/navigation/ServerSideNavigation.js +16 -7
  69. package/src/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
  70. package/src/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
  71. package/src/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
  72. package/src/getLocationBaseFromLocation.js +7 -0
  73. package/src/getLocationUrl.js +2 -5
  74. package/src/index.js +10 -13
  75. package/src/navigationBlockers.js +55 -34
  76. package/src/navigationBlockersEvaluation.js +161 -0
  77. package/src/parseInputLocation.js +2 -2
  78. package/src/parseQueryFromSearch.js +2 -6
  79. package/src/parseQueryString.js +81 -0
  80. package/src/scroll-position/ScrollPositionAutoSaver.js +10 -6
  81. package/src/scroll-position/ScrollPositionRestoration.js +36 -30
  82. package/src/scroll-position/ScrollPositionSaver.js +6 -4
  83. package/src/scroll-position/index.js +1 -1
  84. package/src/session/Session.js +68 -24
  85. package/src/session/subscription/Subscription.js +36 -11
  86. package/src/stringifyQuery.js +71 -0
  87. package/src/stringifyQueryAsSearch.js +9 -0
  88. package/test/NavigationStack.addBasePath.test.js +50 -0
  89. package/test/{redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockNonProgrammaticNavigationIfRequired.test.js} +51 -63
  90. package/test/{redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockProgrammaticNavigationIfRequired.test.js} +98 -78
  91. package/test/NavigationStack.general.test.js +68 -0
  92. package/test/NavigationStack.parseInputLocation.test.js +52 -0
  93. package/test/NavigationStack.removeBasePath.test.js +69 -0
  94. package/test/NavigationStack.test.js +97 -29
  95. package/test/data-storage/LocationDataStorage.test.js +3 -2
  96. package/test/index.js +7 -31
  97. package/test/index.test.js +4 -5
  98. package/test/parseQueryFromSearch.test.js +19 -0
  99. package/test/parseQueryString.test.js +18 -0
  100. package/test/scroll-position/ScrollPositionRestoration.test.js +34 -13
  101. package/test/scroll-position/createApp.js +8 -8
  102. package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +4 -4
  103. package/test/session/{InMemorySession.test.js → Session.InMemoryEnvironment.test.js} +10 -9
  104. package/test/session/{ServerSession.test.js → Session.ServerSideRenderEnvironment.test.js} +5 -4
  105. package/test/session/{WebBrowserSession.test.js → Session.WebBrowserEnvironment.test.js} +63 -13
  106. package/test/shouldWarn.js +44 -0
  107. package/test/stringifyQuery.test.js +65 -0
  108. package/types/index.d.ts +180 -34
  109. package/types/tsconfig.json +0 -1
  110. package/data-storage/package.json +0 -7
  111. package/lib/cjs/createSearchFromQuery.js +0 -13
  112. package/lib/cjs/debug.js +0 -12
  113. package/lib/cjs/redux/ActionTypes.js +0 -14
  114. package/lib/cjs/redux/ActionTypesInternal.js +0 -8
  115. package/lib/cjs/redux/Actions.js +0 -28
  116. package/lib/cjs/redux/createMiddlewares.js +0 -60
  117. package/lib/cjs/redux/index.js +0 -13
  118. package/lib/cjs/redux/internalLocationReducer.js +0 -14
  119. package/lib/cjs/redux/locationReducer.js +0 -13
  120. package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -32
  121. package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -113
  122. package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
  123. package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -30
  124. package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -73
  125. package/lib/cjs/redux/middleware/navigationOperationMiddleware.js +0 -40
  126. package/lib/cjs/redux/middleware/parseInputLocationMiddleware.js +0 -29
  127. package/lib/cjs/redux/middleware/updateLocationMiddleware.js +0 -34
  128. package/lib/cjs/session/InMemorySession.js +0 -22
  129. package/lib/cjs/session/WebBrowserSession.js +0 -20
  130. package/lib/data-storage/index.d.ts +0 -35
  131. package/lib/esm/createSearchFromQuery.js +0 -8
  132. package/lib/esm/debug.js +0 -7
  133. package/lib/esm/redux/ActionTypes.js +0 -9
  134. package/lib/esm/redux/ActionTypesInternal.js +0 -3
  135. package/lib/esm/redux/Actions.js +0 -22
  136. package/lib/esm/redux/createMiddlewares.js +0 -54
  137. package/lib/esm/redux/index.js +0 -4
  138. package/lib/esm/redux/internalLocationReducer.js +0 -8
  139. package/lib/esm/redux/locationReducer.js +0 -7
  140. package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
  141. package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -108
  142. package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -88
  143. package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -25
  144. package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -68
  145. package/lib/esm/redux/middleware/navigationOperationMiddleware.js +0 -35
  146. package/lib/esm/redux/middleware/parseInputLocationMiddleware.js +0 -24
  147. package/lib/esm/redux/middleware/updateLocationMiddleware.js +0 -28
  148. package/lib/esm/session/InMemorySession.js +0 -15
  149. package/lib/esm/session/ServerSideRenderSession.js +0 -11
  150. package/lib/esm/session/WebBrowserSession.js +0 -13
  151. package/lib/redux/index.d.ts +0 -90
  152. package/lib/scroll-position/index.d.ts +0 -107
  153. package/redux/package.json +0 -7
  154. package/scroll-position/package.json +0 -7
  155. package/src/createSearchFromQuery.js +0 -9
  156. package/src/debug.js +0 -8
  157. package/src/redux/ActionTypes.js +0 -9
  158. package/src/redux/ActionTypesInternal.js +0 -3
  159. package/src/redux/Actions.js +0 -27
  160. package/src/redux/createMiddlewares.js +0 -65
  161. package/src/redux/index.js +0 -4
  162. package/src/redux/internalLocationReducer.js +0 -9
  163. package/src/redux/locationReducer.js +0 -8
  164. package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
  165. package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -119
  166. package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
  167. package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -26
  168. package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -72
  169. package/src/redux/middleware/navigationOperationMiddleware.js +0 -34
  170. package/src/redux/middleware/parseInputLocationMiddleware.js +0 -23
  171. package/src/redux/middleware/updateLocationMiddleware.js +0 -28
  172. package/src/session/InMemorySession.js +0 -13
  173. package/src/session/ServerSideRenderSession.js +0 -9
  174. package/src/session/WebBrowserSession.js +0 -13
  175. package/test/middlewareTestUtil.js +0 -31
  176. package/test/redux/Action.test.js +0 -73
  177. package/test/redux/ActionTypes.test.js +0 -13
  178. package/test/redux/createMiddlewares.test.js +0 -96
  179. package/test/redux/index.test.js +0 -10
  180. package/test/redux/locationReducer.test.js +0 -39
  181. package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +0 -40
  182. package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +0 -51
  183. package/test/redux/middleware/navigationOperationMiddleware.test.js +0 -78
  184. package/test/redux/middleware/parseInputLocationMiddleware.test.js +0 -62
  185. package/test/testUtil.js +0 -3
  186. package/types/data-storage/index.d.ts +0 -35
  187. package/types/redux/index.d.ts +0 -90
  188. package/types/scroll-position/index.d.ts +0 -107
  189. /package/lib/cjs/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  190. /package/lib/cjs/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  191. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  192. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  193. /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  194. /package/lib/cjs/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  195. /package/lib/cjs/{session → environment}/navigation/operation/operations.js +0 -0
  196. /package/lib/esm/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  197. /package/lib/esm/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  198. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  199. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  200. /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  201. /package/lib/esm/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  202. /package/lib/esm/{session → environment}/navigation/operation/operations.js +0 -0
  203. /package/src/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
  204. /package/src/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
  205. /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
  206. /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
  207. /package/src/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
  208. /package/src/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
  209. /package/src/{session → environment}/navigation/operation/operations.js +0 -0
  210. /package/test/{parseInputLocationMiddleware.test.js → parseInputLocation.test.js} +0 -0
package/lib/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.removeBasePath = exports.parseLocationUrl = exports.parseInputLocation = exports.getLocationUrl = exports.addNavigationBlocker = exports.addBasePath = exports.WebBrowserSession = exports.Session = exports.ServerSideRenderSession = exports.ServerSideNavigationError = exports.NavigationStack = exports.NavigationOutOfBoundsError = exports.LocationDataStorage = exports.InMemorySession = exports.DataStorage = void 0;
4
+ exports.removeBasePath = exports.parseLocationUrl = exports.parseInputLocation = exports.getLocationUrl = exports.addNavigationBlocker = exports.addBasePath = exports.WebBrowserEnvironment = exports.ServerSideRenderEnvironment = exports.ServerSideRedirectError = exports.NavigationStack = exports.NavigationOutOfBoundsError = exports.InMemoryEnvironment = void 0;
5
5
  var _basePath = require("./basePath");
6
6
  exports.addBasePath = _basePath.addBasePath;
7
7
  exports.removeBasePath = _basePath.removeBasePath;
@@ -15,20 +15,14 @@ var _parseInputLocation = _interopRequireDefault(require("./parseInputLocation")
15
15
  exports.parseInputLocation = _parseInputLocation.default;
16
16
  var _NavigationStack = _interopRequireDefault(require("./NavigationStack"));
17
17
  exports.NavigationStack = _NavigationStack.default;
18
- var _DataStorage = _interopRequireDefault(require("./data-storage/DataStorage"));
19
- exports.DataStorage = _DataStorage.default;
20
- var _LocationDataStorage = _interopRequireDefault(require("./data-storage/LocationDataStorage"));
21
- exports.LocationDataStorage = _LocationDataStorage.default;
22
- var _Session = _interopRequireDefault(require("./session/Session"));
23
- exports.Session = _Session.default;
24
- var _InMemorySession = _interopRequireDefault(require("./session/InMemorySession"));
25
- exports.InMemorySession = _InMemorySession.default;
26
- var _WebBrowserSession = _interopRequireDefault(require("./session/WebBrowserSession"));
27
- exports.WebBrowserSession = _WebBrowserSession.default;
28
- var _ServerSideRenderSession = _interopRequireDefault(require("./session/ServerSideRenderSession"));
29
- exports.ServerSideRenderSession = _ServerSideRenderSession.default;
30
- var _ServerSideNavigationError = _interopRequireDefault(require("./session/navigation/error/ServerSideNavigationError"));
31
- exports.ServerSideNavigationError = _ServerSideNavigationError.default;
32
- var _NavigationOutOfBoundsError = _interopRequireDefault(require("./session/navigation/error/NavigationOutOfBoundsError"));
18
+ var _InMemoryEnvironment = _interopRequireDefault(require("./environment/InMemoryEnvironment"));
19
+ exports.InMemoryEnvironment = _InMemoryEnvironment.default;
20
+ var _WebBrowserEnvironment = _interopRequireDefault(require("./environment/WebBrowserEnvironment"));
21
+ exports.WebBrowserEnvironment = _WebBrowserEnvironment.default;
22
+ var _ServerSideRenderEnvironment = _interopRequireDefault(require("./environment/ServerSideRenderEnvironment"));
23
+ exports.ServerSideRenderEnvironment = _ServerSideRenderEnvironment.default;
24
+ var _ServerSideRedirectError = _interopRequireDefault(require("./environment/navigation/error/ServerSideRedirectError"));
25
+ exports.ServerSideRedirectError = _ServerSideRedirectError.default;
26
+ var _NavigationOutOfBoundsError = _interopRequireDefault(require("./environment/navigation/error/NavigationOutOfBoundsError"));
33
27
  exports.NavigationOutOfBoundsError = _NavigationOutOfBoundsError.default;
34
28
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -5,58 +5,55 @@ exports.addNavigationBlocker = addNavigationBlocker;
5
5
  exports.getNavigationBlockers = getNavigationBlockers;
6
6
  exports.removeAllNavigationBlockers = removeAllNavigationBlockers;
7
7
  exports.runNavigationBlockers = runNavigationBlockers;
8
- var _debug = _interopRequireDefault(require("./debug"));
9
8
  var _isPromise = _interopRequireDefault(require("./isPromise"));
10
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
10
  /* eslint-disable no-underscore-dangle */
12
11
 
13
- function getNavigationBlockers(session) {
14
- return session._navigationBlockersList || [];
12
+ function getNavigationBlockers(container) {
13
+ return container._navigationBlockersList || [];
15
14
  }
16
- function addNavigationBlockerToTheList(blocker, session) {
17
- if (!session._navigationBlockersList) {
18
- session._navigationBlockersList = [];
15
+ function addNavigationBlockerToTheList(blocker, container) {
16
+ if (!container._navigationBlockersList) {
17
+ container._navigationBlockersList = [];
19
18
  }
20
- session._navigationBlockersList.push(blocker);
19
+ container._navigationBlockersList.push(blocker);
21
20
  }
22
- function removeNavigationBlockerFromTheList(blocker, session) {
23
- if (session._navigationBlockersList) {
24
- session._navigationBlockersList = session._navigationBlockersList.filter(_ => _ !== blocker);
21
+ function removeNavigationBlockerFromTheList(blocker, container) {
22
+ if (container._navigationBlockersList) {
23
+ container._navigationBlockersList = container._navigationBlockersList.filter(_ => _ !== blocker);
25
24
  }
26
25
  }
27
26
  function removeAllNavigationBlockers(session) {
28
- if (getNavigationBlockers(session).some(blocker => blocker.beforeTermination)) {
27
+ // `navigationBlockers` are stored in `session`.
28
+ const container = session;
29
+ if (getNavigationBlockers(container).some(blocker => blocker.beforeTermination)) {
29
30
  if (!session._removeTerminationBlocker) {
30
31
  throw new Error('`_removeTerminationBlocker` property not found in the `session`');
31
32
  }
32
33
  session._removeTerminationBlocker();
33
34
  session._removeTerminationBlocker = undefined;
34
35
  }
35
- session._navigationBlockersList = [];
36
+ container._navigationBlockersList = [];
36
37
  }
37
38
 
38
39
  // Runs the `blocker` while ignoring any errors that might be thrown by it.
39
40
  function runNavigationBlocker({
40
41
  blocker
41
- }, location) {
42
+ }, location, environment) {
42
43
  let result;
43
44
  try {
44
45
  result = blocker(location);
45
46
  } catch (error) {
46
- // eslint-disable-next-line no-console
47
- console.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
48
- // eslint-disable-next-line no-console
49
- console.error(error);
47
+ environment.log.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
48
+ environment.log.error(error);
50
49
  }
51
50
 
52
51
  // If the blocker returned a `Promise`, await for that `Promise`
53
52
  // and then return the result.
54
53
  if ((0, _isPromise.default)(result)) {
55
54
  return result.catch(error => {
56
- // eslint-disable-next-line no-console
57
- console.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
58
- // eslint-disable-next-line no-console
59
- console.error(error);
55
+ environment.log.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
56
+ environment.log.error(error);
60
57
  });
61
58
  }
62
59
  // The blocker didn't return a `Promise`.
@@ -67,28 +64,28 @@ function runNavigationBlocker({
67
64
  // Runs all blockers in order.
68
65
  // If any blocker returns `true`, it stops and returns the result.
69
66
  // If there's no such blocker, returns `undefined`.
70
- function runNavigationBlockers(navigationBlockers, toLocation) {
67
+ function runNavigationBlockers(navigationBlockers, toLocation, environment) {
71
68
  if (navigationBlockers.length === 0) {
72
69
  return undefined;
73
70
  }
74
71
 
75
72
  // Call the first blocker in the list.
76
- const result = runNavigationBlocker(navigationBlockers[0], toLocation);
73
+ const result = runNavigationBlocker(navigationBlockers[0], toLocation, environment);
77
74
  const next = () => {
78
75
  // Proceed to the next blocker.
79
- return runNavigationBlockers(navigationBlockers.slice(1), toLocation);
76
+ return runNavigationBlockers(navigationBlockers.slice(1), toLocation, environment);
80
77
  };
81
78
  if ((0, _isPromise.default)(result)) {
82
79
  return result.then(resultValue => {
83
80
  if (resultValue) {
84
- (0, _debug.default)('Navigation blocked', toLocation.pathname);
81
+ environment.log.debug('Navigation blocked', toLocation.pathname);
85
82
  return resultValue;
86
83
  }
87
84
  return next();
88
85
  });
89
86
  }
90
87
  if (result) {
91
- (0, _debug.default)('Navigation blocked', toLocation.pathname);
88
+ environment.log.debug('Navigation blocked', toLocation.pathname);
92
89
  return result;
93
90
  }
94
91
  return next();
@@ -96,7 +93,9 @@ function runNavigationBlockers(navigationBlockers, toLocation) {
96
93
 
97
94
  /* istanbul ignore next: not testable with Karma */
98
95
  function terminationBlocker(session) {
99
- const result = runNavigationBlockers(getNavigationBlockers(session), null);
96
+ // `navigationBlockers` are stored in `session`.
97
+ const container = session;
98
+ const result = runNavigationBlockers(getNavigationBlockers(container), null, session.environment);
100
99
 
101
100
  // If no blocker returned anything, so don't prevent the navigation.
102
101
  if (!result) {
@@ -115,6 +114,9 @@ function terminationBlocker(session) {
115
114
  return true;
116
115
  }
117
116
  function addNavigationBlocker(session, blocker) {
117
+ // `navigationBlockers` are stored in `session`.
118
+ const container = session;
119
+
118
120
  // All navigation blockers also run on `beforeTermination` event.
119
121
  // If required, this could be a parameter of this function.
120
122
  // The rationale could be that adding a `beforeunload` listener
@@ -131,11 +133,11 @@ function addNavigationBlocker(session, blocker) {
131
133
  // and this is bad for performance."
132
134
  //
133
135
  // https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
134
- if (beforeTermination && !getNavigationBlockers(session).some(navigationBlocker => navigationBlocker.beforeTermination)) {
136
+ if (beforeTermination && !getNavigationBlockers(container).some(navigationBlocker => navigationBlocker.beforeTermination)) {
135
137
  if (session._removeTerminationBlocker) {
136
138
  throw new Error('Unexpected `_removeTerminationBlocker` property found in the `session`');
137
139
  }
138
- session._removeTerminationBlocker = session.lifecycle.addTerminationBlocker(() => {
140
+ session._removeTerminationBlocker = session.environment.lifecycle.addTerminationBlocker(() => {
139
141
  return terminationBlocker(session);
140
142
  });
141
143
  }
@@ -143,12 +145,12 @@ function addNavigationBlocker(session, blocker) {
143
145
  blocker,
144
146
  beforeTermination
145
147
  };
146
- addNavigationBlockerToTheList(newNavigationBlocker, session);
148
+ addNavigationBlockerToTheList(newNavigationBlocker, container);
147
149
  return () => {
148
- removeNavigationBlockerFromTheList(newNavigationBlocker, session);
150
+ removeNavigationBlockerFromTheList(newNavigationBlocker, container);
149
151
 
150
152
  // If it was the last "beforeTermination" blocker, remove navigation blocker.
151
- if (beforeTermination && !getNavigationBlockers(session).some(navigationBlocker => navigationBlocker.beforeTermination)) {
153
+ if (beforeTermination && !getNavigationBlockers(container).some(navigationBlocker => navigationBlocker.beforeTermination)) {
152
154
  if (!session._removeTerminationBlocker) {
153
155
  throw new Error('`_removeTerminationBlocker` property not found in the `session`');
154
156
  }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.blockNonProgrammaticNavigationIfRequired = blockNonProgrammaticNavigationIfRequired;
5
+ exports.blockProgrammaticNavigationIfRequired = blockProgrammaticNavigationIfRequired;
6
+ var _getLocationBaseFromLocation = _interopRequireDefault(require("./getLocationBaseFromLocation"));
7
+ var _getLocationFromInternalLocation = _interopRequireDefault(require("./getLocationFromInternalLocation"));
8
+ var _isPromise = _interopRequireDefault(require("./isPromise"));
9
+ var _navigationBlockers = require("./navigationBlockers");
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ // Creates "navigation blockers evaluation" status object.
12
+ // It tracks the "cancelled" status of the evaluation:
13
+ // when next navigation happens, the previous one is no longer relevant
14
+ // so the evaluation of navigation blockers for it can be cancelled.
15
+ function createNavigationBlockersEvaluationStatus(container) {
16
+ /* eslint-disable no-underscore-dangle */
17
+ if (container._navigationBlockersEvaluationStatus) {
18
+ container._navigationBlockersEvaluationStatus.cancelled = true;
19
+ }
20
+ container._navigationBlockersEvaluationStatus = {
21
+ cancelled: false
22
+ };
23
+ return container._navigationBlockersEvaluationStatus;
24
+ }
25
+
26
+ // Prevents or allows navigation that was initiated by the application code
27
+ // by making a `.push()` or `.replace()` method call.
28
+ //
29
+ // It doesn't handle `.shift()` method calls because it doesn't yet know
30
+ // the `location` that it's gonna `shift` to. Instead, it waits for the web browser
31
+ // to "shift" to that `location` and then reads the `location` from the address bar
32
+ // and, if such "shift" should've been blocked, it "rewinds" the address bar back
33
+ // to the previous location. This part is handled by another function.
34
+ //
35
+ // Such type of "shifting" and then rewinding the "shift" doesn't really matter to the application code at all.
36
+ // From the application code's point of view, all web browser's address bar doesn't matter and even doesn't exist.
37
+ // All that exists from the application code's point of view is the `location` object in the `NavigationStack`'s state.
38
+ // Until the `location` object in the `NavigationStack`'s state is updated, the "old" page is still rendered.
39
+ // The appliation is only concerned with the updates of the `location` object in the `NavigationStack`'s state
40
+ // and completely ignores any updates to the URL in the web browser's address bar.
41
+ //
42
+ function blockProgrammaticNavigationIfRequired(toLocationBase,
43
+ // `location` of type `LocationBase`
44
+ session) {
45
+ // `resultValue` variable name works around a stupid javascript error:
46
+ // "Cannot redeclare block-scoped variable 'result'".
47
+ const result = (0, _navigationBlockers.runNavigationBlockers)((0, _navigationBlockers.getNavigationBlockers)(session),
48
+ // Here `payload.location` is `LocationBase`.
49
+ toLocationBase, session.environment);
50
+ if ((0, _isPromise.default)(result)) {
51
+ const evaluationStatus = createNavigationBlockersEvaluationStatus(session);
52
+ // eslint-disable-next-line consistent-return
53
+ return result.then(promiseResult => {
54
+ if (evaluationStatus.cancelled) {
55
+ return true;
56
+ }
57
+ return promiseResult;
58
+ });
59
+ }
60
+ return result;
61
+ }
62
+
63
+ // Runs navigation blockers on internal location update and "undoes" the location update
64
+ // if it should've been blocked.
65
+ //
66
+ // One could ask: Why the hassle of running navigation blockers on internal location update
67
+ // and then rewinding back if the location change should've been blocked?
68
+ // Why not just run navigation blockers on `.push()`/`.replace()`/`.shift()`?
69
+ //
70
+ // The reason why it runs on internal location update here is because
71
+ // aside from programmatic `.shift()` that can be initiated from the application code,
72
+ // there's non-programmatic "shift" navigation when the user manually clicks "Back" or "Forward" button
73
+ // in a web browser. And even if a "shift" navigation is initiated programmatically in the application code,
74
+ // it still doesn't know yet what the new location is gonna be cause it only knows the numeric `delta`.
75
+ // Such cases could only be handled by reacting to internal location updates which,
76
+ // in case of "Back"/"Forward", only happen after the URL in the browser's address bar has changed.
77
+ //
78
+ // There's no real drawback in reacting to an internal location update "post factum" because
79
+ // from the application code's point of view, web browser's address bar doesn't matter and even doesn't exist.
80
+ // All that exists from the application code's point of view is the `navigationStack.current` location
81
+ // returned from the `NavigationStack`. Until that location is updated, the "old" page is still rendered.
82
+ // The appliation is only concerned with the updates of the internal `location` object in the `NavigationStack``
83
+ // and completely ignores any updates to the URL in the web browser's address bar.
84
+ //
85
+ // So here, the code attempts to prevent or allow navigation that has already happened
86
+ // in the web browser's address bar but hasn't yet happened in the `NavigationStack`'s state.
87
+ // For example, it could be a user clicking a "Back"/"Forward" button in a web browser.
88
+ // If such navigation should've been blocked, it will simply not update the `location` object
89
+ // in the `NavigationStack`'s state, and it will also "rewind" the change of the URL in the web browser's
90
+ // address bar so that it's consistent with the `location` in the `NavigationStack`'s state.
91
+ //
92
+ // Returns either a `boolean` value or a `Promise` that resolves to a `boolean` value:
93
+ // * `false` when navigation should not have been blocked and therefore was not "rewinded".
94
+ // * `true` when navigation should have been blocked and therefore was "rewinded".
95
+ // * `true` when it "rewinded" the navigation "just in case" and then started evaluating async blockers,
96
+ // but while doing that, next navigation already happened so this one is no longer relevant.
97
+ //
98
+ function blockNonProgrammaticNavigationIfRequired(toLocationInternal,
99
+ // `location` of type `LocationInternal`
100
+ session, doAndIgnoreLocationUpdates) {
101
+ // If there're no navigation blockers to run, don't do anything.
102
+ if ((0, _navigationBlockers.getNavigationBlockers)(session).length === 0) {
103
+ return false;
104
+ }
105
+
106
+ // If it was the initial page load or a redirect,
107
+ // it's not really a navigation that could be rolled back.
108
+ if (toLocationInternal.delta === 0) {
109
+ return false;
110
+ }
111
+ const result = (0, _navigationBlockers.runNavigationBlockers)((0, _navigationBlockers.getNavigationBlockers)(session), (0, _getLocationBaseFromLocation.default)((0, _getLocationFromInternalLocation.default)(toLocationInternal)), session.environment);
112
+
113
+ // If some navigation blocker returned a `Promise`.
114
+ if ((0, _isPromise.default)(result)) {
115
+ const evaluationStatus = createNavigationBlockersEvaluationStatus(session);
116
+
117
+ // While location blockers are running, rewind to the previous location.
118
+ doAndIgnoreLocationUpdates(() => {
119
+ session.shift(-toLocationInternal.delta);
120
+ });
121
+ return result.then(promiseResult => {
122
+ if (evaluationStatus.cancelled) {
123
+ return true;
124
+ }
125
+ if (promiseResult) {
126
+ // Navigation blocked.
127
+ // Already rewound to a previous location.
128
+ return true;
129
+ }
130
+ // Navigation not blocked.
131
+ // Rewind back to the new location.
132
+ doAndIgnoreLocationUpdates(() => {
133
+ session.shift(toLocationInternal.delta);
134
+ });
135
+ // Update the location.
136
+ return false;
137
+ });
138
+ }
139
+
140
+ // Navigation blockers did not return a `Promise`.
141
+ if (result) {
142
+ // Prevent the navigation: rewind to the previous location.
143
+ doAndIgnoreLocationUpdates(() => {
144
+ session.shift(-toLocationInternal.delta);
145
+ });
146
+ return true;
147
+ }
148
+ // Update the location.
149
+ return false;
150
+ }
@@ -2,9 +2,9 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.default = parseInputLocation;
5
- var _createSearchFromQuery = _interopRequireDefault(require("./createSearchFromQuery"));
6
5
  var _parseLocationUrl = _interopRequireDefault(require("./parseLocationUrl"));
7
6
  var _parseQueryFromSearch = _interopRequireDefault(require("./parseQueryFromSearch"));
7
+ var _stringifyQueryAsSearch = _interopRequireDefault(require("./stringifyQueryAsSearch"));
8
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
9
  function stringifyQueryParameterValue(value) {
10
10
  if (value === null || value === undefined) {
@@ -45,7 +45,7 @@ function parseInputLocation(location) {
45
45
  // if `query` is present but `search` is not.
46
46
  if (location.query && !location.search) {
47
47
  location = Object.assign({}, location, {
48
- search: (0, _createSearchFromQuery.default)(location.query)
48
+ search: (0, _stringifyQueryAsSearch.default)(location.query)
49
49
  });
50
50
  }
51
51
 
@@ -2,14 +2,11 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.default = parseQueryFromSearch;
5
- var _queryString = require("query-string");
5
+ var _parseQueryString = _interopRequireDefault(require("./parseQueryString"));
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
6
7
  function parseQueryFromSearch(search) {
7
8
  if (search.length > '?'.length) {
8
- try {
9
- return (0, _queryString.parse)(search.slice(1));
10
- } catch (error) {
11
- // Ignore any query parsing errors.
12
- }
9
+ return (0, _parseQueryString.default)(search.slice('?'.length));
13
10
  }
14
11
  return {};
15
12
  }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = parseQueryString;
5
+ function splitAtFirstOccurence(string, separator) {
6
+ const separatorIndex = string.indexOf(separator);
7
+ if (separatorIndex === -1) {
8
+ return [string, ''];
9
+ }
10
+ return [string.slice(0, separatorIndex), string.slice(separatorIndex + separator.length)];
11
+ }
12
+ function decode(value) {
13
+ // There's a convention that a space character could be encoded
14
+ // either as "%20" or as "+". Both of them are valid.
15
+ // The "+" character is unusally preferred because it results in a more
16
+ // human-readable URL.
17
+ //
18
+ // https://dev.to/lico/understanding-how-spaces-are-encoded-20-with-encodeuri-vs-with-url-2d6c
19
+ // https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding
20
+ //
21
+ // Those "+" characters don't get transformed to spaces by `decodeURIComponent()` function.
22
+ // This means that they should be transformed to spaces manually.
23
+ //
24
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#decoding_query_parameters_from_a_url
25
+ //
26
+ value = value.replaceAll('+', ' ');
27
+
28
+ // `decodeURIComponent()` could throw an error of class `URIError`.
29
+ //
30
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError
31
+ //
32
+ // Example: "URIError: malformed URI sequence".
33
+ try {
34
+ return decodeURIComponent(value);
35
+ } catch (error) {
36
+ // eslint-disable-next-line no-console
37
+ console.error(error);
38
+ return value;
39
+ }
40
+ }
41
+ function parseQueryString(queryString) {
42
+ // Create an object with no prototype
43
+ const query = Object.create(null);
44
+
45
+ // query parameter parsing is described in the specification:
46
+ // https://url.spec.whatwg.org/#urlencoded-parsing
47
+ for (const keyValuePair of queryString.split('&')) {
48
+ if (!keyValuePair) {
49
+ continue;
50
+ }
51
+ let [key, value] = splitAtFirstOccurence(keyValuePair, '=');
52
+
53
+ // If `key` is empty, the specification considers this a valid case with `key: null`.
54
+ // But, there seems to be no practical use for a query parameter with `key: null`.
55
+ // So just skip it.
56
+ if (!key) {
57
+ continue;
58
+ }
59
+ key = decode(key);
60
+
61
+ // According to the specification, missing `=` should be treated as `value: null`.
62
+ if (value === '') {
63
+ value = null;
64
+ } else {
65
+ value = decode(value);
66
+ }
67
+
68
+ // The handling of duplicate URL query parameters is not explicitly defined by a single,
69
+ // universally enforced specification. Hence, we just assume such query parameters invalid
70
+ // and only include the first occurrence of the query parameter in the query string.
71
+ if (query[key] === undefined) {
72
+ query[key] = value;
73
+ }
74
+ }
75
+ return query;
76
+ }
77
+ module.exports = exports.default;
@@ -4,17 +4,18 @@ exports.__esModule = true;
4
4
  exports.default = void 0;
5
5
  var _constants = require("./constants");
6
6
  var _scheduleNextTick = _interopRequireDefault(require("./scheduleNextTick"));
7
- var _debug = _interopRequireDefault(require("../debug"));
8
7
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
8
  /* eslint-disable no-underscore-dangle */
10
9
 
11
10
  class ScrollPositionAutoSaver {
12
11
  constructor({
12
+ log,
13
13
  scrollPosition,
14
14
  scrollPositionSaver,
15
15
  getScrollableContainers,
16
16
  shouldSaveScrollPosition
17
17
  }) {
18
+ this._log = log;
18
19
  this._scrollPosition = scrollPosition;
19
20
  this._scrollPositionSaver = scrollPositionSaver;
20
21
  this._shouldSaveScrollPosition = shouldSaveScrollPosition;
@@ -66,7 +67,7 @@ class ScrollPositionAutoSaver {
66
67
  cancelSavePageScrollPosition(hasRun) {
67
68
  if (this._cancelSavePageScrollPosition) {
68
69
  if (!hasRun) {
69
- (0, _debug.default)('cancel delayed save scroll position', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
70
+ this._log.debug('cancel delayed save scroll position', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
70
71
  }
71
72
  this._cancelSavePageScrollPosition();
72
73
  this._cancelSavePageScrollPosition = null;
@@ -76,7 +77,7 @@ class ScrollPositionAutoSaver {
76
77
  const scrollableContainerEntry = this._getScrollableContainers()[scrollableContainerKey];
77
78
  if (scrollableContainerEntry.cancelSaveScrollPosition) {
78
79
  if (!hasRun) {
79
- (0, _debug.default)('cancel delayed save scroll position', scrollableContainerKey);
80
+ this._log.debug('cancel delayed save scroll position', scrollableContainerKey);
80
81
  }
81
82
  scrollableContainerEntry.cancelSaveScrollPosition();
82
83
  scrollableContainerEntry.cancelSaveScrollPosition = null;
@@ -108,9 +109,9 @@ class ScrollPositionAutoSaver {
108
109
  // because there might be too many in a given short period of time
109
110
  // which could affect the performance of the application.
110
111
  if (!scrollableContainerEntry.cancelSaveScrollPosition) {
111
- (0, _debug.default)('scroll detected', scrollableContainerKey);
112
+ this._log.debug('scroll detected', scrollableContainerKey);
112
113
  scrollableContainerEntry.cancelSaveScrollPosition = (0, _scheduleNextTick.default)(() => {
113
- (0, _debug.default)('auto-save scroll position after scroll', scrollableContainerKey);
114
+ this._log.debug('auto-save scroll position after scroll', scrollableContainerKey);
114
115
  this._scrollPositionSaver.saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainerEntry.scrollableContainer);
115
116
  });
116
117
  }
@@ -119,7 +120,7 @@ class ScrollPositionAutoSaver {
119
120
  addPageScrollListener() {
120
121
  // Set up scroll listener on the page.
121
122
  this._removePageScrollListener = this._scrollPosition.addPageScrollListener(() => {
122
- (0, _debug.default)('scroll detected', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
123
+ this._log.debug('scroll detected', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
123
124
 
124
125
  // This flag is not used in real life and is only used in tests (for some reason).
125
126
  if (!this._shouldSaveScrollPosition()) {