crelte 0.5.1 → 0.5.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/cookies/ServerCookies.js +2 -2
- package/dist/init/client.js +2 -2
- package/dist/init/server.js +1 -1
- package/dist/queries/Queries.js +2 -2
- package/dist/queries/index.d.ts +2 -2
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +2 -2
- package/dist/queries/vars.d.ts +34 -0
- package/dist/queries/vars.d.ts.map +1 -1
- package/dist/queries/vars.js +47 -2
- package/dist/routing/router/ClientRouter.d.ts +1 -1
- package/dist/routing/router/ClientRouter.d.ts.map +1 -1
- package/dist/routing/router/ClientRouter.js +1 -1
- package/dist/server/ServerRouter.js +1 -1
- package/dist/server/queries/routes.d.ts.map +1 -1
- package/dist/server/queries/routes.js +8 -2
- package/dist/server/shared.js +1 -1
- package/dist/vite/index.js +1 -1
- package/package.json +1 -1
- package/src/cookies/ServerCookies.ts +2 -2
- package/src/init/client.ts +2 -2
- package/src/init/server.ts +1 -1
- package/src/queries/Queries.ts +2 -2
- package/src/queries/index.ts +2 -1
- package/src/queries/vars.ts +51 -2
- package/src/routing/router/ClientRouter.ts +2 -2
- package/src/server/ServerRouter.ts +1 -1
- package/src/server/queries/routes.ts +11 -2
- package/src/server/shared.ts +1 -1
- package/src/vite/index.ts +1 -1
|
@@ -7,7 +7,7 @@ export default class ServerCookies {
|
|
|
7
7
|
requestCookies;
|
|
8
8
|
setCookies;
|
|
9
9
|
constructor(headers) {
|
|
10
|
-
this.requestCookies = parseCookies(headers.get('
|
|
10
|
+
this.requestCookies = parseCookies(headers.get('Cookie') ?? '');
|
|
11
11
|
this.setCookies = new Map();
|
|
12
12
|
}
|
|
13
13
|
/// Rethrns the value of the cookie with the given name, or null if it doesn't exist.
|
|
@@ -27,7 +27,7 @@ export default class ServerCookies {
|
|
|
27
27
|
}
|
|
28
28
|
_populateHeaders(headers) {
|
|
29
29
|
for (const setCookie of this.setCookies.values()) {
|
|
30
|
-
headers.append('
|
|
30
|
+
headers.append('Set-Cookie', setCookieToString(setCookie));
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
package/dist/init/client.js
CHANGED
|
@@ -89,11 +89,11 @@ export async function main(data) {
|
|
|
89
89
|
intro: config.playIntro,
|
|
90
90
|
});
|
|
91
91
|
};
|
|
92
|
-
router.onError = e => {
|
|
92
|
+
router.onError = (e, req) => {
|
|
93
93
|
console.error('routing failed:', e, 'reloading trying to fix it');
|
|
94
94
|
// since onError is called only on subsequent requests we should never
|
|
95
95
|
// have an infinite loop here
|
|
96
|
-
window.location.
|
|
96
|
+
window.location.href = req.url.href;
|
|
97
97
|
};
|
|
98
98
|
router.onRender = async (cr, readyForRoute, domUpdated) => {
|
|
99
99
|
if (appInstance && cr.req.disableLoadData) {
|
package/dist/init/server.js
CHANGED
|
@@ -44,7 +44,7 @@ export async function main(data) {
|
|
|
44
44
|
ssrCache.set('FRONTEND_URL', data.serverData.frontend);
|
|
45
45
|
const cookies = new ServerCookies(data.serverData.headers);
|
|
46
46
|
ssrCache.set('crelteSites', data.serverData.sites);
|
|
47
|
-
const router = new ServerRouter(data.serverData.sites, data.serverData.headers.get('
|
|
47
|
+
const router = new ServerRouter(data.serverData.sites, data.serverData.headers.get('Accept-Language') ?? '', { debugTiming: config.debugTiming ?? false });
|
|
48
48
|
const queries = newQueries(ssrCache, router.route.readonly(), config);
|
|
49
49
|
const crelte = newCrelte({
|
|
50
50
|
config,
|
package/dist/queries/Queries.js
CHANGED
|
@@ -171,8 +171,8 @@ class Inner {
|
|
|
171
171
|
if (!resp.ok) {
|
|
172
172
|
throw new QueryError({ status: resp.status, body: await resp.text() }, 'resp not ok');
|
|
173
173
|
}
|
|
174
|
-
if (resp.headers.get('
|
|
175
|
-
console.log('Debug link', resp.headers.get('
|
|
174
|
+
if (resp.headers.get('X-Debug-Link'))
|
|
175
|
+
console.log('Debug link', resp.headers.get('X-Debug-Link'));
|
|
176
176
|
if (timing) {
|
|
177
177
|
console.log(logName + ' completed took: ' + (Date.now() - timing) + 'ms', vars);
|
|
178
178
|
}
|
package/dist/queries/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Queries, { isQuery, QueriesOptions, Query, QueryOptions } from '../queries/Queries.js';
|
|
2
2
|
import { gql } from './gql.js';
|
|
3
3
|
import QueryError from './QueryError.js';
|
|
4
|
-
import { QueryVar, ValidIf, vars } from './vars.js';
|
|
5
|
-
export { Queries, type QueriesOptions, type QueryOptions, type Query, isQuery, QueryError, gql, vars, type ValidIf, QueryVar, };
|
|
4
|
+
import { QueryVar, ValidIf, vars, varsIdsEqual } from './vars.js';
|
|
5
|
+
export { Queries, type QueriesOptions, type QueryOptions, type Query, isQuery, QueryError, gql, vars, type ValidIf, QueryVar, varsIdsEqual, };
|
|
6
6
|
type InferQueryVarType<T> = T extends QueryVar<infer U> ? U : never;
|
|
7
7
|
type InferVariableTypes<T> = {
|
|
8
8
|
[K in keyof T]: InferQueryVarType<T[K]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/queries/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EACf,OAAO,EACP,cAAc,EACd,KAAK,EACL,YAAY,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,UAAU,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/queries/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EACf,OAAO,EACP,cAAc,EACd,KAAK,EACL,YAAY,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,UAAU,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAElE,OAAO,EACN,OAAO,EACP,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,OAAO,EACP,UAAU,EACV,GAAG,EACH,IAAI,EACJ,KAAK,OAAO,EACZ,QAAQ,EACR,YAAY,GACZ,CAAC;AAEF,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEpE,KAAK,kBAAkB,CAAC,CAAC,IAAI;KAC3B,CAAC,IAAI,MAAM,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACvC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,OAAO,CAClB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,IACpE,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC"}
|
package/dist/queries/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Queries, { isQuery, } from '../queries/Queries.js';
|
|
2
2
|
import { gql } from './gql.js';
|
|
3
3
|
import QueryError from './QueryError.js';
|
|
4
|
-
import { QueryVar, vars } from './vars.js';
|
|
5
|
-
export { Queries, isQuery, QueryError, gql, vars, QueryVar, };
|
|
4
|
+
import { QueryVar, vars, varsIdsEqual } from './vars.js';
|
|
5
|
+
export { Queries, isQuery, QueryError, gql, vars, QueryVar, varsIdsEqual, };
|
package/dist/queries/vars.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export declare const vars: {
|
|
|
19
19
|
* the returned array will **never be empty**, but might be null if
|
|
20
20
|
* allowed. Id's are always non negative integers
|
|
21
21
|
*
|
|
22
|
+
* The numbers are always unique and sorted in ascending order
|
|
23
|
+
*
|
|
22
24
|
* ## Warning
|
|
23
25
|
* Ids are not automatically safe to be cached, it is also not
|
|
24
26
|
* enough to just check if the filter returned some results.
|
|
@@ -61,4 +63,36 @@ export declare class QueryVar<T = any> {
|
|
|
61
63
|
__QueryVar__(): void;
|
|
62
64
|
}
|
|
63
65
|
export declare function isQueryVar(v: any): v is QueryVar;
|
|
66
|
+
/**
|
|
67
|
+
* Checks if two id arrays are equal
|
|
68
|
+
*
|
|
69
|
+
* The first argument needs to come from a `vars.ids()` variable.
|
|
70
|
+
* The second argument should come from a query, where the output is trusted.
|
|
71
|
+
*
|
|
72
|
+
* ## Example
|
|
73
|
+
* ```
|
|
74
|
+
* export const variables = {
|
|
75
|
+
* categories: vars.ids()
|
|
76
|
+
* };
|
|
77
|
+
*
|
|
78
|
+
* export const caching: Caching<typeof variables> = (res, vars) => {
|
|
79
|
+
* // res is the graphql response
|
|
80
|
+
* return varsIdsEqual(vars.categories, res.categories);
|
|
81
|
+
* };
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* ## Note
|
|
85
|
+
* The following cases are considered equal:
|
|
86
|
+
* ```
|
|
87
|
+
* varsIdsEqual(null, null);
|
|
88
|
+
* varsIdsEqual([], null);
|
|
89
|
+
* varsIdsEqual([1,2], ['2',1]);
|
|
90
|
+
* ```
|
|
91
|
+
* These are not equal:
|
|
92
|
+
* ```
|
|
93
|
+
* varsIdsEqual([1], null);
|
|
94
|
+
* varsIdsEqual([2,1], [2,1]); // because the second arg gets ordered
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function varsIdsEqual(a: number[] | null | undefined, b: (string | number)[] | null | undefined): boolean;
|
|
64
98
|
//# sourceMappingURL=vars.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vars.d.ts","sourceRoot":"","sources":["../../src/queries/vars.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,2BAA2B,CAAC;AAErD,eAAO,MAAM,IAAI;eACP,QAAQ,CAAC,GAAG,CAAC;kBACV,QAAQ,CAAC,MAAM,CAAC;kBAChB,QAAQ,CAAC,MAAM,CAAC;IAE5B;;;;;;;;OAQG;cACK,QAAQ,CAAC,MAAM,CAAC;IAExB
|
|
1
|
+
{"version":3,"file":"vars.d.ts","sourceRoot":"","sources":["../../src/queries/vars.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,2BAA2B,CAAC;AAErD,eAAO,MAAM,IAAI;eACP,QAAQ,CAAC,GAAG,CAAC;kBACV,QAAQ,CAAC,MAAM,CAAC;kBAChB,QAAQ,CAAC,MAAM,CAAC;IAE5B;;;;;;;;OAQG;cACK,QAAQ,CAAC,MAAM,CAAC;IAExB;;;;;;;;;;;;;;;;;;OAkBG;eACM,QAAQ,CAAC,MAAM,EAAE,CAAC;kBAEf,QAAQ,CAAC,MAAM,CAAC;CAM5B,CAAC;AAIF,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,KAAK,OAAO,CAAC;AAE7D,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IAC5B,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,IAAI,CAA6C;IACzD,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,SAAS,CAAa;;IAS9B,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC;IAK1B,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC;IAK1B,EAAE,IAAI,QAAQ,CAAC,MAAM,CAAC;IAKtB,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;IAKzB,QAAQ,IAAI,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC;IAK9B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAKpC,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,GAAG,CAAC,GAAG,IAAI;IAqE9C;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IAKpC,YAAY;CACZ;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,QAAQ,CAEhD;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,YAAY,CAC3B,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAC9B,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GACvC,OAAO,CAWT"}
|
package/dist/queries/vars.js
CHANGED
|
@@ -18,6 +18,8 @@ export const vars = {
|
|
|
18
18
|
* the returned array will **never be empty**, but might be null if
|
|
19
19
|
* allowed. Id's are always non negative integers
|
|
20
20
|
*
|
|
21
|
+
* The numbers are always unique and sorted in ascending order
|
|
22
|
+
*
|
|
21
23
|
* ## Warning
|
|
22
24
|
* Ids are not automatically safe to be cached, it is also not
|
|
23
25
|
* enough to just check if the filter returned some results.
|
|
@@ -97,7 +99,7 @@ export class QueryVar {
|
|
|
97
99
|
break;
|
|
98
100
|
case 'id':
|
|
99
101
|
if (typeof v === 'string')
|
|
100
|
-
v =
|
|
102
|
+
v = Number(v);
|
|
101
103
|
if (!isValidId(v))
|
|
102
104
|
throw new Error(`variable ${this.name} is not a valid id`);
|
|
103
105
|
break;
|
|
@@ -112,7 +114,7 @@ export class QueryVar {
|
|
|
112
114
|
throw new Error(`variable ${this.name} is not allowed to be empty`);
|
|
113
115
|
}
|
|
114
116
|
// convert strings to numbers
|
|
115
|
-
v = v.map(
|
|
117
|
+
v = v.map(Number);
|
|
116
118
|
if (!v.every(isValidId))
|
|
117
119
|
throw new Error(`variable ${this.name} is not a list of valid ids`);
|
|
118
120
|
// make unique and sort by number
|
|
@@ -142,3 +144,46 @@ export function isQueryVar(v) {
|
|
|
142
144
|
function isValidId(id) {
|
|
143
145
|
return typeof id === 'number' && Number.isInteger(id) && id >= 0;
|
|
144
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Checks if two id arrays are equal
|
|
149
|
+
*
|
|
150
|
+
* The first argument needs to come from a `vars.ids()` variable.
|
|
151
|
+
* The second argument should come from a query, where the output is trusted.
|
|
152
|
+
*
|
|
153
|
+
* ## Example
|
|
154
|
+
* ```
|
|
155
|
+
* export const variables = {
|
|
156
|
+
* categories: vars.ids()
|
|
157
|
+
* };
|
|
158
|
+
*
|
|
159
|
+
* export const caching: Caching<typeof variables> = (res, vars) => {
|
|
160
|
+
* // res is the graphql response
|
|
161
|
+
* return varsIdsEqual(vars.categories, res.categories);
|
|
162
|
+
* };
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* ## Note
|
|
166
|
+
* The following cases are considered equal:
|
|
167
|
+
* ```
|
|
168
|
+
* varsIdsEqual(null, null);
|
|
169
|
+
* varsIdsEqual([], null);
|
|
170
|
+
* varsIdsEqual([1,2], ['2',1]);
|
|
171
|
+
* ```
|
|
172
|
+
* These are not equal:
|
|
173
|
+
* ```
|
|
174
|
+
* varsIdsEqual([1], null);
|
|
175
|
+
* varsIdsEqual([2,1], [2,1]); // because the second arg gets ordered
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
export function varsIdsEqual(a, b) {
|
|
179
|
+
const aEmpty = !a?.length;
|
|
180
|
+
const bEmpty = !b?.length;
|
|
181
|
+
if (aEmpty && bEmpty)
|
|
182
|
+
return true;
|
|
183
|
+
if (aEmpty || bEmpty)
|
|
184
|
+
return false;
|
|
185
|
+
if (a.length !== b.length)
|
|
186
|
+
return false;
|
|
187
|
+
const nb = b.map(Number).sort((a, b) => a - b);
|
|
188
|
+
return a.every((v, i) => v === nb[i]);
|
|
189
|
+
}
|
|
@@ -9,7 +9,7 @@ export type ClientRouterOptions = {
|
|
|
9
9
|
export default class ClientRouter extends BaseRouter {
|
|
10
10
|
private scrollDebounceTimeout;
|
|
11
11
|
private preloadOnMouseOver;
|
|
12
|
-
onError: (e: any) => void;
|
|
12
|
+
onError: (e: any, req: Request) => void;
|
|
13
13
|
constructor(sites: SiteFromGraphQl[], opts: ClientRouterOptions);
|
|
14
14
|
/**
|
|
15
15
|
* ## Throws
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientRouter.d.ts","sourceRoot":"","sources":["../../../src/routing/router/ClientRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,UAAU,EAAE,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IACjC,kBAAkB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,CAAC;AAEtB,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,UAAU;IACnD,OAAO,CAAC,qBAAqB,CAAa;IAC1C,OAAO,CAAC,kBAAkB,CAAU;IAEpC,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ClientRouter.d.ts","sourceRoot":"","sources":["../../../src/routing/router/ClientRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,UAAU,EAAE,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IACjC,kBAAkB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,CAAC;AAEtB,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,UAAU;IACnD,OAAO,CAAC,qBAAqB,CAAa;IAC1C,OAAO,CAAC,kBAAkB,CAAU;IAEpC,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;gBAE5B,KAAK,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,mBAAmB;IAQ/D;;OAEG;IACG,IAAI;IAuBV;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IA6ChD,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,cAAmB;IAYpD,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,GAAE,cAAmB;IAiB7D;;;OAGG;IACG,qBAAqB,CAC1B,GAAG,EAAE,OAAO,EACZ,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GACnC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IASxB,MAAM;IAgEN,QAAQ;IAgCR,YAAY,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,GAAG,IAAI;CAwEpD"}
|
|
@@ -82,7 +82,7 @@ export default class ServerRouter {
|
|
|
82
82
|
const { params, handlers } = this.inner.find(req.method, new URL(req.url).pathname);
|
|
83
83
|
if (!handlers.length)
|
|
84
84
|
return null;
|
|
85
|
-
const languages = parseAcceptLanguage(req.headers.get('
|
|
85
|
+
const languages = parseAcceptLanguage(req.headers.get('Accept-Language') ?? '').map(([l]) => l);
|
|
86
86
|
const prefSite = preferredSite(this._sites, languages);
|
|
87
87
|
const site = siteFromUrl(this._sites, new URL(req.url)) ??
|
|
88
88
|
prefSite ??
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/server/queries/routes.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,oBAAoB,CAAC;AACrD,OAAO,cAAc,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAQ,MAAM,uBAAuB,CAAC;AAEvD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAG9C,MAAM,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;AAG9E,qBAAa,UAAU;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;gBAG3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,EACrC,SAAS,EAAE,SAAS,GAAG,IAAI;IAoB5B,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,kBAAkB;IAO1B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAexD,MAAM,CACX,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE,mBAAmB,GACtB,OAAO,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/server/queries/routes.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,oBAAoB,CAAC;AACrD,OAAO,cAAc,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAQ,MAAM,uBAAuB,CAAC;AAEvD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAG9C,MAAM,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;AAG9E,qBAAa,UAAU;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;gBAG3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,EACrC,SAAS,EAAE,SAAS,GAAG,IAAI;IAoB5B,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,kBAAkB;IAO1B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAexD,MAAM,CACX,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE,mBAAmB,GACtB,OAAO,CAAC,QAAQ,CAAC;CAiHpB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAEzD"}
|
|
@@ -79,6 +79,8 @@ export class QueryRoute {
|
|
|
79
79
|
try {
|
|
80
80
|
const reqVars = await csr.req.json();
|
|
81
81
|
vars = this.validateVars(reqVars, caching.router);
|
|
82
|
+
if ('qName' in vars || 'xCraftSite' in vars)
|
|
83
|
+
throw new Error('qName and xCraftSite are reserved variable names');
|
|
82
84
|
}
|
|
83
85
|
catch (e) {
|
|
84
86
|
return newError(e, 400);
|
|
@@ -96,10 +98,12 @@ export class QueryRoute {
|
|
|
96
98
|
else if (reqSearch.has('siteToken')) {
|
|
97
99
|
siteToken = reqSearch.get('siteToken');
|
|
98
100
|
}
|
|
101
|
+
// check for x-craft-site header and pass it on
|
|
102
|
+
const xCraftSite = csr.req.headers.get('X-Craft-Site');
|
|
99
103
|
let cacheKey = null;
|
|
100
104
|
const useCache = !previewToken && caching.isEnabled();
|
|
101
105
|
if (useCache) {
|
|
102
|
-
cacheKey = await calcKey({
|
|
106
|
+
cacheKey = await calcKey({ ...vars, qName: this.name, xCraftSite });
|
|
103
107
|
const cached = await caching.getCache(cacheKey);
|
|
104
108
|
if (logInfo)
|
|
105
109
|
console.log(`${logInfo} ${cached ? 'hit' : 'miss'}`);
|
|
@@ -121,6 +125,8 @@ export class QueryRoute {
|
|
|
121
125
|
const xDebug = csr.req.headers.get('X-Debug');
|
|
122
126
|
if (xDebug)
|
|
123
127
|
headers['X-Debug'] = xDebug;
|
|
128
|
+
if (xCraftSite)
|
|
129
|
+
headers['X-Craft-Site'] = xCraftSite;
|
|
124
130
|
// now execute the gql request
|
|
125
131
|
let resp;
|
|
126
132
|
try {
|
|
@@ -141,7 +147,7 @@ export class QueryRoute {
|
|
|
141
147
|
return newError(e, 500);
|
|
142
148
|
}
|
|
143
149
|
const respHeaders = {};
|
|
144
|
-
const xDebugLink = resp.headers.get('
|
|
150
|
+
const xDebugLink = resp.headers.get('X-Debug-Link');
|
|
145
151
|
if (xDebugLink)
|
|
146
152
|
respHeaders['X-Debug'] = xDebugLink;
|
|
147
153
|
let jsonResp;
|
package/dist/server/shared.js
CHANGED
|
@@ -96,7 +96,7 @@ export async function modRender(env, mod, template, req, opts = {}) {
|
|
|
96
96
|
return new Response(html, { status, headers: nHeaders });
|
|
97
97
|
}
|
|
98
98
|
export async function modRenderError(env, mod, thrownError, template, req, opts = {}) {
|
|
99
|
-
const acceptLang = req.headers.get('
|
|
99
|
+
const acceptLang = req.headers.get('Accept-Language') ?? null;
|
|
100
100
|
// in the case of an error let's try to render a nice Error Page
|
|
101
101
|
const error = {
|
|
102
102
|
status: 500,
|
package/dist/vite/index.js
CHANGED
|
@@ -250,7 +250,7 @@ export default function crelte(opts) {
|
|
|
250
250
|
async function serveVite(env, vite) {
|
|
251
251
|
vite.middlewares.use(async (nReq, res, next) => {
|
|
252
252
|
const protocol = vite.config.server.https ? 'https' : 'http';
|
|
253
|
-
const baseUrl = protocol + '://' + nReq.headers['
|
|
253
|
+
const baseUrl = protocol + '://' + nReq.headers['Host'];
|
|
254
254
|
const req = requestToWebRequest(baseUrl, nReq);
|
|
255
255
|
let thrownError = null;
|
|
256
256
|
let serverMod;
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export default class ServerCookies implements Cookies {
|
|
|
10
10
|
setCookies: Map<string, SetCookie>;
|
|
11
11
|
|
|
12
12
|
constructor(headers: Headers) {
|
|
13
|
-
this.requestCookies = parseCookies(headers.get('
|
|
13
|
+
this.requestCookies = parseCookies(headers.get('Cookie') ?? '');
|
|
14
14
|
this.setCookies = new Map();
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -35,7 +35,7 @@ export default class ServerCookies implements Cookies {
|
|
|
35
35
|
|
|
36
36
|
_populateHeaders(headers: Headers) {
|
|
37
37
|
for (const setCookie of this.setCookies.values()) {
|
|
38
|
-
headers.append('
|
|
38
|
+
headers.append('Set-Cookie', setCookieToString(setCookie));
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
package/src/init/client.ts
CHANGED
|
@@ -124,11 +124,11 @@ export async function main(data: MainData) {
|
|
|
124
124
|
});
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
-
router.onError = e => {
|
|
127
|
+
router.onError = (e, req) => {
|
|
128
128
|
console.error('routing failed:', e, 'reloading trying to fix it');
|
|
129
129
|
// since onError is called only on subsequent requests we should never
|
|
130
130
|
// have an infinite loop here
|
|
131
|
-
window.location.
|
|
131
|
+
window.location.href = req.url.href;
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
router.onRender = async (cr, readyForRoute, domUpdated) => {
|
package/src/init/server.ts
CHANGED
|
@@ -73,7 +73,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
|
|
|
73
73
|
ssrCache.set('crelteSites', data.serverData.sites);
|
|
74
74
|
const router = new ServerRouter(
|
|
75
75
|
data.serverData.sites,
|
|
76
|
-
data.serverData.headers.get('
|
|
76
|
+
data.serverData.headers.get('Accept-Language') ?? '',
|
|
77
77
|
{ debugTiming: config.debugTiming ?? false },
|
|
78
78
|
);
|
|
79
79
|
|
package/src/queries/Queries.ts
CHANGED
|
@@ -304,8 +304,8 @@ class Inner {
|
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
if (resp.headers.get('
|
|
308
|
-
console.log('Debug link', resp.headers.get('
|
|
307
|
+
if (resp.headers.get('X-Debug-Link'))
|
|
308
|
+
console.log('Debug link', resp.headers.get('X-Debug-Link'));
|
|
309
309
|
|
|
310
310
|
if (timing) {
|
|
311
311
|
console.log(
|
package/src/queries/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import Queries, {
|
|
|
6
6
|
} from '../queries/Queries.js';
|
|
7
7
|
import { gql } from './gql.js';
|
|
8
8
|
import QueryError from './QueryError.js';
|
|
9
|
-
import { QueryVar, ValidIf, vars } from './vars.js';
|
|
9
|
+
import { QueryVar, ValidIf, vars, varsIdsEqual } from './vars.js';
|
|
10
10
|
|
|
11
11
|
export {
|
|
12
12
|
Queries,
|
|
@@ -19,6 +19,7 @@ export {
|
|
|
19
19
|
vars,
|
|
20
20
|
type ValidIf,
|
|
21
21
|
QueryVar,
|
|
22
|
+
varsIdsEqual,
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
type InferQueryVarType<T> = T extends QueryVar<infer U> ? U : never;
|
package/src/queries/vars.ts
CHANGED
|
@@ -22,6 +22,8 @@ export const vars = {
|
|
|
22
22
|
* the returned array will **never be empty**, but might be null if
|
|
23
23
|
* allowed. Id's are always non negative integers
|
|
24
24
|
*
|
|
25
|
+
* The numbers are always unique and sorted in ascending order
|
|
26
|
+
*
|
|
25
27
|
* ## Warning
|
|
26
28
|
* Ids are not automatically safe to be cached, it is also not
|
|
27
29
|
* enough to just check if the filter returned some results.
|
|
@@ -122,7 +124,7 @@ export class QueryVar<T = any> {
|
|
|
122
124
|
break;
|
|
123
125
|
|
|
124
126
|
case 'id':
|
|
125
|
-
if (typeof v === 'string') v =
|
|
127
|
+
if (typeof v === 'string') v = Number(v);
|
|
126
128
|
|
|
127
129
|
if (!isValidId(v))
|
|
128
130
|
throw new Error(`variable ${this.name} is not a valid id`);
|
|
@@ -144,7 +146,7 @@ export class QueryVar<T = any> {
|
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
// convert strings to numbers
|
|
147
|
-
v = v.map(
|
|
149
|
+
v = v.map(Number);
|
|
148
150
|
|
|
149
151
|
if (!v.every(isValidId))
|
|
150
152
|
throw new Error(
|
|
@@ -185,3 +187,50 @@ export function isQueryVar(v: any): v is QueryVar {
|
|
|
185
187
|
function isValidId(id: any): id is number {
|
|
186
188
|
return typeof id === 'number' && Number.isInteger(id) && id >= 0;
|
|
187
189
|
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Checks if two id arrays are equal
|
|
193
|
+
*
|
|
194
|
+
* The first argument needs to come from a `vars.ids()` variable.
|
|
195
|
+
* The second argument should come from a query, where the output is trusted.
|
|
196
|
+
*
|
|
197
|
+
* ## Example
|
|
198
|
+
* ```
|
|
199
|
+
* export const variables = {
|
|
200
|
+
* categories: vars.ids()
|
|
201
|
+
* };
|
|
202
|
+
*
|
|
203
|
+
* export const caching: Caching<typeof variables> = (res, vars) => {
|
|
204
|
+
* // res is the graphql response
|
|
205
|
+
* return varsIdsEqual(vars.categories, res.categories);
|
|
206
|
+
* };
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* ## Note
|
|
210
|
+
* The following cases are considered equal:
|
|
211
|
+
* ```
|
|
212
|
+
* varsIdsEqual(null, null);
|
|
213
|
+
* varsIdsEqual([], null);
|
|
214
|
+
* varsIdsEqual([1,2], ['2',1]);
|
|
215
|
+
* ```
|
|
216
|
+
* These are not equal:
|
|
217
|
+
* ```
|
|
218
|
+
* varsIdsEqual([1], null);
|
|
219
|
+
* varsIdsEqual([2,1], [2,1]); // because the second arg gets ordered
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export function varsIdsEqual(
|
|
223
|
+
a: number[] | null | undefined,
|
|
224
|
+
b: (string | number)[] | null | undefined,
|
|
225
|
+
): boolean {
|
|
226
|
+
const aEmpty = !a?.length;
|
|
227
|
+
const bEmpty = !b?.length;
|
|
228
|
+
if (aEmpty && bEmpty) return true;
|
|
229
|
+
if (aEmpty || bEmpty) return false;
|
|
230
|
+
|
|
231
|
+
if (a.length !== b.length) return false;
|
|
232
|
+
|
|
233
|
+
const nb = b.map(Number).sort((a, b) => a - b);
|
|
234
|
+
|
|
235
|
+
return a.every((v, i) => v === nb[i]);
|
|
236
|
+
}
|
|
@@ -13,7 +13,7 @@ export default class ClientRouter extends BaseRouter {
|
|
|
13
13
|
private scrollDebounceTimeout: any | null;
|
|
14
14
|
private preloadOnMouseOver: boolean;
|
|
15
15
|
|
|
16
|
-
onError: (e: any) => void;
|
|
16
|
+
onError: (e: any, req: Request) => void;
|
|
17
17
|
|
|
18
18
|
constructor(sites: SiteFromGraphQl[], opts: ClientRouterOptions) {
|
|
19
19
|
super(sites, navigator.languages.slice(), opts);
|
|
@@ -138,7 +138,7 @@ export default class ClientRouter extends BaseRouter {
|
|
|
138
138
|
return await this.handleRequest(req, updateHistory);
|
|
139
139
|
} catch (e) {
|
|
140
140
|
console.error('request failed', e);
|
|
141
|
-
this.onError(e);
|
|
141
|
+
this.onError(e, req);
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -121,7 +121,7 @@ export default class ServerRouter {
|
|
|
121
121
|
if (!handlers.length) return null;
|
|
122
122
|
|
|
123
123
|
const languages = parseAcceptLanguage(
|
|
124
|
-
req.headers.get('
|
|
124
|
+
req.headers.get('Accept-Language') ?? '',
|
|
125
125
|
).map(([l]) => l);
|
|
126
126
|
const prefSite = preferredSite(this._sites, languages);
|
|
127
127
|
const site =
|
|
@@ -104,6 +104,10 @@ export class QueryRoute {
|
|
|
104
104
|
try {
|
|
105
105
|
const reqVars = await csr.req.json();
|
|
106
106
|
vars = this.validateVars(reqVars, caching.router);
|
|
107
|
+
if ('qName' in vars || 'xCraftSite' in vars)
|
|
108
|
+
throw new Error(
|
|
109
|
+
'qName and xCraftSite are reserved variable names',
|
|
110
|
+
);
|
|
107
111
|
} catch (e) {
|
|
108
112
|
return newError(e, 400);
|
|
109
113
|
}
|
|
@@ -124,10 +128,13 @@ export class QueryRoute {
|
|
|
124
128
|
siteToken = reqSearch.get('siteToken');
|
|
125
129
|
}
|
|
126
130
|
|
|
131
|
+
// check for x-craft-site header and pass it on
|
|
132
|
+
const xCraftSite = csr.req.headers.get('X-Craft-Site');
|
|
133
|
+
|
|
127
134
|
let cacheKey: string | null = null;
|
|
128
135
|
const useCache = !previewToken && caching.isEnabled();
|
|
129
136
|
if (useCache) {
|
|
130
|
-
cacheKey = await calcKey({
|
|
137
|
+
cacheKey = await calcKey({ ...vars, qName: this.name, xCraftSite });
|
|
131
138
|
const cached = await caching.getCache(cacheKey);
|
|
132
139
|
|
|
133
140
|
if (logInfo) console.log(`${logInfo} ${cached ? 'hit' : 'miss'}`);
|
|
@@ -150,6 +157,8 @@ export class QueryRoute {
|
|
|
150
157
|
const xDebug = csr.req.headers.get('X-Debug');
|
|
151
158
|
if (xDebug) headers['X-Debug'] = xDebug;
|
|
152
159
|
|
|
160
|
+
if (xCraftSite) headers['X-Craft-Site'] = xCraftSite;
|
|
161
|
+
|
|
153
162
|
// now execute the gql request
|
|
154
163
|
let resp: Response;
|
|
155
164
|
try {
|
|
@@ -170,7 +179,7 @@ export class QueryRoute {
|
|
|
170
179
|
}
|
|
171
180
|
|
|
172
181
|
const respHeaders: Record<string, string> = {};
|
|
173
|
-
const xDebugLink = resp.headers.get('
|
|
182
|
+
const xDebugLink = resp.headers.get('X-Debug-Link');
|
|
174
183
|
if (xDebugLink) respHeaders['X-Debug'] = xDebugLink;
|
|
175
184
|
|
|
176
185
|
let jsonResp: Record<string, any>;
|
package/src/server/shared.ts
CHANGED
|
@@ -219,7 +219,7 @@ export async function modRenderError(
|
|
|
219
219
|
req: Request,
|
|
220
220
|
opts: ModRenderOptions = {},
|
|
221
221
|
): Promise<Response> {
|
|
222
|
-
const acceptLang = req.headers.get('
|
|
222
|
+
const acceptLang = req.headers.get('Accept-Language') ?? null;
|
|
223
223
|
|
|
224
224
|
// in the case of an error let's try to render a nice Error Page
|
|
225
225
|
const error = {
|
package/src/vite/index.ts
CHANGED
|
@@ -323,7 +323,7 @@ export default function crelte(opts?: CrelteOptions): Plugin {
|
|
|
323
323
|
async function serveVite(env: EnvData, vite: ViteDevServer) {
|
|
324
324
|
vite.middlewares.use(async (nReq, res, next) => {
|
|
325
325
|
const protocol = vite.config.server.https ? 'https' : 'http';
|
|
326
|
-
const baseUrl = protocol + '://' + nReq.headers['
|
|
326
|
+
const baseUrl = protocol + '://' + nReq.headers['Host'];
|
|
327
327
|
|
|
328
328
|
const req = requestToWebRequest(baseUrl, nReq);
|
|
329
329
|
|