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
@@ -53,12 +53,14 @@ export class QueryVar<T = any> {
53
53
  private name: string | null;
54
54
  private type: 'any' | 'string' | 'number' | 'id' | 'ids';
55
55
  private flagNullable: boolean;
56
+ private defaultValue: T | undefined;
56
57
  private validIfFn: ValidIf<T>;
57
58
 
58
59
  constructor() {
59
60
  this.name = null;
60
61
  this.type = 'any';
61
62
  this.flagNullable = false;
63
+ this.defaultValue = undefined;
62
64
  this.validIfFn = () => true;
63
65
  }
64
66
 
@@ -87,6 +89,11 @@ export class QueryVar<T = any> {
87
89
  return this as QueryVar<T | null>;
88
90
  }
89
91
 
92
+ default(value: T): QueryVar<T> {
93
+ this.defaultValue = value;
94
+ return this;
95
+ }
96
+
90
97
  /**
91
98
  * Set a validation function for this variable
92
99
  *
@@ -103,6 +110,8 @@ export class QueryVar<T = any> {
103
110
  if (typeof v === 'undefined') v = null;
104
111
 
105
112
  if (v === null) {
113
+ if (this.defaultValue !== undefined) return this.defaultValue;
114
+
106
115
  if (!this.flagNullable)
107
116
  throw new Error(`variable ${this.name} cannot be null`);
108
117
 
@@ -139,7 +148,11 @@ export class QueryVar<T = any> {
139
148
  );
140
149
 
141
150
  if (v.length <= 0) {
151
+ if (this.defaultValue !== undefined)
152
+ return this.defaultValue;
153
+
142
154
  if (this.flagNullable) return null;
155
+
143
156
  throw new Error(
144
157
  `variable ${this.name} is not allowed to be empty`,
145
158
  );
@@ -1,5 +1,5 @@
1
1
  import Site from '../Site.js';
2
- import { objClone } from '../../utils.js';
2
+ import { objClone, promiseThen } from '../../utils.js';
3
3
  import BaseRoute, { RouteOrigin } from './BaseRoute.js';
4
4
  import Route, { TemplateModule } from './Route.js';
5
5
  import { Entry } from '../../loadData/index.js';
@@ -254,8 +254,15 @@ class RenderBarrier {
254
254
  const action = this.inner.add();
255
255
 
256
256
  return {
257
- ready: async () => {
258
- if (!this.inner.isOpen()) await action.ready(null);
257
+ ready: () => {
258
+ if (!this.inner.isOpen())
259
+ return promiseThen(
260
+ // wait for action.ready
261
+ action.ready(null),
262
+ // then return if it was cancelled
263
+ () => this.cancelled,
264
+ );
265
+
259
266
  return this.cancelled;
260
267
  },
261
268
  remove: () => {
@@ -292,7 +299,7 @@ export type DelayRender = {
292
299
  *
293
300
  * @returns if the render was cancelled
294
301
  */
295
- ready: () => Promise<boolean>;
302
+ ready: () => Promise<boolean> | boolean;
296
303
 
297
304
  /**
298
305
  * If youre not interested when the render happens anymore
@@ -123,8 +123,10 @@ export default class BaseRouter {
123
123
  }
124
124
 
125
125
  /**
126
- * todo check that the router uses the correct sites for each function
126
+ * Returns the default site tries to use a site which matches the preferred language
127
+ * else returns the primary site
127
128
  */
129
+ // todo check that the router uses the correct sites for each function
128
130
  defaultSite(): Site {
129
131
  return this.preferredSite() ?? this.primarySite();
130
132
  }
@@ -233,7 +235,7 @@ export default class BaseRouter {
233
235
  const req = this.targetToRequest(target, { origin: 'preload' });
234
236
  const current = this.route.get();
235
237
 
236
- // if the origin matches, the route will be able to be load
238
+ // if the origin matches, the route will be able to be loaded
237
239
  // so let's preload it
238
240
  if (current && current.url.origin === req.url.origin) {
239
241
  // todo i don't wan't to send a CrelteRequest?
@@ -107,7 +107,7 @@ export default class ClientRouter extends BaseRouter {
107
107
  async pushRequest(req: Request, _opts: RequestOptions = {}) {
108
108
  const url = req.url;
109
109
 
110
- return await this.handleRequest(req, route => {
110
+ return this.handleRequest(req, route => {
111
111
  window.history.pushState(
112
112
  route.z_toState(),
113
113
  '',
@@ -119,18 +119,13 @@ export default class ClientRouter extends BaseRouter {
119
119
  async replaceRequest(req: Request, _opts: RequestOptions = {}) {
120
120
  const url = req.url;
121
121
 
122
- try {
123
- return await this.handleRequest(req, () => {
124
- window.history.replaceState(
125
- req.z_toState(),
126
- '',
127
- url.pathname + url.search + url.hash,
128
- );
129
- });
130
- } catch (e) {
131
- console.warn('replacing route failed', e);
132
- throw e;
133
- }
122
+ return this.handleRequest(req, () => {
123
+ window.history.replaceState(
124
+ req.z_toState(),
125
+ '',
126
+ url.pathname + url.search + url.hash,
127
+ );
128
+ });
134
129
  }
135
130
 
136
131
  back(): void {
@@ -164,6 +159,7 @@ export default class ClientRouter extends BaseRouter {
164
159
  const req = this.targetToRequest(link.href, {
165
160
  origin: 'click',
166
161
  context: { ...link.dataset },
162
+ disableScroll: attributeToBool(link, 'data-disable-scroll'),
167
163
  });
168
164
  const currRoute = this.route.get();
169
165
  const routeEq =
@@ -189,7 +185,9 @@ export default class ClientRouter extends BaseRouter {
189
185
 
190
186
  if (
191
187
  link &&
192
- !link.hasAttribute('data-no-preload') &&
188
+ // todo remove data-no-preload
189
+ !attributeToBool(link, 'data-no-preload') &&
190
+ !attributeToBool(link, 'data-disable-preload') &&
193
191
  link.href
194
192
  ) {
195
193
  this.preload(link.href);
@@ -202,11 +200,10 @@ export default class ClientRouter extends BaseRouter {
202
200
  window.addEventListener('scroll', () => this.onScroll());
203
201
 
204
202
  window.addEventListener('popstate', async e => {
205
- if (!e.state?.route) return;
206
-
207
- const req = this.targetToRequest(window.location.href);
203
+ const req = this.targetToRequest(window.location.href, {
204
+ origin: 'pop',
205
+ });
208
206
  req.z_fillFromState(e.state);
209
- req.origin = 'pop';
210
207
 
211
208
  // todo handle errors
212
209
  this.handleRequest(req, () => {});
@@ -312,3 +309,14 @@ export default class ClientRouter extends BaseRouter {
312
309
  }
313
310
  }
314
311
  }
312
+
313
+ function attributeToBool(el: HTMLElement, attr: string): boolean {
314
+ switch (el.getAttribute(attr)) {
315
+ case '':
316
+ case 'true':
317
+ return true;
318
+ case 'false':
319
+ default:
320
+ return false;
321
+ }
322
+ }
@@ -226,19 +226,18 @@ export default class Router {
226
226
  });
227
227
  if (!req) return;
228
228
 
229
- try {
230
- return await this.inner.pushRequest(req, opts);
231
- } catch (e) {
229
+ return this.inner.pushRequest(req, opts).catch(e => {
232
230
  console.warn('pushing route failed', e);
233
231
  throw e;
234
- }
232
+ });
235
233
  }
236
234
 
237
235
  /**
238
236
  * @deprecated use push instead
239
237
  */
240
238
  pushState(route: Route | Request) {
241
- console.warn('pushState is deprecated, use push instead');
239
+ if (import.meta.env.DEV)
240
+ console.warn('pushState is deprecated, use push instead');
242
241
  this.push(route);
243
242
  }
244
243
 
@@ -287,19 +286,18 @@ export default class Router {
287
286
  });
288
287
  if (!req) return;
289
288
 
290
- try {
291
- return await this.inner.replaceRequest(req, opts);
292
- } catch (e) {
289
+ return this.inner.replaceRequest(req, opts).catch(e => {
293
290
  console.warn('replacing route failed', e);
294
291
  throw e;
295
- }
292
+ });
296
293
  }
297
294
 
298
295
  /**
299
296
  * @deprecated use replace instead
300
297
  */
301
298
  replaceState(route: Route | Request) {
302
- console.warn('replaceState is deprecated, use replace instead');
299
+ if (import.meta.env.DEV)
300
+ console.warn('replaceState is deprecated, use replace instead');
303
301
  this.replace(route);
304
302
  }
305
303
 
@@ -409,7 +407,8 @@ export default class Router {
409
407
  return nRouter;
410
408
  }
411
409
 
412
- _requestCompleted() {
410
+ /** @hidden */
411
+ z_requestCompleted() {
413
412
  this._request = null;
414
413
  }
415
414
  }
@@ -5,7 +5,7 @@ export function trimSlashEnd(str: string) {
5
5
  return str.endsWith('/') ? str.substring(0, str.length - 1) : str;
6
6
  }
7
7
 
8
- // same as ?? but only for undefined
8
+ /** same as ?? but only for undefined */
9
9
  export function orDef<T>(a: T | undefined, def: T): T {
10
10
  return a === undefined ? def : a;
11
11
  }
@@ -24,6 +24,7 @@ export default class CrelteServerRequest {
24
24
  private _sites: Site[];
25
25
  private _langs: string[];
26
26
  private _queries: Queries;
27
+ private _scookies: ServerCookies;
27
28
  private _cookies: Cookies;
28
29
 
29
30
  constructor(req: ServerRequest, opts: CrelteServerRequestOptions) {
@@ -37,7 +38,8 @@ export default class CrelteServerRequest {
37
38
  this._queries = opts.queries.z_toRequest(
38
39
  new Request(new URL(req.url), req.site),
39
40
  );
40
- this._cookies = new ServerCookies(req.headers);
41
+ this._scookies = new ServerCookies(req.headers);
42
+ this._cookies = new Cookies(this._scookies);
41
43
  }
42
44
 
43
45
  /**
@@ -145,6 +147,6 @@ export default class CrelteServerRequest {
145
147
 
146
148
  /** @hidden */
147
149
  z_finishResponse(resp: Response) {
148
- (this.cookies as ServerCookies)._populateHeaders(resp.headers);
150
+ this._scookies._populateHeaders(resp.headers);
149
151
  }
150
152
  }
@@ -139,9 +139,7 @@ export default class ServerRouter {
139
139
  this.endpointUrl,
140
140
  this.frontendUrl,
141
141
  new SsrCache(),
142
- {
143
- bearerToken: this.endpointToken,
144
- },
142
+ { bearerToken: this.endpointToken },
145
143
  );
146
144
 
147
145
  const csr = new CrelteServerRequest(nReq, {
@@ -152,12 +150,25 @@ export default class ServerRouter {
152
150
  queries,
153
151
  });
154
152
 
153
+ let resp: Response | null = null;
155
154
  for (const handler of handlers) {
156
- const res = await handler(csr);
157
- if (res) {
158
- csr.z_finishResponse(res);
159
- return res;
155
+ try {
156
+ const res = await handler(csr);
157
+ if (!res) continue;
158
+
159
+ resp = res;
160
+ } catch (e) {
161
+ if (!(e instanceof Response)) throw e;
162
+
163
+ resp = e;
160
164
  }
165
+
166
+ break;
167
+ }
168
+
169
+ if (resp) {
170
+ csr.z_finishResponse(resp);
171
+ return resp;
161
172
  }
162
173
 
163
174
  return null;
@@ -0,0 +1,224 @@
1
+ import CrelteServerRequest from '../CrelteServer.js';
2
+ import QueriesCaching from './QueriesCaching.js';
3
+ import { QueryVar, vars } from '../../queries/vars.js';
4
+ import { extractEntry } from '../../loadData/index.js';
5
+ import { calcKey } from '../../ssr/index.js';
6
+ import { CacheIfFn, newError, TransformFn, validateVars } from './routes.js';
7
+
8
+ export type QueryGqlArgs = {
9
+ vars: Record<string, QueryVar> | null;
10
+ cacheIfFn: CacheIfFn | null;
11
+ preventCaching: boolean;
12
+ transformFn: TransformFn | null;
13
+ };
14
+
15
+ // only internal
16
+ export default class QueryGqlRoute {
17
+ name: string;
18
+ query: string;
19
+ vars: Record<string, QueryVar> | null;
20
+ cacheIfFn: CacheIfFn | null;
21
+ transformFn: TransformFn | null;
22
+
23
+ constructor(name: string, query: string, args: QueryGqlArgs) {
24
+ if (args.cacheIfFn && !vars)
25
+ throw new Error(
26
+ 'queryRoute: ' +
27
+ name +
28
+ ' cannot have caching function if there are no ' +
29
+ 'variables defined',
30
+ );
31
+
32
+ this.name = name;
33
+ this.query = query;
34
+ this.vars = args.vars;
35
+ this.cacheIfFn = args.cacheIfFn;
36
+ this.transformFn = args.transformFn;
37
+
38
+ if (args.preventCaching) {
39
+ if (this.cacheIfFn) throw new Error('unreachable');
40
+ // prevent filling defaults
41
+ return;
42
+ }
43
+
44
+ // add default vars and cacheIfFn if we know the route
45
+ if (this.name === 'entry') this.fillEntryDefaults();
46
+ else if (this.name === 'global') this.fillGlobalDefaults();
47
+ else this.fillBasicDefaults();
48
+ }
49
+
50
+ private fillEntryDefaults() {
51
+ if (this.vars) return;
52
+
53
+ // the _setName step happens in parseVars which happens before setting
54
+ // the defaults, so since we're adding vars here we need to set the name
55
+ // manually
56
+ this.vars = {
57
+ siteId: vars.siteId().z_setName('siteId'),
58
+ uri: vars.string().z_setName('uri'),
59
+ };
60
+ this.cacheIfFn = res => !!extractEntry(res);
61
+ }
62
+
63
+ private fillGlobalDefaults() {
64
+ if (this.vars) return;
65
+
66
+ this.vars = { siteId: vars.siteId().z_setName('siteId') };
67
+ this.cacheIfFn = () => true;
68
+ }
69
+
70
+ /**
71
+ * This adds caching to queries containing `query {` or
72
+ * `query ($siteId: [QueryArgument) {` without any additional vars
73
+ */
74
+ private fillBasicDefaults() {
75
+ if (this.vars) return;
76
+
77
+ const NO_VAR_TEST = /(^|\s)query\s*{/;
78
+ const SITE_ID_VAR_TEST =
79
+ /(^|\s)query\s*\(\s*\$siteId\s*:\s*\[\s*QueryArgument\s*\]\s*\)\s*{/;
80
+
81
+ if (NO_VAR_TEST.test(this.query)) {
82
+ this.vars = {};
83
+ this.cacheIfFn = () => true;
84
+ } else if (SITE_ID_VAR_TEST.test(this.query)) {
85
+ this.vars = { siteId: vars.siteId().z_setName('siteId') };
86
+ this.cacheIfFn = () => true;
87
+ } else if (!this.query.includes('query')) {
88
+ // this warning might be shown to mutation queries or subscriptions
89
+ // in that case, the user should explicitly set caching to false
90
+ console.warn(
91
+ `cannot determine if query (${this.name}) is cacheable, see` +
92
+ ' https://github.com/crelte/crelte/issues/114 for infos',
93
+ );
94
+ }
95
+ }
96
+
97
+ private async transform(
98
+ jsonResp: Record<string, any>,
99
+ vars: Record<string, any>,
100
+ ): Promise<void> {
101
+ if (!this.transformFn || !jsonResp.data) return;
102
+
103
+ const transformed = await this.transformFn(jsonResp.data, vars);
104
+ if (typeof transformed !== 'undefined') jsonResp.data = transformed;
105
+ }
106
+
107
+ async handle(
108
+ caching: QueriesCaching,
109
+ csr: CrelteServerRequest,
110
+ ): Promise<Response> {
111
+ let vars: Record<string, any>;
112
+ try {
113
+ const reqVars = await csr.req.json();
114
+ vars = validateVars(this.vars, reqVars, caching.router);
115
+ if ('qName' in vars || 'xCraftSite' in vars)
116
+ throw new Error(
117
+ 'qName and xCraftSite are reserved variable names',
118
+ );
119
+ } catch (e) {
120
+ return newError(e, 400);
121
+ }
122
+
123
+ let logInfo: string | null = null;
124
+ if (caching.debug) {
125
+ logInfo = `[queries: ${this.name}] vars: ${JSON.stringify(vars)}`;
126
+ }
127
+
128
+ let previewToken: string | null = null;
129
+ let siteToken: string | null = null;
130
+
131
+ const reqSearch = new URL(csr.req.url).searchParams;
132
+
133
+ if (reqSearch.has('token')) {
134
+ previewToken = reqSearch.get('token');
135
+ } else if (reqSearch.has('siteToken')) {
136
+ siteToken = reqSearch.get('siteToken');
137
+ }
138
+
139
+ // check for x-craft-site header and pass it on
140
+ const xCraftSite = csr.req.headers.get('X-Craft-Site');
141
+
142
+ let cacheKey: string | null = null;
143
+ const useCache = !previewToken && caching.isEnabled();
144
+ if (useCache) {
145
+ cacheKey = await calcKey({ ...vars, qName: this.name, xCraftSite });
146
+ const cached = await caching.getCache(cacheKey);
147
+
148
+ if (logInfo) console.log(`${logInfo} ${cached ? 'hit' : 'miss'}`);
149
+
150
+ // we found something in the cache
151
+ if (cached) return Response.json(cached);
152
+ }
153
+
154
+ const headers: Record<string, string> = {
155
+ 'Content-Type': 'application/json',
156
+ };
157
+
158
+ const auth = csr.getEnv('ENDPOINT_TOKEN');
159
+ if (auth) headers['Authorization'] = 'Bearer ' + auth;
160
+
161
+ const url = new URL(csr.getEnv('ENDPOINT_URL'));
162
+ if (previewToken) url.searchParams.set('token', previewToken);
163
+ if (siteToken) url.searchParams.set('siteToken', siteToken);
164
+
165
+ const xDebug = csr.req.headers.get('X-Debug');
166
+ if (xDebug) headers['X-Debug'] = xDebug;
167
+
168
+ if (xCraftSite) headers['X-Craft-Site'] = xCraftSite;
169
+
170
+ // now execute the gql request
171
+ let resp: Response;
172
+ try {
173
+ resp = await fetch(url, {
174
+ method: 'POST',
175
+ headers,
176
+ body: JSON.stringify({
177
+ query: this.query,
178
+ variables: vars,
179
+ }),
180
+ });
181
+
182
+ // if the response is not ok we don't cache anything
183
+ // and just return the response
184
+ if (!resp.ok) return resp;
185
+ } catch (e) {
186
+ return newError(e, 500);
187
+ }
188
+
189
+ const respHeaders: Record<string, string> = {};
190
+ const xDebugLink = resp.headers.get('X-Debug-Link');
191
+ if (xDebugLink) respHeaders['X-Debug'] = xDebugLink;
192
+
193
+ let jsonResp: Record<string, any>;
194
+ try {
195
+ jsonResp = await resp.json();
196
+ if (!jsonResp || typeof jsonResp !== 'object')
197
+ throw new Error('invalid json response');
198
+ await this.transform(jsonResp, vars);
199
+ } catch (e) {
200
+ return newError(e, 500);
201
+ }
202
+
203
+ // also no caching for errors
204
+ if (jsonResp.errors) {
205
+ return Response.json(jsonResp, { headers: respHeaders });
206
+ }
207
+
208
+ // now we have a valid json resp.
209
+ // should we cache it?
210
+ if (cacheKey && this.cacheIfFn?.(jsonResp.data, vars)) {
211
+ try {
212
+ await caching.setCache(cacheKey, jsonResp);
213
+ if (logInfo) console.log(logInfo + ' set cache');
214
+ } catch (e) {
215
+ console.error('could not cache gql response', e);
216
+ }
217
+ // if caching is enabled but not used we warn
218
+ } else if (cacheKey && logInfo) {
219
+ console.warn('!! ' + logInfo + ' caching not allowed');
220
+ }
221
+
222
+ return Response.json(jsonResp, { headers: respHeaders });
223
+ }
224
+ }
@@ -0,0 +1,37 @@
1
+ import { QueryVar } from '../../queries/vars.js';
2
+ import CrelteServerRequest from '../CrelteServer.js';
3
+ import ServerRouter from '../ServerRouter.js';
4
+ import { HandleFn, newError, validateVars } from './routes.js';
5
+
6
+ // only internal
7
+ export default class QueryHandleRoute {
8
+ name: string;
9
+ handleFn: HandleFn;
10
+ vars: Record<string, QueryVar> | null;
11
+
12
+ constructor(
13
+ name: string,
14
+ handleFn: HandleFn,
15
+ vars: Record<string, QueryVar> | null,
16
+ ) {
17
+ this.name = name;
18
+ this.handleFn = handleFn;
19
+ this.vars = vars;
20
+ }
21
+
22
+ async handle(
23
+ cs: ServerRouter,
24
+ csr: CrelteServerRequest,
25
+ ): Promise<Response> {
26
+ let vars: Record<string, any>;
27
+ try {
28
+ const reqVars = await csr.req.json();
29
+ vars = validateVars(this.vars, reqVars, cs);
30
+ } catch (e) {
31
+ return newError(e, 400);
32
+ }
33
+
34
+ const res = await this.handleFn(csr, vars);
35
+ return Response.json({ data: res });
36
+ }
37
+ }