@tinkoff/router 0.1.65 → 0.1.69

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/README.md CHANGED
@@ -60,14 +60,35 @@ export const myGuard: NavigationGuard = async ({ to }) => {
60
60
  return false; // block this transition
61
61
  }
62
62
 
63
- if (to.config.redirect) {
64
- return '/login/'; // call a redirect to the specified page
63
+ if (typeof to.config.redirect === 'string') {
64
+ return to.config.redirect; // call a redirect to the specified page
65
+ }
66
+
67
+ if (typeof to.config.needAuth && !isAuth()) {
68
+ return { url: '/login/', code: '302' }; // redirect to page with NavigateOptions
65
69
  }
66
70
 
67
71
  // if nothing is returned, the transition will be performed as usual
68
72
  };
73
+
74
+ router.registerGuard(myGuard);
69
75
  ```
70
76
 
77
+ #### Rules
78
+
79
+ - guards are asynchronous and it execution will be awaited inside routing
80
+ - all guards are running in parallel and they are all awaited
81
+ - if several guars return something then the result from a guard that was registered early will be used
82
+
83
+ #### Possible result
84
+
85
+ The behaviour of routing depends on the result of executing guards functions and there result might be next:
86
+
87
+ - if all of the guards returns `undefined` than navigation will continue executing
88
+ - if any of the guards returns `false` than navigation is getting blocked and next action differs on server and client
89
+ - if any of the guards returns `string` it is considered as url to which redirect should be happen
90
+ - if any of the guards returns [`NavigateOptions`](#navigateoptions) interface, `url` property from it is considered as url to which redirect should be happen
91
+
71
92
  ### Transitions hooks
72
93
 
73
94
  Transition hooks allow you to perform your asynchronous actions at different stages of the transition.
@@ -78,8 +99,27 @@ import { NavigationHook } from '@tinkoff/router';
78
99
  export const myHook: NavigationHook = async ({ from, to, url, fromUrl }) => {
79
100
  console.log(`navigating from ${from} to route ${to}`);
80
101
  };
102
+
103
+ router.registerHook('beforeNavigate', myHook);
81
104
  ```
82
105
 
106
+ #### Rules
107
+
108
+ - all hooks from the same event are running in parallel
109
+ - most of the hooks are asynchronous and are awaited inside router
110
+ - if some error happens when running hook it will be logged to console but wont affect navigation (except for the `beforeResolve` hook - error for it will be rethrown)
111
+
112
+ #### List of available hooks
113
+
114
+ Async hooks:
115
+
116
+ - [navigate hooks](#navigate-hooks) - asynchronous hooks only for navigate calls
117
+ - [updateCurrentRoute hooks](#updatecurrentroute-hooks) - asynchronous hooks only for updateCurrentRoute calls
118
+
119
+ Sync hooks:
120
+
121
+ - `change` - runs when any of changes to current route\url happens
122
+
83
123
  ## API
84
124
 
85
125
  ### Getting data about the current route or url
@@ -102,12 +142,20 @@ router.navigate('/test');
102
142
  router.navigate({ url: './test', query: { a: '1' } });
103
143
  ```
104
144
 
105
- Transition hooks:
145
+ ##### navigate hooks
106
146
 
107
147
  - beforeResolve
108
148
  - beforeNavigate
109
149
  - afterNavigate
110
150
 
151
+ ##### navigate workflow
152
+
153
+ 1. `beforeResolve` hook
154
+ 2. [guards](#router-guards)
155
+ 3. `beforeNavigate`
156
+ 4. `change`
157
+ 5. `afterNavigate`
158
+
111
159
  #### updateCurrentRoute
112
160
 
113
161
  The transition is based on the current route (therefore this method cannot be called on the server) and allows you to simply update some data for the current page
@@ -117,11 +165,24 @@ router.updateCurrentRoute({ params: { id: 'abc' } });
117
165
  router.updateCurrentRoute({ query: { a: '1' } });
118
166
  ```
119
167
 
120
- Hooks:
168
+ ##### updateCurrentRoute hooks
121
169
 
122
170
  - beforeUpdateCurrent
123
171
  - afterUpdateCurrent
124
172
 
173
+ ##### updateCurrentRoute workflow
174
+
175
+ 1. `beforeUpdateCurrent`
176
+ 2. `change`
177
+ 3. `afterUpdateCurrent`
178
+
179
+ ### NavigateOptions
180
+
181
+ Object that allows to specify transition options both to [navigate](#navigate) and [updateCurrentRoute](#updatecurrentroute) transitions
182
+
183
+ - `code` - redirect code that is returned on server in case of redirects
184
+ - `navigateState` - any additional data that is stored with route
185
+
125
186
  ### Working with query
126
187
 
127
188
  #### query option
@@ -161,6 +222,11 @@ router.updateCurrentRoute({ query: { a: undefined, c: 'c' }, preserveQuery: true
161
222
  router.getCurrentUrl().query; // { b: 'b', c: 'c' }
162
223
  ```
163
224
 
225
+ ### Constructor options
226
+
227
+ - `trailingSlash` - do router should force all urls to end with slash. If `true` - force trailing slash for every path, `false` - force no trailing slash, `undefined` - trailing slash is specified by request and both trailing and not trailing slashes are used. By default value if `undefined`
228
+ - `mergeSlashes` - replace several consecutive slashes by single slashes (slashes after protocol are still be with `//` after protocol name). By default is `false` - no merge for slashes.
229
+
164
230
  ### Integration with React
165
231
 
166
232
  Library has some useful React hooks and components for working with routing
@@ -229,3 +295,26 @@ export const WrapLink = () => {
229
295
  return <Link url="/test/">Click me</Link>;
230
296
  };
231
297
  ```
298
+
299
+ ## How to
300
+
301
+ ### Load route config from external api
302
+
303
+ Use [transition hook](#transitions-hooks) `beforeResolve` and load routes config based on url.
304
+
305
+ ```ts
306
+ router.registerHook('beforeResolve', async (navigation) => {
307
+ const route = await routeResolve(navigation);
308
+
309
+ if (route) {
310
+ router.addRoute(routeTransform(route));
311
+ }
312
+ });
313
+ ```
314
+
315
+ ### App behind proxy
316
+
317
+ Router doesn't support proxy setup directly. But proxy still can be used with some limitations:
318
+
319
+ - setup proxy server to pass requests to app with rewriting request and response paths. (E.g. for [nginx](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect))
320
+ - it wont work as expected on spa navigation on client, so only option in this case is use the `NoSpaRouter`
@@ -220,17 +220,16 @@ class AbstractRouter {
220
220
  await this.runHooks('afterUpdateCurrent', navigation);
221
221
  }
222
222
  async navigate(navigateOptions) {
223
- return this.internalNavigate(navigateOptions, {});
223
+ return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
224
224
  }
225
225
  async internalNavigate(navigateOptions, { history }) {
226
226
  var _a;
227
- const options = typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions;
228
- const { url, replace, params, navigateState, code } = options;
227
+ const { url, replace, params, navigateState, code } = navigateOptions;
229
228
  const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
230
229
  if (!url && !prevNavigation) {
231
230
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
232
231
  }
233
- const resolvedUrl = this.resolveUrl(options);
232
+ const resolvedUrl = this.resolveUrl(navigateOptions);
234
233
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
235
234
  let navigation = {
236
235
  type: 'navigate',
package/lib/index.es.js CHANGED
@@ -220,17 +220,16 @@ class AbstractRouter {
220
220
  await this.runHooks('afterUpdateCurrent', navigation);
221
221
  }
222
222
  async navigate(navigateOptions) {
223
- return this.internalNavigate(navigateOptions, {});
223
+ return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
224
224
  }
225
225
  async internalNavigate(navigateOptions, { history }) {
226
226
  var _a;
227
- const options = typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions;
228
- const { url, replace, params, navigateState, code } = options;
227
+ const { url, replace, params, navigateState, code } = navigateOptions;
229
228
  const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
230
229
  if (!url && !prevNavigation) {
231
230
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
232
231
  }
233
- const resolvedUrl = this.resolveUrl(options);
232
+ const resolvedUrl = this.resolveUrl(navigateOptions);
234
233
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
235
234
  let navigation = {
236
235
  type: 'navigate',
@@ -682,13 +681,16 @@ class Router extends AbstractRouter {
682
681
  });
683
682
  return this.lastNavigation;
684
683
  }
685
- async run(navigation) {
686
- if (this.redirectCode) {
687
- return this.redirect(navigation, { url: navigation.url.path, code: this.redirectCode });
688
- }
684
+ async internalNavigate(navigateOptions, internalOptions) {
689
685
  // any navigation after initial should be considered as redirects
690
686
  if (this.getCurrentRoute()) {
691
- return this.redirect(navigation, { url: navigation.url.path, code: navigation.code });
687
+ return this.redirect(this.lastNavigation, navigateOptions);
688
+ }
689
+ return super.internalNavigate(navigateOptions, internalOptions);
690
+ }
691
+ async run(navigation) {
692
+ if (this.redirectCode) {
693
+ return this.redirect(navigation, { url: navigation.url.href, code: this.redirectCode });
692
694
  }
693
695
  await super.run(navigation);
694
696
  }
package/lib/index.js CHANGED
@@ -236,17 +236,16 @@ class AbstractRouter {
236
236
  await this.runHooks('afterUpdateCurrent', navigation);
237
237
  }
238
238
  async navigate(navigateOptions) {
239
- return this.internalNavigate(navigateOptions, {});
239
+ return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
240
240
  }
241
241
  async internalNavigate(navigateOptions, { history }) {
242
242
  var _a;
243
- const options = typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions;
244
- const { url, replace, params, navigateState, code } = options;
243
+ const { url, replace, params, navigateState, code } = navigateOptions;
245
244
  const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
246
245
  if (!url && !prevNavigation) {
247
246
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
248
247
  }
249
- const resolvedUrl = this.resolveUrl(options);
248
+ const resolvedUrl = this.resolveUrl(navigateOptions);
250
249
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
251
250
  let navigation = {
252
251
  type: 'navigate',
@@ -698,13 +697,16 @@ class Router extends AbstractRouter {
698
697
  });
699
698
  return this.lastNavigation;
700
699
  }
701
- async run(navigation) {
702
- if (this.redirectCode) {
703
- return this.redirect(navigation, { url: navigation.url.path, code: this.redirectCode });
704
- }
700
+ async internalNavigate(navigateOptions, internalOptions) {
705
701
  // any navigation after initial should be considered as redirects
706
702
  if (this.getCurrentRoute()) {
707
- return this.redirect(navigation, { url: navigation.url.path, code: navigation.code });
703
+ return this.redirect(this.lastNavigation, navigateOptions);
704
+ }
705
+ return super.internalNavigate(navigateOptions, internalOptions);
706
+ }
707
+ async run(navigation) {
708
+ if (this.redirectCode) {
709
+ return this.redirect(navigation, { url: navigation.url.href, code: this.redirectCode });
708
710
  }
709
711
  await super.run(navigation);
710
712
  }
@@ -45,7 +45,7 @@ export declare abstract class AbstractRouter {
45
45
  protected internalUpdateCurrentRoute(updateRouteOptions: UpdateCurrentRouteOptions, { history }: InternalOptions): Promise<void>;
46
46
  protected runUpdateCurrentRoute(navigation: Navigation): Promise<void>;
47
47
  navigate(navigateOptions: NavigateOptions | string): Promise<void>;
48
- protected internalNavigate(navigateOptions: NavigateOptions | string, { history }: InternalOptions): Promise<void>;
48
+ protected internalNavigate(navigateOptions: NavigateOptions, { history }: InternalOptions): Promise<void>;
49
49
  protected runNavigate(navigation: Navigation): Promise<void>;
50
50
  protected run(navigation: Navigation): Promise<void>;
51
51
  resolve(resolveOptions: NavigateOptions | string, options?: Parameters<AbstractRouter['resolveRoute']>[1]): import("../types").NavigationRoute;
@@ -8,6 +8,7 @@ export declare class Router extends AbstractRouter {
8
8
  constructor(options: Options);
9
9
  protected onRedirect: (navigation: Navigation) => Promise<void>;
10
10
  dehydrate(): Promise<Navigation>;
11
+ protected internalNavigate(navigateOptions: NavigateOptions, internalOptions: any): Promise<void>;
11
12
  protected run(navigation: Navigation): Promise<void>;
12
13
  protected redirect(navigation: Navigation, target: NavigateOptions): Promise<void>;
13
14
  protected notfound(navigation: Navigation): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinkoff/router",
3
- "version": "0.1.65",
3
+ "version": "0.1.69",
4
4
  "description": "router",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git@github.com:TinkoffCreditSystems/tramvai.git"
17
+ "url": "git@github.com:Tinkoff/tramvai.git"
18
18
  },
19
19
  "scripts": {
20
20
  "build": "tramvai-build --for-publish",