@sveltejs/kit 1.1.4 → 1.2.1
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 +3 -2
- package/src/core/config/index.js +24 -15
- package/src/core/sync/write_server.js +6 -2
- package/src/exports/index.js +25 -1
- package/src/exports/vite/dev/index.js +2 -4
- package/src/runtime/env-public.js +1 -0
- package/src/runtime/server/cookie.js +11 -0
- package/src/runtime/server/data/index.js +2 -1
- package/src/runtime/server/page/index.js +4 -3
- package/src/runtime/server/page/render.js +4 -2
- package/src/runtime/server/respond.js +6 -6
- package/src/runtime/server/utils.js +5 -5
- package/types/index.d.ts +8 -1
- package/types/internal.d.ts +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"set-cookie-parser": "^2.5.1",
|
|
23
23
|
"sirv": "^2.0.2",
|
|
24
24
|
"tiny-glob": "^0.2.9",
|
|
25
|
-
"undici": "5.15.
|
|
25
|
+
"undici": "5.15.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@playwright/test": "^1.29.2",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"format": "pnpm lint --write",
|
|
87
87
|
"test": "pnpm test:unit && pnpm test:integration",
|
|
88
88
|
"test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
|
|
89
|
+
"test:cross-browser": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-browser",
|
|
89
90
|
"test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
|
|
90
91
|
"postinstall": "node postinstall.js"
|
|
91
92
|
}
|
package/src/core/config/index.js
CHANGED
|
@@ -9,24 +9,33 @@ import options from './options.js';
|
|
|
9
9
|
* @param {string} cwd
|
|
10
10
|
* @param {import('types').ValidatedConfig} config
|
|
11
11
|
*/
|
|
12
|
-
export function load_template(cwd,
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const expected_tags = ['%sveltekit.head%', '%sveltekit.body%'];
|
|
20
|
-
expected_tags.forEach((tag) => {
|
|
21
|
-
if (contents.indexOf(tag) === -1) {
|
|
22
|
-
throw new Error(`${relative} is missing ${tag}`);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
} else {
|
|
12
|
+
export function load_template(cwd, { kit }) {
|
|
13
|
+
const { env, files } = kit;
|
|
14
|
+
|
|
15
|
+
const relative = path.relative(cwd, files.appTemplate);
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(files.appTemplate)) {
|
|
26
18
|
throw new Error(`${relative} does not exist`);
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
const contents = fs.readFileSync(files.appTemplate, 'utf8');
|
|
22
|
+
|
|
23
|
+
const expected_tags = ['%sveltekit.head%', '%sveltekit.body%'];
|
|
24
|
+
expected_tags.forEach((tag) => {
|
|
25
|
+
if (contents.indexOf(tag) === -1) {
|
|
26
|
+
throw new Error(`${relative} is missing ${tag}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
for (const match of contents.matchAll(/%sveltekit\.env\.([^%]+)%/g)) {
|
|
31
|
+
if (!match[1].startsWith(env.publicPrefix)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Environment variables in ${relative} must start with ${env.publicPrefix} (saw %sveltekit.env.${match[1]}%)`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return contents;
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
/**
|
|
@@ -38,11 +38,15 @@ export const options = {
|
|
|
38
38
|
root,
|
|
39
39
|
service_worker: ${has_service_worker},
|
|
40
40
|
templates: {
|
|
41
|
-
app: ({ head, body, assets, nonce }) => ${s(template)
|
|
41
|
+
app: ({ head, body, assets, nonce, env }) => ${s(template)
|
|
42
42
|
.replace('%sveltekit.head%', '" + head + "')
|
|
43
43
|
.replace('%sveltekit.body%', '" + body + "')
|
|
44
44
|
.replace(/%sveltekit\.assets%/g, '" + assets + "')
|
|
45
|
-
.replace(/%sveltekit\.nonce%/g, '" + nonce + "')
|
|
45
|
+
.replace(/%sveltekit\.nonce%/g, '" + nonce + "')
|
|
46
|
+
.replace(
|
|
47
|
+
/%sveltekit\.env\.([^%]+)%/g,
|
|
48
|
+
(_match, capture) => `" + (env[${s(capture)}] ?? "") + "`
|
|
49
|
+
)},
|
|
46
50
|
error: ({ status, message }) => ${s(error_page)
|
|
47
51
|
.replace(/%sveltekit\.status%/g, '" + status + "')
|
|
48
52
|
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
|
package/src/exports/index.js
CHANGED
|
@@ -29,12 +29,36 @@ export function redirect(status, location) {
|
|
|
29
29
|
export function json(data, init) {
|
|
30
30
|
// TODO deprecate this in favour of `Response.json` when it's
|
|
31
31
|
// more widely supported
|
|
32
|
+
const body = JSON.stringify(data);
|
|
33
|
+
|
|
34
|
+
// we can't just do `text(JSON.stringify(data), init)` because
|
|
35
|
+
// it will set a default `content-type` header. duplicated code
|
|
36
|
+
// means less duplicated work
|
|
32
37
|
const headers = new Headers(init?.headers);
|
|
38
|
+
if (!headers.has('content-length')) {
|
|
39
|
+
headers.set('content-length', encoder.encode(body).byteLength.toString());
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
if (!headers.has('content-type')) {
|
|
34
43
|
headers.set('content-type', 'application/json');
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
return new Response(
|
|
46
|
+
return new Response(body, {
|
|
47
|
+
...init,
|
|
48
|
+
headers
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const encoder = new TextEncoder();
|
|
53
|
+
|
|
54
|
+
/** @type {import('@sveltejs/kit').text} */
|
|
55
|
+
export function text(body, init) {
|
|
56
|
+
const headers = new Headers(init?.headers);
|
|
57
|
+
if (!headers.has('content-length')) {
|
|
58
|
+
headers.set('content-length', encoder.encode(body).byteLength.toString());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return new Response(body, {
|
|
38
62
|
...init,
|
|
39
63
|
headers
|
|
40
64
|
});
|
|
@@ -73,8 +73,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
73
73
|
} catch (error) {
|
|
74
74
|
manifest_error = /** @type {Error} */ (error);
|
|
75
75
|
|
|
76
|
-
console.error(colors.bold().red(
|
|
77
|
-
console.error(error);
|
|
76
|
+
console.error(colors.bold().red(manifest_error.message));
|
|
78
77
|
vite.ws.send({
|
|
79
78
|
type: 'error',
|
|
80
79
|
err: {
|
|
@@ -444,8 +443,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
444
443
|
}
|
|
445
444
|
|
|
446
445
|
if (manifest_error) {
|
|
447
|
-
console.error(colors.bold().red(
|
|
448
|
-
console.error(manifest_error);
|
|
446
|
+
console.error(colors.bold().red(manifest_error.message));
|
|
449
447
|
|
|
450
448
|
const error_page = load_error_page(svelte_config);
|
|
451
449
|
|
|
@@ -8,6 +8,12 @@ import { normalize_path } from '../../utils/url.js';
|
|
|
8
8
|
* @type {Record<string, Set<string>>} */
|
|
9
9
|
const cookie_paths = {};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Cookies that are larger than this size (including the name and other
|
|
13
|
+
* attributes) are discarded by browsers
|
|
14
|
+
*/
|
|
15
|
+
const MAX_COOKIE_SIZE = 4129;
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* @param {Request} request
|
|
13
19
|
* @param {URL} url
|
|
@@ -111,6 +117,11 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
111
117
|
};
|
|
112
118
|
|
|
113
119
|
if (__SVELTEKIT_DEV__) {
|
|
120
|
+
const serialized = serialize(name, value, new_cookies[name].options);
|
|
121
|
+
if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
|
|
122
|
+
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
cookie_paths[name] = cookie_paths[name] ?? new Set();
|
|
115
126
|
if (!value) {
|
|
116
127
|
if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
|
|
@@ -4,6 +4,7 @@ import { once } from '../../../utils/functions.js';
|
|
|
4
4
|
import { load_server_data } from '../page/load_data.js';
|
|
5
5
|
import { clarify_devalue_error, handle_error_and_jsonify, serialize_data_node } from '../utils.js';
|
|
6
6
|
import { normalize_path } from '../../../utils/url.js';
|
|
7
|
+
import { text } from '../../../exports/index.js';
|
|
7
8
|
|
|
8
9
|
export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
|
|
9
10
|
|
|
@@ -139,7 +140,7 @@ export async function render_data(
|
|
|
139
140
|
* @param {number} [status]
|
|
140
141
|
*/
|
|
141
142
|
function json_response(json, status = 200) {
|
|
142
|
-
return
|
|
143
|
+
return text(json, {
|
|
143
144
|
status,
|
|
144
145
|
headers: {
|
|
145
146
|
'content-type': 'application/json',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from '../../../exports/index.js';
|
|
1
2
|
import { compact } from '../../../utils/array.js';
|
|
2
3
|
import { normalize_error } from '../../../utils/error.js';
|
|
3
4
|
import { add_data_suffix } from '../../../utils/url.js';
|
|
@@ -32,7 +33,7 @@ import { respond_with_error } from './respond_with_error.js';
|
|
|
32
33
|
export async function render_page(event, route, page, options, manifest, state, resolve_opts) {
|
|
33
34
|
if (state.initiator === route) {
|
|
34
35
|
// infinite request cycle detected
|
|
35
|
-
return
|
|
36
|
+
return text(`Not found: ${event.url.pathname}`, {
|
|
36
37
|
status: 404
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -239,7 +240,7 @@ export async function render_page(event, route, page, options, manifest, state,
|
|
|
239
240
|
});
|
|
240
241
|
|
|
241
242
|
state.prerendering.dependencies.set(data_pathname, {
|
|
242
|
-
response:
|
|
243
|
+
response: text(body),
|
|
243
244
|
body
|
|
244
245
|
});
|
|
245
246
|
}
|
|
@@ -294,7 +295,7 @@ export async function render_page(event, route, page, options, manifest, state,
|
|
|
294
295
|
.join(',')}]}`;
|
|
295
296
|
|
|
296
297
|
state.prerendering.dependencies.set(data_pathname, {
|
|
297
|
-
response:
|
|
298
|
+
response: text(body),
|
|
298
299
|
body
|
|
299
300
|
});
|
|
300
301
|
}
|
|
@@ -9,6 +9,7 @@ import { uneval_action_response } from './actions.js';
|
|
|
9
9
|
import { clarify_devalue_error } from '../utils.js';
|
|
10
10
|
import { assets, base, version } from '../../shared.js';
|
|
11
11
|
import { env } from '../../env-public.js';
|
|
12
|
+
import { text } from '../../../exports/index.js';
|
|
12
13
|
|
|
13
14
|
// TODO rename this function/module
|
|
14
15
|
|
|
@@ -366,7 +367,8 @@ export async function render_response({
|
|
|
366
367
|
head,
|
|
367
368
|
body,
|
|
368
369
|
assets: resolved_assets,
|
|
369
|
-
nonce: /** @type {string} */ (csp.nonce)
|
|
370
|
+
nonce: /** @type {string} */ (csp.nonce),
|
|
371
|
+
env
|
|
370
372
|
});
|
|
371
373
|
|
|
372
374
|
// TODO flush chunks as early as we can
|
|
@@ -407,7 +409,7 @@ export async function render_response({
|
|
|
407
409
|
}
|
|
408
410
|
}
|
|
409
411
|
|
|
410
|
-
return
|
|
412
|
+
return text(transformed, {
|
|
411
413
|
status,
|
|
412
414
|
headers
|
|
413
415
|
});
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
validate_page_server_exports,
|
|
24
24
|
validate_server_exports
|
|
25
25
|
} from '../../utils/exports.js';
|
|
26
|
-
import { error, json } from '../../exports/index.js';
|
|
26
|
+
import { error, json, text } from '../../exports/index.js';
|
|
27
27
|
import * as paths from '../shared.js';
|
|
28
28
|
|
|
29
29
|
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
@@ -53,7 +53,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
53
53
|
if (request.headers.get('accept') === 'application/json') {
|
|
54
54
|
return json(csrf_error.body, { status: csrf_error.status });
|
|
55
55
|
}
|
|
56
|
-
return
|
|
56
|
+
return text(csrf_error.body.message, { status: csrf_error.status });
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -61,7 +61,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
61
61
|
try {
|
|
62
62
|
decoded = decode_pathname(url.pathname);
|
|
63
63
|
} catch {
|
|
64
|
-
return
|
|
64
|
+
return text('Malformed URI', { status: 400 });
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** @type {import('types').SSRRoute | null} */
|
|
@@ -72,7 +72,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
72
72
|
|
|
73
73
|
if (paths.base && !state.prerendering?.fallback) {
|
|
74
74
|
if (!decoded.startsWith(paths.base)) {
|
|
75
|
-
return
|
|
75
|
+
return text('Not found', { status: 404 });
|
|
76
76
|
}
|
|
77
77
|
decoded = decoded.slice(paths.base.length) || '/';
|
|
78
78
|
}
|
|
@@ -374,7 +374,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
if (state.initiator === GENERIC_ERROR) {
|
|
377
|
-
return
|
|
377
|
+
return text('Internal Server Error', {
|
|
378
378
|
status: 500
|
|
379
379
|
});
|
|
380
380
|
}
|
|
@@ -394,7 +394,7 @@ export async function respond(request, options, manifest, state) {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
if (state.prerendering) {
|
|
397
|
-
return
|
|
397
|
+
return text('not found', { status: 404 });
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
// we can't load the endpoint from our own manifest,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
|
+
import { json, text } from '../../exports/index.js';
|
|
2
3
|
import { coalesce_to_error } from '../../utils/error.js';
|
|
3
4
|
import { negotiate } from '../../utils/http.js';
|
|
4
5
|
import { has_data_suffix } from '../../utils/url.js';
|
|
@@ -27,7 +28,7 @@ export const GENERIC_ERROR = {
|
|
|
27
28
|
* @param {import('types').HttpMethod} method
|
|
28
29
|
*/
|
|
29
30
|
export function method_not_allowed(mod, method) {
|
|
30
|
-
return
|
|
31
|
+
return text(`${method} method not allowed`, {
|
|
31
32
|
status: 405,
|
|
32
33
|
headers: {
|
|
33
34
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
|
@@ -75,7 +76,7 @@ export function get_option(nodes, option) {
|
|
|
75
76
|
* @param {string} message
|
|
76
77
|
*/
|
|
77
78
|
export function static_error_page(options, status, message) {
|
|
78
|
-
return
|
|
79
|
+
return text(options.templates.error({ status, message }), {
|
|
79
80
|
headers: { 'content-type': 'text/html; charset=utf-8' },
|
|
80
81
|
status
|
|
81
82
|
});
|
|
@@ -98,9 +99,8 @@ export async function handle_fatal_error(event, options, error) {
|
|
|
98
99
|
]);
|
|
99
100
|
|
|
100
101
|
if (has_data_suffix(new URL(event.request.url).pathname) || type === 'application/json') {
|
|
101
|
-
return
|
|
102
|
-
status
|
|
103
|
-
headers: { 'content-type': 'application/json; charset=utf-8' }
|
|
102
|
+
return json(body, {
|
|
103
|
+
status
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
|
package/types/index.d.ts
CHANGED
|
@@ -1123,10 +1123,17 @@ export interface Redirect {
|
|
|
1123
1123
|
/**
|
|
1124
1124
|
* Create a JSON `Response` object from the supplied data.
|
|
1125
1125
|
* @param data The value that will be serialized as JSON.
|
|
1126
|
-
* @param init Options such as `status` and `headers` that will be added to the response.
|
|
1126
|
+
* @param init Options such as `status` and `headers` that will be added to the response. `Content-Type: application/json` and `Content-Length` headers will be added automatically.
|
|
1127
1127
|
*/
|
|
1128
1128
|
export function json(data: any, init?: ResponseInit): Response;
|
|
1129
1129
|
|
|
1130
|
+
/**
|
|
1131
|
+
* Create a `Response` object from the supplied body.
|
|
1132
|
+
* @param body The value that will be used as-is.
|
|
1133
|
+
* @param init Options such as `status` and `headers` that will be added to the response. A `Content-Length` header will be added automatically.
|
|
1134
|
+
*/
|
|
1135
|
+
export function text(body: string, init?: ResponseInit): Response;
|
|
1136
|
+
|
|
1130
1137
|
/**
|
|
1131
1138
|
* Create an `ActionFailure` object.
|
|
1132
1139
|
* @param status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
|
package/types/internal.d.ts
CHANGED
|
@@ -306,7 +306,13 @@ export interface SSROptions {
|
|
|
306
306
|
root: SSRComponent['default'];
|
|
307
307
|
service_worker: boolean;
|
|
308
308
|
templates: {
|
|
309
|
-
app(values: {
|
|
309
|
+
app(values: {
|
|
310
|
+
head: string;
|
|
311
|
+
body: string;
|
|
312
|
+
assets: string;
|
|
313
|
+
nonce: string;
|
|
314
|
+
env: Record<string, string>;
|
|
315
|
+
}): string;
|
|
310
316
|
error(values: { message: string; status: number }): string;
|
|
311
317
|
};
|
|
312
318
|
}
|