constructor(use_hashes, directives, nonce) {
		this.#use_hashes = use_hashes;
		this.#directives = __SVELTEKIT_DEV__ ? { ...directives } : directives; // clone in dev so we can safely mutate

		const d = this.#directives;

		this.#script_src = [];
		this.#script_src_elem = [];
		this.#style_src = [];
		this.#style_src_attr = [];
		this.#style_src_elem = [];

		const effective_script_src = d['script-src'] || d['default-src'];
		const script_src_elem = d['script-src-elem'];
		const effective_style_src = d['style-src'] || d['default-src'];
		const style_src_attr = d['style-src-attr'];
		const style_src_elem = d['style-src-elem'];

		if (__SVELTEKIT_DEV__) {
			// remove strict-dynamic in dev...
			// TODO reinstate this if we can figure out how to make strict-dynamic work
			// if (d['default-src']) {
			// 	d['default-src'] = d['default-src'].filter((name) => name !== 'strict-dynamic');
			// 	if (d['default-src'].length === 0) delete d['default-src'];
			// }

			// if (d['script-src']) {
			// 	d['script-src'] = d['script-src'].filter((name) => name !== 'strict-dynamic');
			// 	if (d['script-src'].length === 0) delete d['script-src'];
			// }

			// ...and add unsafe-inline so we can inject <style> elements
			// Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list, so we remove those during dev when injecting unsafe-inline
			if (effective_style_src && !effective_style_src.includes('unsafe-inline')) {
				d['style-src'] = [
					...effective_style_src.filter(
						(value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
					),
					'unsafe-inline'
				];
			}

			if (style_src_attr && !style_src_attr.includes('unsafe-inline')) {
				d['style-src-attr'] = [
					...style_src_attr.filter(
						(value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
					),
					'unsafe-inline'
				];
			}

			if (style_src_elem && !style_src_elem.includes('unsafe-inline')) {
				d['style-src-elem'] = [
					...style_src_elem.filter(
						(value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
					),
					'unsafe-inline'
				];
			}
		}

		/** @param {(import('types').Csp.Source | import('types').Csp.ActionSource)[] | undefined} directive */
		const needs_csp = (directive) =>
			!!directive && !directive.some((value) => value === 'unsafe-inline');

		this.#script_src_needs_csp = needs_csp(effective_script_src);
		this.#script_src_elem_needs_csp = needs_csp(script_src_elem);
		this.#style_src_needs_csp = needs_csp(effective_style_src);
		this.#style_src_attr_needs_csp = needs_csp(style_src_attr);
		this.#style_src_elem_needs_csp = needs_csp(style_src_elem);

		this.#script_needs_csp = this.#script_src_needs_csp || this.#script_src_elem_needs_csp;
		this.#style_needs_csp =
			!__SVELTEKIT_DEV__ &&
			(this.#style_src_needs_csp ||
				this.#style_src_attr_needs_csp ||
				this.#style_src_elem_needs_csp);

		this.script_needs_nonce = this.#script_needs_csp && !this.#use_hashes;
		this.style_needs_nonce = this.#style_needs_csp && !this.#use_hashes;

		this.#nonce = nonce;
	}
@param {boolean} use_hashes
@param {import('types').CspDirectives} directives
@param {string} nonce