Repository: sveltejs/kit
Pull request #6958 (2022-09-22 06:50:19)

CSP add wasm-unsafe-eval
Add `wasm-unsafe-eval` keyword to `BaseSource` to conform to CSP directive [source list](https://www.w3.org/TR/CSP/#framework-directive-source-list)

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [ ] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: c658e343f0abe29997c1dd63cbb2f210c04651c2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/fcrozatier/kit/new/add-wasm-unsafe-eval?filename=.changeset/nervous-icons-fold.md&value=---%0A%22%40sveltejs%2Fkit%22%3A%20patch%0A---%0A%0ACSP%20add%20wasm-unsafe-eval%0A)



Patch:
diff --git a/.changeset/cuddly-dragons-punch.md b/.changeset/cuddly-dragons-punch.md
new file mode 100644
index 000000000..1491e81fb
--- /dev/null
+++ b/.changeset/cuddly-dragons-punch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Fix fetch type
diff --git a/.changeset/mean-chefs-swim.md b/.changeset/mean-chefs-swim.md
new file mode 100644
index 000000000..f37560970
--- /dev/null
+++ b/.changeset/mean-chefs-swim.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Add csp wasm-unsafe-eval keyword
diff --git a/.changeset/orange-cups-itch.md b/.changeset/orange-cups-itch.md
new file mode 100644
index 000000000..7a0b57312
--- /dev/null
+++ b/.changeset/orange-cups-itch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Register service worker with relative URL
diff --git a/packages/kit/src/exports/vite/build/build_server.js b/packages/kit/src/exports/vite/build/build_server.js
index 9b116d423..0f1120e5f 100644
--- a/packages/kit/src/exports/vite/build/build_server.js
+++ b/packages/kit/src/exports/vite/build/build_server.js
@@ -82,7 +82,7 @@ export class Server {
 			public_env: {},
 			read,
 			root,
-			service_worker: ${has_service_worker ? "base + '/service-worker.js'" : 'null'},
+			service_worker: ${has_service_worker},
 			app_template,
 			app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
 			error_template,
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index 36e2fa0f2..3c971ce19 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -460,6 +460,7 @@ export async function dev(vite, vite_config, svelte_config) {
 								.replace(/%sveltekit\.status%/g, String(status))
 								.replace(/%sveltekit\.error\.message%/g, message);
 						},
+						service_worker: false,
 						trailing_slash: svelte_config.kit.trailingSlash
 					},
 					{
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 195eab6ca..071423a6f 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -537,9 +537,7 @@ export function create_client({ target, base, trailing_slash }) {
 				async fetch(resource, init) {
 					let requested;
 
-					if (typeof resource === 'string') {
-						requested = resource;
-					} else {
+					if (resource instanceof Request) {
 						requested = resource.url;
 
 						// we're not allowed to modify the received `Request` object, so in order
@@ -564,6 +562,8 @@ export function create_client({ target, base, trailing_slash }) {
 							signal: resource.signal,
 							...init
 						};
+					} else {
+						requested = resource;
 					}
 
 					// we must fixup relative urls so they are resolved from the target page
diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js
index 1552656fa..e355ee2db 100644
--- a/packages/kit/src/runtime/client/fetcher.js
+++ b/packages/kit/src/runtime/client/fetcher.js
@@ -62,12 +62,12 @@ const cache = new Map();
 /**
  * Should be called on the initial run of load functions that hydrate the page.
  * Saves any requests with cache-control max-age to the cache.
- * @param {RequestInfo} resource
+ * @param {RequestInfo | URL} resource
  * @param {string} resolved
  * @param {RequestInit} [opts]
  */
 export function initial_fetch(resource, resolved, opts) {
-	const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
+	const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
 
 	let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
 
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 16a060597..282369140 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -1,5 +1,5 @@
 import { escape_html_attr } from '../../../utils/escape.js';
-import { sha256, base64 } from './crypto.js';
+import { base64, sha256 } from './crypto.js';
 
 const array = new Uint8Array(16);
 
@@ -15,7 +15,8 @@ const quoted = new Set([
 	'unsafe-inline',
 	'none',
 	'strict-dynamic',
-	'report-sample'
+	'report-sample',
+	'wasm-unsafe-eval'
 ]);
 
 const crypto_pattern = /^(nonce|sha\d\d\d)-/;
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 308d02e0c..2bb13b92d 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -187,37 +187,6 @@ export async function render_response({
 		serialized.form = devalue(form_value);
 	}
 
-	// prettier-ignore
-	const init_app = `
-		import { start } from ${s(prefixed(entry.file))};
-
-		start({
-			env: ${s(options.public_env)},
-			hydrate: ${page_config.ssr ? `{
-				status: ${status},
-				error: ${s(error)},
-				node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
-				params: ${devalue(event.params)},
-				routeId: ${s(event.routeId)},
-				data: ${serialized.data},
-				form: ${serialized.form}
-			}` : 'null'},
-			paths: ${s(options.paths)},
-			target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
-			trailing_slash: ${s(options.trailing_slash)}
-		});
-	`;
-
-	// we use an anonymous function instead of an arrow function to support
-	// older browsers (https://github.com/sveltejs/kit/pull/5417)
-	const init_service_worker = `
-		if ('serviceWorker' in navigator) {
-			addEventListener('load', function () {
-				navigator.serviceWorker.register('${options.service_worker}');
-			});
-		}
-	`;
-
 	if (inline_styles.size > 0) {
 		const content = Array.from(inline_styles.values()).join('\n');
 
@@ -248,15 +217,36 @@ export async function render_response({
 		}
 
 		attributes.unshift('rel="stylesheet"');
-		head += `\n\t<link href="${path}" ${attributes.join(' ')}>`;
+		head += `\n\t\t<link href="${path}" ${attributes.join(' ')}>`;
 	}
 
 	if (page_config.csr) {
+		// prettier-ignore
+		const init_app = `
+			import { start } from ${s(prefixed(entry.file))};
+
+			start({
+				env: ${s(options.public_env)},
+				hydrate: ${page_config.ssr ? `{
+					status: ${status},
+					error: ${s(error)},
+					node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
+					params: ${devalue(event.params)},
+					routeId: ${s(event.routeId)},
+					data: ${serialized.data},
+					form: ${serialized.form}
+				}` : 'null'},
+				paths: ${s(options.paths)},
+				target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
+				trailing_slash: ${s(options.trailing_slash)}
+			});
+		`;
+
 		for (const dep of modulepreloads) {
 			const path = prefixed(dep);
 			link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
 			if (state.prerendering) {
-				head += `\n\t<link rel="modulepreload" href="${path}">`;
+				head += `\n\t\t<link rel="modulepreload" href="${path}">`;
 			}
 		}
 
@@ -280,11 +270,21 @@ export async function render_response({
 	}
 
 	if (options.service_worker) {
+		// we use an anonymous function instead of an arrow function to support
+		// older browsers (https://github.com/sveltejs/kit/pull/5417)
+		const init_service_worker = `
+			if ('serviceWorker' in navigator) {
+				addEventListener('load', function () {
+					navigator.serviceWorker.register('${prefixed('service-worker.js')}');
+				});
+			}
+		`;
+
 		// always include service worker unless it's turned off explicitly
 		csp.add_script(init_service_worker);
 
 		head += `
-			<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
+		<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
 	}
 
 	if (state.prerendering) {
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index 247f3b710..c48dba8ad 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -265,7 +265,7 @@ export interface LoadEvent<
 	Data extends Record<string, unknown> | null = Record<string, any> | null,
 	ParentData extends Record<string, unknown> = Record<string, any>
 > extends NavigationEvent<Params> {
-	fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
+	fetch: typeof fetch;
 	data: Data;
 	setHeaders: (headers: Record<string, string>) => void;
 	parent: () => Promise<ParentData>;
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index aebf0be71..29f84240d 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -298,7 +298,7 @@ export interface SSROptions {
 	public_env: Record<string, string>;
 	read(file: string): Buffer;
 	root: SSRComponent['default'];
-	service_worker?: string;
+	service_worker: boolean;
 	app_template({
 		head,
 		body,
diff --git a/packages/kit/types/private.d.ts b/packages/kit/types/private.d.ts
index f5101b88b..36ed7cfbe 100644
--- a/packages/kit/types/private.d.ts
+++ b/packages/kit/types/private.d.ts
@@ -53,7 +53,13 @@ export interface AdapterEntry {
 
 export namespace Csp {
 	type ActionSource = 'strict-dynamic' | 'report-sample';
-	type BaseSource = 'self' | 'unsafe-eval' | 'unsafe-hashes' | 'unsafe-inline' | 'none';
+	type BaseSource =
+		| 'self'
+		| 'unsafe-eval'
+		| 'unsafe-hashes'
+		| 'unsafe-inline'
+		| 'wasm-unsafe-eval'
+		| 'none';
 	type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`;
 	type FrameSource = HostSource | SchemeSource | 'self' | 'none';
 	type HostNameScheme = `${string}.${string}` | 'localhost';


Test patch:
diff --git a/packages/kit/test/prerendering/basics/src/service-worker.js b/packages/kit/test/prerendering/basics/src/service-worker.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js
index 44d80bfff..a7135660a 100644
--- a/packages/kit/test/prerendering/basics/test/test.js
+++ b/packages/kit/test/prerendering/basics/test/test.js
@@ -225,4 +225,9 @@ test('prerenders a page in a (group)', () => {
 	assert.ok(content.includes('<h1>grouped</h1>'));
 });
 
+test('injects relative service worker', () => {
+	const content = read('index.html');
+	assert.ok(content.includes(`navigator.serviceWorker.register('./service-worker.js')`));
+});
+
 test.run();