@sveltejs/kit 2.65.1 → 2.66.0

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 (39) hide show
  1. package/package.json +3 -3
  2. package/src/core/adapt/builder.js +33 -6
  3. package/src/core/config/index.js +60 -2
  4. package/src/core/config/options.js +12 -3
  5. package/src/core/env.js +7 -3
  6. package/src/core/postbuild/analyse.js +4 -1
  7. package/src/core/postbuild/prerender.js +13 -4
  8. package/src/core/sync/create_manifest_data/index.js +13 -2
  9. package/src/core/sync/write_client_manifest.js +2 -0
  10. package/src/core/sync/write_server.js +12 -10
  11. package/src/core/sync/write_tsconfig.js +1 -2
  12. package/src/exports/internal/env.js +1 -1
  13. package/src/exports/public.d.ts +13 -1
  14. package/src/exports/vite/build/build_server.js +6 -1
  15. package/src/exports/vite/build/build_service_worker.js +8 -2
  16. package/src/exports/vite/index.js +44 -16
  17. package/src/exports/vite/preview/index.js +13 -4
  18. package/src/exports/vite/static_analysis/index.js +8 -3
  19. package/src/runtime/app/paths/server.js +7 -1
  20. package/src/runtime/app/server/remote/form.js +2 -2
  21. package/src/runtime/client/client.js +30 -8
  22. package/src/runtime/client/fetcher.js +3 -2
  23. package/src/runtime/client/remote-functions/form.svelte.js +16 -6
  24. package/src/runtime/client/remote-functions/prerender.svelte.js +5 -0
  25. package/src/runtime/client/remote-functions/query/instance.svelte.js +8 -1
  26. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +25 -8
  27. package/src/runtime/client/remote-functions/shared.svelte.js +7 -1
  28. package/src/runtime/client/types.d.ts +6 -0
  29. package/src/runtime/form-utils.js +1 -0
  30. package/src/runtime/server/page/render.js +15 -13
  31. package/src/runtime/server/remote.js +9 -3
  32. package/src/runtime/server/respond.js +4 -1
  33. package/src/types/internal.d.ts +3 -1
  34. package/src/types/private.d.ts +24 -1
  35. package/src/utils/shared-iterator.js +5 -0
  36. package/src/utils/url.js +1 -1
  37. package/src/version.js +1 -1
  38. package/types/index.d.ts +54 -11
  39. package/types/index.d.ts.map +2 -1
@@ -51,10 +51,19 @@ export async function preview(vite, vite_config, svelte_config) {
51
51
  set_assets(assets);
52
52
 
53
53
  const server = new Server(manifest);
54
- await server.init({
55
- env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''),
56
- read: (file) => createReadableStream(`${dir}/${file}`)
57
- });
54
+
55
+ try {
56
+ await server.init({
57
+ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''),
58
+ read: (file) => createReadableStream(`${dir}/${file}`)
59
+ });
60
+ } catch (error) {
61
+ // Vite erases the error message when starting the preview server so we store
62
+ // it in the stack instead. This ensures errors thrown using `stackless`
63
+ // are still readable
64
+ if (error instanceof Error) error.stack = error.message;
65
+ throw error;
66
+ }
58
67
 
59
68
  const emulator = await svelte_config.kit.adapter?.emulate?.();
60
69
 
@@ -1,4 +1,6 @@
1
1
  /** @import { PageOptions } from './types.js' */
2
+ import process from 'node:process';
3
+ import path from 'node:path';
2
4
  import { tsPlugin } from '@sveltejs/acorn-typescript';
3
5
  import { Parser } from 'acorn';
4
6
  import { read } from '../../../utils/filesystem.js';
@@ -227,7 +229,10 @@ export function get_page_options(filepath) {
227
229
  }
228
230
  }
229
231
 
230
- export function create_node_analyser() {
232
+ /**
233
+ * @param {string} cwd
234
+ */
235
+ export function create_node_analyser(cwd = process.cwd()) {
231
236
  const static_exports = new Map();
232
237
 
233
238
  /**
@@ -271,7 +276,7 @@ export function create_node_analyser() {
271
276
  }
272
277
 
273
278
  if (node.server) {
274
- const server_page_options = get_page_options(node.server);
279
+ const server_page_options = get_page_options(path.join(cwd, node.server));
275
280
  if (server_page_options === null) {
276
281
  cache(key, null);
277
282
  return null;
@@ -280,7 +285,7 @@ export function create_node_analyser() {
280
285
  }
281
286
 
282
287
  if (node.universal) {
283
- const universal_page_options = get_page_options(node.universal);
288
+ const universal_page_options = get_page_options(path.join(cwd, node.universal));
284
289
  if (universal_page_options === null) {
285
290
  cache(key, null);
286
291
  return null;
@@ -1,6 +1,7 @@
1
1
  import { base, assets, relative, initial_base } from './internal/server.js';
2
2
  import { resolve_route, find_route } from '../../../utils/routing.js';
3
3
  import { decode_pathname } from '../../../utils/url.js';
4
+ import { add_data_suffix } from '../../pathname.js';
4
5
  import { try_get_request_store } from '@sveltejs/kit/internal/server';
5
6
  import { manifest } from '__sveltekit/server';
6
7
  import { get_hooks } from '__SERVER__/internal.js';
@@ -26,7 +27,12 @@ export function resolve(id, params) {
26
27
  const store = try_get_request_store();
27
28
 
28
29
  if (store && !store.state.prerendering?.fallback) {
29
- const after_base = store.event.url.pathname.slice(initial_base.length);
30
+ // the relative path depth must reflect the URL the browser is actually at, which
31
+ // for a data request includes the `__data.json` suffix that was stripped during routing
32
+ const pathname = store.event.isDataRequest
33
+ ? add_data_suffix(store.event.url.pathname)
34
+ : store.event.url.pathname;
35
+ const after_base = pathname.slice(initial_base.length);
30
36
  const segments = after_base.split('/').slice(2);
31
37
  const prefix = segments.map(() => '..').join('/') || '.';
32
38
 
@@ -1,5 +1,5 @@
1
1
  /** @import { RemoteFormInput, RemoteForm, InvalidField } from '@sveltejs/kit' */
2
- /** @import { InternalRemoteFormIssue, MaybePromise, RemoteFormInternals } from 'types' */
2
+ /** @import { InternalRemoteFormIssue, MaybePromise, HasNonOptionalBoolean, RemoteFormInternals } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
4
  import { get_request_store } from '@sveltejs/kit/internal/server';
5
5
  import { DEV } from 'esm-env';
@@ -46,7 +46,7 @@ import { ValidationError } from '@sveltejs/kit/internal';
46
46
  * @template {StandardSchemaV1<RemoteFormInput, Record<string, any>>} Schema
47
47
  * @template Output
48
48
  * @overload
49
- * @param {Schema} validate
49
+ * @param {true extends HasNonOptionalBoolean<StandardSchemaV1.InferInput<Schema>> ? 'Error: All booleans in form schemas must be optional (e.g. `v.optional(v.boolean(), false)`) because checkbox inputs do not send a false value when unchecked.' : Schema} validate
50
50
  * @param {(data: StandardSchemaV1.InferOutput<Schema>, issue: InvalidField<StandardSchemaV1.InferInput<Schema>>) => MaybePromise<Output>} fn
51
51
  * @returns {RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>}
52
52
  * @since 2.27
@@ -718,8 +718,6 @@ async function initialize(result, target, hydrate) {
718
718
  // which causes component script blocks to run asynchronously
719
719
  void (await Promise.resolve());
720
720
 
721
- restore_snapshot(current_navigation_index);
722
-
723
721
  if (hydrate) {
724
722
  /** @type {import('@sveltejs/kit').AfterNavigate} */
725
723
  const navigation = {
@@ -736,6 +734,8 @@ async function initialize(result, target, hydrate) {
736
734
  after_navigate_callbacks.forEach((fn) => fn(navigation));
737
735
  }
738
736
 
737
+ restore_snapshot(current_navigation_index);
738
+
739
739
  started = true;
740
740
  }
741
741
 
@@ -1488,7 +1488,17 @@ async function load_root_error_page({ status, error, url, route }) {
1488
1488
  return _goto(new URL(error.location, location.href), {}, 0);
1489
1489
  }
1490
1490
 
1491
- // TODO: this falls back to the server when a server exists, but what about SPA mode?
1491
+ const error_template = await app.get_error_template();
1492
+ const handled = await handle_error(error, { url, params, route });
1493
+ const message = String(handled?.message ?? '')
1494
+ .replace(/&/g, '&amp;')
1495
+ .replace(/</g, '&lt;')
1496
+ .replace(/>/g, '&gt;');
1497
+ const html = error_template({ status, message });
1498
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
1499
+ document.documentElement.replaceChild(document.adoptNode(parsed.head), document.head);
1500
+ document.documentElement.replaceChild(document.adoptNode(parsed.body), document.body);
1501
+
1492
1502
  throw error;
1493
1503
  }
1494
1504
  }
@@ -1907,6 +1917,16 @@ async function navigate({
1907
1917
  navigation_result.props.page.url = url;
1908
1918
  }
1909
1919
 
1920
+ // Remove focus before updating the component tree, so that blur/focusout
1921
+ // handlers fire while the old component's data is still valid (#14575)
1922
+ if (
1923
+ !keepfocus &&
1924
+ document.activeElement instanceof HTMLElement &&
1925
+ document.activeElement !== document.body
1926
+ ) {
1927
+ document.activeElement.blur();
1928
+ }
1929
+
1910
1930
  const fork = load_cache_fork && (await load_cache_fork);
1911
1931
 
1912
1932
  if (fork) {
@@ -1985,10 +2005,6 @@ async function navigate({
1985
2005
 
1986
2006
  is_navigating = false;
1987
2007
 
1988
- if (type === 'popstate') {
1989
- restore_snapshot(current_navigation_index);
1990
- }
1991
-
1992
2008
  nav.fulfil(undefined);
1993
2009
 
1994
2010
  // Update to.scroll to the actual scroll position after navigation completed
@@ -2000,6 +2016,10 @@ async function navigate({
2000
2016
  fn(/** @type {import('@sveltejs/kit').AfterNavigate} */ (nav.navigation))
2001
2017
  );
2002
2018
 
2019
+ if (type === 'popstate') {
2020
+ restore_snapshot(current_navigation_index);
2021
+ }
2022
+
2003
2023
  stores.navigating.set((navigating.current = null));
2004
2024
 
2005
2025
  updating = false;
@@ -2419,7 +2439,9 @@ export async function preloadCode(pathname) {
2419
2439
  throw new Error('Cannot call preloadCode(...) on the server');
2420
2440
  }
2421
2441
 
2422
- const url = new URL(pathname, current.url);
2442
+ // `current.url` is null until the first navigation/hydration completes, so fall back
2443
+ // to `location` to support calling `preloadCode` during initial page load (#13297)
2444
+ const url = new URL(pathname, current.url ?? location.href);
2423
2445
 
2424
2446
  if (DEV) {
2425
2447
  if (!pathname.startsWith('/')) {
@@ -94,8 +94,6 @@ export function initial_fetch(resource, opts) {
94
94
  script.remove(); // In case multiple script tags match the same selector
95
95
  let { body, ...init } = JSON.parse(script.textContent);
96
96
 
97
- const ttl = script.getAttribute('data-ttl');
98
- if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
99
97
  const b64 = script.getAttribute('data-b64');
100
98
  if (b64 !== null) {
101
99
  // Can't use native_fetch('data:...;base64,${body}')
@@ -103,6 +101,9 @@ export function initial_fetch(resource, opts) {
103
101
  body = base64_decode(body);
104
102
  }
105
103
 
104
+ const ttl = script.getAttribute('data-ttl');
105
+ if (ttl) cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
106
+
106
107
  return Promise.resolve(new Response(body, init));
107
108
  }
108
109
 
@@ -53,6 +53,9 @@ export function form(id) {
53
53
  /** @type {Map<any, { count: number, instance: RemoteForm<T, U> }>} */
54
54
  const instances = new Map();
55
55
 
56
+ /** @type {StandardSchemaV1 | null} */
57
+ let shared_preflight_schema = null;
58
+
56
59
  /** @param {string | number | boolean} [key] */
57
60
  function create_instance(key) {
58
61
  const action_id_without_key = id;
@@ -90,6 +93,7 @@ export function form(id) {
90
93
  */
91
94
  let enhance_callback = async (instance) => {
92
95
  if (await instance.submit()) {
96
+ await tick();
93
97
  instance.element.reset();
94
98
  }
95
99
  };
@@ -319,7 +323,8 @@ export function form(id) {
319
323
  */
320
324
  async function preflight(form_data) {
321
325
  const data = convert(form_data);
322
- const validated = await preflight_schema?.['~standard'].validate(data);
326
+ const schema = preflight_schema ?? shared_preflight_schema;
327
+ const validated = await schema?.['~standard'].validate(data);
323
328
 
324
329
  if (validated?.issues) {
325
330
  raw_issues = merge_with_server_issues(
@@ -520,7 +525,6 @@ export function form(id) {
520
525
  form.removeEventListener('input', handle_input);
521
526
  form.removeEventListener('reset', handle_reset);
522
527
  element = null;
523
- preflight_schema = undefined;
524
528
  };
525
529
  };
526
530
 
@@ -565,9 +569,10 @@ export function form(id) {
565
569
 
566
570
  const submission = submit(form_data, true);
567
571
 
568
- void submission.finally(() => {
572
+ const decrement = () => {
569
573
  pending_count--;
570
- });
574
+ };
575
+ void submission.then(decrement, decrement);
571
576
 
572
577
  return submission;
573
578
  }
@@ -611,6 +616,11 @@ export function form(id) {
611
616
  /** @type {RemoteForm<T, U>['preflight']} */
612
617
  value: (schema) => {
613
618
  preflight_schema = schema;
619
+
620
+ if (key === undefined) {
621
+ shared_preflight_schema = schema;
622
+ }
623
+
614
624
  return instance;
615
625
  }
616
626
  },
@@ -634,8 +644,8 @@ export function form(id) {
634
644
  let array = [];
635
645
 
636
646
  const data = convert(form_data);
637
-
638
- const validated = await preflight_schema?.['~standard'].validate(data);
647
+ const schema = preflight_schema ?? shared_preflight_schema;
648
+ const validated = await schema?.['~standard'].validate(data);
639
649
 
640
650
  if (validate_id !== id) {
641
651
  return;
@@ -5,6 +5,7 @@ import * as devalue from 'devalue';
5
5
  import { app, goto, prerender_responses } from '../client.js';
6
6
  import { get_remote_request_headers, remote_request, unwrap_node } from './shared.svelte.js';
7
7
  import { create_remote_key, stringify_remote_arg } from '../../shared.js';
8
+ import { noop } from '../../../utils/functions.js';
8
9
 
9
10
  // Initialize Cache API for prerender functions
10
11
  const CACHE_NAME = __SVELTEKIT_DEV__ ? `sveltekit:${Date.now()}` : `sveltekit:${version}`;
@@ -166,6 +167,10 @@ class Prerender {
166
167
  throw error;
167
168
  }
168
169
  );
170
+
171
+ // rejections are surfaced via `.error` for reactive consumers — make sure the
172
+ // stored promise (consumed without `await`) never becomes an unhandled rejection
173
+ this.#promise.catch(noop);
169
174
  }
170
175
 
171
176
  /**
@@ -88,7 +88,9 @@ export class Query {
88
88
  // every time you see this comment, try removing the `tick.then` here and see
89
89
  // if all the tests still pass with the latest svelte version
90
90
  // if they do, congrats, you can remove tick.then
91
- void tick().then(() => this.#get_promise());
91
+ void tick()
92
+ .then(() => this.#get_promise())
93
+ .catch(noop);
92
94
  }
93
95
 
94
96
  #clear_pending() {
@@ -101,6 +103,11 @@ export class Query {
101
103
 
102
104
  const { promise, resolve, reject } = with_resolvers();
103
105
 
106
+ // the rejection is surfaced via `.error` / the `then` getter for awaiting
107
+ // consumers — a purely reactive consumer (`.current`) attaches no handler,
108
+ // so make sure the stored promise can never become an unhandled rejection
109
+ promise.catch(noop);
110
+
104
111
  this.#latest.push(resolve);
105
112
 
106
113
  Promise.resolve(this.#fn())
@@ -124,6 +124,7 @@ export class LiveQuery {
124
124
 
125
125
  /** @type {PromiseWithResolvers<void>} */
126
126
  const { promise: stopped, resolve: on_stop } = with_resolvers();
127
+ let connected = false;
127
128
 
128
129
  while (!this.#done) {
129
130
  const controller = new AbortController();
@@ -136,16 +137,22 @@ export class LiveQuery {
136
137
  const generator = create_live_iterator(this.#id, this.#payload, controller, () => {
137
138
  this.#connected = true;
138
139
  this.#attempt = 0;
140
+ connected = true;
139
141
  on_connect();
140
142
  });
141
143
 
142
144
  try {
143
145
  const { done, value } = await generator.next();
144
146
 
145
- // TODO how much special handling does this need?
146
- // should we even try to reconnect if this is the case?
147
- if (done && !this.#ready) {
148
- throw new Error('Live query completed before yielding a value');
147
+ if (done) {
148
+ if (!this.#ready) {
149
+ throw new Error('Live query completed before yielding a value');
150
+ }
151
+ // stream completed without yielding (e.g. the generator returned
152
+ // immediately on reconnect) — keep the last good value
153
+ this.#done = true;
154
+ this.#fan_out.done();
155
+ break;
149
156
  }
150
157
 
151
158
  this.set(value);
@@ -202,6 +209,12 @@ export class LiveQuery {
202
209
  }
203
210
 
204
211
  this.#interrupt = null;
212
+ // If the loop exited without ever successfully connecting, settle the
213
+ // reconnect handshake so callers (e.g. `invalidateAll()`) never await
214
+ // a forever-pending promise.
215
+ if (!connected) {
216
+ on_connect_failed(this.#error ?? new Error('Live query connection was interrupted'));
217
+ }
205
218
  on_stop();
206
219
  }
207
220
 
@@ -345,10 +358,13 @@ export class LiveQuery {
345
358
  promise.catch(noop);
346
359
  this.#done = false;
347
360
  this.#attempt = 0;
348
- // The previous fan-out may have been closed by `done()`/`fail()`. Future
349
- // `for await` consumers need a fresh, open fan-out attached to the new
350
- // `#main` lifetime.
351
- this.#fan_out = new SharedIterator();
361
+ // Keep the existing fan-out open so active `for await` consumers
362
+ // continue receiving values from the new connection without interruption.
363
+ // Only replace it if it was already closed by a prior `done()`/`fail()`
364
+ // (e.g. reconnecting after a finite or hard-failed stream)
365
+ if (this.#fan_out.closed) {
366
+ this.#fan_out = new SharedIterator();
367
+ }
352
368
  this.#main({ on_connect, on_connect_failed }).catch(noop);
353
369
  await promise;
354
370
  }
@@ -383,6 +399,7 @@ export class LiveQuery {
383
399
  void this.#interrupt?.();
384
400
 
385
401
  if (this.#reject_first) {
402
+ this.#promise.catch(noop);
386
403
  this.#reject_first(error);
387
404
  this.#resolve_first = null;
388
405
  this.#reject_first = null;
@@ -114,7 +114,13 @@ export async function remote_request(url, init) {
114
114
  const response = await fetch(url, init);
115
115
 
116
116
  if (!response.ok) {
117
- throw new HttpError(500, 'Failed to execute remote function');
117
+ const result = await response.json().catch(() => ({
118
+ type: 'error',
119
+ status: response.status,
120
+ error: response.statusText
121
+ }));
122
+
123
+ throw new HttpError(result.status ?? response.status ?? 500, result.error);
118
124
  }
119
125
 
120
126
  const result = /** @type {RemoteFunctionResponse} */ (await response.json());
@@ -58,6 +58,12 @@ export interface SvelteKitApp {
58
58
  hash: boolean;
59
59
 
60
60
  root: typeof SvelteComponent;
61
+
62
+ /**
63
+ * Lazily loads the contents of src/error.html, used as a last-resort
64
+ * error page when the root layout's load function throws during client-side rendering.
65
+ */
66
+ get_error_template: () => Promise<(data: { status: number; message: string }) => string>;
61
67
  }
62
68
 
63
69
  export type NavigationIntent = {
@@ -50,6 +50,7 @@ export function convert_formdata(data) {
50
50
  values = values.filter(
51
51
  (entry) => typeof entry === 'string' || entry.name !== '' || entry.size > 0
52
52
  );
53
+ if (values.length === 0 && !is_array) continue;
53
54
 
54
55
  if (key.startsWith('n:')) {
55
56
  key = key.slice(2);
@@ -12,7 +12,7 @@ import { public_env } from '../../shared-server.js';
12
12
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
13
13
  import { SCHEME } from '../../../utils/url.js';
14
14
  import { create_server_routing_response, generate_route_object } from './server_routing.js';
15
- import { add_resolution_suffix } from '../../pathname.js';
15
+ import { add_data_suffix, add_resolution_suffix } from '../../pathname.js';
16
16
  import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
17
17
  import { text_encoder } from '../../utils.js';
18
18
  import {
@@ -120,7 +120,12 @@ export async function render_response({
120
120
  // if appropriate, use relative paths for greater portability
121
121
  if (paths.relative) {
122
122
  if (!state.prerendering?.fallback) {
123
- const segments = event.url.pathname.slice(paths.base.length).split('/').slice(2);
123
+ // the relative path depth must reflect the URL the browser is actually at, which
124
+ // for a data request includes the `__data.json` suffix that was stripped during routing
125
+ const pathname = event.isDataRequest
126
+ ? add_data_suffix(event.url.pathname)
127
+ : event.url.pathname;
128
+ const segments = pathname.slice(paths.base.length).split('/').slice(2);
124
129
 
125
130
  base = segments.map(() => '..').join('/') || '.';
126
131
 
@@ -368,7 +373,13 @@ export async function render_response({
368
373
  if (page_config.csr && client) {
369
374
  const route = client.routes?.find((r) => r.id === event.route.id) ?? null;
370
375
 
371
- if (client.uses_env_dynamic_public && state.prerendering) {
376
+ // when serving a prerendered page in an app that uses runtime public env vars, we must
377
+ // import the env.js module so that it evaluates before any user code can evaluate.
378
+ // TODO revert to using top-level await once https://bugs.webkit.org/show_bug.cgi?id=242740 is fixed
379
+ // https://github.com/sveltejs/kit/pull/11601
380
+ const load_env_eagerly = client.uses_env_dynamic_public && !!state.prerendering;
381
+
382
+ if (load_env_eagerly) {
372
383
  modulepreloads.add(`${paths.app_dir}/env.js`);
373
384
  }
374
385
 
@@ -401,15 +412,6 @@ export async function render_response({
401
412
 
402
413
  const blocks = [];
403
414
 
404
- // when serving a prerendered page in an app that uses $env/dynamic/public, we must
405
- // import the env.js module so that it evaluates before any user code can evaluate.
406
- // TODO revert to using top-level await once https://bugs.webkit.org/show_bug.cgi?id=242740 is fixed
407
- // https://github.com/sveltejs/kit/pull/11601
408
- const load_env_eagerly =
409
- (__SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__ ||
410
- client.uses_env_dynamic_public) &&
411
- state.prerendering;
412
-
413
415
  const properties = [`base: ${base_expression}`];
414
416
 
415
417
  if (paths.assets) {
@@ -433,7 +435,7 @@ export async function render_response({
433
435
 
434
436
  if (Object.keys(options.hooks.transport).length > 0) {
435
437
  if (client.inline) {
436
- app_declaration = `const app = __sveltekit_${options.version_hash}.app.app;`;
438
+ app_declaration = `const app = ${global}.app.app;`;
437
439
  } else if (client.app) {
438
440
  app_declaration = `const app = await import(${s(prefixed(client.app))});`;
439
441
  } else {
@@ -57,6 +57,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
57
57
  'sveltekit.remote.call.name': internals.name
58
58
  });
59
59
 
60
+ /** @type {HeadersInit | undefined} */
61
+ const headers = state.prerendering ? undefined : { 'cache-control': 'private, no-store' };
62
+
60
63
  try {
61
64
  /** @type {RemoteFunctionData} */
62
65
  const data = {};
@@ -226,7 +229,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
226
229
  /** @type {RemoteFunctionResponse} */ ({
227
230
  type: 'result',
228
231
  data: stringify(data, transport)
229
- })
232
+ }),
233
+ { headers }
230
234
  );
231
235
  }
232
236
 
@@ -275,7 +279,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
275
279
  /** @type {RemoteFunctionResponse} */ ({
276
280
  type: 'result',
277
281
  data: stringify(data, transport)
278
- })
282
+ }),
283
+ { headers }
279
284
  );
280
285
  } catch (error) {
281
286
  if (error instanceof Redirect) {
@@ -285,7 +290,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
285
290
  /** @type {RemoteFunctionResponse} */ ({
286
291
  type: 'result',
287
292
  data: stringify(data, transport)
288
- })
293
+ }),
294
+ { headers }
289
295
  );
290
296
  }
291
297
 
@@ -619,7 +619,10 @@ export async function internal_respond(request, options, manifest, state) {
619
619
  invalidated_data_nodes,
620
620
  trailing_slash
621
621
  );
622
- } else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
622
+ } else if (
623
+ route.endpoint &&
624
+ (!route.page || (!state.prerendering && is_endpoint_request(event)))
625
+ ) {
623
626
  response = await render_endpoint(event, event_state, await route.endpoint(), state);
624
627
  } else if (route.page) {
625
628
  if (!page_nodes) {
@@ -44,7 +44,6 @@ export interface ServerModule {
44
44
  export interface ServerInternalModule {
45
45
  set_assets(path: string): void;
46
46
  set_building(): void;
47
- set_env(environment: Record<string, string>): void;
48
47
  set_manifest(manifest: SSRManifest): void;
49
48
  set_prerendering(): void;
50
49
  set_private_env(environment: Record<string, string>): void;
@@ -103,6 +102,9 @@ export interface BuildData {
103
102
  routes?: SSRClientRoute[];
104
103
  stylesheets: string[];
105
104
  fonts: string[];
105
+ /**
106
+ * Whether the client uses public dynamic env vars — `$env/dynamic/public` or `$app/env/public`.
107
+ */
106
108
  uses_env_dynamic_public: boolean;
107
109
  /** Only set in case of `bundleStrategy === 'inline'`. */
108
110
  inline?: {
@@ -61,7 +61,10 @@ export namespace Csp {
61
61
  | 'unsafe-eval'
62
62
  | 'unsafe-hashes'
63
63
  | 'unsafe-inline'
64
+ | 'unsafe-allow-redirects'
65
+ | 'unsafe-webtransport-hashes'
64
66
  | 'wasm-unsafe-eval'
67
+ | 'trusted-types-eval'
65
68
  | 'none';
66
69
  type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`;
67
70
  type FrameSource = HostSource | SchemeSource | 'self' | 'none';
@@ -70,7 +73,16 @@ export namespace Csp {
70
73
  type HostProtocolSchemes = `${string}://` | '';
71
74
  type HttpDelineator = '/' | '?' | '#' | '\\';
72
75
  type PortScheme = `:${number}` | '' | ':*';
73
- type SchemeSource = 'http:' | 'https:' | 'data:' | 'mediastream:' | 'blob:' | 'filesystem:';
76
+ type SchemeSource =
77
+ | 'http:'
78
+ | 'https:'
79
+ | 'ws:'
80
+ | 'wss:'
81
+ | 'data:'
82
+ | 'mediastream:'
83
+ | 'blob:'
84
+ | 'filesystem:'
85
+ | (`${string}:` & {});
74
86
  type Source = HostSource | SchemeSource | CryptoSource | BaseSource;
75
87
  type Sources = Source[];
76
88
  }
@@ -252,3 +264,14 @@ export type DeepPartial<T> = T extends Record<PropertyKey, unknown> | unknown[]
252
264
  : T | undefined;
253
265
 
254
266
  export type IsAny<T> = 0 extends 1 & T ? true : false;
267
+
268
+ export type HasNonOptionalBoolean<T> =
269
+ IsAny<T> extends true
270
+ ? never
271
+ : [T] extends [boolean]
272
+ ? true
273
+ : T extends Array<infer U>
274
+ ? HasNonOptionalBoolean<U>
275
+ : T extends Record<string, any>
276
+ ? { [K in keyof T]: HasNonOptionalBoolean<T[K]> }[keyof T]
277
+ : never;
@@ -52,6 +52,11 @@ export class SharedIterator {
52
52
  /** @type {unknown} */
53
53
  #terminal_error = undefined;
54
54
 
55
+ /** Whether `done()` or `fail()` has been broadcast. */
56
+ get closed() {
57
+ return this.#closed;
58
+ }
59
+
55
60
  /**
56
61
  * @param {(instance: SharedIterator<T>) => (() => void)} [start]
57
62
  */
package/src/utils/url.js CHANGED
@@ -119,6 +119,7 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
119
119
  /**
120
120
  * URL properties that could change during the lifetime of the page,
121
121
  * which excludes things like `origin`
122
+ * @type {(keyof URL)[]}
122
123
  */
123
124
  const tracked_url_properties = ['href', 'pathname', 'search', 'toString', 'toJSON'];
124
125
  if (allow_hash) tracked_url_properties.push('hash');
@@ -127,7 +128,6 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
127
128
  Object.defineProperty(tracked, property, {
128
129
  get() {
129
130
  callback();
130
- // @ts-expect-error
131
131
  return url[property];
132
132
  },
133
133
 
package/src/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '2.65.1';
4
+ export const VERSION = '2.66.0';