@sveltejs/kit 3.0.0-next.14 → 3.0.0-next.16

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 (56) hide show
  1. package/package.json +6 -3
  2. package/src/cli.js +6 -3
  3. package/src/core/adapt/builder.js +12 -5
  4. package/src/core/config/index.js +4 -5
  5. package/src/core/env.js +3 -3
  6. package/src/core/postbuild/prerender.js +6 -10
  7. package/src/core/sync/sync.js +3 -2
  8. package/src/core/sync/utils.js +2 -2
  9. package/src/core/sync/write_tsconfig/index.js +71 -53
  10. package/src/core/sync/write_tsconfig/utils.js +0 -61
  11. package/src/core/sync/write_tsconfig/validate.js +128 -0
  12. package/src/core/sync/write_types/index.js +2 -2
  13. package/src/exports/public.d.ts +22 -16
  14. package/src/exports/vite/build/build_server.js +2 -3
  15. package/src/exports/vite/dev/index.js +44 -36
  16. package/src/exports/vite/index.js +46 -5
  17. package/src/exports/vite/utils.js +61 -8
  18. package/src/runner.js +4 -2
  19. package/src/runtime/app/forms.js +4 -7
  20. package/src/runtime/app/internal/transport.js +53 -0
  21. package/src/runtime/app/paths/client.js +2 -2
  22. package/src/runtime/app/paths/internal/client.js +2 -2
  23. package/src/runtime/app/server/remote/prerender.js +6 -10
  24. package/src/runtime/app/server/remote/query.js +3 -3
  25. package/src/runtime/app/server/remote/requested.js +2 -2
  26. package/src/runtime/app/server/remote/shared.js +1 -16
  27. package/src/runtime/client/client.js +29 -25
  28. package/src/runtime/client/parse.js +1 -1
  29. package/src/runtime/client/remote-functions/command.svelte.js +1 -2
  30. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  31. package/src/runtime/client/remote-functions/query/proxy.js +2 -2
  32. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -2
  33. package/src/runtime/pathname.js +7 -1
  34. package/src/runtime/server/data/index.js +5 -8
  35. package/src/runtime/server/dev.js +22 -0
  36. package/src/runtime/server/endpoint.js +3 -4
  37. package/src/runtime/server/fetch.js +22 -24
  38. package/src/runtime/server/index.js +54 -22
  39. package/src/runtime/server/internal.js +12 -5
  40. package/src/runtime/server/page/actions.js +18 -43
  41. package/src/runtime/server/page/data_serializer.js +12 -13
  42. package/src/runtime/server/page/index.js +16 -32
  43. package/src/runtime/server/page/load_data.js +26 -15
  44. package/src/runtime/server/page/render.js +9 -11
  45. package/src/runtime/server/page/respond_with_error.js +8 -20
  46. package/src/runtime/server/remote-functions.js +11 -15
  47. package/src/runtime/server/respond.js +26 -31
  48. package/src/runtime/server/state.js +36 -19
  49. package/src/runtime/server/utils.js +0 -20
  50. package/src/runtime/shared.js +16 -38
  51. package/src/types/internal.d.ts +45 -52
  52. package/src/utils/filesystem.js +1 -23
  53. package/src/version.js +1 -1
  54. package/types/index.d.ts +24 -18
  55. package/types/index.d.ts.map +1 -1
  56. package/src/runtime/server/app.js +0 -9
@@ -0,0 +1,53 @@
1
+ /** @import { Transport } from '@sveltejs/kit' */
2
+ import * as devalue from 'devalue';
3
+ import { DEV } from 'esm-env';
4
+
5
+ /** @type {(thing: any) => string} */
6
+ export let uneval = () => {
7
+ throw new Error(DEV ? 'called uneval before init_transport' : '');
8
+ };
9
+
10
+ /** @type {(data: any) => string} */
11
+ export let stringify = () => {
12
+ throw new Error(DEV ? 'called stringify before init_transport' : '');
13
+ };
14
+
15
+ /** @type {(data: string) => any} */
16
+ export let parse = () => {
17
+ throw new Error(DEV ? 'called parse before init_transport' : '');
18
+ };
19
+
20
+ /** @type {Record<string, (data: any) => any>} */
21
+ export let encoders = {};
22
+
23
+ /** @type {Record<string, (data: any) => any>} */
24
+ export let decoders = {};
25
+
26
+ export let has_custom_transporters = false;
27
+
28
+ /**
29
+ *
30
+ * @param {Transport} transport
31
+ */
32
+ export function init_transport(transport) {
33
+ const transporters = Object.entries(transport);
34
+
35
+ has_custom_transporters = transporters.length > 0;
36
+
37
+ /** @param {unknown} thing */
38
+ const replacer = (thing) => {
39
+ for (const key of Object.keys(transport)) {
40
+ const encoded = transport[key].encode(thing);
41
+ if (encoded) {
42
+ return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
43
+ }
44
+ }
45
+ };
46
+
47
+ encoders = Object.fromEntries(transporters.map(([k, v]) => [k, v.encode]));
48
+ decoders = Object.fromEntries(transporters.map(([k, v]) => [k, v.decode]));
49
+
50
+ uneval = (data) => devalue.uneval(data, replacer);
51
+ stringify = (data) => devalue.stringify(data, encoders);
52
+ parse = (data) => devalue.parse(data, decoders);
53
+ }
@@ -1,4 +1,4 @@
1
- /** @import { AssetPath, RouteId, RouteIdWithSearchOrHash, Path, PathnameWithSearchOrHash, ResolvedPathname, RouteParams } from '$app/types' */
1
+ /** @import { AssetPath, RouteId, RouteIdWithSearchOrHash, PathnameWithSearchOrHash, ResolvedPathname, RouteParams } from '$app/types' */
2
2
  /** @import { ResolveArgs } from './types.js' */
3
3
  import { base, assets, hash_routing, match_implementation } from './internal/client.js';
4
4
  import { resolve_route } from '../../../utils/routing.js';
@@ -92,7 +92,7 @@ export function resolve(...args) {
92
92
  * ```
93
93
  * @since 2.52.0
94
94
  *
95
- * @param {Path | URL | (string & {})} url
95
+ * @param {URL | string} url
96
96
  * @returns {Promise<{ [K in RouteId]: { id: K; params: RouteParams<K>; } }[RouteId] | null>}
97
97
  */
98
98
  export function match(url) {
@@ -1,4 +1,4 @@
1
- /** @import { RouteId, Path } from '$app/types' */
1
+ /** @import { RouteId } from '$app/types' */
2
2
  import { payload } from '../../../client/payload.js';
3
3
 
4
4
  export const base = payload.base ?? __SVELTEKIT_PATHS_BASE__;
@@ -9,7 +9,7 @@ export const hash_routing = __SVELTEKIT_HASH_ROUTING__;
9
9
  /**
10
10
  * We make this configurable per-environment so that it's possible to import `$app/paths`
11
11
  * into a service worker without importing the entire client
12
- * @param {Path | URL | (string & {})} _url
12
+ * @param {URL | string} _url
13
13
  * @returns {Promise<{ [K in RouteId]: { id: K; params: import('$app/types').RouteParams<K>; } }[RouteId] | null>}
14
14
  */
15
15
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -4,15 +4,11 @@
4
4
  import { json } from '@sveltejs/kit';
5
5
  import { HttpError } from '@sveltejs/kit/internal';
6
6
  import { get_request_store } from '@sveltejs/kit/internal/server';
7
- import { stringify, stringify_remote_arg } from '../../../shared.js';
7
+ import { stringify_remote_arg } from '../../../shared.js';
8
+ import { parse, stringify } from '#app/internal/transport';
8
9
  import { noop } from '../../../../utils/functions.js';
9
10
  import { app_dir, base } from '$app/paths/internal/server';
10
- import {
11
- create_validator,
12
- get_response,
13
- parse_remote_response,
14
- run_remote_function
15
- } from './shared.js';
11
+ import { create_validator, get_response, run_remote_function } from './shared.js';
16
12
 
17
13
  /**
18
14
  * Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -89,7 +85,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
89
85
  /** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
90
86
  const wrapper = (arg) => {
91
87
  const { event, state } = get_request_store();
92
- const payload = stringify_remote_arg(arg, state.transport);
88
+ const payload = stringify_remote_arg(arg);
93
89
 
94
90
  // `get_response` (as opposed to bare `get_cache`) also registers the call in the
95
91
  // implicit lookup, so that the result is inlined into the page payload (`data.p`)
@@ -121,7 +117,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
121
117
  throw new HttpError(prerendered.error);
122
118
  }
123
119
 
124
- return parse_remote_response(prerendered.data, state.transport)._;
120
+ return parse(prerendered.data)._;
125
121
  }
126
122
  }
127
123
 
@@ -146,7 +142,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
146
142
  const result = await promise;
147
143
 
148
144
  if (state.prerendering) {
149
- const body = { type: 'result', data: stringify({ _: result }, state.transport) };
145
+ const body = { type: 'result', data: stringify({ _: result }) };
150
146
  state.prerendering.dependencies.set(url, {
151
147
  body: JSON.stringify(body),
152
148
  response: json(body)
@@ -98,7 +98,7 @@ export function query(validate_or_fn, maybe_fn) {
98
98
  }
99
99
 
100
100
  const { event, state } = get_request_store();
101
- const payload = stringify_remote_arg(arg, state.transport);
101
+ const payload = stringify_remote_arg(arg);
102
102
 
103
103
  return create_query_resource(__, payload, event, state, () =>
104
104
  run_remote_function(
@@ -197,7 +197,7 @@ function live(validate_or_fn, maybe_fn) {
197
197
  }
198
198
 
199
199
  const { event, state } = get_request_store();
200
- const payload = stringify_remote_arg(arg, state.transport);
200
+ const payload = stringify_remote_arg(arg);
201
201
 
202
202
  return create_live_query_resource(__, payload, event, state, () =>
203
203
  run(event, state, () => validate(arg))
@@ -376,7 +376,7 @@ function batch(validate_or_fn, maybe_fn) {
376
376
  }
377
377
 
378
378
  const { event, state } = get_request_store();
379
- const payload = stringify_remote_arg(arg, state.transport);
379
+ const payload = stringify_remote_arg(arg);
380
380
 
381
381
  return create_query_resource(__, payload, event, state, () =>
382
382
  // Collect all the calls to the same query in the same macrotask,
@@ -163,7 +163,7 @@ export function requested(query, limit) {
163
163
  *[Symbol.iterator]() {
164
164
  for (const payload of selected) {
165
165
  try {
166
- const parsed = parse_remote_arg(payload, state.transport);
166
+ const parsed = parse_remote_arg(payload);
167
167
  const validated = __.validate(parsed);
168
168
 
169
169
  if (is_thenable(validated)) {
@@ -183,7 +183,7 @@ export function requested(query, limit) {
183
183
  async *[Symbol.asyncIterator]() {
184
184
  yield* race_all(selected, async (payload) => {
185
185
  try {
186
- const parsed = parse_remote_arg(payload, state.transport);
186
+ const parsed = parse_remote_arg(payload);
187
187
  const validated = await __.validate(parsed);
188
188
  return { arg: validated, query: __.bind(payload, validated) };
189
189
  } catch (error) {
@@ -1,6 +1,5 @@
1
1
  /** @import { RequestEvent } from '@sveltejs/kit' */
2
- /** @import { ServerHooks, MaybePromise, RequestState, RemoteInternals, RequestStore, RemoteLiveQueryUserFunctionReturnType } from 'types' */
3
- import { parse } from 'devalue';
2
+ /** @import { MaybePromise, RequestState, RemoteInternals, RequestStore, RemoteLiveQueryUserFunctionReturnType } from 'types' */
4
3
  import { error } from '@sveltejs/kit';
5
4
  import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
6
5
 
@@ -80,20 +79,6 @@ export async function get_response(internals, payload, state, get_result) {
80
79
  return (cache[payload] ??= get_result());
81
80
  }
82
81
 
83
- /**
84
- * @param {any} data
85
- * @param {ServerHooks['transport']} transport
86
- */
87
- export function parse_remote_response(data, transport) {
88
- /** @type {Record<string, any>} */
89
- const revivers = {};
90
- for (const key in transport) {
91
- revivers[key] = transport[key].decode;
92
- }
93
-
94
- return parse(data, revivers);
95
- }
96
-
97
82
  /**
98
83
  * @param {RequestEvent} event
99
84
  * @param {RequestState} state
@@ -9,7 +9,7 @@ import { settled, tick, fork, onMount, hydrate, mount } from 'svelte';
9
9
  import { HttpError, Redirect, SvelteKitError } from '@sveltejs/kit/internal';
10
10
  import { decode_pathname, strip_hash, make_trackable, normalize_path } from '../../utils/url.js';
11
11
  import { dev_fetch, initial_fetch, lock_fetch, subsequent_fetch, unlock_fetch } from './fetcher.js';
12
- import { parse, parse_server_route } from './parse.js';
12
+ import { parse_routes, parse_server_route } from './parse.js';
13
13
  import * as storage from './session-storage.js';
14
14
  import {
15
15
  find_anchor,
@@ -50,13 +50,14 @@ import { noop_span } from '../telemetry/noop.js';
50
50
  import { read_ndjson } from './ndjson.js';
51
51
  import Root from '../components/root.svelte';
52
52
  import { Props, RenderNode } from '../props.svelte.js';
53
+ import { init_transport, parse, stringify } from '#app/internal/transport';
53
54
 
54
55
  /**
55
56
  * @typedef {{
56
57
  * historyIndex: number;
57
58
  * navigationIndex: number;
58
59
  * pageUrl?: string;
59
- * state: Record<string, any>;
60
+ * state: string;
60
61
  * persistState: boolean;
61
62
  * resetIndex: number;
62
63
  * }} HistoryMetadata
@@ -487,9 +488,11 @@ async function _start(_app, _target, data) {
487
488
 
488
489
  app = _app;
489
490
 
491
+ init_transport(app.hooks.transport ?? {});
492
+
490
493
  await _app.hooks.init?.();
491
494
 
492
- routes = __SVELTEKIT_CLIENT_ROUTING__ ? parse(_app) : [];
495
+ routes = __SVELTEKIT_CLIENT_ROUTING__ ? parse_routes(_app) : [];
493
496
  container = __SVELTEKIT_EMBEDDED__ ? _target : document.documentElement;
494
497
  target = _target;
495
498
 
@@ -530,7 +533,7 @@ async function _start(_app, _target, data) {
530
533
  [HISTORY_METADATA_KEY]: {
531
534
  historyIndex: current_history_index,
532
535
  navigationIndex: current_navigation_index,
533
- state: {},
536
+ state: stringify({}),
534
537
  persistState: false,
535
538
  resetIndex: current_history_index
536
539
  }
@@ -563,7 +566,7 @@ async function _start(_app, _target, data) {
563
566
  type: 'enter',
564
567
  url: resolve_url(app.hash ? decode_hash(new URL(location.href)) : location.href),
565
568
  replace_state: true,
566
- state: history_metadata?.persistState ? history_metadata.state : {},
569
+ state: history_metadata?.persistState ? parse(history_metadata.state) : {},
567
570
  persist_state: history_metadata?.persistState ?? false
568
571
  });
569
572
 
@@ -2054,10 +2057,17 @@ async function navigate({
2054
2057
  url.pathname = navigation_result.props.page.url.pathname;
2055
2058
  }
2056
2059
 
2057
- state = popped ? popped.state : state;
2058
-
2059
- if (!popped) {
2060
- // this is a new navigation, rather than a popstate
2060
+ if (popped) {
2061
+ state = popped.state;
2062
+ } else {
2063
+ // we immediately serialize-then-parse to ensure that the value is
2064
+ // serializable, and to prevent the developer from dangerously
2065
+ // relying on the identity of the serialized objects
2066
+ const serialized_state = stringify(state);
2067
+ state = parse(serialized_state);
2068
+
2069
+ // Store the serialized state so the browser history can preserve custom transport values.
2070
+ // This is a new navigation, rather than a popstate.
2061
2071
  const change = replace_state ? 0 : 1;
2062
2072
  if (type !== 'enter') {
2063
2073
  if (reset) current_reset_index += 1;
@@ -2067,7 +2077,7 @@ async function navigate({
2067
2077
  [HISTORY_METADATA_KEY]: /** @satisfies {HistoryMetadata} */ ({
2068
2078
  historyIndex: (current_history_index += change),
2069
2079
  navigationIndex: (current_navigation_index += change),
2070
- state,
2080
+ state: serialized_state,
2071
2081
  persistState: persist_state,
2072
2082
  resetIndex: current_reset_index
2073
2083
  })
@@ -2874,18 +2884,8 @@ export async function replaceState(url, state) {
2874
2884
  async function update_state(intent, state, { replace, persist_state, reset }, caller) {
2875
2885
  const url = intent.url;
2876
2886
 
2877
- if (DEV) {
2878
- if (!started) {
2879
- throw new Error(`Cannot call ${caller}(...) before router is initialized`);
2880
- }
2881
-
2882
- try {
2883
- // use `devalue.stringify` as a convenient way to ensure we exclude values that can't be properly rehydrated, such as custom class instances
2884
- devalue.stringify(state);
2885
- } catch (error) {
2886
- // @ts-expect-error
2887
- throw new Error(`Could not serialize state${error.path}`, { cause: error });
2888
- }
2887
+ if (DEV && !started) {
2888
+ throw new Error(`Cannot call ${caller}(...) before router is initialized`);
2889
2889
  }
2890
2890
 
2891
2891
  const nav =
@@ -2906,12 +2906,16 @@ async function update_state(intent, state, { replace, persist_state, reset }, ca
2906
2906
  if (!replace) capture_scroll(current_history_index);
2907
2907
  if (reset) current_reset_index += 1;
2908
2908
 
2909
+ // as above, serialize-then-parse to prevent bugs
2910
+ const serialized_state = stringify(state);
2911
+ state = parse(serialized_state);
2912
+
2909
2913
  const entry = {
2910
2914
  [HISTORY_METADATA_KEY]: /** @satisfies {HistoryMetadata} */ ({
2911
2915
  historyIndex: (current_history_index += replace ? 0 : 1),
2912
2916
  navigationIndex: current_navigation_index,
2913
2917
  pageUrl: page.url.href,
2914
- state,
2918
+ state: serialized_state,
2915
2919
  persistState: persist_state,
2916
2920
  resetIndex: current_reset_index
2917
2921
  })
@@ -3291,7 +3295,7 @@ function _start_router() {
3291
3295
  const reset_index = history_metadata.resetIndex;
3292
3296
  const reset = reset_index !== (source_info?.resetIndex ?? current_reset_index);
3293
3297
  const scroll = history_info[history_index]?.scroll;
3294
- const state = history_metadata.state;
3298
+ const state = parse(history_metadata.state);
3295
3299
  const url = new URL(history_metadata.pageUrl ?? location.href);
3296
3300
  const navigation_index = history_metadata.navigationIndex;
3297
3301
  const is_hash_change =
@@ -3537,7 +3541,7 @@ async function _hydrate(
3537
3541
 
3538
3542
  if (result.props.page) {
3539
3543
  const history_metadata = get_history_metadata();
3540
- result.props.page.state = history_metadata?.persistState ? history_metadata.state : {};
3544
+ result.props.page.state = history_metadata?.persistState ? parse(history_metadata.state) : {};
3541
3545
  }
3542
3546
 
3543
3547
  await initialize(result, target, should_hydrate);
@@ -4,7 +4,7 @@ import { exec, parse_route_id } from '../../utils/routing.js';
4
4
  * @param {import('./types.js').SvelteKitApp} app
5
5
  * @returns {import('types').CSRRoute[]}
6
6
  */
7
- export function parse({ nodes, server_loads, dictionary, matchers }) {
7
+ export function parse_routes({ nodes, server_loads, dictionary, matchers }) {
8
8
  const layouts_with_server_load = new Set(server_loads);
9
9
 
10
10
  return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
@@ -1,6 +1,5 @@
1
1
  /** @import { RemoteCommand, RemoteQueryUpdate } from '@sveltejs/kit' */
2
2
  import { app_dir, base } from '$app/paths/internal/client';
3
- import { app } from '../client.js';
4
3
  import { stringify_command_arg } from '../../shared.js';
5
4
  import { get_remote_request_headers, categorize_updates, remote_request } from './shared.svelte.js';
6
5
 
@@ -48,7 +47,7 @@ export function command(id) {
48
47
  const response = await remote_request(`${base}/${app_dir}/remote/${id}`, {
49
48
  method: 'POST',
50
49
  body: JSON.stringify({
51
- payload: await stringify_command_arg(arg, app.hooks.transport),
50
+ payload: await stringify_command_arg(arg),
52
51
  refreshes: Array.from(refreshes ?? [])
53
52
  }),
54
53
  headers
@@ -57,7 +57,7 @@ function put(url, encoded) {
57
57
  */
58
58
  export function prerender(id) {
59
59
  return (arg) => {
60
- const payload = stringify_remote_arg(arg, app.hooks.transport);
60
+ const payload = stringify_remote_arg(arg);
61
61
  const cache_key = create_remote_key(id, payload);
62
62
 
63
63
  let resource = prerender_resources.get(cache_key)?.deref();
@@ -1,4 +1,4 @@
1
- import { app, query_map } from '../../client.js';
1
+ import { query_map } from '../../client.js';
2
2
  import {
3
3
  pin_in_effect,
4
4
  pin_while_resolving,
@@ -29,7 +29,7 @@ export class QueryProxy {
29
29
  */
30
30
  constructor(id, arg, fn) {
31
31
  this.#id = id;
32
- this.#payload = stringify_remote_arg(arg, app.hooks.transport);
32
+ this.#payload = stringify_remote_arg(arg);
33
33
  this.#key = create_remote_key(id, this.#payload);
34
34
  Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
35
35
  this.#fn = fn;
@@ -1,4 +1,4 @@
1
- import { app, live_query_map } from '../../client.js';
1
+ import { live_query_map } from '../../client.js';
2
2
  import { pin_in_effect, pin_while_resolving, QUERY_RESOURCE_KEY } from '../shared.svelte.js';
3
3
  import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
4
4
  import { LiveQuery } from './instance.svelte.js';
@@ -20,7 +20,7 @@ export class LiveQueryProxy {
20
20
  */
21
21
  constructor(id, arg) {
22
22
  this.#id = id;
23
- this.#payload = stringify_remote_arg(arg, app.hooks.transport);
23
+ this.#payload = stringify_remote_arg(arg);
24
24
  this.#key = create_remote_key(id, this.#payload);
25
25
  Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
26
26
 
@@ -22,13 +22,14 @@ export function strip_data_suffix(pathname) {
22
22
  }
23
23
 
24
24
  const ROUTE_SUFFIX = '/__route.js';
25
+ const HTML_ROUTE_SUFFIX = '.html__route.js';
25
26
 
26
27
  /**
27
28
  * @param {string} pathname
28
29
  * @returns {boolean}
29
30
  */
30
31
  export function has_resolution_suffix(pathname) {
31
- return pathname.endsWith(ROUTE_SUFFIX);
32
+ return pathname.endsWith(ROUTE_SUFFIX) || pathname.endsWith(HTML_ROUTE_SUFFIX);
32
33
  }
33
34
 
34
35
  /**
@@ -37,6 +38,7 @@ export function has_resolution_suffix(pathname) {
37
38
  * @returns {string}
38
39
  */
39
40
  export function add_resolution_suffix(pathname) {
41
+ if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_ROUTE_SUFFIX);
40
42
  return pathname.replace(/\/$/, '') + ROUTE_SUFFIX;
41
43
  }
42
44
 
@@ -45,6 +47,10 @@ export function add_resolution_suffix(pathname) {
45
47
  * @returns {string}
46
48
  */
47
49
  export function strip_resolution_suffix(pathname) {
50
+ if (pathname.endsWith(HTML_ROUTE_SUFFIX)) {
51
+ return pathname.slice(0, -HTML_ROUTE_SUFFIX.length) + '.html';
52
+ }
53
+
48
54
  return pathname.slice(0, -ROUTE_SUFFIX.length);
49
55
  }
50
56
 
@@ -11,22 +11,20 @@ import { with_version_header } from '../utils.js';
11
11
 
12
12
  /**
13
13
  * @param {import('@sveltejs/kit').RequestEvent} event
14
- * @param {import('types').RequestState} event_state
14
+ * @param {import('types').RequestState} state
15
15
  * @param {import('types').SSRRoute} route
16
16
  * @param {import('types').SSROptions} options
17
17
  * @param {import('@sveltejs/kit').SSRManifest} manifest
18
- * @param {import('types').SSRState} state
19
18
  * @param {boolean[] | undefined} invalidated_data_nodes
20
19
  * @param {import('types').TrailingSlash} trailing_slash
21
20
  * @returns {Promise<Response>}
22
21
  */
23
22
  export async function render_data(
24
23
  event,
25
- event_state,
24
+ state,
26
25
  route,
27
26
  options,
28
27
  manifest,
29
- state,
30
28
  invalidated_data_nodes,
31
29
  trailing_slash
32
30
  ) {
@@ -60,7 +58,6 @@ export async function render_data(
60
58
  // load this. for the child, return as is. for the final result, stream things
61
59
  return load_server_data({
62
60
  event: new_event,
63
- event_state,
64
61
  state,
65
62
  node,
66
63
  parent: async () => {
@@ -95,7 +92,7 @@ export async function render_data(
95
92
  return fn();
96
93
  });
97
94
 
98
- const data_serializer = server_data_serializer_json(event, event_state, options);
95
+ const data_serializer = server_data_serializer_json(event, state, options);
99
96
  await Promise.all(
100
97
  promises.map(async (p, i) => {
101
98
  const node = await p.catch(async (error) => {
@@ -103,7 +100,7 @@ export async function render_data(
103
100
  throw error;
104
101
  }
105
102
 
106
- const transformed = await handle_error_and_jsonify(event, event_state, options, error);
103
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
107
104
 
108
105
  return /** @type {import('types').ServerErrorNode} */ ({
109
106
  type: 'error',
@@ -151,7 +148,7 @@ export async function render_data(
151
148
  if (error instanceof Redirect) {
152
149
  return redirect_json_response(error);
153
150
  } else {
154
- const transformed = await handle_error_and_jsonify(event, event_state, options, error);
151
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
155
152
  return json_response(transformed, transformed.status);
156
153
  }
157
154
  }
@@ -0,0 +1,22 @@
1
+ const symbol = Symbol.for('sveltekit.global_state');
2
+ const state = /** @type {Record<symbol, any>} */ (
3
+ /** @type {Record<symbol, unknown>} */ (/** @type {unknown} */ (globalThis))[symbol] ??= {}
4
+ );
5
+
6
+ /**
7
+ * Persist a value across dev server reloads, in case a freshly-invalidated module
8
+ * reads global state that is not set until a request is handled
9
+ * @param {symbol} key
10
+ * @param {any} value
11
+ */
12
+ export function save(key, value) {
13
+ state[key] = value;
14
+ }
15
+
16
+ /**
17
+ * Restore a value after a dev server reload
18
+ * @param {symbol} key
19
+ */
20
+ export function restore(key) {
21
+ return state[key];
22
+ }
@@ -6,12 +6,11 @@ import { method_not_allowed } from './utils.js';
6
6
 
7
7
  /**
8
8
  * @param {import('@sveltejs/kit').RequestEvent} event
9
- * @param {import('types').RequestState} event_state
9
+ * @param {import('types').RequestState} state
10
10
  * @param {import('types').SSREndpoint} mod
11
- * @param {import('types').SSRState} state
12
11
  * @returns {Promise<Response>}
13
12
  */
14
- export async function render_endpoint(event, event_state, mod, state) {
13
+ export async function render_endpoint(event, state, mod) {
15
14
  const method = /** @type {import('types').HttpMethod} */ (event.request.method);
16
15
 
17
16
  let handler = mod[method] || mod.fallback;
@@ -42,7 +41,7 @@ export async function render_endpoint(event, event_state, mod, state) {
42
41
  }
43
42
 
44
43
  try {
45
- const response = await with_request_store({ event, state: event_state }, () =>
44
+ const response = await with_request_store({ event, state }, () =>
46
45
  handler(/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event))
47
46
  );
48
47
 
@@ -11,7 +11,7 @@ import { fork_state_for_subrequest } from './state.js';
11
11
  * event: import('@sveltejs/kit').RequestEvent;
12
12
  * options: import('types').SSROptions;
13
13
  * manifest: import('@sveltejs/kit').SSRManifest;
14
- * state: import('types').SSRState;
14
+ * state: import('types').RequestState;
15
15
  * get_cookie_header: (url: URL, header: string | null) => string;
16
16
  * set_internal: (name: string, value: string, opts: import('./page/types.js').Cookie['options']) => void;
17
17
  * }} opts
@@ -195,34 +195,32 @@ function normalize_fetch_input(info, init, url) {
195
195
  * @param {Request} request
196
196
  * @param {import('types').SSROptions} options
197
197
  * @param {import('@sveltejs/kit').SSRManifest} manifest
198
- * @param {import('types').SSRState} state
198
+ * @param {import('types').RequestState} state
199
199
  * @returns {Promise<Response>}
200
200
  */
201
201
  async function internal_fetch(request, options, manifest, state) {
202
- const subrequest_state = fork_state_for_subrequest(state);
202
+ if (request.signal?.aborted) {
203
+ throw new DOMException('The operation was aborted.', 'AbortError');
204
+ }
203
205
 
204
- if (request.signal) {
205
- if (request.signal.aborted) {
206
- throw new DOMException('The operation was aborted.', 'AbortError');
207
- }
208
-
209
- let remove_abort_listener = noop;
210
- /** @type {Promise<never>} */
211
- const abort_promise = new Promise((_, reject) => {
212
- const on_abort = () => {
213
- reject(new DOMException('The operation was aborted.', 'AbortError'));
214
- };
215
- request.signal.addEventListener('abort', on_abort, { once: true });
216
- remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
217
- });
206
+ const subrequest_state = fork_state_for_subrequest(state);
218
207
 
219
- const result = await Promise.race([
220
- respond(request, options, manifest, subrequest_state),
221
- abort_promise
222
- ]);
223
- remove_abort_listener();
224
- return result;
225
- } else {
208
+ if (!request.signal) {
226
209
  return await respond(request, options, manifest, subrequest_state);
227
210
  }
211
+
212
+ let remove_abort_listener = noop;
213
+ /** @type {Promise<never>} */
214
+ const abort_promise = new Promise((_, reject) => {
215
+ const on_abort = () => {
216
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
217
+ };
218
+ request.signal.addEventListener('abort', on_abort, { once: true });
219
+ remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
220
+ });
221
+
222
+ return Promise.race([
223
+ respond(request, options, manifest, subrequest_state),
224
+ abort_promise
225
+ ]).finally(remove_abort_listener);
228
226
  }