@sveltejs/kit 3.0.0-next.12 → 3.0.0-next.14
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.
- package/package.json +11 -11
- package/src/cli.js +2 -2
- package/src/core/adapt/builder.js +8 -5
- package/src/core/generate_manifest/index.js +6 -0
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +15 -6
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +33 -2
- package/src/core/sync/write_app_types.js +73 -29
- package/src/core/sync/write_tsconfig/index.js +15 -2
- package/src/core/sync/write_types/index.js +5 -2
- package/src/exports/index.js +23 -12
- package/src/exports/internal/shared.js +4 -12
- package/src/exports/node/index.js +2 -7
- package/src/exports/public.d.ts +61 -12
- package/src/exports/vite/build/remote.js +10 -12
- package/src/exports/vite/dev/index.js +32 -19
- package/src/exports/vite/index.js +162 -133
- package/src/exports/vite/utils.js +1 -7
- package/src/runtime/app/server/remote/form.js +9 -1
- package/src/runtime/app/server/remote/prerender.js +13 -9
- package/src/runtime/app/server/remote/query.js +0 -6
- package/src/runtime/app/server/remote/requested.js +4 -4
- package/src/runtime/app/state/client.js +3 -0
- package/src/runtime/app/state/index.js +2 -2
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/client/client.js +731 -342
- package/src/runtime/client/constants.js +2 -6
- package/src/runtime/client/fetcher.js +24 -25
- package/src/runtime/client/remote-functions/form.svelte.js +42 -14
- package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query/index.js +1 -1
- package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
- package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
- package/src/runtime/client/remote-functions/shared.svelte.js +12 -11
- package/src/runtime/client/state.svelte.js +10 -22
- package/src/runtime/client/types.d.ts +1 -2
- package/src/runtime/client/utils.js +22 -14
- package/src/runtime/components/root.svelte +4 -14
- package/src/runtime/form-utils.js +4 -6
- package/src/runtime/pathname.js +37 -0
- package/src/runtime/props.svelte.js +71 -0
- package/src/runtime/server/fetch.js +9 -14
- package/src/runtime/server/index.js +10 -24
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +88 -104
- package/src/runtime/server/page/index.js +1 -2
- package/src/runtime/server/page/load_data.js +16 -33
- package/src/runtime/server/page/render.js +46 -25
- package/src/runtime/server/page/respond_with_error.js +1 -3
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +58 -9
- package/src/runtime/server/respond.js +20 -29
- package/src/runtime/server/state.js +43 -0
- package/src/runtime/shared.js +4 -2
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +2 -2
- package/src/types/ambient.d.ts +59 -8
- package/src/types/global-private.d.ts +1 -1
- package/src/types/internal.d.ts +4 -8
- package/src/types/private.d.ts +3 -12
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +2 -1
- package/src/utils/params.js +1 -1
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +52 -27
- package/src/utils/url.js +1 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +165 -66
- package/types/index.d.ts.map +2 -1
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/types.d.ts +0 -8
package/src/runtime/pathname.js
CHANGED
|
@@ -47,3 +47,40 @@ export function add_resolution_suffix(pathname) {
|
|
|
47
47
|
export function strip_resolution_suffix(pathname) {
|
|
48
48
|
return pathname.slice(0, -ROUTE_SUFFIX.length);
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
const ROUTES_PREFIX = '/routes';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The pathname of the route-ID-keyed resolution module for a given route ID,
|
|
55
|
+
* e.g. `/_app/routes/blog/[slug]/__route.js` (before prefixing with `base`).
|
|
56
|
+
* @param {string} app_dir
|
|
57
|
+
* @param {string} route_id
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
export function route_id_resolution_pathname(app_dir, route_id) {
|
|
61
|
+
return add_resolution_suffix(`/${app_dir}${ROUTES_PREFIX}${route_id === '/' ? '' : route_id}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether a pathname (with the `/__route.js` suffix already stripped, and `base` NOT yet stripped)
|
|
66
|
+
* is a route-ID resolution request rather than a pathname resolution request.
|
|
67
|
+
* @param {string} pathname
|
|
68
|
+
* @param {string} base
|
|
69
|
+
* @param {string} app_dir
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
export function is_route_id_resolution_path(pathname, base, app_dir) {
|
|
73
|
+
const prefix = `${base}/${app_dir}${ROUTES_PREFIX}`;
|
|
74
|
+
return pathname === prefix || pathname.startsWith(prefix + '/');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Extract the route ID from a decoded, base-stripped, suffix-stripped pathname,
|
|
79
|
+
* e.g. `/_app/routes/blog/[slug]` -> `/blog/[slug]`, `/_app/routes` -> `/`.
|
|
80
|
+
* @param {string} pathname
|
|
81
|
+
* @param {string} app_dir
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
export function extract_route_id(pathname, app_dir) {
|
|
85
|
+
return pathname.slice(`/${app_dir}${ROUTES_PREFIX}`.length) || '/';
|
|
86
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** @import { Component } from 'svelte'; */
|
|
2
|
+
/** @import { Page } from '@sveltejs/kit'; */
|
|
3
|
+
|
|
4
|
+
import { noop } from '../utils/functions.js';
|
|
5
|
+
|
|
6
|
+
export class Props {
|
|
7
|
+
/** @type {Page} */
|
|
8
|
+
page;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An array of the `+layout.svelte` and `+page.svelte` component instances
|
|
12
|
+
* that currently live on the page — used for capturing and restoring snapshots.
|
|
13
|
+
* It's updated/manipulated through `bind:this` in `Root.svelte`.
|
|
14
|
+
* @type {Array<Record<string, any>>}
|
|
15
|
+
*/
|
|
16
|
+
components = [];
|
|
17
|
+
|
|
18
|
+
/** @type {any} */
|
|
19
|
+
form;
|
|
20
|
+
|
|
21
|
+
/** @type {App.Error | undefined} */
|
|
22
|
+
error;
|
|
23
|
+
|
|
24
|
+
/** @type {RenderNode} */
|
|
25
|
+
tree;
|
|
26
|
+
|
|
27
|
+
/** @type {(error: unknown, reset: () => void) => void} */
|
|
28
|
+
onerror;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {{
|
|
32
|
+
* page: Page;
|
|
33
|
+
* tree: RenderNode;
|
|
34
|
+
* form: any;
|
|
35
|
+
* error: App.Error | undefined;
|
|
36
|
+
* onerror?: (error: unknown, reset: () => void) => void;
|
|
37
|
+
* }} props
|
|
38
|
+
*/
|
|
39
|
+
constructor({ page, tree, form, error, onerror = noop }) {
|
|
40
|
+
this.page = page;
|
|
41
|
+
this.tree = tree;
|
|
42
|
+
this.onerror = onerror;
|
|
43
|
+
|
|
44
|
+
this.form = $state.raw(form);
|
|
45
|
+
this.error = $state.raw(error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class RenderNode {
|
|
50
|
+
/** @type {Component} */
|
|
51
|
+
component;
|
|
52
|
+
|
|
53
|
+
/** @type {Component | undefined} */
|
|
54
|
+
error;
|
|
55
|
+
|
|
56
|
+
/** @type {Record<string, any>} */
|
|
57
|
+
data = $state.raw({});
|
|
58
|
+
|
|
59
|
+
/** @type {RenderNode | undefined} */
|
|
60
|
+
child = $state.raw();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {Component} component
|
|
65
|
+
* @param {Component} error
|
|
66
|
+
*/
|
|
67
|
+
constructor(component, error) {
|
|
68
|
+
this.component = component;
|
|
69
|
+
this.error = error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -4,6 +4,7 @@ import { respond } from './respond.js';
|
|
|
4
4
|
import * as paths from '$app/paths/internal/server';
|
|
5
5
|
import { read_implementation } from './internal.js';
|
|
6
6
|
import { has_prerendered_path } from './utils.js';
|
|
7
|
+
import { fork_state_for_subrequest } from './state.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @param {{
|
|
@@ -117,7 +118,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
117
118
|
return await fetch(request);
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
if (has_prerendered_path(manifest,
|
|
121
|
+
if (has_prerendered_path(manifest, decoded)) {
|
|
121
122
|
// The path of something prerendered could match a different route
|
|
122
123
|
// that is still in the manifest, leading to the wrong route being loaded.
|
|
123
124
|
// We therefore bail early here. The prerendered logic is different for
|
|
@@ -142,11 +143,9 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
142
143
|
request.headers.set('accept', '*/*');
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
/** @type {string} */ (event.request.headers.get('accept-language'))
|
|
149
|
-
);
|
|
146
|
+
const accept_language = event.request.headers.get('accept-language');
|
|
147
|
+
if (accept_language && !request.headers.has('accept-language')) {
|
|
148
|
+
request.headers.set('accept-language', accept_language);
|
|
150
149
|
}
|
|
151
150
|
|
|
152
151
|
const response = await internal_fetch(request, options, manifest, state);
|
|
@@ -200,6 +199,8 @@ function normalize_fetch_input(info, init, url) {
|
|
|
200
199
|
* @returns {Promise<Response>}
|
|
201
200
|
*/
|
|
202
201
|
async function internal_fetch(request, options, manifest, state) {
|
|
202
|
+
const subrequest_state = fork_state_for_subrequest(state);
|
|
203
|
+
|
|
203
204
|
if (request.signal) {
|
|
204
205
|
if (request.signal.aborted) {
|
|
205
206
|
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
@@ -216,18 +217,12 @@ async function internal_fetch(request, options, manifest, state) {
|
|
|
216
217
|
});
|
|
217
218
|
|
|
218
219
|
const result = await Promise.race([
|
|
219
|
-
respond(request, options, manifest,
|
|
220
|
-
...state,
|
|
221
|
-
depth: state.depth + 1
|
|
222
|
-
}),
|
|
220
|
+
respond(request, options, manifest, subrequest_state),
|
|
223
221
|
abort_promise
|
|
224
222
|
]);
|
|
225
223
|
remove_abort_listener();
|
|
226
224
|
return result;
|
|
227
225
|
} else {
|
|
228
|
-
return await respond(request, options, manifest,
|
|
229
|
-
...state,
|
|
230
|
-
depth: state.depth + 1
|
|
231
|
-
});
|
|
226
|
+
return await respond(request, options, manifest, subrequest_state);
|
|
232
227
|
}
|
|
233
228
|
}
|
|
@@ -67,31 +67,17 @@ export class Server {
|
|
|
67
67
|
const result = read(file);
|
|
68
68
|
if (result instanceof ReadableStream) {
|
|
69
69
|
return result;
|
|
70
|
-
} else {
|
|
71
|
-
return new ReadableStream({
|
|
72
|
-
async start(controller) {
|
|
73
|
-
try {
|
|
74
|
-
const stream = await Promise.resolve(result);
|
|
75
|
-
if (!stream) {
|
|
76
|
-
controller.close();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const reader = stream.getReader();
|
|
81
|
-
|
|
82
|
-
while (true) {
|
|
83
|
-
const { done, value } = await reader.read();
|
|
84
|
-
if (done) break;
|
|
85
|
-
controller.enqueue(value);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
controller.close();
|
|
89
|
-
} catch (error) {
|
|
90
|
-
controller.error(error);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
70
|
}
|
|
71
|
+
|
|
72
|
+
// TODO remove the cast once TypeScript's lib includes `ReadableStream.from`
|
|
73
|
+
return /** @type {ReadableStream} */ (
|
|
74
|
+
/** @type {any} */ (ReadableStream).from(
|
|
75
|
+
(async function* () {
|
|
76
|
+
const stream = await result;
|
|
77
|
+
if (stream) yield* stream;
|
|
78
|
+
})()
|
|
79
|
+
)
|
|
80
|
+
);
|
|
95
81
|
};
|
|
96
82
|
|
|
97
83
|
set_read_implementation(wrapped_read);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { text_encoder } from '../../utils.js';
|
|
1
|
+
import { base64_encode, text_encoder } from '../../utils.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SHA-256 hashing function adapted from https://bitwiseshiftleft.github.io/sjcl
|
|
@@ -102,7 +102,7 @@ export function sha256(data) {
|
|
|
102
102
|
const bytes = new Uint8Array(out.buffer);
|
|
103
103
|
reverse_endianness(bytes);
|
|
104
104
|
|
|
105
|
-
return
|
|
105
|
+
return base64_encode(bytes);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/** The SHA-256 initialization vector */
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { escape_html } from '../../../utils/escape.js';
|
|
2
|
+
import { base64_encode } from '../../utils.js';
|
|
2
3
|
import { sha256 } from './crypto.js';
|
|
3
4
|
|
|
4
5
|
const array = new Uint8Array(16);
|
|
5
6
|
|
|
6
7
|
function generate_nonce() {
|
|
7
8
|
crypto.getRandomValues(array);
|
|
8
|
-
return
|
|
9
|
+
return base64_encode(array);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const quoted = new Set([
|
|
@@ -53,19 +54,19 @@ class BaseProvider {
|
|
|
53
54
|
#directives;
|
|
54
55
|
|
|
55
56
|
/** @type {Set<import('types').Csp.Source>} */
|
|
56
|
-
#script_src;
|
|
57
|
+
#script_src = new Set();
|
|
57
58
|
|
|
58
59
|
/** @type {Set<import('types').Csp.Source>} */
|
|
59
|
-
#script_src_elem;
|
|
60
|
+
#script_src_elem = new Set();
|
|
60
61
|
|
|
61
62
|
/** @type {Set<import('types').Csp.Source>} */
|
|
62
|
-
#style_src;
|
|
63
|
+
#style_src = new Set();
|
|
63
64
|
|
|
64
65
|
/** @type {Set<import('types').Csp.Source>} */
|
|
65
|
-
#style_src_attr;
|
|
66
|
+
#style_src_attr = new Set();
|
|
66
67
|
|
|
67
68
|
/** @type {Set<import('types').Csp.Source>} */
|
|
68
|
-
#style_src_elem;
|
|
69
|
+
#style_src_elem = new Set();
|
|
69
70
|
|
|
70
71
|
/** @type {boolean} */
|
|
71
72
|
script_needs_nonce;
|
|
@@ -90,12 +91,6 @@ class BaseProvider {
|
|
|
90
91
|
|
|
91
92
|
const d = this.#directives;
|
|
92
93
|
|
|
93
|
-
this.#script_src = new Set();
|
|
94
|
-
this.#script_src_elem = new Set();
|
|
95
|
-
this.#style_src = new Set();
|
|
96
|
-
this.#style_src_attr = new Set();
|
|
97
|
-
this.#style_src_elem = new Set();
|
|
98
|
-
|
|
99
94
|
const effective_script_src = d['script-src'] || d['default-src'];
|
|
100
95
|
const script_src_elem = d['script-src-elem'];
|
|
101
96
|
const effective_style_src = d['style-src'] || d['default-src'];
|
|
@@ -117,32 +112,25 @@ class BaseProvider {
|
|
|
117
112
|
|
|
118
113
|
// ...and add unsafe-inline so we can inject <style> elements
|
|
119
114
|
// 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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
d['style-src-elem'] = [
|
|
140
|
-
...style_src_elem.filter(
|
|
141
|
-
(value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
|
|
142
|
-
),
|
|
143
|
-
'unsafe-inline'
|
|
144
|
-
];
|
|
145
|
-
}
|
|
115
|
+
/**
|
|
116
|
+
* @template {'style-src' | 'style-src-attr' | 'style-src-elem'} K
|
|
117
|
+
* @param {K} key
|
|
118
|
+
* @param {import('types').CspDirectives[K]} directive
|
|
119
|
+
*/
|
|
120
|
+
const add_unsafe_inline = (key, directive) => {
|
|
121
|
+
if (directive && !directive.includes('unsafe-inline')) {
|
|
122
|
+
d[key] = /** @type {import('types').CspDirectives[K]} */ ([
|
|
123
|
+
...directive.filter(
|
|
124
|
+
(value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
|
|
125
|
+
),
|
|
126
|
+
'unsafe-inline'
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
add_unsafe_inline('style-src', effective_style_src);
|
|
132
|
+
add_unsafe_inline('style-src-attr', style_src_attr);
|
|
133
|
+
add_unsafe_inline('style-src-elem', style_src_elem);
|
|
146
134
|
}
|
|
147
135
|
|
|
148
136
|
/** @param {(import('types').Csp.Source | import('types').Csp.ActionSource)[] | undefined} directive */
|
|
@@ -175,13 +163,16 @@ class BaseProvider {
|
|
|
175
163
|
this.#nonce = nonce;
|
|
176
164
|
}
|
|
177
165
|
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
166
|
+
/**
|
|
167
|
+
* @param {string} content
|
|
168
|
+
* @returns {`nonce-${string}` | `sha256-${string}`}
|
|
169
|
+
*/
|
|
170
|
+
#get_source(content) {
|
|
171
|
+
return this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
|
|
172
|
+
}
|
|
184
173
|
|
|
174
|
+
/** @param {`nonce-${string}` | `sha256-${string}`} source */
|
|
175
|
+
#add_script_source(source) {
|
|
185
176
|
if (this.#script_src_needs_csp) {
|
|
186
177
|
this.#script_src.add(source);
|
|
187
178
|
}
|
|
@@ -191,15 +182,17 @@ class BaseProvider {
|
|
|
191
182
|
}
|
|
192
183
|
}
|
|
193
184
|
|
|
185
|
+
/** @param {string} content */
|
|
186
|
+
add_script(content) {
|
|
187
|
+
if (!this.#script_needs_csp) return;
|
|
188
|
+
|
|
189
|
+
this.#add_script_source(this.#get_source(content));
|
|
190
|
+
}
|
|
191
|
+
|
|
194
192
|
/** @param {`sha256-${string}`[]} hashes */
|
|
195
193
|
add_script_hashes(hashes) {
|
|
196
194
|
for (const hash of hashes) {
|
|
197
|
-
|
|
198
|
-
this.#script_src.add(hash);
|
|
199
|
-
}
|
|
200
|
-
if (this.#script_src_elem_needs_csp) {
|
|
201
|
-
this.#script_src_elem.add(hash);
|
|
202
|
-
}
|
|
195
|
+
this.#add_script_source(hash);
|
|
203
196
|
}
|
|
204
197
|
}
|
|
205
198
|
|
|
@@ -207,8 +200,7 @@ class BaseProvider {
|
|
|
207
200
|
add_style(content) {
|
|
208
201
|
if (!this.#style_needs_csp) return;
|
|
209
202
|
|
|
210
|
-
|
|
211
|
-
const source = this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
|
|
203
|
+
const source = this.#get_source(content);
|
|
212
204
|
|
|
213
205
|
if (this.#style_src_needs_csp) {
|
|
214
206
|
this.#style_src.add(source);
|
|
@@ -251,40 +243,34 @@ class BaseProvider {
|
|
|
251
243
|
|
|
252
244
|
const directives = { ...this.#directives };
|
|
253
245
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
]
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
]
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (this.#script_src_elem.size > 0) {
|
|
283
|
-
directives['script-src-elem'] = [
|
|
284
|
-
...(directives['script-src-elem'] || []),
|
|
285
|
-
...this.#script_src_elem
|
|
286
|
-
];
|
|
287
|
-
}
|
|
246
|
+
/**
|
|
247
|
+
* @template {'style-src' | 'style-src-attr' | 'style-src-elem' | 'script-src' | 'script-src-elem'} K
|
|
248
|
+
* @param {K} key
|
|
249
|
+
* @param {Set<import('types').Csp.Source>} sources
|
|
250
|
+
* @param {import('types').CspDirectives[K]} [base]
|
|
251
|
+
*/
|
|
252
|
+
const merge_sources = (key, sources, base) => {
|
|
253
|
+
if (sources.size > 0) {
|
|
254
|
+
directives[key] = /** @type {import('types').CspDirectives[K]} */ ([
|
|
255
|
+
...(base || []),
|
|
256
|
+
...sources
|
|
257
|
+
]);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
merge_sources(
|
|
262
|
+
'style-src',
|
|
263
|
+
this.#style_src,
|
|
264
|
+
directives['style-src'] || directives['default-src']
|
|
265
|
+
);
|
|
266
|
+
merge_sources('style-src-attr', this.#style_src_attr, directives['style-src-attr']);
|
|
267
|
+
merge_sources('style-src-elem', this.#style_src_elem, directives['style-src-elem']);
|
|
268
|
+
merge_sources(
|
|
269
|
+
'script-src',
|
|
270
|
+
this.#script_src,
|
|
271
|
+
directives['script-src'] || directives['default-src']
|
|
272
|
+
);
|
|
273
|
+
merge_sources('script-src-elem', this.#script_src_elem, directives['script-src-elem']);
|
|
288
274
|
|
|
289
275
|
for (const key in directives) {
|
|
290
276
|
if (is_meta && (key === 'frame-ancestors' || key === 'report-uri' || key === 'sandbox')) {
|
|
@@ -300,13 +286,11 @@ class BaseProvider {
|
|
|
300
286
|
|
|
301
287
|
const directive = [key];
|
|
302
288
|
if (Array.isArray(value)) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
});
|
|
289
|
+
for (const source of value) {
|
|
290
|
+
directive.push(
|
|
291
|
+
quoted.has(source) || crypto_pattern.test(source) ? `'${source}'` : source
|
|
292
|
+
);
|
|
293
|
+
}
|
|
310
294
|
}
|
|
311
295
|
|
|
312
296
|
header.push(directive.join(' '));
|
|
@@ -337,17 +321,17 @@ class CspReportOnlyProvider extends BaseProvider {
|
|
|
337
321
|
constructor(use_hashes, directives, nonce) {
|
|
338
322
|
super(use_hashes, directives, nonce);
|
|
339
323
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
324
|
+
// If we're generating content-security-policy-report-only,
|
|
325
|
+
// if there are any directives, we need a report-uri or report-to (or both)
|
|
326
|
+
// else it's just an expensive noop.
|
|
327
|
+
if (
|
|
328
|
+
Object.values(directives).some((v) => !!v) &&
|
|
329
|
+
!directives['report-to']?.length &&
|
|
330
|
+
!directives['report-uri']?.length
|
|
331
|
+
) {
|
|
332
|
+
throw Error(
|
|
333
|
+
'`content-security-policy-report-only` must be specified with either the `report-to` or `report-uri` directives, or both'
|
|
334
|
+
);
|
|
351
335
|
}
|
|
352
336
|
}
|
|
353
337
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @import { ActionResult, RequestEvent, SSRManifest } from '@sveltejs/kit' */
|
|
3
3
|
/** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode, SSRNode, SSROptions, SSRState } from 'types' */
|
|
4
4
|
import { text } from '@sveltejs/kit';
|
|
5
|
-
import {
|
|
5
|
+
import { Redirect } from '@sveltejs/kit/internal';
|
|
6
6
|
import { compact } from '../../../utils/array.js';
|
|
7
7
|
import { get_status, normalize_error } from '../../../utils/error.js';
|
|
8
8
|
import { noop } from '../../../utils/functions.js';
|
|
@@ -386,7 +386,6 @@ export async function render_page(
|
|
|
386
386
|
options,
|
|
387
387
|
manifest,
|
|
388
388
|
state,
|
|
389
|
-
status: e instanceof HttpError ? e.status : 500,
|
|
390
389
|
error: e,
|
|
391
390
|
resolve_opts
|
|
392
391
|
});
|
|
@@ -334,14 +334,24 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
|
|
|
334
334
|
);
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
const request_body =
|
|
338
|
+
input instanceof Request && cloned_body
|
|
339
|
+
? await new Response(cloned_body).text()
|
|
340
|
+
: init?.body;
|
|
341
|
+
|
|
342
|
+
if (
|
|
343
|
+
request_body &&
|
|
344
|
+
typeof request_body !== 'string' &&
|
|
345
|
+
!ArrayBuffer.isView(request_body)
|
|
346
|
+
) {
|
|
347
|
+
// requests whose body can't be hashed aren't serialized — the browser repeats the fetch
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
337
351
|
fetched.push({
|
|
338
352
|
url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
|
|
339
353
|
method: event.request.method,
|
|
340
|
-
request_body: /** @type {string | ArrayBufferView | undefined} */ (
|
|
341
|
-
input instanceof Request && cloned_body
|
|
342
|
-
? await stream_to_string(cloned_body)
|
|
343
|
-
: init?.body
|
|
344
|
-
),
|
|
354
|
+
request_body: /** @type {string | ArrayBufferView | null | undefined} */ (request_body),
|
|
345
355
|
request_headers: cloned_headers,
|
|
346
356
|
response_body: body,
|
|
347
357
|
response,
|
|
@@ -361,16 +371,7 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
|
|
|
361
371
|
const [a, b] = response.body.tee();
|
|
362
372
|
|
|
363
373
|
void (async () => {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
for await (const chunk of a) {
|
|
367
|
-
const combined = new Uint8Array(result.length + chunk.length);
|
|
368
|
-
|
|
369
|
-
combined.set(result, 0);
|
|
370
|
-
combined.set(chunk, result.length);
|
|
371
|
-
|
|
372
|
-
result = combined;
|
|
373
|
-
}
|
|
374
|
+
const result = new Uint8Array(await new Response(a).arrayBuffer());
|
|
374
375
|
|
|
375
376
|
if (dependency) {
|
|
376
377
|
dependency.body = new Uint8Array(result);
|
|
@@ -486,21 +487,3 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
|
|
|
486
487
|
return response;
|
|
487
488
|
};
|
|
488
489
|
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* @param {ReadableStream<Uint8Array>} stream
|
|
492
|
-
*/
|
|
493
|
-
async function stream_to_string(stream) {
|
|
494
|
-
let result = '';
|
|
495
|
-
const reader = stream.getReader();
|
|
496
|
-
const decoder = new TextDecoder();
|
|
497
|
-
while (true) {
|
|
498
|
-
const { done, value } = await reader.read();
|
|
499
|
-
if (done) {
|
|
500
|
-
result += decoder.decode();
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
503
|
-
result += decoder.decode(value, { stream: true });
|
|
504
|
-
}
|
|
505
|
-
return result;
|
|
506
|
-
}
|