@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.
Files changed (88) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +3 -1
  4. package/src/core/adapt/builder.js +3 -6
  5. package/src/core/config/index.js +6 -1
  6. package/src/core/config/options.js +54 -104
  7. package/src/core/env.js +25 -11
  8. package/src/core/postbuild/fallback.js +2 -1
  9. package/src/core/postbuild/prerender.js +75 -28
  10. package/src/core/postbuild/queue.js +2 -1
  11. package/src/core/sync/sync.js +11 -14
  12. package/src/core/sync/utils.js +21 -1
  13. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +23 -21
  14. package/src/core/sync/write_client_manifest.js +4 -12
  15. package/src/core/sync/write_env.js +6 -4
  16. package/src/core/sync/write_server.js +12 -17
  17. package/src/core/sync/write_tsconfig/index.js +245 -0
  18. package/src/core/sync/write_tsconfig/utils.js +162 -0
  19. package/src/core/sync/write_types/index.js +80 -88
  20. package/src/core/utils.js +28 -3
  21. package/src/exports/env/index.js +68 -3
  22. package/src/exports/internal/env.js +6 -3
  23. package/src/exports/internal/server/index.js +3 -1
  24. package/src/exports/params.js +9 -4
  25. package/src/exports/public.d.ts +69 -71
  26. package/src/exports/vite/dev/index.js +112 -56
  27. package/src/exports/vite/index.js +483 -258
  28. package/src/exports/vite/module_ids.js +2 -2
  29. package/src/exports/vite/preview/index.js +3 -2
  30. package/src/exports/vite/utils.js +21 -10
  31. package/src/runner.js +13 -0
  32. package/src/runtime/app/forms.js +4 -0
  33. package/src/runtime/app/manifest/index.js +1 -0
  34. package/src/runtime/app/paths/client.js +30 -30
  35. package/src/runtime/app/paths/internal/client.js +26 -0
  36. package/src/runtime/app/paths/server.js +23 -10
  37. package/src/runtime/app/paths/types.d.ts +11 -19
  38. package/src/runtime/app/server/index.js +1 -1
  39. package/src/runtime/app/server/remote/command.js +12 -7
  40. package/src/runtime/app/server/remote/form.js +29 -26
  41. package/src/runtime/app/server/remote/prerender.js +9 -2
  42. package/src/runtime/app/server/remote/query.js +6 -5
  43. package/src/runtime/app/server/remote/shared.js +48 -30
  44. package/src/runtime/app/service-worker/index.js +24 -0
  45. package/src/runtime/app/state/index.js +1 -1
  46. package/src/runtime/client/client.js +46 -9
  47. package/src/runtime/client/ndjson.js +1 -1
  48. package/src/runtime/client/remote-functions/form.svelte.js +43 -51
  49. package/src/runtime/client/remote-functions/query/index.js +2 -2
  50. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -3
  51. package/src/runtime/client/remote-functions/query-live/iterator.js +5 -2
  52. package/src/runtime/client/remote-functions/shared.svelte.js +4 -1
  53. package/src/runtime/client/state.svelte.js +54 -21
  54. package/src/runtime/client/stream.js +3 -2
  55. package/src/runtime/form-utils.js +89 -54
  56. package/src/runtime/server/cookie.js +1 -1
  57. package/src/runtime/server/data/index.js +32 -29
  58. package/src/runtime/server/errors.js +135 -0
  59. package/src/runtime/server/fetch.js +1 -1
  60. package/src/runtime/server/index.js +20 -10
  61. package/src/runtime/server/internal.js +25 -0
  62. package/src/runtime/server/page/actions.js +4 -3
  63. package/src/runtime/server/page/data_serializer.js +10 -10
  64. package/src/runtime/server/page/index.js +3 -2
  65. package/src/runtime/server/page/load_data.js +1 -1
  66. package/src/runtime/server/page/render.js +22 -35
  67. package/src/runtime/server/page/respond_with_error.js +6 -5
  68. package/src/runtime/server/{remote.js → remote-functions.js} +67 -36
  69. package/src/runtime/server/respond.js +58 -20
  70. package/src/runtime/server/sourcemaps.js +183 -0
  71. package/src/runtime/server/utils.js +12 -157
  72. package/src/types/ambient-private.d.ts +10 -0
  73. package/src/types/ambient.d.ts +22 -27
  74. package/src/types/global-private.d.ts +12 -5
  75. package/src/types/internal.d.ts +19 -9
  76. package/src/types/private.d.ts +8 -0
  77. package/src/utils/escape.js +9 -25
  78. package/src/utils/features.js +1 -1
  79. package/src/utils/fork.js +7 -2
  80. package/src/utils/page_nodes.js +3 -5
  81. package/src/utils/url.js +12 -0
  82. package/src/version.js +1 -1
  83. package/types/index.d.ts +152 -120
  84. package/types/index.d.ts.map +6 -1
  85. package/src/core/sync/write_ambient.js +0 -18
  86. package/src/core/sync/write_tsconfig.js +0 -258
  87. package/src/runtime/shared-server.js +0 -7
  88. /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 sveltekit_server = '\0virtual:__sveltekit/server';
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 new Error(`Server files not found at ${dir}, did you run \`build\` first?`);
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
- 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';
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
+ }
@@ -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
+ }
@@ -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 '__sveltekit/server';
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];
@@ -1,4 +1,4 @@
1
- import { read_implementation, manifest } from '__sveltekit/server';
1
+ import { read_implementation, manifest } from '../../server/internal.js';
2
2
  import { assets } from '$app/paths/internal/server';
3
3
  import { base64_decode } from '../../utils.js';
4
4
 
@@ -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);
@@ -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/utils.js';
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 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,