@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,640 @@
1
+ /** @import { SSRManifest } from '@sveltejs/kit' */
2
+ /** @import { RemoteForm } from '@sveltejs/kit/remote' */
3
+ /** @import { RequestEvent } from '$app/server' */
4
+ /** @import { ActionResult } from '$app/forms' */
5
+ /** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, SSROptions } from 'types' */
6
+
7
+ import { json, error } from '@sveltejs/kit';
8
+ import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
9
+ import { with_request_store, merge_tracing, record_span } from '@sveltejs/kit/internal/server';
10
+ import { app_dir, base } from '#app/paths';
11
+ import { is_form_content_type } from '../../utils/http.js';
12
+ import { create_remote_key, parse_remote_arg, split_remote_key } from '../shared.js';
13
+ import { stringify } from '#app/internal/transport';
14
+ import { handle_error_and_jsonify } from './errors.js';
15
+ import { normalize_error } from '../../utils/error.js';
16
+ import { check_incorrect_fail_use, get_action_location } from './page/actions.js';
17
+ import { DEV } from 'esm-env';
18
+ import { deserialize_binary_form } from '../form-utils.js';
19
+ import { with_version_header } from './utils.js';
20
+
21
+ /**
22
+ * How long (in milliseconds) to wait after the last message was sent before
23
+ * sending a `: keep-alive` SSE comment, to prevent proxies/load balancers with
24
+ * an idle timeout from closing an otherwise-quiet `query.live` connection.
25
+ */
26
+ const KEEP_ALIVE_INTERVAL = 30_000;
27
+
28
+ /** @type {typeof handle_remote_call_internal} */
29
+ export async function handle_remote_call(event, state, options, manifest, id) {
30
+ return record_span({
31
+ name: 'sveltekit.remote.call',
32
+ attributes: {
33
+ 'sveltekit.remote.call.id': id
34
+ },
35
+ fn: async (current) => {
36
+ const traced_event = merge_tracing(event, current);
37
+ const response = await with_request_store({ event: traced_event, state }, () =>
38
+ handle_remote_call_internal(traced_event, state, options, manifest, id)
39
+ );
40
+ return with_version_header(response);
41
+ }
42
+ });
43
+ }
44
+
45
+ /**
46
+ * @param {RequestEvent} event
47
+ * @param {RequestState} state
48
+ * @param {SSROptions} options
49
+ * @param {SSRManifest} manifest
50
+ * @param {string} id
51
+ */
52
+ async function handle_remote_call_internal(event, state, options, manifest, id) {
53
+ const [hash, name, additional_args] = id.split('/');
54
+ const remotes = manifest._.remotes;
55
+
56
+ if (!Object.hasOwn(remotes, hash)) error(404);
57
+
58
+ const module = await remotes[hash]();
59
+ const fn = Object.hasOwn(module.default, name) ? module.default[name] : undefined;
60
+
61
+ if (!fn) error(404);
62
+
63
+ /** @type {RemoteInternals} */
64
+ const internals = fn.__;
65
+
66
+ event.tracing.current.setAttributes({
67
+ 'sveltekit.remote.call.type': internals.type,
68
+ 'sveltekit.remote.call.name': internals.name
69
+ });
70
+
71
+ /** @type {HeadersInit | undefined} */
72
+ const headers = state.prerendering ? undefined : { 'cache-control': 'private, no-store' };
73
+
74
+ try {
75
+ /** @type {RemoteFunctionData} */
76
+ const data = {};
77
+
78
+ switch (internals.type) {
79
+ case 'query_live': {
80
+ if (event.request.method !== 'GET') {
81
+ throw new SvelteKitError(
82
+ 405,
83
+ 'Method Not Allowed',
84
+ `\`query.live\` functions must be invoked via GET request, not ${event.request.method}`
85
+ );
86
+ }
87
+
88
+ const payload = /** @type {string} */ (
89
+ new URL(event.request.url).searchParams.get('payload')
90
+ );
91
+
92
+ const generator = internals.run(event, state, parse_remote_arg(payload));
93
+
94
+ const encoder = new TextEncoder();
95
+
96
+ let closed = false;
97
+
98
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
99
+ let keep_alive;
100
+
101
+ /**
102
+ * (Re)schedule the keep-alive comment. Called whenever a message is sent, so
103
+ * that a keep-alive is only emitted once `KEEP_ALIVE_INTERVAL` has elapsed
104
+ * without any other activity.
105
+ * @param {ReadableStreamDefaultController} controller
106
+ */
107
+ function schedule_keep_alive(controller) {
108
+ clearTimeout(keep_alive);
109
+ keep_alive = setTimeout(() => {
110
+ if (closed || event.request.signal.aborted) return;
111
+ // SSE comments (lines starting with `:`) are ignored by the client
112
+ controller.enqueue(encoder.encode(': keep-alive\n\n'));
113
+ schedule_keep_alive(controller);
114
+ }, KEEP_ALIVE_INTERVAL);
115
+ }
116
+
117
+ /**
118
+ * @param {ReadableStreamDefaultController} controller
119
+ * @param {any} payload
120
+ */
121
+ function send(controller, payload) {
122
+ controller.enqueue(encoder.encode('data: ' + JSON.stringify(payload) + '\n\n'));
123
+ schedule_keep_alive(controller);
124
+ }
125
+
126
+ /** @type {string | undefined} */
127
+ let result = undefined;
128
+
129
+ async function cancel() {
130
+ if (closed) return;
131
+ closed = true;
132
+ clearTimeout(keep_alive);
133
+ await generator.return(undefined);
134
+ }
135
+
136
+ event.request.signal.addEventListener('abort', cancel, { once: true });
137
+
138
+ return new Response(
139
+ new ReadableStream({
140
+ start(controller) {
141
+ schedule_keep_alive(controller);
142
+ },
143
+ async pull(controller) {
144
+ if (event.request.signal.aborted) {
145
+ await cancel();
146
+ controller.close();
147
+ return;
148
+ }
149
+
150
+ try {
151
+ while (true) {
152
+ const { value, done } = await generator.next();
153
+
154
+ if (done) {
155
+ await cancel();
156
+ controller.close();
157
+ return;
158
+ }
159
+
160
+ // only send changed data
161
+ if (result !== (result = stringify(value))) {
162
+ send(controller, {
163
+ type: 'result',
164
+ result
165
+ });
166
+
167
+ return;
168
+ }
169
+ }
170
+ } catch (error) {
171
+ if (!event.request.signal.aborted) {
172
+ if (error instanceof Redirect) {
173
+ send(controller, {
174
+ type: 'redirect',
175
+ location: error.location
176
+ });
177
+ } else {
178
+ const transformed = await handle_error_and_jsonify(
179
+ event,
180
+ state,
181
+ options,
182
+ error
183
+ );
184
+
185
+ send(controller, {
186
+ type: 'error',
187
+ error: transformed
188
+ });
189
+ }
190
+ }
191
+
192
+ await cancel();
193
+ controller.close();
194
+ }
195
+ },
196
+ cancel
197
+ }),
198
+ {
199
+ headers: {
200
+ 'cache-control': 'private, no-store',
201
+ 'content-type': 'text/event-stream'
202
+ }
203
+ }
204
+ );
205
+ }
206
+
207
+ case 'query_batch': {
208
+ if (event.request.method !== 'POST') {
209
+ throw new SvelteKitError(
210
+ 405,
211
+ 'Method Not Allowed',
212
+ `\`query.batch\` functions must be invoked via POST request, not ${event.request.method}`
213
+ );
214
+ }
215
+
216
+ /** @type {{ payloads: string[] }} */
217
+ const { payloads } = await event.request.json();
218
+
219
+ const args = await Promise.all(payloads.map((payload) => parse_remote_arg(payload)));
220
+
221
+ data._ = await with_request_store({ event, state }, () => internals.run(args, options));
222
+
223
+ break;
224
+ }
225
+
226
+ case 'form': {
227
+ if (event.request.method !== 'POST') {
228
+ throw new SvelteKitError(
229
+ 405,
230
+ 'Method Not Allowed',
231
+ `\`form\` functions must be invoked via POST request, not ${event.request.method}`
232
+ );
233
+ }
234
+
235
+ if (!is_form_content_type(event.request)) {
236
+ throw new SvelteKitError(
237
+ 415,
238
+ 'Unsupported Media Type',
239
+ `\`form\` functions expect form-encoded data — received ${event.request.headers.get(
240
+ 'content-type'
241
+ )}`
242
+ );
243
+ }
244
+
245
+ const {
246
+ data: input,
247
+ meta,
248
+ form_data
249
+ } = await deserialize_binary_form(event.request, internals.id);
250
+ state.remote.requested = create_requested_map(meta.remote_refreshes);
251
+
252
+ // If this is a keyed form instance (created via form.for(key)), add the key to the form data (unless already set)
253
+ // Note that additional_args will only be set if the form is not enhanced, as enhanced forms transfer the key inside `data`.
254
+ if (additional_args && !('id' in input)) {
255
+ input.id = JSON.parse(decodeURIComponent(additional_args));
256
+ }
257
+
258
+ const fn = internals.fn;
259
+ data._ = await with_request_store(
260
+ { event, state: { ...state, is_in_remote_form_or_command: true } },
261
+ () => fn(input, meta, form_data)
262
+ );
263
+
264
+ if (data._.issues) {
265
+ // special case — don't serialize refreshes/reconnects
266
+ return json(
267
+ /** @type {RemoteFunctionResponse} */ ({
268
+ type: 'result',
269
+ data: stringify(data)
270
+ }),
271
+ { headers }
272
+ );
273
+ }
274
+
275
+ break;
276
+ }
277
+
278
+ case 'command': {
279
+ /** @type {{ payload: string, refreshes?: string[] }} */
280
+ const { payload, refreshes } = await event.request.json();
281
+ state.remote.requested = create_requested_map(refreshes);
282
+ const arg = parse_remote_arg(payload);
283
+
284
+ data._ = await with_request_store(
285
+ { event, state: { ...state, is_in_remote_form_or_command: true } },
286
+ () => fn(arg)
287
+ );
288
+
289
+ break;
290
+ }
291
+
292
+ case 'prerender': {
293
+ data._ = await with_request_store({ event, state }, () =>
294
+ fn(parse_remote_arg(additional_args))
295
+ );
296
+
297
+ break;
298
+ }
299
+
300
+ case 'query': {
301
+ const payload = /** @type {string} */ (
302
+ // new URL(...) necessary because we're hiding the URL from the user in the event object
303
+ new URL(event.request.url).searchParams.get('payload')
304
+ );
305
+
306
+ data._ = await with_request_store({ event, state }, () => fn(parse_remote_arg(payload)));
307
+
308
+ break;
309
+ }
310
+ }
311
+
312
+ await collect_remote_data(data, event, state, options);
313
+
314
+ return json(
315
+ /** @type {RemoteFunctionResponse} */ ({
316
+ type: 'result',
317
+ data: stringify(data)
318
+ }),
319
+ { headers }
320
+ );
321
+ } catch (error) {
322
+ if (error instanceof Redirect) {
323
+ const data = await collect_remote_data({ redirect: error.location }, event, state, options);
324
+
325
+ return json(
326
+ /** @type {RemoteFunctionResponse} */ ({
327
+ type: 'result',
328
+ data: stringify(data)
329
+ }),
330
+ { headers }
331
+ );
332
+ }
333
+
334
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
335
+
336
+ return json(
337
+ /** @type {RemoteFunctionResponse} */ ({
338
+ type: 'error',
339
+ error: transformed
340
+ }),
341
+ {
342
+ // By setting a non-200 during prerendering we fail the prerender process (unless handleHttpError handles it).
343
+ // Errors at runtime will be passed to the client and are handled there
344
+ status: state.prerendering ? transformed.status : undefined,
345
+ headers: {
346
+ 'cache-control': 'private, no-store'
347
+ }
348
+ }
349
+ );
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Collects all the query/prerender data that was retrieved
355
+ * during the request and adds it to `data`
356
+ * @param {RemoteFunctionData} data
357
+ * @param {RequestEvent} event
358
+ * @param {RequestState} state
359
+ * @param {SSROptions} options
360
+ */
361
+ export async function collect_remote_data(data, event, state, options) {
362
+ /**
363
+ *
364
+ * @param {unknown} error
365
+ * @returns {Promise<App.Error>}
366
+ */
367
+ function convert_error(error) {
368
+ // TODO 4.0 remove the `Promise.resolve(...)`
369
+ return Promise.resolve(handle_error_and_jsonify(event, state, options, error));
370
+ }
371
+
372
+ /** @type {Promise<any>[]} */
373
+ const promises = [];
374
+
375
+ // Keys the explicit pass has serialized. Invoking a query's `fn` there can, as a
376
+ // side effect, register the same query in `state.remote.implicit` (via
377
+ // `get_response`), so we skip those keys in the implicit pass below to avoid
378
+ // processing them twice.
379
+ /** @type {Set<string>} */
380
+ const processed = new Set();
381
+
382
+ if (state.remote.explicit) {
383
+ const { explicit } = state.remote;
384
+
385
+ /** @type {Promise<void>[]} */
386
+ const inflight = [];
387
+
388
+ const drain = () => {
389
+ for (const [remote_key, { internals, fn }] of explicit) {
390
+ explicit.delete(remote_key);
391
+ if (processed.has(remote_key)) continue;
392
+ processed.add(remote_key);
393
+
394
+ // there were explicit refreshes/reconnects (via `refresh()`/`set()`/`reconnect()`),
395
+ // so the client should apply these single-flight updates instead of calling `invalidateAll()`
396
+ data.r = true;
397
+
398
+ const type = /** @type {'p' | 'q' | 'l'} */ (
399
+ internals.type === 'query_live' ? 'l' : internals.type[0]
400
+ );
401
+
402
+ // `fn` is deferred until now so the query runs after any state mutations
403
+ // in the command/form body. If the query was re-awaited in the meantime,
404
+ // `fn` returns the existing (fresh) cache entry rather than re-running.
405
+ inflight.push(
406
+ fn().then(
407
+ (v) => {
408
+ // a fresh value replaces the node entirely, so a re-run can't leave
409
+ // a stale error from a previous run alongside the new value
410
+ (data[type] ??= {})[remote_key] = { v };
411
+ drain();
412
+ },
413
+ async (e) => {
414
+ if (!(e instanceof Redirect)) {
415
+ // (a Redirect is already handled elsewhere)
416
+ (data[type] ??= {})[remote_key] = { e: await convert_error(e) };
417
+ }
418
+ drain();
419
+ }
420
+ )
421
+ );
422
+ }
423
+ };
424
+
425
+ drain();
426
+
427
+ // `inflight` grows as settles drain newly-refreshed queries
428
+ for (const promise of inflight) {
429
+ await promise;
430
+ }
431
+ }
432
+
433
+ if (state.remote.implicit) {
434
+ for (const [internals, record] of state.remote.implicit) {
435
+ // Private (non-exported) remote functions have no `id` and must never be
436
+ // serialized into the response — otherwise their (potentially private) result
437
+ // would be shipped to the client under a malformed `undefined/...` key.
438
+ if (!internals.id) continue;
439
+
440
+ for (const key in record) {
441
+ // form outputs are registered under the client-side action id directly
442
+ const remote_key = internals.type === 'form' ? key : create_remote_key(internals.id, key);
443
+
444
+ // already serialized by the explicit pass (which always awaits and wins),
445
+ // so don't reprocess it here with the implicit "still loading" heuristic
446
+ if (processed.has(remote_key)) continue;
447
+
448
+ const type = /** @type {'p' | 'q' | 'l' | 'f'} */ (
449
+ internals.type === 'query_live' ? 'l' : internals.type[0]
450
+ );
451
+
452
+ const promise = state.remote.data?.get(internals)?.[key] ?? record[key]();
453
+
454
+ // If the promise is still pending (e.g. the query was rendered in its loading
455
+ // state during SSR), omit it from the payload entirely so that the client
456
+ // fetches it itself — an entry without `v`/`e` would hydrate as `undefined`.
457
+ let resolved = true;
458
+
459
+ await Promise.race([
460
+ Promise.resolve(promise).then(
461
+ (v) => {
462
+ if (resolved) {
463
+ ((data[type] ??= {})[remote_key] ??= {}).v = v;
464
+ }
465
+ },
466
+ (e) => {
467
+ if (e instanceof Redirect) {
468
+ // already handled elsewhere
469
+ return;
470
+ }
471
+
472
+ if (resolved) {
473
+ promises.push(
474
+ convert_error(e).then((e) => {
475
+ ((data[type] ??= {})[remote_key] ??= {}).e = e;
476
+ })
477
+ );
478
+ }
479
+ }
480
+ ),
481
+ Promise.resolve().then(() => (resolved = false))
482
+ ]);
483
+ }
484
+ }
485
+ }
486
+
487
+ await Promise.all(promises);
488
+
489
+ return data;
490
+ }
491
+
492
+ /**
493
+ * @param {string[] | undefined} refreshes
494
+ */
495
+ function create_requested_map(refreshes) {
496
+ /** @type {Map<string, string[]>} */
497
+ const requested = new Map();
498
+
499
+ for (const key of refreshes ?? []) {
500
+ const parts = split_remote_key(key);
501
+
502
+ const existing = requested.get(parts.id);
503
+
504
+ if (existing) {
505
+ existing.push(parts.payload);
506
+ } else {
507
+ requested.set(parts.id, [parts.payload]);
508
+ }
509
+ }
510
+
511
+ return requested;
512
+ }
513
+
514
+ /** @type {typeof handle_remote_form_post_internal} */
515
+ export async function handle_remote_form_post(event, state, manifest, id) {
516
+ return record_span({
517
+ name: 'sveltekit.remote.form.post',
518
+ attributes: {
519
+ 'sveltekit.remote.form.post.id': id
520
+ },
521
+ fn: (current) => {
522
+ const traced_event = merge_tracing(event, current);
523
+ return with_request_store({ event: traced_event, state }, () =>
524
+ handle_remote_form_post_internal(traced_event, state, manifest, id)
525
+ );
526
+ }
527
+ });
528
+ }
529
+
530
+ /**
531
+ * @param {RequestEvent} event
532
+ * @param {RequestState} state
533
+ * @param {SSRManifest} manifest
534
+ * @param {string} id
535
+ * @returns {Promise<ActionResult>}
536
+ */
537
+ async function handle_remote_form_post_internal(event, state, manifest, id) {
538
+ const location = get_action_location(event.url);
539
+ // `hash` and `name` can never contain a `/`, but the JSON-stringified key of a
540
+ // keyed (`form.for(key)`) instance can — rejoin the remaining segments
541
+ const [hash, name, ...rest] = id.split('/');
542
+ const action_id = rest.join('/');
543
+ const remotes = manifest._.remotes;
544
+ const module = Object.hasOwn(remotes, hash) ? await remotes[hash]() : undefined;
545
+
546
+ let form = /** @type {RemoteForm<any, any>} */ (
547
+ module && Object.hasOwn(module.default, name) ? module.default[name] : undefined
548
+ );
549
+
550
+ if (!form) {
551
+ event.setHeaders({
552
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
553
+ // "The server must generate an Allow header field in a 405 status code response"
554
+ allow: 'GET'
555
+ });
556
+ return {
557
+ type: 'error',
558
+ location,
559
+ // We're lying a bit with the types here; this will be transformed into a proper App.Error object later
560
+ error: new SvelteKitError(
561
+ 405,
562
+ 'Method Not Allowed',
563
+ `POST method not allowed. No form actions exist for ${DEV ? `the page at ${event.route.id}` : 'this page'}`
564
+ )
565
+ };
566
+ }
567
+
568
+ if (action_id) {
569
+ // @ts-expect-error
570
+ form = with_request_store({ event, state }, () => form.for(JSON.parse(action_id)));
571
+ }
572
+
573
+ try {
574
+ const __ = /** @type {RemoteFormInternals} */ (/** @type {any} */ (form).__);
575
+
576
+ const { data, meta, form_data } = await deserialize_binary_form(event.request, __.id);
577
+
578
+ if (action_id && !('id' in data)) {
579
+ data.id = JSON.parse(decodeURIComponent(action_id));
580
+ }
581
+
582
+ await with_request_store(
583
+ { event, state: { ...state, is_in_remote_form_or_command: true } },
584
+ () => __.fn(data, meta, form_data)
585
+ );
586
+
587
+ // We don't want the data to appear on `let { form } = $props()`, which is why we're not returning it.
588
+ // It is instead available on `myForm.result`, setting of which happens within the remote `form` function.
589
+ return {
590
+ type: 'success',
591
+ status: 200,
592
+ location
593
+ };
594
+ } catch (e) {
595
+ const err = normalize_error(e);
596
+
597
+ if (err instanceof Redirect) {
598
+ return {
599
+ type: 'redirect',
600
+ status: err.status,
601
+ location: err.location
602
+ };
603
+ }
604
+
605
+ return {
606
+ type: 'error',
607
+ location,
608
+ // @ts-expect-error We're lying a bit with the types here; this will be transformed into a proper App.Error object later
609
+ error: check_incorrect_fail_use(err)
610
+ };
611
+ }
612
+ }
613
+
614
+ /**
615
+ * @param {URL} url
616
+ */
617
+ export function has_remote_prefix(url) {
618
+ return url.pathname.startsWith(`${base}/${app_dir}/remote/`);
619
+ }
620
+
621
+ /**
622
+ * @param {URL} url
623
+ */
624
+ export function strip_remote_prefix(url) {
625
+ return url.pathname.replace(`${base}/${app_dir}/remote/`, '');
626
+ }
627
+
628
+ /**
629
+ * @param {URL} url
630
+ */
631
+ export function get_remote_id(url) {
632
+ return has_remote_prefix(url) && strip_remote_prefix(url);
633
+ }
634
+
635
+ /**
636
+ * @param {URL} url
637
+ */
638
+ export function get_remote_action(url) {
639
+ return url.searchParams.get('/remote');
640
+ }