@sveltejs/kit 1.0.0-next.577 → 1.0.0-next.579
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 +7 -7
- package/src/core/adapt/builder.js +42 -11
- package/src/core/prerender/fallback.js +43 -0
- package/src/core/prerender/prerender.js +0 -12
- package/src/exports/index.js +9 -4
- package/src/exports/vite/dev/index.js +5 -20
- package/src/exports/vite/index.js +1 -1
- package/src/exports/vite/preview/index.js +2 -1
- package/src/exports/vite/utils.js +29 -0
- package/src/runtime/app/forms.js +1 -1
- package/src/runtime/client/client.js +1 -0
- package/src/runtime/client/fetcher.js +10 -10
- package/src/runtime/control.js +1 -1
- package/src/runtime/server/page/actions.js +11 -11
- package/src/runtime/server/page/index.js +1 -1
- package/src/runtime/server/page/render.js +1 -1
- package/types/index.d.ts +13 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.579",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"homepage": "https://kit.svelte.dev",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
13
|
+
"@sveltejs/vite-plugin-svelte": "^2.0.0",
|
|
14
14
|
"@types/cookie": "^0.5.1",
|
|
15
15
|
"cookie": "^0.5.0",
|
|
16
16
|
"devalue": "^4.2.0",
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
"@types/sade": "^1.7.4",
|
|
33
33
|
"@types/set-cookie-parser": "^2.4.2",
|
|
34
34
|
"marked": "^4.2.3",
|
|
35
|
-
"rollup": "^
|
|
36
|
-
"svelte": "^3.
|
|
35
|
+
"rollup": "^3.7.0",
|
|
36
|
+
"svelte": "^3.54.0",
|
|
37
37
|
"svelte-preprocess": "^4.10.7",
|
|
38
38
|
"typescript": "^4.9.3",
|
|
39
39
|
"uvu": "^0.5.6",
|
|
40
|
-
"vite": "^
|
|
40
|
+
"vite": "^4.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"svelte": "^3.
|
|
44
|
-
"vite": "^
|
|
43
|
+
"svelte": "^3.54.0",
|
|
44
|
+
"vite": "^4.0.0"
|
|
45
45
|
},
|
|
46
46
|
"bin": {
|
|
47
47
|
"svelte-kit": "svelte-kit.js"
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { existsSync, statSync, createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { pipeline } from 'node:stream';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { fork } from 'node:child_process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
1
6
|
import glob from 'tiny-glob';
|
|
2
7
|
import zlib from 'zlib';
|
|
3
|
-
import { existsSync, statSync, createReadStream, createWriteStream } from 'fs';
|
|
4
|
-
import { pipeline } from 'stream';
|
|
5
|
-
import { promisify } from 'util';
|
|
6
8
|
import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
|
|
7
9
|
import { generate_manifest } from '../generate_manifest/index.js';
|
|
8
10
|
import { get_route_segments } from '../../utils/routing.js';
|
|
11
|
+
import { get_env } from '../../exports/vite/utils.js';
|
|
9
12
|
|
|
10
13
|
const pipe = promisify(pipeline);
|
|
11
14
|
|
|
@@ -104,6 +107,33 @@ export function create_builder({ config, build_data, routes, prerendered, log })
|
|
|
104
107
|
}
|
|
105
108
|
},
|
|
106
109
|
|
|
110
|
+
generateFallback(dest) {
|
|
111
|
+
// do prerendering in a subprocess so any dangling stuff gets killed upon completion
|
|
112
|
+
const script = fileURLToPath(new URL('../prerender/fallback.js', import.meta.url));
|
|
113
|
+
|
|
114
|
+
const manifest_path = `${config.kit.outDir}/output/server/manifest-full.js`;
|
|
115
|
+
|
|
116
|
+
const env = get_env(config.kit.env, 'production');
|
|
117
|
+
|
|
118
|
+
return new Promise((fulfil, reject) => {
|
|
119
|
+
const child = fork(
|
|
120
|
+
script,
|
|
121
|
+
[dest, manifest_path, JSON.stringify({ ...env.private, ...env.public })],
|
|
122
|
+
{
|
|
123
|
+
stdio: 'inherit'
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
child.on('exit', (code) => {
|
|
128
|
+
if (code) {
|
|
129
|
+
reject(new Error(`Could not create a fallback page — failed with code ${code}`));
|
|
130
|
+
} else {
|
|
131
|
+
fulfil(undefined);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
|
|
107
137
|
generateManifest: ({ relativePath }) => {
|
|
108
138
|
return generate_manifest({
|
|
109
139
|
build_data,
|
|
@@ -132,16 +162,17 @@ export function create_builder({ config, build_data, routes, prerendered, log })
|
|
|
132
162
|
return [...copy(`${config.kit.outDir}/output/client`, dest)];
|
|
133
163
|
},
|
|
134
164
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
165
|
+
// @ts-expect-error
|
|
166
|
+
writePrerendered(dest, opts) {
|
|
167
|
+
// TODO remove for 1.0
|
|
168
|
+
if (opts?.fallback) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'The fallback option no longer exists — use builder.generateFallback(fallback) instead'
|
|
171
|
+
);
|
|
142
172
|
}
|
|
143
173
|
|
|
144
|
-
|
|
174
|
+
const source = `${config.kit.outDir}/output/prerendered`;
|
|
175
|
+
return [...copy(`${source}/pages`, dest), ...copy(`${source}/dependencies`, dest)];
|
|
145
176
|
},
|
|
146
177
|
|
|
147
178
|
writeServer(dest) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { mkdirp } from '../../utils/filesystem.js';
|
|
5
|
+
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
6
|
+
import { load_config } from '../config/index.js';
|
|
7
|
+
|
|
8
|
+
const [, , dest, manifest_path, env] = process.argv;
|
|
9
|
+
|
|
10
|
+
/** @type {import('types').ValidatedKitConfig} */
|
|
11
|
+
const config = (await load_config()).kit;
|
|
12
|
+
|
|
13
|
+
installPolyfills();
|
|
14
|
+
|
|
15
|
+
const server_root = join(config.outDir, 'output');
|
|
16
|
+
|
|
17
|
+
/** @type {import('types').ServerModule} */
|
|
18
|
+
const { Server, override } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
|
|
19
|
+
|
|
20
|
+
/** @type {import('types').SSRManifest} */
|
|
21
|
+
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
|
22
|
+
|
|
23
|
+
override({
|
|
24
|
+
building: true,
|
|
25
|
+
paths: config.paths,
|
|
26
|
+
read: (file) => readFileSync(join(config.files.assets, file))
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const server = new Server(manifest);
|
|
30
|
+
await server.init({ env: JSON.parse(env) });
|
|
31
|
+
|
|
32
|
+
const rendered = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
|
|
33
|
+
getClientAddress: () => {
|
|
34
|
+
throw new Error('Cannot read clientAddress during prerendering');
|
|
35
|
+
},
|
|
36
|
+
prerendering: {
|
|
37
|
+
fallback: true,
|
|
38
|
+
dependencies: new Map()
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
mkdirp(dirname(dest));
|
|
43
|
+
writeFileSync(dest, await rendered.text());
|
|
@@ -453,18 +453,6 @@ export async function prerender() {
|
|
|
453
453
|
);
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
-
const rendered = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
|
|
457
|
-
getClientAddress,
|
|
458
|
-
prerendering: {
|
|
459
|
-
fallback: true,
|
|
460
|
-
dependencies: new Map()
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
const file = `${config.outDir}/output/prerendered/fallback.html`;
|
|
465
|
-
mkdirp(dirname(file));
|
|
466
|
-
writeFileSync(file, await rendered.text());
|
|
467
|
-
|
|
468
456
|
output_and_exit({ prerendered, prerender_map });
|
|
469
457
|
}
|
|
470
458
|
|
package/src/exports/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpError, Redirect,
|
|
1
|
+
import { HttpError, Redirect, ActionFailure } from '../runtime/control.js';
|
|
2
2
|
|
|
3
3
|
// For some reason we need to type the params as well here,
|
|
4
4
|
// JSdoc doesn't seem to like @type with function overloads
|
|
@@ -46,10 +46,15 @@ export function json(data, init) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Generates
|
|
49
|
+
* Generates an `ActionFailure` object.
|
|
50
50
|
* @param {number} status
|
|
51
51
|
* @param {Record<string, any> | undefined} [data]
|
|
52
52
|
*/
|
|
53
|
-
export function
|
|
54
|
-
return new
|
|
53
|
+
export function fail(status, data) {
|
|
54
|
+
return new ActionFailure(status, data);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// TODO remove for 1.0
|
|
58
|
+
export function invalid() {
|
|
59
|
+
throw new Error('invalid(...) is now fail(...)');
|
|
55
60
|
}
|
|
@@ -3,6 +3,7 @@ import colors from 'kleur';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import sirv from 'sirv';
|
|
5
5
|
import { URL } from 'url';
|
|
6
|
+
import { isCSSRequest } from 'vite';
|
|
6
7
|
import { getRequest, setResponse } from '../../../exports/node/index.js';
|
|
7
8
|
import { installPolyfills } from '../../../exports/node/polyfills.js';
|
|
8
9
|
import { coalesce_to_error } from '../../../utils/error.js';
|
|
@@ -12,10 +13,7 @@ import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
|
|
12
13
|
import * as sync from '../../../core/sync/sync.js';
|
|
13
14
|
import { get_mime_lookup, runtime_base, runtime_prefix } from '../../../core/utils.js';
|
|
14
15
|
import { compact } from '../../../utils/array.js';
|
|
15
|
-
|
|
16
|
-
// Vite doesn't expose this so we just copy the list for now
|
|
17
|
-
// https://github.com/vitejs/vite/blob/3edd1af56e980aef56641a5a51cf2932bb580d41/packages/vite/src/node/plugins/css.ts#L96
|
|
18
|
-
const style_pattern = /\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/;
|
|
16
|
+
import { not_found } from '../utils.js';
|
|
19
17
|
|
|
20
18
|
const cwd = process.cwd();
|
|
21
19
|
|
|
@@ -131,7 +129,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
131
129
|
const query = parsed.searchParams;
|
|
132
130
|
|
|
133
131
|
if (
|
|
134
|
-
|
|
132
|
+
isCSSRequest(dep.file) ||
|
|
135
133
|
(query.has('svelte') && query.get('type') === 'style')
|
|
136
134
|
) {
|
|
137
135
|
try {
|
|
@@ -327,10 +325,7 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
327
325
|
}
|
|
328
326
|
|
|
329
327
|
if (!decoded.startsWith(svelte_config.kit.paths.base)) {
|
|
330
|
-
return not_found(
|
|
331
|
-
res,
|
|
332
|
-
`Not found (did you mean ${svelte_config.kit.paths.base + req.url}?)`
|
|
333
|
-
);
|
|
328
|
+
return not_found(req, res, svelte_config.kit.paths.base);
|
|
334
329
|
}
|
|
335
330
|
|
|
336
331
|
if (decoded === svelte_config.kit.paths.base + '/service-worker.js') {
|
|
@@ -531,21 +526,11 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
531
526
|
};
|
|
532
527
|
}
|
|
533
528
|
|
|
534
|
-
/** @param {import('http').ServerResponse} res */
|
|
535
|
-
function not_found(res, message = 'Not found') {
|
|
536
|
-
res.statusCode = 404;
|
|
537
|
-
res.end(message);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
529
|
/**
|
|
541
530
|
* @param {import('connect').Server} server
|
|
542
531
|
*/
|
|
543
532
|
function remove_static_middlewares(server) {
|
|
544
|
-
|
|
545
|
-
// https://github.com/vitejs/vite/issues/9260
|
|
546
|
-
// https://github.com/vitejs/vite/issues/9236
|
|
547
|
-
// https://github.com/vitejs/vite/issues/9234
|
|
548
|
-
const static_middlewares = ['viteServePublicMiddleware', 'viteServeStaticMiddleware'];
|
|
533
|
+
const static_middlewares = ['viteServeStaticMiddleware'];
|
|
549
534
|
for (let i = server.stack.length - 1; i > 0; i--) {
|
|
550
535
|
// @ts-expect-error using internals
|
|
551
536
|
if (static_middlewares.includes(server.stack[i].handle.name)) {
|
|
@@ -583,7 +583,7 @@ function kit({ svelte_config }) {
|
|
|
583
583
|
|
|
584
584
|
function check_vite_version() {
|
|
585
585
|
// TODO parse from kit peer deps and maybe do a full semver compare if we ever require feature releases a min
|
|
586
|
-
const min_required_vite_major =
|
|
586
|
+
const min_required_vite_major = 4;
|
|
587
587
|
const vite_version = vite.version ?? '2.x'; // vite started exporting it's version in 3.0
|
|
588
588
|
const current_vite_major = parseInt(vite_version.split('.')[0], 10);
|
|
589
589
|
|
|
@@ -6,6 +6,7 @@ import { getRequest, setResponse } from '../../../exports/node/index.js';
|
|
|
6
6
|
import { installPolyfills } from '../../../exports/node/polyfills.js';
|
|
7
7
|
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
|
8
8
|
import { loadEnv, normalizePath } from 'vite';
|
|
9
|
+
import { not_found } from '../utils.js';
|
|
9
10
|
|
|
10
11
|
/** @typedef {import('http').IncomingMessage} Req */
|
|
11
12
|
/** @typedef {import('http').ServerResponse} Res */
|
|
@@ -73,7 +74,7 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
73
74
|
next();
|
|
74
75
|
} else {
|
|
75
76
|
res.statusCode = 404;
|
|
76
|
-
|
|
77
|
+
not_found(req, res, base);
|
|
77
78
|
}
|
|
78
79
|
});
|
|
79
80
|
|
|
@@ -2,6 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { loadConfigFromFile, loadEnv, mergeConfig } from 'vite';
|
|
3
3
|
import { runtime_directory } from '../../core/utils.js';
|
|
4
4
|
import { posixify } from '../../utils/filesystem.js';
|
|
5
|
+
import { negotiate } from '../../utils/http.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {import('vite').ResolvedConfig} config
|
|
@@ -149,3 +150,31 @@ export function get_env(env_config, mode) {
|
|
|
149
150
|
private: Object.fromEntries(entries.filter(([k]) => !k.startsWith(env_config.publicPrefix)))
|
|
150
151
|
};
|
|
151
152
|
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {import('http').IncomingMessage} req
|
|
156
|
+
* @param {import('http').ServerResponse} res
|
|
157
|
+
* @param {string} base
|
|
158
|
+
*/
|
|
159
|
+
export function not_found(req, res, base) {
|
|
160
|
+
const type = negotiate(req.headers.accept ?? '*', ['text/plain', 'text/html']);
|
|
161
|
+
|
|
162
|
+
// special case — handle `/` request automatically
|
|
163
|
+
if (req.url === '/' && type === 'text/html') {
|
|
164
|
+
res.statusCode = 307;
|
|
165
|
+
res.setHeader('location', base);
|
|
166
|
+
res.end();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
res.statusCode = 404;
|
|
171
|
+
|
|
172
|
+
const prefixed = base + req.url;
|
|
173
|
+
|
|
174
|
+
if (type === 'text/html') {
|
|
175
|
+
res.setHeader('Content-Type', 'text/html');
|
|
176
|
+
res.end(`Not found (did you mean <a href="${prefixed}">${prefixed}</a>?)`);
|
|
177
|
+
} else {
|
|
178
|
+
res.end(`Not found (did you mean ${prefixed}?)`);
|
|
179
|
+
}
|
|
180
|
+
}
|
package/src/runtime/app/forms.js
CHANGED
|
@@ -47,7 +47,7 @@ export function enhance(form, submit = () => {}) {
|
|
|
47
47
|
await invalidateAll();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// For success/
|
|
50
|
+
// For success/failure results, only apply action if it belongs to the
|
|
51
51
|
// current page, otherwise `form` will be updated erroneously
|
|
52
52
|
if (
|
|
53
53
|
location.origin + location.pathname === action.origin + action.pathname ||
|
|
@@ -41,9 +41,7 @@ if (import.meta.env.DEV) {
|
|
|
41
41
|
const method = input instanceof Request ? input.method : init?.method || 'GET';
|
|
42
42
|
|
|
43
43
|
if (method !== 'GET') {
|
|
44
|
-
|
|
45
|
-
.href;
|
|
46
|
-
cache.delete(url);
|
|
44
|
+
cache.delete(build_selector(input));
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
return native_fetch(input, init);
|
|
@@ -53,9 +51,7 @@ if (import.meta.env.DEV) {
|
|
|
53
51
|
const method = input instanceof Request ? input.method : init?.method || 'GET';
|
|
54
52
|
|
|
55
53
|
if (method !== 'GET') {
|
|
56
|
-
|
|
57
|
-
.href;
|
|
58
|
-
cache.delete(url);
|
|
54
|
+
cache.delete(build_selector(input));
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
return native_fetch(input, init);
|
|
@@ -67,7 +63,7 @@ const cache = new Map();
|
|
|
67
63
|
/**
|
|
68
64
|
* Should be called on the initial run of load functions that hydrate the page.
|
|
69
65
|
* Saves any requests with cache-control max-age to the cache.
|
|
70
|
-
* @param {
|
|
66
|
+
* @param {URL | string} resource
|
|
71
67
|
* @param {RequestInit} [opts]
|
|
72
68
|
*/
|
|
73
69
|
export function initial_fetch(resource, opts) {
|
|
@@ -88,7 +84,7 @@ export function initial_fetch(resource, opts) {
|
|
|
88
84
|
|
|
89
85
|
/**
|
|
90
86
|
* Tries to get the response from the cache, if max-age allows it, else does a fetch.
|
|
91
|
-
* @param {
|
|
87
|
+
* @param {URL | string} resource
|
|
92
88
|
* @param {string} resolved
|
|
93
89
|
* @param {RequestInit} [opts]
|
|
94
90
|
*/
|
|
@@ -97,7 +93,11 @@ export function subsequent_fetch(resource, resolved, opts) {
|
|
|
97
93
|
const selector = build_selector(resource, opts);
|
|
98
94
|
const cached = cache.get(selector);
|
|
99
95
|
if (cached) {
|
|
100
|
-
|
|
96
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Request/cache#value
|
|
97
|
+
if (
|
|
98
|
+
performance.now() < cached.ttl &&
|
|
99
|
+
['default', 'force-cache', 'only-if-cached', undefined].includes(opts?.cache)
|
|
100
|
+
) {
|
|
101
101
|
return new Response(cached.body, cached.init);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -110,7 +110,7 @@ export function subsequent_fetch(resource, resolved, opts) {
|
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Build the cache key for a given request
|
|
113
|
-
* @param {
|
|
113
|
+
* @param {URL | RequestInfo} resource
|
|
114
114
|
* @param {RequestInit} [opts]
|
|
115
115
|
*/
|
|
116
116
|
function build_selector(resource, opts) {
|
package/src/runtime/control.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as devalue from 'devalue';
|
|
|
2
2
|
import { error, json } from '../../../exports/index.js';
|
|
3
3
|
import { normalize_error } from '../../../utils/error.js';
|
|
4
4
|
import { is_form_content_type, negotiate } from '../../../utils/http.js';
|
|
5
|
-
import { HttpError, Redirect,
|
|
5
|
+
import { HttpError, Redirect, ActionFailure } from '../../control.js';
|
|
6
6
|
import { handle_error_and_jsonify } from '../utils.js';
|
|
7
7
|
|
|
8
8
|
/** @param {import('types').RequestEvent} event */
|
|
@@ -50,9 +50,9 @@ export async function handle_action_json_request(event, options, server) {
|
|
|
50
50
|
try {
|
|
51
51
|
const data = await call_action(event, actions);
|
|
52
52
|
|
|
53
|
-
if (data instanceof
|
|
53
|
+
if (data instanceof ActionFailure) {
|
|
54
54
|
return action_json({
|
|
55
|
-
type: '
|
|
55
|
+
type: 'failure',
|
|
56
56
|
status: data.status,
|
|
57
57
|
// @ts-expect-error we assign a string to what is supposed to be an object. That's ok
|
|
58
58
|
// because we don't use the object outside, and this way we have better code navigation
|
|
@@ -81,7 +81,7 @@ export async function handle_action_json_request(event, options, server) {
|
|
|
81
81
|
return action_json(
|
|
82
82
|
{
|
|
83
83
|
type: 'error',
|
|
84
|
-
error: await handle_error_and_jsonify(event, options,
|
|
84
|
+
error: await handle_error_and_jsonify(event, options, check_incorrect_fail_use(error))
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
status: error instanceof HttpError ? error.status : 500
|
|
@@ -93,9 +93,9 @@ export async function handle_action_json_request(event, options, server) {
|
|
|
93
93
|
/**
|
|
94
94
|
* @param {HttpError | Error} error
|
|
95
95
|
*/
|
|
96
|
-
function
|
|
97
|
-
return error instanceof
|
|
98
|
-
? new Error(`Cannot "throw
|
|
96
|
+
function check_incorrect_fail_use(error) {
|
|
97
|
+
return error instanceof ActionFailure
|
|
98
|
+
? new Error(`Cannot "throw fail()". Use "return fail()"`)
|
|
99
99
|
: error;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -142,8 +142,8 @@ export async function handle_action_request(event, server) {
|
|
|
142
142
|
try {
|
|
143
143
|
const data = await call_action(event, actions);
|
|
144
144
|
|
|
145
|
-
if (data instanceof
|
|
146
|
-
return { type: '
|
|
145
|
+
if (data instanceof ActionFailure) {
|
|
146
|
+
return { type: 'failure', status: data.status, data: data.data };
|
|
147
147
|
} else {
|
|
148
148
|
return {
|
|
149
149
|
type: 'success',
|
|
@@ -164,7 +164,7 @@ export async function handle_action_request(event, server) {
|
|
|
164
164
|
|
|
165
165
|
return {
|
|
166
166
|
type: 'error',
|
|
167
|
-
error:
|
|
167
|
+
error: check_incorrect_fail_use(error)
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -183,7 +183,7 @@ function check_named_default_separate(actions) {
|
|
|
183
183
|
/**
|
|
184
184
|
* @param {import('types').RequestEvent} event
|
|
185
185
|
* @param {NonNullable<import('types').SSRNode['server']['actions']>} actions
|
|
186
|
-
* @throws {Redirect |
|
|
186
|
+
* @throws {Redirect | ActionFailure | HttpError | Error}
|
|
187
187
|
*/
|
|
188
188
|
export async function call_action(event, actions) {
|
|
189
189
|
const url = new URL(event.request.url);
|
|
@@ -68,7 +68,7 @@ export async function render_page(event, route, page, options, state, resolve_op
|
|
|
68
68
|
const error = action_result.error;
|
|
69
69
|
status = error instanceof HttpError ? error.status : 500;
|
|
70
70
|
}
|
|
71
|
-
if (action_result?.type === '
|
|
71
|
+
if (action_result?.type === 'failure') {
|
|
72
72
|
status = action_result.status;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -67,7 +67,7 @@ export async function render_response({
|
|
|
67
67
|
let rendered;
|
|
68
68
|
|
|
69
69
|
const form_value =
|
|
70
|
-
action_result?.type === 'success' || action_result?.type === '
|
|
70
|
+
action_result?.type === 'success' || action_result?.type === 'failure'
|
|
71
71
|
? action_result.data ?? null
|
|
72
72
|
: null;
|
|
73
73
|
|
package/types/index.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ type OptionalUnion<
|
|
|
61
61
|
A extends keyof U = U extends U ? keyof U : never
|
|
62
62
|
> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
|
63
63
|
|
|
64
|
-
type UnpackValidationError<T> = T extends
|
|
64
|
+
type UnpackValidationError<T> = T extends ActionFailure<infer X>
|
|
65
65
|
? X
|
|
66
66
|
: T extends void
|
|
67
67
|
? undefined // needs to be undefined, because void will corrupt union type
|
|
@@ -90,6 +90,11 @@ export interface Builder {
|
|
|
90
90
|
*/
|
|
91
91
|
createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise<void>;
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Generate a fallback page for a static webserver to use when no route is matched. Useful for single-page apps.
|
|
95
|
+
*/
|
|
96
|
+
generateFallback(dest: string): Promise<void>;
|
|
97
|
+
|
|
93
98
|
/**
|
|
94
99
|
* Generate a server-side manifest to initialise the SvelteKit [server](https://kit.svelte.dev/docs/types#public-types-server) with.
|
|
95
100
|
* @param opts a relative path to the base directory of the app and optionally in which format (esm or cjs) the manifest should be generated
|
|
@@ -117,15 +122,9 @@ export interface Builder {
|
|
|
117
122
|
/**
|
|
118
123
|
* Write prerendered files to `dest`.
|
|
119
124
|
* @param dest the destination folder
|
|
120
|
-
* @param opts.fallback the name of a file for fallback responses, like `200.html` or `404.html` depending on where the app is deployed
|
|
121
125
|
* @returns an array of files written to `dest`
|
|
122
126
|
*/
|
|
123
|
-
writePrerendered(
|
|
124
|
-
dest: string,
|
|
125
|
-
opts?: {
|
|
126
|
-
fallback?: string;
|
|
127
|
-
}
|
|
128
|
-
): string[];
|
|
127
|
+
writePrerendered(dest: string): string[];
|
|
129
128
|
/**
|
|
130
129
|
* Write server-side code to `dest`.
|
|
131
130
|
* @param dest the destination folder
|
|
@@ -1074,7 +1073,7 @@ export type ActionResult<
|
|
|
1074
1073
|
Invalid extends Record<string, unknown> | undefined = Record<string, any>
|
|
1075
1074
|
> =
|
|
1076
1075
|
| { type: 'success'; status: number; data?: Success }
|
|
1077
|
-
| { type: '
|
|
1076
|
+
| { type: 'failure'; status: number; data?: Invalid }
|
|
1078
1077
|
| { type: 'redirect'; status: number; location: string }
|
|
1079
1078
|
| { type: 'error'; error: any };
|
|
1080
1079
|
|
|
@@ -1129,17 +1128,17 @@ export interface Redirect {
|
|
|
1129
1128
|
export function json(data: any, init?: ResponseInit): Response;
|
|
1130
1129
|
|
|
1131
1130
|
/**
|
|
1132
|
-
* Create
|
|
1131
|
+
* Create an `ActionFailure` object.
|
|
1133
1132
|
*/
|
|
1134
|
-
export function
|
|
1133
|
+
export function fail<T extends Record<string, unknown> | undefined>(
|
|
1135
1134
|
status: number,
|
|
1136
1135
|
data?: T
|
|
1137
|
-
):
|
|
1136
|
+
): ActionFailure<T>;
|
|
1138
1137
|
|
|
1139
1138
|
/**
|
|
1140
|
-
* The object returned by the [`
|
|
1139
|
+
* The object returned by the [`fail`](https://kit.svelte.dev/docs/modules#sveltejs-kit-fail) function
|
|
1141
1140
|
*/
|
|
1142
|
-
export interface
|
|
1141
|
+
export interface ActionFailure<T extends Record<string, unknown> | undefined = undefined>
|
|
1143
1142
|
extends UniqueInterface {
|
|
1144
1143
|
status: number;
|
|
1145
1144
|
data: T;
|