@sveltejs/kit 3.0.0-next.11 → 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.
Files changed (61) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +1 -1
  4. package/src/core/config/options.js +43 -35
  5. package/src/core/env.js +25 -11
  6. package/src/core/sync/sync.js +11 -14
  7. package/src/core/sync/utils.js +21 -1
  8. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +23 -21
  9. package/src/core/sync/write_client_manifest.js +4 -12
  10. package/src/core/sync/write_env.js +6 -4
  11. package/src/core/sync/write_server.js +10 -15
  12. package/src/core/sync/write_tsconfig/index.js +245 -0
  13. package/src/core/sync/write_tsconfig/utils.js +162 -0
  14. package/src/core/sync/write_types/index.js +80 -88
  15. package/src/exports/internal/server/index.js +3 -1
  16. package/src/exports/public.d.ts +28 -51
  17. package/src/exports/vite/dev/index.js +88 -76
  18. package/src/exports/vite/index.js +364 -144
  19. package/src/exports/vite/module_ids.js +3 -1
  20. package/src/exports/vite/utils.js +1 -10
  21. package/src/runner.js +13 -0
  22. package/src/runtime/app/forms.js +4 -0
  23. package/src/runtime/app/manifest/index.js +1 -0
  24. package/src/runtime/app/paths/client.js +30 -30
  25. package/src/runtime/app/paths/internal/client.js +26 -0
  26. package/src/runtime/app/paths/server.js +22 -9
  27. package/src/runtime/app/paths/types.d.ts +11 -19
  28. package/src/runtime/app/server/remote/command.js +12 -7
  29. package/src/runtime/app/server/remote/form.js +29 -26
  30. package/src/runtime/app/server/remote/prerender.js +9 -2
  31. package/src/runtime/app/server/remote/query.js +5 -4
  32. package/src/runtime/app/server/remote/shared.js +48 -30
  33. package/src/runtime/app/service-worker/index.js +24 -0
  34. package/src/runtime/app/state/index.js +1 -1
  35. package/src/runtime/client/client.js +46 -9
  36. package/src/runtime/client/remote-functions/form.svelte.js +43 -51
  37. package/src/runtime/client/remote-functions/query/index.js +2 -2
  38. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -3
  39. package/src/runtime/client/remote-functions/query-live/iterator.js +5 -2
  40. package/src/runtime/client/remote-functions/shared.svelte.js +4 -1
  41. package/src/runtime/client/state.svelte.js +54 -21
  42. package/src/runtime/form-utils.js +89 -54
  43. package/src/runtime/server/cookie.js +1 -1
  44. package/src/runtime/server/data/index.js +31 -28
  45. package/src/runtime/server/errors.js +1 -1
  46. package/src/runtime/server/page/actions.js +3 -3
  47. package/src/runtime/server/page/render.js +19 -28
  48. package/src/runtime/server/remote-functions.js +66 -35
  49. package/src/runtime/server/respond.js +56 -14
  50. package/src/runtime/server/utils.js +10 -0
  51. package/src/types/ambient-private.d.ts +8 -0
  52. package/src/types/ambient.d.ts +22 -27
  53. package/src/types/global-private.d.ts +12 -0
  54. package/src/types/internal.d.ts +13 -3
  55. package/src/utils/url.js +12 -0
  56. package/src/version.js +1 -1
  57. package/types/index.d.ts +80 -98
  58. package/types/index.d.ts.map +4 -1
  59. package/src/core/sync/write_ambient.js +0 -18
  60. package/src/core/sync/write_tsconfig.js +0 -258
  61. /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
+ }
@@ -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 { Asset, RouteId, RouteIdWithSearchOrHash, Pathname, PathnameWithSearchOrHash, ResolvedPathname, RouteParams } from '$app/types' */
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 { get_navigation_intent } from '../../client/client.js';
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('/potato.jpg')} />
18
+ * <img alt="a potato" src={asset('potato.jpg')} />
19
19
  * ```
20
20
  * @since 2.26
21
21
  *
22
- * @param {Asset} file
22
+ * @param {AssetPath} file
23
23
  * @returns {string}
24
24
  */
25
25
  export function asset(file) {
26
- return (assets || base) + file;
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(`/blog/hello-world`);
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 (!args[0].startsWith('/')) {
56
- throw new Error(
57
- `Cannot use \`resolve(...)\` with a non-absolute pathname or route ID (got "${args[0]}"). ` +
58
- '`resolve` is only for internal pathnames and route IDs; external URLs should be used directly.'
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 + resolve_route(args[0], args[1] ?? {});
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('/blog/hello-world');
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 {Pathname | URL | (string & {})} url
95
+ * @param {Path | URL | (string & {})} url
83
96
  * @returns {Promise<{ [K in RouteId]: { id: K; params: RouteParams<K>; } }[RouteId] | null>}
84
97
  */
85
- export async function match(url) {
86
- if (typeof url === 'string') {
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
- // @ts-expect-error we use the `resolve` mechanism, but with the 'wrong' input
12
- return assets && assets !== base ? assets + file : resolve(file);
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
- if (!id.startsWith('/')) {
18
- throw new Error(
19
- `Cannot use \`resolve(...)\` with a non-absolute pathname or route ID (got "${id}"). ` +
20
- '`resolve` is only for internal pathnames and route IDs; external URLs should be used directly.'
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
- const resolved = resolve_route(id, params ?? {});
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 Pathname}?${string}`
9
- ? Pathname
10
- : T extends `${infer Pathname}#${string}`
11
- ? Pathname
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 extends RouteIdWithSearchOrHash | PathnameWithSearchOrHash> =
15
- T extends RouteId
16
- ? RouteParams<T> extends Record<string, never>
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<T>]
19
- : StripSearchOrHash<T> extends infer U extends RouteId
20
- ? RouteParams<U> extends Record<string, never>
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 (!MUTATIVE_METHODS.includes(event.request.method)) {
69
- throw new Error(
70
- `Cannot call a command (\`${__.name}(${maybe_fn ? '...' : ''})\`) from a ${event.request.method} handler`
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)[__.action_id ?? __.id] = () => cache[''];
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
- () => flatten_issues(get_cache(__, get_request_store().state)?.['']?.issues ?? []),
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 = create_instance(key);
235
- instance.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key))}`;
236
- instance.__.action_id = `${__.id}/${JSON.stringify(key)}`;
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 (let key of form_data.keys()) {
271
+ for (const field_name of form_data.keys()) {
267
272
  // redact sensitive fields
268
- if (/^[.\]]?_/.test(key)) continue;
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
- if (is_array) key = key.slice(0, -2);
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
- key,
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
- const response = await fetch(new URL(url, event.url.origin).href);
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(event, state, false, () => validate(arg), fn);
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);
@@ -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 command/form (in `collect_remote_data`), so that it observes any state
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 command finishes, the cache entry created by that await
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,
@@ -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
- return {
105
- event: {
106
- ...event,
107
- setHeaders: () => {
108
- throw new Error('setHeaders is not allowed in remote functions');
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
- 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);
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
  };
@@ -0,0 +1,24 @@
1
+ /// <reference no-default-lib="true"/>
2
+ /// <reference lib="esnext" />
3
+ /// <reference lib="webworker" />
4
+
5
+ import { DEV } from 'esm-env';
6
+
7
+ /**
8
+ * The execution context of a service worker. This export exists to make it easier to
9
+ * use service workers with the correct types, provided the importing module is governed
10
+ * by a `tsconfig.json` that extends [`$app/tsconfig/service-worker`](https://svelte.dev/docs/kit/$app-tsconfig-service-worker).
11
+ *
12
+ */
13
+ export const self = /** @type {ServiceWorkerGlobalScope} */ (
14
+ /** @type {unknown} */ (globalThis.self)
15
+ );
16
+
17
+ if (DEV) {
18
+ if (
19
+ typeof ServiceWorkerGlobalScope === 'undefined' ||
20
+ !(self instanceof ServiceWorkerGlobalScope)
21
+ ) {
22
+ throw new Error('The `$app/service-worker` module can only be imported into a service worker');
23
+ }
24
+ }
@@ -58,7 +58,7 @@ export const page = BROWSER ? client_page : server_page;
58
58
  export const navigating = BROWSER ? client_navigating : server_navigating;
59
59
 
60
60
  /**
61
- * A read-only reactive value that's initially `false`. If [`version.pollInterval`](https://svelte.dev/docs/kit/configuration#version) is a non-zero value, SvelteKit will poll for new versions of the app and update `current` to `true` when it detects one. `updated.check()` will force an immediate check, regardless of polling.
61
+ * A read-only reactive value that's initially `false`. SvelteKit checks for new versions on data, remote, and form action responses (via the `x-sveltekit-version` header), when the tab regains focus or becomes visible, and on a poll interval (see [`version.pollInterval`](https://svelte.dev/docs/kit/configuration#version)). `updated.current` is set to `true` when a new version is detected. `updated.check()` will force an immediate check, regardless of polling.
62
62
  * @type {{ get current(): boolean; check(): Promise<boolean>; }}
63
63
  */
64
64
  export const updated = BROWSER ? client_updated : server_updated;