crelte 0.5.10 → 0.5.12

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 (133) hide show
  1. package/dist/bodyClass/BodyClass.d.ts +39 -0
  2. package/dist/bodyClass/BodyClass.d.ts.map +1 -0
  3. package/dist/bodyClass/BodyClass.js +51 -0
  4. package/dist/bodyClass/ClientBodyClass.d.ts +12 -0
  5. package/dist/bodyClass/ClientBodyClass.d.ts.map +1 -0
  6. package/dist/bodyClass/ClientBodyClass.js +57 -0
  7. package/dist/bodyClass/ServerBodyClass.d.ts +12 -0
  8. package/dist/bodyClass/ServerBodyClass.d.ts.map +1 -0
  9. package/dist/bodyClass/ServerBodyClass.js +47 -0
  10. package/dist/bodyClass/index.d.ts +2 -0
  11. package/dist/bodyClass/index.d.ts.map +1 -0
  12. package/dist/bodyClass/index.js +1 -0
  13. package/dist/cookies/ClientCookies.d.ts +8 -3
  14. package/dist/cookies/ClientCookies.d.ts.map +1 -1
  15. package/dist/cookies/ClientCookies.js +31 -7
  16. package/dist/cookies/Cookies.d.ts +42 -0
  17. package/dist/cookies/Cookies.d.ts.map +1 -0
  18. package/dist/cookies/Cookies.js +44 -0
  19. package/dist/cookies/ServerCookies.d.ts +3 -2
  20. package/dist/cookies/ServerCookies.d.ts.map +1 -1
  21. package/dist/cookies/ServerCookies.js +6 -4
  22. package/dist/cookies/index.d.ts +1 -25
  23. package/dist/cookies/index.d.ts.map +1 -1
  24. package/dist/cookies/index.js +1 -1
  25. package/dist/crelte.d.ts +7 -1
  26. package/dist/crelte.d.ts.map +1 -1
  27. package/dist/crelte.js +2 -1
  28. package/dist/index.d.ts +13 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +9 -0
  31. package/dist/init/client.d.ts +1 -8
  32. package/dist/init/client.d.ts.map +1 -1
  33. package/dist/init/client.js +26 -24
  34. package/dist/init/server.d.ts.map +1 -1
  35. package/dist/init/server.js +12 -4
  36. package/dist/init/shared.d.ts +1 -0
  37. package/dist/init/shared.d.ts.map +1 -1
  38. package/dist/init/shared.js +16 -5
  39. package/dist/loadData/Globals.d.ts.map +1 -1
  40. package/dist/node/index.js +1 -1
  41. package/dist/plugins/Events.d.ts +12 -7
  42. package/dist/plugins/Events.d.ts.map +1 -1
  43. package/dist/plugins/Plugins.d.ts +36 -1
  44. package/dist/plugins/Plugins.d.ts.map +1 -1
  45. package/dist/plugins/Plugins.js +32 -0
  46. package/dist/queries/Queries.d.ts +30 -5
  47. package/dist/queries/Queries.d.ts.map +1 -1
  48. package/dist/queries/Queries.js +19 -2
  49. package/dist/queries/gql.d.ts +2 -2
  50. package/dist/queries/gql.d.ts.map +1 -1
  51. package/dist/queries/index.d.ts +47 -2
  52. package/dist/queries/index.d.ts.map +1 -1
  53. package/dist/queries/index.js +2 -2
  54. package/dist/queries/vars.d.ts +2 -0
  55. package/dist/queries/vars.d.ts.map +1 -1
  56. package/dist/queries/vars.js +10 -0
  57. package/dist/routing/route/Request.d.ts +1 -1
  58. package/dist/routing/route/Request.d.ts.map +1 -1
  59. package/dist/routing/route/Request.js +7 -3
  60. package/dist/routing/router/BaseRouter.d.ts +2 -1
  61. package/dist/routing/router/BaseRouter.d.ts.map +1 -1
  62. package/dist/routing/router/BaseRouter.js +4 -2
  63. package/dist/routing/router/ClientRouter.d.ts.map +1 -1
  64. package/dist/routing/router/ClientRouter.js +21 -15
  65. package/dist/routing/router/Router.d.ts +2 -1
  66. package/dist/routing/router/Router.d.ts.map +1 -1
  67. package/dist/routing/router/Router.js +10 -13
  68. package/dist/routing/utils.d.ts +1 -0
  69. package/dist/routing/utils.d.ts.map +1 -1
  70. package/dist/routing/utils.js +1 -1
  71. package/dist/server/CrelteServer.d.ts +1 -0
  72. package/dist/server/CrelteServer.d.ts.map +1 -1
  73. package/dist/server/CrelteServer.js +5 -2
  74. package/dist/server/ServerRouter.d.ts.map +1 -1
  75. package/dist/server/ServerRouter.js +17 -7
  76. package/dist/server/queries/QueryGqlRoute.d.ts +28 -0
  77. package/dist/server/queries/QueryGqlRoute.d.ts.map +1 -0
  78. package/dist/server/queries/QueryGqlRoute.js +194 -0
  79. package/dist/server/queries/QueryHandleRoute.d.ts +12 -0
  80. package/dist/server/queries/QueryHandleRoute.d.ts.map +1 -0
  81. package/dist/server/queries/QueryHandleRoute.js +24 -0
  82. package/dist/server/queries/queries.d.ts.map +1 -1
  83. package/dist/server/queries/queries.js +42 -19
  84. package/dist/server/queries/routes.d.ts +7 -30
  85. package/dist/server/queries/routes.d.ts.map +1 -1
  86. package/dist/server/queries/routes.js +13 -199
  87. package/dist/std/stores/StagedWritable.d.ts +48 -0
  88. package/dist/std/stores/StagedWritable.d.ts.map +1 -0
  89. package/dist/std/stores/StagedWritable.js +84 -0
  90. package/dist/std/stores/index.d.ts +2 -1
  91. package/dist/std/stores/index.d.ts.map +1 -1
  92. package/dist/std/stores/index.js +2 -1
  93. package/dist/std/sync/Barrier.js +1 -1
  94. package/dist/utils.d.ts +9 -0
  95. package/dist/utils.d.ts.map +1 -1
  96. package/dist/utils.js +11 -0
  97. package/package.json +5 -1
  98. package/src/bodyClass/BodyClass.ts +72 -0
  99. package/src/bodyClass/ClientBodyClass.ts +62 -0
  100. package/src/bodyClass/ServerBodyClass.ts +65 -0
  101. package/src/bodyClass/index.ts +1 -0
  102. package/src/cookies/ClientCookies.ts +41 -10
  103. package/src/cookies/Cookies.ts +70 -0
  104. package/src/cookies/ServerCookies.ts +9 -6
  105. package/src/cookies/index.ts +5 -29
  106. package/src/crelte.ts +9 -0
  107. package/src/index.ts +15 -1
  108. package/src/init/client.ts +29 -24
  109. package/src/init/server.ts +12 -4
  110. package/src/init/shared.ts +18 -6
  111. package/src/loadData/Globals.ts +1 -1
  112. package/src/node/index.ts +1 -1
  113. package/src/plugins/Events.ts +22 -7
  114. package/src/plugins/Plugins.ts +66 -1
  115. package/src/queries/Queries.ts +47 -14
  116. package/src/queries/gql.ts +2 -2
  117. package/src/queries/index.ts +71 -0
  118. package/src/queries/vars.ts +13 -0
  119. package/src/routing/route/Request.ts +11 -4
  120. package/src/routing/router/BaseRouter.ts +4 -2
  121. package/src/routing/router/ClientRouter.ts +26 -18
  122. package/src/routing/router/Router.ts +10 -11
  123. package/src/routing/utils.ts +1 -1
  124. package/src/server/CrelteServer.ts +4 -2
  125. package/src/server/ServerRouter.ts +18 -7
  126. package/src/server/queries/QueryGqlRoute.ts +224 -0
  127. package/src/server/queries/QueryHandleRoute.ts +37 -0
  128. package/src/server/queries/queries.ts +57 -21
  129. package/src/server/queries/routes.ts +25 -229
  130. package/src/std/stores/StagedWritable.ts +96 -0
  131. package/src/std/stores/index.ts +2 -1
  132. package/src/std/sync/Barrier.ts +1 -1
  133. package/src/utils.ts +15 -0
@@ -3,6 +3,7 @@ import {
3
3
  loadFn,
4
4
  newQueries,
5
5
  onNewCrelteRequest,
6
+ pluginsAfterRender,
6
7
  pluginsBeforeRender,
7
8
  pluginsBeforeRequest,
8
9
  setupPlugins,
@@ -19,6 +20,9 @@ import Plugins from '../plugins/Plugins.js';
19
20
  import Events from '../plugins/Events.js';
20
21
  import Globals from '../loadData/Globals.js';
21
22
  import { Writable } from '../std/stores/index.js';
23
+ import ClientBodyClass from '../bodyClass/ClientBodyClass.js';
24
+ import { BodyClass } from '../bodyClass/index.js';
25
+ import { Cookies } from '../cookies/index.js';
22
26
 
23
27
  /**
24
28
  * The main function to start the client side rendering
@@ -37,16 +41,9 @@ export type MainData = {
37
41
  * ```js
38
42
  * import * as app from './App.svelte';
39
43
  * import * as errorPage from './Error.svelte';
40
- * import entryQuery from './queries/entry.graphql';
41
- * import globalQuery from './queries/global.graphql';
42
44
  * import { main } from 'crelte/client';
43
45
  *
44
- * main({
45
- * app,
46
- * errorPage,
47
- * entryQuery,
48
- * globalQuery,
49
- * });
46
+ * main({ app, errorPage });
50
47
  * ```
51
48
  */
52
49
  export async function main(data: MainData) {
@@ -81,7 +78,6 @@ export async function main(data: MainData) {
81
78
  });
82
79
 
83
80
  const queries = newQueries(ssrCache, router.route.readonly(), config);
84
- const cookies = new ClientCookies();
85
81
 
86
82
  const crelte = newCrelte({
87
83
  config,
@@ -91,7 +87,8 @@ export async function main(data: MainData) {
91
87
  globals: new Globals(),
92
88
  router: new Router(router),
93
89
  queries,
94
- cookies,
90
+ cookies: new Cookies(new ClientCookies()),
91
+ bodyClass: new BodyClass(new ClientBodyClass()),
95
92
  });
96
93
 
97
94
  const app = new InternalApp(data.app);
@@ -108,22 +105,22 @@ export async function main(data: MainData) {
108
105
 
109
106
  // render Space
110
107
 
111
- let appInstance: any;
112
- let routeProp: Writable<Route>;
108
+ let routeProp: Writable<Route> | null = null;
113
109
  const renderApp = (route: Route) => {
114
- if (appInstance) {
115
- routeProp!.set(route);
110
+ if (routeProp) {
111
+ routeProp.set(route);
116
112
  return;
117
113
  }
118
114
 
119
115
  routeProp = new Writable(route);
120
- appInstance = svelteMount(data.app.default, {
116
+ svelteMount(data.app.default, {
121
117
  target: document.body,
122
118
  props: { route: routeProp },
123
119
  context: new Map([['crelte', crelte]]),
124
120
  intro: config.playIntro,
125
121
  });
126
122
  };
123
+ const appMounted = () => !!routeProp;
127
124
 
128
125
  router.onError = (e, req) => {
129
126
  console.error('routing failed:', e, 'reloading trying to fix it');
@@ -133,12 +130,12 @@ export async function main(data: MainData) {
133
130
  };
134
131
 
135
132
  router.onRender = async (cr, readyForRoute, domUpdated) => {
136
- if (appInstance && cr.req.disableLoadData) {
133
+ if (appMounted() && cr.req.disableLoadData) {
137
134
  // if the app is already rendered and entry did not change
138
- // we just wan't to run domUpdated because we don't wan't to update anything
135
+ // we just wan't to run domUpdated because we don't want to update anything
139
136
 
140
137
  const route = readyForRoute();
141
- cr.router._requestCompleted();
138
+ cr.router.z_requestCompleted();
142
139
  // globals should not be run because they will be empty
143
140
  // since nobody called loadGlobalData (todo maybe globals should also,
144
141
  // know if it accepts updates)
@@ -147,6 +144,7 @@ export async function main(data: MainData) {
147
144
  await tick();
148
145
 
149
146
  domUpdated(cr, route);
147
+ pluginsAfterRender(cr, route);
150
148
 
151
149
  return route;
152
150
  }
@@ -155,10 +153,16 @@ export async function main(data: MainData) {
155
153
 
156
154
  let render = async () => {
157
155
  const route = readyForRoute();
158
- cr.router._requestCompleted();
159
- if (route.entryChanged) cr.globals.z_syncToStores();
160
- // we should trigger the route update here
161
- pluginsBeforeRender(cr, route);
156
+ cr.router.z_requestCompleted();
157
+ // this is only important on the first render
158
+ // else we will catch an earlier branch in onRender
159
+ if (route.entryChanged) {
160
+ cr.globals.z_syncToStores();
161
+ pluginsBeforeRender(cr, route);
162
+ cr.cookies.z_render();
163
+ cr.bodyClass.z_render();
164
+ }
165
+
162
166
  renderApp(route);
163
167
 
164
168
  await tick();
@@ -170,6 +174,7 @@ export async function main(data: MainData) {
170
174
  }
171
175
 
172
176
  domUpdated(cr, route);
177
+ pluginsAfterRender(cr, route);
173
178
 
174
179
  return route;
175
180
  };
@@ -177,7 +182,7 @@ export async function main(data: MainData) {
177
182
  // render with view Transition if enabled and not in hydration
178
183
  if (
179
184
  config.viewTransition &&
180
- appInstance &&
185
+ appMounted() &&
181
186
  (document as any).startViewTransition
182
187
  ) {
183
188
  const prevRender = render;
@@ -218,8 +223,8 @@ function handleLoadError(e: any) {
218
223
 
219
224
  // Messages in different languages
220
225
  const messages: Record<string, string> = {
221
- en: 'An error has occurred. Please reload the page or try again later.',
222
226
  de: 'Leider ist ein Fehler aufgetreten. Laden Sie die Seite neu, oder versuchen Sie es später noch mal.',
227
+ en: 'An error has occurred. Please reload the page or try again later.',
223
228
  fr: 'Une erreur s’est produite. Veuillez recharger la page ou réessayer plus tard.',
224
229
  it: 'Si è verificato un errore. Ricarica la pagina o riprova più tardi.',
225
230
  nl: 'Er is een fout opgetreden. Herlaad de pagina of probeer het later opnieuw.',
@@ -23,6 +23,9 @@ import {
23
23
  RenderRequest,
24
24
  RenderResponse,
25
25
  } from '../server/shared.js';
26
+ import ServerBodyClass from '../bodyClass/ServerBodyClass.js';
27
+ import BodyClass from '../bodyClass/BodyClass.js';
28
+ import { Cookies } from '../cookies/index.js';
26
29
 
27
30
  export { type RenderRequest, type RenderResponse } from '../server/shared.js';
28
31
 
@@ -75,6 +78,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
75
78
  ssrCache.set('FRONTEND_URL', data.serverData.frontend);
76
79
 
77
80
  const cookies = new ServerCookies(data.serverData.headers);
81
+ const bodyClass = new ServerBodyClass();
78
82
 
79
83
  ssrCache.set('crelteSites', data.serverData.sites);
80
84
  const router = new ServerRouter(
@@ -93,7 +97,8 @@ export async function main(data: MainData): Promise<RenderResponse> {
93
97
  globals: new Globals(),
94
98
  router: new Router(router),
95
99
  queries,
96
- cookies,
100
+ cookies: new Cookies(cookies),
101
+ bodyClass: new BodyClass(bodyClass),
97
102
  });
98
103
 
99
104
  const app = new InternalApp(data.app);
@@ -108,9 +113,11 @@ export async function main(data: MainData): Promise<RenderResponse> {
108
113
 
109
114
  router.onRender = (cr, readyForRoute, _domUpdated) => {
110
115
  const route = readyForRoute();
111
- cr.router._requestCompleted();
116
+ cr.router.z_requestCompleted();
112
117
  cr.globals.z_syncToStores();
113
118
  pluginsBeforeRender(cr, route);
119
+ cr.cookies.z_render();
120
+ cr.bodyClass.z_render();
114
121
 
115
122
  return route;
116
123
  };
@@ -121,7 +128,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
121
128
  // if redirect
122
129
  if (!route) {
123
130
  const headers = new Headers();
124
- (crelte.cookies as ServerCookies)._populateHeaders(headers);
131
+ cookies._populateHeaders(headers);
125
132
 
126
133
  return {
127
134
  status: req.statusCode ?? 302,
@@ -153,6 +160,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
153
160
  '<!--page-lang-->',
154
161
  route.site.language,
155
162
  );
163
+ htmlTemplate = bodyClass.z_processHtmlTemplate(htmlTemplate);
156
164
 
157
165
  const finalHtml = htmlTemplate
158
166
  .replace('</head>', head + '\n\t</head>')
@@ -161,7 +169,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
161
169
  const entry = route.entry;
162
170
 
163
171
  const headers = new Headers();
164
- (crelte.cookies as ServerCookies)._populateHeaders(headers);
172
+ cookies._populateHeaders(headers);
165
173
 
166
174
  return {
167
175
  status:
@@ -32,6 +32,11 @@ export function pluginsBeforeRequest(cr: CrelteRequest): Promise<void> | void {
32
32
 
33
33
  export function pluginsBeforeRender(cr: CrelteRequest, route: Route): void {
34
34
  cr.events.trigger('beforeRender', cr, route);
35
+ cr.plugins.z_render(cr, route);
36
+ }
37
+
38
+ export function pluginsAfterRender(cr: CrelteRequest, route: Route): void {
39
+ cr.events.trigger('afterRender', cr, route);
35
40
  }
36
41
 
37
42
  export function newQueries(
@@ -64,12 +69,15 @@ export function onNewCrelteRequest(
64
69
  ...crelte,
65
70
  router: crelte.router.z_toRequest(req),
66
71
  queries: crelte.queries.z_toRequest(req),
72
+ plugins: crelte.plugins.z_toRequest(req),
67
73
  globals: crelte.globals.z_toRequest(),
74
+ cookies: crelte.cookies.z_toRequest(),
75
+ bodyClass: crelte.bodyClass.z_toRequest(),
68
76
  };
69
77
  return crelteToRequest(nCrelte, req);
70
78
  }
71
79
 
72
- async function pluginsLoadEntry(cr: CrelteRequest): Promise<Entry | null> {
80
+ async function eventsLoadEntry(cr: CrelteRequest): Promise<Entry | null> {
73
81
  const listeners = cr.events.getListeners('loadEntry');
74
82
 
75
83
  for (const loadEntry of listeners) {
@@ -123,7 +131,7 @@ export async function loadFn(
123
131
  // checked to be empty before doing it
124
132
  const entryProm = (async () => {
125
133
  // first let's try to call the plugin loadEntry
126
- let entry = await pluginsLoadEntry(cr);
134
+ let entry = await eventsLoadEntry(cr);
127
135
  if (isCanceled()) return [];
128
136
 
129
137
  // if no plugin provides an entry we load it from the app
@@ -152,7 +160,8 @@ export async function loadFn(
152
160
  return [entry, template] as [Entry, TemplateModule];
153
161
  })();
154
162
 
155
- const pluginsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
163
+ const eventsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
164
+ const pluginsLoadGlobalData = cr.plugins.z_loadGlobalData(cr);
156
165
 
157
166
  // loading progress is at 20%
158
167
  loadOpts?.setProgress(0.2);
@@ -160,6 +169,7 @@ export async function loadFn(
160
169
  const loadGlobalDataProm = Promise.all([
161
170
  globalProm,
162
171
  entryProm,
172
+ ...eventsLoadGlobalData,
163
173
  ...pluginsLoadGlobalData,
164
174
  ]);
165
175
 
@@ -167,7 +177,7 @@ export async function loadFn(
167
177
  // we force resolve them to prevent deadlocks
168
178
  if (
169
179
  import.meta.env.DEV &&
170
- !(await Promise.any([loadGlobalDataProm, timeout(2000)]))
180
+ !(await Promise.race([loadGlobalDataProm, timeout(2000)]))
171
181
  ) {
172
182
  console.error(
173
183
  'DEV: globals took longer than 2 seconds to load. ' +
@@ -184,8 +194,6 @@ export async function loadFn(
184
194
  // loading progress is at 60%
185
195
  loadOpts?.setProgress(0.6);
186
196
 
187
- const pluginsLoadData = cr.events.trigger('loadData', cr, entry);
188
-
189
197
  let loadDataProm = null;
190
198
  if (template.loadData) {
191
199
  loadDataProm = callLoadData(template.loadData, cr, entry);
@@ -196,9 +204,13 @@ export async function loadFn(
196
204
  entryDataProm = callLoadData(app.loadEntryData, cr, entry);
197
205
  }
198
206
 
207
+ const eventsLoadData = cr.events.trigger('loadData', cr, entry);
208
+ const pluginsLoadData = cr.plugins.z_loadData(cr);
209
+
199
210
  const [templateData, entryData] = await Promise.all([
200
211
  loadDataProm,
201
212
  entryDataProm,
213
+ ...eventsLoadData,
202
214
  ...pluginsLoadData,
203
215
  ]);
204
216
 
@@ -131,7 +131,7 @@ export default class Globals {
131
131
  * @hidden
132
132
  * call this before starting the loadGlobalData phase
133
133
  */
134
- z_toRequest() {
134
+ z_toRequest(): Globals {
135
135
  const nGlobals = new Globals(this.stores);
136
136
  nGlobals.waiters = new Map();
137
137
  nGlobals.newData = new Map();
package/src/node/index.ts CHANGED
@@ -166,7 +166,7 @@ export default async function createServer(serverMod: any, buildTime: string) {
166
166
  } catch (e) {
167
167
  basicError(res, e);
168
168
  }
169
- }).listen(8080);
169
+ }).listen(process.env.PORT ?? 8080);
170
170
  }
171
171
 
172
172
  function basicError(res: ServerResponse, err: any) {
@@ -32,6 +32,9 @@ export default class Events {
32
32
  * Will be executed in preload as well.
33
33
  *
34
34
  * @returns a function to remove the listener
35
+ *
36
+ * #### afterRender
37
+ * Note this will also be executed when disableLoadData is true
35
38
  */
36
39
  // override this function to add your own function signatures
37
40
  on(
@@ -40,7 +43,7 @@ export default class Events {
40
43
  ): () => void;
41
44
  on(
42
45
  ev: 'loadGlobalData',
43
- fn: (cr: CrelteRequest) => Promise<any>,
46
+ fn: (cr: CrelteRequest) => Promise<any> | any,
44
47
  ): () => void;
45
48
  on(
46
49
  ev: 'loadEntry',
@@ -52,13 +55,20 @@ export default class Events {
52
55
  ): () => void;
53
56
  on(
54
57
  ev: 'afterLoadEntry',
55
- fn: (cr: CrelteRequest) => Promise<any>,
58
+ fn: (cr: CrelteRequest) => Promise<any> | any,
56
59
  ): () => void;
57
60
  on(
58
61
  ev: 'loadData',
59
- fn: (cr: CrelteRequest, entry: Entry) => Promise<any>,
62
+ fn: (cr: CrelteRequest, entry: Entry) => Promise<any> | any,
63
+ ): () => void;
64
+ on(
65
+ ev: 'beforeRender',
66
+ fn: (cr: CrelteRequest, route: Route) => void,
67
+ ): () => void;
68
+ on(
69
+ ev: 'afterRender',
70
+ fn: (cr: CrelteRequest, route: Route) => void,
60
71
  ): () => void;
61
- on(ev: 'beforeRender', fn: (cr: CrelteRequest) => void): () => void;
62
72
  on(ev: string, fn: (...args: any[]) => any): () => void {
63
73
  let set = this.inner.get(ev);
64
74
  if (!set) {
@@ -95,15 +105,20 @@ export default class Events {
95
105
  * Trigger an event
96
106
  */
97
107
  trigger(ev: 'beforeRequest', cr: CrelteRequest): (Promise<void> | void)[];
98
- trigger(ev: 'loadGlobalData', cr: CrelteRequest): Promise<any>[];
108
+ trigger(ev: 'loadGlobalData', cr: CrelteRequest): (Promise<any> | any)[];
99
109
  trigger(
100
110
  ev: 'beforeQueryEntry',
101
111
  cr: CrelteRequest,
102
112
  vars: EntryQueryVars,
103
113
  ): (Promise<void> | void)[];
104
- trigger(ev: 'afterLoadEntry', cr: CrelteRequest): Promise<any>[];
105
- trigger(ev: 'loadData', cr: CrelteRequest, entry: Entry): Promise<any>[];
114
+ trigger(ev: 'afterLoadEntry', cr: CrelteRequest): (Promise<any> | any)[];
115
+ trigger(
116
+ ev: 'loadData',
117
+ cr: CrelteRequest,
118
+ entry: Entry,
119
+ ): (Promise<any> | any)[];
106
120
  trigger(ev: 'beforeRender', cr: CrelteRequest, route: Route): void[];
121
+ trigger(ev: 'afterRender', cr: CrelteRequest, route: Route): void[];
107
122
  trigger(ev: string, ...args: any[]): any[] {
108
123
  const set = this.inner.get(ev);
109
124
  if (!set) return [];
@@ -1,10 +1,32 @@
1
- import { Crelte } from '../crelte.js';
1
+ import { Crelte, CrelteRequest } from '../crelte.js';
2
+ import { Request, Route } from '../routing/index.js';
2
3
 
3
4
  /**
4
5
  * A plugin
5
6
  */
6
7
  export interface Plugin {
7
8
  name: string;
9
+ /**
10
+ * The returned value will be used inside of CrelteRequest
11
+ */
12
+ toRequest?: (req: Request) => Plugin;
13
+
14
+ /**
15
+ * This will be called during the loadGlobalData phase.
16
+ */
17
+ loadGlobalData?: (cr: CrelteRequest) => Promise<void> | void;
18
+
19
+ /**
20
+ * This will be called during the loadData phase.
21
+ */
22
+ loadData?: (cr: CrelteRequest) => Promise<void> | void;
23
+
24
+ /**
25
+ * This will be called before the dom gets updated.
26
+ *
27
+ * At this point you can update variables or stores.
28
+ */
29
+ render?: (cr: CrelteRequest, route: Route) => void;
8
30
  }
9
31
 
10
32
  /**
@@ -46,4 +68,47 @@ export default class Plugins {
46
68
  get(name: string): Plugin | null {
47
69
  return this.plugins.get(name) ?? null;
48
70
  }
71
+
72
+ /**
73
+ * @hidden
74
+ */
75
+ z_toRequest(req: Request): Plugins {
76
+ const nPlugins = new Plugins();
77
+
78
+ for (let plugin of this.plugins.values()) {
79
+ if (typeof plugin.toRequest === 'function')
80
+ plugin = plugin.toRequest(req);
81
+
82
+ nPlugins.add(plugin);
83
+ }
84
+
85
+ return nPlugins;
86
+ }
87
+
88
+ /**
89
+ * @hidden
90
+ */
91
+ z_loadGlobalData(cr: CrelteRequest): (Promise<void> | void | undefined)[] {
92
+ return Array.from(this.plugins.values()).map(plugin =>
93
+ plugin.loadGlobalData?.(cr),
94
+ );
95
+ }
96
+
97
+ /**
98
+ * @hidden
99
+ */
100
+ z_loadData(cr: CrelteRequest): (Promise<void> | void | undefined)[] {
101
+ return Array.from(this.plugins.values()).map(plugin =>
102
+ plugin.loadData?.(cr),
103
+ );
104
+ }
105
+
106
+ /**
107
+ * @hidden
108
+ */
109
+ z_render(cr: CrelteRequest, route: Route): void {
110
+ for (const plugin of this.plugins.values()) {
111
+ plugin.render?.(cr, route);
112
+ }
113
+ }
49
114
  }
@@ -17,17 +17,35 @@ export type QueriesOptions = {
17
17
  /**
18
18
  * A GraphQL query
19
19
  *
20
- * You should almost never create this object directly
21
- * but instead import a graphql file or use the gql template.
20
+ * **You should almost never**
21
+ *
22
+ * When importing a graphql file you will get a {@link NamedQuery}
23
+ * or use the {@link gql} template function to create
24
+ * an {@link InlineQuery}.
25
+ * Alternatively you can use the {@link namedQuery} function to create
26
+ * a {@link NamedQuery}.
27
+ */
28
+ export type Query = InlineQuery | NamedQuery;
29
+
30
+ /**
31
+ * You should never create this type directly. It is returned from
32
+ * the {@link gql} template function.
33
+ */
34
+ export type InlineQuery = { path?: string; query: string };
35
+
36
+ /**
37
+ * Always create this object via the {@link namedQuery} function
38
+ */
39
+ export type NamedQuery = { queryName: string };
40
+
41
+ /**
42
+ * Create a NamedQuery for the given server query name.
43
+ *
44
+ * Prefer importing a graphql file instead.
22
45
  */
23
- export type Query =
24
- | {
25
- path?: string;
26
- query: string;
27
- }
28
- | {
29
- queryName: string;
30
- };
46
+ export function namedQuery(name: string): NamedQuery {
47
+ return { queryName: name };
48
+ }
31
49
 
32
50
  /** Returns true if the passed object is a GraphQlQuery */
33
51
  export function isQuery(obj: any): obj is Query {
@@ -102,8 +120,8 @@ export default class Queries {
102
120
  */
103
121
  static new(
104
122
  endpoint: string,
105
- frontend: string,
106
- ssrCache: SsrCache,
123
+ frontend: string | null = null,
124
+ ssrCache: SsrCache = new SsrCache(),
107
125
  opts: QueriesOptions = {},
108
126
  ): Queries {
109
127
  return new Queries(
@@ -113,6 +131,18 @@ export default class Queries {
113
131
  );
114
132
  }
115
133
 
134
+ /**
135
+ * Create a new Queries instance that always intented to call an
136
+ * external GraphQl endpoint.
137
+ */
138
+ static newExternal(endpoint: string, bearerToken?: string): Queries {
139
+ return new Queries(
140
+ new Inner(endpoint, null, new SsrCache(), { bearerToken }),
141
+ null,
142
+ null,
143
+ );
144
+ }
145
+
116
146
  /**
117
147
  * Run a GraphQl Query
118
148
  *
@@ -170,7 +200,7 @@ type InnerQueryOptions = {
170
200
 
171
201
  class Inner {
172
202
  endpoint: string;
173
- frontend: string;
203
+ frontend: string | null;
174
204
  ssrCache: SsrCache;
175
205
  private listeners: Map<
176
206
  string,
@@ -184,7 +214,7 @@ class Inner {
184
214
 
185
215
  constructor(
186
216
  endpoint: string,
187
- frontend: string,
217
+ frontend: string | null,
188
218
  ssrCache: SsrCache,
189
219
  opts: QueriesOptions = {},
190
220
  ) {
@@ -264,6 +294,9 @@ class Inner {
264
294
  let logName: string, url: URL;
265
295
 
266
296
  if ('queryName' in query) {
297
+ if (!this.frontend)
298
+ throw new Error('only inline queries supported');
299
+
267
300
  logName = `query (server: ${query.queryName})`;
268
301
  url = new URL(this.frontend);
269
302
  url.pathname = '/queries/' + query.queryName;
@@ -1,4 +1,4 @@
1
- import { isQuery, Query } from './Queries.js';
1
+ import { InlineQuery, isQuery } from './Queries.js';
2
2
 
3
3
  /**
4
4
  * Create a GraphQL query string with variables.
@@ -13,7 +13,7 @@ import { isQuery, Query } from './Queries.js';
13
13
  export function gql(
14
14
  strings: TemplateStringsArray | string[] | string,
15
15
  ...keys: unknown[]
16
- ): Query {
16
+ ): InlineQuery {
17
17
  if (typeof strings === 'string') strings = [strings];
18
18
 
19
19
  let query = '';
@@ -1,9 +1,13 @@
1
1
  import Queries, {
2
+ InlineQuery,
2
3
  isQuery,
4
+ namedQuery,
5
+ NamedQuery,
3
6
  QueriesOptions,
4
7
  Query,
5
8
  QueryOptions,
6
9
  } from '../queries/Queries.js';
10
+ import type CrelteServerRequest from '../server/CrelteServer.js';
7
11
  import { gql } from './gql.js';
8
12
  import QueryError, { QueryErrorResponse } from './QueryError.js';
9
13
  import { QueryVar, ValidIf, vars, varsIdsEqual } from './vars.js';
@@ -13,6 +17,9 @@ export {
13
17
  type QueriesOptions,
14
18
  type QueryOptions,
15
19
  type Query,
20
+ type InlineQuery as GqlQuery,
21
+ type NamedQuery,
22
+ namedQuery,
16
23
  isQuery,
17
24
  QueryError,
18
25
  type QueryErrorResponse,
@@ -32,6 +39,8 @@ export type InferVariableTypes<T> = {
32
39
  };
33
40
 
34
41
  /**
42
+ * Defines when a query can safely be cached.
43
+ *
35
44
  * #### Example
36
45
  * ```ts
37
46
  * import { vars, Caching } from 'crelte/queries';
@@ -48,3 +57,65 @@ export type InferVariableTypes<T> = {
48
57
  export type Caching<
49
58
  T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
50
59
  > = boolean | ((response: any, vars: InferVariableTypes<T>) => boolean);
60
+
61
+ /** use {@link Transfrom} */
62
+ export type TransformFn<
63
+ T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
64
+ > = (
65
+ response: any,
66
+ vars: InferVariableTypes<T>,
67
+ ) => void | any | Promise<void | any>;
68
+
69
+ /**
70
+ * Transforms the query response before it is returned or cached.
71
+ *
72
+ * #### Example
73
+ * ```ts
74
+ * export const transform: Transform<typeof variables> = (response, vars) => {
75
+ * for (const entry of response.entries) {
76
+ * entry.title = entry.title.toUpperCase();
77
+ * }
78
+ * };
79
+ * ```
80
+ */
81
+ export type Transform<
82
+ T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
83
+ F extends TransformFn<T> = TransformFn<T>,
84
+ > = (response: any, vars: InferVariableTypes<T>) => Awaited<ReturnType<F>>;
85
+
86
+ /** use {@link Handle} */
87
+ export type HandleFn<
88
+ T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
89
+ > = (csr: CrelteServerRequest, vars: InferVariableTypes<T>) => any;
90
+
91
+ /**
92
+ * Handles a query request.
93
+ *
94
+ * #### Example
95
+ * ```ts
96
+ * // queries/custom.ts
97
+ * import { vars, type Handle, gql, namedQuery } from 'crelte/queries';
98
+ *
99
+ * // It is good practice to have the query name inside the file
100
+ * export const customQuery = namedQuery('custom');
101
+ *
102
+ * export const variables = {
103
+ * name: vars.string(),
104
+ * };
105
+ *
106
+ * export const handle: Handle<typeof variables> = async (csr, vars) => {
107
+ * if (vars.name === 'demo') {
108
+ * throw new Response('not allowed', { status: 400 });
109
+ * }
110
+ *
111
+ * return { name: vars.name };
112
+ * };
113
+ * ```
114
+ */
115
+ export type Handle<
116
+ T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
117
+ F extends HandleFn<T> = HandleFn<T>,
118
+ > = (
119
+ csr: CrelteServerRequest,
120
+ vars: InferVariableTypes<T>,
121
+ ) => Awaited<ReturnType<F>>;