@sveltejs/kit 3.0.0-next.24 → 3.0.0-next.27

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 (71) hide show
  1. package/package.json +16 -10
  2. package/src/constants.js +6 -0
  3. package/src/core/adapt/builder.js +128 -33
  4. package/src/core/adapt/index.js +3 -0
  5. package/src/core/config/index.js +4 -7
  6. package/src/core/env.js +39 -14
  7. package/src/core/generate_manifest/index.js +42 -43
  8. package/src/core/postbuild/analyse.js +4 -4
  9. package/src/core/postbuild/fallback.js +1 -1
  10. package/src/core/postbuild/prerender.js +4 -4
  11. package/src/core/sync/create_manifest_data/index.js +0 -1
  12. package/src/core/sync/write_app_manifest.js +3 -2
  13. package/src/core/sync/write_server.js +3 -17
  14. package/src/core/utils.js +10 -0
  15. package/src/exports/adapter.js +22 -0
  16. package/src/exports/public.d.ts +101 -49
  17. package/src/exports/vite/build/index.js +1173 -0
  18. package/src/exports/vite/build/remote.js +4 -4
  19. package/src/exports/vite/build/service-worker.js +98 -0
  20. package/src/exports/vite/dev/generate_manifest.js +308 -0
  21. package/src/exports/vite/dev/index.js +40 -282
  22. package/src/exports/vite/index.js +156 -1717
  23. package/src/exports/vite/plugins/env-vars.js +53 -34
  24. package/src/exports/vite/plugins/guard.js +217 -0
  25. package/src/exports/vite/plugins/remote.js +237 -0
  26. package/src/exports/vite/preview/index.js +8 -8
  27. package/src/exports/vite/static_analysis/index.js +15 -15
  28. package/src/exports/vite/utils.js +98 -1
  29. package/src/runner.js +4 -3
  30. package/src/runtime/app/paths/server.js +2 -2
  31. package/src/runtime/app/server/index.js +3 -3
  32. package/src/runtime/app/server/public.d.ts +114 -57
  33. package/src/runtime/app/server/remote/query.js +2 -2
  34. package/src/runtime/app/server/remote/requested.js +31 -15
  35. package/src/runtime/app/state/client.svelte.js +3 -1
  36. package/src/runtime/client/client.js +43 -198
  37. package/src/runtime/client/fetcher.js +6 -6
  38. package/src/runtime/client/focus.js +120 -0
  39. package/src/runtime/client/remote-functions/command.svelte.js +20 -9
  40. package/src/runtime/client/remote-functions/form.svelte.js +12 -7
  41. package/src/runtime/client/remote-functions/query/instance.svelte.js +19 -5
  42. package/src/runtime/client/remote-functions/shared.svelte.js +22 -1
  43. package/src/runtime/client/scroll.js +39 -0
  44. package/src/runtime/client/utils.js +28 -0
  45. package/src/runtime/form-utils.js +254 -264
  46. package/src/runtime/server/data/index.js +14 -36
  47. package/src/runtime/server/errors.js +7 -10
  48. package/src/runtime/server/fetch.js +21 -25
  49. package/src/runtime/server/index.js +35 -31
  50. package/src/runtime/server/internal.js +34 -5
  51. package/src/runtime/server/page/actions.js +6 -8
  52. package/src/runtime/server/page/data_serializer.js +6 -10
  53. package/src/runtime/server/page/index.js +18 -28
  54. package/src/runtime/server/page/load_data.js +2 -2
  55. package/src/runtime/server/page/render.js +22 -40
  56. package/src/runtime/server/page/respond_with_error.js +10 -13
  57. package/src/runtime/server/page/server_routing.js +21 -27
  58. package/src/runtime/server/remote-functions.js +136 -136
  59. package/src/runtime/server/respond.js +66 -64
  60. package/src/runtime/server/state.js +2 -0
  61. package/src/runtime/server/utils.js +4 -11
  62. package/src/runtime/shared.js +9 -0
  63. package/src/runtime/utils.js +41 -0
  64. package/src/types/ambient-private.d.ts +1 -2
  65. package/src/types/global-private.d.ts +8 -0
  66. package/src/types/internal.d.ts +56 -17
  67. package/src/utils/filesystem.js +44 -28
  68. package/src/utils/streaming.js +5 -15
  69. package/src/version.js +1 -1
  70. package/types/index.d.ts +235 -93
  71. package/types/index.d.ts.map +9 -2
@@ -1,6 +1,6 @@
1
- /** @import { RequestEvent, SSRManifest } from '@sveltejs/kit' */
1
+ /** @import { RequestEvent } from '@sveltejs/kit' */
2
2
  /** @import { RemoteForm } from '$app/server' */
3
- /** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, ServerActionResult, SSROptions } from 'types' */
3
+ /** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, ServerActionResult } from 'types' */
4
4
 
5
5
  import { error } from '@sveltejs/kit';
6
6
  import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
@@ -16,7 +16,9 @@ import {
16
16
  method_not_allowed_result
17
17
  } from './page/actions.js';
18
18
  import { deserialize_binary_form } from '../form-utils.js';
19
+ import { text_encoder } from '../utils.js';
19
20
  import { with_version_header } from './utils.js';
21
+ import { manifest } from './internal.js';
20
22
 
21
23
  /**
22
24
  * How long (in milliseconds) to wait after the last message was sent before
@@ -25,8 +27,121 @@ import { with_version_header } from './utils.js';
25
27
  */
26
28
  const KEEP_ALIVE_INTERVAL = 30_000;
27
29
 
30
+ /**
31
+ * @param {RequestEvent} event
32
+ * @param {RequestState} state
33
+ * @param {import('types').RemoteQueryLiveInternals} internals
34
+ * @param {any} arg
35
+ */
36
+ export function create_live_query_response(event, state, internals, arg) {
37
+ const cancellation = new AbortController();
38
+ const live_event = {
39
+ ...event,
40
+ request: new Request(event.request, {
41
+ signal: AbortSignal.any([event.request.signal, cancellation.signal])
42
+ })
43
+ };
44
+
45
+ const generator = internals.run(live_event, state, arg);
46
+
47
+ let open = true;
48
+ let pulling = false;
49
+ /** @type {ReadableStreamDefaultController<Uint8Array>} */
50
+ let stream_controller;
51
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
52
+ let keep_alive;
53
+ /** @type {string | undefined} */
54
+ let result;
55
+
56
+ function schedule_keep_alive() {
57
+ clearTimeout(keep_alive);
58
+ keep_alive = setTimeout(() => {
59
+ if (!open) return;
60
+ if ((stream_controller.desiredSize ?? 0) > 0) {
61
+ stream_controller.enqueue(text_encoder.encode(': keep-alive\n\n'));
62
+ }
63
+ schedule_keep_alive();
64
+ }, KEEP_ALIVE_INTERVAL);
65
+ }
66
+
67
+ /** @param {any} data */
68
+ function send(data) {
69
+ if (!open) return;
70
+ stream_controller.enqueue(text_encoder.encode('data: ' + JSON.stringify(data) + '\n\n'));
71
+ schedule_keep_alive();
72
+ }
73
+
74
+ /** @param {boolean} cancelled */
75
+ function teardown(cancelled) {
76
+ if (!open) return;
77
+ open = false;
78
+ clearTimeout(keep_alive);
79
+ cancellation.abort();
80
+ if (!cancelled) stream_controller.close();
81
+ // AsyncGenerator.return() cannot interrupt a pending next(). Cleanup is
82
+ // cooperative via request.signal, so stream cancellation must not await it.
83
+ void generator.return(undefined).catch(() => {});
84
+ }
85
+
86
+ event.request.signal.addEventListener('abort', () => teardown(true), { once: true });
87
+
88
+ return new Response(
89
+ new ReadableStream({
90
+ start(controller) {
91
+ stream_controller = controller;
92
+ schedule_keep_alive();
93
+ },
94
+ async pull() {
95
+ if (!open || pulling) return;
96
+ pulling = true;
97
+
98
+ try {
99
+ while (open) {
100
+ const { value, done } = await generator.next();
101
+
102
+ if (!open) return;
103
+
104
+ if (done) {
105
+ teardown(false);
106
+ return;
107
+ }
108
+
109
+ if (result !== (result = stringify(value))) {
110
+ send({ type: 'result', result });
111
+ return;
112
+ }
113
+ }
114
+ } catch (error) {
115
+ if (!open) return;
116
+
117
+ if (error instanceof Redirect) {
118
+ send({ type: 'redirect', location: error.location });
119
+ } else {
120
+ const transformed = await handle_error_and_jsonify(event, state, error);
121
+
122
+ send({ type: 'error', error: transformed });
123
+ }
124
+
125
+ teardown(false);
126
+ } finally {
127
+ pulling = false;
128
+ }
129
+ },
130
+ cancel() {
131
+ teardown(true);
132
+ }
133
+ }),
134
+ {
135
+ headers: {
136
+ 'cache-control': 'private, no-store',
137
+ 'content-type': 'text/event-stream'
138
+ }
139
+ }
140
+ );
141
+ }
142
+
28
143
  /** @type {typeof handle_remote_call_internal} */
29
- export async function handle_remote_call(event, state, options, manifest, id) {
144
+ export async function handle_remote_call(event, state, id) {
30
145
  return record_span({
31
146
  name: 'sveltekit.remote.call',
32
147
  attributes: {
@@ -35,7 +150,7 @@ export async function handle_remote_call(event, state, options, manifest, id) {
35
150
  fn: async (current) => {
36
151
  const traced_event = merge_tracing(event, current);
37
152
  const response = await with_request_store({ event: traced_event, state }, () =>
38
- handle_remote_call_internal(traced_event, state, options, manifest, id)
153
+ handle_remote_call_internal(traced_event, state, id)
39
154
  );
40
155
  return with_version_header(response);
41
156
  }
@@ -45,13 +160,11 @@ export async function handle_remote_call(event, state, options, manifest, id) {
45
160
  /**
46
161
  * @param {RequestEvent} event
47
162
  * @param {RequestState} state
48
- * @param {SSROptions} options
49
- * @param {SSRManifest} manifest
50
163
  * @param {string} id
51
164
  */
52
- async function handle_remote_call_internal(event, state, options, manifest, id) {
165
+ async function handle_remote_call_internal(event, state, id) {
53
166
  const [hash, name, additional_args] = id.split('/');
54
- const remotes = manifest._.remotes;
167
+ const remotes = manifest.remotes;
55
168
 
56
169
  if (!Object.hasOwn(remotes, hash)) error(404);
57
170
 
@@ -89,119 +202,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
89
202
  new URL(event.request.url).searchParams.get('payload')
90
203
  );
91
204
 
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
+ return create_live_query_response(event, state, internals, parse_remote_arg(payload));
205
206
  }
206
207
 
207
208
  case 'query_batch': {
@@ -218,7 +219,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
218
219
 
219
220
  const args = await Promise.all(payloads.map((payload) => parse_remote_arg(payload)));
220
221
 
221
- data._ = await with_request_store({ event, state }, () => internals.run(args, options));
222
+ data._ = await with_request_store({ event, state }, () => internals.run(args));
222
223
 
223
224
  break;
224
225
  }
@@ -309,7 +310,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
309
310
  }
310
311
  }
311
312
 
312
- await collect_remote_data(data, event, state, options);
313
+ await collect_remote_data(data, event, state);
314
+ if (state.remote.ignored?.size) data.i = Array.from(state.remote.ignored);
313
315
 
314
316
  return Response.json(
315
317
  /** @type {RemoteFunctionResponse} */ ({
@@ -320,7 +322,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
320
322
  );
321
323
  } catch (error) {
322
324
  if (error instanceof Redirect) {
323
- const data = await collect_remote_data({ redirect: error.location }, event, state, options);
325
+ const data = await collect_remote_data({ redirect: error.location }, event, state);
324
326
 
325
327
  return Response.json(
326
328
  /** @type {RemoteFunctionResponse} */ ({
@@ -331,7 +333,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
331
333
  );
332
334
  }
333
335
 
334
- const transformed = await handle_error_and_jsonify(event, state, options, error);
336
+ const transformed = await handle_error_and_jsonify(event, state, error);
335
337
 
336
338
  return Response.json(
337
339
  /** @type {RemoteFunctionResponse} */ ({
@@ -356,9 +358,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
356
358
  * @param {RemoteFunctionData} data
357
359
  * @param {RequestEvent} event
358
360
  * @param {RequestState} state
359
- * @param {SSROptions} options
360
361
  */
361
- export async function collect_remote_data(data, event, state, options) {
362
+ export async function collect_remote_data(data, event, state) {
362
363
  /**
363
364
  *
364
365
  * @param {unknown} error
@@ -366,7 +367,7 @@ export async function collect_remote_data(data, event, state, options) {
366
367
  */
367
368
  function convert_error(error) {
368
369
  // TODO 4.0 remove the `Promise.resolve(...)`
369
- return Promise.resolve(handle_error_and_jsonify(event, state, options, error));
370
+ return Promise.resolve(handle_error_and_jsonify(event, state, error));
370
371
  }
371
372
 
372
373
  /** @type {Promise<any>[]} */
@@ -493,7 +494,7 @@ export async function collect_remote_data(data, event, state, options) {
493
494
  * @param {string[] | undefined} refreshes
494
495
  */
495
496
  function create_requested_map(refreshes) {
496
- /** @type {Map<string, string[]>} */
497
+ /** @type {Map<string, Set<string>>} */
497
498
  const requested = new Map();
498
499
 
499
500
  for (const key of refreshes ?? []) {
@@ -502,9 +503,9 @@ function create_requested_map(refreshes) {
502
503
  const existing = requested.get(parts.id);
503
504
 
504
505
  if (existing) {
505
- existing.push(parts.payload);
506
+ existing.add(parts.payload);
506
507
  } else {
507
- requested.set(parts.id, [parts.payload]);
508
+ requested.set(parts.id, new Set([parts.payload]));
508
509
  }
509
510
  }
510
511
 
@@ -512,7 +513,7 @@ function create_requested_map(refreshes) {
512
513
  }
513
514
 
514
515
  /** @type {typeof handle_remote_form_post_internal} */
515
- export async function handle_remote_form_post(event, state, manifest, id) {
516
+ export async function handle_remote_form_post(event, state, id) {
516
517
  return record_span({
517
518
  name: 'sveltekit.remote.form.post',
518
519
  attributes: {
@@ -521,7 +522,7 @@ export async function handle_remote_form_post(event, state, manifest, id) {
521
522
  fn: (current) => {
522
523
  const traced_event = merge_tracing(event, current);
523
524
  return with_request_store({ event: traced_event, state }, () =>
524
- handle_remote_form_post_internal(traced_event, state, manifest, id)
525
+ handle_remote_form_post_internal(traced_event, state, id)
525
526
  );
526
527
  }
527
528
  });
@@ -530,17 +531,16 @@ export async function handle_remote_form_post(event, state, manifest, id) {
530
531
  /**
531
532
  * @param {RequestEvent} event
532
533
  * @param {RequestState} state
533
- * @param {SSRManifest} manifest
534
534
  * @param {string} id
535
535
  * @returns {Promise<ServerActionResult>}
536
536
  */
537
- async function handle_remote_form_post_internal(event, state, manifest, id) {
537
+ async function handle_remote_form_post_internal(event, state, id) {
538
538
  const location = get_action_location(event.url);
539
539
  // `hash` and `name` can never contain a `/`, but the JSON-stringified key of a
540
540
  // keyed (`form.for(key)`) instance can — rejoin the remaining segments
541
541
  const [hash, name, ...rest] = id.split('/');
542
542
  const action_id = rest.join('/');
543
- const remotes = manifest._.remotes;
543
+ const remotes = manifest.remotes;
544
544
  const module = Object.hasOwn(remotes, hash) ? await remotes[hash]() : undefined;
545
545
 
546
546
  let form = /** @type {RemoteForm<any, any>} */ (