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

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 (45) hide show
  1. package/package.json +2 -2
  2. package/src/core/postbuild/analyse.js +0 -8
  3. package/src/core/postbuild/prerender.js +2 -0
  4. package/src/core/sync/create_manifest_data/index.js +24 -1
  5. package/src/core/sync/write_env.js +2 -1
  6. package/src/exports/internal/env.js +1 -1
  7. package/src/exports/public.d.ts +1 -1
  8. package/src/exports/vite/build/build_server.js +47 -58
  9. package/src/exports/vite/build/remote.js +18 -11
  10. package/src/exports/vite/build/utils.js +0 -8
  11. package/src/exports/vite/index.js +221 -217
  12. package/src/exports/vite/static_analysis/index.js +2 -4
  13. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  14. package/src/exports/vite/utils.js +1 -12
  15. package/src/runtime/app/server/remote/command.js +0 -3
  16. package/src/runtime/app/server/remote/form.js +18 -13
  17. package/src/runtime/app/server/remote/prerender.js +28 -34
  18. package/src/runtime/app/server/remote/query.js +105 -94
  19. package/src/runtime/app/server/remote/requested.js +14 -10
  20. package/src/runtime/app/server/remote/shared.js +25 -19
  21. package/src/runtime/client/client.js +19 -13
  22. package/src/runtime/client/ndjson.js +6 -33
  23. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  24. package/src/runtime/client/remote-functions/form.svelte.js +62 -82
  25. package/src/runtime/client/remote-functions/prerender.svelte.js +14 -6
  26. package/src/runtime/client/remote-functions/query/index.js +6 -14
  27. package/src/runtime/client/remote-functions/query/instance.svelte.js +20 -0
  28. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  29. package/src/runtime/client/remote-functions/query-batch.svelte.js +59 -68
  30. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +21 -6
  31. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  32. package/src/runtime/client/remote-functions/shared.svelte.js +76 -59
  33. package/src/runtime/client/sse.js +32 -0
  34. package/src/runtime/client/stream.js +38 -0
  35. package/src/runtime/server/page/render.js +23 -80
  36. package/src/runtime/server/page/server_routing.js +20 -15
  37. package/src/runtime/server/remote.js +296 -204
  38. package/src/runtime/server/respond.js +4 -2
  39. package/src/runtime/shared.js +83 -13
  40. package/src/types/global-private.d.ts +3 -3
  41. package/src/types/internal.d.ts +54 -35
  42. package/src/utils/error.js +12 -0
  43. package/src/version.js +1 -1
  44. package/types/index.d.ts +17 -7
  45. package/types/index.d.ts.map +5 -3
@@ -2,6 +2,7 @@ import path from 'node:path';
2
2
  import { posixify } from '../../utils/os.js';
3
3
  import { negotiate } from '../../utils/http.js';
4
4
  import { escape_html } from '../../utils/escape.js';
5
+ import { stackless } from '../../utils/error.js';
5
6
  import { dedent } from '../../core/sync/utils.js';
6
7
  import {
7
8
  app_server,
@@ -148,18 +149,6 @@ export function normalize_id(id, lib, cwd) {
148
149
  return posixify(id);
149
150
  }
150
151
 
151
- /**
152
- * For times when you need to throw an error, but without
153
- * displaying a useless stack trace (since the developer
154
- * can't do anything useful with it)
155
- * @param {string} message
156
- */
157
- export function stackless(message) {
158
- const error = new Error(message);
159
- error.stack = '';
160
- return error;
161
- }
162
-
163
152
  export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
164
153
 
165
154
  /**
@@ -77,9 +77,6 @@ export function command(validate_or_fn, maybe_fn) {
77
77
  );
78
78
  }
79
79
 
80
- state.remote.refreshes ??= new Map();
81
- state.remote.reconnects ??= new Map();
82
-
83
80
  const promise = Promise.resolve(
84
81
  run_remote_function(event, state, true, () => validate(arg), fn)
85
82
  );
@@ -9,7 +9,7 @@ import {
9
9
  normalize_issue,
10
10
  flatten_issues
11
11
  } from '../../../form-utils.js';
12
- import { get_cache, run_remote_function } from './shared.js';
12
+ import { get_cache, get_implicit_lookup, run_remote_function } from './shared.js';
13
13
  import { ValidationError } from '@sveltejs/kit/internal';
14
14
 
15
15
  /**
@@ -108,9 +108,6 @@ export function form(validate_or_fn, maybe_fn) {
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 {
@@ -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)[__.action_id ?? __.id] = () => cache[''];
137
139
  }
138
140
 
139
141
  return output;
@@ -149,28 +151,30 @@ export function form(validate_or_fn, maybe_fn) {
149
151
 
150
152
  Object.defineProperty(instance, 'fields', {
151
153
  get() {
154
+ // the form instance is created once per module and shared across requests,
155
+ // so the current request's state has to be resolved at access time
152
156
  return create_field_proxy(
153
157
  {},
154
- () => get_cache(__)?.['']?.data?.input ?? {},
158
+ () => get_cache(__, get_request_store().state)?.['']?.input ?? {},
155
159
  (path, value) => {
156
- const cache = get_cache(__);
160
+ const cache = get_cache(__, get_request_store().state);
157
161
  const entry = cache[''];
158
162
 
159
- if (entry?.data?.submission) {
163
+ if (entry?.submission) {
160
164
  // don't override a submission
161
165
  return;
162
166
  }
163
167
 
164
168
  if (path.length === 0) {
165
- (cache[''] ??= { serialize: true, data: {} }).data.input = value;
169
+ (cache[''] ??= {}).input = value;
166
170
  return;
167
171
  }
168
172
 
169
- const input = entry?.data?.input ?? {};
173
+ const input = entry?.input ?? {};
170
174
  deep_set(input, path.map(String), value);
171
- (cache[''] ??= { serialize: true, data: {} }).data.input = input;
175
+ (cache[''] ??= {}).input = input;
172
176
  },
173
- () => flatten_issues(get_cache(__)?.['']?.data?.issues ?? [])
177
+ () => flatten_issues(get_cache(__, get_request_store().state)?.['']?.issues ?? [])
174
178
  );
175
179
  }
176
180
  });
@@ -178,7 +182,7 @@ export function form(validate_or_fn, maybe_fn) {
178
182
  Object.defineProperty(instance, 'result', {
179
183
  get() {
180
184
  try {
181
- return get_cache(__)?.['']?.data?.result;
185
+ return get_cache(__, get_request_store().state)?.['']?.result;
182
186
  } catch {
183
187
  return undefined;
184
188
  }
@@ -222,6 +226,7 @@ export function form(validate_or_fn, maybe_fn) {
222
226
  if (!instance) {
223
227
  instance = create_instance(key);
224
228
  instance.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key))}`;
229
+ instance.__.action_id = `${__.id}/${JSON.stringify(key)}`;
225
230
  instance.__.name = __.name;
226
231
 
227
232
  state.remote.forms.set(cache_key, instance);
@@ -1,7 +1,7 @@
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';
4
+ import { json, error } from '@sveltejs/kit';
5
5
  import { DEV } from 'esm-env';
6
6
  import { get_request_store } from '@sveltejs/kit/internal/server';
7
7
  import { stringify, stringify_remote_arg } from '../../../shared.js';
@@ -9,7 +9,6 @@ import { noop } from '../../../../utils/functions.js';
9
9
  import { app_dir, base } from '$app/paths/internal/server';
10
10
  import {
11
11
  create_validator,
12
- get_cache,
13
12
  get_response,
14
13
  parse_remote_response,
15
14
  run_remote_function
@@ -89,51 +88,46 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
89
88
 
90
89
  /** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
91
90
  const wrapper = (arg) => {
91
+ const { event, state } = get_request_store();
92
+ const payload = stringify_remote_arg(arg, state.transport);
93
+
94
+ // `get_response` (as opposed to bare `get_cache`) also registers the call in the
95
+ // implicit lookup, so that the result is inlined into the page payload (`data.p`)
96
+ // and the client doesn't need to fetch it again upon hydration
92
97
  /** @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);
98
+ const promise = get_response(__, payload, state, async () => {
96
99
  const id = __.id;
97
100
  const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
98
101
 
99
102
  if (!state.prerendering && !DEV && !event.isRemoteRequest) {
100
103
  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
- });
104
+ // TODO adapters can provide prerendered data more efficiently than
105
+ // fetching from the public internet
106
+ const response = await fetch(new URL(url, event.url.origin).href);
107
+
108
+ if (!response.ok) {
109
+ throw new Error('Prerendered response not found');
110
+ }
111
+
112
+ const prerendered = /** @type {RemoteFunctionResponse} */ await response.json();
113
+
114
+ if (prerendered.type === 'error') {
115
+ error(prerendered.status, prerendered.error);
116
+ }
117
+
118
+ return parse_remote_response(prerendered.data, state.transport)._;
125
119
  } catch {
126
120
  // not available prerendered, fallback to normal function
127
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)
136
- );
130
+ const promise = run_remote_function(event, state, false, () => validate(arg), fn);
137
131
 
138
132
  if (state.prerendering) {
139
133
  state.prerendering.remote_responses.set(url, promise);
@@ -142,7 +136,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
142
136
  const result = await promise;
143
137
 
144
138
  if (state.prerendering) {
145
- const body = { type: 'result', result: stringify(result, state.transport) };
139
+ const body = { type: 'result', data: stringify({ _: result }, state.transport) };
146
140
  state.prerendering.dependencies.set(url, {
147
141
  body: JSON.stringify(body),
148
142
  response: json(body)
@@ -151,7 +145,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
151
145
 
152
146
  // TODO this is missing error/loading/current/status
153
147
  return result;
154
- })();
148
+ });
155
149
 
156
150
  promise.catch(noop);
157
151
 
@@ -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,10 @@ 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';
17
+ import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
18
18
 
19
19
  /**
20
20
  * Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -78,8 +78,14 @@ export function query(validate_or_fn, maybe_fn) {
78
78
  bind(payload, validated_arg) {
79
79
  const { event, state } = get_request_store();
80
80
 
81
- return create_query_resource(__, payload, state, () =>
82
- run_remote_function(event, state, false, () => validated_arg, fn)
81
+ return create_query_resource(__, payload, event, state, () =>
82
+ run_remote_function(
83
+ event,
84
+ { ...state, is_in_remote_query: true },
85
+ false,
86
+ () => validated_arg,
87
+ fn
88
+ )
83
89
  );
84
90
  }
85
91
  };
@@ -95,8 +101,14 @@ export function query(validate_or_fn, maybe_fn) {
95
101
  const { event, state } = get_request_store();
96
102
  const payload = stringify_remote_arg(arg, state.transport);
97
103
 
98
- return create_query_resource(__, payload, state, () =>
99
- run_remote_function(event, state, false, () => validate(arg), fn)
104
+ return create_query_resource(__, payload, event, state, () =>
105
+ run_remote_function(
106
+ event,
107
+ { ...state, is_in_remote_query: true },
108
+ false,
109
+ () => validate(arg),
110
+ fn
111
+ )
100
112
  );
101
113
  };
102
114
 
@@ -152,7 +164,14 @@ function live(validate_or_fn, maybe_fn) {
152
164
  * @param {any} get_input
153
165
  */
154
166
  const run = (event, state, get_input) =>
155
- run_remote_generator(event, state, false, get_input, fn, __.name);
167
+ run_remote_generator(
168
+ event,
169
+ { ...state, is_in_remote_query: true },
170
+ false,
171
+ get_input,
172
+ fn,
173
+ __.name
174
+ );
156
175
 
157
176
  /** @type {RemoteQueryLiveInternals} */
158
177
  const __ = {
@@ -164,7 +183,7 @@ function live(validate_or_fn, maybe_fn) {
164
183
  bind(payload, validated_arg) {
165
184
  const { event, state } = get_request_store();
166
185
 
167
- return create_live_query_resource(__, payload, state, event.request.signal, () =>
186
+ return create_live_query_resource(__, payload, event, state, () =>
168
187
  run(event, state, () => validated_arg)
169
188
  );
170
189
  }
@@ -181,7 +200,7 @@ function live(validate_or_fn, maybe_fn) {
181
200
  const { event, state } = get_request_store();
182
201
  const payload = stringify_remote_arg(arg, state.transport);
183
202
 
184
- return create_live_query_resource(__, payload, state, event.request.signal, () =>
203
+ return create_live_query_resource(__, payload, event, state, () =>
185
204
  run(event, state, () => validate(arg))
186
205
  );
187
206
  };
@@ -273,7 +292,7 @@ function batch(validate_or_fn, maybe_fn) {
273
292
  try {
274
293
  return await run_remote_function(
275
294
  event,
276
- state,
295
+ { ...state, is_in_remote_query: true },
277
296
  false,
278
297
  async () => Promise.all(entries.map((entry) => entry.get_validated())),
279
298
  async (input) => {
@@ -316,7 +335,7 @@ function batch(validate_or_fn, maybe_fn) {
316
335
 
317
336
  return run_remote_function(
318
337
  event,
319
- state,
338
+ { ...state, is_in_remote_query: true },
320
339
  false,
321
340
  async () => Promise.all(args.map(validate)),
322
341
  async (/** @type {any[]} */ input) => {
@@ -326,7 +345,7 @@ function batch(validate_or_fn, maybe_fn) {
326
345
  input.map(async (arg, i) => {
327
346
  try {
328
347
  const data = get_result(arg, i);
329
- return { type: 'result', data: stringify(data, state.transport) };
348
+ return { type: 'result', data };
330
349
  } catch (error) {
331
350
  return {
332
351
  type: 'error',
@@ -343,9 +362,11 @@ function batch(validate_or_fn, maybe_fn) {
343
362
  );
344
363
  },
345
364
  bind(payload, validated_arg) {
346
- const { state } = get_request_store();
365
+ const { event, state } = get_request_store();
347
366
 
348
- return create_query_resource(__, payload, state, () => enqueue(payload, () => validated_arg));
367
+ return create_query_resource(__, payload, event, state, () =>
368
+ enqueue(payload, () => validated_arg)
369
+ );
349
370
  }
350
371
  };
351
372
 
@@ -357,10 +378,10 @@ function batch(validate_or_fn, maybe_fn) {
357
378
  );
358
379
  }
359
380
 
360
- const { state } = get_request_store();
381
+ const { event, state } = get_request_store();
361
382
  const payload = stringify_remote_arg(arg, state.transport);
362
383
 
363
- return create_query_resource(__, payload, state, () =>
384
+ return create_query_resource(__, payload, event, state, () =>
364
385
  // Collect all the calls to the same query in the same macrotask,
365
386
  // then execute them as one backend request.
366
387
  enqueue(payload, () => validate(arg))
@@ -372,14 +393,53 @@ function batch(validate_or_fn, maybe_fn) {
372
393
  return wrapper;
373
394
  }
374
395
 
396
+ /**
397
+ * Include this value in the returned payload...
398
+ * @param {RequestEvent} event
399
+ * @param {RequestState} state
400
+ * @param {RemoteInternals} internals
401
+ * @param {string} payload
402
+ * @param {() => Promise<any>} fn
403
+ */
404
+ export function refresh(event, state, internals, payload, fn) {
405
+ if (!internals.id) {
406
+ // unless this is a non-exported (i.e. private) query...
407
+ return;
408
+ }
409
+
410
+ if (!event.isRemoteRequest) {
411
+ // or this is a no-JS form submission
412
+ return;
413
+ }
414
+
415
+ const key = create_remote_key(internals.id, payload);
416
+
417
+ // `fn()` is invoked eagerly here, which starts running the query immediately.
418
+ // The resulting promise is normally awaited (and its rejection handled) in
419
+ // `collect_remote_data`, but some code paths (e.g. a command throwing a
420
+ // non-redirect error) never reach that point. Attach a no-op `catch` to the
421
+ // promise so a rejection is always considered handled and can never become an
422
+ // unhandled promise rejection (which crashes the process on modern Node).
423
+ // We still store the original promise so `collect_remote_data` can serialize
424
+ // either its value or its error as before.
425
+ const promise = fn();
426
+ promise.catch(() => {});
427
+
428
+ (state.remote.explicit ??= new Map()).set(key, {
429
+ internals,
430
+ promise
431
+ });
432
+ }
433
+
375
434
  /**
376
435
  * @param {RemoteInternals} __
377
436
  * @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
437
+ * @param {RequestEvent} event
378
438
  * @param {RequestState} state
379
439
  * @param {() => Promise<any>} fn
380
440
  * @returns {RemoteQuery<any>}
381
441
  */
382
- function create_query_resource(__, payload, state, fn) {
442
+ function create_query_resource(__, payload, event, state, fn) {
383
443
  /** @type {Promise<any> | null} */
384
444
  let promise = null;
385
445
 
@@ -391,7 +451,11 @@ function create_query_resource(__, payload, state, fn) {
391
451
  // accessing data properties needs to kick off the work
392
452
  // so that it gets seeded in the hydration cache
393
453
  // and becomes available on the client
394
- void (__.id && state.is_in_render && get_promise());
454
+ if (__.id && state.is_in_render) {
455
+ // swallow rejections so they don't crash the server — the error is
456
+ // serialized into the response and surfaced on the client instead
457
+ get_promise().catch(noop);
458
+ }
395
459
  };
396
460
 
397
461
  return {
@@ -420,20 +484,19 @@ function create_query_resource(__, payload, state, fn) {
420
484
  return false;
421
485
  },
422
486
  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);
487
+ promise = null;
488
+ delete get_cache(__, state)[payload];
489
+
490
+ refresh(event, state, __, payload, get_promise);
491
+
492
+ return Promise.resolve();
433
493
  },
434
494
  /** @param {any} value */
435
495
  set(value) {
436
- return update_refresh_value(get_refresh_context(__, 'set', payload), value);
496
+ const p = (promise = Promise.resolve(value));
497
+ get_cache(__, state)[payload] = p;
498
+
499
+ refresh(event, state, __, payload, () => p);
437
500
  },
438
501
  /** @type {Promise<any>['then']} */
439
502
  then(onfulfilled, onrejected) {
@@ -451,12 +514,12 @@ function create_query_resource(__, payload, state, fn) {
451
514
  /**
452
515
  * @param {RemoteQueryLiveInternals} __
453
516
  * @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
517
+ * @param {RequestEvent} event
454
518
  * @param {RequestState} state
455
- * @param {AbortSignal} signal — the request signal; aborts in-flight iteration when the client disconnects
456
519
  * @param {() => AsyncGenerator<any, void, void>} get_generator
457
520
  * @returns {RemoteLiveQuery<any>}
458
521
  */
459
- function create_live_query_resource(__, payload, state, signal, get_generator) {
522
+ function create_live_query_resource(__, payload, event, state, get_generator) {
460
523
  /** @type {Promise<any> | null} */
461
524
  let promise = null;
462
525
 
@@ -472,7 +535,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
472
535
  };
473
536
 
474
537
  const populate_hydratable = () => {
475
- void (__.id && state.is_in_render && get_promise());
538
+ if (__.id && state.is_in_render) {
539
+ // swallow rejections so they don't crash the server — the error is
540
+ // serialized into the response and surfaced on the client instead
541
+ get_promise().catch(noop);
542
+ }
476
543
  };
477
544
 
478
545
  return {
@@ -509,15 +576,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
509
576
  return false;
510
577
  },
511
578
  reconnect() {
512
- const reconnects = state.remote.reconnects;
579
+ promise = null;
580
+ delete get_cache(__, state)[payload];
513
581
 
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
- }
582
+ refresh(event, state, __, payload, get_promise);
519
583
 
520
- reconnects.set(create_remote_key(__.id, payload), get_promise());
521
584
  return Promise.resolve();
522
585
  },
523
586
  /** @ts-expect-error This method no longer exists */
@@ -535,7 +598,7 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
535
598
  const cache = (state.remote.live_iterators ??= new Map());
536
599
  let cached = cache.get(key);
537
600
  if (!cached) {
538
- cached = create_shared_live_iterator(signal, get_generator);
601
+ cached = create_shared_live_iterator(event.request.signal, get_generator);
539
602
  cache.set(key, cached);
540
603
  }
541
604
  return cached.subscribe();
@@ -613,55 +676,3 @@ function create_shared_live_iterator(signal, get_generator) {
613
676
  // Add batch as a property to the query function
614
677
  Object.defineProperty(query, 'batch', { value: batch, enumerable: true });
615
678
  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,8 +1,11 @@
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
11
  * In the context of a remote `command` or `form` request, returns an iterable
@@ -95,7 +98,7 @@ import { noop } from '../../../../utils/functions.js';
95
98
  * @returns {RequestedResult<Validated, Output>}
96
99
  */
97
100
  export function requested(query, limit) {
98
- const { state } = get_request_store();
101
+ const { event, state } = get_request_store();
99
102
  const internals = /** @type {RemoteAnyQueryInternals | undefined} */ (
100
103
  /** @type {any} */ (query).__
101
104
  );
@@ -115,15 +118,12 @@ export function requested(query, limit) {
115
118
 
116
119
  const requested = state.remote.requested;
117
120
  const payloads = requested?.get(__.id) ?? [];
121
+
118
122
  // note: don't initialize these maps here -- they will be initialized by the
119
123
  // command/form wrapper when we enter them, and if we initialize them here
120
124
  // we will enable requested(...) in contexts where it shouldn't be allowed,
121
125
  // 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) {
126
+ if (!state.is_in_remote_form_or_command) {
127
127
  throw new Error(
128
128
  'requested(...) can only be called in the context of a command/form remote function'
129
129
  );
@@ -131,6 +131,9 @@ export function requested(query, limit) {
131
131
  const [selected, skipped] = split_limit(payloads, limit);
132
132
 
133
133
  /**
134
+ * Registers the failure exactly like `.set()` registers a value: the error record
135
+ * is serialized to the client (putting the query there into a failed state), and
136
+ * subsequent server-side calls of the query with the same argument reject with it.
134
137
  * @param {string} payload
135
138
  * @param {unknown} error
136
139
  */
@@ -138,14 +141,15 @@ export function requested(query, limit) {
138
141
  const promise = Promise.reject(error);
139
142
  promise.catch(noop);
140
143
 
141
- const key = create_remote_key(__.id, payload);
142
- store.set(key, promise);
144
+ get_cache(__, state)[payload] = promise;
145
+ refresh(event, state, __, payload, () => promise);
143
146
  };
144
147
 
145
148
  for (const payload of skipped) {
146
149
  record_failure(
147
150
  payload,
148
- new Error(
151
+ new HttpError(
152
+ 400,
149
153
  `Requested refresh was rejected because it exceeded requested(${__.name}, ${limit}) limit`
150
154
  )
151
155
  );