@esportsplus/routing 0.0.16 → 0.0.17

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.
@@ -1,3 +1,3 @@
1
- import { Middleware, Request } from '../types';
2
- declare const _default: <R>(request: Request) => Middleware<Request, R>;
1
+ import { Request, Responder } from '../types';
2
+ declare const _default: <R>(request: Request<R>) => R | Promise<R>;
3
3
  export default _default;
@@ -2,11 +2,9 @@ import factory from '@esportsplus/middleware';
2
2
  import dispatch from './dispatch';
3
3
  import match from './match';
4
4
  declare const _default: {
5
- dispatch: <R>(request: import("../types").Request) => import("@esportsplus/middleware/build/types").Middleware<import("../types").Request, R>;
5
+ dispatch: <R>(request: import("../types").Request<R>) => R | Promise<R>;
6
6
  factory: <I, R_1>(...middleware: import("@esportsplus/middleware/build/types").Middleware<I, R_1>[]) => import("@esportsplus/middleware/build/types").Next<I, R_1>;
7
- match: <R_2>(router: import("../router").Router, { spa }?: {
8
- spa?: boolean | undefined;
9
- }) => import("@esportsplus/middleware/build/types").Middleware<import("../types").Request, R_2>;
7
+ match: <R_2>(router: import("../router").Router<R_2>, subdomain?: string | undefined) => import("../types").Middleware<R_2>;
10
8
  };
11
9
  export default _default;
12
10
  export { dispatch, factory, match };
@@ -1,5 +1,3 @@
1
- import { Middleware, Request, Router } from '../types';
2
- declare const _default: <R>(router: Router, { spa }?: {
3
- spa?: boolean | undefined;
4
- }) => Middleware<Request, R>;
1
+ import { Middleware, Router } from '../types';
2
+ declare const _default: <R>(router: Router<R>, subdomain?: string) => Middleware<R>;
5
3
  export default _default;
@@ -1,21 +1,21 @@
1
- export default (router, { spa } = {}) => {
2
- let subdomain = null;
1
+ export default (router, subdomain) => {
3
2
  return (request, next) => {
4
- if ((typeof request.subdomain !== 'string' && !spa) || subdomain === null) {
3
+ let match = subdomain || request.subdomain;
4
+ if (match === undefined) {
5
5
  if (router.subdomains) {
6
6
  for (let i = 0, n = router.subdomains.length; i < n; i++) {
7
7
  if (!request.hostname.startsWith(router.subdomains[i])) {
8
8
  continue;
9
9
  }
10
- subdomain = router.subdomains[i];
10
+ match = router.subdomains[i];
11
11
  break;
12
12
  }
13
13
  }
14
- if (subdomain === null) {
15
- subdomain = '';
14
+ if (match === undefined) {
15
+ match = '';
16
16
  }
17
17
  }
18
- let { parameters, route } = router.match(request.method, request.path, request.subdomain || subdomain);
18
+ let { parameters, route } = router.match(request.method, request.path, match);
19
19
  request.data.parameters = parameters;
20
20
  request.data.route = route;
21
21
  return next(request);
@@ -1,26 +1,26 @@
1
1
  import { Options } from '../types';
2
2
  import { Node } from './node';
3
3
  import { Route } from './route';
4
- declare class Router {
5
- groups: Omit<Options, 'responder'>[];
6
- root: Node;
7
- routes: Record<string, Route>;
8
- static: Record<string, Route>;
4
+ declare class Router<R> {
5
+ groups: Omit<Options<R>, 'responder'>[];
6
+ root: Node<R>;
7
+ routes: Record<string, Route<R>>;
8
+ static: Record<string, Route<R>>;
9
9
  subdomains: string[] | null;
10
10
  constructor();
11
11
  private add;
12
12
  private route;
13
- delete(options: Options): this;
14
- get(options: Options): this;
15
- group(options: Router['groups'][0]): {
16
- routes: (fn: (router: Router) => void) => void;
13
+ delete(options: Options<R>): this;
14
+ get(options: Options<R>): this;
15
+ group(options: Router<R>['groups'][0]): {
16
+ routes: (fn: (router: Router<R>) => void) => void;
17
17
  };
18
- match(method: string, path: string, subdomain?: string | null): ReturnType<Node['find']>;
19
- on(methods: string[], options: Options): this;
20
- post(options: Options): this;
21
- put(options: Options): this;
18
+ match(method: string, path: string, subdomain?: string | null): ReturnType<Node<R>['find']>;
19
+ on(methods: string[], options: Options<R>): this;
20
+ post(options: Options<R>): this;
21
+ put(options: Options<R>): this;
22
22
  uri(name: string, values?: unknown[]): string;
23
23
  }
24
- declare const _default: () => Router;
24
+ declare const _default: <R>() => Router<R>;
25
25
  export default _default;
26
26
  export { Router, Route };
@@ -1,8 +1,19 @@
1
1
  import { STATIC } from '../constants';
2
2
  import { Node } from './node';
3
- import { normalize, radixkey } from './path';
4
3
  import { Route } from './route';
5
4
  let { isArray } = Array;
5
+ function normalize(path) {
6
+ if (path[0] !== '/') {
7
+ path = '/' + path;
8
+ }
9
+ if (path[path.length - 1] === '/') {
10
+ path = path.slice(0, -1);
11
+ }
12
+ return path || '/';
13
+ }
14
+ function radixkey(method, path, subdomain) {
15
+ return ((subdomain ? subdomain + ' ' : '') + method).toUpperCase() + ' ' + normalize(path);
16
+ }
6
17
  function set(route, key, value) {
7
18
  if (!value) {
8
19
  return;
@@ -78,9 +89,11 @@ class Router {
78
89
  };
79
90
  }
80
91
  match(method, path, subdomain) {
81
- let key = radixkey(path, { method, subdomain });
92
+ let key = radixkey(method, path, subdomain);
82
93
  if (key in this.static) {
83
- return { route: this.static[key] };
94
+ return {
95
+ route: this.static[key]
96
+ };
84
97
  }
85
98
  return this.root.find(key);
86
99
  }
@@ -94,10 +107,7 @@ class Router {
94
107
  }
95
108
  if (route.path) {
96
109
  for (let i = 0, n = methods.length; i < n; i++) {
97
- let key = radixkey(route.path, {
98
- method: methods[i],
99
- subdomain: route.subdomain
100
- });
110
+ let key = radixkey(methods[i], route.path, route.subdomain);
101
111
  if (key.indexOf('?:') !== -1) {
102
112
  let segments = key.split('?:'), url = '';
103
113
  for (let i = 0, n = segments.length; i < n; i++) {
@@ -1,17 +1,17 @@
1
1
  import { Route } from './index';
2
- declare class Node {
3
- children: Map<string | number, Node> | null;
4
- parent: Node | null;
2
+ declare class Node<R> {
3
+ children: Map<string | number, Node<R>> | null;
4
+ parent: Node<R> | null;
5
5
  path: string | null;
6
6
  property: string | null;
7
- route: Route | null;
7
+ route: Route<R> | null;
8
8
  type: number | null;
9
- constructor(parent?: Node['parent']);
10
- add(path: string, route: Route): Node;
9
+ constructor(parent?: Node<R>['parent']);
10
+ add(path: string, route: Route<R>): Node<R>;
11
11
  find(path: string): {
12
12
  parameters?: Record<PropertyKey, unknown>;
13
- route?: Route;
13
+ route?: Route<R>;
14
14
  };
15
- remove(path: string): Node | undefined;
15
+ remove(path: string): void;
16
16
  }
17
17
  export { Node };
@@ -20,12 +20,12 @@ class Node {
20
20
  }
21
21
  node.children.set(segment, (child = new Node(node)));
22
22
  if (symbol === ':') {
23
- child.property = segment.slice(1) || `${unnamed++}`;
23
+ child.property = (segment.slice(1) || unnamed++).toString();
24
24
  node.children.set(PLACEHOLDER, child);
25
25
  type = null;
26
26
  }
27
27
  else if (symbol === '*') {
28
- child.property = segment.slice(2) || `${unnamed++}`;
28
+ child.property = (segment.slice(2) || unnamed++).toString();
29
29
  node.children.set(WILDCARD, child);
30
30
  type = null;
31
31
  }
@@ -79,15 +79,15 @@ class Node {
79
79
  return;
80
80
  }
81
81
  }
82
- if (!node?.children?.size) {
83
- let parent = node.parent;
84
- if (parent && parent.children) {
85
- parent.children.delete(segments[segments.length - 1]);
86
- parent.children.delete(WILDCARD);
87
- parent.children.delete(PLACEHOLDER);
88
- }
82
+ if (node.children?.size) {
83
+ return;
84
+ }
85
+ let parent = node.parent;
86
+ if (parent && parent.children) {
87
+ parent.children.delete(segments[segments.length - 1]);
88
+ parent.children.delete(WILDCARD);
89
+ parent.children.delete(PLACEHOLDER);
89
90
  }
90
- return node;
91
91
  }
92
92
  }
93
93
  export { Node };
@@ -1,13 +1,12 @@
1
- import { Middleware, Responder } from '../types';
2
- import { factory } from '../middleware';
3
- declare class Route {
4
- dispatch: ReturnType<typeof factory> | null;
1
+ import { Middleware, Next, Responder } from '../types';
2
+ declare class Route<R> {
3
+ dispatch: Next<R> | null;
5
4
  name: string | null;
6
5
  path: string | null;
7
- responder: Responder;
8
- stack: Middleware<unknown, unknown>[] | null;
6
+ responder: Responder<R>;
7
+ stack: Middleware<R>[] | null;
9
8
  subdomain: string | null;
10
- constructor(responder: Responder);
11
- get dispatcher(): import("@esportsplus/middleware/build/types").Next<unknown, unknown>;
9
+ constructor(responder: Responder<R>);
10
+ get dispatcher(): Next<R>;
12
11
  }
13
12
  export { Route };
@@ -11,7 +11,7 @@ class Route {
11
11
  }
12
12
  get dispatcher() {
13
13
  if (this.dispatch === null) {
14
- if (!this.stack?.length) {
14
+ if (this.stack === null) {
15
15
  this.dispatch = (request) => this.responder(request);
16
16
  }
17
17
  else {
package/build/spa.d.ts CHANGED
@@ -1,16 +1,14 @@
1
- import { Router } from './types';
2
- type Cache<T = Record<string, unknown>> = {
3
- factory: () => T;
4
- state: T;
5
- };
6
- declare const _default: (router: Router, fn?: Cache['factory']) => {
7
- back: () => void;
8
- forward: () => void;
1
+ import { Request, Router } from './types';
2
+ declare function back(): void;
3
+ declare function forward(): void;
4
+ declare const _default: <R>(router: Router<R>) => {
5
+ back: typeof back;
6
+ forward: typeof forward;
9
7
  redirect: (path: string, { state, values }: {
10
8
  state?: Record<PropertyKey, unknown> | undefined;
11
9
  values?: unknown[] | undefined;
12
10
  }) => void;
13
- request: Record<string, unknown>;
11
+ request: Request<unknown>;
14
12
  uri: (path: string, values?: unknown[]) => string;
15
13
  };
16
14
  export default _default;
package/build/spa.js CHANGED
@@ -1,6 +1,18 @@
1
- let cache = [];
2
- function request(url = window?.location?.href || '') {
3
- let { hash, hostname, href, origin, port, protocol } = new URL(url), path = hash?.replace('#/', '/')?.split('?') || ['/', ''];
1
+ let state = request();
2
+ function back() {
3
+ window.history.back();
4
+ }
5
+ function forward() {
6
+ window.history.forward();
7
+ }
8
+ function normalize(uri) {
9
+ if (uri[0] === '/') {
10
+ return '#' + uri;
11
+ }
12
+ return uri;
13
+ }
14
+ function request() {
15
+ let { hash, hostname, href, origin, port, protocol } = new URL(window?.location?.href || ''), path = hash?.replace('#/', '/')?.split('?') || ['/', ''];
4
16
  return {
5
17
  data: {},
6
18
  href,
@@ -14,43 +26,25 @@ function request(url = window?.location?.href || '') {
14
26
  };
15
27
  }
16
28
  function update() {
17
- for (let i = 0, n = cache.length; i < n; i++) {
18
- let { factory, state } = cache[i], values = factory();
19
- for (let key in values) {
20
- state[key] = values[key];
21
- }
29
+ let values = request();
30
+ for (let key in values) {
31
+ state[key] = values[key];
22
32
  }
23
33
  }
24
- const back = () => window.history.back();
25
- const forward = () => window.history.forward();
26
- export default (router, fn = request) => {
27
- let state = {};
28
- cache.push({
29
- factory: fn,
30
- state
31
- });
32
- update();
34
+ export default (router) => {
33
35
  window.addEventListener('popstate', update);
34
36
  return {
35
37
  back,
36
38
  forward,
37
39
  redirect: (path, { state, values }) => {
38
- if (path.startsWith('http://') || path.startsWith('https://')) {
40
+ if (path.startsWith('https://') || path.startsWith('http://')) {
39
41
  return window.location.replace(path);
40
42
  }
41
- let uri = router.uri(path, values || []);
42
- if (uri[0] === '/') {
43
- uri = '#' + uri;
44
- }
45
- window.history.pushState(state || {}, '', uri);
43
+ window.history.pushState((state || {}), '', normalize(router.uri(path, values || [])));
46
44
  },
47
45
  request: state,
48
46
  uri: (path, values = []) => {
49
- let uri = router.uri(path, values || []);
50
- if (uri[0] === '/') {
51
- uri = '#' + uri;
52
- }
53
- return uri;
47
+ return normalize(router.uri(path, values || []));
54
48
  }
55
49
  };
56
50
  };
package/build/types.d.ts CHANGED
@@ -1,14 +1,16 @@
1
- import { Middleware, Next } from '@esportsplus/middleware';
1
+ import { Middleware as M, Next as N } from '@esportsplus/middleware';
2
2
  import { Route, Router } from './router';
3
- type Options = {
4
- middleware?: Middleware<Request, unknown>[];
3
+ type Middleware<R> = M<Request<R>, ReturnType<Responder<R>>>;
4
+ type Next<R> = N<Request<R>, ReturnType<Responder<R>>>;
5
+ type Options<R> = {
6
+ middleware?: Middleware<R>[];
5
7
  name?: string;
6
8
  path?: string;
7
- responder: Responder;
9
+ responder: Responder<R>;
8
10
  subdomain?: string;
9
11
  };
10
- type Request = {
11
- data: ReturnType<Router['match']> & Record<PropertyKey, unknown>;
12
+ type Request<R> = {
13
+ data: ReturnType<Router<R>['match']> & Record<PropertyKey, unknown>;
12
14
  href: string;
13
15
  hostname: string;
14
16
  method: string;
@@ -19,5 +21,5 @@ type Request = {
19
21
  query: Record<string, unknown>;
20
22
  subdomain?: string;
21
23
  };
22
- type Responder = <T, U>(request: T) => Promise<U> | U;
24
+ type Responder<R> = (request: Request<R>) => Promise<R> | R;
23
25
  export { Middleware, Next, Options, Request, Responder, Route, Router };
package/package.json CHANGED
@@ -16,5 +16,5 @@
16
16
  "prepublishOnly": "npm run build"
17
17
  },
18
18
  "types": "./build/index.d.ts",
19
- "version": "0.0.16"
19
+ "version": "0.0.17"
20
20
  }
@@ -1,12 +1,12 @@
1
- import { Middleware, Request } from '~/types';
1
+ import { Request, Responder } from '~/types';
2
2
 
3
3
 
4
- export default <R>(request: Request) => {
4
+ export default <R>(request: Request<R>): ReturnType<Responder<R>> => {
5
5
  let { route } = request.data;
6
6
 
7
7
  if (!route) {
8
8
  throw new Error(`Routing: route dispatching failed, route is undefined!`);
9
9
  }
10
10
 
11
- return route.dispatcher(request) as Middleware<Request, R>;
11
+ return route.dispatcher(request);
12
12
  };
@@ -1,28 +1,28 @@
1
- import { Middleware, Request, Router } from '~/types';
1
+ import { Middleware, Router } from '~/types';
2
2
 
3
3
 
4
- export default <R>(router: Router, { spa }: { spa?: boolean } = {}): Middleware<Request, R> => {
5
- let subdomain: string | null = null;
6
-
4
+ export default <R>(router: Router<R>, subdomain?: string): Middleware<R> => {
7
5
  return (request, next) => {
8
- if ((typeof request.subdomain !== 'string' && !spa) || subdomain === null) {
6
+ let match = subdomain || request.subdomain;
7
+
8
+ if (match === undefined) {
9
9
  if (router.subdomains) {
10
10
  for (let i = 0, n = router.subdomains.length; i < n; i++) {
11
11
  if (!request.hostname.startsWith(router.subdomains[i])) {
12
12
  continue;
13
13
  }
14
14
 
15
- subdomain = router.subdomains[i];
15
+ match = router.subdomains[i];
16
16
  break;
17
17
  }
18
18
  }
19
19
 
20
- if (subdomain === null) {
21
- subdomain = '';
20
+ if (match === undefined) {
21
+ match = '';
22
22
  }
23
23
  }
24
24
 
25
- let { parameters, route } = router.match(request.method, request.path, request.subdomain || subdomain);
25
+ let { parameters, route } = router.match(request.method, request.path, match);
26
26
 
27
27
  request.data.parameters = parameters;
28
28
  request.data.route = route;
@@ -1,14 +1,29 @@
1
1
  import { STATIC } from '~/constants';
2
2
  import { Options } from '~/types';
3
3
  import { Node } from './node';
4
- import { normalize, radixkey } from './path';
5
4
  import { Route } from './route';
6
5
 
7
6
 
8
7
  let { isArray } = Array;
9
8
 
10
9
 
11
- function set(route: Route, key: keyof Route, value?: unknown) {
10
+ function normalize(path: string) {
11
+ if (path[0] !== '/') {
12
+ path = '/' + path;
13
+ }
14
+
15
+ if (path[path.length - 1] === '/') {
16
+ path = path.slice(0, -1);
17
+ }
18
+
19
+ return path || '/';
20
+ }
21
+
22
+ function radixkey(method: string, path: string, subdomain?: string | null) {
23
+ return ((subdomain ? subdomain + ' ' : '') + method).toUpperCase() + ' ' + normalize(path);
24
+ }
25
+
26
+ function set<R>(route: Route<R>, key: keyof Route<R>, value?: unknown) {
12
27
  if (!value) {
13
28
  return;
14
29
  }
@@ -29,11 +44,11 @@ function set(route: Route, key: keyof Route, value?: unknown) {
29
44
  }
30
45
 
31
46
 
32
- class Router {
33
- groups: Omit<Options, 'responder'>[] = [];
34
- root: Node;
35
- routes: Record<string, Route> = {};
36
- static: Record<string, Route> = {};
47
+ class Router<R> {
48
+ groups: Omit<Options<R>, 'responder'>[] = [];
49
+ root: Node<R>;
50
+ routes: Record<string, Route<R>> = {};
51
+ static: Record<string, Route<R>> = {};
37
52
  subdomains: string[] | null = null;
38
53
 
39
54
 
@@ -42,7 +57,7 @@ class Router {
42
57
  }
43
58
 
44
59
 
45
- private add(radixkey: string, route: Route) {
60
+ private add(radixkey: string, route: Route<R>) {
46
61
  if (radixkey.indexOf(':') === -1 || this.root.add(radixkey, route).type === STATIC) {
47
62
  if (this.static[radixkey]) {
48
63
  throw new Error(`Routing: static path '${radixkey}' is already in use`);
@@ -54,7 +69,7 @@ class Router {
54
69
  return this;
55
70
  }
56
71
 
57
- private route({ middleware, name, path, responder, subdomain }: Options) {
72
+ private route({ middleware, name, path, responder, subdomain }: Options<R>) {
58
73
  let route = new Route(responder);
59
74
 
60
75
  for (let i = 0, n = this.groups.length; i < n; i++) {
@@ -83,19 +98,19 @@ class Router {
83
98
  }
84
99
 
85
100
 
86
- delete(options: Options) {
101
+ delete(options: Options<R>) {
87
102
  this.on(['DELETE'], options);
88
103
  return this;
89
104
  }
90
105
 
91
- get(options: Options) {
106
+ get(options: Options<R>) {
92
107
  this.on(['GET'], options);
93
108
  return this;
94
109
  }
95
110
 
96
- group(options: Router['groups'][0]) {
111
+ group(options: Router<R>['groups'][0]) {
97
112
  return {
98
- routes: (fn: (router: Router) => void) => {
113
+ routes: (fn: (router: Router<R>) => void) => {
99
114
  this.groups.push(options);
100
115
  fn(this);
101
116
  this.groups.pop();
@@ -103,17 +118,19 @@ class Router {
103
118
  }
104
119
  }
105
120
 
106
- match(method: string, path: string, subdomain?: string | null): ReturnType<Node['find']> {
107
- let key = radixkey(path, { method, subdomain });
121
+ match(method: string, path: string, subdomain?: string | null): ReturnType<Node<R>['find']> {
122
+ let key = radixkey(method, path, subdomain);
108
123
 
109
124
  if (key in this.static) {
110
- return { route: this.static[key] };
125
+ return {
126
+ route: this.static[key]
127
+ };
111
128
  }
112
129
 
113
130
  return this.root.find(key);
114
131
  }
115
132
 
116
- on(methods: string[], options: Options) {
133
+ on(methods: string[], options: Options<R>) {
117
134
  let route = this.route(options);
118
135
 
119
136
  if (route.name) {
@@ -126,10 +143,7 @@ class Router {
126
143
 
127
144
  if (route.path) {
128
145
  for (let i = 0, n = methods.length; i < n; i++) {
129
- let key = radixkey(route.path, {
130
- method: methods[i],
131
- subdomain: route.subdomain
132
- });
146
+ let key = radixkey(methods[i], route.path, route.subdomain);
133
147
 
134
148
  if (key.indexOf('?:') !== -1) {
135
149
  let segments = key.split('?:'),
@@ -157,12 +171,12 @@ class Router {
157
171
  return this;
158
172
  }
159
173
 
160
- post(options: Options) {
174
+ post(options: Options<R>) {
161
175
  this.on(['POST'], options);
162
176
  return this;
163
177
  }
164
178
 
165
- put(options: Options) {
179
+ put(options: Options<R>) {
166
180
  this.on(['PUT'], options);
167
181
  return this;
168
182
  }
@@ -205,5 +219,5 @@ class Router {
205
219
  }
206
220
 
207
221
 
208
- export default () => new Router();
222
+ export default <R>() => new Router<R>();
209
223
  export { Router, Route };
@@ -2,28 +2,28 @@ import { PLACEHOLDER, STATIC, WILDCARD } from '~/constants';
2
2
  import { Route } from './index';
3
3
 
4
4
 
5
- class Node {
6
- children: Map<string | number, Node> | null = null;
7
- parent: Node | null = null;
5
+ class Node<R> {
6
+ children: Map<string | number, Node<R>> | null = null;
7
+ parent: Node<R> | null = null;
8
8
  path: string | null = null;
9
9
  property: string | null = null;
10
- route: Route | null = null;
10
+ route: Route<R> | null = null;
11
11
  type: number | null = null;
12
12
 
13
13
 
14
- constructor(parent: Node['parent'] = null) {
14
+ constructor(parent: Node<R>['parent'] = null) {
15
15
  this.parent = parent;
16
16
  }
17
17
 
18
18
 
19
- add(path: string, route: Route) {
20
- let node: Node | undefined = this,
19
+ add(path: string, route: Route<R>) {
20
+ let node: Node<R> | undefined = this,
21
21
  segments = path.split('/'),
22
- type: Node['type'] = STATIC,
22
+ type: Node<R>['type'] = STATIC,
23
23
  unnamed = 0;
24
24
 
25
25
  for (let i = 0, n = segments.length; i < n; i++) {
26
- let child: Node | undefined = node.children?.get(segments[i]);
26
+ let child: Node<R> | undefined = node.children?.get(segments[i]);
27
27
 
28
28
  if (!child) {
29
29
  let segment = segments[i],
@@ -33,17 +33,17 @@ class Node {
33
33
  node.children = new Map();
34
34
  }
35
35
 
36
- node.children.set(segment, (child = new Node(node)));
36
+ node.children.set(segment, (child = new Node<R>(node)));
37
37
 
38
38
  // Named property
39
39
  if (symbol === ':') {
40
- child.property = segment.slice(1) || `${unnamed++}`;
40
+ child.property = (segment.slice(1) || unnamed++).toString();
41
41
  node.children.set(PLACEHOLDER, child);
42
42
  type = null;
43
43
  }
44
44
  // "*:" Wildcard property
45
45
  else if (symbol === '*') {
46
- child.property = segment.slice(2) || `${unnamed++}`;
46
+ child.property = (segment.slice(2) || unnamed++).toString();
47
47
  node.children.set(WILDCARD, child);
48
48
  type = null;
49
49
  }
@@ -61,12 +61,12 @@ class Node {
61
61
 
62
62
  find(path: string): {
63
63
  parameters?: Record<PropertyKey, unknown>;
64
- route?: Route;
64
+ route?: Route<R>;
65
65
  } {
66
- let node: Node | undefined = this,
66
+ let node: Node<R> | undefined = this,
67
67
  parameters: Record<PropertyKey, unknown> = {},
68
68
  segments = path.split('/'),
69
- wildcard: { node: Node, value: string } | null = null;
69
+ wildcard: { node: Node<R>, value: string } | null = null;
70
70
 
71
71
  for (let i = 0, n = segments.length; i < n; i++) {
72
72
  let segment = segments[i],
@@ -80,7 +80,7 @@ class Node {
80
80
  }
81
81
 
82
82
  // Exact matches take precedence over placeholders
83
- let next: Node | undefined = node.children?.get(segment);
83
+ let next: Node<R> | undefined = node.children?.get(segment);
84
84
 
85
85
  if (next) {
86
86
  node = next;
@@ -112,7 +112,7 @@ class Node {
112
112
  }
113
113
 
114
114
  remove(path: string) {
115
- let node: Node | undefined = this,
115
+ let node: Node<R> | undefined = this,
116
116
  segments = path.split('/');
117
117
 
118
118
  for (let i = 0, n = segments.length; i < n; i++) {
@@ -123,17 +123,17 @@ class Node {
123
123
  }
124
124
  }
125
125
 
126
- if (!node?.children?.size) {
127
- let parent = node.parent;
128
-
129
- if (parent && parent.children) {
130
- parent.children.delete( segments[segments.length - 1] );
131
- parent.children.delete(WILDCARD);
132
- parent.children.delete(PLACEHOLDER);
133
- }
126
+ if (node.children?.size) {
127
+ return;
134
128
  }
135
129
 
136
- return node;
130
+ let parent = node.parent;
131
+
132
+ if (parent && parent.children) {
133
+ parent.children.delete( segments[segments.length - 1] );
134
+ parent.children.delete(WILDCARD);
135
+ parent.children.delete(PLACEHOLDER);
136
+ }
137
137
  }
138
138
  }
139
139
 
@@ -1,25 +1,25 @@
1
- import { Middleware, Responder } from '~/types';
1
+ import { Middleware, Next, Responder } from '~/types';
2
2
  import { factory } from '~/middleware';
3
3
 
4
4
 
5
- class Route {
6
- dispatch: ReturnType<typeof factory> | null = null;
5
+ class Route<R> {
6
+ dispatch: Next<R> | null = null;
7
7
  name: string | null = null;
8
8
  path: string | null = null;
9
- responder: Responder;
10
- stack: Middleware<unknown, unknown>[] | null = null;
9
+ responder: Responder<R>;
10
+ stack: Middleware<R>[] | null = null;
11
11
  subdomain: string | null = null;
12
12
 
13
13
 
14
- constructor(responder: Responder) {
14
+ constructor(responder: Responder<R>) {
15
15
  this.responder = responder;
16
16
  }
17
17
 
18
18
 
19
19
  get dispatcher() {
20
20
  if (this.dispatch === null) {
21
- if (!this.stack?.length) {
22
- this.dispatch = <T>(request: T) => this.responder(request);
21
+ if (this.stack === null) {
22
+ this.dispatch = (request) => this.responder(request);
23
23
  }
24
24
  else {
25
25
  this.dispatch = factory(...this.stack, (request => this.responder(request)));
package/src/spa.ts CHANGED
@@ -1,17 +1,27 @@
1
1
  import { Request, Router } from './types';
2
2
 
3
3
 
4
- type Cache<T = Record<string, unknown>> = {
5
- factory: () => T;
6
- state: T;
7
- };
4
+ let state = request();
8
5
 
9
6
 
10
- let cache: Cache[] = [];
7
+ function back() {
8
+ window.history.back();
9
+ }
10
+
11
+ function forward() {
12
+ window.history.forward();
13
+ }
14
+
15
+ function normalize(uri: string) {
16
+ if (uri[0] === '/') {
17
+ return '#' + uri;
18
+ }
11
19
 
20
+ return uri;
21
+ }
12
22
 
13
- function request(url: string = window?.location?.href || ''): Request {
14
- let { hash, hostname, href, origin, port, protocol } = new URL( url ),
23
+ function request<R>(): Request<R> {
24
+ let { hash, hostname, href, origin, port, protocol } = new URL( window?.location?.href || '' ),
15
25
  path = hash?.replace('#/', '/')?.split('?') || ['/', ''];
16
26
 
17
27
  return {
@@ -28,59 +38,31 @@ function request(url: string = window?.location?.href || ''): Request {
28
38
  }
29
39
 
30
40
  function update() {
31
- for (let i = 0, n = cache.length; i < n; i++) {
32
- let { factory, state } = cache[i],
33
- values = factory();
41
+ let values = request();
34
42
 
35
- for (let key in values) {
36
- state[key] = values[key];
37
- }
43
+ for (let key in values) {
44
+ // @ts-ignore
45
+ state[key] = values[key];
38
46
  }
39
47
  }
40
48
 
41
49
 
42
- const back = () => window.history.back();
43
-
44
- const forward = () => window.history.forward();
45
-
46
-
47
- export default (router: Router, fn: Cache['factory'] = request) => {
48
- let state = {} as ReturnType< typeof fn >;
49
-
50
- cache.push({
51
- factory: fn,
52
- state
53
- });
54
-
55
- update();
56
-
50
+ export default <R>(router: Router<R>) => {
57
51
  window.addEventListener('popstate', update);
58
52
 
59
53
  return {
60
54
  back,
61
55
  forward,
62
56
  redirect: (path: string, { state, values }: { state?: Record<PropertyKey, unknown>; values?: unknown[] }) => {
63
- if (path.startsWith('http://') || path.startsWith('https://')) {
57
+ if (path.startsWith('https://') || path.startsWith('http://')) {
64
58
  return window.location.replace(path);
65
59
  }
66
60
 
67
- let uri = router.uri(path, values || []);
68
-
69
- if (uri[0] === '/') {
70
- uri = '#' + uri;
71
- }
72
-
73
- window.history.pushState(state || {}, '', uri);
61
+ window.history.pushState( (state || {}), '', normalize(router.uri(path, values || [])) );
74
62
  },
75
63
  request: state,
76
64
  uri: (path: string, values: unknown[] = []) => {
77
- let uri = router.uri(path, values || []);
78
-
79
- if (uri[0] === '/') {
80
- uri = '#' + uri;
81
- }
82
-
83
- return uri;
65
+ return normalize( router.uri(path, values || []) );
84
66
  }
85
67
  };
86
68
  };
package/src/types.ts CHANGED
@@ -1,17 +1,21 @@
1
- import { Middleware, Next } from '@esportsplus/middleware';
1
+ import { Middleware as M, Next as N } from '@esportsplus/middleware';
2
2
  import { Route, Router } from './router';
3
3
 
4
4
 
5
- type Options = {
6
- middleware?: Middleware<Request, unknown>[];
5
+ type Middleware<R> = M<Request<R>, ReturnType<Responder<R>>>;
6
+
7
+ type Next<R> = N<Request<R>, ReturnType<Responder<R>>>;
8
+
9
+ type Options<R> = {
10
+ middleware?: Middleware<R>[];
7
11
  name?: string;
8
12
  path?: string;
9
- responder: Responder;
13
+ responder: Responder<R>;
10
14
  subdomain?: string;
11
15
  };
12
16
 
13
- type Request = {
14
- data: ReturnType<Router['match']> & Record<PropertyKey, unknown>;
17
+ type Request<R> = {
18
+ data: ReturnType<Router<R>['match']> & Record<PropertyKey, unknown>;
15
19
  href: string;
16
20
  hostname: string;
17
21
  method: string;
@@ -23,7 +27,7 @@ type Request = {
23
27
  subdomain?: string;
24
28
  };
25
29
 
26
- type Responder = <T, U>(request: T) => Promise<U> | U;
30
+ type Responder<R> = (request: Request<R>) => Promise<R> | R;
27
31
 
28
32
 
29
33
  export { Middleware, Next, Options, Request, Responder, Route, Router };
@@ -1,28 +0,0 @@
1
- const normalize = (path: string) => {
2
- if (path[0] !== '/') {
3
- path = '/' + path;
4
- }
5
-
6
- if (path.endsWith('/')) {
7
- path = path.slice(0, -1);
8
- }
9
-
10
- return path || '/';
11
- };
12
-
13
- const radixkey = (path: string, { method, subdomain }: { method?: string | null; subdomain?: string | null; } = {}) => {
14
- let prefix = '';
15
-
16
- if (subdomain) {
17
- prefix = subdomain + ' ';
18
- }
19
-
20
- if (method) {
21
- prefix += method + ' ';
22
- }
23
-
24
- return prefix.toUpperCase() + normalize(path);
25
- };
26
-
27
-
28
- export { normalize, radixkey };