@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.21

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 (195) hide show
  1. package/package.json +62 -36
  2. package/src/cli.js +15 -12
  3. package/src/constants.js +6 -1
  4. package/src/core/adapt/builder.js +66 -72
  5. package/src/core/adapt/index.js +17 -6
  6. package/src/core/config/index.js +134 -80
  7. package/src/core/config/options.js +295 -273
  8. package/src/core/config/types.d.ts +1 -1
  9. package/src/core/env.js +146 -16
  10. package/src/core/generate_manifest/index.js +18 -15
  11. package/src/core/postbuild/analyse.js +8 -20
  12. package/src/core/postbuild/crawl.js +22 -6
  13. package/src/core/postbuild/entities.js +8 -2
  14. package/src/core/postbuild/fallback.js +4 -2
  15. package/src/core/postbuild/prerender.js +222 -70
  16. package/src/core/postbuild/queue.js +2 -1
  17. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  18. package/src/core/sync/create_manifest_data/index.js +82 -62
  19. package/src/core/sync/sync.js +40 -31
  20. package/src/core/sync/ts.js +1 -1
  21. package/src/core/sync/utils.js +22 -2
  22. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
  23. package/src/core/sync/write_client_manifest.js +14 -29
  24. package/src/core/sync/write_env.js +7 -5
  25. package/src/core/sync/write_server.js +37 -45
  26. package/src/core/sync/write_tsconfig/index.js +274 -0
  27. package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
  28. package/src/core/sync/write_tsconfig/utils.js +77 -0
  29. package/src/core/sync/write_tsconfig/validate.js +128 -0
  30. package/src/core/sync/write_types/index.js +127 -123
  31. package/src/core/utils.js +30 -5
  32. package/src/exports/env/index.js +77 -0
  33. package/src/exports/env/public.d.ts +55 -0
  34. package/src/exports/hooks/index.js +3 -9
  35. package/src/exports/hooks/public.d.ts +195 -0
  36. package/src/exports/hooks/sequence.js +9 -6
  37. package/src/exports/index.js +52 -29
  38. package/src/exports/internal/client.js +5 -0
  39. package/src/exports/internal/env.js +8 -5
  40. package/src/exports/internal/index.js +1 -90
  41. package/src/exports/internal/{event.js → server/event.js} +2 -3
  42. package/src/exports/internal/server/index.js +37 -0
  43. package/src/exports/internal/server/telemetry.js +95 -0
  44. package/src/exports/internal/shared.js +90 -0
  45. package/src/exports/node/index.js +64 -22
  46. package/src/exports/params/index.js +70 -0
  47. package/src/exports/params/public.d.ts +63 -0
  48. package/src/exports/public.d.ts +50 -1733
  49. package/src/exports/remote/index.js +11 -0
  50. package/src/exports/remote/public.d.ts +519 -0
  51. package/src/exports/url.js +86 -0
  52. package/src/exports/vite/build/build_server.js +54 -65
  53. package/src/exports/vite/build/remote.js +24 -19
  54. package/src/exports/vite/build/utils.js +0 -8
  55. package/src/exports/vite/dev/index.js +218 -146
  56. package/src/exports/vite/index.js +1590 -858
  57. package/src/exports/vite/module_ids.js +2 -2
  58. package/src/exports/vite/preview/index.js +40 -32
  59. package/src/exports/vite/public.d.ts +588 -0
  60. package/src/exports/vite/utils.js +84 -46
  61. package/src/pathname.js +55 -0
  62. package/src/runner.js +15 -0
  63. package/src/runtime/app/env/internal.js +4 -4
  64. package/src/runtime/app/env/types.d.ts +1 -1
  65. package/src/runtime/app/environment/index.js +3 -3
  66. package/src/runtime/app/{forms.js → forms/index.js} +73 -42
  67. package/src/runtime/app/forms/public.d.ts +2 -0
  68. package/src/runtime/app/forms/types.d.ts +56 -0
  69. package/src/runtime/app/internal/transport.js +53 -0
  70. package/src/runtime/app/manifest/index.js +1 -0
  71. package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
  72. package/src/runtime/app/navigation/public.d.ts +237 -0
  73. package/src/runtime/app/paths/client.js +37 -37
  74. package/src/runtime/app/paths/index.js +1 -1
  75. package/src/runtime/app/paths/internal/client.js +34 -2
  76. package/src/runtime/app/paths/internal/server.js +6 -23
  77. package/src/runtime/app/paths/internal.d.ts +3 -0
  78. package/src/runtime/app/paths/public.d.ts +1 -29
  79. package/src/runtime/app/paths/server.js +36 -17
  80. package/src/runtime/app/paths/types.d.ts +11 -19
  81. package/src/runtime/app/server/index.js +2 -2
  82. package/src/runtime/app/server/public.d.ts +201 -0
  83. package/src/runtime/app/server/remote/command.js +13 -11
  84. package/src/runtime/app/server/remote/form.js +61 -39
  85. package/src/runtime/app/server/remote/prerender.js +45 -45
  86. package/src/runtime/app/server/remote/query.js +106 -108
  87. package/src/runtime/app/server/remote/requested.js +27 -19
  88. package/src/runtime/app/server/remote/shared.js +76 -76
  89. package/src/runtime/app/service-worker/index.js +24 -0
  90. package/src/runtime/app/state/client.js +4 -2
  91. package/src/runtime/app/state/index.js +7 -5
  92. package/src/runtime/app/state/public.d.ts +72 -0
  93. package/src/runtime/app/state/server.js +3 -0
  94. package/src/runtime/app/stores.js +15 -78
  95. package/src/runtime/client/bundle.js +1 -1
  96. package/src/runtime/client/client-entry.js +3 -0
  97. package/src/runtime/client/client.js +1336 -648
  98. package/src/runtime/client/constants.js +3 -6
  99. package/src/runtime/client/entry.js +24 -3
  100. package/src/runtime/client/fetcher.js +25 -25
  101. package/src/runtime/client/ndjson.js +1 -1
  102. package/src/runtime/client/parse.js +1 -1
  103. package/src/runtime/client/payload.js +17 -0
  104. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  105. package/src/runtime/client/remote-functions/command.svelte.js +37 -61
  106. package/src/runtime/client/remote-functions/form.svelte.js +267 -178
  107. package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
  108. package/src/runtime/client/remote-functions/query/index.js +10 -17
  109. package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
  110. package/src/runtime/client/remote-functions/query/proxy.js +5 -5
  111. package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
  112. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  113. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
  114. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
  115. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  116. package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
  117. package/src/runtime/client/snapshots.js +147 -0
  118. package/src/runtime/client/state.svelte.js +94 -55
  119. package/src/runtime/client/stream.js +3 -2
  120. package/src/runtime/client/types.d.ts +13 -9
  121. package/src/runtime/client/utils.js +22 -110
  122. package/src/runtime/components/root.svelte +56 -0
  123. package/src/runtime/env/dynamic/private.js +7 -0
  124. package/src/runtime/env/dynamic/public.js +7 -0
  125. package/src/runtime/env/static/private.js +6 -0
  126. package/src/runtime/env/static/public.js +6 -0
  127. package/src/runtime/form-utils.js +172 -61
  128. package/src/runtime/pathname.js +20 -32
  129. package/src/runtime/props.svelte.js +72 -0
  130. package/src/runtime/server/constants.js +0 -3
  131. package/src/runtime/server/cookie.js +72 -55
  132. package/src/runtime/server/csrf.js +65 -0
  133. package/src/runtime/server/data/index.js +49 -53
  134. package/src/runtime/server/dev.js +22 -0
  135. package/src/runtime/server/endpoint.js +6 -7
  136. package/src/runtime/server/env_module.js +0 -5
  137. package/src/runtime/server/errors.js +160 -0
  138. package/src/runtime/server/fetch.js +32 -39
  139. package/src/runtime/server/index.js +90 -64
  140. package/src/runtime/server/internal.js +71 -0
  141. package/src/runtime/server/page/actions.js +85 -64
  142. package/src/runtime/server/page/crypto.js +2 -2
  143. package/src/runtime/server/page/csp.js +88 -104
  144. package/src/runtime/server/page/data_serializer.js +24 -25
  145. package/src/runtime/server/page/index.js +36 -53
  146. package/src/runtime/server/page/load_data.js +50 -57
  147. package/src/runtime/server/page/render.js +173 -239
  148. package/src/runtime/server/page/respond_with_error.js +17 -31
  149. package/src/runtime/server/page/serialize_data.js +2 -13
  150. package/src/runtime/server/page/server_routing.js +85 -26
  151. package/src/runtime/server/remote-functions.js +640 -0
  152. package/src/runtime/server/respond.js +190 -131
  153. package/src/runtime/server/sourcemaps.js +183 -0
  154. package/src/runtime/server/state.js +53 -0
  155. package/src/runtime/server/utils.js +13 -155
  156. package/src/runtime/shared.js +20 -40
  157. package/src/runtime/utils.js +3 -0
  158. package/src/types/ambient-private.d.ts +11 -1
  159. package/src/types/ambient.d.ts +87 -36
  160. package/src/types/global-private.d.ts +25 -25
  161. package/src/types/internal.d.ts +163 -157
  162. package/src/types/private.d.ts +41 -1
  163. package/src/utils/error.js +28 -4
  164. package/src/utils/escape.js +9 -25
  165. package/src/utils/features.js +1 -1
  166. package/src/utils/filesystem.js +1 -23
  167. package/src/utils/fork.js +7 -2
  168. package/src/utils/hash.js +21 -0
  169. package/src/utils/http.js +8 -7
  170. package/src/utils/import.js +9 -2
  171. package/src/utils/imports.js +83 -0
  172. package/src/utils/mime.js +9 -0
  173. package/src/utils/page_nodes.js +6 -7
  174. package/src/utils/params.js +67 -0
  175. package/src/utils/regex.js +9 -0
  176. package/src/utils/routing.js +145 -73
  177. package/src/utils/shared-iterator.js +5 -0
  178. package/src/utils/streaming.js +14 -4
  179. package/src/utils/url.js +33 -2
  180. package/src/version.js +1 -1
  181. package/types/index.d.ts +2616 -2433
  182. package/types/index.d.ts.map +120 -106
  183. package/src/core/sync/write_ambient.js +0 -18
  184. package/src/core/sync/write_root.js +0 -148
  185. package/src/core/sync/write_tsconfig.js +0 -250
  186. package/src/exports/internal/server.js +0 -22
  187. package/src/exports/node/polyfills.js +0 -30
  188. package/src/runtime/server/app.js +0 -9
  189. package/src/runtime/server/remote.js +0 -457
  190. package/src/runtime/shared-server.js +0 -7
  191. package/src/runtime/telemetry/otel.js +0 -21
  192. package/src/runtime/telemetry/record_span.js +0 -65
  193. package/src/types/synthetic/$lib.md +0 -5
  194. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
  195. /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
@@ -0,0 +1,201 @@
1
+ import { RouteId as AppRouteId, LayoutParams as AppLayoutParams } from '$app/types';
2
+
3
+ export * from './index.js';
4
+
5
+ // @ts-ignore this is an optional peer dependency so could be missing. Written like this so dts-buddy preserves the ts-ignore
6
+ type Span = import('@opentelemetry/api').Span;
7
+
8
+ export interface Cookies {
9
+ /**
10
+ * Gets a cookie that was previously set with `cookies.set`, or from the request headers.
11
+ * @param name the name of the cookie
12
+ * @param opts the options, passed directly to `cookie.parseCookie`. See documentation [here](https://github.com/jshttp/cookie?tab=readme-ov-file#cookieparsecookiestr-options)
13
+ */
14
+ get: (name: string, opts?: import('cookie').ParseOptions) => string | undefined;
15
+
16
+ /**
17
+ * Gets all cookies that were previously set with `cookies.set`, or from the request headers.
18
+ * @param opts the options, passed directly to `cookie.parseCookie`. See documentation [here](https://github.com/jshttp/cookie?tab=readme-ov-file#cookieparsecookiestr-options)
19
+ */
20
+ getAll: (opts?: import('cookie').ParseOptions) => Array<{ name: string; value: string }>;
21
+
22
+ /**
23
+ * Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` or `cookies.getAll` during the current request.
24
+ *
25
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
26
+ *
27
+ * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
28
+ * @param name the name of the cookie
29
+ * @param value the cookie value
30
+ * @param opts the options passed to `cookie.stringifySetCookie` with the SvelteKit defaults described above. See documentation [here](https://github.com/jshttp/cookie?tab=readme-ov-file#cookiestringifysetcookiesetcookieobj-options)
31
+ */
32
+ set: (name: string, value: string, opts: import('cookie').SerializeOptions) => void;
33
+
34
+ /**
35
+ * Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
36
+ *
37
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
38
+ *
39
+ * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
40
+ * @param name the name of the cookie
41
+ * @param opts the options passed to `cookie.stringifySetCookie` with the SvelteKit defaults described above. See documentation [here](https://github.com/jshttp/cookie?tab=readme-ov-file#cookiestringifysetcookiesetcookieobj-options)
42
+ */
43
+ delete: (name: string, opts: import('cookie').SerializeOptions) => void;
44
+
45
+ /**
46
+ * Parses a single `Set-Cookie` header. This allows you to apply cookies received from an external source:
47
+ *
48
+ * ```js
49
+ * import { getRequestEvent } from '$app/server';
50
+ *
51
+ * export async function GET() {
52
+ * const { cookies } = getRequestEvent();
53
+ *
54
+ * const response = await fetch('...');
55
+ *
56
+ * for (const str of response.headers.getSetCookie()) {
57
+ * const { name, value, ...options } = cookies.parse(str);
58
+ * cookies.set(name, value, { ...options, path: '/' });
59
+ * }
60
+ *
61
+ * // ...
62
+ * }
63
+ * ```
64
+ *
65
+ * Note the use of `headers.getSetCookie()`, which returns an array of cookie headers, _not_ `headers.get('set-cookie')` which returns a single comma-separated string.
66
+ */
67
+ parse: typeof import('cookie').parseSetCookie;
68
+
69
+ /**
70
+ * Serialize a cookie name-value pair into a `Set-Cookie` header string, but don't apply it to the response.
71
+ *
72
+ * The `httpOnly` is `true` by default, as is `secure`, except during development, when it defaults to `false`. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
73
+ *
74
+ * The `path` option is `'/'` by default. You can use relative paths, or set `path: ''` to make the cookie only available on the current path and its children.
75
+ * @param name the name of the cookie
76
+ * @param value the cookie value
77
+ * @param opts the options passed to `cookie.stringifySetCookie` with the SvelteKit defaults described above. See documentation [here](https://github.com/jshttp/cookie?tab=readme-ov-file#cookiestringifysetcookiesetcookieobj-options)
78
+ */
79
+ serialize: (name: string, value: string, opts: import('cookie').SerializeOptions) => string;
80
+ }
81
+
82
+ export interface RequestEvent<
83
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
84
+ RouteId extends AppRouteId | null = AppRouteId | null
85
+ > {
86
+ /**
87
+ * Get or set cookies related to the current request
88
+ */
89
+ readonly cookies: Cookies;
90
+ /**
91
+ * `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
92
+ *
93
+ * - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request.
94
+ * - It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context).
95
+ * - Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
96
+ * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#handle)
97
+ * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
98
+ *
99
+ * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies).
100
+ */
101
+ readonly fetch: typeof fetch;
102
+ /**
103
+ * The client's IP address, set by the adapter.
104
+ */
105
+ readonly getClientAddress: () => string;
106
+ /**
107
+ * Contains custom data that was added to the request within the [`server handle hook`](https://svelte.dev/docs/kit/hooks#handle).
108
+ */
109
+ readonly locals: App.Locals;
110
+ /**
111
+ * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
112
+ *
113
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
114
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
115
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
116
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
117
+ */
118
+ readonly params: Params;
119
+ /**
120
+ * Additional data made available through the adapter.
121
+ */
122
+ readonly platform: Readonly<App.Platform> | undefined;
123
+ /**
124
+ * The original request object.
125
+ */
126
+ readonly request: Request;
127
+ /**
128
+ * Info about the current route.
129
+ */
130
+ readonly route: {
131
+ /**
132
+ * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. It is `null` when no route is matched.
133
+ *
134
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
135
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
136
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
137
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
138
+ */
139
+ id: RouteId;
140
+ };
141
+ /**
142
+ * If you need to set headers for the response, you can do so using the this method. This is useful if you want the page to be cached, for example:
143
+ *
144
+ * ```js
145
+ * /// file: src/routes/blog/+page.js
146
+ * export async function load({ fetch, setHeaders }) {
147
+ * const url = `https://cms.example.com/articles.json`;
148
+ * const response = await fetch(url);
149
+ *
150
+ * setHeaders({
151
+ * age: response.headers.get('age'),
152
+ * 'cache-control': response.headers.get('cache-control')
153
+ * });
154
+ *
155
+ * return response.json();
156
+ * }
157
+ * ```
158
+ *
159
+ * Setting the same header multiple times (even in separate `load` functions) is an error — you can only set a given header once.
160
+ *
161
+ * You cannot add a `set-cookie` header with `setHeaders` — use the [`cookies`](https://svelte.dev/docs/kit/$app-server#Cookies) API instead.
162
+ */
163
+ readonly setHeaders: (headers: Record<string, string>) => void;
164
+ /**
165
+ * The requested URL.
166
+ *
167
+ * Inside `query` functions (including `query.batch` and `query.live`), accessing this property throws an error.
168
+ * Pass values from the page as arguments to the query instead. Inside `form` and `command` functions it relates to the page
169
+ * the remote function was called from, _not_ the URL of the endpoint SvelteKit creates for the remote function. Never use it
170
+ * to determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.
171
+ */
172
+ readonly url: URL;
173
+ /**
174
+ * `true` if the request comes from the client asking for `+page/layout.server.js` data. The `url` property will be stripped of the internal information
175
+ * related to the data request in this case. Use this property instead if the distinction is important to you.
176
+ */
177
+ readonly isDataRequest: boolean;
178
+ /**
179
+ * `true` for `+server.js` calls coming from SvelteKit without the overhead of actually making an HTTP request. This happens when you make same-origin `fetch` requests on the server.
180
+ */
181
+ readonly isSubRequest: boolean;
182
+
183
+ /**
184
+ * Access to spans for tracing. If tracing is not enabled, these spans will do nothing.
185
+ * @since 2.31.0
186
+ */
187
+ readonly tracing: {
188
+ /** Whether tracing is enabled. */
189
+ enabled: boolean;
190
+ /** The root span for the request. This span is named `sveltekit.handle.root`. */
191
+ root: Span;
192
+ /** The span associated with the current `handle` hook, `load` function, or form action. */
193
+ current: Span;
194
+ };
195
+
196
+ /**
197
+ * `true` if the request comes from the client via a remote function. The `url` property will be stripped of the internal information
198
+ * related to the data request in this case. Use this property instead if the distinction is important to you.
199
+ */
200
+ readonly isRemoteRequest: boolean;
201
+ }
@@ -1,4 +1,4 @@
1
- /** @import { RemoteCommand } from '@sveltejs/kit' */
1
+ /** @import { RemoteCommand } from '@sveltejs/kit/remote' */
2
2
  /** @import { MaybePromise, RemoteCommandInternals } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
@@ -65,21 +65,23 @@ 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
- state.remote.refreshes ??= new Map();
81
- state.remote.reconnects ??= new Map();
82
-
83
85
  const promise = Promise.resolve(
84
86
  run_remote_function(event, state, true, () => validate(arg), fn)
85
87
  );
@@ -1,5 +1,5 @@
1
- /** @import { RemoteFormInput, RemoteForm, InvalidField } from '@sveltejs/kit' */
2
- /** @import { InternalRemoteFormIssue, MaybePromise, RemoteFormInternals } from 'types' */
1
+ /** @import { RemoteFormInput, RemoteForm, InvalidField } from '@sveltejs/kit/remote' */
2
+ /** @import { InternalRemoteFormIssue, MaybePromise, HasNonOptionalBoolean, RemoteFormInternals } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
5
5
  import {
@@ -7,9 +7,10 @@ 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
- import { get_cache, run_remote_function } from './shared.js';
13
+ import { get_cache, get_implicit_lookup, run_remote_function } from './shared.js';
13
14
  import { ValidationError } from '@sveltejs/kit/internal';
14
15
 
15
16
  /**
@@ -44,7 +45,7 @@ import { ValidationError } from '@sveltejs/kit/internal';
44
45
  * @template {StandardSchemaV1<RemoteFormInput, Record<string, any>>} Schema
45
46
  * @template Output
46
47
  * @overload
47
- * @param {Schema} validate
48
+ * @param {true extends HasNonOptionalBoolean<StandardSchemaV1.InferInput<Schema>> ? 'Error: All booleans in form schemas must be optional (e.g. `v.optional(v.boolean(), false)`) because checkbox inputs do not send a false value when unchecked.' : Schema} validate
48
49
  * @param {(data: StandardSchemaV1.InferOutput<Schema>, issue: InvalidField<StandardSchemaV1.InferInput<Schema>>) => MaybePromise<Output>} fn
49
50
  * @returns {RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>}
50
51
  * @since 2.27
@@ -71,8 +72,7 @@ export function form(validate_or_fn, maybe_fn) {
71
72
  * @param {string | number | boolean} [key]
72
73
  */
73
74
  function create_instance(key) {
74
- /** @type {RemoteForm<Input, Output>} */
75
- const instance = {};
75
+ const instance = /** @type {RemoteForm<Input, Output>} */ ({});
76
76
 
77
77
  instance.method = 'POST';
78
78
 
@@ -88,8 +88,8 @@ export function form(validate_or_fn, maybe_fn) {
88
88
  name: '',
89
89
  id: '',
90
90
  fn: async (data, meta, form_data) => {
91
- /** @type {{ submission: true, input?: Record<string, any>, issues?: InternalRemoteFormIssue[], result: Output }} */
92
- const output = {};
91
+ const output =
92
+ /** @type {{ submission: true, input?: Record<string, any>, issues?: InternalRemoteFormIssue[], result: Output }} */ ({});
93
93
 
94
94
  // make it possible to differentiate between user submission and programmatic `field.set(...)` updates
95
95
  output.submission = true;
@@ -102,15 +102,12 @@ export function form(validate_or_fn, maybe_fn) {
102
102
  }
103
103
 
104
104
  if (validated?.issues !== undefined) {
105
- handle_issues(output, validated.issues, form_data);
105
+ handle_issues(output, validated.issues, form_data, __.id);
106
106
  } else {
107
107
  if (validated !== undefined) {
108
108
  data = validated.value;
109
109
  }
110
110
 
111
- state.remote.refreshes ??= new Map();
112
- state.remote.reconnects ??= new Map();
113
-
114
111
  const issue = create_issues();
115
112
 
116
113
  try {
@@ -123,7 +120,7 @@ export function form(validate_or_fn, maybe_fn) {
123
120
  );
124
121
  } catch (e) {
125
122
  if (e instanceof ValidationError) {
126
- handle_issues(output, e.issues, form_data);
123
+ handle_issues(output, e.issues, form_data, __.id);
127
124
  } else {
128
125
  throw e;
129
126
  }
@@ -133,7 +130,12 @@ export function form(validate_or_fn, maybe_fn) {
133
130
  // We don't need to care about args or deduplicating calls, because uneval results are only relevant in full page reloads
134
131
  // where only one form submission is active at the same time
135
132
  if (!event.isRemoteRequest) {
136
- get_cache(__, state)[''] ??= { serialize: true, data: output };
133
+ const cache = get_cache(__, state);
134
+ cache[''] ??= output;
135
+
136
+ // register under the client-side action id so the output is serialized
137
+ // into the page, allowing the hydrated client to restore `result`/`issues`/`input`
138
+ get_implicit_lookup(__, state)[__.key ? `${__.id}/${__.key}` : __.id] = () => cache[''];
137
139
  }
138
140
 
139
141
  return output;
@@ -143,42 +145,55 @@ export function form(validate_or_fn, maybe_fn) {
143
145
  Object.defineProperty(instance, '__', { value: __ });
144
146
 
145
147
  Object.defineProperty(instance, 'action', {
146
- get: () => `?/remote=${__.id}`,
148
+ get: () => {
149
+ const search = new URLSearchParams(get_request_store().event.url.search);
150
+ search.delete('/remote');
151
+
152
+ const query = search.toString();
153
+ const action_id = __.key ? `${__.id}/${encodeURIComponent(__.key)}` : __.id;
154
+
155
+ return `?${query ? `${query}&` : ''}/remote=${action_id}`;
156
+ },
147
157
  enumerable: true
148
158
  });
149
159
 
150
160
  Object.defineProperty(instance, 'fields', {
151
161
  get() {
152
- return create_field_proxy(
153
- {},
154
- () => get_cache(__)?.['']?.data?.input ?? {},
155
- (path, value) => {
156
- const cache = get_cache(__);
162
+ // the form instance is created once per module and shared across requests,
163
+ // so the current request's state has to be resolved at access time
164
+ return create_field_proxy({
165
+ form_id: __.id,
166
+ get: () => get_cache(__, get_request_store().state)?.['']?.input ?? {},
167
+ set: (path, value) => {
168
+ const cache = get_cache(__, get_request_store().state);
157
169
  const entry = cache[''];
158
170
 
159
- if (entry?.data?.submission) {
171
+ if (entry?.submission) {
160
172
  // don't override a submission
161
173
  return;
162
174
  }
163
175
 
164
176
  if (path.length === 0) {
165
- (cache[''] ??= { serialize: true, data: {} }).data.input = value;
177
+ (cache[''] ??= {}).input = value;
166
178
  return;
167
179
  }
168
180
 
169
- const input = entry?.data?.input ?? {};
181
+ const input = entry?.input ?? {};
170
182
  deep_set(input, path.map(String), value);
171
- (cache[''] ??= { serialize: true, data: {} }).data.input = input;
183
+ (cache[''] ??= {}).input = input;
172
184
  },
173
- () => flatten_issues(get_cache(__)?.['']?.data?.issues ?? [])
174
- );
185
+ get_issues: () =>
186
+ flatten_issues(get_cache(__, get_request_store().state)?.['']?.issues ?? []),
187
+ get_touched: () => ({}),
188
+ get_dirty: () => ({})
189
+ });
175
190
  }
176
191
  });
177
192
 
178
193
  Object.defineProperty(instance, 'result', {
179
194
  get() {
180
195
  try {
181
- return get_cache(__)?.['']?.data?.result;
196
+ return get_cache(__, get_request_store().state)?.['']?.result;
182
197
  } catch {
183
198
  return undefined;
184
199
  }
@@ -190,6 +205,10 @@ export function form(validate_or_fn, maybe_fn) {
190
205
  get: () => 0
191
206
  });
192
207
 
208
+ Object.defineProperty(instance, 'submitted', {
209
+ get: () => false
210
+ });
211
+
193
212
  Object.defineProperty(instance, 'preflight', {
194
213
  // preflight is a noop on the server
195
214
  value: () => instance
@@ -217,11 +236,15 @@ export function form(validate_or_fn, maybe_fn) {
217
236
  value: (key) => {
218
237
  const { state } = get_request_store();
219
238
  const cache_key = __.id + '|' + JSON.stringify(key);
239
+ /** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */
220
240
  let instance = (state.remote.forms ??= new Map()).get(cache_key);
221
241
 
222
242
  if (!instance) {
223
- instance = create_instance(key);
224
- instance.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key))}`;
243
+ instance = /** @type {RemoteForm<Input, Output> & { __: RemoteFormInternals }} */ (
244
+ create_instance(key)
245
+ );
246
+ instance.__.id = __.id;
247
+ instance.__.key = JSON.stringify(key);
225
248
  instance.__.name = __.name;
226
249
 
227
250
  state.remote.forms.set(cache_key, instance);
@@ -242,8 +265,9 @@ export function form(validate_or_fn, maybe_fn) {
242
265
  * @param {{ issues?: InternalRemoteFormIssue[], input?: Record<string, any>, result: any }} output
243
266
  * @param {readonly StandardSchemaV1.Issue[]} issues
244
267
  * @param {FormData | null} form_data - null if the form is progressively enhanced
268
+ * @param {string} form_id - hash/name of the form
245
269
  */
246
- function handle_issues(output, issues, form_data) {
270
+ function handle_issues(output, issues, form_data, form_id) {
247
271
  output.issues = issues.map((issue) => normalize_issue(issue, true));
248
272
 
249
273
  // if it was a progressively-enhanced submission, we don't need
@@ -251,19 +275,17 @@ function handle_issues(output, issues, form_data) {
251
275
  if (form_data) {
252
276
  output.input = {};
253
277
 
254
- for (let key of form_data.keys()) {
278
+ for (const field_name of form_data.keys()) {
255
279
  // redact sensitive fields
256
- if (/^[.\]]?_/.test(key)) continue;
257
-
258
- const is_array = key.endsWith('[]');
259
- const values = form_data.getAll(key).filter((value) => typeof value === 'string');
280
+ if (/^[.\]]?_/.test(field_name)) continue;
260
281
 
261
- if (is_array) key = key.slice(0, -2);
282
+ const values = form_data.getAll(field_name).filter((value) => typeof value === 'string');
283
+ const field = parse_form_key(form_id, field_name);
262
284
 
263
285
  set_nested_value(
264
286
  /** @type {Record<string, any>} */ (output.input),
265
- key,
266
- is_array ? values : values[0]
287
+ field,
288
+ field.is_array ? values : values[0]
267
289
  );
268
290
  }
269
291
  }
@@ -1,19 +1,14 @@
1
- /** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit' */
2
- /** @import { RemotePrerenderInputsGenerator, RemotePrerenderInternals, MaybePromise } from 'types' */
1
+ /** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit/remote' */
2
+ /** @import { RemoteFunctionResponse, RemotePrerenderInputsGenerator, RemotePrerenderInternals, MaybePromise } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
- import { error, json } from '@sveltejs/kit';
5
- import { DEV } from 'esm-env';
4
+ import { json } from '@sveltejs/kit';
5
+ import { HandledHttpError } from '@sveltejs/kit/internal';
6
6
  import { get_request_store } from '@sveltejs/kit/internal/server';
7
- import { stringify, stringify_remote_arg } from '../../../shared.js';
7
+ import { stringify_remote_arg } from '../../../shared.js';
8
+ import { parse, stringify } from '#app/internal/transport';
8
9
  import { noop } from '../../../../utils/functions.js';
9
- import { app_dir, base } from '$app/paths/internal/server';
10
- import {
11
- create_validator,
12
- get_cache,
13
- get_response,
14
- parse_remote_response,
15
- run_remote_function
16
- } from './shared.js';
10
+ import { app_dir, base } from '#app/paths';
11
+ import { create_validator, get_response, run_remote_function } from './shared.js';
17
12
 
18
13
  /**
19
14
  * Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -89,50 +84,55 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
89
84
 
90
85
  /** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
91
86
  const wrapper = (arg) => {
87
+ const { event, state } = get_request_store();
88
+ const payload = stringify_remote_arg(arg);
89
+
90
+ // `get_response` (as opposed to bare `get_cache`) also registers the call in the
91
+ // implicit lookup, so that the result is inlined into the page payload (`data.p`)
92
+ // and the client doesn't need to fetch it again upon hydration
92
93
  /** @type {Promise<Output> & Partial<RemoteResource<Output>>} */
93
- const promise = (async () => {
94
- const { event, state } = get_request_store();
95
- const payload = stringify_remote_arg(arg, state.transport);
94
+ const promise = get_response(__, payload, state, async () => {
96
95
  const id = __.id;
97
96
  const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
98
97
 
99
- if (!state.prerendering && !DEV && !event.isRemoteRequest) {
98
+ if (!state.prerendering && !__SVELTEKIT_DEV__ && !event.isRemoteRequest) {
99
+ /** @type {RemoteFunctionResponse | undefined} */
100
+ let prerendered;
101
+
100
102
  try {
101
- return await get_response(__, payload, state, async () => {
102
- const cache = get_cache(__, state);
103
-
104
- // TODO adapters can provide prerendered data more efficiently than
105
- // fetching from the public internet
106
- const promise = (cache[payload] ??= {
107
- serialize: true,
108
- data: fetch(new URL(url, event.url.origin).href).then(async (response) => {
109
- if (!response.ok) {
110
- throw new Error('Prerendered response not found');
111
- }
112
-
113
- const prerendered = await response.json();
114
-
115
- if (prerendered.type === 'error') {
116
- error(prerendered.status, prerendered.error);
117
- }
118
-
119
- return prerendered.result;
120
- })
121
- }).data;
122
-
123
- return parse_remote_response(await promise, state.transport);
124
- });
103
+ // TODO adapters can provide prerendered data more efficiently than
104
+ // fetching from the public internet
105
+ // `request.url` rather than `event.url`, which throws inside queries
106
+ const response = await fetch(new URL(url, event.request.url).href);
107
+
108
+ if (response.ok) {
109
+ prerendered = /** @type {RemoteFunctionResponse} */ (await response.json());
110
+ }
125
111
  } catch {
126
112
  // not available prerendered, fallback to normal function
127
113
  }
114
+
115
+ if (prerendered) {
116
+ if (prerendered.type === 'error') {
117
+ throw new HandledHttpError(prerendered.error);
118
+ }
119
+
120
+ return parse(prerendered.data)._;
121
+ }
128
122
  }
129
123
 
124
+ // during a prerender run, the same function might be invoked while rendering
125
+ // multiple pages — share the result across the entire run
130
126
  if (state.prerendering?.remote_responses.has(url)) {
131
127
  return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
132
128
  }
133
129
 
134
- const promise = get_response(__, payload, state, () =>
135
- 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
136
  );
137
137
 
138
138
  if (state.prerendering) {
@@ -142,7 +142,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
142
142
  const result = await promise;
143
143
 
144
144
  if (state.prerendering) {
145
- const body = { type: 'result', result: stringify(result, state.transport) };
145
+ const body = { type: 'result', data: stringify({ _: result }) };
146
146
  state.prerendering.dependencies.set(url, {
147
147
  body: JSON.stringify(body),
148
148
  response: json(body)
@@ -151,7 +151,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
151
151
 
152
152
  // TODO this is missing error/loading/current/status
153
153
  return result;
154
- })();
154
+ });
155
155
 
156
156
  promise.catch(noop);
157
157