crelte 0.3.1 → 0.4.0

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 (63) hide show
  1. package/dist/Crelte.d.ts.map +1 -1
  2. package/dist/CrelteRequest.d.ts.map +1 -1
  3. package/dist/blocks/Blocks.d.ts.map +1 -1
  4. package/dist/blocks/Blocks.svelte.d.ts.map +1 -1
  5. package/dist/blocks/index.d.ts.map +1 -1
  6. package/dist/cookies/ClientCookies.d.ts.map +1 -1
  7. package/dist/cookies/ServerCookies.d.ts.map +1 -1
  8. package/dist/cookies/index.d.ts.map +1 -1
  9. package/dist/cookies/internal.d.ts.map +1 -1
  10. package/dist/cookies/utils.d.ts.map +1 -1
  11. package/dist/entry/EntryRouter.d.ts +30 -0
  12. package/dist/entry/EntryRouter.d.ts.map +1 -0
  13. package/dist/entry/EntryRouter.js +45 -0
  14. package/dist/entry/index.d.ts +32 -0
  15. package/dist/entry/index.d.ts.map +1 -0
  16. package/dist/entry/index.js +31 -0
  17. package/dist/graphql/GraphQl.d.ts.map +1 -1
  18. package/dist/graphql/gql.test.d.ts.map +1 -1
  19. package/dist/graphql/index.d.ts.map +1 -1
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/init/client.d.ts +1 -1
  23. package/dist/init/client.d.ts.map +1 -1
  24. package/dist/init/client.js +4 -3
  25. package/dist/init/server.d.ts +3 -3
  26. package/dist/init/server.d.ts.map +1 -1
  27. package/dist/init/server.js +6 -46
  28. package/dist/init/shared.d.ts +3 -1
  29. package/dist/init/shared.d.ts.map +1 -1
  30. package/dist/init/shared.js +47 -26
  31. package/dist/loadData/Globals.d.ts.map +1 -1
  32. package/dist/loadData/index.d.ts.map +1 -1
  33. package/dist/plugins/Events.d.ts.map +1 -1
  34. package/dist/plugins/Plugins.d.ts.map +1 -1
  35. package/dist/plugins/index.d.ts.map +1 -1
  36. package/dist/routing/History.d.ts.map +1 -1
  37. package/dist/routing/InnerRouter.d.ts.map +1 -1
  38. package/dist/routing/PageLoader.d.ts.map +1 -1
  39. package/dist/routing/Request.d.ts.map +1 -1
  40. package/dist/routing/Route.d.ts +11 -0
  41. package/dist/routing/Route.d.ts.map +1 -1
  42. package/dist/routing/Route.js +13 -0
  43. package/dist/routing/Router.d.ts +47 -10
  44. package/dist/routing/Router.d.ts.map +1 -1
  45. package/dist/routing/Router.js +74 -17
  46. package/dist/routing/Site.d.ts.map +1 -1
  47. package/dist/routing/index.d.ts +2 -2
  48. package/dist/routing/index.d.ts.map +1 -1
  49. package/dist/routing/utils.d.ts.map +1 -1
  50. package/dist/ssr/SsrCache.d.ts.map +1 -1
  51. package/dist/ssr/SsrComponents.d.ts.map +1 -1
  52. package/dist/ssr/index.d.ts.map +1 -1
  53. package/dist/utils.d.ts.map +1 -1
  54. package/package.json +9 -4
  55. package/src/entry/EntryRouter.ts +71 -0
  56. package/src/entry/index.ts +48 -0
  57. package/src/index.ts +2 -0
  58. package/src/init/client.ts +10 -3
  59. package/src/init/server.ts +15 -59
  60. package/src/init/shared.ts +78 -28
  61. package/src/routing/Route.ts +14 -0
  62. package/src/routing/Router.ts +102 -20
  63. package/src/routing/index.ts +2 -1
@@ -2,7 +2,7 @@ import { CrelteBuilder } from '../Crelte.js';
2
2
  import CrelteRequest from '../CrelteRequest.js';
3
3
  import { GraphQlQuery } from '../graphql/GraphQl.js';
4
4
  import { SiteFromGraphQl } from '../routing/Site.js';
5
- import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
5
+ import { pluginsBeforeRender, prepareLoadFn, setupPlugins } from './shared.js';
6
6
  import { tick } from 'svelte';
7
7
 
8
8
  /**
@@ -49,7 +49,7 @@ const mainDataDefault = {
49
49
  * });
50
50
  * ```
51
51
  */
52
- export function main(data: MainData) {
52
+ export async function main(data: MainData) {
53
53
  data = { ...mainDataDefault, ...data };
54
54
 
55
55
  // rendering steps
@@ -97,11 +97,18 @@ export function main(data: MainData) {
97
97
  // setup plugins
98
98
  setupPlugins(crelte, data.app.plugins ?? []);
99
99
 
100
+ const loadFn = await prepareLoadFn(
101
+ crelte,
102
+ data.app,
103
+ data.entryQuery,
104
+ data.globalQuery,
105
+ );
106
+
100
107
  // setup load Data
101
108
 
102
109
  crelte.router._internal.onLoad = (req, opts) => {
103
110
  const cr = new CrelteRequest(crelte, req);
104
- return loadFn(cr, data.app, data.entryQuery, data.globalQuery, opts);
111
+ return loadFn(cr, opts);
105
112
  };
106
113
 
107
114
  // render Space
@@ -1,11 +1,11 @@
1
1
  import { CrelteBuilder } from '../Crelte.js';
2
2
  import { SiteFromGraphQl } from '../routing/Site.js';
3
- import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
3
+ import { pluginsBeforeRender, prepareLoadFn, setupPlugins } from './shared.js';
4
4
  import SsrComponents from '../ssr/SsrComponents.js';
5
5
  import SsrCache from '../ssr/SsrCache.js';
6
6
  import ServerCookies from '../cookies/ServerCookies.js';
7
7
  import CrelteRequest from '../CrelteRequest.js';
8
- import { gql, GraphQlQuery } from '../graphql/GraphQl.js';
8
+ import { GraphQlQuery } from '../graphql/GraphQl.js';
9
9
 
10
10
  export type ServerData = {
11
11
  url: string;
@@ -15,9 +15,8 @@ export type ServerData = {
15
15
  endpoint: string;
16
16
  craftWeb: string;
17
17
  viteEnv: Map<string, string>;
18
- cookies?: string;
19
- readSitesCache?: () => Promise<any>;
20
- writeSitesCache?: (data: any) => Promise<void>;
18
+ cookies: string;
19
+ sites: SiteFromGraphQl[];
21
20
  };
22
21
 
23
22
  /**
@@ -76,20 +75,26 @@ export async function main(data: MainData): Promise<{
76
75
  const cookies = data.serverData.cookies ?? '';
77
76
  builder.setupCookies(cookies);
78
77
 
79
- const csites = await loadSites(builder, data.serverData);
80
- builder.ssrCache.set('crelteSites', csites);
81
- builder.setupRouter(csites);
78
+ builder.ssrCache.set('crelteSites', data.serverData.sites);
79
+ builder.setupRouter(data.serverData.sites);
82
80
 
83
81
  const crelte = builder.build();
84
82
 
85
83
  // setup plugins
86
84
  setupPlugins(crelte, data.app.plugins ?? []);
87
85
 
86
+ const loadFn = await prepareLoadFn(
87
+ crelte,
88
+ data.app,
89
+ data.entryQuery,
90
+ data.globalQuery,
91
+ );
92
+
88
93
  // setup load Data
89
94
 
90
95
  crelte.router._internal.onLoad = req => {
91
96
  const cr = new CrelteRequest(crelte, req);
92
- return loadFn(cr, data.app, data.entryQuery, data.globalQuery);
97
+ return loadFn(cr);
93
98
  };
94
99
 
95
100
  const { success, redirect, req, props } =
@@ -187,7 +192,7 @@ export async function mainError(
187
192
  htmlTemplate = htmlTemplate.replace('<!--page-lang-->', 'de');
188
193
 
189
194
  const finalHtml = htmlTemplate
190
- .replace('<!--ssr-head-->', head)
195
+ .replace('</head>', head + '\n\t</head>')
191
196
  .replace('<!--ssr-body-->', html);
192
197
 
193
198
  return {
@@ -195,52 +200,3 @@ export async function mainError(
195
200
  html: finalHtml,
196
201
  };
197
202
  }
198
-
199
- // requires, GraphQl, SsrCache
200
- async function loadSites(
201
- builder: CrelteBuilder,
202
- serverData: ServerData,
203
- ): Promise<SiteFromGraphQl[]> {
204
- if (!builder.graphQl) throw new Error();
205
-
206
- if ('CRAFT_SITES_CACHED' in globalThis) {
207
- return (globalThis as any)['CRAFT_SITES_CACHED'];
208
- }
209
-
210
- if (import.meta.env.PROD && serverData.readSitesCache) {
211
- try {
212
- const sites =
213
- (await serverData.readSitesCache()) as SiteFromGraphQl[];
214
- // @ts-ignore
215
- globalThis['CRAFT_SITES_CACHED'] = sites;
216
- return sites;
217
- } catch (_e: any) {
218
- // ignore
219
- }
220
- }
221
-
222
- const resp = (await builder.graphQl.query(
223
- gql`
224
- query {
225
- crelteSites {
226
- id
227
- baseUrl
228
- language
229
- name
230
- handle
231
- primary
232
- }
233
- }
234
- `,
235
- {},
236
- // don't cache since we cache ourself
237
- { caching: false },
238
- )) as { crelteSites: SiteFromGraphQl[] };
239
-
240
- // @ts-ignore
241
- globalThis['CRAFT_SITES_CACHED'] = resp.crelteSites;
242
- if (import.meta.env.PROD && serverData.writeSitesCache) {
243
- await serverData.writeSitesCache(resp.crelteSites);
244
- }
245
- return resp.crelteSites;
246
- }
@@ -1,5 +1,6 @@
1
1
  import Crelte from '../Crelte.js';
2
2
  import CrelteRequest from '../CrelteRequest.js';
3
+ import EntryRouter, { EntryRoutes } from '../entry/EntryRouter.js';
3
4
  import { GraphQlQuery } from '../graphql/GraphQl.js';
4
5
  import { LoadData, callLoadData } from '../loadData/index.js';
5
6
  import { PluginCreator } from '../plugins/Plugins.js';
@@ -12,6 +13,8 @@ interface App<E, T> {
12
13
  loadEntryData?: LoadData<any>;
13
14
 
14
15
  templates?: Record<string, LazyTemplateModule<E, T>>;
16
+
17
+ entryRoutes?: EntryRoutes;
15
18
  }
16
19
 
17
20
  interface TemplateModule<E, T> {
@@ -58,14 +61,43 @@ export function getEntry(page: any): any {
58
61
  };
59
62
  }
60
63
 
61
- export async function loadFn<D, E, T>(
64
+ // todo it would be nice to call this only once per server start
65
+ export async function prepareLoadFn<E, T>(
66
+ crelte: Crelte,
67
+ app: App<E, T>,
68
+ entryQuery: GraphQlQuery,
69
+ globalQuery?: GraphQlQuery,
70
+ ): Promise<(cr: CrelteRequest, loadOpts?: LoadOptions) => Promise<any>> {
71
+ const templateModules = prepareTemplates(app.templates ?? {});
72
+ let entryRouter: EntryRouter | null = null;
73
+ if (app.entryRoutes) {
74
+ entryRouter = new EntryRouter(crelte);
75
+ await app.entryRoutes(entryRouter);
76
+ }
77
+
78
+ return async (cr, loadOpts) => {
79
+ return await loadFn(
80
+ cr,
81
+ app,
82
+ templateModules,
83
+ entryRouter,
84
+ entryQuery,
85
+ globalQuery,
86
+ loadOpts,
87
+ );
88
+ };
89
+ }
90
+
91
+ async function loadFn<E, T>(
62
92
  cr: CrelteRequest,
63
93
  app: App<E, T>,
94
+ templateModules: Map<string, LazyTemplateModule<E, T>>,
95
+ entryRouter: EntryRouter | null,
64
96
  entryQuery: GraphQlQuery,
65
97
  globalQuery?: GraphQlQuery,
66
98
  loadOpts?: LoadOptions,
67
99
  ): Promise<any> {
68
- let dataProm: Promise<D> | null = null;
100
+ let dataProm: Promise<any> | null = null;
69
101
  // @ts-ignore
70
102
  if (app.loadData) {
71
103
  throw new Error(
@@ -93,43 +125,29 @@ export async function loadFn<D, E, T>(
93
125
  })();
94
126
  }
95
127
 
96
- let pageProm = null;
97
- if (cr.req.siteMatches()) {
98
- let uri = decodeURI(cr.req.uri);
99
- if (uri.startsWith('/')) uri = uri.substring(1);
100
- if (uri === '' || uri === '/') uri = '__home__';
101
-
102
- pageProm = cr.query(entryQuery, {
103
- uri,
104
- siteId: cr.site.id,
105
- });
106
- }
128
+ const entryProm = queryEntry(cr, app, entryRouter, entryQuery);
107
129
 
108
130
  const pluginsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
109
131
 
110
132
  // loading progress is at 20%
111
133
  loadOpts?.setProgress(0.2);
112
134
 
113
- const [data, global, page] = await Promise.all([
135
+ const [data, global, entry] = await Promise.all([
114
136
  dataProm,
115
137
  globalProm,
116
- pageProm,
138
+ entryProm,
117
139
  ...pluginsLoadGlobalData,
118
140
  ]);
119
141
 
120
- if (global) {
121
- cr.globals._setData(cr.site.id, global);
122
- } else if (!cr.globals._wasLoaded(cr.site.id)) {
123
- // we need to set the global data to an empty object
124
- // so any waiters get's triggered
125
- cr.globals._setData(cr.site.id, {});
142
+ // global is only set if !wasLoaded but we need to store something
143
+ // even if no globalQuery exists
144
+ if (global || !cr.globals._wasLoaded(cr.site.id)) {
145
+ cr.globals._setData(cr.site.id, global ?? {});
126
146
  }
127
147
 
128
- const entry = getEntry(page);
129
-
130
148
  let template;
131
149
  if (app.templates) {
132
- template = await loadTemplate(app.templates, entry);
150
+ template = await loadTemplate(templateModules, entry);
133
151
  } else {
134
152
  throw new Error('App must have templates or loadTemplate method');
135
153
  }
@@ -180,12 +198,39 @@ function parseFilename(path: string): [string, string] {
180
198
  return [name, ext];
181
199
  }
182
200
 
183
- async function loadTemplate<E, T>(
201
+ async function queryEntry<E, T>(
202
+ cr: CrelteRequest,
203
+ app: App<E, T>,
204
+ entryRouter: EntryRouter | null,
205
+ entryQuery: GraphQlQuery,
206
+ ): Promise<any | null> {
207
+ // check
208
+ if (entryRouter) {
209
+ const entry = await entryRouter._handle(cr);
210
+ if (entry) return entry;
211
+ }
212
+
213
+ if (cr.req.siteMatches()) {
214
+ let uri = decodeURI(cr.req.uri);
215
+ if (uri.startsWith('/')) uri = uri.substring(1);
216
+ if (uri === '' || uri === '/') uri = '__home__';
217
+
218
+ const page = await cr.query(entryQuery, {
219
+ uri,
220
+ siteId: cr.site.id,
221
+ });
222
+
223
+ return getEntry(page);
224
+ }
225
+
226
+ return null;
227
+ }
228
+
229
+ function prepareTemplates<E, T>(
184
230
  rawModules: Record<string, LazyTemplateModule<E, T>>,
185
- entry: E,
186
- ): Promise<TemplateModule<E, T>> {
231
+ ): Map<string, LazyTemplateModule<E, T>> {
187
232
  // parse modules
188
- const modules = new Map(
233
+ return new Map(
189
234
  Object.entries(rawModules)
190
235
  .map(([path, mod]) => {
191
236
  const [name, _ext] = parseFilename(path);
@@ -193,7 +238,12 @@ async function loadTemplate<E, T>(
193
238
  })
194
239
  .filter(([name, _mod]) => !!name),
195
240
  );
241
+ }
196
242
 
243
+ async function loadTemplate<E, T>(
244
+ modules: Map<string, LazyTemplateModule<E, T>>,
245
+ entry: E,
246
+ ): Promise<TemplateModule<E, T>> {
197
247
  const entr = entry as any;
198
248
  const handle = `${entr.sectionHandle}-${entr.typeHandle}`;
199
249
 
@@ -187,6 +187,20 @@ export default class Route {
187
187
  return this.url.hash;
188
188
  }
189
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
+
190
204
  /**
191
205
  * Checks if there are previous routes which would allow it to go back
192
206
  */
@@ -17,6 +17,18 @@ const defaultRouterOpts = {
17
17
  debugTiming: false,
18
18
  };
19
19
 
20
+ /**
21
+ * Allows to easely modify a Request
22
+ *
23
+ * If you return `false` the request will be aborted
24
+ *
25
+ * ## Example
26
+ * ```
27
+ * router.replace(req => (req.hash = ''));
28
+ * ```
29
+ */
30
+ export type UpdateRequest = (req: Request) => boolean | null | undefined | void;
31
+
20
32
  type LoadedMore = {
21
33
  changeHistory: () => void;
22
34
  };
@@ -122,6 +134,8 @@ export default class Router {
122
134
 
123
135
  this._onRequest = new Listeners();
124
136
 
137
+ // these functions are exposed to the init "module"
138
+ // but should not be used by anybody else
125
139
  this._internal = {
126
140
  onLoaded: () => {},
127
141
  onNothingLoaded: () => {},
@@ -209,11 +223,15 @@ export default class Router {
209
223
  * // the following page will be opened https://example.com/de/foo/bar
210
224
  * ```
211
225
  */
212
- open(target: string | URL | Route | Request, opts: RequestOptions = {}) {
213
- const req = this.inner.targetToRequest(target, {
214
- ...opts,
226
+ open(
227
+ target: string | URL | Route | Request | UpdateRequest,
228
+ opts: RequestOptions = {},
229
+ ) {
230
+ const req = this.targetOrUpdateToRequest(target, opts, {
215
231
  origin: 'manual',
216
232
  });
233
+ if (!req) return;
234
+
217
235
  this.inner.open(req);
218
236
  }
219
237
 
@@ -228,7 +246,17 @@ export default class Router {
228
246
  * And will clear the scrollY value if you not provide a new one via the `opts`
229
247
  * This will disableLoadData by default if you not provide an override via the `opts`
230
248
  *
231
- * ## Example
249
+ * ## Example using the update function
250
+ * ```
251
+ * import { getRouter } from 'crelte';
252
+ *
253
+ * const router = getRouter();
254
+ *
255
+ * const page = 1;
256
+ * router.push(req => req.setSearchParam('page', page || null));
257
+ * ```
258
+ *
259
+ * ## Example using the route object
232
260
  * ```
233
261
  * import { getRouter } from 'crelte';
234
262
  *
@@ -240,15 +268,16 @@ export default class Router {
240
268
  * router.push(route);
241
269
  * ```
242
270
  */
243
- push(route: Route | Request, opts: RequestOptions = {}) {
244
- // cancel previous request
245
- this.pageLoader.discard();
246
- const req = this.inner.targetToRequest(route, {
247
- ...opts,
271
+ push(route: Route | Request | UpdateRequest, opts: RequestOptions = {}) {
272
+ // theoretically string and URL also work but we might
273
+ // change that in the future
274
+ const req = this.targetOrUpdateToRequest(route, opts, {
248
275
  origin: 'push',
249
276
  scrollY: opts.scrollY ?? undefined,
250
277
  disableLoadData: opts.disableLoadData ?? true,
251
278
  });
279
+ if (!req) return;
280
+
252
281
  this.inner.push(req);
253
282
  }
254
283
 
@@ -261,16 +290,26 @@ export default class Router {
261
290
  }
262
291
 
263
292
  /**
264
- * This replaces the state of the route without triggering an event
293
+ * This replaces the state of the route without triggering a new pageload
265
294
  *
266
295
  * You can use this when using some filters for example a search filter
267
296
  *
268
297
  * ## Note
269
298
  * This will always set the origin to 'replace'
270
- * And will clear the scrollY value if you not provide a new one via the `opts`
271
- * This will disableLoadData by default if you not provide an override via the `opts`
299
+ * And will clear the scrollY value if you don't provide a new one via the `opts`
300
+ * This will disableLoadData by default if you don't provide an override via the `opts`
272
301
  *
273
- * ## Example
302
+ * ## Example using the update function
303
+ * ```
304
+ * import { getRouter } from 'crelte';
305
+ *
306
+ * const router = getRouter();
307
+ *
308
+ * const search = 'foo';
309
+ * router.replace(req => req.setSearchParam('search', search));
310
+ * ```
311
+ *
312
+ * ## Example using the route object
274
313
  * ```
275
314
  * import { getRouter } from 'crelte';
276
315
  *
@@ -278,18 +317,20 @@ export default class Router {
278
317
  *
279
318
  * const search = 'foo';
280
319
  * const route = router.route.get();
281
- * route.setSearchParam('search', search ? search : null);
282
- * router.replaceState(route);
320
+ * route.setSearchParam('search', search);
321
+ * router.replace(route);
283
322
  * ```
284
323
  */
285
- replace(route: Route | Request, opts: RequestOptions = {}) {
286
- // cancel previous request
287
- this.pageLoader.discard();
288
- const req = this.inner.targetToRequest(route, {
324
+ replace(route: Route | Request | UpdateRequest, opts: RequestOptions = {}) {
325
+ // theoretically string and URL also work but we might
326
+ // change that in the future
327
+ const req = this.targetOrUpdateToRequest(route, opts, {
289
328
  origin: 'replace',
290
329
  scrollY: opts.scrollY ?? undefined,
291
330
  disableLoadData: opts.disableLoadData ?? true,
292
331
  });
332
+ if (!req) return;
333
+
293
334
  this.inner.replace(req);
294
335
  }
295
336
 
@@ -434,6 +475,7 @@ export default class Router {
434
475
  return resp;
435
476
  }
436
477
 
478
+ // gets called by the InnerRouter when a new route is requested
437
479
  private _onRoute(req: Request, changeHistory: () => void) {
438
480
  this.destroyRequest();
439
481
 
@@ -450,6 +492,7 @@ export default class Router {
450
492
  if (!req.disableLoadData) {
451
493
  this.pageLoader.load(req, { changeHistory });
452
494
  } else {
495
+ this.pageLoader.discard();
453
496
  this._onNothingLoaded(req, { changeHistory });
454
497
  }
455
498
  }
@@ -465,6 +508,7 @@ export default class Router {
465
508
  this.pageLoader.preload(req);
466
509
  }
467
510
 
511
+ // gets called by the pageLoader when teh loadData completes
468
512
  private async _onLoaded(
469
513
  resp: LoadResponse,
470
514
  req: Request,
@@ -474,7 +518,7 @@ export default class Router {
474
518
  if (await req._renderBarrier.ready()) return;
475
519
 
476
520
  // when the data is loaded let's update the route of the inner
477
- // this is will only happen if no other route has been requested
521
+ // this will only happen if no other route has been requested
478
522
  // in the meantime
479
523
  more.changeHistory();
480
524
 
@@ -487,6 +531,7 @@ export default class Router {
487
531
  });
488
532
  }
489
533
 
534
+ // this gets called if loadData is not called
490
535
  private async _onNothingLoaded(req: Request, more: LoadedMore) {
491
536
  // check if the render was cancelled
492
537
  if (await req._renderBarrier.ready()) return;
@@ -505,9 +550,46 @@ export default class Router {
505
550
  });
506
551
  }
507
552
 
553
+ // this is called by the pageLoader if we get a progress update
508
554
  private _onProgress(loading: boolean, progress?: number): void {
509
555
  if (this._loading.get() !== loading) this._loading.set(loading);
510
556
 
511
557
  if (typeof progress === 'number') this._loadingProgress.set(progress);
512
558
  }
559
+
560
+ /**
561
+ * Transforms a target to a request
562
+ *
563
+ * returns null if the request was canceled by the update request
564
+ */
565
+ private targetOrUpdateToRequest(
566
+ target: string | URL | Route | Request | UpdateRequest,
567
+ opts: RequestOptions = {},
568
+ forcedOpts: RequestOptions = {},
569
+ ): Request | null {
570
+ // we have an update request
571
+ if (typeof target === 'function') {
572
+ const route = this.route.get();
573
+ if (!route) {
574
+ throw new Error(
575
+ 'route to update missing in first loadData call',
576
+ );
577
+ }
578
+
579
+ // first get a req
580
+ const req = this.inner.targetToRequest(route, opts);
581
+ // check if the request was canceled by the update request
582
+ if (target(req) === false) return null;
583
+
584
+ // now we add the forcedOpts
585
+ req._updateOpts(forcedOpts);
586
+
587
+ return req;
588
+ }
589
+
590
+ return this.inner.targetToRequest(target, {
591
+ ...opts,
592
+ ...forcedOpts,
593
+ });
594
+ }
513
595
  }
@@ -1,10 +1,11 @@
1
- import Router from './Router.js';
1
+ import Router, { type UpdateRequest } from './Router.js';
2
2
  import Route, { type RouteOptions } from './Route.js';
3
3
  import Request, { type RequestOptions, type DelayRender } from './Request.js';
4
4
  import Site from './Site.js';
5
5
 
6
6
  export {
7
7
  Router,
8
+ UpdateRequest,
8
9
  Route,
9
10
  RouteOptions,
10
11
  Site,