@sveltejs/kit 1.0.0-next.49 → 1.0.0-next.491

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 (123) hide show
  1. package/README.md +12 -9
  2. package/package.json +93 -66
  3. package/scripts/special-types/$env+dynamic+private.md +10 -0
  4. package/scripts/special-types/$env+dynamic+public.md +8 -0
  5. package/scripts/special-types/$env+static+private.md +19 -0
  6. package/scripts/special-types/$env+static+public.md +7 -0
  7. package/scripts/special-types/$lib.md +5 -0
  8. package/src/cli.js +112 -0
  9. package/src/constants.js +7 -0
  10. package/src/core/adapt/builder.js +206 -0
  11. package/src/core/adapt/index.js +31 -0
  12. package/src/core/config/default-error.html +56 -0
  13. package/src/core/config/index.js +110 -0
  14. package/src/core/config/options.js +504 -0
  15. package/src/core/config/types.d.ts +1 -0
  16. package/src/core/env.js +121 -0
  17. package/src/core/generate_manifest/index.js +93 -0
  18. package/src/core/prerender/crawl.js +198 -0
  19. package/src/core/prerender/entities.js +2252 -0
  20. package/src/core/prerender/prerender.js +431 -0
  21. package/src/core/prerender/queue.js +80 -0
  22. package/src/core/sync/create_manifest_data/index.js +470 -0
  23. package/src/core/sync/create_manifest_data/types.d.ts +37 -0
  24. package/src/core/sync/sync.js +59 -0
  25. package/src/core/sync/utils.js +33 -0
  26. package/src/core/sync/write_ambient.js +53 -0
  27. package/src/core/sync/write_client_manifest.js +106 -0
  28. package/src/core/sync/write_matchers.js +25 -0
  29. package/src/core/sync/write_root.js +91 -0
  30. package/src/core/sync/write_tsconfig.js +195 -0
  31. package/src/core/sync/write_types/index.js +678 -0
  32. package/src/core/utils.js +70 -0
  33. package/src/exports/hooks/index.js +1 -0
  34. package/src/exports/hooks/sequence.js +44 -0
  35. package/src/exports/index.js +45 -0
  36. package/src/exports/node/index.js +168 -0
  37. package/src/exports/node/polyfills.js +41 -0
  38. package/src/exports/vite/build/build_server.js +378 -0
  39. package/src/exports/vite/build/build_service_worker.js +90 -0
  40. package/src/exports/vite/build/utils.js +180 -0
  41. package/src/exports/vite/dev/index.js +576 -0
  42. package/src/exports/vite/graph_analysis/index.js +277 -0
  43. package/src/exports/vite/graph_analysis/types.d.ts +5 -0
  44. package/src/exports/vite/graph_analysis/utils.js +30 -0
  45. package/src/exports/vite/index.js +598 -0
  46. package/src/exports/vite/preview/index.js +189 -0
  47. package/src/exports/vite/types.d.ts +3 -0
  48. package/src/exports/vite/utils.js +157 -0
  49. package/src/runtime/app/env.js +1 -0
  50. package/src/runtime/app/environment.js +11 -0
  51. package/src/runtime/app/forms.js +108 -0
  52. package/src/runtime/app/navigation.js +23 -0
  53. package/src/runtime/app/paths.js +1 -0
  54. package/src/runtime/app/stores.js +102 -0
  55. package/src/runtime/client/ambient.d.ts +26 -0
  56. package/src/runtime/client/client.js +1581 -0
  57. package/src/runtime/client/fetcher.js +107 -0
  58. package/src/runtime/client/parse.js +60 -0
  59. package/src/runtime/client/singletons.js +21 -0
  60. package/src/runtime/client/start.js +37 -0
  61. package/src/runtime/client/types.d.ts +85 -0
  62. package/src/runtime/client/utils.js +159 -0
  63. package/src/runtime/components/error.svelte +16 -0
  64. package/{assets → src/runtime}/components/layout.svelte +0 -0
  65. package/src/runtime/control.js +98 -0
  66. package/src/runtime/env/dynamic/private.js +1 -0
  67. package/src/runtime/env/dynamic/public.js +1 -0
  68. package/src/runtime/env-private.js +6 -0
  69. package/src/runtime/env-public.js +6 -0
  70. package/src/runtime/env.js +6 -0
  71. package/src/runtime/hash.js +16 -0
  72. package/src/runtime/paths.js +11 -0
  73. package/src/runtime/server/cookie.js +115 -0
  74. package/src/runtime/server/data/index.js +136 -0
  75. package/src/runtime/server/endpoint.js +83 -0
  76. package/src/runtime/server/index.js +337 -0
  77. package/src/runtime/server/page/actions.js +243 -0
  78. package/src/runtime/server/page/crypto.js +239 -0
  79. package/src/runtime/server/page/csp.js +249 -0
  80. package/src/runtime/server/page/fetch.js +288 -0
  81. package/src/runtime/server/page/index.js +304 -0
  82. package/src/runtime/server/page/load_data.js +124 -0
  83. package/src/runtime/server/page/render.js +342 -0
  84. package/src/runtime/server/page/respond_with_error.js +104 -0
  85. package/src/runtime/server/page/serialize_data.js +87 -0
  86. package/src/runtime/server/page/types.d.ts +41 -0
  87. package/src/runtime/server/utils.js +179 -0
  88. package/src/utils/array.js +9 -0
  89. package/src/utils/error.js +22 -0
  90. package/src/utils/escape.js +46 -0
  91. package/src/utils/filesystem.js +137 -0
  92. package/src/utils/functions.js +16 -0
  93. package/src/utils/http.js +55 -0
  94. package/src/utils/misc.js +1 -0
  95. package/src/utils/routing.js +117 -0
  96. package/src/utils/unit_test.js +11 -0
  97. package/src/utils/url.js +142 -0
  98. package/svelte-kit.js +1 -1
  99. package/types/ambient.d.ts +426 -0
  100. package/types/index.d.ts +428 -0
  101. package/types/internal.d.ts +378 -0
  102. package/types/private.d.ts +209 -0
  103. package/CHANGELOG.md +0 -476
  104. package/assets/components/error.svelte +0 -13
  105. package/assets/runtime/app/env.js +0 -5
  106. package/assets/runtime/app/navigation.js +0 -44
  107. package/assets/runtime/app/paths.js +0 -1
  108. package/assets/runtime/app/stores.js +0 -93
  109. package/assets/runtime/chunks/utils.js +0 -22
  110. package/assets/runtime/internal/singletons.js +0 -23
  111. package/assets/runtime/internal/start.js +0 -773
  112. package/assets/runtime/paths.js +0 -12
  113. package/dist/chunks/index.js +0 -3517
  114. package/dist/chunks/index2.js +0 -587
  115. package/dist/chunks/index3.js +0 -246
  116. package/dist/chunks/index4.js +0 -530
  117. package/dist/chunks/index5.js +0 -763
  118. package/dist/chunks/index6.js +0 -322
  119. package/dist/chunks/standard.js +0 -99
  120. package/dist/chunks/utils.js +0 -83
  121. package/dist/cli.js +0 -553
  122. package/dist/ssr.js +0 -2584
  123. package/types.d.ts +0 -32
@@ -0,0 +1,342 @@
1
+ import { devalue } from 'devalue';
2
+ import { readable, writable } from 'svelte/store';
3
+ import { hash } from '../../hash.js';
4
+ import { serialize_data } from './serialize_data.js';
5
+ import { s } from '../../../utils/misc.js';
6
+ import { Csp } from './csp.js';
7
+ import { add_cookies_to_headers } from '../cookie.js';
8
+
9
+ // TODO rename this function/module
10
+
11
+ const updated = {
12
+ ...readable(false),
13
+ check: () => false
14
+ };
15
+
16
+ /**
17
+ * Creates the HTML response.
18
+ * @param {{
19
+ * branch: Array<import('./types').Loaded>;
20
+ * fetched: Array<import('./types').Fetched>;
21
+ * cookies: import('./types').Cookie[];
22
+ * options: import('types').SSROptions;
23
+ * state: import('types').SSRState;
24
+ * page_config: { ssr: boolean; csr: boolean };
25
+ * status: number;
26
+ * error: App.PageError | null;
27
+ * event: import('types').RequestEvent;
28
+ * resolve_opts: import('types').RequiredResolveOptions;
29
+ * action_result?: import('types').ActionResult;
30
+ * }} opts
31
+ */
32
+ export async function render_response({
33
+ branch,
34
+ fetched,
35
+ cookies,
36
+ options,
37
+ state,
38
+ page_config,
39
+ status,
40
+ error = null,
41
+ event,
42
+ resolve_opts,
43
+ action_result
44
+ }) {
45
+ if (state.prerendering) {
46
+ if (options.csp.mode === 'nonce') {
47
+ throw new Error('Cannot use prerendering if config.kit.csp.mode === "nonce"');
48
+ }
49
+
50
+ if (options.app_template_contains_nonce) {
51
+ throw new Error('Cannot use prerendering if page template contains %sveltekit.nonce%');
52
+ }
53
+ }
54
+
55
+ const { entry } = options.manifest._;
56
+
57
+ const stylesheets = new Set(entry.stylesheets);
58
+ const modulepreloads = new Set(entry.imports);
59
+
60
+ /** @type {Set<string>} */
61
+ const link_header_preloads = new Set();
62
+
63
+ /** @type {Map<string, string>} */
64
+ // TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold
65
+ const inline_styles = new Map();
66
+
67
+ let rendered;
68
+
69
+ const form_value =
70
+ action_result?.type === 'success' || action_result?.type === 'invalid'
71
+ ? action_result.data ?? null
72
+ : null;
73
+
74
+ if (page_config.ssr) {
75
+ /** @type {Record<string, any>} */
76
+ const props = {
77
+ stores: {
78
+ page: writable(null),
79
+ navigating: writable(null),
80
+ updated
81
+ },
82
+ components: await Promise.all(branch.map(({ node }) => node.component())),
83
+ form: form_value
84
+ };
85
+
86
+ let data = {};
87
+
88
+ // props_n (instead of props[n]) makes it easy to avoid
89
+ // unnecessary updates for layout components
90
+ for (let i = 0; i < branch.length; i += 1) {
91
+ data = { ...data, ...branch[i].data };
92
+ props[`data_${i}`] = data;
93
+ }
94
+
95
+ props.page = {
96
+ error,
97
+ params: /** @type {Record<string, any>} */ (event.params),
98
+ routeId: event.routeId,
99
+ status,
100
+ url: event.url,
101
+ data
102
+ };
103
+
104
+ // TODO remove this for 1.0
105
+ /**
106
+ * @param {string} property
107
+ * @param {string} replacement
108
+ */
109
+ const print_error = (property, replacement) => {
110
+ Object.defineProperty(props.page, property, {
111
+ get: () => {
112
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
113
+ }
114
+ });
115
+ };
116
+
117
+ print_error('origin', 'origin');
118
+ print_error('path', 'pathname');
119
+ print_error('query', 'searchParams');
120
+
121
+ rendered = options.root.render(props);
122
+
123
+ for (const { node } of branch) {
124
+ if (node.imports) {
125
+ node.imports.forEach((url) => modulepreloads.add(url));
126
+ }
127
+
128
+ if (node.stylesheets) {
129
+ node.stylesheets.forEach((url) => stylesheets.add(url));
130
+ }
131
+
132
+ if (node.inline_styles) {
133
+ Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
134
+ }
135
+ }
136
+ } else {
137
+ rendered = { head: '', html: '', css: { code: '', map: null } };
138
+ }
139
+
140
+ let { head, html: body } = rendered;
141
+
142
+ const csp = new Csp(options.csp, {
143
+ dev: options.dev,
144
+ prerender: !!state.prerendering
145
+ });
146
+
147
+ const target = hash(body);
148
+
149
+ /**
150
+ * The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
151
+ * @type {string}
152
+ */
153
+ let assets;
154
+
155
+ if (options.paths.assets) {
156
+ // if an asset path is specified, use it
157
+ assets = options.paths.assets;
158
+ } else if (state.prerendering?.fallback) {
159
+ // if we're creating a fallback page, asset paths need to be root-relative
160
+ assets = options.paths.base;
161
+ } else {
162
+ // otherwise we want asset paths to be relative to the page, so that they
163
+ // will work in odd contexts like IPFS, the internet archive, and so on
164
+ const segments = event.url.pathname.slice(options.paths.base.length).split('/').slice(2);
165
+ assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
166
+ }
167
+
168
+ /** @param {string} path */
169
+ const prefixed = (path) => (path.startsWith('/') ? path : `${assets}/${path}`);
170
+
171
+ const serialized = { data: '', form: 'null' };
172
+
173
+ try {
174
+ serialized.data = devalue(branch.map(({ server_data }) => server_data));
175
+ } catch (e) {
176
+ // If we're here, the data could not be serialized with devalue
177
+ // TODO if we wanted to get super fancy we could track down the origin of the `load`
178
+ // function, but it would mean passing more stuff around than we currently do
179
+ const error = /** @type {any} */ (e);
180
+ const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
181
+ if (match) throw new Error(`${error.message} (data.${match[2]})`);
182
+ throw error;
183
+ }
184
+
185
+ if (form_value) {
186
+ // no need to check it can be serialized, we already verified that it's JSON-friendly
187
+ serialized.form = devalue(form_value);
188
+ }
189
+
190
+ // prettier-ignore
191
+ const init_app = `
192
+ import { start } from ${s(prefixed(entry.file))};
193
+
194
+ start({
195
+ env: ${s(options.public_env)},
196
+ hydrate: ${page_config.ssr ? `{
197
+ status: ${status},
198
+ error: ${s(error)},
199
+ node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
200
+ params: ${devalue(event.params)},
201
+ routeId: ${s(event.routeId)},
202
+ data: ${serialized.data},
203
+ form: ${serialized.form}
204
+ }` : 'null'},
205
+ paths: ${s(options.paths)},
206
+ target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
207
+ trailing_slash: ${s(options.trailing_slash)}
208
+ });
209
+ `;
210
+
211
+ // we use an anonymous function instead of an arrow function to support
212
+ // older browsers (https://github.com/sveltejs/kit/pull/5417)
213
+ const init_service_worker = `
214
+ if ('serviceWorker' in navigator) {
215
+ addEventListener('load', function () {
216
+ navigator.serviceWorker.register('${options.service_worker}');
217
+ });
218
+ }
219
+ `;
220
+
221
+ if (inline_styles.size > 0) {
222
+ const content = Array.from(inline_styles.values()).join('\n');
223
+
224
+ const attributes = [];
225
+ if (options.dev) attributes.push(' data-sveltekit');
226
+ if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
227
+
228
+ csp.add_style(content);
229
+
230
+ head += `\n\t<style${attributes.join('')}>${content}</style>`;
231
+ }
232
+
233
+ for (const dep of stylesheets) {
234
+ const path = prefixed(dep);
235
+ const attributes = [];
236
+
237
+ if (csp.style_needs_nonce) {
238
+ attributes.push(`nonce="${csp.nonce}"`);
239
+ }
240
+
241
+ if (inline_styles.has(dep)) {
242
+ // don't load stylesheets that are already inlined
243
+ // include them in disabled state so that Vite can detect them and doesn't try to add them
244
+ attributes.push('disabled', 'media="(max-width: 0)"');
245
+ } else {
246
+ const preload_atts = ['rel="preload"', 'as="style"'].concat(attributes);
247
+ link_header_preloads.add(`<${encodeURI(path)}>; ${preload_atts.join(';')}; nopush`);
248
+ }
249
+
250
+ attributes.unshift('rel="stylesheet"');
251
+ head += `\n\t<link href="${path}" ${attributes.join(' ')}>`;
252
+ }
253
+
254
+ if (page_config.csr) {
255
+ for (const dep of modulepreloads) {
256
+ const path = prefixed(dep);
257
+ link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
258
+ if (state.prerendering) {
259
+ head += `\n\t<link rel="modulepreload" href="${path}">`;
260
+ }
261
+ }
262
+
263
+ const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
264
+
265
+ csp.add_script(init_app);
266
+
267
+ if (csp.script_needs_nonce) {
268
+ attributes.push(`nonce="${csp.nonce}"`);
269
+ }
270
+
271
+ body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
272
+ }
273
+
274
+ if (page_config.ssr && page_config.csr) {
275
+ body += `\n\t${fetched
276
+ .map((item) =>
277
+ serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
278
+ )
279
+ .join('\n\t')}`;
280
+ }
281
+
282
+ if (options.service_worker) {
283
+ // always include service worker unless it's turned off explicitly
284
+ csp.add_script(init_service_worker);
285
+
286
+ head += `
287
+ <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
288
+ }
289
+
290
+ if (state.prerendering) {
291
+ // TODO read headers set with setHeaders and convert into http-equiv where possible
292
+ const http_equiv = [];
293
+
294
+ const csp_headers = csp.csp_provider.get_meta();
295
+ if (csp_headers) {
296
+ http_equiv.push(csp_headers);
297
+ }
298
+
299
+ if (state.prerendering.cache) {
300
+ http_equiv.push(`<meta http-equiv="cache-control" content="${state.prerendering.cache}">`);
301
+ }
302
+
303
+ if (http_equiv.length > 0) {
304
+ head = http_equiv.join('\n') + head;
305
+ }
306
+ }
307
+
308
+ // TODO flush chunks as early as we can
309
+ const html =
310
+ (await resolve_opts.transformPageChunk({
311
+ html: options.app_template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }),
312
+ done: true
313
+ })) || '';
314
+
315
+ const headers = new Headers({
316
+ 'x-sveltekit-page': 'true',
317
+ 'content-type': 'text/html',
318
+ etag: `"${hash(html)}"`
319
+ });
320
+
321
+ if (!state.prerendering) {
322
+ const csp_header = csp.csp_provider.get_header();
323
+ if (csp_header) {
324
+ headers.set('content-security-policy', csp_header);
325
+ }
326
+ const report_only_header = csp.report_only_provider.get_header();
327
+ if (report_only_header) {
328
+ headers.set('content-security-policy-report-only', report_only_header);
329
+ }
330
+
331
+ add_cookies_to_headers(headers, cookies);
332
+
333
+ if (link_header_preloads.size) {
334
+ headers.set('link', Array.from(link_header_preloads).join(', '));
335
+ }
336
+ }
337
+
338
+ return new Response(html, {
339
+ status,
340
+ headers
341
+ });
342
+ }
@@ -0,0 +1,104 @@
1
+ import { render_response } from './render.js';
2
+ import { load_data, load_server_data } from './load_data.js';
3
+ import {
4
+ handle_error_and_jsonify,
5
+ GENERIC_ERROR,
6
+ get_option,
7
+ static_error_page,
8
+ redirect_response
9
+ } from '../utils.js';
10
+ import { create_fetch } from './fetch.js';
11
+ import { HttpError, Redirect } from '../../control.js';
12
+
13
+ /**
14
+ * @typedef {import('./types.js').Loaded} Loaded
15
+ * @typedef {import('types').SSROptions} SSROptions
16
+ * @typedef {import('types').SSRState} SSRState
17
+ */
18
+
19
+ /**
20
+ * @param {{
21
+ * event: import('types').RequestEvent;
22
+ * options: SSROptions;
23
+ * state: SSRState;
24
+ * status: number;
25
+ * error: unknown;
26
+ * resolve_opts: import('types').RequiredResolveOptions;
27
+ * }} opts
28
+ */
29
+ export async function respond_with_error({ event, options, state, status, error, resolve_opts }) {
30
+ const { fetcher, fetched, cookies } = create_fetch({
31
+ event,
32
+ options,
33
+ state,
34
+ route: GENERIC_ERROR,
35
+ resolve_opts
36
+ });
37
+
38
+ try {
39
+ const branch = [];
40
+ const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
41
+ const ssr = get_option([default_layout], 'ssr') ?? true;
42
+
43
+ if (ssr) {
44
+ const server_data_promise = load_server_data({
45
+ event,
46
+ state,
47
+ node: default_layout,
48
+ parent: async () => ({})
49
+ });
50
+
51
+ const server_data = await server_data_promise;
52
+
53
+ const data = await load_data({
54
+ event,
55
+ fetcher,
56
+ node: default_layout,
57
+ parent: async () => ({}),
58
+ server_data_promise,
59
+ state
60
+ });
61
+
62
+ branch.push(
63
+ {
64
+ node: default_layout,
65
+ server_data,
66
+ data
67
+ },
68
+ {
69
+ node: await options.manifest._.nodes[1](), // 1 is always the root error
70
+ data: null,
71
+ server_data: null
72
+ }
73
+ );
74
+ }
75
+
76
+ return await render_response({
77
+ options,
78
+ state,
79
+ page_config: {
80
+ ssr,
81
+ csr: get_option([default_layout], 'csr') ?? true
82
+ },
83
+ status,
84
+ error: handle_error_and_jsonify(event, options, error),
85
+ branch,
86
+ fetched,
87
+ cookies,
88
+ event,
89
+ resolve_opts
90
+ });
91
+ } catch (error) {
92
+ // Edge case: If route is a 404 and the user redirects to somewhere from the root layout,
93
+ // we end up here.
94
+ if (error instanceof Redirect) {
95
+ return redirect_response(error.status, error.location, cookies);
96
+ }
97
+
98
+ return static_error_page(
99
+ options,
100
+ error instanceof HttpError ? error.status : 500,
101
+ handle_error_and_jsonify(event, options, error).message
102
+ );
103
+ }
104
+ }
@@ -0,0 +1,87 @@
1
+ import { escape_html_attr } from '../../../utils/escape.js';
2
+ import { hash } from '../../hash.js';
3
+
4
+ /**
5
+ * Inside a script element, only `</script` and `<!--` hold special meaning to the HTML parser.
6
+ *
7
+ * The first closes the script element, so everything after is treated as raw HTML.
8
+ * The second disables further parsing until `-->`, so the script element might be unexpectedly
9
+ * kept open until until an unrelated HTML comment in the page.
10
+ *
11
+ * U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are escaped for the sake of pre-2018
12
+ * browsers.
13
+ *
14
+ * @see tests for unsafe parsing examples.
15
+ * @see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
16
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#cdata-rcdata-restrictions
17
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-state
18
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-state
19
+ * @see https://github.com/tc39/proposal-json-superset
20
+ * @type {Record<string, string>}
21
+ */
22
+ const replacements = {
23
+ '<': '\\u003C',
24
+ '\u2028': '\\u2028',
25
+ '\u2029': '\\u2029'
26
+ };
27
+
28
+ const pattern = new RegExp(`[${Object.keys(replacements).join('')}]`, 'g');
29
+
30
+ /**
31
+ * Generates a raw HTML string containing a safe script element carrying data and associated attributes.
32
+ *
33
+ * It escapes all the special characters needed to guarantee the element is unbroken, but care must
34
+ * be taken to ensure it is inserted in the document at an acceptable position for a script element,
35
+ * and that the resulting string isn't further modified.
36
+ *
37
+ * @param {import('./types.js').Fetched} fetched
38
+ * @param {(name: string, value: string) => boolean} filter
39
+ * @param {boolean} [prerendering]
40
+ * @returns {string} The raw HTML of a script element carrying the JSON payload.
41
+ * @example const html = serialize_data('/data.json', null, { foo: 'bar' });
42
+ */
43
+ export function serialize_data(fetched, filter, prerendering = false) {
44
+ /** @type {Record<string, string>} */
45
+ const headers = {};
46
+
47
+ let cache_control = null;
48
+ let age = null;
49
+
50
+ for (const [key, value] of fetched.response.headers) {
51
+ if (filter(key, value)) {
52
+ headers[key] = value;
53
+ }
54
+
55
+ if (key === 'cache-control') cache_control = value;
56
+ if (key === 'age') age = value;
57
+ }
58
+
59
+ const payload = {
60
+ status: fetched.response.status,
61
+ statusText: fetched.response.statusText,
62
+ headers,
63
+ body: fetched.response_body
64
+ };
65
+
66
+ const safe_payload = JSON.stringify(payload).replace(pattern, (match) => replacements[match]);
67
+
68
+ const attrs = [
69
+ 'type="application/json"',
70
+ 'data-sveltekit-fetched',
71
+ `data-url=${escape_html_attr(fetched.url)}`
72
+ ];
73
+
74
+ if (fetched.request_body) {
75
+ attrs.push(`data-hash=${escape_html_attr(hash(fetched.request_body))}`);
76
+ }
77
+
78
+ if (!prerendering && fetched.method === 'GET' && cache_control) {
79
+ const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control);
80
+ if (match) {
81
+ const ttl = +match[1] - +(age ?? '0');
82
+ attrs.push(`data-ttl="${ttl}"`);
83
+ }
84
+ }
85
+
86
+ return `<script ${attrs.join(' ')}>${safe_payload}</script>`;
87
+ }
@@ -0,0 +1,41 @@
1
+ import { CookieSerializeOptions } from 'cookie';
2
+ import { SSRNode, CspDirectives } from 'types';
3
+
4
+ export interface Fetched {
5
+ url: string;
6
+ method: string;
7
+ request_body?: string | null;
8
+ response_body: string;
9
+ response: Response;
10
+ }
11
+
12
+ export interface FetchState {
13
+ fetched: Fetched[];
14
+ cookies: string[];
15
+ new_cookies: string[];
16
+ }
17
+
18
+ export type Loaded = {
19
+ node: SSRNode;
20
+ data: Record<string, any> | null;
21
+ server_data: any;
22
+ };
23
+
24
+ type CspMode = 'hash' | 'nonce' | 'auto';
25
+
26
+ export interface CspConfig {
27
+ mode: CspMode;
28
+ directives: CspDirectives;
29
+ reportOnly: CspDirectives;
30
+ }
31
+
32
+ export interface CspOpts {
33
+ dev: boolean;
34
+ prerender: boolean;
35
+ }
36
+
37
+ export interface Cookie {
38
+ name: string;
39
+ value: string;
40
+ options: CookieSerializeOptions;
41
+ }