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
@@ -0,0 +1,329 @@
1
+ import CrelteRequest from '../CrelteRequest.js';
2
+ import BaseRouter, { BaseRouterOptions } from './BaseRouter.js';
3
+ import { Request, RequestOptions } from './index.js';
4
+ import Route from './Route.js';
5
+ import { SiteFromGraphQl } from './Site.js';
6
+
7
+ export type ClientRouterOptions = {
8
+ preloadOnMouseOver: boolean;
9
+ } & BaseRouterOptions;
10
+
11
+ export default class ClientRouter extends BaseRouter {
12
+ private scrollDebounceTimeout: any | null;
13
+ private preloadOnMouseOver: boolean;
14
+
15
+ onError: (e: any) => void;
16
+
17
+ constructor(sites: SiteFromGraphQl[], opts: ClientRouterOptions) {
18
+ super(sites, opts);
19
+
20
+ this.scrollDebounceTimeout = null;
21
+ this.preloadOnMouseOver = opts.preloadOnMouseOver;
22
+ this.onError = () => {};
23
+ }
24
+
25
+ /**
26
+ * ## Throws
27
+ */
28
+ async init() {
29
+ this.listen();
30
+
31
+ // let's first try to load from the state
32
+ const req = this.targetToRequest(window.location.href);
33
+ req._fillFromState(window.history.state);
34
+
35
+ req.origin = 'init';
36
+ window.history.scrollRestoration = 'manual';
37
+
38
+ return await this.handleRequest(req, () => {});
39
+ }
40
+
41
+ /**
42
+ * Do not call this with origin push or replace
43
+ */
44
+ async openRequest(req: Request): Promise<Route | void> {
45
+ if (['push', 'replace'].includes(req.origin)) {
46
+ throw new Error('Do not use open with push or replace');
47
+ }
48
+
49
+ const current = this.route.get();
50
+ // store scrollY
51
+ if (current) {
52
+ // if the scrollY would still be updated we clear the timeout
53
+ // since we should have the latest scrollY
54
+ if (this.scrollDebounceTimeout) {
55
+ clearTimeout(this.scrollDebounceTimeout);
56
+ this.scrollDebounceTimeout = null;
57
+ }
58
+
59
+ // store the scroll position
60
+ const scrollY = window.scrollY;
61
+ if (typeof scrollY === 'number') {
62
+ current.scrollY = scrollY;
63
+ window.history.replaceState(current._toState(), '');
64
+ }
65
+ }
66
+
67
+ // if the domain of the current site is different than the domain of the
68
+ // new site we need to do a window.location.href call
69
+ if (
70
+ (current && current.url.origin !== req.url.origin) ||
71
+ // @ts-ignore
72
+ import.meta.env.SSR
73
+ ) {
74
+ window.location.href = req.url.href;
75
+ return;
76
+ }
77
+
78
+ req.index = (current?.index ?? 0) + 1;
79
+ return await this.handleRequestAndError(req, route => {
80
+ const url = route.url;
81
+ window.history.pushState(
82
+ route._toState(),
83
+ '',
84
+ url.pathname + url.search + url.hash,
85
+ );
86
+ });
87
+ }
88
+
89
+ async pushRequest(req: Request, _opts: RequestOptions = {}) {
90
+ const url = req.url;
91
+ // todo a push should also store the previous scrollY
92
+
93
+ let nReq = req;
94
+ if (req.scrollY === null) {
95
+ // if there is no scrollY stored we store the current scrollY
96
+ // since a push does not cause a scroll top
97
+ // todo: probably should refactor something probably
98
+ // should not be here
99
+ nReq = req.clone();
100
+ nReq.scrollY = window.scrollY;
101
+
102
+ // todo this does not work? nReq is never used
103
+ // why not assign it without a clone?
104
+ }
105
+
106
+ return await this.handleRequest(req, route => {
107
+ window.history.pushState(
108
+ route._toState(),
109
+ url.pathname + url.search + url.hash,
110
+ );
111
+ });
112
+ }
113
+
114
+ async replaceRequest(req: Request, _opts: RequestOptions = {}) {
115
+ const url = req.url;
116
+
117
+ let nReq = req;
118
+ if (req.scrollY === null) {
119
+ // if there is no scrollY stored we store the current scrollY
120
+ // since a replace does not cause a scrollTo and we wan't
121
+ // history back to work as intended
122
+ // todo: probably should refactor something probably
123
+ // should not be here
124
+ nReq = req.clone();
125
+ nReq.scrollY = window.scrollY;
126
+
127
+ // todo this does not work? nReq is never used
128
+ // why not assign it without a clone?
129
+ }
130
+
131
+ try {
132
+ return await this.handleRequest(req, () => {
133
+ window.history.replaceState(
134
+ req._toState(),
135
+ url.pathname + url.search + url.hash,
136
+ );
137
+ });
138
+ } catch (e) {
139
+ console.warn('replacing route failed', e);
140
+ throw e;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * This returns a route if it was handled else if an error occured
146
+ * or the request was cancelled returns void
147
+ */
148
+ async handleRequestAndError(
149
+ req: Request,
150
+ updateHistory: (route: Route) => void,
151
+ ): Promise<Route | void> {
152
+ try {
153
+ return await this.handleRequest(req, updateHistory);
154
+ } catch (e) {
155
+ console.error('request failed', e);
156
+ this.onError(e);
157
+ }
158
+ }
159
+
160
+ listen() {
161
+ window.addEventListener('click', async e => {
162
+ // @ts-ignore
163
+ const link = e.target.closest('a');
164
+ const openInNewTab = e.metaKey || e.ctrlKey || e.shiftKey;
165
+ const saveLink = e.altKey;
166
+ if (!link || !link.href || openInNewTab || saveLink) return;
167
+ if (link.target.toLowerCase() === '_blank') return;
168
+ if (!link.href.startsWith('http')) return;
169
+
170
+ e.preventDefault();
171
+
172
+ const req = this.targetToRequest(link.href, { origin: 'click' });
173
+ const currRoute = this.route.get();
174
+ const routeEq =
175
+ currRoute && currRoute.eqUrl(req) && currRoute.eqSearch(req);
176
+
177
+ // this means the route is the same maybe with a different hash
178
+ // so it is not necessary to load the data again
179
+ if (routeEq) {
180
+ req.disableLoadData = true;
181
+ }
182
+
183
+ this.openRequest(req);
184
+ });
185
+
186
+ if (this.preloadOnMouseOver) {
187
+ let currentMouseOver: any = null;
188
+ window.addEventListener('mouseover', e => {
189
+ // @ts-ignore
190
+ const link = e.target.closest('a');
191
+
192
+ if (currentMouseOver && link === currentMouseOver) return;
193
+ if (link && link.target.toLowerCase() === '_blank') return;
194
+
195
+ if (
196
+ link &&
197
+ !link.hasAttribute('data-no-preload') &&
198
+ link.href
199
+ ) {
200
+ this.preload(link.href);
201
+ }
202
+
203
+ currentMouseOver = link;
204
+ });
205
+ }
206
+
207
+ // store the scrollY position every 200ms
208
+ // we can't do this at the time of the open call since the pop event
209
+ // has already changed to a new history state so we can't update our
210
+ // current/previous state
211
+ // eslint-disable-next-line no-constant-condition
212
+ if (true) {
213
+ window.addEventListener('scroll', () => {
214
+ const current = this.route.get();
215
+ if (!current) return;
216
+
217
+ // store the scroll position
218
+ current.scrollY = window.scrollY;
219
+
220
+ if (this.scrollDebounceTimeout) return;
221
+
222
+ // this might cause `Attempt to use history.replaceState() more than
223
+ // 100 times per 30 seconds` in safari
224
+ // since we wait a moment we should almost ever be fine
225
+ this.scrollDebounceTimeout = setTimeout(() => {
226
+ const routerRoute = this.route.get();
227
+ if (!routerRoute || !current.eq(routerRoute)) return;
228
+
229
+ // use the latest state
230
+ window.history.replaceState(routerRoute._toState(), '');
231
+
232
+ if (current.inLivePreview()) {
233
+ sessionStorage.setItem(
234
+ 'live-preview-scroll',
235
+ // use the latest scrollY
236
+ routerRoute.scrollY + '',
237
+ );
238
+ }
239
+
240
+ this.scrollDebounceTimeout = null;
241
+ }, 280);
242
+ });
243
+ }
244
+
245
+ window.addEventListener('popstate', async e => {
246
+ if (!e.state?.route) return;
247
+
248
+ const req = this.targetToRequest(window.location.href);
249
+ req._fillFromState(e.state);
250
+ req.origin = 'pop';
251
+
252
+ // todo handle errors
253
+ this.handleRequest(req, () => {});
254
+ });
255
+ }
256
+
257
+ updateScroll(cr: CrelteRequest, _route: Route): void {
258
+ const req = cr.req;
259
+
260
+ if (req.disableScroll) return;
261
+
262
+ // scroll to target
263
+ let scrollTo:
264
+ | { top: number; behavior: ScrollBehavior }
265
+ | { intoView: HTMLElement; behavior: ScrollBehavior }
266
+ | null = null;
267
+
268
+ // if the route is a live preview init and we have a scrollY stored
269
+ // scroll to that
270
+ if (req.inLivePreview()) {
271
+ const scrollY = sessionStorage.getItem('live-preview-scroll');
272
+ if (scrollY) {
273
+ scrollTo = {
274
+ top: parseInt(scrollY),
275
+ behavior: 'instant',
276
+ };
277
+ }
278
+ // if we have a hash and the route was not visited
279
+ } else if (
280
+ req.hash &&
281
+ ((req.origin === 'init' && typeof req.scrollY !== 'number') ||
282
+ req.origin === 'click')
283
+ ) {
284
+ const el = document.getElementById(req.hash.substring(1));
285
+ if (el) {
286
+ scrollTo = {
287
+ intoView: el,
288
+ behavior: 'smooth',
289
+ };
290
+ }
291
+ }
292
+
293
+ // restore scroll position
294
+ if (
295
+ !scrollTo &&
296
+ req.origin !== 'click' &&
297
+ typeof req.scrollY === 'number'
298
+ ) {
299
+ scrollTo = {
300
+ top: req.scrollY,
301
+ behavior: 'instant',
302
+ };
303
+ }
304
+
305
+ // make sure push and replace don't cause a scroll if it is not intended
306
+ if (!scrollTo && (req.origin === 'push' || req.origin === 'replace'))
307
+ return;
308
+
309
+ // scroll to the top if nothing else matches
310
+ if (!scrollTo) {
311
+ scrollTo = {
312
+ top: 0,
313
+ behavior: 'instant',
314
+ };
315
+ }
316
+
317
+ if ('top' in scrollTo) {
318
+ window.scrollTo({
319
+ top: scrollTo.top,
320
+ behavior: scrollTo.behavior,
321
+ });
322
+ } else {
323
+ scrollTo.intoView.scrollIntoView({
324
+ behavior: scrollTo.behavior,
325
+ block: 'start',
326
+ });
327
+ }
328
+ }
329
+ }
@@ -1,30 +1,28 @@
1
- import Request from './Request.js';
1
+ import { type CrelteRequest } from '../index.js';
2
2
 
3
- export type PageLoaderOptions = {
3
+ export type LoadRunnerOptions = {
4
4
  debugTiming: boolean;
5
5
  };
6
6
 
7
- export type LoadResponse = {
8
- success: boolean;
9
- data: any;
10
- };
11
-
12
- export type LoadFn = (req: Request, opts: LoadOptions) => Promise<any> | any;
7
+ export type LoadFn = (
8
+ cr: CrelteRequest,
9
+ opts: LoadOptions,
10
+ ) => Promise<void> | void;
13
11
 
14
12
  export type LoadOptions = {
15
13
  setProgress: (num: number) => void;
14
+ isCanceled: () => boolean;
16
15
  };
17
16
 
18
17
  /**
19
18
  * The PageLoader which is responsible for loading page Data
20
19
  */
21
- export default class PageLoader<More> {
20
+ export default class LoadRunner {
22
21
  private debugTiming: boolean;
23
22
  private preloadedUrls: Set<string>;
24
23
 
25
24
  private loadingVersion: number;
26
25
 
27
- onLoaded: (resp: LoadResponse, req: Request, more: More) => void;
28
26
  onProgress: (loading: boolean, progress?: number) => void;
29
27
  loadFn: LoadFn;
30
28
 
@@ -33,13 +31,12 @@ export default class PageLoader<More> {
33
31
  *
34
32
  * @param {Object} options `{debugTiming}`
35
33
  */
36
- constructor(options: PageLoaderOptions) {
34
+ constructor(options: LoadRunnerOptions) {
37
35
  this.debugTiming = options.debugTiming;
38
36
  this.preloadedUrls = new Set();
39
37
 
40
38
  this.loadingVersion = 0;
41
39
 
42
- this.onLoaded = () => null!;
43
40
  this.onProgress = () => null!;
44
41
  this.loadFn = () => null!;
45
42
  }
@@ -52,49 +49,65 @@ export default class PageLoader<More> {
52
49
  this.onProgress(false);
53
50
  }
54
51
 
55
- async load(req: Request, more: More) {
52
+ /**
53
+ * @returns true if the load was completed
54
+ *
55
+ * ## Throws
56
+ * if there was an error but not if the request
57
+ * was cancelled before
58
+ */
59
+ async load(req: CrelteRequest): Promise<boolean> {
56
60
  this.onProgress(true);
57
61
 
58
62
  const version = ++this.loadingVersion;
59
63
  const startTime = this.debugTiming ? Date.now() : null;
60
64
 
65
+ const isCanceled = () => this.loadingVersion !== version;
66
+
61
67
  const setProgress = (num: number) => {
62
- if (this.loadingVersion !== version) return;
68
+ if (isCanceled()) return;
63
69
 
64
70
  this.onProgress(true, num);
65
71
  };
66
72
 
67
- const resp: LoadResponse = { success: false, data: null };
73
+ // a function which should return the response
74
+ let resp: () => void;
68
75
  try {
69
- resp.data = await this.loadFn(req, { setProgress });
70
- resp.success = true;
76
+ const data = await this.loadFn(req, { isCanceled, setProgress });
77
+ resp = () => data;
71
78
  } catch (e) {
72
- resp.success = false;
73
- resp.data = e;
79
+ resp = () => {
80
+ throw e;
81
+ };
74
82
  }
75
83
 
84
+ if (isCanceled()) {
85
+ console.log('route changed quickly, ignoring response');
86
+ return false;
87
+ }
88
+
89
+ this.onProgress(false);
90
+
76
91
  if (startTime)
77
92
  console.log('page load took ' + (Date.now() - startTime) + 'ms');
78
93
 
79
- // if were the last that called loading, trigger the loaded event
80
- if (this.loadingVersion !== version)
81
- return console.log('route changed quickly, ignoring response');
82
-
83
- this.onProgress(false);
84
- this.onLoaded(resp, req, more);
94
+ return (resp(), true);
85
95
  }
86
96
 
87
97
  // you don't need to wait on this call
88
- async preload(req: Request) {
89
- const url = req.url.origin + req.url.pathname;
98
+ async preload(cr: CrelteRequest) {
99
+ const url = cr.req.url.origin + cr.req.url.pathname;
90
100
  if (this.preloadedUrls.has(url)) return;
91
101
 
92
102
  this.preloadedUrls.add(url);
93
103
 
94
104
  try {
95
- await this.loadFn(req, { setProgress: () => null });
96
- } catch (_e) {
97
- console.log('preload failed');
105
+ await this.loadFn(cr, {
106
+ isCanceled: () => false,
107
+ setProgress: () => null,
108
+ });
109
+ } catch (e) {
110
+ console.log('preload failed', e);
98
111
  // retry at another time
99
112
  this.preloadedUrls.delete(url);
100
113
  }
@@ -1,12 +1,17 @@
1
1
  import { Barrier } from 'crelte-std/sync';
2
- import Route, { RouteOrigin } from './Route.js';
3
2
  import Site from './Site.js';
4
3
  import { objClone } from '../utils.js';
4
+ import { Entry } from '../entry/index.js';
5
+ import BaseRoute, { RouteOrigin } from './BaseRoute.js';
6
+ import Route, { TemplateModule } from './Route.js';
5
7
 
6
8
  /**
7
9
  * Options to create a Request
8
10
  */
9
11
  export type RequestOptions = {
12
+ entry?: Entry;
13
+ template?: TemplateModule;
14
+ loadedData?: Record<string, any>;
10
15
  scrollY?: number;
11
16
  index?: number;
12
17
  origin?: RouteOrigin;
@@ -22,7 +27,22 @@ export type RequestOptions = {
22
27
  * you get a Request from the onRequest event or
23
28
  * in a loadGlobal function.
24
29
  */
25
- export default class Request extends Route {
30
+ export default class Request extends BaseRoute {
31
+ /**
32
+ * The entry of the request once loaded
33
+ */
34
+ entry: Entry | null;
35
+
36
+ /**
37
+ * The template module of the request once loaded
38
+ */
39
+ template: TemplateModule | null;
40
+
41
+ /**
42
+ * The loaded data of the request
43
+ */
44
+ loadedData: Record<string, any> | null;
45
+
26
46
  /**
27
47
  * Disable scrolling for this request
28
48
  * @default false
@@ -43,20 +63,29 @@ export default class Request extends Route {
43
63
  /** @hidden */
44
64
  _renderBarrier: RenderBarrier;
45
65
 
66
+ private _cancelled: boolean;
67
+
46
68
  /**
47
69
  * Create a new Request
48
70
  */
49
71
  constructor(url: string | URL, site: Site, opts: RequestOptions = {}) {
50
72
  super(url, site, opts);
51
73
 
74
+ this.entry = opts.entry ?? null;
75
+ this.template = opts.template ?? null;
76
+ this.loadedData = opts.loadedData ?? null;
52
77
  this.disableScroll = opts.disableScroll ?? false;
53
78
  this.disableLoadData = opts.disableLoadData ?? false;
54
79
  this.statusCode = opts.statusCode ?? null;
55
80
  this._renderBarrier = new RenderBarrier();
81
+ this._cancelled = false;
56
82
  }
57
83
 
58
84
  /**
59
85
  * Create a Request from a Route
86
+ *
87
+ * Clears the entry, template, and loadedData
88
+ * todo should it?
60
89
  */
61
90
  static fromRoute(route: Route, opts: RequestOptions = {}) {
62
91
  return new Request(route.url.href, route.site, {
@@ -69,6 +98,10 @@ export default class Request extends Route {
69
98
  });
70
99
  }
71
100
 
101
+ get cancelled(): boolean {
102
+ return this._cancelled;
103
+ }
104
+
72
105
  /**
73
106
  * With delayRender you can make sure that the render waits
74
107
  * until you are ready. This is useful for building page transitions.
@@ -103,9 +136,20 @@ export default class Request extends Route {
103
136
 
104
137
  /**
105
138
  * Create a copy of the request
139
+ *
140
+ * without including the entry, template, loadedData or cancelled state
141
+ *
142
+ * Is this what we wan't, maybe it should copy everything
106
143
  */
107
144
  clone() {
108
- return new Request(this.url.href, this.site, {
145
+ // todo should this clone the entry
146
+ // the route for example does not do it
147
+ //
148
+ // todo should this clone keep the entry?
149
+ const req = new Request(this.url.href, this.site, {
150
+ // entry: objClone(this.entry),
151
+ // template: this.template ?? undefined,
152
+ // loadedData: objClone(this.loadedData),
109
153
  scrollY: this.scrollY ?? undefined,
110
154
  index: this.index,
111
155
  origin: this.origin,
@@ -115,23 +159,55 @@ export default class Request extends Route {
115
159
  disableLoadData: this.disableLoadData,
116
160
  statusCode: this.statusCode ?? undefined,
117
161
  });
162
+
163
+ return req;
118
164
  }
119
165
 
120
166
  /**
121
167
  * Create a Route from the Request
168
+ *
169
+ * ## Throws
170
+ * if the entry, template or loadedData is missing
171
+ * or the request was cancelled
122
172
  */
123
173
  toRoute() {
124
- return new Route(this.url.href, this.site, {
125
- scrollY: this.scrollY ?? undefined,
126
- index: this.index,
127
- origin: this.origin,
128
- state: objClone(this._state),
129
- context: this._context,
130
- });
174
+ if (this.cancelled)
175
+ throw new Error(
176
+ 'cannot create a new route because it was cancelled',
177
+ );
178
+
179
+ if (!this.entry || !this.template || !this.loadedData)
180
+ throw new Error(
181
+ 'cannot create a new route because entry, template or loadedData is missing',
182
+ );
183
+
184
+ const route = new Route(
185
+ this.url.href,
186
+ this.site,
187
+ this.entry,
188
+ this.template,
189
+ this.loadedData,
190
+ {
191
+ scrollY: this.scrollY ?? undefined,
192
+ index: this.index,
193
+ origin: this.origin,
194
+ state: objClone(this._state),
195
+ context: this._context,
196
+ },
197
+ );
198
+ route.entryChanged = !this.disableLoadData;
199
+
200
+ return route;
131
201
  }
132
202
 
133
203
  /** @hidden */
134
204
  _updateOpts(opts: RequestOptions = {}) {
205
+ // todo maybe should check that if entry is updated
206
+ // template and loadedData is also updated
207
+
208
+ this.entry = opts.entry ?? this.entry;
209
+ this.template = opts.template ?? this.template;
210
+ this.loadedData = opts.loadedData ?? this.loadedData;
135
211
  this.scrollY = opts.scrollY ?? this.scrollY;
136
212
  this.index = opts.index ?? this.index;
137
213
  this.origin = opts.origin ?? this.origin;
@@ -141,6 +217,12 @@ export default class Request extends Route {
141
217
  this.disableLoadData = opts.disableLoadData ?? this.disableLoadData;
142
218
  this.statusCode = opts.statusCode ?? this.statusCode;
143
219
  }
220
+
221
+ /** @hidden */
222
+ _cancel() {
223
+ this._cancelled = true;
224
+ this._renderBarrier.cancel();
225
+ }
144
226
  }
145
227
 
146
228
  export function isRequest(req: any): req is Request {
@@ -178,15 +260,18 @@ class RenderBarrier {
178
260
 
179
261
  /** @hidden */
180
262
  cancel() {
263
+ // if the barrier is already open
264
+ // we don't wan't to flag the render as cancelled
265
+ // because it already happened
181
266
  if (this.inner.isOpen()) return;
182
267
 
183
268
  this.cancelled = true;
184
269
  this.root.remove();
185
270
  }
186
271
 
187
- // returns if the render was cancelled
272
+ // returns true if the render was cancelled
188
273
  /** @hidden */
189
- ready(): Promise<boolean> {
274
+ ready(): Promise<boolean> | boolean {
190
275
  return this.root.ready();
191
276
  }
192
277
  }