crelte 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Crelte.d.ts +49 -9
- package/dist/Crelte.d.ts.map +1 -1
- package/dist/Crelte.js +30 -10
- package/dist/CrelteRequest.d.ts +3 -7
- package/dist/CrelteRequest.d.ts.map +1 -1
- package/dist/CrelteRequest.js +9 -15
- package/dist/cookies/ServerCookies.d.ts +4 -0
- package/dist/cookies/ServerCookies.d.ts.map +1 -1
- package/dist/cookies/ServerCookies.js +4 -0
- package/dist/cookies/internal.d.ts +3 -0
- package/dist/cookies/internal.d.ts.map +1 -0
- package/dist/cookies/internal.js +2 -0
- package/dist/graphql/GraphQl.d.ts +7 -0
- package/dist/graphql/GraphQl.d.ts.map +1 -1
- package/dist/graphql/GraphQl.js +16 -3
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/init/client.d.ts +0 -19
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +9 -12
- package/dist/init/server.d.ts +0 -4
- package/dist/init/server.d.ts.map +1 -1
- package/dist/init/server.js +2 -5
- package/dist/init/shared.d.ts.map +1 -1
- package/dist/init/shared.js +8 -8
- package/dist/loadData/Globals.d.ts +15 -31
- package/dist/loadData/Globals.d.ts.map +1 -1
- package/dist/loadData/Globals.js +65 -72
- package/dist/routing/InnerRouter.d.ts.map +1 -1
- package/dist/routing/InnerRouter.js +8 -2
- package/dist/routing/Router.d.ts +27 -2
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +32 -2
- package/dist/ssr/SsrCache.d.ts.map +1 -1
- package/dist/ssr/SsrCache.js +6 -1
- package/package.json +6 -2
- package/src/Crelte.ts +80 -13
- package/src/CrelteRequest.ts +14 -23
- package/src/cookies/ServerCookies.ts +4 -0
- package/src/cookies/internal.ts +3 -0
- package/src/graphql/GraphQl.ts +25 -6
- package/src/index.ts +30 -8
- package/src/init/client.ts +9 -40
- package/src/init/server.ts +2 -13
- package/src/init/shared.ts +8 -9
- package/src/loadData/Globals.ts +76 -93
- package/src/routing/InnerRouter.ts +9 -2
- package/src/routing/Router.ts +40 -6
- package/src/ssr/SsrCache.ts +6 -1
package/src/loadData/Globals.ts
CHANGED
|
@@ -4,25 +4,30 @@ const emergency = getGlobal('emergency');
|
|
|
4
4
|
|
|
5
5
|
// returns the data based on the current site (no store)
|
|
6
6
|
cr.getGlobal('emergency')
|
|
7
|
+
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { Writable } from 'crelte-std/stores';
|
|
10
11
|
|
|
11
|
-
export type GlobalWaiters = [(g:
|
|
12
|
+
export type GlobalWaiters<T> = [(g: T | null) => void];
|
|
12
13
|
|
|
13
14
|
export default class Globals {
|
|
14
15
|
// while the globals are not loaded if somebody calls
|
|
15
|
-
//
|
|
16
|
-
private waiters: Map<string, GlobalWaiters
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
private
|
|
16
|
+
// getAsync then we need to store the waiters
|
|
17
|
+
private waiters: Map<number, Map<string, GlobalWaiters<any>>>;
|
|
18
|
+
private data: Map<number, Map<string, any>>;
|
|
19
|
+
private stores: Map<string, Global<any>>;
|
|
20
|
+
private currentSiteId: number | null;
|
|
20
21
|
|
|
21
22
|
constructor() {
|
|
22
23
|
this.waiters = new Map();
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
24
|
+
this.data = new Map();
|
|
25
|
+
this.stores = new Map();
|
|
26
|
+
this.currentSiteId = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get<T = any>(name: string, siteId: number): T | null {
|
|
30
|
+
return this.data.get(siteId)?.get(name) ?? null;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/**
|
|
@@ -31,10 +36,10 @@ export default class Globals {
|
|
|
31
36
|
* ## Note
|
|
32
37
|
* This only works in loadData, in loadGlobalData this will
|
|
33
38
|
* always return null. In that context you should use
|
|
34
|
-
* `.
|
|
39
|
+
* `.getAsync`
|
|
35
40
|
*/
|
|
36
|
-
|
|
37
|
-
return this.
|
|
41
|
+
getStore<T = any>(name: string): Global<T> | null {
|
|
42
|
+
return this.stores.get(name) ?? null;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
@@ -44,15 +49,20 @@ export default class Globals {
|
|
|
44
49
|
* This is only useful in loadGlobalData in all other cases
|
|
45
50
|
* you can use `.getGlobal` which does return a Promise
|
|
46
51
|
*/
|
|
47
|
-
getAsync<T
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
getAsync<T = any>(name: string, siteId: number): Promise<T | null> {
|
|
53
|
+
if (this._wasLoaded(siteId))
|
|
54
|
+
return Promise.resolve(this.get(name, siteId));
|
|
55
|
+
|
|
56
|
+
let listeners = this.waiters.get(siteId);
|
|
57
|
+
if (!listeners) {
|
|
58
|
+
listeners = new Map();
|
|
59
|
+
this.waiters.set(siteId, listeners);
|
|
60
|
+
}
|
|
51
61
|
|
|
52
|
-
let waiter =
|
|
62
|
+
let waiter = listeners.get(name);
|
|
53
63
|
if (!waiter) {
|
|
54
64
|
waiter = [] as any;
|
|
55
|
-
|
|
65
|
+
listeners.set(name, waiter!);
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
return new Promise(resolve => {
|
|
@@ -61,88 +71,73 @@ export default class Globals {
|
|
|
61
71
|
}
|
|
62
72
|
|
|
63
73
|
/** @hidden */
|
|
64
|
-
_wasLoaded(): boolean {
|
|
65
|
-
return this.
|
|
74
|
+
_wasLoaded(siteId: number): boolean {
|
|
75
|
+
return this.data.has(siteId);
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
// data is the data from the global graphql
|
|
69
79
|
// so it contains some keys and data which should be parsed
|
|
70
80
|
// and created a store for each key
|
|
81
|
+
// do not call this if _wasLoaded returns true with the same siteId
|
|
71
82
|
/** @hidden */
|
|
72
83
|
_setData(siteId: number, data: any) {
|
|
73
|
-
const
|
|
74
|
-
this.
|
|
84
|
+
const map = new Map(Object.entries(data));
|
|
85
|
+
this.data.set(siteId, map);
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!wasLoaded) {
|
|
81
|
-
this.waiters.forEach((waiters, key) => {
|
|
82
|
-
waiters.forEach(waiter => waiter(this.get(key)));
|
|
83
|
-
});
|
|
84
|
-
this.waiters.clear();
|
|
85
|
-
}
|
|
87
|
+
this.waiters.get(siteId)?.forEach((waiters, key) => {
|
|
88
|
+
waiters.forEach(waiter => waiter(map.get(key)));
|
|
89
|
+
});
|
|
90
|
+
this.waiters.delete(siteId);
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/** @hidden */
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
_updateSiteId(siteId: number) {
|
|
95
|
+
if (this.currentSiteId === siteId) return;
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
map.set(key, global.bySiteId(siteId));
|
|
94
|
-
}
|
|
97
|
+
const data = this.data.get(siteId) ?? new Map();
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
// we set all global data to null via setSilent
|
|
100
|
+
// then set them all with the new data
|
|
101
|
+
// and update all of them
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
_updateSiteId(siteId: number) {
|
|
101
|
-
// todo we should only trigger
|
|
102
|
-
if (this.prevSiteId === siteId) return;
|
|
103
|
+
this.stores.forEach(global => global._setSilent(null));
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
data.forEach((value, key) => {
|
|
106
|
+
let global = this.stores.get(key);
|
|
107
|
+
if (global) {
|
|
108
|
+
global._setSilent(value);
|
|
109
|
+
} else {
|
|
110
|
+
global = new Global(key, value);
|
|
111
|
+
this.stores.set(key, global);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*
|
|
111
|
-
* Each global query should contain the siteId
|
|
112
|
-
*/
|
|
113
|
-
export interface GlobalData {
|
|
114
|
-
siteId?: number;
|
|
115
|
-
[key: string]: any;
|
|
115
|
+
this.stores.forEach(global => global._notify());
|
|
116
|
+
}
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
/**
|
|
119
120
|
* A globalSet store
|
|
120
121
|
*/
|
|
121
|
-
export class Global<T
|
|
122
|
+
export class Global<T = any> {
|
|
123
|
+
/** @hidden */
|
|
122
124
|
private inner: Writable<T>;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (!inner?.siteId) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
`The global query ${name} does not contain the required siteId property`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
inner = data;
|
|
125
|
+
|
|
126
|
+
constructor(name: string, data: T) {
|
|
127
|
+
// todo remove in v1.0
|
|
128
|
+
// In v0.2, we queried the global data for all sites.
|
|
129
|
+
// We now check if the siteId is present and notify the user to remove it.
|
|
130
|
+
if (
|
|
131
|
+
typeof (data as any).siteId === 'number' ||
|
|
132
|
+
(Array.isArray(data) && typeof data[0]?.siteId === 'number')
|
|
133
|
+
) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`The global query ${name} should not include the siteId` +
|
|
136
|
+
` property. Instead, use the siteId as a parameter.`,
|
|
137
|
+
);
|
|
143
138
|
}
|
|
144
139
|
|
|
145
|
-
this.inner = new Writable(
|
|
140
|
+
this.inner = new Writable(data);
|
|
146
141
|
}
|
|
147
142
|
|
|
148
143
|
/**
|
|
@@ -162,25 +157,13 @@ export class Global<T extends GlobalData> {
|
|
|
162
157
|
return this.inner.get();
|
|
163
158
|
}
|
|
164
159
|
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
* ## Note
|
|
169
|
-
* If you pass a siteId which comes from craft
|
|
170
|
-
* you will never receive null
|
|
171
|
-
*/
|
|
172
|
-
bySiteId(siteId: number): T | null {
|
|
173
|
-
if (this.languages)
|
|
174
|
-
return this.languages.find(d => d.siteId === siteId) ?? null;
|
|
175
|
-
|
|
176
|
-
return this.inner.get();
|
|
160
|
+
/** @hidden */
|
|
161
|
+
_setSilent(value: T) {
|
|
162
|
+
this.inner.setSilent(value);
|
|
177
163
|
}
|
|
178
164
|
|
|
179
165
|
/** @hidden */
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const inner = this.languages.find(d => d.siteId === siteId);
|
|
184
|
-
this.inner.set(inner!);
|
|
166
|
+
_notify() {
|
|
167
|
+
this.inner.notify();
|
|
185
168
|
}
|
|
186
169
|
}
|
|
@@ -133,6 +133,7 @@ export default class InnerRouter {
|
|
|
133
133
|
return this.sites.find(s => s.id === id) ?? null;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// keep this doc in sync with Router.targetToRequest
|
|
136
137
|
/**
|
|
137
138
|
* Resolve a url or Route and convert it to a Request
|
|
138
139
|
*
|
|
@@ -150,6 +151,8 @@ export default class InnerRouter {
|
|
|
150
151
|
// exists
|
|
151
152
|
const site = this.route?.site ?? this.defaultSite();
|
|
152
153
|
target = new URL(site.uri + target, site.url);
|
|
154
|
+
} else if (!target) {
|
|
155
|
+
throw new Error('the url is not allowed to be empty');
|
|
153
156
|
} else {
|
|
154
157
|
target = new URL(target);
|
|
155
158
|
}
|
|
@@ -242,7 +245,11 @@ export default class InnerRouter {
|
|
|
242
245
|
if (currentMouseOver && link === currentMouseOver) return;
|
|
243
246
|
if (link && link.target.toLowerCase() === '_blank') return;
|
|
244
247
|
|
|
245
|
-
if (
|
|
248
|
+
if (
|
|
249
|
+
link &&
|
|
250
|
+
!link.hasAttribute('data-no-preload') &&
|
|
251
|
+
link.href
|
|
252
|
+
) {
|
|
246
253
|
this.preload(link.href);
|
|
247
254
|
}
|
|
248
255
|
|
|
@@ -288,7 +295,7 @@ export default class InnerRouter {
|
|
|
288
295
|
}
|
|
289
296
|
|
|
290
297
|
window.addEventListener('popstate', async e => {
|
|
291
|
-
if (!
|
|
298
|
+
if (!e.state?.route) return;
|
|
292
299
|
|
|
293
300
|
const req = this.targetToRequest(window.location.href);
|
|
294
301
|
req._fillFromState(e.state);
|
package/src/routing/Router.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type RouterOptions = {
|
|
|
14
14
|
|
|
15
15
|
const defaultRouterOpts = {
|
|
16
16
|
preloadOnMouseOver: false,
|
|
17
|
-
|
|
17
|
+
debugTiming: false,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
type LoadedMore = {
|
|
@@ -67,13 +67,19 @@ type ServerInited = {
|
|
|
67
67
|
export default class Router {
|
|
68
68
|
/**
|
|
69
69
|
* The current route
|
|
70
|
+
*
|
|
71
|
+
* ## Note
|
|
72
|
+
* Will always contain a route expect in the first loadData call
|
|
70
73
|
*/
|
|
71
|
-
private _route: Writable<Route>;
|
|
74
|
+
private _route: Writable<Route | null>;
|
|
72
75
|
|
|
73
76
|
/**
|
|
74
77
|
* The current site
|
|
78
|
+
*
|
|
79
|
+
* ## Note
|
|
80
|
+
* Will always contain a site expect in the first loadData call
|
|
75
81
|
*/
|
|
76
|
-
private _site: Writable<Site>;
|
|
82
|
+
private _site: Writable<Site | null>;
|
|
77
83
|
|
|
78
84
|
// the next request, just here to destroy it
|
|
79
85
|
private _request: Request | null;
|
|
@@ -139,15 +145,25 @@ export default class Router {
|
|
|
139
145
|
|
|
140
146
|
/**
|
|
141
147
|
* returns a store with the current route
|
|
148
|
+
*
|
|
149
|
+
* ## Note
|
|
150
|
+
* Will always contain a route expect in the first loadData call
|
|
151
|
+
*
|
|
152
|
+
* Consider to use CrelteRequest instead
|
|
142
153
|
*/
|
|
143
|
-
get route(): Readable<Route> {
|
|
154
|
+
get route(): Readable<Route | null> {
|
|
144
155
|
return this._route.readclone();
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
/**
|
|
148
159
|
* returns a store with the current site
|
|
160
|
+
*
|
|
161
|
+
* ## Note
|
|
162
|
+
* Will always contain a site expect in the first loadData call
|
|
163
|
+
*
|
|
164
|
+
* Consider to use CrelteRequest instead
|
|
149
165
|
*/
|
|
150
|
-
get site(): Readable<Site> {
|
|
166
|
+
get site(): Readable<Site | null> {
|
|
151
167
|
return this._site.readonly();
|
|
152
168
|
}
|
|
153
169
|
|
|
@@ -242,6 +258,7 @@ export default class Router {
|
|
|
242
258
|
* @deprecated use push instead
|
|
243
259
|
*/
|
|
244
260
|
pushState(route: Route | Request) {
|
|
261
|
+
console.warn('pushState is deprecated, use push instead');
|
|
245
262
|
this.push(route);
|
|
246
263
|
}
|
|
247
264
|
|
|
@@ -284,6 +301,7 @@ export default class Router {
|
|
|
284
301
|
* @deprecated use replace instead
|
|
285
302
|
*/
|
|
286
303
|
replaceState(route: Route | Request) {
|
|
304
|
+
console.warn('replaceState is deprecated, use replace instead');
|
|
287
305
|
this.replace(route);
|
|
288
306
|
}
|
|
289
307
|
|
|
@@ -313,11 +331,13 @@ export default class Router {
|
|
|
313
331
|
*
|
|
314
332
|
* This will trigger every time a new route is set
|
|
315
333
|
* and is equivalent to router.route.subscribe(fn)
|
|
334
|
+
* expect that it will not trigger instantly
|
|
316
335
|
*
|
|
317
336
|
* @returns a function to remove the listener
|
|
318
337
|
*/
|
|
319
338
|
onRoute(fn: (route: Route) => void): () => void {
|
|
320
|
-
|
|
339
|
+
let first = true;
|
|
340
|
+
return this.route.subscribe(r => (first ? (first = false) : fn(r!)));
|
|
321
341
|
}
|
|
322
342
|
|
|
323
343
|
/**
|
|
@@ -331,6 +351,20 @@ export default class Router {
|
|
|
331
351
|
return this._onRequest.add(fn);
|
|
332
352
|
}
|
|
333
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Resolve a url or Route and convert it to a Request
|
|
356
|
+
*
|
|
357
|
+
* @param target
|
|
358
|
+
* @param opts, any option present will override the value in target
|
|
359
|
+
* @return Returns null if the url does not match our host (the protocol get's ignored)
|
|
360
|
+
*/
|
|
361
|
+
targetToRequest(
|
|
362
|
+
target: string | URL | Route | Request,
|
|
363
|
+
opts: RequestOptions = {},
|
|
364
|
+
): Request {
|
|
365
|
+
return this.inner.targetToRequest(target, opts);
|
|
366
|
+
}
|
|
367
|
+
|
|
334
368
|
private setNewRoute(route: Route) {
|
|
335
369
|
this._route.setSilent(route);
|
|
336
370
|
const siteChanged = this.site.get()?.id !== route.site.id;
|
package/src/ssr/SsrCache.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export async function calcKey(data: any) {
|
|
2
|
+
const json = JSON.stringify(data);
|
|
3
|
+
// this should only happen in an unsecure context
|
|
4
|
+
// specifically in the craft preview locally
|
|
5
|
+
if (!crypto?.subtle) return json;
|
|
6
|
+
|
|
2
7
|
// Convert the string data to an ArrayBuffer
|
|
3
8
|
const encoder = new TextEncoder();
|
|
4
|
-
const dataBuffer = encoder.encode(
|
|
9
|
+
const dataBuffer = encoder.encode(json);
|
|
5
10
|
|
|
6
11
|
// Use the Web Crypto API to hash the data with SHA-1
|
|
7
12
|
const hashBuffer = await crypto.subtle.digest('SHA-1', dataBuffer);
|