@sveltejs/kit 3.0.0-next.11 → 3.0.0-next.13

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 (84) hide show
  1. package/package.json +10 -10
  2. package/src/cli.js +8 -8
  3. package/src/constants.js +1 -1
  4. package/src/core/config/options.js +43 -35
  5. package/src/core/env.js +25 -11
  6. package/src/core/postbuild/prerender.js +3 -3
  7. package/src/core/sync/sync.js +11 -14
  8. package/src/core/sync/utils.js +21 -1
  9. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +24 -23
  10. package/src/core/sync/write_client_manifest.js +4 -12
  11. package/src/core/sync/write_env.js +6 -4
  12. package/src/core/sync/write_server.js +10 -15
  13. package/src/core/sync/write_tsconfig/index.js +257 -0
  14. package/src/core/sync/write_tsconfig/utils.js +162 -0
  15. package/src/core/sync/write_types/index.js +80 -88
  16. package/src/exports/index.js +23 -12
  17. package/src/exports/internal/server/index.js +3 -1
  18. package/src/exports/internal/shared.js +4 -12
  19. package/src/exports/public.d.ts +87 -62
  20. package/src/exports/vite/build/remote.js +10 -12
  21. package/src/exports/vite/dev/index.js +113 -88
  22. package/src/exports/vite/index.js +452 -237
  23. package/src/exports/vite/module_ids.js +3 -1
  24. package/src/exports/vite/utils.js +1 -10
  25. package/src/runner.js +13 -0
  26. package/src/runtime/app/forms.js +4 -0
  27. package/src/runtime/app/manifest/index.js +1 -0
  28. package/src/runtime/app/paths/client.js +30 -30
  29. package/src/runtime/app/paths/internal/client.js +26 -0
  30. package/src/runtime/app/paths/server.js +22 -9
  31. package/src/runtime/app/paths/types.d.ts +11 -19
  32. package/src/runtime/app/server/remote/command.js +12 -7
  33. package/src/runtime/app/server/remote/form.js +37 -26
  34. package/src/runtime/app/server/remote/prerender.js +22 -11
  35. package/src/runtime/app/server/remote/query.js +5 -4
  36. package/src/runtime/app/server/remote/requested.js +4 -4
  37. package/src/runtime/app/server/remote/shared.js +48 -30
  38. package/src/runtime/app/service-worker/index.js +24 -0
  39. package/src/runtime/app/state/client.js +3 -0
  40. package/src/runtime/app/state/index.js +3 -3
  41. package/src/runtime/app/state/server.js +3 -0
  42. package/src/runtime/client/client.js +627 -311
  43. package/src/runtime/client/constants.js +2 -6
  44. package/src/runtime/client/fetcher.js +27 -17
  45. package/src/runtime/client/remote-functions/form.svelte.js +82 -58
  46. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  47. package/src/runtime/client/remote-functions/query/index.js +2 -2
  48. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  49. package/src/runtime/client/remote-functions/query-batch.svelte.js +3 -4
  50. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  51. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -10
  52. package/src/runtime/client/remote-functions/shared.svelte.js +15 -11
  53. package/src/runtime/client/state.svelte.js +54 -33
  54. package/src/runtime/client/types.d.ts +1 -2
  55. package/src/runtime/client/utils.js +21 -14
  56. package/src/runtime/components/root.svelte +4 -14
  57. package/src/runtime/form-utils.js +89 -54
  58. package/src/runtime/props.svelte.js +71 -0
  59. package/src/runtime/server/cookie.js +1 -1
  60. package/src/runtime/server/data/index.js +31 -28
  61. package/src/runtime/server/errors.js +1 -1
  62. package/src/runtime/server/fetch.js +4 -6
  63. package/src/runtime/server/page/actions.js +3 -3
  64. package/src/runtime/server/page/csp.js +86 -103
  65. package/src/runtime/server/page/index.js +1 -2
  66. package/src/runtime/server/page/load_data.js +15 -5
  67. package/src/runtime/server/page/render.js +46 -52
  68. package/src/runtime/server/page/respond_with_error.js +1 -3
  69. package/src/runtime/server/remote-functions.js +66 -35
  70. package/src/runtime/server/respond.js +56 -16
  71. package/src/runtime/server/utils.js +10 -0
  72. package/src/types/ambient-private.d.ts +9 -1
  73. package/src/types/ambient.d.ts +23 -28
  74. package/src/types/global-private.d.ts +12 -0
  75. package/src/types/internal.d.ts +15 -10
  76. package/src/types/private.d.ts +3 -12
  77. package/src/utils/url.js +12 -0
  78. package/src/version.js +1 -1
  79. package/types/index.d.ts +172 -155
  80. package/types/index.d.ts.map +5 -1
  81. package/src/core/sync/write_ambient.js +0 -18
  82. package/src/core/sync/write_tsconfig.js +0 -258
  83. package/src/runtime/types.d.ts +0 -8
  84. /package/src/core/sync/{write_tsconfig_test → write_tsconfig/test-app}/package.json +0 -0
@@ -1,10 +1,6 @@
1
1
  export const SNAPSHOT_KEY = 'sveltekit:snapshot';
2
- export const SCROLL_KEY = 'sveltekit:scroll';
3
- export const STATES_KEY = 'sveltekit:states';
4
- export const PAGE_URL_KEY = 'sveltekit:pageurl';
5
-
6
- export const HISTORY_INDEX = 'sveltekit:history';
7
- export const NAVIGATION_INDEX = 'sveltekit:navigation';
2
+ export const HISTORY_INFO_KEY = 'sveltekit:history-info';
3
+ export const HISTORY_METADATA_KEY = 'sveltekit:metadata';
8
4
 
9
5
  export const PRELOAD_PRIORITIES = /** @type {const} */ ({
10
6
  tap: 1,
@@ -89,22 +89,24 @@ const cache = new Map();
89
89
  export function initial_fetch(resource, opts) {
90
90
  const selector = build_selector(resource, opts);
91
91
 
92
- const script = document.querySelector(selector);
93
- if (script?.textContent) {
94
- script.remove(); // In case multiple script tags match the same selector
95
- let { body, ...init } = JSON.parse(script.textContent);
96
-
97
- const b64 = script.getAttribute('data-b64');
98
- if (b64 !== null) {
99
- // Can't use native_fetch('data:...;base64,${body}')
100
- // csp can block the request
101
- body = base64_decode(body);
102
- }
92
+ if (selector) {
93
+ const script = document.querySelector(selector);
94
+ if (script?.textContent) {
95
+ script.remove(); // In case multiple script tags match the same selector
96
+ let { body, ...init } = JSON.parse(script.textContent);
97
+
98
+ const b64 = script.getAttribute('data-b64');
99
+ if (b64 !== null) {
100
+ // Can't use native_fetch('data:...;base64,${body}')
101
+ // csp can block the request
102
+ body = base64_decode(body);
103
+ }
103
104
 
104
- const ttl = script.getAttribute('data-ttl');
105
- if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
105
+ const ttl = script.getAttribute('data-ttl');
106
+ if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
106
107
 
107
- return Promise.resolve(new Response(body, init));
108
+ return Promise.resolve(new Response(body, init));
109
+ }
108
110
  }
109
111
 
110
112
  return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts);
@@ -119,7 +121,7 @@ export function initial_fetch(resource, opts) {
119
121
  export function subsequent_fetch(resource, resolved, opts) {
120
122
  if (cache.size > 0) {
121
123
  const selector = build_selector(resource, opts);
122
- const cached = cache.get(selector);
124
+ const cached = selector && cache.get(selector);
123
125
  if (cached) {
124
126
  // https://developer.mozilla.org/en-US/docs/Web/API/Request/cache#value
125
127
  if (
@@ -155,6 +157,7 @@ export function dev_fetch(resource, opts) {
155
157
  * Build the cache key for a given request
156
158
  * @param {URL | RequestInfo} resource
157
159
  * @param {RequestInit} [opts]
160
+ * @returns {string | null} `null` for requests the server never serializes
158
161
  */
159
162
  function build_selector(resource, opts) {
160
163
  const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
@@ -162,6 +165,13 @@ function build_selector(resource, opts) {
162
165
  let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
163
166
 
164
167
  if (opts?.headers || opts?.body) {
168
+ const body = opts.body;
169
+
170
+ if (body && typeof body !== 'string' && !ArrayBuffer.isView(body)) {
171
+ // the server skips serializing these, so a matching script tag belongs to another request
172
+ return null;
173
+ }
174
+
165
175
  /** @type {import('types').StrictBody[]} */
166
176
  const values = [];
167
177
 
@@ -169,8 +179,8 @@ function build_selector(resource, opts) {
169
179
  values.push([...new Headers(opts.headers)].join(','));
170
180
  }
171
181
 
172
- if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
173
- values.push(opts.body);
182
+ if (body) {
183
+ values.push(/** @type {import('types').StrictBody} */ (body));
174
184
  }
175
185
 
176
186
  selector += `[data-hash="${hash(...values)}"]`;
@@ -11,6 +11,7 @@ import {
11
11
  handle_error,
12
12
  refreshAll
13
13
  } from '../client.js';
14
+ import { page } from '../state.svelte.js';
14
15
  import { tick } from 'svelte';
15
16
  import { categorize_updates, remote_request } from './shared.svelte.js';
16
17
  import { createAttachmentKey } from 'svelte/attachments';
@@ -25,7 +26,9 @@ import {
25
26
  serialize_binary_form,
26
27
  deep_get,
27
28
  DELETE_KEY,
28
- BINARY_FORM_CONTENT_TYPE
29
+ BINARY_FORM_CONTENT_TYPE,
30
+ parse_form_key,
31
+ coerce_form_value
29
32
  } from '../../form-utils.js';
30
33
 
31
34
  /**
@@ -37,16 +40,22 @@ import {
37
40
  * @returns {InternalRemoteFormIssue[]}
38
41
  */
39
42
  function merge_with_server_issues(form_data, current_issues, client_issues) {
43
+ const client_names = new Set(client_issues.map((issue) => issue.name));
44
+
40
45
  const merged = [
41
- ...current_issues.filter(
42
- (issue) => issue.server && !client_issues.some((i) => i.name === issue.name)
43
- ),
46
+ ...current_issues.filter((issue) => issue.server && !client_names.has(issue.name)),
44
47
  ...client_issues
45
48
  ];
46
49
 
47
- const keys = Array.from(form_data.keys());
50
+ /** @type {Map<string, number>} */
51
+ const positions = new Map();
52
+ let i = 0;
53
+ for (const key of form_data.keys()) {
54
+ if (!positions.has(key)) positions.set(key, i);
55
+ i++;
56
+ }
48
57
 
49
- return merged.sort((a, b) => keys.indexOf(a.name) - keys.indexOf(b.name));
58
+ return merged.sort((a, b) => (positions.get(a.name) ?? -1) - (positions.get(b.name) ?? -1));
50
59
  }
51
60
 
52
61
  /**
@@ -67,7 +76,29 @@ export function form(id) {
67
76
  function create_instance(key) {
68
77
  const action_id_without_key = id;
69
78
  const action_id = id + (key != undefined ? `/${JSON.stringify(key)}` : '');
70
- const action = '?/remote=' + encodeURIComponent(action_id);
79
+ const action = '/remote=' + encodeURIComponent(action_id);
80
+
81
+ /** @type {string} */
82
+ let cached_search = '';
83
+ /** @type {string} */
84
+ let cached_query = '';
85
+
86
+ /** @returns {string} */
87
+ function get_action() {
88
+ if (page.url.search !== cached_search) {
89
+ cached_search = page.url.search;
90
+
91
+ if (page.url.search) {
92
+ const params = new URLSearchParams(page.url.search);
93
+ params.delete('/remote');
94
+ cached_query = params.toString();
95
+ } else {
96
+ cached_query = '';
97
+ }
98
+ }
99
+
100
+ return `?${cached_query && `${cached_query}&`}${action}`;
101
+ }
71
102
 
72
103
  // the output of a non-enhanced submission that resulted in this page —
73
104
  // consume it so the form's state survives hydration (form outputs are
@@ -123,8 +154,8 @@ export function form(id) {
123
154
  /** @type {InternalRemoteFormIssue[] | null} */
124
155
  let unread_issues = null;
125
156
 
126
- /** @type {string | null} */
127
- let previous_submitter_name = null;
157
+ /** @type {{ name: string; type: 'number' | 'boolean' | null; is_array: boolean } | null} */
158
+ let previous_submitter = null;
128
159
 
129
160
  /**
130
161
  * In dev, warn if there are validation issues going unread
@@ -160,8 +191,8 @@ export function form(id) {
160
191
  * @returns {Record<string, any>}
161
192
  */
162
193
  function convert(form_data) {
163
- const data = convert_formdata(form_data);
164
- if (key !== undefined && !form_data.has('id')) {
194
+ const data = convert_formdata(action_id_without_key, form_data);
195
+ if (key !== undefined && !('id' in data)) {
165
196
  data.id = key;
166
197
  }
167
198
  return data;
@@ -353,7 +384,10 @@ export function form(id) {
353
384
  const instance = {};
354
385
 
355
386
  instance.method = 'POST';
356
- instance.action = action;
387
+ Object.defineProperty(instance, 'action', {
388
+ get: get_action,
389
+ enumerable: true
390
+ });
357
391
 
358
392
  instance[createAttachmentKey()] = (/** @type {HTMLFormElement} */ form) => {
359
393
  if (element) {
@@ -405,26 +439,30 @@ export function form(id) {
405
439
  const form_data = new FormData(form, event.submitter);
406
440
 
407
441
  if (
408
- previous_submitter_name !== null &&
409
- !Array.from(form_data.keys()).map(strip_prefix).includes(previous_submitter_name)
442
+ previous_submitter !== null &&
443
+ !Array.from(form_data.keys())
444
+ .map((k) => parse_form_key(action_id_without_key, k).name)
445
+ .includes(previous_submitter.name)
410
446
  ) {
411
- // Strip any `n:`/`b:` type prefix before clearing, otherwise
412
- // `set_nested_value` would coerce `undefined` to `NaN`/`false`
413
- // instead of clearing the previously-submitted value.
414
- set_nested_value(input, previous_submitter_name, undefined);
447
+ set_nested_value(input, previous_submitter, undefined);
415
448
  }
416
449
 
417
450
  if (event.submitter) {
418
451
  const name = event.submitter.getAttribute('name');
452
+
453
+ /** @type {null | ReturnType<typeof parse_form_key>} */
454
+ let submitter = null;
455
+
419
456
  const value = /** @type {any} */ (event.submitter).value;
420
457
 
421
458
  if (name !== null && value !== undefined) {
422
- set_nested_value(input, name, value);
459
+ submitter = parse_form_key(action_id_without_key, name);
460
+ set_nested_value(input, submitter, coerce_form_value(submitter.type, value));
423
461
  }
424
462
 
425
- previous_submitter_name = strip_prefix(name);
463
+ previous_submitter = submitter;
426
464
  } else {
427
- previous_submitter_name = null;
465
+ previous_submitter = null;
428
466
  }
429
467
 
430
468
  if (DEV) {
@@ -463,15 +501,14 @@ export function form(id) {
463
501
  // but that makes the types unnecessarily awkward
464
502
  const element = /** @type {HTMLInputElement} */ (e.target);
465
503
 
466
- let name = element.name;
504
+ const name = element.name;
467
505
  if (!name) return;
468
506
 
469
- const is_array = name.endsWith('[]');
470
- if (is_array) name = name.slice(0, -2);
507
+ const field = parse_form_key(action_id_without_key, name);
471
508
 
472
509
  const is_file = element.type === 'file';
473
510
 
474
- if (is_array) {
511
+ if (field.is_array) {
475
512
  let value;
476
513
 
477
514
  if (element.tagName === 'SELECT') {
@@ -481,7 +518,7 @@ export function form(id) {
481
518
  );
482
519
  } else {
483
520
  const elements = /** @type {HTMLInputElement[]} */ (
484
- Array.from(form.querySelectorAll(`[name="${name}[]"]`))
521
+ Array.from(form.querySelectorAll(`[name="${name}"]`))
485
522
  );
486
523
 
487
524
  if (DEV) {
@@ -502,7 +539,7 @@ export function form(id) {
502
539
  }
503
540
  }
504
541
 
505
- set_nested_value(input, name, value);
542
+ set_nested_value(input, field, value);
506
543
  } else if (is_file) {
507
544
  if (DEV && element.multiple) {
508
545
  throw new Error(
@@ -513,21 +550,19 @@ export function form(id) {
513
550
  const file = /** @type {HTMLInputElement & { files: FileList }} */ (element).files[0];
514
551
 
515
552
  if (file) {
516
- set_nested_value(input, name, file);
553
+ set_nested_value(input, field, file);
517
554
  } else {
518
- set_nested_value(input, name, DELETE_KEY);
555
+ set_nested_value(input, field, DELETE_KEY);
519
556
  }
520
557
  } else {
521
558
  set_nested_value(
522
559
  input,
523
- name,
560
+ field,
524
561
  element.type === 'checkbox' && !element.checked ? null : element.value
525
562
  );
526
563
  }
527
564
 
528
- name = strip_prefix(name);
529
-
530
- dirty[name] = true;
565
+ dirty[field.name] = true;
531
566
  };
532
567
 
533
568
  const handle_reset = async () => {
@@ -535,7 +570,7 @@ export function form(id) {
535
570
  // the inputs are actually updated (so that it can be cancelled)
536
571
  await tick();
537
572
 
538
- input = convert_formdata(new FormData(form));
573
+ input = convert_formdata(action_id_without_key, new FormData(form));
539
574
  raw_issues = [];
540
575
  touched = {};
541
576
  dirty = {};
@@ -545,15 +580,15 @@ export function form(id) {
545
580
 
546
581
  /** @param {Event} e */
547
582
  const handle_focusout = (e) => {
548
- let name = /** @type {HTMLInputElement} */ (e.target).name;
583
+ const name = /** @type {HTMLInputElement} */ (e.target).name;
549
584
  if (!name) return;
550
585
 
551
- name = strip_prefix(name).replace(/\[\]$/, '');
586
+ const field = parse_form_key(action_id_without_key, name);
552
587
 
553
- touched[name] = true;
588
+ touched[field.name] = true;
554
589
 
555
- if (Object.hasOwn(dirty, name)) {
556
- can_validate[name] = true;
590
+ if (Object.hasOwn(dirty, field.name)) {
591
+ can_validate[field.name] = true;
557
592
  }
558
593
  };
559
594
 
@@ -608,10 +643,10 @@ export function form(id) {
608
643
  },
609
644
  fields: {
610
645
  get: () =>
611
- create_field_proxy(
612
- {},
613
- () => input,
614
- (path, value) => {
646
+ create_field_proxy({
647
+ form_id: action_id_without_key,
648
+ get: () => input,
649
+ set: (path, value) => {
615
650
  if (path.length === 0) {
616
651
  input = value;
617
652
  } else if (value !== deep_get(input, path)) {
@@ -626,7 +661,7 @@ export function form(id) {
626
661
  }
627
662
  }
628
663
  },
629
- (path, all) => {
664
+ get_issues: (path, all) => {
630
665
  if (DEV && unread_issues !== null && path !== undefined) {
631
666
  unread_issues = unread_issues.filter((issue) => {
632
667
  return (
@@ -638,10 +673,9 @@ export function form(id) {
638
673
 
639
674
  return issues;
640
675
  },
641
- () => touched,
642
- () => dirty,
643
- []
644
- )
676
+ get_touched: () => touched,
677
+ get_dirty: () => dirty
678
+ })
645
679
  },
646
680
  result: {
647
681
  get: () => result
@@ -809,13 +843,3 @@ function validate_form_data(form_data, enctype) {
809
843
  }
810
844
  }
811
845
  }
812
-
813
- /**
814
- * Remove the `n:` or `b:` prefix from a field name
815
- * @template {string | null} T
816
- * @param {T} name
817
- * @returns {T}
818
- */
819
- function strip_prefix(name) {
820
- return /** @type {T} */ (name && name.replace(/^[nb]:/, ''));
821
- }
@@ -172,7 +172,7 @@ class Prerender {
172
172
  });
173
173
  this.#loading = false;
174
174
  this.#error = error;
175
- throw new HttpError(error.status, error); // so that transformError doesn't transform it again
175
+ throw new HttpError(error); // so that transformError doesn't transform it again
176
176
  }
177
177
  );
178
178
 
@@ -1,7 +1,7 @@
1
1
  /** @import { RemoteQueryFunction } from '@sveltejs/kit' */
2
2
  import { app_dir, base } from '$app/paths/internal/client';
3
3
  import { _goto, query_map } from '../../client.js';
4
- import { get_remote_request_headers, QUERY_FUNCTION_ID, remote_request } from '../shared.svelte.js';
4
+ import { QUERY_FUNCTION_ID, remote_request } from '../shared.svelte.js';
5
5
  import { DEV } from 'esm-env';
6
6
  import { QueryProxy } from './proxy.js';
7
7
 
@@ -26,7 +26,7 @@ export function query(id) {
26
26
  return new QueryProxy(id, arg, async (payload) => {
27
27
  const url = `${base}/${app_dir}/remote/${id}${payload ? `?payload=${payload}` : ''}`;
28
28
 
29
- const result = await remote_request(url, { headers: get_remote_request_headers() });
29
+ const result = await remote_request(url);
30
30
 
31
31
  if (result.redirect) {
32
32
  // Use internal version to allow redirects to external URLs
@@ -70,7 +70,7 @@ export class Query {
70
70
  delete query_responses[key];
71
71
 
72
72
  if (node.e) {
73
- this.fail(new HttpError(node.e.status, node.e));
73
+ this.fail(new HttpError(node.e));
74
74
  } else {
75
75
  this.set(/** @type {T} */ (node.v));
76
76
  }
@@ -152,7 +152,7 @@ export class Query {
152
152
  this.#loading = false;
153
153
  });
154
154
 
155
- reject(new HttpError(error.status, error)); // so that transformError doesn't transform it again
155
+ reject(new HttpError(error)); // so that transformError doesn't transform it again
156
156
  });
157
157
 
158
158
  return promise;
@@ -1,7 +1,7 @@
1
1
  /** @import { RemoteQueryFunction } from '@sveltejs/kit' */
2
2
  import { app_dir, base } from '$app/paths/internal/client';
3
3
  import { _goto } from '../client.js';
4
- import { get_remote_request_headers, QUERY_FUNCTION_ID, remote_request } from './shared.svelte.js';
4
+ import { QUERY_FUNCTION_ID, remote_request } from './shared.svelte.js';
5
5
  import { QueryProxy } from './query/proxy.js';
6
6
  import { HttpError } from '@sveltejs/kit/internal';
7
7
 
@@ -30,8 +30,7 @@ export function query_batch(id) {
30
30
  // but in different forks/async contexts and in the same macrotask?
31
31
  // If so this would potentially be buggy
32
32
  const headers = {
33
- 'Content-Type': 'application/json',
34
- ...get_remote_request_headers()
33
+ 'Content-Type': 'application/json'
35
34
  };
36
35
 
37
36
  // Wait for the next macrotask - don't use microtask as Svelte runtime uses these to collect changes and flush them,
@@ -72,7 +71,7 @@ export function query_batch(id) {
72
71
 
73
72
  for (const { resolve, reject } of resolvers) {
74
73
  if (result.type === 'error') {
75
- reject(new HttpError(result.error.status, result.error));
74
+ reject(new HttpError(result.error));
76
75
  } else {
77
76
  resolve(result.data);
78
77
  }
@@ -91,7 +91,7 @@ export class LiveQuery {
91
91
  // the query failed during SSR — seed the failed state (mirroring `fail()`,
92
92
  // minus its terminal `#done`), so the main loop still connects as usual
93
93
  // and the query can recover
94
- const error = new HttpError(node.e.status, node.e);
94
+ const error = new HttpError(node.e);
95
95
  this.#loading = false;
96
96
  this.#error = error.body;
97
97
 
@@ -417,7 +417,7 @@ export class LiveQuery {
417
417
  route: { id: null },
418
418
  url: new URL(location.href)
419
419
  });
420
- this.fail(new HttpError(error.status, error));
420
+ this.fail(new HttpError(error));
421
421
  }
422
422
 
423
423
  get [Symbol.toStringTag]() {
@@ -1,6 +1,8 @@
1
+ /** @import { RemoteFunctionResponse } from 'types' */
1
2
  import { app_dir, base } from '$app/paths/internal/client';
2
3
  import { app } from '../../client.js';
3
- import { get_remote_request_headers, handle_side_channel_response } from '../shared.svelte.js';
4
+ import { notify_version } from '../../state.svelte.js';
5
+ import { handle_side_channel_response } from '../shared.svelte.js';
4
6
  import * as devalue from 'devalue';
5
7
  import { HttpError } from '@sveltejs/kit/internal';
6
8
  import { noop } from '../../../../utils/functions.js';
@@ -23,25 +25,28 @@ export async function* create_live_iterator(
23
25
  const url = `${base}/${app_dir}/remote/${id}${payload ? `?payload=${payload}` : ''}`;
24
26
 
25
27
  const response = await fetch(url, {
26
- headers: get_remote_request_headers(),
27
28
  signal: controller.signal
28
29
  });
29
30
 
31
+ // detect new deployments from the response header
32
+ notify_version(response.headers.get('x-sveltekit-version'));
33
+
30
34
  if (!response.ok) {
31
- const result = await response.json().catch(() => ({
32
- type: 'error',
33
- status: response.status,
34
- error: response.statusText
35
- }));
35
+ /** @type {RemoteFunctionResponse | undefined} */
36
+ const result = await response.json().catch(() => undefined);
36
37
 
37
- throw new HttpError(result.status ?? response.status ?? 500, result.error);
38
+ throw new HttpError(
39
+ result?.type === 'error'
40
+ ? result.error
41
+ : { status: response.status, message: response.statusText }
42
+ );
38
43
  }
39
44
 
40
45
  if (response.headers.get('content-type')?.includes('application/json')) {
41
46
  // we can end up here if we e.g. redirect in `handle`
42
47
  const result = await response.json();
43
48
  await handle_side_channel_response(result);
44
- throw new HttpError(500, 'Invalid query.live response');
49
+ throw new HttpError({ status: 500, message: 'Invalid query.live response' });
45
50
  }
46
51
 
47
52
  if (!response.body) {
@@ -60,7 +65,7 @@ export async function* create_live_iterator(
60
65
  }
61
66
 
62
67
  await handle_side_channel_response(node);
63
- throw new HttpError(500, 'Invalid query.live response');
68
+ throw new HttpError({ status: 500, message: 'Invalid query.live response' });
64
69
  }
65
70
  } finally {
66
71
  try {
@@ -6,7 +6,7 @@ import { app, _goto, live_query_map, query_map, query_responses } from '../clien
6
6
  import { HttpError, Redirect } from '@sveltejs/kit/internal';
7
7
  import { untrack } from 'svelte';
8
8
  import { create_remote_key, split_remote_key } from '../../shared.js';
9
- import { navigating, page } from '../state.svelte.js';
9
+ import { navigating, page, notify_version } from '../state.svelte.js';
10
10
 
11
11
  /** Indicates a query function, as opposed to a query instance */
12
12
  export const QUERY_FUNCTION_ID = Symbol('sveltekit.query_function_id');
@@ -86,7 +86,7 @@ export function pin_while_resolving(cache_map, cache, id, payload, then) {
86
86
  */
87
87
  export function unwrap_node(node) {
88
88
  if (node.e) {
89
- throw new HttpError(node.e.status, node.e);
89
+ throw new HttpError(node.e);
90
90
  }
91
91
 
92
92
  return node.v;
@@ -112,24 +112,28 @@ export function get_remote_request_headers() {
112
112
  */
113
113
  export async function remote_request(url, init) {
114
114
  const response = await fetch(url, init);
115
+ const status = response.status;
116
+
117
+ // detect new deployments from the response header
118
+ notify_version(response.headers.get('x-sveltekit-version'));
115
119
 
116
120
  if (!response.ok) {
117
121
  const result = await response.json().catch(() => ({
118
122
  type: 'error',
119
- status: response.status,
120
- error: response.statusText
123
+ status,
124
+ error: {
125
+ status,
126
+ message: response.statusText
127
+ }
121
128
  }));
122
129
 
123
- throw new HttpError(
124
- result.error?.status ?? result.status ?? response.status ?? 500,
125
- result.error
126
- );
130
+ throw new HttpError({ status, ...result.error });
127
131
  }
128
132
 
129
133
  const result = /** @type {RemoteFunctionResponse} */ (await response.json());
130
134
 
131
135
  if (result.type === 'error') {
132
- throw new HttpError(result.error.status, result.error);
136
+ throw new HttpError(result.error);
133
137
  }
134
138
 
135
139
  const data = /** @type {RemoteFunctionData} */ (
@@ -144,7 +148,7 @@ export async function remote_request(url, init) {
144
148
  function refresh(key, entry, result) {
145
149
  if (entry?.resource) {
146
150
  if (result.e) {
147
- entry.resource.fail(new HttpError(result.e.status, result.e));
151
+ entry.resource.fail(new HttpError(result.e));
148
152
  } else {
149
153
  entry.resource.set(result.v);
150
154
  }
@@ -198,7 +202,7 @@ export async function handle_side_channel_response(response) {
198
202
  }
199
203
 
200
204
  if (response.type === 'error') {
201
- throw new HttpError(response.error.status, response.error);
205
+ throw new HttpError(response.error);
202
206
  }
203
207
 
204
208
  return response;