crelte 0.4.8 → 0.5.0-alpha.2

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 (110) hide show
  1. package/dist/Crelte.d.ts +7 -6
  2. package/dist/Crelte.d.ts.map +1 -1
  3. package/dist/Crelte.js +5 -13
  4. package/dist/CrelteRequest.d.ts +9 -0
  5. package/dist/CrelteRequest.d.ts.map +1 -1
  6. package/dist/CrelteRequest.js +16 -2
  7. package/dist/blocks/Blocks.svelte +2 -2
  8. package/dist/blocks/Blocks.svelte.d.ts +3 -19
  9. package/dist/blocks/Blocks.svelte.d.ts.map +1 -1
  10. package/dist/cookies/ClientCookies.d.ts +0 -1
  11. package/dist/cookies/ClientCookies.d.ts.map +1 -1
  12. package/dist/cookies/ClientCookies.js +0 -1
  13. package/dist/cookies/ServerCookies.d.ts +1 -2
  14. package/dist/cookies/ServerCookies.d.ts.map +1 -1
  15. package/dist/cookies/ServerCookies.js +2 -6
  16. package/dist/cookies/index.d.ts +0 -2
  17. package/dist/cookies/index.d.ts.map +1 -1
  18. package/dist/graphql/GraphQl.d.ts +2 -2
  19. package/dist/graphql/GraphQl.d.ts.map +1 -1
  20. package/dist/index.d.ts +9 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +14 -6
  23. package/dist/init/InternalApp.d.ts +30 -0
  24. package/dist/init/InternalApp.d.ts.map +1 -0
  25. package/dist/init/InternalApp.js +71 -0
  26. package/dist/init/client.d.ts +0 -5
  27. package/dist/init/client.d.ts.map +1 -1
  28. package/dist/init/client.js +88 -75
  29. package/dist/init/crelte-vite-plugin.d.ts +5 -0
  30. package/dist/init/server.d.ts +0 -5
  31. package/dist/init/server.d.ts.map +1 -1
  32. package/dist/init/server.js +49 -20
  33. package/dist/init/shared.d.ts +7 -18
  34. package/dist/init/shared.d.ts.map +1 -1
  35. package/dist/init/shared.js +97 -154
  36. package/dist/init/svelteComponents.d.ts +3 -0
  37. package/dist/init/svelteComponents.d.ts.map +1 -0
  38. package/dist/init/svelteComponents.js +7 -0
  39. package/dist/loadData/Globals.d.ts +40 -33
  40. package/dist/loadData/Globals.d.ts.map +1 -1
  41. package/dist/loadData/Globals.js +99 -88
  42. package/dist/loadData/index.d.ts +3 -2
  43. package/dist/loadData/index.d.ts.map +1 -1
  44. package/dist/loadData/index.js +2 -0
  45. package/dist/plugins/Events.d.ts +11 -13
  46. package/dist/plugins/Events.d.ts.map +1 -1
  47. package/dist/plugins/Events.js +10 -3
  48. package/dist/routing/BaseRoute.d.ts +255 -0
  49. package/dist/routing/BaseRoute.d.ts.map +1 -0
  50. package/dist/routing/BaseRoute.js +349 -0
  51. package/dist/routing/BaseRouter.d.ts +210 -0
  52. package/dist/routing/BaseRouter.d.ts.map +1 -0
  53. package/dist/routing/BaseRouter.js +444 -0
  54. package/dist/routing/ClientRouter.d.ts +32 -0
  55. package/dist/routing/ClientRouter.d.ts.map +1 -0
  56. package/dist/routing/ClientRouter.js +259 -0
  57. package/dist/routing/LoadRunner.d.ts +39 -0
  58. package/dist/routing/LoadRunner.d.ts.map +1 -0
  59. package/dist/routing/{PageLoader.js → LoadRunner.js} +32 -20
  60. package/dist/routing/Request.d.ts +35 -3
  61. package/dist/routing/Request.d.ts.map +1 -1
  62. package/dist/routing/Request.js +64 -5
  63. package/dist/routing/Route.d.ts +24 -223
  64. package/dist/routing/Route.d.ts.map +1 -1
  65. package/dist/routing/Route.js +26 -315
  66. package/dist/routing/Router.d.ts +49 -73
  67. package/dist/routing/Router.d.ts.map +1 -1
  68. package/dist/routing/Router.js +85 -251
  69. package/dist/routing/ServerRouter.d.ts +23 -0
  70. package/dist/routing/ServerRouter.d.ts.map +1 -0
  71. package/dist/routing/ServerRouter.js +57 -0
  72. package/dist/routing/utils.d.ts +5 -0
  73. package/dist/routing/utils.d.ts.map +1 -1
  74. package/dist/routing/utils.js +39 -0
  75. package/dist/utils.d.ts +1 -0
  76. package/dist/utils.d.ts.map +1 -1
  77. package/dist/utils.js +3 -0
  78. package/package.json +7 -6
  79. package/src/Crelte.ts +12 -18
  80. package/src/CrelteRequest.ts +21 -2
  81. package/src/cookies/ClientCookies.ts +0 -2
  82. package/src/cookies/ServerCookies.ts +2 -7
  83. package/src/cookies/index.ts +0 -3
  84. package/src/graphql/GraphQl.ts +2 -1
  85. package/src/index.ts +17 -9
  86. package/src/init/InternalApp.ts +134 -0
  87. package/src/init/client.ts +104 -93
  88. package/src/init/crelte-vite-plugin.d.ts +5 -0
  89. package/src/init/server.ts +67 -35
  90. package/src/init/shared.ts +107 -227
  91. package/src/init/svelteComponents.ts +12 -0
  92. package/src/loadData/Globals.ts +121 -102
  93. package/src/loadData/index.ts +3 -2
  94. package/src/plugins/Events.ts +40 -42
  95. package/src/routing/BaseRoute.ts +422 -0
  96. package/src/routing/BaseRouter.ts +528 -0
  97. package/src/routing/ClientRouter.ts +329 -0
  98. package/src/routing/{PageLoader.ts → LoadRunner.ts} +43 -30
  99. package/src/routing/Request.ts +97 -12
  100. package/src/routing/Route.ts +56 -376
  101. package/src/routing/Router.ts +100 -359
  102. package/src/routing/ServerRouter.ts +78 -0
  103. package/src/routing/utils.ts +53 -0
  104. package/src/utils.ts +4 -0
  105. package/dist/routing/InnerRouter.d.ts +0 -113
  106. package/dist/routing/InnerRouter.d.ts.map +0 -1
  107. package/dist/routing/InnerRouter.js +0 -417
  108. package/dist/routing/PageLoader.d.ts +0 -36
  109. package/dist/routing/PageLoader.d.ts.map +0 -1
  110. package/src/routing/InnerRouter.ts +0 -498
@@ -1,9 +1,8 @@
1
1
  import CrelteRequest from '../CrelteRequest.js';
2
2
  import { isGraphQlQuery, type GraphQlQuery } from '../graphql/GraphQl.js';
3
3
  import type Globals from './Globals.js';
4
- import type { Global } from './Globals.js';
5
4
 
6
- export type { Globals, Global };
5
+ export type { Globals };
7
6
 
8
7
  export interface LoadDataFn<A1 = any> {
9
8
  (cr: CrelteRequest, entryOrBlock: A1, ...args: any[]): Promise<any> | any;
@@ -137,6 +136,8 @@ export async function callLoadData<A1 = any>(
137
136
  /**
138
137
  * Spread the data of two loadData functions.
139
138
  *
139
+ * Prefer to use the array syntax.
140
+ *
140
141
  * ## Example
141
142
  * ```
142
143
  * export const loadData = mergeLoadData(
@@ -1,5 +1,5 @@
1
- import { EntryQueryVars } from '../entry/index.js';
2
1
  import { CrelteRequest, Entry } from '../index.js';
2
+ import { Route } from '../routing/index.js';
3
3
 
4
4
  export default class Events {
5
5
  inner: Map<string, Set<any>>;
@@ -11,35 +11,41 @@ export default class Events {
11
11
  /**
12
12
  * Listens for an event.
13
13
  *
14
+ * ## beforeRequest
15
+ * Please prefer to return not return a promise only if you need to wait
16
+ * for something. This allows a push request to be done without a microtask.
17
+ * Allowing for a better DX.
18
+ *
14
19
  * @returns a function to remove the listener
15
20
  */
16
21
  // override this function to add your own function signatures
22
+ on(
23
+ ev: 'beforeRequest',
24
+ fn: (cr: CrelteRequest) => Promise<void> | void,
25
+ ): () => void;
17
26
  on(
18
27
  ev: 'loadGlobalData',
19
28
  fn: (cr: CrelteRequest) => Promise<any>,
20
29
  ): () => void;
30
+ on(
31
+ ev: 'afterLoadEntry',
32
+ fn: (cr: CrelteRequest) => Promise<any>,
33
+ ): () => void;
21
34
  on(
22
35
  ev: 'loadData',
23
36
  fn: (cr: CrelteRequest, entry: Entry) => Promise<any>,
24
37
  ): () => void;
25
38
  on(ev: 'beforeRender', fn: (cr: CrelteRequest) => void): () => void;
26
- on(
27
- ev: 'beforeQueryEntry',
28
- fn: (cr: CrelteRequest, vars: EntryQueryVars) => Promise<void> | void,
29
- ): () => void;
30
- on(
31
- ev: 'queryEntry',
32
- fn: (
33
- cr: CrelteRequest,
34
- vars: EntryQueryVars | null,
35
- /** this might contain other plugin calls */
36
- runQuery: (vars: EntryQueryVars | null) => Promise<Entry | null>,
37
- ) => Promise<Entry | null>,
38
- ): () => void;
39
- on(
40
- ev: 'afterQueryEntry',
41
- fn: (cr: CrelteRequest, entry: Entry) => Promise<void> | void,
42
- ): () => void;
39
+ // todo maybe add this
40
+ // on(
41
+ // ev: 'loadEntry',
42
+ // fn: (
43
+ // cr: CrelteRequest,
44
+ // vars: EntryQueryVars | null,
45
+ // /** this might contain other plugin calls */
46
+ // runQuery: (vars: EntryQueryVars | null) => Promise<Entry | null>,
47
+ // ) => Promise<Entry | null>,
48
+ // ): () => void;
43
49
  on(ev: string, fn: (...args: any[]) => any): () => void {
44
50
  let set = this.inner.get(ev);
45
51
  if (!set) {
@@ -75,19 +81,11 @@ export default class Events {
75
81
  /**
76
82
  * Trigger an event
77
83
  */
84
+ trigger(ev: 'beforeRequest', cr: CrelteRequest): (Promise<void> | void)[];
78
85
  trigger(ev: 'loadGlobalData', cr: CrelteRequest): Promise<any>[];
86
+ trigger(ev: 'afterLoadEntry', cr: CrelteRequest): Promise<any>[];
79
87
  trigger(ev: 'loadData', cr: CrelteRequest, entry: Entry): Promise<any>[];
80
- trigger(ev: 'beforeRender', cr: CrelteRequest): void[];
81
- trigger(
82
- ev: 'beforeQueryEntry',
83
- cr: CrelteRequest,
84
- vars: EntryQueryVars,
85
- ): void[];
86
- trigger(
87
- ev: 'afterQueryEntry',
88
- cr: CrelteRequest,
89
- entry: Entry,
90
- ): Promise<void>[];
88
+ trigger(ev: 'beforeRender', cr: CrelteRequest, route: Route): void[];
91
89
  trigger(ev: string, ...args: any[]): any[] {
92
90
  const set = this.inner.get(ev);
93
91
  if (!set) return [];
@@ -95,17 +93,17 @@ export default class Events {
95
93
  return Array.from(set).map(fn => fn(...args));
96
94
  }
97
95
 
98
- /**
99
- * Get all listeners for an event
100
- */
101
- getListeners(
102
- ev: 'queryEntry',
103
- ): ((
104
- cr: CrelteRequest,
105
- vars: EntryQueryVars | null,
106
- runQuery: (vars: EntryQueryVars | null) => Promise<Entry | null>,
107
- ) => Promise<Entry | null>)[];
108
- getListeners(ev: string): any[] {
109
- return Array.from(this.inner.get(ev) ?? []);
110
- }
96
+ // /**
97
+ // * Get all listeners for an event
98
+ // */
99
+ // // getListeners(
100
+ // // ev: 'queryEntry',
101
+ // // ): ((
102
+ // // cr: CrelteRequest,
103
+ // // vars: EntryQueryVars | null,
104
+ // // runQuery: (vars: EntryQueryVars | null) => Promise<Entry | null>,
105
+ // // ) => Promise<Entry | null>)[];
106
+ // getListeners(ev: string): any[] {
107
+ // return Array.from(this.inner.get(ev) ?? []);
108
+ // }
111
109
  }
@@ -0,0 +1,422 @@
1
+ import { objClone } from '../utils.js';
2
+ import Site from './Site.js';
3
+ import { trimSlashEnd } from './utils.js';
4
+
5
+ export type BaseRouteOptions = {
6
+ scrollY?: number;
7
+ index?: number;
8
+ origin?: RouteOrigin;
9
+ state?: Record<string, any>;
10
+ context?: Record<string, any>;
11
+ };
12
+
13
+ /**
14
+ * RouteOrigin represents the origin of a route.
15
+ * This type is non-exhaustive and might expand in the future.
16
+ *
17
+ * - `'init'`: is set on the first page load
18
+ * - `'manual'`: is set when a route is triggered manually via `Router.open`
19
+ * - `'click'`: is set when a route is triggered by a click event
20
+ * - `'pop'`: is set when a route is triggered by a popstate event (back/forward)
21
+ * - `'replace'`: is set when a route is replaced via `Router.replaceState`
22
+ * - `'push'`: is set when a route is pushed via `Router.pushState`
23
+ *
24
+ * ## Note
25
+ * `replace` and `push` will not call loadData
26
+ */
27
+ export type RouteOrigin =
28
+ | 'init'
29
+ | 'manual'
30
+ | 'click'
31
+ | 'pop'
32
+ | 'replace'
33
+ | 'push';
34
+
35
+ /**
36
+ * A Route contains information about the current page for example the url and
37
+ * the site
38
+ */
39
+ export default class BaseRoute {
40
+ /**
41
+ * The url of the route
42
+ */
43
+ url: URL;
44
+
45
+ /**
46
+ * The site of the route
47
+ *
48
+ * ## Note
49
+ * The site might not always match with the current route
50
+ * but be the site default site or one that matches the
51
+ * users language.
52
+ *
53
+ * If that is important call `route.siteMatches()` to verify
54
+ */
55
+ site: Site;
56
+
57
+ /**
58
+ * The scroll position of the current route
59
+ *
60
+ * ## Note
61
+ * This does not have to represent the current scroll position
62
+ * should more be used internally.
63
+ *
64
+ * It might be useful for a new request to specify the wanted
65
+ * scroll position
66
+ */
67
+ scrollY: number | null;
68
+
69
+ /**
70
+ * the position in the browser history of this route
71
+ * this allows to find out if we can go back
72
+ */
73
+ index: number;
74
+
75
+ /**
76
+ * The origin of this route, See [[RouteOrigin]]
77
+ */
78
+ origin: RouteOrigin;
79
+
80
+ /**
81
+ * @hidden
82
+ * State data that can be used to store additional information
83
+ */
84
+ _state: Record<string, any>;
85
+
86
+ /**
87
+ * @hidden
88
+ * Any data that should be passed to onRoute and onRequest handlers
89
+ * or exchanged between loadData's
90
+ * This context is not persistant and should be considered "valid"
91
+ * only for the current request / route
92
+ *
93
+ * ## Note
94
+ * Consider using state instead. This will not be cloned in the clone
95
+ * call so will always be the same object
96
+ */
97
+ _context: Record<string, any>;
98
+
99
+ /**
100
+ * Creates a new Route
101
+ */
102
+ constructor(url: string | URL, site: Site, opts: BaseRouteOptions = {}) {
103
+ this.url = new URL(url);
104
+
105
+ this.site = site;
106
+ this.scrollY = opts.scrollY ?? null;
107
+ this.index = opts.index ?? 0;
108
+ this.origin = opts.origin ?? 'manual';
109
+ this._state = opts.state ?? {};
110
+ this._context = opts.context ?? {};
111
+ }
112
+
113
+ /**
114
+ * Returns the uri of the route
115
+ *
116
+ * Never ends with a slash
117
+ *
118
+ * ## Example
119
+ * ```
120
+ * const site = _; // site with url https://example.com/fo
121
+ * const route = new Route('https://example.com/foo/bar/', site);
122
+ * console.log(route.uri); // '/bar'
123
+ *
124
+ * const site2 = _; // site with url https://example.com/other
125
+ * const route2 = new Route('https://example.com/foo/bar/?a=1', site2);
126
+ * console.log(route2.uri); // '/foo/bar'
127
+ * ```
128
+ */
129
+ get uri(): string {
130
+ if (this.siteMatches()) {
131
+ return trimSlashEnd(
132
+ this.url.pathname.substring(this.site.uri.length),
133
+ );
134
+ }
135
+
136
+ return trimSlashEnd(this.url.pathname);
137
+ }
138
+
139
+ /**
140
+ * Returns the base url of the route
141
+ *
142
+ * Never ends with a slash
143
+ *
144
+ * ## Example
145
+ * ```
146
+ * const site = _; // site with url https://example.com/foo
147
+ * const route = new Route('https://example.com/foo/bar/', null);
148
+ * console.log(route.baseUrl); // 'https://example.com/foo'
149
+ *
150
+ * const site2 = _; // site with url https://example.com/other
151
+ * const route2 = new Route('https://example.com/foo/bar/', site2);
152
+ * console.log(route2.baseUrl); // 'https://example.com'
153
+ * ```
154
+ */
155
+ get baseUrl(): string {
156
+ if (this.siteMatches()) return trimSlashEnd(this.site.url.href);
157
+
158
+ return this.url.origin;
159
+ }
160
+
161
+ /**
162
+ * Returns the search params
163
+ *
164
+ * ## Note
165
+ * You might also have a look at `getSearchParam` and `setSearchParam`
166
+ *
167
+ * ## Example
168
+ * ```
169
+ * const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
170
+ * console.log(route.search.get('a')); // '1'
171
+ * ```
172
+ */
173
+ get search(): URLSearchParams {
174
+ return this.url.searchParams;
175
+ }
176
+
177
+ /**
178
+ * Returns the hash of the route
179
+ *
180
+ * ## Example
181
+ * ```
182
+ * const route = new Route('https://example.com/foo/bar/#hash', null);
183
+ * console.log(route.hash); // '#hash'
184
+ * ```
185
+ */
186
+ get hash(): string {
187
+ return this.url.hash;
188
+ }
189
+
190
+ /**
191
+ * Set the hash of the route
192
+ *
193
+ * ## Example
194
+ * ```
195
+ * const route = new Route('https://example.com/foo/bar/', null);
196
+ * route.hash = '#hash';
197
+ * console.log(route.url.href); // 'https://example.com/foo/bar/#hash'
198
+ * ```
199
+ */
200
+ set hash(hash: string) {
201
+ this.url.hash = hash;
202
+ }
203
+
204
+ /**
205
+ * Checks if there are previous routes which would allow it to go back
206
+ */
207
+ canGoBack(): boolean {
208
+ return !!this.index;
209
+ }
210
+
211
+ /**
212
+ * Gets the search param
213
+ *
214
+ * ## Example
215
+ * ```
216
+ * const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
217
+ * console.log(route.getSearchParam('a')); // '1'
218
+ * ```
219
+ */
220
+ getSearchParam(key: string): string | null {
221
+ return this.search.get(key);
222
+ }
223
+
224
+ /**
225
+ * Sets the search param or removes it if the value is null, undefined or an
226
+ * empty string
227
+ *
228
+ * ## Example
229
+ * ```
230
+ * const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
231
+ * route.setSearchParam('a', '3');
232
+ * console.log(route.url.href); // 'https://example.com/foo/bar/?a=3&b=2'
233
+ *
234
+ * route.setSearchParam('a', null);
235
+ * console.log(route.url.href); // 'https://example.com/foo/bar/?b=2'
236
+ * ```
237
+ */
238
+ setSearchParam(key: string, value?: string | number | null) {
239
+ const deleteValue =
240
+ typeof value === 'undefined' ||
241
+ value === null ||
242
+ (typeof value === 'string' && value === '');
243
+
244
+ if (!deleteValue) {
245
+ this.search.set(key, value as string);
246
+ } else {
247
+ this.search.delete(key);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Returns a state value if it exists.
253
+ */
254
+ getState<T = any>(key: string): T | null {
255
+ return this._state[key] ?? null;
256
+ }
257
+
258
+ /**
259
+ * Sets a state value.
260
+ * If the value is null or undefined, the key will be removed.
261
+ *
262
+ * ## When to use state
263
+ * State is used to store additional information that persists across route changes.
264
+ * The State is only available in the client code since it is stored using window.history.
265
+ *
266
+ * Consider using setSearchParam instead to enable server side rendering.
267
+ */
268
+ setState<T>(key: string, value: T | null | undefined) {
269
+ if (typeof value === 'undefined' || value === null) {
270
+ delete this._state[key];
271
+ } else {
272
+ this._state[key] = value;
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Returns a context value if it exists.
278
+ */
279
+ getContext<T = any>(key: string): T | null {
280
+ return this._context[key] ?? null;
281
+ }
282
+
283
+ /**
284
+ * Sets a context value.
285
+ * If the value is null or undefined, the key will be removed.
286
+ *
287
+ * ## When to use context
288
+ * Context is used to pass data to onRoute and onRequest handlers or exchange data between loadData calls.
289
+ * This context is not persistent and should be considered valid only for the current request/route.
290
+ * The context is not cloned in the clone call and will be the same object.
291
+ */
292
+ setContext<T>(key: string, value: T | null | undefined) {
293
+ if (typeof value === 'undefined' || value === null) {
294
+ delete this._context[key];
295
+ } else {
296
+ this._context[key] = value;
297
+ }
298
+ }
299
+ /**
300
+ * Returns true if the route is in live preview mode
301
+ */
302
+ inLivePreview(): boolean {
303
+ return !!this.search.get('x-craft-live-preview');
304
+ }
305
+
306
+ /**
307
+ * Returns if the site matches the url
308
+ */
309
+ siteMatches(): boolean {
310
+ if (this.url.origin !== this.site.url.origin) return false;
311
+
312
+ // now we need to validate the pathname we should make sure both end with a slash
313
+ // todo can we do this better?
314
+
315
+ // make sure that urls like pathname: /abcbc and site: /abc don't match
316
+ return (this.url.pathname + '/').startsWith(
317
+ // uri never returns a slash at the end
318
+ this.site.uri + '/',
319
+ );
320
+ }
321
+
322
+ /**
323
+ * Checks if the route is equal to another route
324
+ *
325
+ * This checks the url but search params do not have to be in the
326
+ * same order
327
+ *
328
+ * ## Note
329
+ * This only check the url, not site or anything else.
330
+ */
331
+ eq(route: BaseRoute | null) {
332
+ return (
333
+ route &&
334
+ this.eqUrl(route) &&
335
+ this.eqSearch(route) &&
336
+ this.eqHash(route)
337
+ );
338
+ }
339
+
340
+ /**
341
+ * Checks if the route is equal to another route
342
+ *
343
+ * This does not check the search params or hash
344
+ */
345
+ eqUrl(route: BaseRoute | null) {
346
+ return (
347
+ route &&
348
+ this.url.pathname === route.url.pathname &&
349
+ this.url.origin === route.url.origin
350
+ );
351
+ }
352
+
353
+ /**
354
+ * Checks if the search params are equal to another route
355
+ */
356
+ eqSearch(route: BaseRoute | null) {
357
+ const searchEq = (a: URLSearchParams, b: URLSearchParams) => {
358
+ if (a.size !== b.size) return false;
359
+
360
+ a.sort();
361
+ b.sort();
362
+
363
+ const aEntries = Array.from(a.entries());
364
+ const bEntries = Array.from(b.entries());
365
+
366
+ return aEntries
367
+ .map((a, i) => [a, bEntries[i]])
368
+ .every(([[ak, av], [bk, bv]]) => ak === bk && av === bv);
369
+ };
370
+
371
+ return route && searchEq(this.search, route.search);
372
+ }
373
+
374
+ /**
375
+ * Checks if the hash is equal to another route
376
+ */
377
+ eqHash(route: BaseRoute | null) {
378
+ return route && this.hash === route.hash;
379
+ }
380
+
381
+ /**
382
+ * Create a copy of the Route
383
+ *
384
+ * ## Note
385
+ * This does not make a copy of the template or context
386
+ */
387
+ clone() {
388
+ return new BaseRoute(this.url.href, this.site, {
389
+ scrollY: this.scrollY ?? undefined,
390
+ index: this.index,
391
+ origin: this.origin,
392
+ state: objClone(this._state),
393
+ context: this._context,
394
+ });
395
+ }
396
+
397
+ /** @hidden */
398
+ _fillFromState(state: any) {
399
+ // todo should this be here?
400
+ // not better in the request?
401
+ if (typeof state?.route?.scrollY === 'number')
402
+ this.scrollY = state.route.scrollY;
403
+
404
+ if (typeof state?.route?.index === 'number')
405
+ this.index = state.route.index;
406
+
407
+ if (typeof state?.state === 'object' && state.state !== null) {
408
+ this._state = state.state;
409
+ }
410
+ }
411
+
412
+ /** @hidden */
413
+ _toState(): any {
414
+ return {
415
+ route: {
416
+ scrollY: this.scrollY,
417
+ index: this.index,
418
+ },
419
+ state: this._state,
420
+ };
421
+ }
422
+ }