@sveltejs/kit 3.0.0-next.1 → 3.0.0-next.10

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 (127) hide show
  1. package/package.json +33 -21
  2. package/src/core/adapt/builder.js +29 -43
  3. package/src/core/adapt/index.js +1 -4
  4. package/src/core/config/index.js +132 -71
  5. package/src/core/config/options.js +294 -244
  6. package/src/core/config/types.d.ts +1 -1
  7. package/src/core/env.js +121 -5
  8. package/src/core/generate_manifest/index.js +12 -15
  9. package/src/core/postbuild/analyse.js +7 -19
  10. package/src/core/postbuild/crawl.js +22 -6
  11. package/src/core/postbuild/prerender.js +42 -23
  12. package/src/core/sync/create_manifest_data/index.js +53 -50
  13. package/src/core/sync/sync.js +0 -2
  14. package/src/core/sync/write_client_manifest.js +8 -15
  15. package/src/core/sync/write_env.js +2 -1
  16. package/src/core/sync/write_non_ambient.js +12 -9
  17. package/src/core/sync/write_server.js +13 -14
  18. package/src/core/sync/write_tsconfig.js +16 -8
  19. package/src/core/sync/write_tsconfig_test/package.json +7 -0
  20. package/src/core/sync/write_types/index.js +28 -24
  21. package/src/core/utils.js +2 -2
  22. package/src/exports/env/index.js +12 -0
  23. package/src/exports/hooks/index.js +3 -9
  24. package/src/exports/hooks/sequence.js +3 -2
  25. package/src/exports/index.js +35 -13
  26. package/src/exports/internal/client.js +5 -0
  27. package/src/exports/internal/env.js +2 -2
  28. package/src/exports/internal/index.js +1 -90
  29. package/src/exports/internal/{event.js → server/event.js} +1 -2
  30. package/src/exports/internal/server/index.js +33 -0
  31. package/src/exports/internal/shared.js +89 -0
  32. package/src/exports/node/index.js +62 -15
  33. package/src/exports/params.js +63 -0
  34. package/src/exports/public.d.ts +323 -170
  35. package/src/exports/url.js +86 -0
  36. package/src/exports/vite/build/build_server.js +53 -59
  37. package/src/exports/vite/build/remote.js +18 -11
  38. package/src/exports/vite/build/utils.js +0 -8
  39. package/src/exports/vite/dev/index.js +42 -44
  40. package/src/exports/vite/index.js +810 -593
  41. package/src/exports/vite/preview/index.js +29 -22
  42. package/src/exports/vite/static_analysis/index.js +2 -4
  43. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  44. package/src/exports/vite/utils.js +21 -36
  45. package/src/runtime/app/env/internal.js +4 -4
  46. package/src/runtime/app/env/types.d.ts +1 -1
  47. package/src/runtime/app/environment/index.js +3 -3
  48. package/src/runtime/app/forms.js +24 -7
  49. package/src/runtime/app/paths/client.js +4 -10
  50. package/src/runtime/app/paths/internal/client.js +4 -2
  51. package/src/runtime/app/paths/internal/server.js +2 -23
  52. package/src/runtime/app/paths/public.d.ts +0 -28
  53. package/src/runtime/app/paths/server.js +9 -5
  54. package/src/runtime/app/server/remote/command.js +0 -3
  55. package/src/runtime/app/server/remote/form.js +27 -15
  56. package/src/runtime/app/server/remote/prerender.js +29 -36
  57. package/src/runtime/app/server/remote/query.js +101 -99
  58. package/src/runtime/app/server/remote/requested.js +22 -14
  59. package/src/runtime/app/server/remote/shared.js +30 -26
  60. package/src/runtime/app/state/client.js +1 -2
  61. package/src/runtime/app/stores.js +13 -76
  62. package/src/runtime/client/bundle.js +1 -1
  63. package/src/runtime/client/client-entry.js +3 -0
  64. package/src/runtime/client/client.js +411 -318
  65. package/src/runtime/client/entry.js +24 -3
  66. package/src/runtime/client/fetcher.js +3 -2
  67. package/src/runtime/client/ndjson.js +6 -33
  68. package/src/runtime/client/payload.js +17 -0
  69. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  70. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  71. package/src/runtime/client/remote-functions/form.svelte.js +183 -112
  72. package/src/runtime/client/remote-functions/prerender.svelte.js +29 -8
  73. package/src/runtime/client/remote-functions/query/index.js +7 -14
  74. package/src/runtime/client/remote-functions/query/instance.svelte.js +63 -18
  75. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  76. package/src/runtime/client/remote-functions/query-batch.svelte.js +60 -68
  77. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +60 -18
  78. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  79. package/src/runtime/client/remote-functions/shared.svelte.js +88 -62
  80. package/src/runtime/client/sse.js +32 -0
  81. package/src/runtime/client/state.svelte.js +65 -49
  82. package/src/runtime/client/stream.js +38 -0
  83. package/src/runtime/client/types.d.ts +11 -7
  84. package/src/runtime/client/utils.js +0 -96
  85. package/src/runtime/components/root.svelte +66 -0
  86. package/src/runtime/env/dynamic/private.js +7 -0
  87. package/src/runtime/env/dynamic/public.js +7 -0
  88. package/src/runtime/env/static/private.js +6 -0
  89. package/src/runtime/env/static/public.js +6 -0
  90. package/src/runtime/form-utils.js +100 -22
  91. package/src/runtime/server/cookie.js +69 -52
  92. package/src/runtime/server/csrf.js +65 -0
  93. package/src/runtime/server/data/index.js +14 -18
  94. package/src/runtime/server/env_module.js +0 -5
  95. package/src/runtime/server/index.js +2 -2
  96. package/src/runtime/server/page/actions.js +41 -26
  97. package/src/runtime/server/page/index.js +9 -15
  98. package/src/runtime/server/page/load_data.js +1 -1
  99. package/src/runtime/server/page/render.js +112 -187
  100. package/src/runtime/server/page/respond_with_error.js +7 -8
  101. package/src/runtime/server/page/server_routing.js +27 -18
  102. package/src/runtime/server/remote.js +355 -216
  103. package/src/runtime/server/respond.js +97 -61
  104. package/src/runtime/server/utils.js +32 -9
  105. package/src/runtime/shared.js +83 -13
  106. package/src/runtime/telemetry/otel.js +1 -1
  107. package/src/runtime/types.d.ts +8 -0
  108. package/src/types/ambient.d.ts +10 -5
  109. package/src/types/global-private.d.ts +15 -22
  110. package/src/types/internal.d.ts +89 -74
  111. package/src/types/private.d.ts +33 -1
  112. package/src/utils/error.js +24 -4
  113. package/src/utils/import.js +6 -1
  114. package/src/utils/imports.js +83 -0
  115. package/src/utils/mime.js +9 -0
  116. package/src/utils/params.js +66 -0
  117. package/src/utils/routing.js +90 -44
  118. package/src/utils/shared-iterator.js +5 -0
  119. package/src/utils/streaming.js +14 -4
  120. package/src/utils/url.js +20 -2
  121. package/src/version.js +1 -1
  122. package/types/index.d.ts +467 -537
  123. package/types/index.d.ts.map +22 -39
  124. package/src/core/sync/write_root.js +0 -148
  125. package/src/exports/internal/server.js +0 -22
  126. package/src/types/synthetic/$lib.md +0 -5
  127. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,15 +1,13 @@
1
1
  /** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit' */
2
2
  /** @import { 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, error } from '@sveltejs/kit';
6
5
  import { get_request_store } from '@sveltejs/kit/internal/server';
7
6
  import { stringify, stringify_remote_arg } from '../../../shared.js';
8
7
  import { noop } from '../../../../utils/functions.js';
9
8
  import { app_dir, base } from '$app/paths/internal/server';
10
9
  import {
11
10
  create_validator,
12
- get_cache,
13
11
  get_response,
14
12
  parse_remote_response,
15
13
  run_remote_function
@@ -89,51 +87,46 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
89
87
 
90
88
  /** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
91
89
  const wrapper = (arg) => {
90
+ const { event, state } = get_request_store();
91
+ const payload = stringify_remote_arg(arg, state.transport);
92
+
93
+ // `get_response` (as opposed to bare `get_cache`) also registers the call in the
94
+ // implicit lookup, so that the result is inlined into the page payload (`data.p`)
95
+ // and the client doesn't need to fetch it again upon hydration
92
96
  /** @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);
97
+ const promise = get_response(__, payload, state, async () => {
96
98
  const id = __.id;
97
99
  const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
98
100
 
99
- if (!state.prerendering && !DEV && !event.isRemoteRequest) {
101
+ if (!state.prerendering && !__SVELTEKIT_DEV__ && !event.isRemoteRequest) {
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
+ const response = await fetch(new URL(url, event.url.origin).href);
106
+
107
+ if (!response.ok) {
108
+ throw new Error('Prerendered response not found');
109
+ }
110
+
111
+ const prerendered = /** @type {RemoteFunctionResponse} */ await response.json();
112
+
113
+ if (prerendered.type === 'error') {
114
+ error(prerendered.status, prerendered.error);
115
+ }
116
+
117
+ return parse_remote_response(prerendered.data, state.transport)._;
125
118
  } catch {
126
119
  // not available prerendered, fallback to normal function
127
120
  }
128
121
  }
129
122
 
123
+ // during a prerender run, the same function might be invoked while rendering
124
+ // multiple pages — share the result across the entire run
130
125
  if (state.prerendering?.remote_responses.has(url)) {
131
126
  return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
132
127
  }
133
128
 
134
- const promise = get_response(__, payload, state, () =>
135
- run_remote_function(event, state, false, () => validate(arg), fn)
136
- );
129
+ const promise = run_remote_function(event, state, false, () => validate(arg), fn);
137
130
 
138
131
  if (state.prerendering) {
139
132
  state.prerendering.remote_responses.set(url, promise);
@@ -142,7 +135,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
142
135
  const result = await promise;
143
136
 
144
137
  if (state.prerendering) {
145
- const body = { type: 'result', result: stringify(result, state.transport) };
138
+ const body = { type: 'result', data: stringify({ _: result }, state.transport) };
146
139
  state.prerendering.dependencies.set(url, {
147
140
  body: JSON.stringify(body),
148
141
  response: json(body)
@@ -151,7 +144,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
151
144
 
152
145
  // TODO this is missing error/loading/current/status
153
146
  return result;
154
- })();
147
+ });
155
148
 
156
149
  promise.catch(noop);
157
150
 
@@ -1,8 +1,8 @@
1
- /** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
1
+ /** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction, RequestEvent } from '@sveltejs/kit' */
2
2
  /** @import { RemoteInternals, MaybePromise, RequestState, RemoteQueryLiveInternals, RemoteQueryBatchInternals, RemoteQueryInternals, RemoteLiveQueryUserFunctionReturnType } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
5
- import { create_remote_key, stringify, stringify_remote_arg } from '../../../shared.js';
5
+ import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
6
6
  import { prerendering } from '$app/env/internal';
7
7
  import {
8
8
  create_validator,
@@ -11,10 +11,9 @@ import {
11
11
  run_remote_function,
12
12
  run_remote_generator
13
13
  } from './shared.js';
14
- import { handle_error_and_jsonify } from '../../../server/utils.js';
15
- import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
16
14
  import { noop } from '../../../../utils/functions.js';
17
15
  import { SharedIterator } from '../../../../utils/shared-iterator.js';
16
+ import { handle_error_and_jsonify } from '../../../server/utils.js';
18
17
 
19
18
  /**
20
19
  * Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -78,8 +77,14 @@ export function query(validate_or_fn, maybe_fn) {
78
77
  bind(payload, validated_arg) {
79
78
  const { event, state } = get_request_store();
80
79
 
81
- return create_query_resource(__, payload, state, () =>
82
- run_remote_function(event, state, false, () => validated_arg, fn)
80
+ return create_query_resource(__, payload, event, state, () =>
81
+ run_remote_function(
82
+ event,
83
+ { ...state, is_in_remote_query: true },
84
+ false,
85
+ () => validated_arg,
86
+ fn
87
+ )
83
88
  );
84
89
  }
85
90
  };
@@ -95,8 +100,14 @@ export function query(validate_or_fn, maybe_fn) {
95
100
  const { event, state } = get_request_store();
96
101
  const payload = stringify_remote_arg(arg, state.transport);
97
102
 
98
- return create_query_resource(__, payload, state, () =>
99
- run_remote_function(event, state, false, () => validate(arg), fn)
103
+ return create_query_resource(__, payload, event, state, () =>
104
+ run_remote_function(
105
+ event,
106
+ { ...state, is_in_remote_query: true },
107
+ false,
108
+ () => validate(arg),
109
+ fn
110
+ )
100
111
  );
101
112
  };
102
113
 
@@ -152,7 +163,14 @@ function live(validate_or_fn, maybe_fn) {
152
163
  * @param {any} get_input
153
164
  */
154
165
  const run = (event, state, get_input) =>
155
- run_remote_generator(event, state, false, get_input, fn, __.name);
166
+ run_remote_generator(
167
+ event,
168
+ { ...state, is_in_remote_query: true },
169
+ false,
170
+ get_input,
171
+ fn,
172
+ __.name
173
+ );
156
174
 
157
175
  /** @type {RemoteQueryLiveInternals} */
158
176
  const __ = {
@@ -164,7 +182,7 @@ function live(validate_or_fn, maybe_fn) {
164
182
  bind(payload, validated_arg) {
165
183
  const { event, state } = get_request_store();
166
184
 
167
- return create_live_query_resource(__, payload, state, event.request.signal, () =>
185
+ return create_live_query_resource(__, payload, event, state, () =>
168
186
  run(event, state, () => validated_arg)
169
187
  );
170
188
  }
@@ -181,7 +199,7 @@ function live(validate_or_fn, maybe_fn) {
181
199
  const { event, state } = get_request_store();
182
200
  const payload = stringify_remote_arg(arg, state.transport);
183
201
 
184
- return create_live_query_resource(__, payload, state, event.request.signal, () =>
202
+ return create_live_query_resource(__, payload, event, state, () =>
185
203
  run(event, state, () => validate(arg))
186
204
  );
187
205
  };
@@ -273,7 +291,7 @@ function batch(validate_or_fn, maybe_fn) {
273
291
  try {
274
292
  return await run_remote_function(
275
293
  event,
276
- state,
294
+ { ...state, is_in_remote_query: true },
277
295
  false,
278
296
  async () => Promise.all(entries.map((entry) => entry.get_validated())),
279
297
  async (input) => {
@@ -316,7 +334,7 @@ function batch(validate_or_fn, maybe_fn) {
316
334
 
317
335
  return run_remote_function(
318
336
  event,
319
- state,
337
+ { ...state, is_in_remote_query: true },
320
338
  false,
321
339
  async () => Promise.all(args.map(validate)),
322
340
  async (/** @type {any[]} */ input) => {
@@ -326,15 +344,13 @@ function batch(validate_or_fn, maybe_fn) {
326
344
  input.map(async (arg, i) => {
327
345
  try {
328
346
  const data = get_result(arg, i);
329
- return { type: 'result', data: stringify(data, state.transport) };
347
+ return { type: 'result', data };
330
348
  } catch (error) {
349
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
350
+
331
351
  return {
332
352
  type: 'error',
333
- error: await handle_error_and_jsonify(event, state, options, error),
334
- status:
335
- error instanceof HttpError || error instanceof SvelteKitError
336
- ? error.status
337
- : 500
353
+ error: transformed
338
354
  };
339
355
  }
340
356
  })
@@ -343,9 +359,11 @@ function batch(validate_or_fn, maybe_fn) {
343
359
  );
344
360
  },
345
361
  bind(payload, validated_arg) {
346
- const { state } = get_request_store();
362
+ const { event, state } = get_request_store();
347
363
 
348
- return create_query_resource(__, payload, state, () => enqueue(payload, () => validated_arg));
364
+ return create_query_resource(__, payload, event, state, () =>
365
+ enqueue(payload, () => validated_arg)
366
+ );
349
367
  }
350
368
  };
351
369
 
@@ -357,10 +375,10 @@ function batch(validate_or_fn, maybe_fn) {
357
375
  );
358
376
  }
359
377
 
360
- const { state } = get_request_store();
378
+ const { event, state } = get_request_store();
361
379
  const payload = stringify_remote_arg(arg, state.transport);
362
380
 
363
- return create_query_resource(__, payload, state, () =>
381
+ return create_query_resource(__, payload, event, state, () =>
364
382
  // Collect all the calls to the same query in the same macrotask,
365
383
  // then execute them as one backend request.
366
384
  enqueue(payload, () => validate(arg))
@@ -372,14 +390,47 @@ function batch(validate_or_fn, maybe_fn) {
372
390
  return wrapper;
373
391
  }
374
392
 
393
+ /**
394
+ * Include this value in the returned payload...
395
+ * @param {RequestEvent} event
396
+ * @param {RequestState} state
397
+ * @param {RemoteInternals} internals
398
+ * @param {string} payload
399
+ * @param {() => Promise<any>} fn
400
+ */
401
+ export function refresh(event, state, internals, payload, fn) {
402
+ if (!internals.id) {
403
+ // unless this is a non-exported (i.e. private) query...
404
+ return;
405
+ }
406
+
407
+ if (!event.isRemoteRequest) {
408
+ // or this is a no-JS form submission
409
+ return;
410
+ }
411
+
412
+ const key = create_remote_key(internals.id, payload);
413
+
414
+ // `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
+ // 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
+ // is reused instead of re-running the query.
419
+ (state.remote.explicit ??= new Map()).set(key, {
420
+ internals,
421
+ fn
422
+ });
423
+ }
424
+
375
425
  /**
376
426
  * @param {RemoteInternals} __
377
427
  * @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
428
+ * @param {RequestEvent} event
378
429
  * @param {RequestState} state
379
430
  * @param {() => Promise<any>} fn
380
431
  * @returns {RemoteQuery<any>}
381
432
  */
382
- function create_query_resource(__, payload, state, fn) {
433
+ function create_query_resource(__, payload, event, state, fn) {
383
434
  /** @type {Promise<any> | null} */
384
435
  let promise = null;
385
436
 
@@ -391,7 +442,11 @@ function create_query_resource(__, payload, state, fn) {
391
442
  // accessing data properties needs to kick off the work
392
443
  // so that it gets seeded in the hydration cache
393
444
  // and becomes available on the client
394
- void (__.id && state.is_in_render && get_promise());
445
+ if (__.id && state.is_in_render) {
446
+ // swallow rejections so they don't crash the server — the error is
447
+ // serialized into the response and surfaced on the client instead
448
+ get_promise().catch(noop);
449
+ }
395
450
  };
396
451
 
397
452
  return {
@@ -420,20 +475,19 @@ function create_query_resource(__, payload, state, fn) {
420
475
  return false;
421
476
  },
422
477
  refresh() {
423
- const { event } = get_request_store();
424
- if (!event.isRemoteRequest) {
425
- // If the form submission is not a remote request, refreshing the data is
426
- // useless, because it can't be returned to the client.
427
- return Promise.resolve();
428
- }
429
- const refresh_context = get_refresh_context(__, 'refresh', payload);
430
- const is_immediate_refresh = !refresh_context.cache[refresh_context.payload];
431
- const value = is_immediate_refresh ? get_promise() : fn();
432
- return update_refresh_value(refresh_context, value, is_immediate_refresh);
478
+ promise = null;
479
+ delete get_cache(__, state)[payload];
480
+
481
+ refresh(event, state, __, payload, get_promise);
482
+
483
+ return Promise.resolve();
433
484
  },
434
485
  /** @param {any} value */
435
486
  set(value) {
436
- return update_refresh_value(get_refresh_context(__, 'set', payload), value);
487
+ const p = (promise = Promise.resolve(value));
488
+ get_cache(__, state)[payload] = p;
489
+
490
+ refresh(event, state, __, payload, () => p);
437
491
  },
438
492
  /** @type {Promise<any>['then']} */
439
493
  then(onfulfilled, onrejected) {
@@ -451,12 +505,12 @@ function create_query_resource(__, payload, state, fn) {
451
505
  /**
452
506
  * @param {RemoteQueryLiveInternals} __
453
507
  * @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
508
+ * @param {RequestEvent} event
454
509
  * @param {RequestState} state
455
- * @param {AbortSignal} signal — the request signal; aborts in-flight iteration when the client disconnects
456
510
  * @param {() => AsyncGenerator<any, void, void>} get_generator
457
511
  * @returns {RemoteLiveQuery<any>}
458
512
  */
459
- function create_live_query_resource(__, payload, state, signal, get_generator) {
513
+ function create_live_query_resource(__, payload, event, state, get_generator) {
460
514
  /** @type {Promise<any> | null} */
461
515
  let promise = null;
462
516
 
@@ -472,7 +526,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
472
526
  };
473
527
 
474
528
  const populate_hydratable = () => {
475
- void (__.id && state.is_in_render && get_promise());
529
+ if (__.id && state.is_in_render) {
530
+ // swallow rejections so they don't crash the server — the error is
531
+ // serialized into the response and surfaced on the client instead
532
+ get_promise().catch(noop);
533
+ }
476
534
  };
477
535
 
478
536
  return {
@@ -509,15 +567,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
509
567
  return false;
510
568
  },
511
569
  reconnect() {
512
- const reconnects = state.remote.reconnects;
570
+ promise = null;
571
+ delete get_cache(__, state)[payload];
513
572
 
514
- if (!reconnects) {
515
- throw new Error(
516
- `Cannot call reconnect on query.live '${__.name}' because it is not executed in the context of a command/form remote function`
517
- );
518
- }
573
+ refresh(event, state, __, payload, get_promise);
519
574
 
520
- reconnects.set(create_remote_key(__.id, payload), get_promise());
521
575
  return Promise.resolve();
522
576
  },
523
577
  /** @ts-expect-error This method no longer exists */
@@ -535,7 +589,7 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
535
589
  const cache = (state.remote.live_iterators ??= new Map());
536
590
  let cached = cache.get(key);
537
591
  if (!cached) {
538
- cached = create_shared_live_iterator(signal, get_generator);
592
+ cached = create_shared_live_iterator(event.request.signal, get_generator);
539
593
  cache.set(key, cached);
540
594
  }
541
595
  return cached.subscribe();
@@ -613,55 +667,3 @@ function create_shared_live_iterator(signal, get_generator) {
613
667
  // Add batch as a property to the query function
614
668
  Object.defineProperty(query, 'batch', { value: batch, enumerable: true });
615
669
  Object.defineProperty(query, 'live', { value: live, enumerable: true });
616
-
617
- /**
618
- * @param {RemoteInternals} __
619
- * @param {'set' | 'refresh'} action
620
- * @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
621
- * @returns {{ __: RemoteInternals; state: any; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }}
622
- */
623
- function get_refresh_context(__, action, payload) {
624
- const { state } = get_request_store();
625
- const { refreshes } = state.remote;
626
-
627
- if (!refreshes) {
628
- const name = __.type === 'query_batch' ? `query.batch '${__.name}'` : `query '${__.name}'`;
629
- throw new Error(
630
- `Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function`
631
- );
632
- }
633
-
634
- const cache = get_cache(__, state);
635
- const refreshes_key = create_remote_key(__.id, payload);
636
-
637
- return { __, state, refreshes, refreshes_key, cache, payload };
638
- }
639
-
640
- /**
641
- * @param {{ __: RemoteInternals; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }} context
642
- * @param {any} value
643
- * @param {boolean} [is_immediate_refresh=false]
644
- * @returns {Promise<void>}
645
- */
646
- function update_refresh_value(
647
- { __, refreshes, refreshes_key, cache, payload },
648
- value,
649
- is_immediate_refresh = false
650
- ) {
651
- const promise = Promise.resolve(value);
652
-
653
- if (!is_immediate_refresh) {
654
- cache[payload] = { serialize: true, data: promise };
655
- }
656
-
657
- if (__.id) {
658
- refreshes.set(refreshes_key, promise);
659
- }
660
-
661
- promise.catch(noop);
662
-
663
- // we return an immediately-resolving promise so that the `refresh()` signature is consistent,
664
- // but it doesn't delay anything if awaited inside a command. this way, people aren't
665
- // penalised if they do `await q1.refresh(); await q2.refresh()`
666
- return Promise.resolve();
667
- }
@@ -1,12 +1,15 @@
1
1
  /** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction, RequestedResult, QueryRequestedResult, LiveQueryRequestedResult } from '@sveltejs/kit' */
2
2
  /** @import { MaybePromise, RemoteAnyQueryInternals } from 'types' */
3
+ import { HttpError } from '@sveltejs/kit/internal';
3
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
4
- import { create_remote_key, parse_remote_arg } from '../../../shared.js';
5
+ import { parse_remote_arg } from '../../../shared.js';
5
6
  import { noop } from '../../../../utils/functions.js';
7
+ import { get_cache } from './shared.js';
8
+ import { refresh } from './query.js';
6
9
 
7
10
  /**
8
- * In the context of a remote `command` or `form` request, returns an iterable
9
- * of `{ arg, query }` entries for the refreshes requested by the client, up to
11
+ * Inside a remote `command` or `form` callback, returns an iterable
12
+ * of `{ arg, query }` entries for the query instances the client asked to refresh, up to
10
13
  * the supplied `limit`. Each `query` is a `RemoteQuery` bound to the original
11
14
  * client-side cache key, so `refresh()` / `set()` propagate correctly even when
12
15
  * the query's schema transforms the input. `arg` is the *validated* argument,
@@ -15,6 +18,8 @@ import { noop } from '../../../../utils/functions.js';
15
18
  *
16
19
  * Arguments that fail validation or exceed `limit` are recorded as failures in
17
20
  * the response to the client.
21
+ * See [Client-requested refreshes](https://svelte.dev/docs/kit/remote-functions#Single-flight-mutations-Client-requested-refreshes)
22
+ * for usage in a remote `command` or `form`.
18
23
  *
19
24
  * @example
20
25
  * ```ts
@@ -51,14 +56,16 @@ import { noop } from '../../../../utils/functions.js';
51
56
  * @returns {QueryRequestedResult<Validated, Output>}
52
57
  */
53
58
  /**
54
- * In the context of a remote `command` or `form` request, returns an iterable
55
- * of `{ arg, query }` entries for the reconnects requested by the client, up to
59
+ * Inside a remote `command` or `form` callback, returns an iterable
60
+ * of `{ arg, query }` entries for the live query instances the client asked to reconnect, up to
56
61
  * the supplied `limit`. Each `query` is a `RemoteLiveQuery` bound to the original
57
62
  * client-side cache key, so `reconnect()` propagates correctly even when
58
63
  * the query's schema transforms the input. `arg` is the *validated* argument.
59
64
  *
60
65
  * Arguments that fail validation or exceed `limit` are recorded as failures in
61
66
  * the response to the client.
67
+ * See [Client-requested refreshes](https://svelte.dev/docs/kit/remote-functions#Single-flight-mutations-Client-requested-refreshes)
68
+ * for usage in a remote `command` or `form`.
62
69
  *
63
70
  * @example
64
71
  * ```ts
@@ -95,7 +102,7 @@ import { noop } from '../../../../utils/functions.js';
95
102
  * @returns {RequestedResult<Validated, Output>}
96
103
  */
97
104
  export function requested(query, limit) {
98
- const { state } = get_request_store();
105
+ const { event, state } = get_request_store();
99
106
  const internals = /** @type {RemoteAnyQueryInternals | undefined} */ (
100
107
  /** @type {any} */ (query).__
101
108
  );
@@ -115,15 +122,12 @@ export function requested(query, limit) {
115
122
 
116
123
  const requested = state.remote.requested;
117
124
  const payloads = requested?.get(__.id) ?? [];
125
+
118
126
  // note: don't initialize these maps here -- they will be initialized by the
119
127
  // command/form wrapper when we enter them, and if we initialize them here
120
128
  // we will enable requested(...) in contexts where it shouldn't be allowed,
121
129
  // such as load functions or other server functions
122
- const refreshes = state.remote.refreshes;
123
- const reconnects = state.remote.reconnects;
124
- const store = __.type === 'query_live' ? reconnects : refreshes;
125
-
126
- if (!store) {
130
+ if (!state.is_in_remote_form_or_command) {
127
131
  throw new Error(
128
132
  'requested(...) can only be called in the context of a command/form remote function'
129
133
  );
@@ -131,6 +135,9 @@ export function requested(query, limit) {
131
135
  const [selected, skipped] = split_limit(payloads, limit);
132
136
 
133
137
  /**
138
+ * Registers the failure exactly like `.set()` registers a value: the error record
139
+ * is serialized to the client (putting the query there into a failed state), and
140
+ * subsequent server-side calls of the query with the same argument reject with it.
134
141
  * @param {string} payload
135
142
  * @param {unknown} error
136
143
  */
@@ -138,14 +145,15 @@ export function requested(query, limit) {
138
145
  const promise = Promise.reject(error);
139
146
  promise.catch(noop);
140
147
 
141
- const key = create_remote_key(__.id, payload);
142
- store.set(key, promise);
148
+ get_cache(__, state)[payload] = promise;
149
+ refresh(event, state, __, payload, () => promise);
143
150
  };
144
151
 
145
152
  for (const payload of skipped) {
146
153
  record_failure(
147
154
  payload,
148
- new Error(
155
+ new HttpError(
156
+ 400,
149
157
  `Requested refresh was rejected because it exceeded requested(${__.name}, ${limit}) limit`
150
158
  )
151
159
  );
@@ -2,10 +2,7 @@
2
2
  /** @import { ServerHooks, MaybePromise, RequestState, RemoteInternals, RequestStore, RemoteLiveQueryUserFunctionReturnType } from 'types' */
3
3
  import { parse } from 'devalue';
4
4
  import { error } from '@sveltejs/kit';
5
- import { hydratable } from 'svelte';
6
5
  import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
7
- import { create_remote_key, stringify } from '../../../shared.js';
8
- import { noop } from '../../../../utils/functions.js';
9
6
 
10
7
  /**
11
8
  * @param {any} validate_or_fn
@@ -38,13 +35,11 @@ export function create_validator(validate_or_fn, maybe_fn) {
38
35
 
39
36
  // if the `issues` field exists, the validation failed
40
37
  if (result.issues) {
41
- error(
42
- 400,
43
- await state.handleValidationError({
44
- issues: result.issues,
45
- event
46
- })
47
- );
38
+ const body = await state.handleValidationError({
39
+ issues: result.issues,
40
+ event
41
+ });
42
+ error(body.status ?? 400, body);
48
43
  }
49
44
 
50
45
  return result.value;
@@ -76,24 +71,13 @@ export async function get_response(internals, payload, state, get_result) {
76
71
  await 0;
77
72
 
78
73
  const cache = get_cache(internals, state);
79
- const entry = (cache[payload] ??= {
80
- serialize: false,
81
- data: get_result()
82
- });
83
74
 
84
- entry.serialize ||= !!state.is_in_universal_load;
85
-
86
- if (state.is_in_render && internals.id) {
87
- const remote_key = create_remote_key(internals.id, payload);
88
-
89
- void Promise.resolve(entry.data)
90
- .then((value) => {
91
- void hydratable(remote_key, () => stringify(value, state.transport));
92
- })
93
- .catch(noop);
75
+ if (!state.is_in_remote_query) {
76
+ // if this is a top-level (not nested) `await myQuery()`, include it in the serialized response
77
+ get_implicit_lookup(internals, state)[payload] = get_result;
94
78
  }
95
79
 
96
- return entry.data;
80
+ return (cache[payload] ??= get_result());
97
81
  }
98
82
 
99
83
  /**
@@ -239,10 +223,15 @@ function to_iterator(source, name) {
239
223
  }
240
224
 
241
225
  /**
226
+ * Note that `state` is deliberately not optional: resources that capture the request
227
+ * state at creation must pass it explicitly, because reading it from the request store
228
+ * at call time is only equivalent on runtimes with `AsyncLocalStorage` support.
229
+ * Callers without a captured state (such as the module-level `form` instance getters)
230
+ * should pass `get_request_store().state` themselves.
242
231
  * @param {RemoteInternals} internals
243
232
  * @param {RequestState} state
244
233
  */
245
- export function get_cache(internals, state = get_request_store().state) {
234
+ export function get_cache(internals, state) {
246
235
  let cache = state.remote.data?.get(internals);
247
236
 
248
237
  if (cache === undefined) {
@@ -252,3 +241,18 @@ export function get_cache(internals, state = get_request_store().state) {
252
241
 
253
242
  return cache;
254
243
  }
244
+
245
+ /**
246
+ * @param {RemoteInternals} internals
247
+ * @param {RequestState} state
248
+ */
249
+ export function get_implicit_lookup(internals, state) {
250
+ let cache = state.remote.implicit?.get(internals);
251
+
252
+ if (cache === undefined) {
253
+ cache = {};
254
+ (state.remote.implicit ??= new Map()).set(internals, cache);
255
+ }
256
+
257
+ return cache;
258
+ }
@@ -3,7 +3,6 @@ import {
3
3
  navigating as _navigating,
4
4
  updated as _updated
5
5
  } from '../../client/state.svelte.js';
6
- import { stores } from '../../client/client.js';
7
6
 
8
7
  export const page = {
9
8
  get data() {
@@ -57,5 +56,5 @@ export const updated = {
57
56
  get current() {
58
57
  return _updated.current;
59
58
  },
60
- check: stores.updated.check
59
+ check: _updated.check
61
60
  };