@sveltejs/kit 3.0.0-next.11 → 3.0.0-next.13
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 +10 -10
- package/src/cli.js +8 -8
- package/src/constants.js +1 -1
- package/src/core/config/options.js +43 -35
- package/src/core/env.js +25 -11
- package/src/core/postbuild/prerender.js +3 -3
- 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} +24 -23
- 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 +10 -15
- package/src/core/sync/write_tsconfig/index.js +257 -0
- package/src/core/sync/write_tsconfig/utils.js +162 -0
- package/src/core/sync/write_types/index.js +80 -88
- package/src/exports/index.js +23 -12
- package/src/exports/internal/server/index.js +3 -1
- package/src/exports/internal/shared.js +4 -12
- package/src/exports/public.d.ts +87 -62
- package/src/exports/vite/build/remote.js +10 -12
- package/src/exports/vite/dev/index.js +113 -88
- package/src/exports/vite/index.js +452 -237
- package/src/exports/vite/module_ids.js +3 -1
- package/src/exports/vite/utils.js +1 -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 +22 -9
- package/src/runtime/app/paths/types.d.ts +11 -19
- package/src/runtime/app/server/remote/command.js +12 -7
- package/src/runtime/app/server/remote/form.js +37 -26
- package/src/runtime/app/server/remote/prerender.js +22 -11
- package/src/runtime/app/server/remote/query.js +5 -4
- package/src/runtime/app/server/remote/requested.js +4 -4
- 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/client.js +3 -0
- package/src/runtime/app/state/index.js +3 -3
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/client/client.js +627 -311
- package/src/runtime/client/constants.js +2 -6
- package/src/runtime/client/fetcher.js +27 -17
- package/src/runtime/client/remote-functions/form.svelte.js +82 -58
- package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query/index.js +2 -2
- package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +3 -4
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query-live/iterator.js +15 -10
- package/src/runtime/client/remote-functions/shared.svelte.js +15 -11
- package/src/runtime/client/state.svelte.js +54 -33
- package/src/runtime/client/types.d.ts +1 -2
- package/src/runtime/client/utils.js +21 -14
- package/src/runtime/components/root.svelte +4 -14
- package/src/runtime/form-utils.js +89 -54
- package/src/runtime/props.svelte.js +71 -0
- package/src/runtime/server/cookie.js +1 -1
- package/src/runtime/server/data/index.js +31 -28
- package/src/runtime/server/errors.js +1 -1
- package/src/runtime/server/fetch.js +4 -6
- package/src/runtime/server/page/actions.js +3 -3
- package/src/runtime/server/page/csp.js +86 -103
- package/src/runtime/server/page/index.js +1 -2
- package/src/runtime/server/page/load_data.js +15 -5
- package/src/runtime/server/page/render.js +46 -52
- package/src/runtime/server/page/respond_with_error.js +1 -3
- package/src/runtime/server/remote-functions.js +66 -35
- package/src/runtime/server/respond.js +56 -16
- package/src/runtime/server/utils.js +10 -0
- package/src/types/ambient-private.d.ts +9 -1
- package/src/types/ambient.d.ts +23 -28
- package/src/types/global-private.d.ts +12 -0
- package/src/types/internal.d.ts +15 -10
- package/src/types/private.d.ts +3 -12
- package/src/utils/url.js +12 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +172 -155
- package/types/index.d.ts.map +5 -1
- package/src/core/sync/write_ambient.js +0 -18
- package/src/core/sync/write_tsconfig.js +0 -258
- package/src/runtime/types.d.ts +0 -8
- /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
|
@@ -6,9 +6,11 @@ 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 sveltekit_manifest_data = '\0virtual:__sveltekit/manifest-data';
|
|
13
|
+
|
|
12
14
|
export const app_server = posixify(
|
|
13
15
|
fileURLToPath(new URL('../../runtime/app/server/index.js', import.meta.url))
|
|
14
16
|
);
|
|
@@ -4,12 +4,7 @@ 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
|
-
app_server,
|
|
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';
|
|
13
8
|
import { styleText } from 'node:util';
|
|
14
9
|
|
|
15
10
|
/**
|
|
@@ -143,10 +138,6 @@ export function normalize_id(id, aliases, cwd) {
|
|
|
143
138
|
return '$app/env/private';
|
|
144
139
|
}
|
|
145
140
|
|
|
146
|
-
if (id === service_worker) {
|
|
147
|
-
return '$service-worker';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
141
|
return posixify(id);
|
|
151
142
|
}
|
|
152
143
|
|
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
|
+
}
|
|
@@ -5,23 +5,36 @@ import { add_data_suffix } from '../../pathname.js';
|
|
|
5
5
|
import { try_get_request_store } from '@sveltejs/kit/internal/server';
|
|
6
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,15 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
145
146
|
Object.defineProperty(instance, '__', { value: __ });
|
|
146
147
|
|
|
147
148
|
Object.defineProperty(instance, 'action', {
|
|
148
|
-
get: () =>
|
|
149
|
+
get: () => {
|
|
150
|
+
const search = new URLSearchParams(get_request_store().event.url.search);
|
|
151
|
+
search.delete('/remote');
|
|
152
|
+
|
|
153
|
+
const query = search.toString();
|
|
154
|
+
const action_id = __.key ? `${__.id}/${encodeURIComponent(__.key)}` : __.id;
|
|
155
|
+
|
|
156
|
+
return `?${query ? `${query}&` : ''}/remote=${action_id}`;
|
|
157
|
+
},
|
|
149
158
|
enumerable: true
|
|
150
159
|
});
|
|
151
160
|
|
|
@@ -153,10 +162,10 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
153
162
|
get() {
|
|
154
163
|
// the form instance is created once per module and shared across requests,
|
|
155
164
|
// 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) => {
|
|
165
|
+
return create_field_proxy({
|
|
166
|
+
form_id: __.id,
|
|
167
|
+
get: () => get_cache(__, get_request_store().state)?.['']?.input ?? {},
|
|
168
|
+
set: (path, value) => {
|
|
160
169
|
const cache = get_cache(__, get_request_store().state);
|
|
161
170
|
const entry = cache[''];
|
|
162
171
|
|
|
@@ -174,11 +183,11 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
174
183
|
deep_set(input, path.map(String), value);
|
|
175
184
|
(cache[''] ??= {}).input = input;
|
|
176
185
|
},
|
|
177
|
-
() =>
|
|
178
|
-
|
|
179
|
-
() => ({}),
|
|
180
|
-
|
|
181
|
-
);
|
|
186
|
+
get_issues: () =>
|
|
187
|
+
flatten_issues(get_cache(__, get_request_store().state)?.['']?.issues ?? []),
|
|
188
|
+
get_touched: () => ({}),
|
|
189
|
+
get_dirty: () => ({})
|
|
190
|
+
});
|
|
182
191
|
}
|
|
183
192
|
});
|
|
184
193
|
|
|
@@ -228,12 +237,15 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
228
237
|
value: (key) => {
|
|
229
238
|
const { state } = get_request_store();
|
|
230
239
|
const cache_key = __.id + '|' + JSON.stringify(key);
|
|
240
|
+
/** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */
|
|
231
241
|
let instance = (state.remote.forms ??= new Map()).get(cache_key);
|
|
232
242
|
|
|
233
243
|
if (!instance) {
|
|
234
|
-
instance =
|
|
235
|
-
|
|
236
|
-
|
|
244
|
+
instance = /** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */ (
|
|
245
|
+
create_instance(key)
|
|
246
|
+
);
|
|
247
|
+
instance.__.id = __.id;
|
|
248
|
+
instance.__.key = JSON.stringify(key);
|
|
237
249
|
instance.__.name = __.name;
|
|
238
250
|
|
|
239
251
|
state.remote.forms.set(cache_key, instance);
|
|
@@ -254,8 +266,9 @@ export function form(validate_or_fn, maybe_fn) {
|
|
|
254
266
|
* @param {{ issues?: InternalRemoteFormIssue[], input?: Record<string, any>, result: any }} output
|
|
255
267
|
* @param {readonly StandardSchemaV1.Issue[]} issues
|
|
256
268
|
* @param {FormData | null} form_data - null if the form is progressively enhanced
|
|
269
|
+
* @param {string} form_id - hash/name of the form
|
|
257
270
|
*/
|
|
258
|
-
function handle_issues(output, issues, form_data) {
|
|
271
|
+
function handle_issues(output, issues, form_data, form_id) {
|
|
259
272
|
output.issues = issues.map((issue) => normalize_issue(issue, true));
|
|
260
273
|
|
|
261
274
|
// if it was a progressively-enhanced submission, we don't need
|
|
@@ -263,19 +276,17 @@ function handle_issues(output, issues, form_data) {
|
|
|
263
276
|
if (form_data) {
|
|
264
277
|
output.input = {};
|
|
265
278
|
|
|
266
|
-
for (
|
|
279
|
+
for (const field_name of form_data.keys()) {
|
|
267
280
|
// 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');
|
|
281
|
+
if (/^[.\]]?_/.test(field_name)) continue;
|
|
272
282
|
|
|
273
|
-
|
|
283
|
+
const values = form_data.getAll(field_name).filter((value) => typeof value === 'string');
|
|
284
|
+
const field = parse_form_key(form_id, field_name);
|
|
274
285
|
|
|
275
286
|
set_nested_value(
|
|
276
287
|
/** @type {Record<string, any>} */ (output.input),
|
|
277
|
-
|
|
278
|
-
is_array ? values : values[0]
|
|
288
|
+
field,
|
|
289
|
+
field.is_array ? values : values[0]
|
|
279
290
|
);
|
|
280
291
|
}
|
|
281
292
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { RemotePrerenderInputsGenerator, RemotePrerenderInternals, MaybePromise } from 'types' */
|
|
2
|
+
/** @import { RemoteFunctionResponse, RemotePrerenderInputsGenerator, RemotePrerenderInternals, MaybePromise } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
|
-
import { json
|
|
4
|
+
import { json } from '@sveltejs/kit';
|
|
5
|
+
import { HttpError } from '@sveltejs/kit/internal';
|
|
5
6
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
6
7
|
import { stringify, stringify_remote_arg } from '../../../shared.js';
|
|
7
8
|
import { noop } from '../../../../utils/functions.js';
|
|
@@ -99,24 +100,28 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
99
100
|
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
|
|
100
101
|
|
|
101
102
|
if (!state.prerendering && !__SVELTEKIT_DEV__ && !event.isRemoteRequest) {
|
|
103
|
+
/** @type {RemoteFunctionResponse | undefined} */
|
|
104
|
+
let prerendered;
|
|
105
|
+
|
|
102
106
|
try {
|
|
103
107
|
// TODO adapters can provide prerendered data more efficiently than
|
|
104
108
|
// fetching from the public internet
|
|
105
|
-
|
|
109
|
+
// `request.url` rather than `event.url`, which throws inside queries
|
|
110
|
+
const response = await fetch(new URL(url, event.request.url).href);
|
|
106
111
|
|
|
107
|
-
if (
|
|
108
|
-
|
|
112
|
+
if (response.ok) {
|
|
113
|
+
prerendered = /** @type {RemoteFunctionResponse} */ (await response.json());
|
|
109
114
|
}
|
|
115
|
+
} catch {
|
|
116
|
+
// not available prerendered, fallback to normal function
|
|
117
|
+
}
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
if (prerendered) {
|
|
113
120
|
if (prerendered.type === 'error') {
|
|
114
|
-
|
|
121
|
+
throw new HttpError(prerendered.error);
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
return parse_remote_response(prerendered.data, state.transport)._;
|
|
118
|
-
} catch {
|
|
119
|
-
// not available prerendered, fallback to normal function
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
|
|
@@ -126,7 +131,13 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
126
131
|
return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
const promise = run_remote_function(
|
|
134
|
+
const promise = run_remote_function(
|
|
135
|
+
event,
|
|
136
|
+
{ ...state, is_in_remote_prerender: true },
|
|
137
|
+
false,
|
|
138
|
+
() => validate(arg),
|
|
139
|
+
fn
|
|
140
|
+
);
|
|
130
141
|
|
|
131
142
|
if (state.prerendering) {
|
|
132
143
|
state.prerendering.remote_responses.set(url, promise);
|
|
@@ -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,
|
|
@@ -152,10 +152,10 @@ export function requested(query, limit) {
|
|
|
152
152
|
for (const payload of skipped) {
|
|
153
153
|
record_failure(
|
|
154
154
|
payload,
|
|
155
|
-
new HttpError(
|
|
156
|
-
400,
|
|
157
|
-
`Requested refresh was rejected because it exceeded requested(${__.name}, ${limit}) limit`
|
|
158
|
-
)
|
|
155
|
+
new HttpError({
|
|
156
|
+
status: 400,
|
|
157
|
+
message: `Requested refresh was rejected because it exceeded requested(${__.name}, ${limit}) limit`
|
|
158
|
+
})
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -37,7 +37,7 @@ export function create_validator(validate_or_fn, maybe_fn) {
|
|
|
37
37
|
if (result.issues) {
|
|
38
38
|
const body = await state.handleValidationError({
|
|
39
39
|
issues: result.issues,
|
|
40
|
-
event
|
|
40
|
+
event: state.original_event ?? event
|
|
41
41
|
});
|
|
42
42
|
error(body.status ?? 400, body);
|
|
43
43
|
}
|
|
@@ -101,40 +101,58 @@ export function parse_remote_response(data, transport) {
|
|
|
101
101
|
* @returns {RequestStore}
|
|
102
102
|
*/
|
|
103
103
|
function derive_remote_function_event(event, state, allow_cookies) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
/** @type {RequestEvent} */
|
|
105
|
+
const derived = {
|
|
106
|
+
...event,
|
|
107
|
+
setHeaders: () => {
|
|
108
|
+
throw new Error('setHeaders is not allowed in remote functions');
|
|
109
|
+
},
|
|
110
|
+
cookies: {
|
|
111
|
+
...event.cookies,
|
|
112
|
+
set: (name, value, opts) => {
|
|
113
|
+
if (!allow_cookies) {
|
|
114
|
+
throw new Error('Cannot set cookies in `query` or `prerender` functions');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (opts.path && !opts.path.startsWith('/')) {
|
|
118
|
+
throw new Error('Cookies set in remote functions must have an absolute path');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return event.cookies.set(name, value, opts);
|
|
109
122
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (opts.path && !opts.path.startsWith('/')) {
|
|
118
|
-
throw new Error('Cookies set in remote functions must have an absolute path');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return event.cookies.set(name, value, opts);
|
|
122
|
-
},
|
|
123
|
-
delete: (name, opts) => {
|
|
124
|
-
if (!allow_cookies) {
|
|
125
|
-
throw new Error('Cannot delete cookies in `query` or `prerender` functions');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (opts.path && !opts.path.startsWith('/')) {
|
|
129
|
-
throw new Error('Cookies deleted in remote functions must have an absolute path');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return event.cookies.delete(name, opts);
|
|
123
|
+
delete: (name, opts) => {
|
|
124
|
+
if (!allow_cookies) {
|
|
125
|
+
throw new Error('Cannot delete cookies in `query` or `prerender` functions');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (opts.path && !opts.path.startsWith('/')) {
|
|
129
|
+
throw new Error('Cookies deleted in remote functions must have an absolute path');
|
|
133
130
|
}
|
|
131
|
+
|
|
132
|
+
return event.cookies.delete(name, opts);
|
|
134
133
|
}
|
|
135
|
-
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (state.is_in_remote_query) {
|
|
138
|
+
for (const property of ['url', 'params', 'route']) {
|
|
139
|
+
// non-enumerable so spreading for a nested derivation doesn't invoke the getter
|
|
140
|
+
Object.defineProperty(derived, property, {
|
|
141
|
+
enumerable: false,
|
|
142
|
+
get() {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Cannot access event.${property} in a query. Pass the value as an argument to the query instead`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
event: derived,
|
|
136
153
|
state: {
|
|
137
154
|
...state,
|
|
155
|
+
original_event: state.original_event ?? event,
|
|
138
156
|
is_in_remote_function: true
|
|
139
157
|
}
|
|
140
158
|
};
|