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,21 +1,8 @@
1
- import Route from './Route.js';
2
- import Site, { SiteFromGraphQl } from './Site.js';
3
- import InnerRouter from './InnerRouter.js';
4
- import PageLoader, { LoadFn, LoadResponse } from './PageLoader.js';
5
- import { ServerHistory } from './History.js';
6
- import { Readable, Writable } from 'crelte-std/stores';
7
- import { Listeners } from 'crelte-std/sync';
8
- import Request, { RequestOptions } from './Request.js';
9
-
10
- export type RouterOptions = {
11
- preloadOnMouseOver?: boolean;
12
- debugTiming?: boolean;
13
- };
14
-
15
- const defaultRouterOpts = {
16
- preloadOnMouseOver: false,
17
- debugTiming: false,
18
- };
1
+ import { Readable } from 'crelte-std/stores';
2
+ import BaseRouter from './BaseRouter.js';
3
+ import { Request, RequestOptions, Route, Site } from './index.js';
4
+ import { Entry } from '../entry/index.js';
5
+ import CrelteRequest from '../CrelteRequest.js';
19
6
 
20
7
  /**
21
8
  * Allows to easely modify a Request
@@ -29,156 +16,73 @@ const defaultRouterOpts = {
29
16
  */
30
17
  export type UpdateRequest = (req: Request) => boolean | null | undefined | void;
31
18
 
32
- type LoadedMore = {
33
- changeHistory: () => void;
34
- };
35
-
36
- /**
37
- * internal only
38
- */
39
- type Internal = {
40
- onLoaded: (
41
- success: boolean,
42
- req: Request,
43
- // call ready once your ready to update the dom
44
- // this makes sure we trigger a route and site update
45
- // almost at the same moment and probably the same tick
46
- // to make sure we don't have any flickering
47
- ready: () => any,
48
- ) => void;
49
-
50
- // onNothingLoaded get's called if the request did not load new Data
51
- // since maybe a push or replace was called
52
- onNothingLoaded: (
53
- req: Request,
54
- // call ready once your ready to update the dom
55
- // this makes sure we trigger a route and site update
56
- // almost at the same moment and probably the same tick
57
- // to make sure we don't have any flickering
58
- ready: () => void,
59
- ) => void;
60
-
61
- onLoad: LoadFn;
62
-
63
- domReady: (req: Request) => void;
64
-
65
- initClient: () => void;
66
-
67
- initServer: (url: string, acceptLang?: string) => Promise<ServerInited>;
68
- };
19
+ // Todo this router should be stateful like globals
20
+ // allow to reference the correct route or request
21
+ export default class Router {
22
+ private inner: BaseRouter;
23
+ private _request: Request | null;
69
24
 
70
- type ServerInited = {
71
- success: boolean;
72
- // redirect to the route url
73
- redirect: boolean;
74
- req: Request;
75
- props: any;
76
- };
25
+ constructor(inner: BaseRouter) {
26
+ this.inner = inner;
27
+ this._request = null;
28
+ }
77
29
 
78
- // Make sure route and nextRoute are not the same object as _inner.route
79
- export default class Router {
80
30
  /**
81
- * The current route
31
+ * returns a store with the current route
82
32
  *
83
33
  * ## Note
84
- * Will always contain a route expect in the first loadData call
34
+ * Will always contain a route except in the first loadData call this
35
+ * is intentional since you will get the wrong route in a loadData call.
36
+ * In a loadData you should always use the `CrelteRequest` provided
37
+ * in each loadData call.
85
38
  */
86
- private _route: Writable<Route | null>;
39
+ get route(): Readable<Route | null> {
40
+ return this.inner.route.readclone();
41
+ }
87
42
 
88
43
  /**
89
- * The current site
44
+ * returns a store with the current site
90
45
  *
91
46
  * ## Note
92
- * Will always contain a site expect in the first loadData call
93
- */
94
- private _site: Writable<Site | null>;
95
-
96
- // the next request, just here to destroy it
97
- private _request: Request | null;
98
-
99
- /**
100
- * The loading flag, specifies if a page is currently
101
- * getting loaded
102
- */
103
- private _loading: Writable<boolean>;
104
-
105
- /**
106
- * The loading progress, the value is between 0 and 1
47
+ * Will always contain a site except in the first loadData call this
48
+ * is intentional since you might get the wrong site if a site switch
49
+ * is happening and you call this in loadData. If possible use the CrelteRequest
50
+ * provided in each loadData call.
51
+ *
52
+ * Else use `router.site.get() ?? router.req.site`
107
53
  */
108
- private _loadingProgress: Writable<number>;
109
-
110
- private _onRequest: Listeners<[Request]>;
111
-
112
- /** @hidden */
113
- _internal: Internal;
114
-
115
- private inner: InnerRouter;
116
- private pageLoader: PageLoader<LoadedMore>;
117
-
118
- constructor(sites: SiteFromGraphQl[], opts: RouterOptions = {}) {
119
- opts = { ...defaultRouterOpts, ...opts };
120
-
121
- this.inner = new InnerRouter(sites, {
122
- preloadOnMouseOver: opts.preloadOnMouseOver!,
123
- });
124
- this.pageLoader = new PageLoader({
125
- debugTiming: opts.debugTiming!,
126
- });
127
-
128
- // in the first onRoute call we will update this value
129
- this._route = new Writable(null!);
130
- this._site = new Writable(null!);
131
- this._request = null;
132
- this._loading = new Writable(false);
133
- this._loadingProgress = new Writable(0);
134
-
135
- this._onRequest = new Listeners();
136
-
137
- // these functions are exposed to the init "module"
138
- // but should not be used by anybody else
139
- this._internal = {
140
- onLoaded: () => {},
141
- onNothingLoaded: () => {},
142
- onLoad: () => {},
143
- domReady: req => this.inner.domReady(req),
144
- initClient: () => this._initClient(),
145
- initServer: (url, acceptLang) => this._initServer(url, acceptLang),
146
- };
147
-
148
- this.inner.onRoute = (route, changeHistory) =>
149
- this._onRoute(route, changeHistory);
150
- this.inner.onPreload = route => this._onPreload(route);
151
-
152
- this.pageLoader.onLoaded = (resp, req, more) =>
153
- this._onLoaded(resp, req, more);
154
- this.pageLoader.loadFn = (req, opts) =>
155
- this._internal.onLoad(req, opts);
156
- this.pageLoader.onProgress = (loading, progress) =>
157
- this._onProgress(loading, progress);
54
+ get site(): Readable<Site | null> {
55
+ return this.inner.site.readonly();
158
56
  }
159
57
 
160
58
  /**
161
- * returns a store with the current route
59
+ * returns a store with the current entry
162
60
  *
163
61
  * ## Note
164
- * Will always contain a route expect in the first loadData call
165
- *
166
- * Consider to use CrelteRequest instead
62
+ * Will always contain an entry except in the first loadData call this
63
+ * is intentional since you might get the wrong entry if a request is happening
64
+ * and you call this in loadData. If possible use the CrelteRequest
65
+ * provided in each loadData call.
167
66
  */
168
- get route(): Readable<Route | null> {
169
- return this._route.readclone();
67
+ get entry(): Readable<Entry | null> {
68
+ return this.inner.entry.readonly();
170
69
  }
171
70
 
172
71
  /**
173
- * returns a store with the current site
174
- *
175
- * ## Note
176
- * Will always contain a site expect in the first loadData call
72
+ * returns the latest request in progress otherwise returns null.
177
73
  *
178
- * Consider to use CrelteRequest instead
74
+ * ## Important !!
75
+ * If at all possible prefer using the `CrelteRequest` provided in each
76
+ * loadData call. For example in a preload request this will return null.
77
+ * Or a user has clicked multiple times on different links you might get
78
+ * the url of the newer request.
179
79
  */
180
- get site(): Readable<Site | null> {
181
- return this._site.readonly();
80
+ get req(): Request | null {
81
+ // this._request is not used because that could be a weird
82
+ // behaviour for the user
83
+ // we will use that however internally
84
+ // todo maybe reconsider this?
85
+ return this.inner.request;
182
86
  }
183
87
 
184
88
  /**
@@ -192,14 +96,14 @@ export default class Router {
192
96
  * returns a store which indicates if the a page is loading
193
97
  */
194
98
  get loading(): Readable<boolean> {
195
- return this._loading.readonly();
99
+ return this.inner.loading.readonly();
196
100
  }
197
101
 
198
102
  /**
199
103
  * returns a store which indicates the loading progress between 0 and 1
200
104
  */
201
105
  get loadingProgress(): Readable<number> {
202
- return this._loadingProgress.readonly();
106
+ return this.inner.loadingProgress.readonly();
203
107
  }
204
108
 
205
109
  /**
@@ -223,23 +127,14 @@ export default class Router {
223
127
  * // the following page will be opened https://example.com/de/foo/bar
224
128
  * ```
225
129
  */
226
- open(
130
+ async open(
227
131
  target: string | URL | Route | Request | UpdateRequest,
228
132
  opts: RequestOptions = {},
229
- ) {
230
- const req = this.targetOrUpdateToRequest(target, opts, {
231
- origin: 'manual',
232
- });
133
+ ): Promise<Route | void> {
134
+ const req = this.targetOrUpdateToRequest(target, opts);
233
135
  if (!req) return;
234
136
 
235
- if (req === this._request) {
236
- throw new Error(
237
- 'Cannot open the same request object twice. Either clone the request ' +
238
- 'or just pass in the url.',
239
- );
240
- }
241
-
242
- this.inner.open(req);
137
+ return await this.inner.open(req);
243
138
  }
244
139
 
245
140
  /**
@@ -275,20 +170,14 @@ export default class Router {
275
170
  * router.push(route);
276
171
  * ```
277
172
  */
278
- push(route: Route | Request | UpdateRequest, opts: RequestOptions = {}) {
279
- // todo not sure if that is what we want?
280
- if (import.meta.env.SSR) return this.open(route, opts);
281
-
282
- // theoretically string and URL also work but we might
283
- // change that in the future
284
- const req = this.targetOrUpdateToRequest(route, opts, {
285
- origin: 'push',
286
- scrollY: opts.scrollY ?? undefined,
287
- disableLoadData: opts.disableLoadData ?? true,
288
- });
173
+ async push(
174
+ route: Route | Request | UpdateRequest,
175
+ opts: RequestOptions = {},
176
+ ) {
177
+ const req = this.targetOrUpdateToRequest(route, opts);
289
178
  if (!req) return;
290
179
 
291
- this.inner.push(req);
180
+ return await this.inner.push(req);
292
181
  }
293
182
 
294
183
  /**
@@ -331,20 +220,16 @@ export default class Router {
331
220
  * router.replace(route);
332
221
  * ```
333
222
  */
334
- replace(route: Route | Request | UpdateRequest, opts: RequestOptions = {}) {
335
- // todo not sure if that is what we want?
336
- if (import.meta.env.SSR) return this.open(route, opts);
337
-
338
- // theoretically string and URL also work but we might
339
- // change that in the future
340
- const req = this.targetOrUpdateToRequest(route, opts, {
341
- origin: 'replace',
342
- scrollY: opts.scrollY ?? undefined,
343
- disableLoadData: opts.disableLoadData ?? true,
344
- });
223
+ async replace(
224
+ route: Route | Request | UpdateRequest,
225
+ opts: RequestOptions = {},
226
+ ) {
227
+ const req = this.targetOrUpdateToRequest(route, opts);
345
228
  if (!req) return;
346
229
 
347
- this.inner.replace(req);
230
+ // we don't force disableLoadData here
231
+ // because the user might want to reload the data
232
+ return await this.inner.replace(req);
348
233
  }
349
234
 
350
235
  /**
@@ -357,16 +242,20 @@ export default class Router {
357
242
 
358
243
  /**
359
244
  * Checks if there are previous routes which would allow it to go back
245
+ *
246
+ * On the server this will always return false
360
247
  */
361
248
  canGoBack(): boolean {
362
- return this.inner.route?.canGoBack() ?? false;
249
+ return this.inner.canGoBack();
363
250
  }
364
251
 
365
252
  /**
366
253
  * Go back in the history
254
+ *
255
+ * On the server this throw an error
367
256
  */
368
257
  back() {
369
- this.inner.history.back();
258
+ this.inner.back();
370
259
  }
371
260
 
372
261
  /**
@@ -381,13 +270,12 @@ export default class Router {
381
270
  *
382
271
  * This will trigger every time a new route is set
383
272
  * and is equivalent to router.route.subscribe(fn)
384
- * expect that it will not trigger instantly
273
+ * except that it will not trigger instantly
385
274
  *
386
275
  * @returns a function to remove the listener
387
276
  */
388
277
  onRoute(fn: (route: Route) => void): () => void {
389
- let first = true;
390
- return this.route.subscribe(r => (first ? (first = false) : fn(r!)));
278
+ return this.inner.onRouteListeners.add(fn);
391
279
  }
392
280
 
393
281
  /**
@@ -397,8 +285,8 @@ export default class Router {
397
285
  *
398
286
  * @returns a function to remove the listener
399
287
  */
400
- onRequest(fn: (req: Request) => void): () => void {
401
- return this._onRequest.add(fn);
288
+ onRequest(fn: (req: CrelteRequest) => void): () => void {
289
+ return this.inner.onRequestListeners.add(fn);
402
290
  }
403
291
 
404
292
  /**
@@ -415,161 +303,6 @@ export default class Router {
415
303
  return this.inner.targetToRequest(target, opts);
416
304
  }
417
305
 
418
- private setNewRoute(route: Route) {
419
- this._route.setSilent(route);
420
- const siteChanged = this.site.get()?.id !== route.site.id;
421
- this._site.setSilent(route.site);
422
- this._route.notify();
423
- if (siteChanged) this._site.notify();
424
- }
425
-
426
- private async _initClient() {
427
- this.inner.initClient();
428
- }
429
-
430
- private async _initServer(
431
- url: string,
432
- acceptLang?: string,
433
- ): Promise<ServerInited> {
434
- this.inner.initServer();
435
-
436
- this._internal.onNothingLoaded = (_req, ready) => {
437
- ready();
438
- };
439
-
440
- const prom: Promise<ServerInited> = new Promise(resolve => {
441
- this._internal.onLoaded = (success, req, ready) => {
442
- const props = ready();
443
- this._internal.onLoaded = () => {};
444
-
445
- resolve({
446
- success,
447
- redirect: false,
448
- req,
449
- props,
450
- });
451
- };
452
- });
453
-
454
- const req = this.inner.targetToRequest(url);
455
- req.origin = 'init';
456
-
457
- // let's see if the url matches any route and site
458
- // if not let's redirect to the site which matches the acceptLang
459
- if (!req.siteMatches()) {
460
- const site = this.inner.siteByAcceptLang(acceptLang);
461
-
462
- return {
463
- success: true,
464
- redirect: true,
465
- req: new Request(site.url, site),
466
- props: {},
467
- };
468
- }
469
-
470
- this.inner.route = req.toRoute();
471
- this.inner.onRoute(req, () => {});
472
-
473
- const resp = await prom;
474
-
475
- const hist = this.inner.history as ServerHistory;
476
- if (hist.url || hist.req) {
477
- const nReq = this.inner.targetToRequest(hist.req ?? hist.url!);
478
- if (!req.eq(nReq)) {
479
- return {
480
- success: true,
481
- redirect: true,
482
- req: nReq,
483
- props: {},
484
- };
485
- }
486
- }
487
-
488
- return resp;
489
- }
490
-
491
- // gets called by the InnerRouter when a new route is requested
492
- private _onRoute(req: Request, changeHistory: () => void) {
493
- this.destroyRequest();
494
-
495
- this._request = req;
496
-
497
- const barrier = req._renderBarrier;
498
- if (barrier.isOpen()) {
499
- throw new Error('render barrier is already open');
500
- }
501
-
502
- this._onRequest.trigger(req);
503
-
504
- // route prepared
505
- if (!req.disableLoadData) {
506
- this.pageLoader.load(req, { changeHistory });
507
- } else {
508
- this.pageLoader.discard();
509
- this._onNothingLoaded(req, { changeHistory });
510
- }
511
- }
512
-
513
- private destroyRequest() {
514
- if (!this._request) return;
515
-
516
- this._request._renderBarrier.cancel();
517
- this._request = null;
518
- }
519
-
520
- private _onPreload(req: Request) {
521
- this.pageLoader.preload(req);
522
- }
523
-
524
- // gets called by the pageLoader when teh loadData completes
525
- private async _onLoaded(
526
- resp: LoadResponse,
527
- req: Request,
528
- more: LoadedMore,
529
- ) {
530
- // check if the render was cancelled
531
- if (await req._renderBarrier.ready()) return;
532
-
533
- // when the data is loaded let's update the route of the inner
534
- // this will only happen if no other route has been requested
535
- // in the meantime
536
- more.changeHistory();
537
-
538
- const route = req.toRoute();
539
-
540
- // call the client or server saying we are ready for a new render
541
- this._internal.onLoaded(resp.success, req, () => {
542
- this.setNewRoute(route);
543
- return resp.data;
544
- });
545
- }
546
-
547
- // this gets called if loadData is not called
548
- private async _onNothingLoaded(req: Request, more: LoadedMore) {
549
- // check if the render was cancelled
550
- if (await req._renderBarrier.ready()) return;
551
-
552
- // when the data is loaded let's update the route of the inner
553
- // this is will only happen if no other route has been requested
554
- // in the meantime
555
- more.changeHistory();
556
-
557
- const route = req.toRoute();
558
-
559
- // call the client or server saying there was an update in the route
560
- // but no new data was loaded so no render should happen
561
- this._internal.onNothingLoaded(req, () => {
562
- this.setNewRoute(route);
563
- });
564
- }
565
-
566
- // this is called by the pageLoader if we get a progress update
567
- private _onProgress(loading: boolean, progress?: number): void {
568
- if (this._loading.get() !== loading) this._loading.set(loading);
569
-
570
- if (typeof progress === 'number') this._loadingProgress.set(progress);
571
- }
572
-
573
306
  /**
574
307
  * Transforms a target to a request
575
308
  *
@@ -578,32 +311,40 @@ export default class Router {
578
311
  private targetOrUpdateToRequest(
579
312
  target: string | URL | Route | Request | UpdateRequest,
580
313
  opts: RequestOptions = {},
581
- forcedOpts: RequestOptions = {},
582
314
  ): Request | null {
583
315
  // we have an update request
584
316
  if (typeof target === 'function') {
585
- const route = this.route.get();
586
- if (!route) {
317
+ const source = this._request ?? this.route.get();
318
+ if (!source) {
319
+ // todo should we use the request here?
587
320
  throw new Error(
588
321
  'route to update missing in first loadData call. ' +
589
- 'Use cr.req.clone() instead',
322
+ 'Use `cr.router...` or `cr.req.clone()`',
590
323
  );
591
324
  }
592
325
 
593
326
  // first get a req
594
- const req = this.inner.targetToRequest(route, opts);
327
+ const req = this.inner.targetToRequest(source, opts);
595
328
  // check if the request was canceled by the update request
596
329
  if (target(req) === false) return null;
597
330
 
598
- // now we add the forcedOpts
599
- req._updateOpts(forcedOpts);
600
-
601
331
  return req;
602
332
  }
603
333
 
604
- return this.inner.targetToRequest(target, {
605
- ...opts,
606
- ...forcedOpts,
607
- });
334
+ return this.inner.targetToRequest(target, opts);
335
+ }
336
+
337
+ /**
338
+ * @hidden
339
+ * call this after creating a CrelteRequest
340
+ */
341
+ _toRequest(req: Request) {
342
+ const nRouter = new Router(this.inner);
343
+ nRouter._request = req;
344
+ return nRouter;
345
+ }
346
+
347
+ _requestCompleted() {
348
+ this._request = null;
608
349
  }
609
350
  }
@@ -0,0 +1,78 @@
1
+ import BaseRouter, { BaseRouterOptions } from './BaseRouter.js';
2
+ import { Request, RequestOptions } from './index.js';
3
+ import Route from './Route.js';
4
+ import Site, { SiteFromGraphQl } from './Site.js';
5
+
6
+ export default class ServerRouter extends BaseRouter {
7
+ acceptLang: string | null;
8
+ redirect: Request | null;
9
+
10
+ constructor(sites: SiteFromGraphQl[], opts: BaseRouterOptions) {
11
+ super(sites, opts);
12
+
13
+ this.acceptLang = null;
14
+ this.redirect = null;
15
+ }
16
+
17
+ defaultSite(): Site {
18
+ return this.siteByAcceptLang(this.acceptLang);
19
+ }
20
+
21
+ async openRequest(req: Request) {
22
+ // only handle the first redirect
23
+ // this makes the behaviour the same as client router
24
+ // not true
25
+ // todo: the client only instatly redirects if it belongs to another
26
+ // site (aka needs to use window.location.href)
27
+ if (this.redirect) return;
28
+
29
+ this.redirect = req;
30
+ this.cancelRequest();
31
+
32
+ // request was handled with a redirect so we don't have an entry
33
+ // templateData or anything else
34
+ // todo is that what we wan't to return. Because void would tell
35
+ // the user the request was cancelled
36
+ }
37
+
38
+ async pushRequest(req: Request, _opts: RequestOptions = {}) {
39
+ // todo not sure if that is what we want?
40
+ return await this.openRequest(req);
41
+ }
42
+
43
+ async replaceRequest(req: Request, _opts: RequestOptions = {}) {
44
+ // todo not sure if that is what we want?
45
+ return await this.openRequest(req);
46
+ }
47
+
48
+ /**
49
+ * This function always returns the request or a redirect one
50
+ *
51
+ * And then if no redirect happen the final route
52
+ *
53
+ * ## Throws
54
+ * If the request fails
55
+ */
56
+ async init(
57
+ url: string,
58
+ acceptLang?: string,
59
+ ): Promise<[Request, Route | null]> {
60
+ this.acceptLang = acceptLang ?? null;
61
+
62
+ const req = this.targetToRequest(url);
63
+ req.origin = 'init';
64
+
65
+ // throws if something goes wrong
66
+ const route = await this.handleRequest(req, () => {});
67
+ if (!route) {
68
+ if (!this.redirect)
69
+ throw new Error(
70
+ 'if the request gets cancelled on the server there should be a redirect',
71
+ );
72
+
73
+ return [this.redirect!, null];
74
+ }
75
+
76
+ return [req, route];
77
+ }
78
+ }