• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kiva / ui / 14911871966

08 May 2025 04:57PM UTC coverage: 49.059% (+0.09%) from 48.969%
14911871966

push

github

emuvente
fix: provide current route ref value from server entry and remove contentful cookie

1595 of 3417 branches covered (46.68%)

Branch coverage included in aggregate %.

0 of 11 new or added lines in 5 files covered. (0.0%)

3 existing lines in 2 files now uncovered.

2393 of 4712 relevant lines covered (50.79%)

286.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/server-entry.js
1
/* eslint-disable vue/multi-word-component-names, no-throw-literal */
2
import { renderToString } from 'vue/server-renderer';
3
import serialize from 'serialize-javascript';
4
import { v4 as uuidv4 } from 'uuid';
5
import { renderSSRHead } from '@unhead/ssr';
6
import CookieStore from '#src/util/cookieStore';
7
import KvAuth0, { MockKvAuth0 } from '#src/util/KvAuth0';
8
import { preFetchAll } from '#src/util/apolloPreFetch';
9
import renderGlobals from '#src/util/renderGlobals';
10
import createApp from '#src/main';
11
import headScript from '#src/head/script';
12
import oneTrustEvent from '#src/head/oneTrustEvent';
13

14
// import noscriptTemplate from '#src/head/noscript.html';
15
import { authenticationGuard } from '#src/util/authenticationGuard';
16

17
import logFormatter from '#src/util/logFormatter';
18
import { buildUserDataGlobal } from '#src/util/optimizelyUserMetrics';
19

20
import fetch from 'make-fetch-happen';
21

22
const isDev = process.env.NODE_ENV !== 'production';
×
23

24
// custom fetch wrapper to log fetch requests
25
const customFetch = async (uri, options) => {
×
26
        const response = await fetch(uri, options);
×
27
        // Log the outgoing options
28
        logFormatter(`Fetch Options: ${uri}, Options: ${JSON.stringify(options)}`);
×
29

30
        // Log the full response
31
        // eslint-disable-next-line max-len
32
        logFormatter(`Server fetch: ${uri}, Status: ${response.status}, Headers: ${JSON.stringify(response.headers.raw())}`);
×
33

34
        return response;
×
35
};
36

37
function fillTemplate(template, data) {
38
        let html = template;
×
39
        Object.keys(data).forEach(key => {
×
40
                html = html.replace(`\${${key}}`, data[key]);
×
41
        });
42
        // TODO: minify html
43
        return html;
×
44
}
45

46
let renderedConfig = '';
×
47
let renderedExternals = '';
×
48
let renderedExternalsOptIn = '';
×
49

50
// This adds non-vue-rendered html strings to the request context.
51
// These strings are added to the final html response using server/index.template.html
52
function renderExtraHtml(config) {
53
        // render config if it hasn't been rendered yet
54
        if (!renderedConfig) {
×
55
                renderedConfig = renderGlobals({ __KV_CONFIG__: config });
×
56
        }
57
        // render externals if they haven't been rendered yet
58
        if (!renderedExternals) {
×
59
                // add OneTrust loader
60
                if (config.oneTrust && config.oneTrust.enable) {
×
61
                        const key = `${config.oneTrust.key}${config.oneTrust.domainSuffix}`;
×
62
                        const src = `https://cdn.cookielaw.org/consent/${key}/otSDKStub.js`;
×
63
                        renderedExternals += `<script type="text/javascript" data-domain-script="${key}" src="${src}"></script>`;
×
64
                }
65
                // add primary head script
66
                const renderedHeadScript = serialize(headScript);
×
67
                const renderedOneTrustEvent = serialize(oneTrustEvent);
×
68
                // eslint-disable-next-line max-len
69
                renderedExternals += `<script>(${renderedHeadScript})(window.__KV_CONFIG__, ${renderedOneTrustEvent});</script>`;
×
70
        }
71
        // render externals for users that are not opted out of 3rd party cookies
72
        if (!renderedExternalsOptIn) {
×
73
                // setup Optimizely loader
74
                if (config?.enableOptimizely && config?.optimizelyProjectId) {
×
75
                        // eslint-disable-next-line max-len
76
                        renderedExternalsOptIn += '<script type="text/javascript">window["optimizely"]=window["optimizely"]||[];window["optimizely"].push({"type":"holdEvents"});</script>';
×
77
                        const optimizelySrc = `https://cdn.optimizely.com/js/${config?.optimizelyProjectId}.js`;
×
78
                        renderedExternalsOptIn += `<script type="text/javascript" src="${optimizelySrc}"></script>`;
×
79
                }
80
                // append regular externals
81
                renderedExternalsOptIn += renderedExternals;
×
82
        }
83
}
84

85
// This function renders a <link> tag for a given file
86
function renderPreloadLink(file) {
87
        if (file.endsWith('.js')) {
×
88
                return `<link rel="modulepreload" crossorigin href="${file}">`;
×
89
        }
90
        if (file.endsWith('.css')) {
×
91
                return `<link rel="stylesheet" href="${file}">`;
×
92
        }
93
        // TODO: handle other file types if needed
94
        return '';
×
95
}
96

97
// This function renders <link> tags for all files in the manifest for the given modules
98
function renderPreloadLinks(modules, manifest = {}) {
×
99
        let links = '';
×
100
        const seen = new Set();
×
101
        modules.forEach(id => {
×
102
                const files = manifest[id];
×
103
                if (files) {
×
104
                        files.forEach(file => {
×
105
                                if (!seen.has(file)) {
×
106
                                        seen.add(file);
×
107
                                        links += renderPreloadLink(file);
×
108
                                }
109
                        });
110
                }
111
        });
112
        return links;
×
113
}
114

115
// This exported function will be called by vue-render.
116
// This is where we perform data-prefetching to determine the
117
// state of our application before actually rendering it.
118
// Since data fetching is async, this function is expected to
119
// return a Promise that resolves to the app instance.
120
export default async context => {
121
        const s = isDev && Date.now();
×
122
        const {
123
                url,
124
                config,
125
                kivaUserAgent,
126
                cookies,
127
                user,
128
                locale,
129
                device,
130
                ssrManifest,
131
                template,
132
        } = context;
×
133
        const { accessToken, ...profile } = user;
×
134

135
        // Create cookie store with cookies passed from express middleware
136
        const cookieStore = new CookieStore(cookies);
×
137

138
        // Create random visitor id if none is set
139
        if (!cookieStore.get('uiv')) {
×
140
                // Set visitor id cookie expiration for 2 years from now
141
                const expires = new Date();
×
142
                expires.setFullYear(expires.getFullYear() + 2);
×
143
                // Store visitor id as 'uiv' cookie
144
                cookieStore.set('uiv', uuidv4(), {
×
145
                        expires,
146
                        sameSite: true,
147
                        secure: true,
148
                        path: '/',
149
                });
150
        }
151

152
        let kvAuth0;
153
        if (config.auth0.enable) {
×
154
                kvAuth0 = new KvAuth0({
×
155
                        accessToken,
156
                        checkFakeAuth: config.auth0.checkFakeAuth,
157
                        cookieStore,
158
                        domain: config.auth0.domain,
159
                        user: profile,
160
                });
161
        } else {
162
                kvAuth0 = MockKvAuth0;
×
163
        }
164

165
        const {
166
                app,
167
                head,
168
                router,
169
                apolloClient,
170
        } = createApp({
×
171
                name: '',
172
                appConfig: config,
173
                apollo: {
174
                        uri: config.graphqlUri,
175
                        types: config.graphqlPossibleTypes
176
                },
177
                cookieStore,
178
                device,
179
                kvAuth0,
180
                locale,
181
                fetch: config?.apolloQueryFetchLogging ? customFetch : fetch,
×
182
                kivaUserAgent,
183
                url,
184
                isServer: true,
185
        });
186

187
        // redirect to the resolved url if it does not match the requested url
188
        const { fullPath } = router.resolve(url);
×
189
        if (fullPath !== url) {
×
190
                // redirects defined in routes.js use a permanent (301) redirect
191
                throw { url: fullPath, code: 301 };
×
192
        }
193

194
        // render content for template
195
        renderExtraHtml(config);
×
196

197
        // set router's location, ignoring any errors about redirection
198
        router.push(url).catch(() => { });
×
199

200
        // wait until router has resolved possible async hooks
201
        await router.isReady();
×
202

203
        // get the components matched by the route
204
        const matchedComponents = router.currentRoute.value.matched;
×
205

206
        // no matched routes
207
        if (!matchedComponents.length) {
×
208
                // TODO: Check for + redirect to kiva php app external route
209
                throw { code: 404 };
×
210
        }
211

UNCOV
212
        try {
×
213
                // Use route meta property to determine if route needs authentication
214
                // authenticationGuard will reject promise with a redirect to login if
215
                // required authentication query fails
NEW
216
                await authenticationGuard({ route: router.currentRoute.value, apolloClient, kvAuth0 });
×
217

218
                // Pre-fetch graphql queries from the components (and all of their child components)
219
                // matched by the route
220
                // preFetchAll dispatches the queries with Apollo and returns a Promise,
221
                // which is resolved when the action is complete and apollo cache has been updated.
222
                await preFetchAll(matchedComponents, apolloClient, {
×
223
                        cookieStore,
224
                        kvAuth0,
225
                        route: router.currentRoute.value,
226
                        device
227
                });
228

229
                let sp; // Vue serverPrefetch timing start
230
                if (isDev) {
×
231
                        logFormatter(`data pre-fetch: ${Date.now() - s}ms`);
×
232
                        sp = new Date();
×
233
                }
234

235
                // render the app
236
                const appHtml = await renderToString(app, context);
×
237

238
                if (isDev) logFormatter(`vue serverPrefetch: ${Date.now() - sp}ms`);
×
239

240
                // After all preFetch hooks are resolved, our store is now
241
                // filled with the state needed to render the app.
242
                // Expose the state on the render context, and let the request handler
243
                // inline the state in the HTML response. This allows the client-side
244
                // store to pick-up the server-side state without having to duplicate
245
                // the initial data fetching on the client.
246
                const appState = renderGlobals({
×
247
                        __APOLLO_STATE__: apolloClient.cache.extract(),
248
                        pageData: buildUserDataGlobal(router.currentRoute.value, cookieStore, apolloClient)
249
                });
250

251
                // render head tags
252
                const payload = await renderSSRHead(head);
×
253

254
                // render preload links
255
                const preloadLinks = renderPreloadLinks(context.modules, ssrManifest);
×
256

257
                // check for 3rd party script opt-out
258
                const hasOptOut = cookies?.kvgdpr?.indexOf('opted_out=true') > -1;
×
259

260
                const templateData = {
×
261
                        ...payload,
262
                        // Turn off SSR for local development to prevent component FOUC (Flash of Unstyled Content)
263
                        // https://github.com/vitejs/vite/issues/6887#issuecomment-1038664078
264
                        appHtml: isDev ? '' : appHtml,
×
265
                        appState,
266
                        appConfig: renderedConfig,
267
                        externals: hasOptOut ? renderedExternals : renderedExternalsOptIn,
×
268
                        googleTagmanagerId: config.googleTagmanagerId,
269
                        preloadLinks,
270
                };
271

272
                return {
×
273
                        html: fillTemplate(template, templateData),
274
                        setCookies: cookieStore.getSetCookies(),
275
                };
276
        } catch (error) {
277
                if (error instanceof Error) {
×
278
                        throw error;
×
279
                } else {
280
                        context.setCookies = cookieStore.getSetCookies();
×
281
                        throw {
×
282
                                url: router.resolve(error).href,
283
                        };
284
                }
285
        }
286
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc