crelte 0.5.15 → 0.6.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.
- package/dist/blocks/Blocks.svelte +5 -4
- package/dist/blocks/Blocks.svelte.d.ts +4 -19
- package/dist/blocks/Blocks.svelte.d.ts.map +1 -1
- package/dist/crelte.d.ts +2 -2
- package/dist/crelte.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/init/InternalApp.d.ts.map +1 -1
- package/dist/init/InternalApp.js +2 -2
- package/dist/init/server.js +4 -4
- package/dist/loadData/loadData.d.ts +1 -1
- package/dist/loadData/loadData.d.ts.map +1 -1
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +15 -11
- package/dist/queries/Queries.d.ts +16 -27
- package/dist/queries/Queries.d.ts.map +1 -1
- package/dist/queries/Queries.js +13 -20
- package/dist/queries/gql.d.ts +2 -2
- package/dist/queries/gql.d.ts.map +1 -1
- package/dist/queries/gql.js +3 -2
- package/dist/queries/index.d.ts +13 -13
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +2 -2
- package/dist/queries/vars.d.ts +3 -3
- package/dist/queries/vars.d.ts.map +1 -1
- package/dist/queries/vars.js +3 -3
- package/dist/routing/route/BaseRoute.d.ts +4 -0
- package/dist/routing/route/BaseRoute.d.ts.map +1 -1
- package/dist/routing/route/BaseRoute.js +6 -0
- package/dist/routing/router/BaseRouter.d.ts.map +1 -1
- package/dist/routing/router/BaseRouter.js +4 -3
- package/dist/routing/router/ClientRouter.d.ts +1 -0
- package/dist/routing/router/ClientRouter.d.ts.map +1 -1
- package/dist/routing/router/ClientRouter.js +22 -7
- package/dist/server/CrelteServer.d.ts +1 -1
- package/dist/server/CrelteServer.d.ts.map +1 -1
- package/dist/server/ServerRouter.d.ts +1 -1
- package/dist/server/ServerRouter.d.ts.map +1 -1
- package/dist/server/queries/QueriesCaching.d.ts +0 -1
- package/dist/server/queries/QueriesCaching.d.ts.map +1 -1
- package/dist/server/queries/QueriesCaching.js +0 -2
- package/dist/server/queries/QueryGqlRoute.d.ts +2 -0
- package/dist/server/queries/QueryGqlRoute.d.ts.map +1 -1
- package/dist/server/queries/QueryGqlRoute.js +22 -14
- package/dist/server/queries/QueryHandleRoute.d.ts +1 -2
- package/dist/server/queries/QueryHandleRoute.d.ts.map +1 -1
- package/dist/server/queries/QueryHandleRoute.js +2 -2
- package/dist/server/queries/queries.d.ts.map +1 -1
- package/dist/server/queries/queries.js +2 -1
- package/dist/server/queries/routes.d.ts +3 -4
- package/dist/server/queries/routes.d.ts.map +1 -1
- package/dist/server/queries/routes.js +3 -3
- package/dist/server/shared.d.ts +5 -4
- package/dist/server/shared.d.ts.map +1 -1
- package/dist/server/shared.js +26 -16
- package/dist/ssr/SsrCache.d.ts +2 -2
- package/dist/ssr/SsrCache.d.ts.map +1 -1
- package/dist/ssr/SsrCache.js +10 -16
- package/dist/std/encdec/escaping.d.ts +2 -0
- package/dist/std/encdec/escaping.d.ts.map +1 -0
- package/dist/std/encdec/escaping.js +41 -0
- package/dist/std/encdec/index.d.ts +13 -0
- package/dist/std/encdec/index.d.ts.map +1 -0
- package/dist/std/encdec/index.js +15 -0
- package/dist/std/index.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +17 -0
- package/dist/vite/index.d.ts +1 -5
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +39 -79
- package/package.json +12 -7
- package/src/blocks/Blocks.svelte +4 -3
- package/src/crelte.ts +2 -2
- package/src/index.ts +2 -2
- package/src/init/InternalApp.ts +3 -4
- package/src/init/server.ts +4 -4
- package/src/loadData/loadData.ts +0 -2
- package/src/node/index.ts +24 -9
- package/src/queries/Queries.ts +31 -36
- package/src/queries/gql.ts +7 -3
- package/src/queries/index.ts +19 -16
- package/src/queries/vars.ts +5 -7
- package/src/routing/route/BaseRoute.ts +8 -7
- package/src/routing/router/BaseRouter.ts +6 -3
- package/src/routing/router/ClientRouter.ts +29 -8
- package/src/server/CrelteServer.ts +1 -1
- package/src/server/ServerRouter.ts +1 -1
- package/src/server/queries/QueriesCaching.ts +0 -2
- package/src/server/queries/QueryGqlRoute.ts +24 -20
- package/src/server/queries/QueryHandleRoute.ts +2 -6
- package/src/server/queries/queries.ts +2 -3
- package/src/server/queries/routes.ts +9 -6
- package/src/server/shared.ts +34 -16
- package/src/ssr/SsrCache.ts +13 -23
- package/src/std/encdec/escaping.ts +47 -0
- package/src/std/encdec/index.ts +16 -0
- package/src/std/index.ts +1 -6
- package/src/utils.ts +21 -0
- package/src/vite/index.ts +51 -87
- package/types.d.ts +1 -1
package/src/queries/Queries.ts
CHANGED
|
@@ -17,44 +17,28 @@ export type QueriesOptions = {
|
|
|
17
17
|
/**
|
|
18
18
|
* A GraphQL query
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Alternatively you can use the {@link namedQuery} function to create
|
|
26
|
-
* a {@link NamedQuery}.
|
|
20
|
+
* When importing a graphql file you will get a string with the name of the
|
|
21
|
+
* query or you can use the {@link gql} template function to create
|
|
22
|
+
* an {@link GqlQuery}.
|
|
23
|
+
* Alternatively you can use the name of the query directly, note thought
|
|
24
|
+
* that this will not reload when you're query changes.
|
|
27
25
|
*/
|
|
28
|
-
export type Query =
|
|
26
|
+
export type Query = string | GqlQuery;
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
29
|
* You should never create this type directly. It is returned from
|
|
32
30
|
* the {@link gql} template function.
|
|
33
31
|
*/
|
|
34
|
-
export type
|
|
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.
|
|
45
|
-
*
|
|
46
|
-
* #### Note
|
|
47
|
-
* This needs to match the name of the js/ts file.
|
|
48
|
-
*/
|
|
49
|
-
export function namedQuery(name: string): NamedQuery {
|
|
50
|
-
return { queryName: name };
|
|
51
|
-
}
|
|
32
|
+
export type GqlQuery = { path?: string; query: string };
|
|
52
33
|
|
|
53
|
-
/** Returns true if the passed object is a
|
|
34
|
+
/** Returns true if the passed object is a Query */
|
|
54
35
|
export function isQuery(obj: any): obj is Query {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
36
|
+
return (
|
|
37
|
+
typeof obj === 'string' ||
|
|
38
|
+
(typeof obj === 'object' &&
|
|
39
|
+
obj !== null &&
|
|
40
|
+
typeof obj.query === 'string')
|
|
41
|
+
);
|
|
58
42
|
}
|
|
59
43
|
|
|
60
44
|
/**
|
|
@@ -74,11 +58,18 @@ export type QueryOptions = {
|
|
|
74
58
|
bearerToken?: string;
|
|
75
59
|
|
|
76
60
|
/**
|
|
77
|
-
* Configure caching
|
|
61
|
+
* Configure Crelte's local query caching.
|
|
78
62
|
* @default true
|
|
79
63
|
*/
|
|
80
64
|
caching?: boolean;
|
|
81
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Allow Craft's configured GraphQL caching. Set false to bypass it.
|
|
68
|
+
* Does not affect Crelte's caching.
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
craftCaching?: boolean;
|
|
72
|
+
|
|
82
73
|
/**
|
|
83
74
|
* Usually automatically set
|
|
84
75
|
*/
|
|
@@ -135,7 +126,7 @@ export default class Queries {
|
|
|
135
126
|
}
|
|
136
127
|
|
|
137
128
|
/**
|
|
138
|
-
* Create a new Queries instance that always intented to call an
|
|
129
|
+
* Create a new Queries instance that is always intented to call an
|
|
139
130
|
* external GraphQl endpoint.
|
|
140
131
|
*/
|
|
141
132
|
static newExternal(endpoint: string, bearerToken?: string): Queries {
|
|
@@ -181,6 +172,7 @@ export default class Queries {
|
|
|
181
172
|
bearerToken,
|
|
182
173
|
xCraftSiteId,
|
|
183
174
|
caching: opts.caching ?? true,
|
|
175
|
+
craftCaching: opts.craftCaching ?? true,
|
|
184
176
|
});
|
|
185
177
|
}
|
|
186
178
|
|
|
@@ -199,6 +191,7 @@ type InnerQueryOptions = {
|
|
|
199
191
|
bearerToken: string | null;
|
|
200
192
|
xCraftSiteId: number | null;
|
|
201
193
|
caching: boolean;
|
|
194
|
+
craftCaching: boolean;
|
|
202
195
|
};
|
|
203
196
|
|
|
204
197
|
class Inner {
|
|
@@ -296,13 +289,13 @@ class Inner {
|
|
|
296
289
|
): Promise<unknown> {
|
|
297
290
|
let logName: string, url: URL;
|
|
298
291
|
|
|
299
|
-
if ('
|
|
292
|
+
if (typeof query === 'string') {
|
|
300
293
|
if (!this.frontend)
|
|
301
294
|
throw new Error('only inline queries supported');
|
|
302
295
|
|
|
303
|
-
logName = `query (server: ${query
|
|
296
|
+
logName = `query (server: ${query})`;
|
|
304
297
|
url = new URL(this.frontend);
|
|
305
|
-
url.pathname = '/queries/' + query
|
|
298
|
+
url.pathname = '/queries/' + query;
|
|
306
299
|
} else {
|
|
307
300
|
logName = `query (${query.path ?? 'unknown'})`;
|
|
308
301
|
url = new URL(this.endpoint);
|
|
@@ -320,6 +313,8 @@ class Inner {
|
|
|
320
313
|
if (opts.xCraftSiteId)
|
|
321
314
|
headers['X-Craft-Site'] = opts.xCraftSiteId.toString();
|
|
322
315
|
|
|
316
|
+
if (!opts.craftCaching) headers['X-Craft-GQL-Cache'] = 'no-cache';
|
|
317
|
+
|
|
323
318
|
if (this.loggingRequests) {
|
|
324
319
|
console.log(logName + ' to:', url.toString(), vars, headers);
|
|
325
320
|
headers['X-Debug'] = 'enable';
|
|
@@ -329,7 +324,7 @@ class Inner {
|
|
|
329
324
|
if (this.loggingTiming) timing = Date.now();
|
|
330
325
|
|
|
331
326
|
let body: any;
|
|
332
|
-
if ('
|
|
327
|
+
if (typeof query === 'string') {
|
|
333
328
|
body = vars;
|
|
334
329
|
} else {
|
|
335
330
|
body = { query: query.query, variables: vars };
|
package/src/queries/gql.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GqlQuery } from './Queries.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Create a GraphQL query string with variables.
|
|
@@ -13,7 +13,7 @@ import { InlineQuery, isQuery } from './Queries.js';
|
|
|
13
13
|
export function gql(
|
|
14
14
|
strings: TemplateStringsArray | string[] | string,
|
|
15
15
|
...keys: unknown[]
|
|
16
|
-
):
|
|
16
|
+
): GqlQuery {
|
|
17
17
|
if (typeof strings === 'string') strings = [strings];
|
|
18
18
|
|
|
19
19
|
let query = '';
|
|
@@ -24,7 +24,11 @@ export function gql(
|
|
|
24
24
|
const variable = keys[i];
|
|
25
25
|
|
|
26
26
|
// nesting support
|
|
27
|
-
if (
|
|
27
|
+
if (
|
|
28
|
+
typeof variable === 'object' &&
|
|
29
|
+
variable !== null &&
|
|
30
|
+
'query' in variable
|
|
31
|
+
) {
|
|
28
32
|
query += variable.query;
|
|
29
33
|
} else if (typeof variable === 'string') {
|
|
30
34
|
query += variable;
|
package/src/queries/index.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import Queries, {
|
|
2
|
-
|
|
2
|
+
GqlQuery,
|
|
3
3
|
isQuery,
|
|
4
|
-
namedQuery,
|
|
5
|
-
NamedQuery,
|
|
6
4
|
QueriesOptions,
|
|
7
5
|
Query,
|
|
8
6
|
QueryOptions,
|
|
9
7
|
} from '../queries/Queries.js';
|
|
10
8
|
import type CrelteServerRequest from '../server/CrelteServer.js';
|
|
11
|
-
import ServerRouter from '../server/ServerRouter.js';
|
|
12
9
|
import { gql } from './gql.js';
|
|
13
10
|
import QueryError, { QueryErrorResponse } from './QueryError.js';
|
|
14
11
|
import { QueryVar, ValidIf, vars, varsIdsEqual } from './vars.js';
|
|
@@ -18,9 +15,7 @@ export {
|
|
|
18
15
|
type QueriesOptions,
|
|
19
16
|
type QueryOptions,
|
|
20
17
|
type Query,
|
|
21
|
-
type
|
|
22
|
-
type NamedQuery,
|
|
23
|
-
namedQuery,
|
|
18
|
+
type GqlQuery,
|
|
24
19
|
isQuery,
|
|
25
20
|
QueryError,
|
|
26
21
|
type QueryErrorResponse,
|
|
@@ -53,14 +48,14 @@ export type InferVariableTypes<T> = {
|
|
|
53
48
|
* category: vars.id()
|
|
54
49
|
* };
|
|
55
50
|
*
|
|
56
|
-
* export const validVars: ValidVars<typeof variables> = (vars,
|
|
51
|
+
* export const validVars: ValidVars<typeof variables> = (vars, csr) => {
|
|
57
52
|
* if (!vars.category === 5) throw new Error('category needs to be 5');
|
|
58
53
|
* };
|
|
59
54
|
* ```
|
|
60
55
|
*/
|
|
61
56
|
export type ValidVars<T extends Record<string, QueryVar<any>>> = (
|
|
62
57
|
vars: InferVariableTypes<T>,
|
|
63
|
-
|
|
58
|
+
csr: CrelteServerRequest,
|
|
64
59
|
) => void | boolean;
|
|
65
60
|
|
|
66
61
|
/**
|
|
@@ -76,12 +71,18 @@ export type ValidVars<T extends Record<string, QueryVar<any>>> = (
|
|
|
76
71
|
* };
|
|
77
72
|
*
|
|
78
73
|
* export const caching: Caching<typeof variables> =
|
|
79
|
-
* (queryResponse, variables) => !!queryResponse.entries.length;
|
|
74
|
+
* (queryResponse, variables, csr) => !!queryResponse.entries.length;
|
|
80
75
|
* ```
|
|
81
76
|
*/
|
|
82
77
|
export type Caching<
|
|
83
78
|
T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
|
|
84
|
-
> =
|
|
79
|
+
> =
|
|
80
|
+
| boolean
|
|
81
|
+
| ((
|
|
82
|
+
response: any,
|
|
83
|
+
vars: InferVariableTypes<T>,
|
|
84
|
+
csr: CrelteServerRequest,
|
|
85
|
+
) => boolean);
|
|
85
86
|
|
|
86
87
|
/** use {@link Transfrom} */
|
|
87
88
|
export type TransformFn<
|
|
@@ -89,6 +90,7 @@ export type TransformFn<
|
|
|
89
90
|
> = (
|
|
90
91
|
response: any,
|
|
91
92
|
vars: InferVariableTypes<T>,
|
|
93
|
+
csr: CrelteServerRequest,
|
|
92
94
|
) => void | any | Promise<void | any>;
|
|
93
95
|
|
|
94
96
|
/**
|
|
@@ -96,11 +98,12 @@ export type TransformFn<
|
|
|
96
98
|
*
|
|
97
99
|
* #### Example
|
|
98
100
|
* ```ts
|
|
99
|
-
* export const transform: Transform<typeof variables> =
|
|
100
|
-
*
|
|
101
|
-
* entry
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* export const transform: Transform<typeof variables> =
|
|
102
|
+
* (response, vars, csr) => {
|
|
103
|
+
* for (const entry of response.entries) {
|
|
104
|
+
* entry.title = entry.title.toUpperCase();
|
|
105
|
+
* }
|
|
106
|
+
* };
|
|
104
107
|
* ```
|
|
105
108
|
*/
|
|
106
109
|
export type Transform<
|
package/src/queries/vars.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import CrelteServerRequest from '../server/CrelteServer.js';
|
|
2
2
|
|
|
3
3
|
export const vars = {
|
|
4
4
|
/**
|
|
@@ -65,14 +65,12 @@ export const vars = {
|
|
|
65
65
|
siteId: (): QueryVar<number> =>
|
|
66
66
|
new QueryVar()
|
|
67
67
|
.number()
|
|
68
|
-
.validIf(
|
|
69
|
-
(num, router) => !!router.sites.find(site => site.id === num),
|
|
70
|
-
),
|
|
68
|
+
.validIf((num, csr) => !!csr.sites.find(site => site.id === num)),
|
|
71
69
|
};
|
|
72
70
|
|
|
73
71
|
/// either throw with an error which will get returned in a 400 response or
|
|
74
72
|
// return false if the value is not valid
|
|
75
|
-
export type ValidIf<T> = (v: T,
|
|
73
|
+
export type ValidIf<T> = (v: T, csr: CrelteServerRequest) => boolean;
|
|
76
74
|
|
|
77
75
|
export class QueryVar<T = any> {
|
|
78
76
|
private name: string | null;
|
|
@@ -135,7 +133,7 @@ export class QueryVar<T = any> {
|
|
|
135
133
|
return this;
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
validValue(v: any,
|
|
136
|
+
validValue(v: any, csr: CrelteServerRequest): T | null {
|
|
139
137
|
// undefined is treated as null
|
|
140
138
|
if (typeof v === 'undefined') v = null;
|
|
141
139
|
|
|
@@ -229,7 +227,7 @@ export class QueryVar<T = any> {
|
|
|
229
227
|
throw new Error('uknown type ' + this.type);
|
|
230
228
|
}
|
|
231
229
|
|
|
232
|
-
if (!this.validIfFn(v,
|
|
230
|
+
if (!this.validIfFn(v, csr))
|
|
233
231
|
throw new Error(`variable ${this.name} is not valid`);
|
|
234
232
|
|
|
235
233
|
return v;
|
|
@@ -31,13 +31,7 @@ export type BaseRouteOptions = {
|
|
|
31
31
|
* `replace` and `push` will not call loadData
|
|
32
32
|
*/
|
|
33
33
|
export type RouteOrigin =
|
|
34
|
-
| '
|
|
35
|
-
| 'manual'
|
|
36
|
-
| 'preload'
|
|
37
|
-
| 'click'
|
|
38
|
-
| 'pop'
|
|
39
|
-
| 'replace'
|
|
40
|
-
| 'push';
|
|
34
|
+
'init' | 'manual' | 'preload' | 'click' | 'pop' | 'replace' | 'push';
|
|
41
35
|
|
|
42
36
|
/**
|
|
43
37
|
* A Route contains information about the current page for example the url and
|
|
@@ -298,6 +292,13 @@ export default class BaseRoute {
|
|
|
298
292
|
this.z_context[key] = value;
|
|
299
293
|
}
|
|
300
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Returns the site preview token, or null if it is missing or empty.
|
|
297
|
+
*/
|
|
298
|
+
getSiteToken(): string | null {
|
|
299
|
+
return this.search.get('siteToken') || null;
|
|
300
|
+
}
|
|
301
|
+
|
|
301
302
|
/**
|
|
302
303
|
* Returns true if the route is in live preview mode
|
|
303
304
|
*/
|
|
@@ -235,9 +235,12 @@ export default class BaseRouter {
|
|
|
235
235
|
const req = this.targetToRequest(target, { origin: 'preload' });
|
|
236
236
|
const current = this.route.get();
|
|
237
237
|
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
// Preload only within the current origin and site preview context.
|
|
239
|
+
if (
|
|
240
|
+
current &&
|
|
241
|
+
current.url.origin === req.url.origin &&
|
|
242
|
+
current.getSiteToken() === req.getSiteToken()
|
|
243
|
+
) {
|
|
241
244
|
// todo i don't wan't to send a CrelteRequest?
|
|
242
245
|
// todo, it would be nice if this could not have any side effects
|
|
243
246
|
// but at the moment cr.router.open will cause a redirect
|
|
@@ -20,6 +20,7 @@ export default class ClientRouter extends BaseRouter {
|
|
|
20
20
|
|
|
21
21
|
this.scrollDebounceTimeout = null;
|
|
22
22
|
this.preloadOnMouseOver = opts.preloadOnMouseOver;
|
|
23
|
+
|
|
23
24
|
this.onError = () => {};
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -82,13 +83,7 @@ export default class ClientRouter extends BaseRouter {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
// new site we need to do a window.location.href call
|
|
87
|
-
if (
|
|
88
|
-
(current && current.url.origin !== req.url.origin) ||
|
|
89
|
-
// @ts-ignore
|
|
90
|
-
import.meta.env.SSR
|
|
91
|
-
) {
|
|
86
|
+
if (this.requiresPageLoad(req)) {
|
|
92
87
|
window.location.href = req.url.href;
|
|
93
88
|
return;
|
|
94
89
|
}
|
|
@@ -107,6 +102,11 @@ export default class ClientRouter extends BaseRouter {
|
|
|
107
102
|
async pushRequest(req: Request, _opts: RequestOptions = {}) {
|
|
108
103
|
const url = req.url;
|
|
109
104
|
|
|
105
|
+
if (this.requiresPageLoad(req)) {
|
|
106
|
+
window.location.href = url.href;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
110
|
return this.handleRequest(req, route => {
|
|
111
111
|
window.history.pushState(
|
|
112
112
|
route.z_toState(),
|
|
@@ -119,6 +119,11 @@ export default class ClientRouter extends BaseRouter {
|
|
|
119
119
|
async replaceRequest(req: Request, _opts: RequestOptions = {}) {
|
|
120
120
|
const url = req.url;
|
|
121
121
|
|
|
122
|
+
if (this.requiresPageLoad(req)) {
|
|
123
|
+
window.location.replace(url.href);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
return this.handleRequest(req, () => {
|
|
123
128
|
window.history.replaceState(
|
|
124
129
|
req.z_toState(),
|
|
@@ -132,6 +137,15 @@ export default class ClientRouter extends BaseRouter {
|
|
|
132
137
|
window.history.back();
|
|
133
138
|
}
|
|
134
139
|
|
|
140
|
+
private requiresPageLoad(req: Request): boolean {
|
|
141
|
+
const current = this.route.get();
|
|
142
|
+
return (
|
|
143
|
+
!!current &&
|
|
144
|
+
(current.url.origin !== req.url.origin ||
|
|
145
|
+
current.getSiteToken() !== req.getSiteToken())
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
/**
|
|
136
150
|
* This returns a route if it was handled else if an error occured
|
|
137
151
|
* or the request was cancelled returns void
|
|
@@ -205,6 +219,11 @@ export default class ClientRouter extends BaseRouter {
|
|
|
205
219
|
});
|
|
206
220
|
req.z_fillFromState(e.state);
|
|
207
221
|
|
|
222
|
+
if (this.requiresPageLoad(req)) {
|
|
223
|
+
window.location.reload();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
208
227
|
// todo handle errors
|
|
209
228
|
this.handleRequest(req, () => {});
|
|
210
229
|
});
|
|
@@ -249,7 +268,9 @@ export default class ClientRouter extends BaseRouter {
|
|
|
249
268
|
updateScroll(cr: CrelteRequest, _route: Route): void {
|
|
250
269
|
const req = cr.req;
|
|
251
270
|
|
|
252
|
-
if (req.disableScroll)
|
|
271
|
+
if (req.disableScroll) {
|
|
272
|
+
return this.onScroll(); // make sure the current scrollY get's stored to the history
|
|
273
|
+
}
|
|
253
274
|
|
|
254
275
|
// scroll to target
|
|
255
276
|
let scrollTo:
|
|
@@ -86,7 +86,7 @@ export default class CrelteServerRequest {
|
|
|
86
86
|
* returns an env variable from the craft/.env file.
|
|
87
87
|
*/
|
|
88
88
|
getEnv(name: 'ENDPOINT_URL'): string;
|
|
89
|
-
getEnv(name: '
|
|
89
|
+
getEnv(name: 'CRAFT_BASE_CP_URL'): string;
|
|
90
90
|
getEnv(name: 'FRONTEND_URL'): string;
|
|
91
91
|
getEnv(name: string): string | null;
|
|
92
92
|
getEnv(name: string): string | null {
|
|
@@ -81,7 +81,7 @@ export default class ServerRouter {
|
|
|
81
81
|
* returns an env variable from the craft/.env file.
|
|
82
82
|
*/
|
|
83
83
|
getEnv(name: 'ENDPOINT_URL'): string;
|
|
84
|
-
getEnv(name: '
|
|
84
|
+
getEnv(name: 'CRAFT_BASE_CP_URL'): string;
|
|
85
85
|
getEnv(name: string): string | null;
|
|
86
86
|
getEnv(name: string): string | null {
|
|
87
87
|
return this.env.get(name) ?? null;
|
|
@@ -13,7 +13,6 @@ export default class QueriesCaching {
|
|
|
13
13
|
private cacheDir: string | null;
|
|
14
14
|
private validBearerToken: string | null;
|
|
15
15
|
debug: boolean;
|
|
16
|
-
router: ServerRouter;
|
|
17
16
|
|
|
18
17
|
constructor(
|
|
19
18
|
platform: Platform,
|
|
@@ -21,7 +20,6 @@ export default class QueriesCaching {
|
|
|
21
20
|
opts?: QueriesCachingOptions,
|
|
22
21
|
) {
|
|
23
22
|
this.platform = platform;
|
|
24
|
-
this.router = cs;
|
|
25
23
|
this.debug = !!opts?.debug;
|
|
26
24
|
|
|
27
25
|
const enableCaching = cs.getEnv('CACHING') === 'true';
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
validateVars,
|
|
11
11
|
ValidIfFn,
|
|
12
12
|
} from './routes.js';
|
|
13
|
+
import { cryptoSha1Hash } from '../../utils.js';
|
|
13
14
|
|
|
14
15
|
export type QueryGqlArgs = {
|
|
15
16
|
vars: Record<string, QueryVar> | null;
|
|
@@ -23,6 +24,7 @@ export type QueryGqlArgs = {
|
|
|
23
24
|
export default class QueryGqlRoute {
|
|
24
25
|
name: string;
|
|
25
26
|
query: string;
|
|
27
|
+
queryHash: string;
|
|
26
28
|
vars: Record<string, QueryVar> | null;
|
|
27
29
|
validIfFn: ValidIfFn | null;
|
|
28
30
|
cacheIfFn: CacheIfFn | null;
|
|
@@ -39,6 +41,7 @@ export default class QueryGqlRoute {
|
|
|
39
41
|
|
|
40
42
|
this.name = name;
|
|
41
43
|
this.query = query;
|
|
44
|
+
this.queryHash = query;
|
|
42
45
|
this.vars = args.vars;
|
|
43
46
|
this.validIfFn = args.validIfFn;
|
|
44
47
|
this.cacheIfFn = args.cacheIfFn;
|
|
@@ -56,6 +59,10 @@ export default class QueryGqlRoute {
|
|
|
56
59
|
else this.fillBasicDefaults();
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
async init() {
|
|
63
|
+
this.queryHash = await cryptoSha1Hash(this.query);
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
private fillEntryDefaults() {
|
|
60
67
|
if (this.vars) return;
|
|
61
68
|
|
|
@@ -121,16 +128,11 @@ export default class QueryGqlRoute {
|
|
|
121
128
|
let vars: Record<string, any>;
|
|
122
129
|
try {
|
|
123
130
|
const reqVars = await csr.req.json();
|
|
124
|
-
vars = validateVars(
|
|
125
|
-
this.vars,
|
|
126
|
-
reqVars,
|
|
127
|
-
this.validIfFn,
|
|
128
|
-
caching.router,
|
|
129
|
-
);
|
|
131
|
+
vars = validateVars(this.vars, reqVars, this.validIfFn, csr);
|
|
130
132
|
|
|
131
|
-
if ('qName'
|
|
133
|
+
if (['qName', 'qHash', 'xCraftSite'].some(name => name in vars))
|
|
132
134
|
throw new Error(
|
|
133
|
-
'qName and xCraftSite are reserved variable names',
|
|
135
|
+
'qName, qHash and xCraftSite are reserved variable names',
|
|
134
136
|
);
|
|
135
137
|
} catch (e) {
|
|
136
138
|
return newError(e, 400);
|
|
@@ -141,24 +143,22 @@ export default class QueryGqlRoute {
|
|
|
141
143
|
logInfo = `[queries: ${this.name}] vars: ${JSON.stringify(vars)}`;
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
let previewToken: string | null = null;
|
|
145
|
-
let siteToken: string | null = null;
|
|
146
|
-
|
|
147
146
|
const reqSearch = new URL(csr.req.url).searchParams;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
previewToken = reqSearch.get('token');
|
|
151
|
-
} else if (reqSearch.has('siteToken')) {
|
|
152
|
-
siteToken = reqSearch.get('siteToken');
|
|
153
|
-
}
|
|
147
|
+
const previewToken = reqSearch.get('token');
|
|
148
|
+
const siteToken = reqSearch.get('siteToken');
|
|
154
149
|
|
|
155
150
|
// check for x-craft-site header and pass it on
|
|
156
151
|
const xCraftSite = csr.req.headers.get('X-Craft-Site');
|
|
157
152
|
|
|
158
153
|
let cacheKey: string | null = null;
|
|
159
|
-
const useCache = !previewToken && caching.isEnabled();
|
|
154
|
+
const useCache = !previewToken && !siteToken && caching.isEnabled();
|
|
160
155
|
if (useCache) {
|
|
161
|
-
cacheKey = await calcKey({
|
|
156
|
+
cacheKey = await calcKey({
|
|
157
|
+
...vars,
|
|
158
|
+
qName: this.name,
|
|
159
|
+
qHash: this.queryHash,
|
|
160
|
+
xCraftSite,
|
|
161
|
+
});
|
|
162
162
|
const cached = await caching.getCache(cacheKey);
|
|
163
163
|
|
|
164
164
|
if (logInfo) console.log(`${logInfo} ${cached ? 'hit' : 'miss'}`);
|
|
@@ -183,6 +183,10 @@ export default class QueryGqlRoute {
|
|
|
183
183
|
|
|
184
184
|
if (xCraftSite) headers['X-Craft-Site'] = xCraftSite;
|
|
185
185
|
|
|
186
|
+
if (csr.req.headers.get('X-Craft-GQL-Cache') === 'no-cache') {
|
|
187
|
+
headers['X-Craft-GQL-Cache'] = 'no-cache';
|
|
188
|
+
}
|
|
189
|
+
|
|
186
190
|
// now execute the gql request
|
|
187
191
|
let resp: Response;
|
|
188
192
|
try {
|
|
@@ -223,7 +227,7 @@ export default class QueryGqlRoute {
|
|
|
223
227
|
|
|
224
228
|
// now we have a valid json resp.
|
|
225
229
|
// should we cache it?
|
|
226
|
-
if (cacheKey && this.cacheIfFn?.(jsonResp.data, vars)) {
|
|
230
|
+
if (cacheKey && this.cacheIfFn?.(jsonResp.data, vars, csr)) {
|
|
227
231
|
try {
|
|
228
232
|
await caching.setCache(cacheKey, jsonResp);
|
|
229
233
|
if (logInfo) console.log(logInfo + ' set cache');
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { QueryVar } from '../../queries/vars.js';
|
|
2
2
|
import CrelteServerRequest from '../CrelteServer.js';
|
|
3
|
-
import ServerRouter from '../ServerRouter.js';
|
|
4
3
|
import { HandleFn, newError, validateVars, ValidIfFn } from './routes.js';
|
|
5
4
|
|
|
6
5
|
// only internal
|
|
@@ -22,14 +21,11 @@ export default class QueryHandleRoute {
|
|
|
22
21
|
this.vars = vars;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
async handle(
|
|
26
|
-
cs: ServerRouter,
|
|
27
|
-
csr: CrelteServerRequest,
|
|
28
|
-
): Promise<Response> {
|
|
24
|
+
async handle(csr: CrelteServerRequest): Promise<Response> {
|
|
29
25
|
let vars: Record<string, any>;
|
|
30
26
|
try {
|
|
31
27
|
const reqVars = await csr.req.json();
|
|
32
|
-
vars = validateVars(this.vars, reqVars, this.validIfFn,
|
|
28
|
+
vars = validateVars(this.vars, reqVars, this.validIfFn, csr);
|
|
33
29
|
} catch (e) {
|
|
34
30
|
return newError(e, 400);
|
|
35
31
|
}
|
|
@@ -132,6 +132,7 @@ export async function initQueryRoutes(
|
|
|
132
132
|
);
|
|
133
133
|
|
|
134
134
|
const route = new QueryGqlRoute(name, rb.query, rb);
|
|
135
|
+
await route.init();
|
|
135
136
|
|
|
136
137
|
router.post('/queries/' + route.name, csr =>
|
|
137
138
|
route.handle(caching, csr),
|
|
@@ -147,9 +148,7 @@ export async function initQueryRoutes(
|
|
|
147
148
|
rb.vars,
|
|
148
149
|
);
|
|
149
150
|
|
|
150
|
-
router.post('/queries/' + route.name, csr =>
|
|
151
|
-
route.handle(caching.router, csr),
|
|
152
|
-
);
|
|
151
|
+
router.post('/queries/' + route.name, csr => route.handle(csr));
|
|
153
152
|
} else {
|
|
154
153
|
throw new Error(
|
|
155
154
|
`query js/ts ${name} file needs to either have ` +
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { QueryVar } from '../../queries/vars.js';
|
|
2
2
|
import CrelteServerRequest from '../CrelteServer.js';
|
|
3
|
-
import ServerRouter from '../ServerRouter.js';
|
|
4
3
|
|
|
5
|
-
export type CacheIfFn = (
|
|
4
|
+
export type CacheIfFn = (
|
|
5
|
+
response: any,
|
|
6
|
+
vars: Record<string, any>,
|
|
7
|
+
csr: CrelteServerRequest,
|
|
8
|
+
) => boolean;
|
|
6
9
|
|
|
7
10
|
/// Either throw or return a boolean
|
|
8
11
|
export type ValidIfFn = (
|
|
9
12
|
vars: Record<string, any>,
|
|
10
|
-
|
|
13
|
+
csr: CrelteServerRequest,
|
|
11
14
|
) => void | boolean;
|
|
12
15
|
|
|
13
16
|
/// Anything other than returning undefined will replace the response
|
|
@@ -33,7 +36,7 @@ export function validateVars(
|
|
|
33
36
|
qvars: Record<string, QueryVar> | null,
|
|
34
37
|
vars: any,
|
|
35
38
|
validIfFn: ValidIfFn | null,
|
|
36
|
-
|
|
39
|
+
csr: CrelteServerRequest,
|
|
37
40
|
): Record<string, any> {
|
|
38
41
|
if (!vars || typeof vars !== 'object')
|
|
39
42
|
throw new Error('expected an object as vars');
|
|
@@ -43,12 +46,12 @@ export function validateVars(
|
|
|
43
46
|
const nVars: Record<string, any> = {};
|
|
44
47
|
|
|
45
48
|
for (const [k, v] of Object.entries(qvars)) {
|
|
46
|
-
nVars[k] = v.validValue(vars[k],
|
|
49
|
+
nVars[k] = v.validValue(vars[k], csr);
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
if (validIfFn) {
|
|
50
53
|
// or throw
|
|
51
|
-
const valid = validIfFn(nVars,
|
|
54
|
+
const valid = validIfFn(nVars, csr);
|
|
52
55
|
if (typeof valid === 'boolean' && !valid)
|
|
53
56
|
throw new Error('invalid variables for query');
|
|
54
57
|
}
|