@sveltejs/kit 3.0.0-next.10 → 3.0.0-next.12
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 +1 -1
- package/src/cli.js +8 -8
- package/src/constants.js +3 -1
- package/src/core/adapt/builder.js +3 -6
- package/src/core/config/index.js +6 -1
- package/src/core/config/options.js +54 -104
- package/src/core/env.js +25 -11
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +75 -28
- package/src/core/postbuild/queue.js +2 -1
- package/src/core/sync/sync.js +11 -14
- package/src/core/sync/utils.js +21 -1
- package/src/core/sync/{write_non_ambient.js → write_app_types.js} +23 -21
- package/src/core/sync/write_client_manifest.js +4 -12
- package/src/core/sync/write_env.js +6 -4
- package/src/core/sync/write_server.js +12 -17
- package/src/core/sync/write_tsconfig/index.js +245 -0
- package/src/core/sync/write_tsconfig/utils.js +162 -0
- package/src/core/sync/write_types/index.js +80 -88
- package/src/core/utils.js +28 -3
- package/src/exports/env/index.js +68 -3
- package/src/exports/internal/env.js +6 -3
- package/src/exports/internal/server/index.js +3 -1
- package/src/exports/params.js +9 -4
- package/src/exports/public.d.ts +69 -71
- package/src/exports/vite/dev/index.js +112 -56
- package/src/exports/vite/index.js +483 -258
- package/src/exports/vite/module_ids.js +2 -2
- package/src/exports/vite/preview/index.js +3 -2
- package/src/exports/vite/utils.js +21 -10
- package/src/runner.js +13 -0
- package/src/runtime/app/forms.js +4 -0
- package/src/runtime/app/manifest/index.js +1 -0
- package/src/runtime/app/paths/client.js +30 -30
- package/src/runtime/app/paths/internal/client.js +26 -0
- package/src/runtime/app/paths/server.js +23 -10
- package/src/runtime/app/paths/types.d.ts +11 -19
- package/src/runtime/app/server/index.js +1 -1
- package/src/runtime/app/server/remote/command.js +12 -7
- package/src/runtime/app/server/remote/form.js +29 -26
- package/src/runtime/app/server/remote/prerender.js +9 -2
- package/src/runtime/app/server/remote/query.js +6 -5
- package/src/runtime/app/server/remote/shared.js +48 -30
- package/src/runtime/app/service-worker/index.js +24 -0
- package/src/runtime/app/state/index.js +1 -1
- package/src/runtime/client/client.js +46 -9
- package/src/runtime/client/ndjson.js +1 -1
- package/src/runtime/client/remote-functions/form.svelte.js +43 -51
- package/src/runtime/client/remote-functions/query/index.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -3
- package/src/runtime/client/remote-functions/query-live/iterator.js +5 -2
- package/src/runtime/client/remote-functions/shared.svelte.js +4 -1
- package/src/runtime/client/state.svelte.js +54 -21
- package/src/runtime/client/stream.js +3 -2
- package/src/runtime/form-utils.js +89 -54
- package/src/runtime/server/cookie.js +1 -1
- package/src/runtime/server/data/index.js +32 -29
- package/src/runtime/server/errors.js +135 -0
- package/src/runtime/server/fetch.js +1 -1
- package/src/runtime/server/index.js +20 -10
- package/src/runtime/server/internal.js +25 -0
- package/src/runtime/server/page/actions.js +4 -3
- package/src/runtime/server/page/data_serializer.js +10 -10
- package/src/runtime/server/page/index.js +3 -2
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +22 -35
- package/src/runtime/server/page/respond_with_error.js +6 -5
- package/src/runtime/server/{remote.js → remote-functions.js} +67 -36
- package/src/runtime/server/respond.js +58 -20
- package/src/runtime/server/sourcemaps.js +183 -0
- package/src/runtime/server/utils.js +12 -157
- package/src/types/ambient-private.d.ts +10 -0
- package/src/types/ambient.d.ts +22 -27
- package/src/types/global-private.d.ts +12 -5
- package/src/types/internal.d.ts +19 -9
- package/src/types/private.d.ts +8 -0
- package/src/utils/escape.js +9 -25
- package/src/utils/features.js +1 -1
- package/src/utils/fork.js +7 -2
- package/src/utils/page_nodes.js +3 -5
- package/src/utils/url.js +12 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +152 -120
- package/types/index.d.ts.map +6 -1
- package/src/core/sync/write_ambient.js +0 -18
- package/src/core/sync/write_tsconfig.js +0 -258
- package/src/runtime/shared-server.js +0 -7
- /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
|
@@ -6,10 +6,10 @@ export const sveltekit_env_public_client = '\0virtual:__sveltekit/env/public/cli
|
|
|
6
6
|
export const sveltekit_env_public_server = '\0virtual:__sveltekit/env/public/server';
|
|
7
7
|
export const sveltekit_env_private = '\0virtual:__sveltekit/env/private';
|
|
8
8
|
export const sveltekit_env_service_worker = '\0virtual:__sveltekit/env/service-worker';
|
|
9
|
-
|
|
9
|
+
export const sveltekit_server = '\0virtual:__sveltekit/server';
|
|
10
10
|
export const service_worker = '\0virtual:service-worker';
|
|
11
11
|
|
|
12
|
-
export const
|
|
12
|
+
export const sveltekit_manifest_data = '\0virtual:__sveltekit/manifest-data';
|
|
13
13
|
|
|
14
14
|
export const app_server = posixify(
|
|
15
15
|
fileURLToPath(new URL('../../runtime/app/server/index.js', import.meta.url))
|
|
@@ -10,6 +10,7 @@ import { loadEnv, normalizePath } from 'vite';
|
|
|
10
10
|
import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
|
|
11
11
|
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
|
12
12
|
import { is_chrome_devtools_request, not_found } from '../utils.js';
|
|
13
|
+
import { stackless } from '../../../utils/error.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {PreviewServer} vite
|
|
@@ -27,8 +28,8 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
27
28
|
|
|
28
29
|
const dir = join(svelte_config.kit.outDir, 'output/server');
|
|
29
30
|
|
|
30
|
-
if (!fs.existsSync(dir)) {
|
|
31
|
-
throw
|
|
31
|
+
if (!fs.existsSync(`${dir}/manifest.js`)) {
|
|
32
|
+
throw stackless(`Server files not found at ${dir}, did you run \`build\` first?`);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const instrumentation = join(dir, 'instrumentation.server.js');
|
|
@@ -4,12 +4,8 @@ import { negotiate } from '../../utils/http.js';
|
|
|
4
4
|
import { escape_html } from '../../utils/escape.js';
|
|
5
5
|
import { stackless } from '../../utils/error.js';
|
|
6
6
|
import { dedent } from '../../core/sync/utils.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
app_env_private,
|
|
10
|
-
service_worker,
|
|
11
|
-
sveltekit_env_private
|
|
12
|
-
} from './module_ids.js';
|
|
7
|
+
import { app_server, app_env_private, sveltekit_env_private } from './module_ids.js';
|
|
8
|
+
import { styleText } from 'node:util';
|
|
13
9
|
|
|
14
10
|
/**
|
|
15
11
|
* Transforms alias to a valid vite.resolve.alias array.
|
|
@@ -142,13 +138,13 @@ export function normalize_id(id, aliases, cwd) {
|
|
|
142
138
|
return '$app/env/private';
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
if (id === service_worker) {
|
|
146
|
-
return '$service-worker';
|
|
147
|
-
}
|
|
148
|
-
|
|
149
141
|
return posixify(id);
|
|
150
142
|
}
|
|
151
143
|
|
|
144
|
+
export const remote_module_pattern = /[/.]remote(\.[^/]+)+$/;
|
|
145
|
+
export const server_only_module_pattern = /[/.]server(\.[^/]+)+$/;
|
|
146
|
+
export const server_only_directory_pattern = /\/server\//;
|
|
147
|
+
|
|
152
148
|
export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
|
|
153
149
|
|
|
154
150
|
/**
|
|
@@ -187,3 +183,18 @@ export function error_for_missing_config(feature_name, path, value) {
|
|
|
187
183
|
`
|
|
188
184
|
);
|
|
189
185
|
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {number} status
|
|
189
|
+
* @param {Request} request
|
|
190
|
+
*/
|
|
191
|
+
export function log_response(status, request) {
|
|
192
|
+
const url = new URL(request.url);
|
|
193
|
+
const log = `[${status}] ${request.method} ${url.href.replace(url.origin, '')}`;
|
|
194
|
+
|
|
195
|
+
if (status < 400) {
|
|
196
|
+
console.log(log);
|
|
197
|
+
} else {
|
|
198
|
+
console.error(styleText(['bold', 'red'], log));
|
|
199
|
+
}
|
|
200
|
+
}
|
package/src/runner.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @import { ViteDevServer } from 'vite' */
|
|
2
|
+
import * as vite from 'vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {ViteDevServer} server
|
|
6
|
+
*/
|
|
7
|
+
export function get_runner(server) {
|
|
8
|
+
if (!vite.isRunnableDevEnvironment(server.environments.ssr)) {
|
|
9
|
+
throw new Error('The configured Vite SSR environment must be a RunnableDevEnvironment');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return server.environments.ssr.runner;
|
|
13
|
+
}
|
package/src/runtime/app/forms.js
CHANGED
|
@@ -4,6 +4,7 @@ import { noop } from '../../utils/functions.js';
|
|
|
4
4
|
import { refreshAll } from './navigation.js';
|
|
5
5
|
import { app as client_app, applyAction, handle_error } from '../client/client.js';
|
|
6
6
|
import { app as server_app } from '../server/app.js';
|
|
7
|
+
import { notify_version } from '../client/state.svelte.js';
|
|
7
8
|
|
|
8
9
|
export { applyAction };
|
|
9
10
|
|
|
@@ -201,6 +202,9 @@ export function enhance(form_element, submit = noop) {
|
|
|
201
202
|
signal: controller.signal
|
|
202
203
|
});
|
|
203
204
|
|
|
205
|
+
// detect new deployments from the response header
|
|
206
|
+
notify_version(response.headers.get('x-sveltekit-version'));
|
|
207
|
+
|
|
204
208
|
if (response.status === 204) {
|
|
205
209
|
result = { type: 'success', status: 204 };
|
|
206
210
|
} else {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '__sveltekit/manifest-data';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @import {
|
|
1
|
+
/** @import { AssetPath, RouteId, RouteIdWithSearchOrHash, Path, PathnameWithSearchOrHash, ResolvedPathname, RouteParams } from '$app/types' */
|
|
2
2
|
/** @import { ResolveArgs } from './types.js' */
|
|
3
|
-
import { base, assets, hash_routing } from './internal/client.js';
|
|
3
|
+
import { base, assets, hash_routing, match_implementation } from './internal/client.js';
|
|
4
4
|
import { resolve_route } from '../../../utils/routing.js';
|
|
5
|
-
import {
|
|
5
|
+
import { DEV } from 'esm-env';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Resolve the URL of an asset in your `static` directory, by prefixing it with [`config.paths.assets`](https://svelte.dev/docs/kit/configuration#paths) if configured, or otherwise by prefixing it with the base path.
|
|
@@ -15,15 +15,24 @@ import { get_navigation_intent } from '../../client/client.js';
|
|
|
15
15
|
* import { asset } from '$app/paths';
|
|
16
16
|
* </script>
|
|
17
17
|
*
|
|
18
|
-
* <img alt="a potato" src={asset('
|
|
18
|
+
* <img alt="a potato" src={asset('potato.jpg')} />
|
|
19
19
|
* ```
|
|
20
20
|
* @since 2.26
|
|
21
21
|
*
|
|
22
|
-
* @param {
|
|
22
|
+
* @param {AssetPath} file
|
|
23
23
|
* @returns {string}
|
|
24
24
|
*/
|
|
25
25
|
export function asset(file) {
|
|
26
|
-
|
|
26
|
+
// TODO 4.0 remove this
|
|
27
|
+
if (file[0] === '/') {
|
|
28
|
+
if (DEV) {
|
|
29
|
+
console.warn(`\`asset('${file}')\` should now be \`asset('${file.slice(1)}')\``);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
file = file.slice(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (assets || base) + '/' + file;
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
const pathname_prefix = hash_routing ? '#' : '';
|
|
@@ -38,7 +47,7 @@ const pathname_prefix = hash_routing ? '#' : '';
|
|
|
38
47
|
* import { resolve } from '$app/paths';
|
|
39
48
|
*
|
|
40
49
|
* // using a pathname
|
|
41
|
-
* const resolved = resolve(
|
|
50
|
+
* const resolved = resolve(`blog/hello-world`);
|
|
42
51
|
*
|
|
43
52
|
* // using a route ID plus parameters
|
|
44
53
|
* const resolved = resolve('/blog/[slug]', {
|
|
@@ -52,14 +61,18 @@ const pathname_prefix = hash_routing ? '#' : '';
|
|
|
52
61
|
* @returns {ResolvedPathname}
|
|
53
62
|
*/
|
|
54
63
|
export function resolve(...args) {
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
64
|
+
if (args[0][0] === '/') {
|
|
65
|
+
const [id, params] = args;
|
|
66
|
+
|
|
67
|
+
// route ID
|
|
68
|
+
if (id.includes('[') && !params) {
|
|
69
|
+
throw new Error(`Missing params for dynamic route ID ${id}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return base + pathname_prefix + resolve_route(args[0], args[1] ?? {});
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
return base + pathname_prefix +
|
|
75
|
+
return base + pathname_prefix + '/' + args[0];
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
/**
|
|
@@ -69,7 +82,7 @@ export function resolve(...args) {
|
|
|
69
82
|
* ```js
|
|
70
83
|
* import { match } from '$app/paths';
|
|
71
84
|
*
|
|
72
|
-
* const route = await match('
|
|
85
|
+
* const route = await match('blog/hello-world');
|
|
73
86
|
*
|
|
74
87
|
* if (route?.id === '/blog/[slug]') {
|
|
75
88
|
* const slug = route.params.slug;
|
|
@@ -79,22 +92,9 @@ export function resolve(...args) {
|
|
|
79
92
|
* ```
|
|
80
93
|
* @since 2.52.0
|
|
81
94
|
*
|
|
82
|
-
* @param {
|
|
95
|
+
* @param {Path | URL | (string & {})} url
|
|
83
96
|
* @returns {Promise<{ [K in RouteId]: { id: K; params: RouteParams<K>; } }[RouteId] | null>}
|
|
84
97
|
*/
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
url = new URL(url, location.href);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const intent = await get_navigation_intent(url, false);
|
|
91
|
-
|
|
92
|
-
if (intent) {
|
|
93
|
-
return {
|
|
94
|
-
id: /** @type {RouteId} */ (intent.route.id),
|
|
95
|
-
params: intent.params
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return null;
|
|
98
|
+
export function match(url) {
|
|
99
|
+
return match_implementation(url);
|
|
100
100
|
}
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
+
/** @import { RouteId, Path } from '$app/types' */
|
|
1
2
|
import { payload } from '../../../client/payload.js';
|
|
2
3
|
|
|
3
4
|
export const base = payload.base ?? __SVELTEKIT_PATHS_BASE__;
|
|
4
5
|
export const assets = payload.assets ?? base ?? __SVELTEKIT_PATHS_ASSETS__;
|
|
5
6
|
export const app_dir = __SVELTEKIT_APP_DIR__;
|
|
6
7
|
export const hash_routing = __SVELTEKIT_HASH_ROUTING__;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* We make this configurable per-environment so that it's possible to import `$app/paths`
|
|
11
|
+
* into a service worker without importing the entire client
|
|
12
|
+
* @param {Path | URL | (string & {})} _url
|
|
13
|
+
* @returns {Promise<{ [K in RouteId]: { id: K; params: import('$app/types').RouteParams<K>; } }[RouteId] | null>}
|
|
14
|
+
*/
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
16
|
+
export let match_implementation = async (_url) => {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
if (typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Cannot use `match(...)` inside a service worker, as it depends on the SvelteKit client instance'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {typeof match_implementation} fn
|
|
29
|
+
*/
|
|
30
|
+
export function set_match_implementation(fn) {
|
|
31
|
+
match_implementation = fn;
|
|
32
|
+
}
|
|
@@ -3,25 +3,38 @@ import { resolve_route, find_route } from '../../../utils/routing.js';
|
|
|
3
3
|
import { decode_pathname } from '../../../utils/url.js';
|
|
4
4
|
import { add_data_suffix } from '../../pathname.js';
|
|
5
5
|
import { try_get_request_store } from '@sveltejs/kit/internal/server';
|
|
6
|
-
import { manifest } from '
|
|
6
|
+
import { manifest } from '../../server/internal.js';
|
|
7
7
|
import { get_hooks } from '__SERVER__/internal.js';
|
|
8
|
+
import { DEV } from 'esm-env';
|
|
8
9
|
|
|
9
10
|
/** @type {import('./client.js').asset} */
|
|
10
11
|
export function asset(file) {
|
|
11
|
-
//
|
|
12
|
-
|
|
12
|
+
// TODO 4.0 remove this
|
|
13
|
+
if (file[0] === '/') {
|
|
14
|
+
if (DEV) {
|
|
15
|
+
console.warn(`\`asset('${file}')\` should now be \`asset('${file.slice(1)}')\``);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
file = file.slice(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return assets !== base ? `${assets}/${file}` : resolve(file);
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
/** @type {import('./client.js').resolve} */
|
|
16
25
|
export function resolve(id, params) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
26
|
+
let resolved;
|
|
27
|
+
|
|
28
|
+
if (id[0] === '/') {
|
|
29
|
+
// route ID
|
|
30
|
+
if (id.includes('[') && !params) {
|
|
31
|
+
throw new Error(`Missing params for dynamic route ID ${id}`);
|
|
32
|
+
}
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
resolved = resolve_route(id, params ?? {});
|
|
35
|
+
} else {
|
|
36
|
+
resolved = '/' + id;
|
|
37
|
+
}
|
|
25
38
|
|
|
26
39
|
if (relative) {
|
|
27
40
|
const store = try_get_request_store();
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PathnameWithSearchOrHash,
|
|
3
|
-
RouteId,
|
|
4
|
-
RouteIdWithSearchOrHash,
|
|
5
|
-
RouteParams
|
|
6
|
-
} from '$app/types';
|
|
1
|
+
import { RouteId, RouteParams } from '$app/types';
|
|
7
2
|
|
|
8
|
-
type StripSearchOrHash<T extends string> = T extends `${infer
|
|
9
|
-
?
|
|
10
|
-
: T extends `${infer
|
|
11
|
-
?
|
|
3
|
+
type StripSearchOrHash<T extends string> = T extends `${infer U}?${string}`
|
|
4
|
+
? U
|
|
5
|
+
: T extends `${infer U}#${string}`
|
|
6
|
+
? U
|
|
12
7
|
: T;
|
|
13
8
|
|
|
14
|
-
export type ResolveArgs<T
|
|
15
|
-
T extends RouteId
|
|
16
|
-
? RouteParams<
|
|
9
|
+
export type ResolveArgs<T> = T extends `/${string}`
|
|
10
|
+
? StripSearchOrHash<T> extends infer U extends RouteId
|
|
11
|
+
? RouteParams<U> extends Record<string, never>
|
|
17
12
|
? [route: T]
|
|
18
|
-
: [route: T, params: RouteParams<
|
|
19
|
-
:
|
|
20
|
-
|
|
21
|
-
? [route: T]
|
|
22
|
-
: [route: T, params: RouteParams<U>]
|
|
23
|
-
: [route: T];
|
|
13
|
+
: [route: T, params: RouteParams<U>]
|
|
14
|
+
: [never]
|
|
15
|
+
: [pathname: T];
|
|
@@ -65,16 +65,21 @@ export function command(validate_or_fn, maybe_fn) {
|
|
|
65
65
|
const wrapper = (arg) => {
|
|
66
66
|
const { event, state } = get_request_store();
|
|
67
67
|
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
if (
|
|
69
|
+
!MUTATIVE_METHODS.includes(event.request.method) ||
|
|
70
|
+
state.is_in_remote_query ||
|
|
71
|
+
state.is_in_remote_prerender
|
|
72
|
+
) {
|
|
73
|
+
const violation =
|
|
74
|
+
state.is_in_remote_query || state.is_in_remote_prerender
|
|
75
|
+
? `inside a query or prerender function`
|
|
76
|
+
: `from a ${event.request.method} handler`;
|
|
77
|
+
|
|
78
|
+
throw new Error(`Cannot call a command (${__.name}) ${violation}`);
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
if (state.is_in_render) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) during server-side rendering`
|
|
77
|
-
);
|
|
82
|
+
throw new Error(`Cannot call a command (${__.name}) during server-side rendering`);
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
const promise = Promise.resolve(
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
set_nested_value,
|
|
8
8
|
deep_set,
|
|
9
9
|
normalize_issue,
|
|
10
|
-
flatten_issues
|
|
10
|
+
flatten_issues,
|
|
11
|
+
parse_form_key
|
|
11
12
|
} from '../../../form-utils.js';
|
|
12
13
|
import { get_cache, get_implicit_lookup, run_remote_function } from './shared.js';
|
|
13
14
|
import { ValidationError } from '@sveltejs/kit/internal';
|
|
@@ -102,7 +103,7 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
if (validated?.issues !== undefined) {
|
|
105
|
-
handle_issues(output, validated.issues, form_data);
|
|
106
|
+
handle_issues(output, validated.issues, form_data, __.id);
|
|
106
107
|
} else {
|
|
107
108
|
if (validated !== undefined) {
|
|
108
109
|
data = validated.value;
|
|
@@ -120,7 +121,7 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
120
121
|
);
|
|
121
122
|
} catch (e) {
|
|
122
123
|
if (e instanceof ValidationError) {
|
|
123
|
-
handle_issues(output, e.issues, form_data);
|
|
124
|
+
handle_issues(output, e.issues, form_data, __.id);
|
|
124
125
|
} else {
|
|
125
126
|
throw e;
|
|
126
127
|
}
|
|
@@ -135,7 +136,7 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
135
136
|
|
|
136
137
|
// register under the client-side action id so the output is serialized
|
|
137
138
|
// into the page, allowing the hydrated client to restore `result`/`issues`/`input`
|
|
138
|
-
get_implicit_lookup(__, state)[__.
|
|
139
|
+
get_implicit_lookup(__, state)[__.key ? `${__.id}/${__.key}` : __.id] = () => cache[''];
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
return output;
|
|
@@ -145,7 +146,7 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
145
146
|
Object.defineProperty(instance, '__', { value: __ });
|
|
146
147
|
|
|
147
148
|
Object.defineProperty(instance, 'action', {
|
|
148
|
-
get: () => `?/remote=${__.id}`,
|
|
149
|
+
get: () => `?/remote=${__.key ? `${__.id}/${encodeURIComponent(__.key)}` : __.id}`,
|
|
149
150
|
enumerable: true
|
|
150
151
|
});
|
|
151
152
|
|
|
@@ -153,10 +154,10 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
153
154
|
get() {
|
|
154
155
|
// the form instance is created once per module and shared across requests,
|
|
155
156
|
// so the current request's state has to be resolved at access time
|
|
156
|
-
return create_field_proxy(
|
|
157
|
-
|
|
158
|
-
() => get_cache(__, get_request_store().state)?.['']?.input ?? {},
|
|
159
|
-
(path, value) => {
|
|
157
|
+
return create_field_proxy({
|
|
158
|
+
form_id: __.id,
|
|
159
|
+
get: () => get_cache(__, get_request_store().state)?.['']?.input ?? {},
|
|
160
|
+
set: (path, value) => {
|
|
160
161
|
const cache = get_cache(__, get_request_store().state);
|
|
161
162
|
const entry = cache[''];
|
|
162
163
|
|
|
@@ -174,11 +175,11 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
174
175
|
deep_set(input, path.map(String), value);
|
|
175
176
|
(cache[''] ??= {}).input = input;
|
|
176
177
|
},
|
|
177
|
-
() =>
|
|
178
|
-
|
|
179
|
-
() => ({}),
|
|
180
|
-
|
|
181
|
-
);
|
|
178
|
+
get_issues: () =>
|
|
179
|
+
flatten_issues(get_cache(__, get_request_store().state)?.['']?.issues ?? []),
|
|
180
|
+
get_touched: () => ({}),
|
|
181
|
+
get_dirty: () => ({})
|
|
182
|
+
});
|
|
182
183
|
}
|
|
183
184
|
});
|
|
184
185
|
|
|
@@ -228,12 +229,15 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
228
229
|
value: (key) => {
|
|
229
230
|
const { state } = get_request_store();
|
|
230
231
|
const cache_key = __.id + '|' + JSON.stringify(key);
|
|
232
|
+
/** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */
|
|
231
233
|
let instance = (state.remote.forms ??= new Map()).get(cache_key);
|
|
232
234
|
|
|
233
235
|
if (!instance) {
|
|
234
|
-
instance =
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
instance = /** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */ (
|
|
237
|
+
create_instance(key)
|
|
238
|
+
);
|
|
239
|
+
instance.__.id = __.id;
|
|
240
|
+
instance.__.key = JSON.stringify(key);
|
|
237
241
|
instance.__.name = __.name;
|
|
238
242
|
|
|
239
243
|
state.remote.forms.set(cache_key, instance);
|
|
@@ -254,8 +258,9 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
254
258
|
* @param {{ issues?: InternalRemoteFormIssue[], input?: Record<string, any>, result: any }} output
|
|
255
259
|
* @param {readonly StandardSchemaV1.Issue[]} issues
|
|
256
260
|
* @param {FormData | null} form_data - null if the form is progressively enhanced
|
|
261
|
+
* @param {string} form_id - hash/name of the form
|
|
257
262
|
*/
|
|
258
|
-
function handle_issues(output, issues, form_data) {
|
|
263
|
+
function handle_issues(output, issues, form_data, form_id) {
|
|
259
264
|
output.issues = issues.map((issue) => normalize_issue(issue, true));
|
|
260
265
|
|
|
261
266
|
// if it was a progressively-enhanced submission, we don't need
|
|
@@ -263,19 +268,17 @@ function handle_issues(output, issues, form_data) {
|
|
|
263
268
|
if (form_data) {
|
|
264
269
|
output.input = {};
|
|
265
270
|
|
|
266
|
-
for (
|
|
271
|
+
for (const field_name of form_data.keys()) {
|
|
267
272
|
// redact sensitive fields
|
|
268
|
-
if (/^[.\]]?_/.test(
|
|
269
|
-
|
|
270
|
-
const is_array = key.endsWith('[]');
|
|
271
|
-
const values = form_data.getAll(key).filter((value) => typeof value === 'string');
|
|
273
|
+
if (/^[.\]]?_/.test(field_name)) continue;
|
|
272
274
|
|
|
273
|
-
|
|
275
|
+
const values = form_data.getAll(field_name).filter((value) => typeof value === 'string');
|
|
276
|
+
const field = parse_form_key(form_id, field_name);
|
|
274
277
|
|
|
275
278
|
set_nested_value(
|
|
276
279
|
/** @type {Record<string, any>} */ (output.input),
|
|
277
|
-
|
|
278
|
-
is_array ? values : values[0]
|
|
280
|
+
field,
|
|
281
|
+
field.is_array ? values : values[0]
|
|
279
282
|
);
|
|
280
283
|
}
|
|
281
284
|
}
|
|
@@ -102,7 +102,8 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
102
102
|
try {
|
|
103
103
|
// TODO adapters can provide prerendered data more efficiently than
|
|
104
104
|
// fetching from the public internet
|
|
105
|
-
|
|
105
|
+
// `request.url` rather than `event.url`, which throws inside queries
|
|
106
|
+
const response = await fetch(new URL(url, event.request.url).href);
|
|
106
107
|
|
|
107
108
|
if (!response.ok) {
|
|
108
109
|
throw new Error('Prerendered response not found');
|
|
@@ -126,7 +127,13 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
126
127
|
return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
const promise = run_remote_function(
|
|
130
|
+
const promise = run_remote_function(
|
|
131
|
+
event,
|
|
132
|
+
{ ...state, is_in_remote_prerender: true },
|
|
133
|
+
false,
|
|
134
|
+
() => validate(arg),
|
|
135
|
+
fn
|
|
136
|
+
);
|
|
130
137
|
|
|
131
138
|
if (state.prerendering) {
|
|
132
139
|
state.prerendering.remote_responses.set(url, promise);
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from './shared.js';
|
|
14
14
|
import { noop } from '../../../../utils/functions.js';
|
|
15
15
|
import { SharedIterator } from '../../../../utils/shared-iterator.js';
|
|
16
|
-
import { handle_error_and_jsonify } from '../../../server/
|
|
16
|
+
import { handle_error_and_jsonify } from '../../../server/errors.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -404,17 +404,18 @@ export function refresh(event, state, internals, payload, fn) {
|
|
|
404
404
|
return;
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
if (!event.isRemoteRequest) {
|
|
408
|
-
// or this is a no-JS form submission
|
|
407
|
+
if (!event.isRemoteRequest && state.is_in_remote_form_or_command) {
|
|
408
|
+
// ...or this is a no-JS (native) form submission, where the page re-renders
|
|
409
|
+
// anyway so there's no live client cache to apply a single-flight update to.
|
|
409
410
|
return;
|
|
410
411
|
}
|
|
411
412
|
|
|
412
413
|
const key = create_remote_key(internals.id, payload);
|
|
413
414
|
|
|
414
415
|
// `fn` is stored rather than invoked eagerly. The query is run at the end of
|
|
415
|
-
// the
|
|
416
|
+
// the request (in `collect_remote_data`), so that it observes any state
|
|
416
417
|
// mutations that happen after `refresh()` is called. If the developer re-awaits
|
|
417
|
-
// the query before the
|
|
418
|
+
// the query before the request finishes, the cache entry created by that await
|
|
418
419
|
// is reused instead of re-running the query.
|
|
419
420
|
(state.remote.explicit ??= new Map()).set(key, {
|
|
420
421
|
internals,
|