crelte 0.1.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.
Files changed (130) hide show
  1. package/LICENSE.md +41 -0
  2. package/dist/Crelte.d.ts +55 -0
  3. package/dist/Crelte.d.ts.map +1 -0
  4. package/dist/Crelte.js +106 -0
  5. package/dist/CrelteBase.d.ts +16 -0
  6. package/dist/CrelteBase.d.ts.map +1 -0
  7. package/dist/CrelteBase.js +1 -0
  8. package/dist/CrelteRouted.d.ts +50 -0
  9. package/dist/CrelteRouted.d.ts.map +1 -0
  10. package/dist/CrelteRouted.js +88 -0
  11. package/dist/blocks/Blocks.d.ts +35 -0
  12. package/dist/blocks/Blocks.d.ts.map +1 -0
  13. package/dist/blocks/Blocks.js +100 -0
  14. package/dist/blocks/Blocks.svelte +21 -0
  15. package/dist/blocks/Blocks.svelte.d.ts +24 -0
  16. package/dist/blocks/Blocks.svelte.d.ts.map +1 -0
  17. package/dist/blocks/index.d.ts +5 -0
  18. package/dist/blocks/index.d.ts.map +1 -0
  19. package/dist/blocks/index.js +3 -0
  20. package/dist/cookies/ClientCookies.d.ts +9 -0
  21. package/dist/cookies/ClientCookies.d.ts.map +1 -0
  22. package/dist/cookies/ClientCookies.js +22 -0
  23. package/dist/cookies/ServerCookies.d.ts +13 -0
  24. package/dist/cookies/ServerCookies.d.ts.map +1 -0
  25. package/dist/cookies/ServerCookies.js +31 -0
  26. package/dist/cookies/index.d.ts +20 -0
  27. package/dist/cookies/index.d.ts.map +1 -0
  28. package/dist/cookies/index.js +1 -0
  29. package/dist/cookies/utils.d.ts +12 -0
  30. package/dist/cookies/utils.d.ts.map +1 -0
  31. package/dist/cookies/utils.js +32 -0
  32. package/dist/graphql/GraphQl.d.ts +60 -0
  33. package/dist/graphql/GraphQl.d.ts.map +1 -0
  34. package/dist/graphql/GraphQl.js +197 -0
  35. package/dist/graphql/gql.test.d.ts +2 -0
  36. package/dist/graphql/gql.test.d.ts.map +1 -0
  37. package/dist/graphql/gql.test.js +80 -0
  38. package/dist/graphql/index.d.ts +3 -0
  39. package/dist/graphql/index.d.ts.map +1 -0
  40. package/dist/graphql/index.js +2 -0
  41. package/dist/index.d.ts +67 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +83 -0
  44. package/dist/init/client.d.ts +13 -0
  45. package/dist/init/client.d.ts.map +1 -0
  46. package/dist/init/client.js +129 -0
  47. package/dist/init/server.d.ts +38 -0
  48. package/dist/init/server.d.ts.map +1 -0
  49. package/dist/init/server.js +95 -0
  50. package/dist/init/shared.d.ts +29 -0
  51. package/dist/init/shared.d.ts.map +1 -0
  52. package/dist/init/shared.js +154 -0
  53. package/dist/loadData/Globals.d.ts +33 -0
  54. package/dist/loadData/Globals.d.ts.map +1 -0
  55. package/dist/loadData/Globals.js +119 -0
  56. package/dist/loadData/index.d.ts +25 -0
  57. package/dist/loadData/index.d.ts.map +1 -0
  58. package/dist/loadData/index.js +39 -0
  59. package/dist/plugins/Events.d.ts +11 -0
  60. package/dist/plugins/Events.d.ts.map +1 -0
  61. package/dist/plugins/Events.js +29 -0
  62. package/dist/plugins/Plugins.d.ts +12 -0
  63. package/dist/plugins/Plugins.d.ts.map +1 -0
  64. package/dist/plugins/Plugins.js +12 -0
  65. package/dist/plugins/index.d.ts +5 -0
  66. package/dist/plugins/index.d.ts.map +1 -0
  67. package/dist/plugins/index.js +2 -0
  68. package/dist/routing/History.d.ts +22 -0
  69. package/dist/routing/History.d.ts.map +1 -0
  70. package/dist/routing/History.js +36 -0
  71. package/dist/routing/InnerRouter.d.ts +111 -0
  72. package/dist/routing/InnerRouter.d.ts.map +1 -0
  73. package/dist/routing/InnerRouter.js +397 -0
  74. package/dist/routing/PageLoader.d.ts +37 -0
  75. package/dist/routing/PageLoader.d.ts.map +1 -0
  76. package/dist/routing/PageLoader.js +72 -0
  77. package/dist/routing/Route.d.ts +82 -0
  78. package/dist/routing/Route.d.ts.map +1 -0
  79. package/dist/routing/Route.js +134 -0
  80. package/dist/routing/Router.d.ts +162 -0
  81. package/dist/routing/Router.d.ts.map +1 -0
  82. package/dist/routing/Router.js +333 -0
  83. package/dist/routing/Site.d.ts +47 -0
  84. package/dist/routing/Site.d.ts.map +1 -0
  85. package/dist/routing/Site.js +48 -0
  86. package/dist/routing/index.d.ts +5 -0
  87. package/dist/routing/index.d.ts.map +1 -0
  88. package/dist/routing/index.js +4 -0
  89. package/dist/ssr/SsrCache.d.ts +12 -0
  90. package/dist/ssr/SsrCache.d.ts.map +1 -0
  91. package/dist/ssr/SsrCache.js +50 -0
  92. package/dist/ssr/SsrComponents.d.ts +7 -0
  93. package/dist/ssr/SsrComponents.d.ts.map +1 -0
  94. package/dist/ssr/SsrComponents.js +30 -0
  95. package/dist/ssr/index.d.ts +4 -0
  96. package/dist/ssr/index.d.ts.map +1 -0
  97. package/dist/ssr/index.js +3 -0
  98. package/package.json +79 -0
  99. package/src/Crelte.ts +135 -0
  100. package/src/CrelteBase.ts +24 -0
  101. package/src/CrelteRouted.ts +128 -0
  102. package/src/blocks/Blocks.svelte +68 -0
  103. package/src/blocks/Blocks.ts +155 -0
  104. package/src/blocks/index.ts +14 -0
  105. package/src/cookies/ClientCookies.ts +30 -0
  106. package/src/cookies/ServerCookies.ts +42 -0
  107. package/src/cookies/index.ts +24 -0
  108. package/src/cookies/utils.ts +53 -0
  109. package/src/graphql/GraphQl.ts +281 -0
  110. package/src/graphql/gql.test.ts +123 -0
  111. package/src/graphql/index.ts +8 -0
  112. package/src/index.ts +109 -0
  113. package/src/init/client.ts +190 -0
  114. package/src/init/server.ts +177 -0
  115. package/src/init/shared.ts +221 -0
  116. package/src/loadData/Globals.ts +150 -0
  117. package/src/loadData/index.ts +67 -0
  118. package/src/plugins/Events.ts +50 -0
  119. package/src/plugins/Plugins.ts +23 -0
  120. package/src/plugins/index.ts +5 -0
  121. package/src/routing/History.ts +52 -0
  122. package/src/routing/InnerRouter.ts +469 -0
  123. package/src/routing/PageLoader.ts +112 -0
  124. package/src/routing/Route.ts +184 -0
  125. package/src/routing/Router.ts +476 -0
  126. package/src/routing/Site.ts +65 -0
  127. package/src/routing/index.ts +5 -0
  128. package/src/ssr/SsrCache.ts +61 -0
  129. package/src/ssr/SsrComponents.ts +34 -0
  130. package/src/ssr/index.ts +4 -0
@@ -0,0 +1,281 @@
1
+ import SsrCache, { calcKey as ssrCacheCalcKey } from '../ssr/SsrCache.js';
2
+
3
+ export type GraphQlErrorResponse = {
4
+ status?: number;
5
+ body?: string;
6
+ };
7
+
8
+ // todo improve this
9
+ export class GraphQlError extends Error {
10
+ resp: GraphQlErrorResponse;
11
+ ctx: any;
12
+
13
+ // ctx might be anything
14
+ constructor(resp: GraphQlErrorResponse, ctx: any = null) {
15
+ super();
16
+
17
+ this.resp = resp;
18
+ this.ctx = ctx;
19
+ }
20
+
21
+ status(): number {
22
+ return this.resp?.status ?? 500;
23
+ }
24
+
25
+ __isGraphQlError__() {}
26
+
27
+ get message(): string {
28
+ return 'GraphqlError: ' + JSON.stringify(this.resp);
29
+ }
30
+ }
31
+
32
+ export type GraphQlOptions = {
33
+ debug?: boolean;
34
+ debugTiming?: boolean;
35
+ };
36
+
37
+ export type GraphQlRequestOptions = {
38
+ path?: string;
39
+ ignoreStatusCode?: boolean;
40
+ previewToken?: string;
41
+ siteToken?: string;
42
+ caching?: boolean;
43
+ headers?: Record<string, string>;
44
+ // will be set by the request function
45
+ status?: number;
46
+ };
47
+
48
+ export default class GraphQl {
49
+ private endpoint: string;
50
+ private ssrCache: SsrCache;
51
+ private listeners: Map<
52
+ string,
53
+ [(resp: unknown) => void, (error: unknown) => void][]
54
+ >;
55
+
56
+ private loggingRequests: boolean;
57
+ private loggingTiming: boolean;
58
+
59
+ /**
60
+ * Create a new GraphQL
61
+ *
62
+ * @param {Object} [opts={}] opts `{ debug: false, debugTiming: false }`
63
+ */
64
+ constructor(
65
+ endpoint: string,
66
+ ssrCache: SsrCache,
67
+ opts: GraphQlOptions = {},
68
+ ) {
69
+ this.endpoint = endpoint;
70
+ this.ssrCache = ssrCache;
71
+ this.listeners = new Map();
72
+
73
+ this.loggingRequests = opts?.debug ?? false;
74
+ this.loggingTiming = opts?.debugTiming ?? false;
75
+ }
76
+
77
+ // returns {} or throws
78
+ // options: {ignoreStatusCode, previewToken, caching, headers}
79
+ async request(
80
+ query: string,
81
+ variables: Record<string, unknown> = {},
82
+ options: GraphQlRequestOptions = {},
83
+ ): Promise<unknown> {
84
+ let key;
85
+ if (options?.caching ?? true) {
86
+ key = await ssrCacheCalcKey({ query, variables });
87
+
88
+ const inCache = this.ssrCache.get(key);
89
+ if (inCache) return inCache;
90
+
91
+ // check if a listeners exists meaning the same request is already
92
+ // in progress
93
+ const listeners = this.listeners.get(key);
94
+ if (listeners) {
95
+ return new Promise((resolve, error) => {
96
+ listeners.push([resolve, error]);
97
+ });
98
+ }
99
+
100
+ // else setup the listener
101
+ this.listeners.set(key, []);
102
+ }
103
+
104
+ try {
105
+ const resp = await this.rawRequest(query, variables, options);
106
+ if (key) {
107
+ this.ssrCache.set(key, resp);
108
+
109
+ // ! because the listeners get's in the previous if statement and will
110
+ // always be set when the key is set
111
+ const listeners = this.listeners.get(key)!;
112
+ listeners.forEach(([resolve, _error]) => {
113
+ resolve(resp);
114
+ });
115
+
116
+ this.listeners.delete(key);
117
+ }
118
+
119
+ return resp;
120
+ } catch (e: unknown) {
121
+ if (key) {
122
+ // ! because the listeners get's in the previous if statement and will
123
+ // always be set when the key is set
124
+ const listeners = this.listeners.get(key)!;
125
+ listeners.forEach(([_resolve, error]) => {
126
+ error(e);
127
+ });
128
+
129
+ this.listeners.delete(key);
130
+ }
131
+
132
+ throw e;
133
+ }
134
+ }
135
+
136
+ // status will be set in opts
137
+ private async rawRequest(
138
+ query: string,
139
+ variables: Record<string, unknown> = {},
140
+ opts: GraphQlRequestOptions = {},
141
+ ) {
142
+ opts.ignoreStatusCode = opts.ignoreStatusCode ?? false;
143
+
144
+ let url = this.endpoint;
145
+
146
+ if (opts?.previewToken) url += '?token=' + opts.previewToken;
147
+ else if (opts?.siteToken) url += '?siteToken=' + opts.siteToken;
148
+
149
+ const headers = opts?.headers ?? {};
150
+ headers['Content-Type'] = 'application/json';
151
+
152
+ if (this.loggingRequests) {
153
+ console.log('query to ', url, variables, opts);
154
+ headers['X-Debug'] = 'enable';
155
+ }
156
+
157
+ let timing;
158
+ if (this.loggingTiming) timing = Date.now();
159
+
160
+ const resp = await fetch(url, {
161
+ method: 'POST',
162
+ headers,
163
+ body: JSON.stringify({
164
+ query,
165
+ variables,
166
+ }),
167
+ });
168
+
169
+ if (opts.ignoreStatusCode) {
170
+ opts.status = resp.status;
171
+ } else if (!resp.ok) {
172
+ throw new GraphQlError(
173
+ {
174
+ status: resp.status,
175
+ body: await resp.text(),
176
+ },
177
+ 'resp not ok',
178
+ );
179
+ }
180
+
181
+ if (resp.headers.get('x-debug-link'))
182
+ console.log('Debug link', resp.headers.get('x-debug-link'));
183
+
184
+ if (timing) {
185
+ console.log(
186
+ 'request ' +
187
+ opts.path +
188
+ ' vars: ' +
189
+ JSON.stringify(variables) +
190
+ ' took ' +
191
+ (Date.now() - timing) +
192
+ 'ms',
193
+ );
194
+ }
195
+
196
+ let jsonResp;
197
+ try {
198
+ jsonResp = await resp.json();
199
+ } catch (e: unknown) {
200
+ throw new GraphQlError(
201
+ {
202
+ status: resp.status,
203
+ },
204
+ e,
205
+ );
206
+ }
207
+
208
+ if ('errors' in jsonResp) {
209
+ console.log('graphql errors', jsonResp.errors);
210
+ throw new GraphQlError(
211
+ {
212
+ status: resp.status,
213
+ },
214
+ jsonResp.errors,
215
+ );
216
+ }
217
+
218
+ return jsonResp.data ?? null;
219
+ }
220
+ }
221
+
222
+ export class GraphQlQuery {
223
+ query: string;
224
+ path: string;
225
+
226
+ constructor(query: string, path: string) {
227
+ this.query = query;
228
+ this.path = path;
229
+ }
230
+
231
+ /// doc hidden
232
+ __GraphQlQuery__() {
233
+ return true;
234
+ }
235
+ }
236
+
237
+ /** Returns true if the passed object is a GraphQlQuery */
238
+ export function isGraphQlQuery(obj: any): obj is GraphQlQuery {
239
+ return (
240
+ typeof obj === 'object' &&
241
+ obj !== null &&
242
+ typeof obj.__GraphQlQuery__ === 'function'
243
+ );
244
+ }
245
+
246
+ /**
247
+ * @description Create a GraphQL query string with variables.
248
+ * @param strings
249
+ * @param keys
250
+ *
251
+ * @example
252
+ *
253
+ * const query = gql`query ($id: ID!) { page(id: $id) { id } }`
254
+ */
255
+ export function gql(
256
+ strings: TemplateStringsArray | string[] | string,
257
+ ...keys: unknown[]
258
+ ): GraphQlQuery {
259
+ if (typeof strings === 'string') strings = [strings];
260
+
261
+ let query = '';
262
+ strings.forEach((string, i) => {
263
+ query += string;
264
+
265
+ if (typeof keys[i] !== 'undefined') {
266
+ const variable = keys[i];
267
+
268
+ // nesting support
269
+ if (isGraphQlQuery(variable)) {
270
+ query += variable.query;
271
+ } else if (typeof variable === 'string') {
272
+ query += variable;
273
+ } else {
274
+ console.error('invalid key', variable);
275
+ throw new Error('Invalid key: ' + typeof variable);
276
+ }
277
+ }
278
+ });
279
+
280
+ return new GraphQlQuery(query, import.meta.url);
281
+ }
@@ -0,0 +1,123 @@
1
+ import { test, expect, describe } from 'vitest';
2
+ import { gql } from './GraphQl.js';
3
+
4
+ // tests for the gql function
5
+ // testing the query property
6
+ describe('gql: query property', () => {
7
+ test('gql: without variables', () => {
8
+ const query = gql`a bc`;
9
+
10
+ expect(query).toHaveProperty('query', 'a bc');
11
+ expect(query).toHaveProperty('path');
12
+ });
13
+
14
+ test('gql: with one variable', () => {
15
+ const variable = 'var';
16
+ const query = gql`a ${variable} bc`;
17
+
18
+ expect(query).toHaveProperty('query', `a ${variable} bc`);
19
+ expect(query).toHaveProperty('path');
20
+ });
21
+
22
+ test('gql: with multiple variables', () => {
23
+ const variable1 = 'var1';
24
+ const variable2 = 'var2';
25
+ const variable3 = 'var3';
26
+
27
+ const query = gql`a ${variable1} bc ${variable2} def ${variable3}`;
28
+
29
+ expect(query).toHaveProperty(
30
+ 'query',
31
+ `a ${variable1} bc ${variable2} def ${variable3}`,
32
+ );
33
+ expect(query).toHaveProperty('path');
34
+ });
35
+
36
+ test('gql: beginning with variable', () => {
37
+ const variable1 = 'var1';
38
+ const variable2 = 'var2';
39
+ const variable3 = 'var3';
40
+
41
+ const query = gql`${variable1} abc ${variable2} def ${variable3}`;
42
+
43
+ expect(query).toHaveProperty(
44
+ 'query',
45
+ `${variable1} abc ${variable2} def ${variable3}`,
46
+ );
47
+ expect(query).toHaveProperty('path');
48
+ });
49
+
50
+ test('gql: ending with variable', () => {
51
+ const variable1 = 'var1';
52
+
53
+ const query = gql`abcdef ${variable1}`;
54
+
55
+ expect(query.query.trim()).toBe(`abcdef ${variable1}`);
56
+ expect(query).toHaveProperty('path');
57
+ });
58
+
59
+ test('gql: exclusive variable', () => {
60
+ const variable1 = 'var1';
61
+
62
+ const query = gql`
63
+ ${variable1}
64
+ `;
65
+
66
+ expect(query.query.trim()).toBe(`${variable1}`);
67
+ expect(query).toHaveProperty('path');
68
+ });
69
+
70
+ test('gql: empty string', () => {
71
+ const query = gql``;
72
+
73
+ expect(query).toHaveProperty('query', ``);
74
+ expect(query).toHaveProperty('path');
75
+ });
76
+
77
+ test('gql: only variables', () => {
78
+ const variable1 = 'var1';
79
+ const variable2 = 'var2';
80
+ const variable3 = 'var3';
81
+
82
+ // prettier-ignore
83
+ const query = gql`${variable1}${variable2}${variable3}`;
84
+
85
+ expect(query).toHaveProperty(
86
+ 'query',
87
+ `${variable1}${variable2}${variable3}`,
88
+ );
89
+ expect(query).toHaveProperty('path');
90
+ });
91
+ });
92
+
93
+ describe('gql: path property', () => {
94
+ const root = process.cwd();
95
+
96
+ test('gql: path is graphql.js file', () => {
97
+ const query = gql``;
98
+
99
+ expect(query).toHaveProperty('query');
100
+ expect(query).toHaveProperty(
101
+ 'path',
102
+ 'file://' + root + '/src/graphql/GraphQl.ts',
103
+ );
104
+ });
105
+ });
106
+
107
+ describe('gql: nesting', () => {
108
+ test('gql: nesting', () => {
109
+ const variable1 = 'var1';
110
+ const variable2 = 'var2';
111
+ const variable3 = 'var3';
112
+
113
+ const query = gql`a ${variable1} bc ${gql`a ${variable2} bc ${variable3}`}`;
114
+
115
+ expect(query).toHaveProperty(
116
+ 'query',
117
+ `a ${variable1} bc a ${variable2} bc ${variable3}`,
118
+ );
119
+ expect(query).toHaveProperty('path');
120
+ });
121
+ });
122
+
123
+ describe.todo('gql: variable types. Handling of objects and arrays');
@@ -0,0 +1,8 @@
1
+ import GraphQl, {
2
+ gql,
3
+ GraphQlQuery,
4
+ GraphQlError,
5
+ GraphQlRequestOptions,
6
+ } from './GraphQl.js';
7
+
8
+ export { GraphQl, gql, GraphQlQuery, GraphQlError, type GraphQlRequestOptions };
package/src/index.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { getContext } from 'svelte';
2
+ import type Route from './routing/Route.js';
3
+ import type Router from './routing/Router.js';
4
+ import type SsrCache from './ssr/SsrCache.js';
5
+ import type Site from './routing/Site.js';
6
+ import type GraphQl from './graphql/GraphQl.js';
7
+ import Crelte from './Crelte.js';
8
+ import CrelteRouted from './CrelteRouted.js';
9
+ import CrelteBase from './CrelteBase.js';
10
+ import { Global, GlobalData } from './loadData/Globals.js';
11
+ import { Cookies } from './cookies/index.js';
12
+ import { Readable } from 'crelte-std/stores';
13
+
14
+ export { Crelte, CrelteRouted };
15
+ export type { CrelteBase };
16
+
17
+ /**
18
+ * Get the Crelte from the current context
19
+ */
20
+ export function getCrelte(): Crelte {
21
+ return getContext('crelte');
22
+ }
23
+
24
+ /**
25
+ * Get the router from the current context
26
+ */
27
+ export function getRouter(): Router {
28
+ return getCrelte().router;
29
+ }
30
+
31
+ /**
32
+ * Get the SSR cache from the current context
33
+ */
34
+ export function getSsrCache(): SsrCache {
35
+ return getCrelte().ssrCache;
36
+ }
37
+
38
+ /**
39
+ * Get the GraphQl from the current context
40
+ */
41
+ export function getGraphQl(): GraphQl {
42
+ return getCrelte().graphQl;
43
+ }
44
+
45
+ /**
46
+ * Get the current route
47
+ */
48
+ export function getRoute(): Readable<Route> {
49
+ return getRouter().route;
50
+ }
51
+
52
+ /**
53
+ * Get the current site
54
+ */
55
+ export function getSite(): Readable<Site> {
56
+ return getRouter().site;
57
+ }
58
+
59
+ /**
60
+ * Get the next route
61
+ */
62
+ export function getNextRoute(): Readable<Route> {
63
+ return getRouter().nextRoute;
64
+ }
65
+
66
+ /**
67
+ * Get the next site
68
+ */
69
+ export function getNextSite(): Readable<Site> {
70
+ return getRouter().nextSite;
71
+ }
72
+
73
+ /**
74
+ * returns an env Variables, always needs to be prefixed VITE_
75
+ * Except ENDPOINT_URL and CRAFT_WEB_URL
76
+ */
77
+ export function getEnv(name: string): string | null {
78
+ return getCrelte().getEnv(name);
79
+ }
80
+
81
+ /**
82
+ * returns a store which indicates if the a page is loading
83
+ */
84
+ export function getLoading(): Readable<boolean> {
85
+ return getRouter().loading;
86
+ }
87
+
88
+ /**
89
+ * returns a store which indicates the loading progress
90
+ */
91
+ export function getLoadingProgress(): Readable<number> {
92
+ return getRouter().loadingProgress;
93
+ }
94
+
95
+ /**
96
+ * returns a store which contains a globalSet
97
+ */
98
+ export function getGlobal<T extends GlobalData>(
99
+ name: string,
100
+ ): Global<T> | null {
101
+ return getCrelte().globals.get(name) ?? null;
102
+ }
103
+
104
+ /**
105
+ * returns the cookies instance
106
+ */
107
+ export function getCookies(): Cookies {
108
+ return getCrelte().cookies;
109
+ }
@@ -0,0 +1,190 @@
1
+ import { CrelteBuilder } from '../Crelte.js';
2
+ import { SiteFromGraphQl } from '../routing/Site.js';
3
+ import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
4
+ import { tick } from 'svelte';
5
+
6
+ export type MainData = {
7
+ /// svelte app component
8
+ app: any;
9
+ /// error page
10
+ errorPage: any;
11
+ entryQuery: any;
12
+ globalQuery?: any;
13
+
14
+ // options
15
+ preloadOnMouseOver?: boolean;
16
+ viewTransition?: boolean;
17
+ playIntro?: boolean;
18
+
19
+ // debug
20
+ graphQlDebug?: boolean;
21
+ debugTiming?: boolean;
22
+ };
23
+
24
+ const mainDataDefault = {
25
+ preloadOnMouseOver: false,
26
+ viewTransition: false,
27
+ playIntro: false,
28
+
29
+ // will be passed down to ClientRenderer
30
+ graphQlDebug: false,
31
+ debugTiming: false,
32
+ };
33
+
34
+ export function main(data: MainData) {
35
+ data = { ...mainDataDefault, ...data };
36
+
37
+ // rendering steps
38
+
39
+ // loadSites (first time)
40
+ // determine route (if site is empty on server redirect to correct language)
41
+ // loadData, entry (make globally available), pluginsData
42
+ // loadTemplate
43
+ // render
44
+
45
+ // on route change
46
+ // determine route
47
+ // entry, pluginsData
48
+ // loadTemplate
49
+ // update
50
+
51
+ // construct Crelte
52
+
53
+ const builder = new CrelteBuilder();
54
+ const endpoint = builder.ssrCache.get('ENDPOINT_URL') as string;
55
+ builder.setupGraphQl(endpoint, {
56
+ debug: data.graphQlDebug,
57
+ debugTiming: data.debugTiming,
58
+ });
59
+
60
+ // on the client the cookies are always comming from document.cookie
61
+ builder.setupCookies('');
62
+
63
+ const csites = builder.ssrCache.get('crelteSites') as SiteFromGraphQl[];
64
+ builder.setupRouter(csites, {
65
+ preloadOnMouseOver: data.preloadOnMouseOver,
66
+ });
67
+
68
+ const crelte = builder.build();
69
+
70
+ const serverError = crelte.ssrCache.get('ERROR');
71
+ if (serverError) {
72
+ // should this be called??
73
+ crelte.router._internal.initClient();
74
+
75
+ new data.errorPage.default({
76
+ target: document.body,
77
+ props: { ...serverError },
78
+ hydrate: true,
79
+ context: crelte._getContext(),
80
+ });
81
+ return;
82
+ }
83
+
84
+ // setup plugins
85
+ setupPlugins(crelte, data.app.plugins ?? []);
86
+
87
+ // setup load Data
88
+
89
+ crelte.router._internal.onLoad = (route, site, opts) => {
90
+ const cr = crelte.toRouted(route, site);
91
+ return loadFn(cr, data.app, data.entryQuery, data.globalQuery, opts);
92
+ };
93
+
94
+ // render Space
95
+
96
+ let appInstance: any;
97
+ const updateAppProps = (props: any) => {
98
+ if (!appInstance) {
99
+ appInstance = new data.app.default({
100
+ target: document.body,
101
+ props,
102
+ hydrate: true,
103
+ context: crelte._getContext(),
104
+ intro: data.playIntro,
105
+ });
106
+ } else {
107
+ appInstance.$set(props);
108
+ }
109
+ };
110
+
111
+ let firstLoad = true;
112
+ crelte.router._internal.onLoaded = async (
113
+ success,
114
+ route,
115
+ site,
116
+ readyForProps,
117
+ ) => {
118
+ const isFirstLoad = firstLoad;
119
+ firstLoad = false;
120
+
121
+ if (!success) {
122
+ // if this is not the first load we should reload the page
123
+ // we don't reload everytime because this might cause a reload loop
124
+ // and since the first load will probably just contain ssrCache data in almost all cases the first load will never faill
125
+ if (!isFirstLoad) {
126
+ // the load might contain a js error and we prefer the error
127
+ // page
128
+ window.location.reload();
129
+ return;
130
+ }
131
+
132
+ return handleLoadError(readyForProps());
133
+ }
134
+
135
+ const cr = crelte.toRouted(route, site);
136
+
137
+ const startTime = data.debugTiming ? Date.now() : null;
138
+ let render = async () => {
139
+ // we should trigger the route update here
140
+ pluginsBeforeRender(cr);
141
+ crelte.globals._updateSiteId(site.id);
142
+ updateAppProps(readyForProps());
143
+
144
+ await tick();
145
+
146
+ if (startTime) {
147
+ console.log(
148
+ 'dom update took ' + (Date.now() - startTime) + 'ms',
149
+ );
150
+ }
151
+
152
+ crelte.router._internal.domReady(route);
153
+ };
154
+
155
+ // render with view Transition if enabled and not in hydration
156
+ if (
157
+ data.viewTransition &&
158
+ appInstance &&
159
+ (document as any).startViewTransition
160
+ ) {
161
+ render = () => (document as any).startViewTransition(render);
162
+ }
163
+
164
+ await render();
165
+ };
166
+
167
+ crelte.router._internal.initClient();
168
+ }
169
+
170
+ function handleLoadError(e: any) {
171
+ console.log('loading or rendering the page failed with the error:');
172
+ console.error(e);
173
+
174
+ // Detect the browser language
175
+ // @ts-ignore
176
+ const userLang: string = navigator.language || navigator.userLanguage;
177
+
178
+ // Messages in different languages
179
+ const messages: Record<string, string> = {
180
+ en: 'An error has occurred. Please reload the page or try again later.',
181
+ de: 'Leider ist ein Fehler aufgetreten. Laden Sie die Seite neu, oder versuchen Sie es später noch mal.',
182
+ fr: 'Une erreur s’est produite. Veuillez recharger la page ou réessayer plus tard.',
183
+ it: 'Si è verificato un errore. Ricarica la pagina o riprova più tardi.',
184
+ nl: 'Er is een fout opgetreden. Herlaad de pagina of probeer het later opnieuw.',
185
+ };
186
+
187
+ const message = messages[userLang.split('-')[0]] ?? messages.en;
188
+
189
+ alert(message);
190
+ }