diff --git a/packages/kit/package.json b/packages/kit/package.json index 7e3e64b53321..971f274f593e 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -130,6 +130,18 @@ "browser": "./src/runtime/app/paths/client.js", "default": "./src/runtime/app/paths/server.js" }, + "#app/state": { + "types": "./src/runtime/app/state/client.svelte.js", + "workerd": "./src/runtime/app/state/server.js", + "browser": "./src/runtime/app/state/client.svelte.js", + "default": "./src/runtime/app/state/server.js" + }, + "#app/state/client": { + "types": "./src/runtime/app/state/client.svelte.js", + "workerd": "./src/runtime/invalid-import.js", + "browser": "./src/runtime/app/state/client.svelte.js", + "default": "./src/runtime/invalid-import.js" + }, "#internal": { "workerd": "./src/exports/internal/server/index.js", "browser": "./src/exports/internal/client.js", diff --git a/packages/kit/src/runtime/app/forms/client.js b/packages/kit/src/runtime/app/forms/client.js index b8e7b14f110c..5bd46d53b19d 100644 --- a/packages/kit/src/runtime/app/forms/client.js +++ b/packages/kit/src/runtime/app/forms/client.js @@ -8,7 +8,7 @@ import { handle_error, is_current_location } from '../../client/client.js'; -import { notify_version } from '../../client/state.svelte.js'; +import { notify_version } from '#app/state/client'; import { deserialize } from './shared.js'; export { applyAction, deserialize }; diff --git a/packages/kit/src/runtime/app/state/client.js b/packages/kit/src/runtime/app/state/client.js deleted file mode 100644 index 9b76fb9e2d32..000000000000 --- a/packages/kit/src/runtime/app/state/client.js +++ /dev/null @@ -1,63 +0,0 @@ -import { - page as _page, - navigating as _navigating, - updated as _updated -} from '../../client/state.svelte.js'; - -export const page = { - get data() { - return _page.data; - }, - get error() { - return _page.error; - }, - get form() { - return _page.form; - }, - get params() { - return _page.params; - }, - get route() { - return _page.route; - }, - get shallow() { - return _page.shallow; - }, - get state() { - return _page.state; - }, - get status() { - return _page.status; - }, - get url() { - return _page.url; - } -}; - -export const navigating = { - get from() { - return _navigating.current ? _navigating.current.from : null; - }, - get to() { - return _navigating.current ? _navigating.current.to : null; - }, - get type() { - return _navigating.current ? _navigating.current.type : null; - }, - get willUnload() { - return _navigating.current ? _navigating.current.willUnload : null; - }, - get delta() { - return _navigating.current?.type === 'popstate' ? _navigating.current.delta : null; - }, - get complete() { - return _navigating.current ? _navigating.current.complete : null; - } -}; - -export const updated = { - get current() { - return _updated.current; - }, - check: _updated.check -}; diff --git a/packages/kit/src/runtime/app/state/client.svelte.js b/packages/kit/src/runtime/app/state/client.svelte.js new file mode 100644 index 000000000000..d7789c00c38f --- /dev/null +++ b/packages/kit/src/runtime/app/state/client.svelte.js @@ -0,0 +1,207 @@ +/** @import { Navigation } from '$app/navigation' */ +/** @import { Page } from '$app/state' */ +import { DEV } from 'esm-env'; +import { assets } from '#app/paths'; +import { version } from '$app/env'; + +/** @type {Page} */ +export const internal_page = new (class Page { + data = $state.raw({}); + form = $state.raw(null); + error = $state.raw(null); + params = $state.raw({}); + route = $state.raw({ id: null }); + shallow = $state.raw(null); + state = $state.raw({}); + status = $state.raw(-1); + url = $state.raw(new URL('a:')); +})(); + +/** + * @param {Partial} new_page + */ +export function update_page(new_page) { + Object.assign(internal_page, new_page); +} + +/** + * A read-only reactive object with information about the current page, serving several use cases: + * - retrieving the combined `data` of all pages/layouts anywhere in your component tree (also see [loading data](https://svelte.dev/docs/kit/load)) + * - retrieving the current value of the `form` prop anywhere in your component tree (also see [form actions](https://svelte.dev/docs/kit/form-actions)) + * - retrieving the page state that was set through `goto` (also see [goto](https://svelte.dev/docs/kit/$app-navigation#goto) and [shallow routing](https://svelte.dev/docs/kit/shallow-routing)) + * - retrieving metadata such as the URL you're on, the current route and its parameters, the target of a shallow navigation, and whether or not there was an error + * + * ```svelte + * + * + * + *

Currently at {page.url.pathname}

+ * + * {#if page.error} + * Problem detected + * {:else} + * All systems operational + * {/if} + * ``` + * + * Changes to `page` are available exclusively with runes. (The legacy reactivity syntax will not reflect any changes) + * + * ```svelte + * + * + * ``` + * + * On the server, values can only be read during rendering (in other words _not_ in e.g. `load` functions). In the browser, the values can be read at any time. + * + * @type {Page} + */ +export const page = { + get data() { + return internal_page.data; + }, + get error() { + return internal_page.error; + }, + get form() { + return internal_page.form; + }, + get params() { + return internal_page.params; + }, + get route() { + return internal_page.route; + }, + get shallow() { + return internal_page.shallow; + }, + get state() { + return internal_page.state; + }, + get status() { + return internal_page.status; + }, + get url() { + return internal_page.url; + } +}; + +/** @type {Navigation | null} */ +let navigation = $state.raw(null); + +/** + * @param {Navigation | null} value + */ +export function set_navigation(value) { + navigation = value; +} + +/** + * A read-only object representing an in-progress navigation, with `from`, `to`, `type` and (if `type === 'popstate'`) `delta` properties. + * Values are `null` when no navigation is occurring, or during server rendering. + * @type {Navigation | { from: null, to: null, type: null, willUnload: null, delta: null, complete: null }} + */ +export const navigating = { + get from() { + return navigation ? navigation.from : null; + }, + get to() { + return navigation ? navigation.to : null; + }, + get type() { + return navigation ? navigation.type : null; + }, + get willUnload() { + return navigation ? navigation.willUnload : null; + }, + // @ts-expect-error TODO not entirely sure what's going on here + get delta() { + return navigation?.type === 'popstate' ? navigation.delta : null; + }, + get complete() { + return navigation ? navigation.complete : null; + } +}; + +const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__; + +/** @type {number | undefined} */ +let timeout; + +/** @type {Promise | undefined} */ +let checking; + +let _updated = $state(false); + +/** + * A read-only reactive value that's initially `false`. SvelteKit checks for new versions on data, remote, and form action responses (via the `x-sveltekit-version` header), when the tab regains focus or becomes visible, and on a poll interval (see [`version.pollInterval`](https://svelte.dev/docs/kit/configuration#version)). `updated.current` is set to `true` when a new version is detected. `updated.check()` will force an immediate check, regardless of polling. + * @type {{ get current(): boolean; check(): Promise; }} + */ +export const updated = { + get current() { + return _updated; + }, + async check() { + if (DEV) return false; + + window.clearTimeout(timeout); + + if (_updated) { + return Promise.resolve(true); + } + + return (checking ??= (async () => { + try { + const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, { + headers: { + 'cache-control': 'no-cache' + } + }); + + if (!res.ok) { + return false; + } + + const data = await res.json(); + return (_updated ||= data.version !== version); + } catch { + return false; + } finally { + checking = undefined; + if (interval && !_updated) timeout = window.setTimeout(updated.check, interval); + } + })()); + } +}; + +if (!DEV && interval) { + timeout = window.setTimeout(updated.check, interval); +} + +/** + * Mark `updated.current` as `true` if the given version differs from the one + * the app was hydrated with. Called from the server response header path. + * Does NOT reset the poll timer — unlike `check()`, this is a passive observation + * from a single server instance's response, not an explicit version check. The + * poll timer continues on its original schedule as a backstop. This is important + * for platforms that implement skew protection, where `x-sveltekit-version` + * may be out of date — in this case we still need to poll for `version.json`. + * @param {string | null} new_version + */ +export function notify_version(new_version) { + if (__SVELTEKIT_APP_VERSION_CHECKS_ENABLED__ && new_version) { + _updated ||= new_version !== version; + } +} + +/** + * Used for testing + */ +export function reset_updated() { + _updated = false; +} diff --git a/packages/kit/src/runtime/client/state.svelte.spec.js b/packages/kit/src/runtime/app/state/client.svelte.spec.js similarity index 94% rename from packages/kit/src/runtime/client/state.svelte.spec.js rename to packages/kit/src/runtime/app/state/client.svelte.spec.js index ad74dec588d0..3e4e85fd4bfb 100644 --- a/packages/kit/src/runtime/client/state.svelte.spec.js +++ b/packages/kit/src/runtime/app/state/client.svelte.spec.js @@ -1,5 +1,5 @@ import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest'; -import { updated, notify_version } from './state.svelte.js'; +import { updated, notify_version, reset_updated } from './client.svelte.js'; // Mock `esm-env` so the version-check logic is initialised. In the test env, // `DEV` is true which would skip the `if (!DEV && ...)` block. @@ -15,10 +15,7 @@ vi.hoisted(() => { }); describe('updated', () => { - beforeEach(() => { - // reset state between tests - updated.current = false; - }); + beforeEach(reset_updated); afterEach(() => { vi.useRealTimers(); @@ -57,7 +54,7 @@ describe('updated', () => { }) ); - const { updated } = await import('./state.svelte.js'); + const { updated } = await import('./client.svelte.js'); expect(await updated.check()).toBe(true); expect(updated.current).toBe(true); }); @@ -120,7 +117,7 @@ describe('updated', () => { }) ); - const { updated } = await import('./state.svelte.js'); + const { updated } = await import('./client.svelte.js'); const first = updated.check(); expect(resolve_queue).toHaveLength(1); diff --git a/packages/kit/src/runtime/app/state/index.js b/packages/kit/src/runtime/app/state/index.js index 1fc2bc16f3fd..2fae6844ffdc 100644 --- a/packages/kit/src/runtime/app/state/index.js +++ b/packages/kit/src/runtime/app/state/index.js @@ -1,66 +1 @@ -/** @import { Navigation } from '$app/navigation' */ -/** @import { Page } from '$app/state' */ -import { - page as client_page, - navigating as client_navigating, - updated as client_updated -} from './client.js'; -import { - page as server_page, - navigating as server_navigating, - updated as server_updated -} from './server.js'; -import { BROWSER } from 'esm-env'; - -/** - * A read-only reactive object with information about the current page, serving several use cases: - * - retrieving the combined `data` of all pages/layouts anywhere in your component tree (also see [loading data](https://svelte.dev/docs/kit/load)) - * - retrieving the current value of the `form` prop anywhere in your component tree (also see [form actions](https://svelte.dev/docs/kit/form-actions)) - * - retrieving the page state that was set through `goto` (also see [goto](https://svelte.dev/docs/kit/$app-navigation#goto) and [shallow routing](https://svelte.dev/docs/kit/shallow-routing)) - * - retrieving metadata such as the URL you're on, the current route and its parameters, the target of a shallow navigation, and whether or not there was an error - * - * ```svelte - * - * - * - *

Currently at {page.url.pathname}

- * - * {#if page.error} - * Problem detected - * {:else} - * All systems operational - * {/if} - * ``` - * - * Changes to `page` are available exclusively with runes. (The legacy reactivity syntax will not reflect any changes) - * - * ```svelte - * - * - * ``` - * - * On the server, values can only be read during rendering (in other words _not_ in e.g. `load` functions). In the browser, the values can be read at any time. - * - * @type {Page} - */ -export const page = BROWSER ? client_page : server_page; - -/** - * A read-only object representing an in-progress navigation, with `from`, `to`, `type` and (if `type === 'popstate'`) `delta` properties. - * Values are `null` when no navigation is occurring, or during server rendering. - * @type {Navigation | { from: null, to: null, type: null, willUnload: null, delta: null, complete: null }} - */ -// @ts-expect-error -export const navigating = BROWSER ? client_navigating : server_navigating; - -/** - * A read-only reactive value that's initially `false`. SvelteKit checks for new versions on data, remote, and form action responses (via the `x-sveltekit-version` header), when the tab regains focus or becomes visible, and on a poll interval (see [`version.pollInterval`](https://svelte.dev/docs/kit/configuration#version)). `updated.current` is set to `true` when a new version is detected. `updated.check()` will force an immediate check, regardless of polling. - * @type {{ get current(): boolean; check(): Promise; }} - */ -export const updated = BROWSER ? client_updated : server_updated; +export { page, navigating, updated } from '#app/state'; diff --git a/packages/kit/src/runtime/app/state/public.d.ts b/packages/kit/src/runtime/app/state/public.d.ts index 2f8a3dee5d8a..2132d7cde349 100644 --- a/packages/kit/src/runtime/app/state/public.d.ts +++ b/packages/kit/src/runtime/app/state/public.d.ts @@ -4,7 +4,7 @@ import type { RouteId as AppRouteId } from '$app/types'; -export * from './index.js'; +export { page, navigating, updated } from './client.svelte.js'; export type ReadonlyURLSearchParams = Omit; diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 719659279097..4fdba71201ef 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -50,7 +50,7 @@ import { validate_load_response } from '../shared.js'; -import { page, navigating, updated, notify_version } from './state.svelte.js'; +import { page, updated, notify_version, update_page, set_navigation } from '#app/state/client'; import { payload } from './payload.js'; import { add_data_suffix, @@ -653,12 +653,16 @@ async function _invalidate(reset_page_state = true) { return; } - // Preserve `page.state` when invalidating without resetting it (e.g. `refresh`/`refreshAll`) + apply_navigation_result(navigation_result); + + // Preserve `page.state` when invalidating without resetting it (e.g. `refresh`/`refreshAll`). + // Must run after `apply_navigation_result`, which overwrites `state`/`shallow` with the fresh + // page object's `{}`/`null` values when the page changed. if (!reset_page_state) { - navigation_result.props.page.state = prev_state; + update_page({ state: prev_state }); } - navigation_result.props.page.shallow = prev_shallow; - apply_navigation_result(navigation_result); + + update_page({ shallow: prev_shallow }); current = { ...navigation_result.state, nav: current.nav }; reset_invalidation(); @@ -952,8 +956,7 @@ async function initialize(result, target, should_hydrate) { props, transformError: /** @param {unknown} e */ async (e) => { const error = await handle_error(e, current.nav); - page.error = error; - page.status = error.status; + update_page({ error, status: error.status }); return error; } }); @@ -1036,7 +1039,7 @@ async function get_navigation_result_from_branch({ route }, props: { - page, + page: { ...page }, tree: /** @type {RenderNode} */ ({}) } }; @@ -2032,7 +2035,7 @@ async function navigate({ is_navigating = true; if (started && nav.navigation.type !== 'enter') { - navigating.current = nav.navigation; + set_navigation(nav.navigation); } let navigation_result = intent && (await load_route({ ...intent, action_result })); @@ -2305,7 +2308,7 @@ async function navigate({ // new and replaced entries have no stored values, so this only resets there restore_navigation_snapshot(current_history_index, previous_snapshot_registrations); - navigating.current = null; + set_navigation(null); updating = false; } @@ -2966,7 +2969,7 @@ async function update_state(intent, state, { replace, persist_state, reset }, ca if (nav) { navigation_token = invalidation_token = nav_token; is_navigating = true; - navigating.current = nav.navigation; + set_navigation(nav.navigation); updating = true; } @@ -3022,12 +3025,14 @@ async function update_state(intent, state, { replace, persist_state, reset }, ca blur_active_element(reset); - page.state = state; - page.shallow = { - params: intent?.params ?? null, - route: intent ? { id: intent.route.id } : null, - url - }; + update_page({ + state, + shallow: { + params: intent?.params ?? null, + route: intent ? { id: intent.route.id } : null, + url + } + }); if (nav) { const { activeElement } = document; @@ -3055,7 +3060,7 @@ async function update_state(intent, state, { replace, persist_state, reset }, ca restore_navigation_snapshot(current_history_index, previous_snapshot_registrations); if (nav) { - navigating.current = null; + set_navigation(null); updating = false; } } @@ -3078,8 +3083,10 @@ export async function applyAction(result) { if (result.type === 'error') { await set_nearest_error_page(result.error); } else { - page.form = result.data; - page.status = result.status; + update_page({ + form: result.data, + status: result.status + }); /** @type {Record} */ // this brings Svelte's view of the world in line with SvelteKit's @@ -3444,11 +3451,10 @@ function _start_router() { blur_active_element(reset); - if (state !== page.state) { - page.state = state; - } - - page.shallow = shallow_target; + update_page({ + state, + shallow: shallow_target + }); update_url(url); @@ -3537,7 +3543,7 @@ function _start_router() { // the navigation away from it was successful. // Info about bfcache here: https://web.dev/bfcache if (event.persisted) { - navigating.current = null; + set_navigation(null); } }); @@ -3545,7 +3551,8 @@ function _start_router() { * @param {URL} url */ function update_url(url) { - current.url = page.url = url; + current.url = url; + update_page({ url }); } } @@ -4014,7 +4021,7 @@ if (DEV) { * @param {NavigationFinished} result */ function apply_navigation_result(result) { - Object.assign(page, result.props.page); + update_page(result.props.page); props.tree.data = result.props.tree.data; props.tree.child = result.props.tree.child; diff --git a/packages/kit/src/runtime/client/remote-functions/form.svelte.js b/packages/kit/src/runtime/client/remote-functions/form.svelte.js index 2e01667ef479..3cf0da8c50fb 100644 --- a/packages/kit/src/runtime/client/remote-functions/form.svelte.js +++ b/packages/kit/src/runtime/client/remote-functions/form.svelte.js @@ -11,7 +11,7 @@ import { handle_error, refreshAll } from '../client.js'; -import { page } from '../state.svelte.js'; +import { page } from '#app/state/client'; import { tick } from 'svelte'; import { categorize_updates, remote_request } from './shared.svelte.js'; import { createAttachmentKey } from 'svelte/attachments'; diff --git a/packages/kit/src/runtime/client/remote-functions/query-live/iterator.js b/packages/kit/src/runtime/client/remote-functions/query-live/iterator.js index 8cca95e1f7bb..073d5e394bad 100644 --- a/packages/kit/src/runtime/client/remote-functions/query-live/iterator.js +++ b/packages/kit/src/runtime/client/remote-functions/query-live/iterator.js @@ -1,7 +1,7 @@ /** @import { RemoteFunctionResponse } from 'types' */ import { app_dir, base } from '#app/paths'; import { app } from '../../client.js'; -import { notify_version } from '../../state.svelte.js'; +import { notify_version } from '#app/state/client'; import { handle_side_channel_response } from '../shared.svelte.js'; import * as devalue from 'devalue'; import { HttpError, HandledHttpError } from '@sveltejs/kit/internal'; diff --git a/packages/kit/src/runtime/client/remote-functions/shared.svelte.js b/packages/kit/src/runtime/client/remote-functions/shared.svelte.js index 52d60ce1ccc4..ccdde9c9007b 100644 --- a/packages/kit/src/runtime/client/remote-functions/shared.svelte.js +++ b/packages/kit/src/runtime/client/remote-functions/shared.svelte.js @@ -6,7 +6,7 @@ import { app, _goto, live_query_map, query_map, query_responses } from '../clien import { HttpError, Redirect, HandledHttpError } from '@sveltejs/kit/internal'; import { untrack } from 'svelte'; import { create_remote_key, split_remote_key } from '../../shared.js'; -import { navigating, page, notify_version } from '../state.svelte.js'; +import { navigating, page, notify_version } from '#app/state/client'; /** Indicates a query function, as opposed to a query instance */ export const QUERY_FUNCTION_ID = Symbol('sveltekit.query_function_id'); @@ -97,7 +97,7 @@ export function get_remote_request_headers() { // even in forks because it's state-based - therefore not using window.location. // Use untrack(...) to Avoid accidental reactive dependency on pathname/search return untrack(() => { - const url = navigating.current?.to?.url ?? page.url; + const url = navigating?.to?.url ?? page.url; return { 'x-sveltekit-pathname': url.pathname, diff --git a/packages/kit/src/runtime/client/remote-functions/shared.transport.spec.js b/packages/kit/src/runtime/client/remote-functions/shared.transport.spec.js index cf6a58a6f879..6922c5c2279d 100644 --- a/packages/kit/src/runtime/client/remote-functions/shared.transport.spec.js +++ b/packages/kit/src/runtime/client/remote-functions/shared.transport.spec.js @@ -11,11 +11,12 @@ vi.mock(new URL('../client.js', import.meta.url).pathname, () => ({ _goto: () => {} })); -// Mock `state.svelte.js` — imports `navigating` and `page` which are reactive +// Mock `#app/state/client` — imports `navigating` and `page` which are reactive // Svelte state only available in a full SvelteKit runtime. -vi.mock(new URL('../state.svelte.js', import.meta.url).pathname, () => ({ +vi.mock('#app/state/client', () => ({ navigating: { current: null }, page: { url: new URL('http://localhost/') }, + updated: { current: false, check: () => Promise.resolve(false) }, notify_version: () => {} })); diff --git a/packages/kit/src/runtime/client/state.svelte.js b/packages/kit/src/runtime/client/state.svelte.js deleted file mode 100644 index b6733fc89a8c..000000000000 --- a/packages/kit/src/runtime/client/state.svelte.js +++ /dev/null @@ -1,98 +0,0 @@ -/** @import { Navigation } from '$app/navigation' */ -/** @import { Page } from '$app/state' */ -import { version } from '$app/env'; -import { assets } from '#app/paths'; -import { BROWSER, DEV } from 'esm-env'; - -/** @type {Page} */ -export const page = new (class Page { - data = $state.raw({}); - form = $state.raw(null); - error = $state.raw(null); - params = $state.raw({}); - route = $state.raw({ id: null }); - shallow = $state.raw(null); - state = $state.raw({}); - status = $state.raw(-1); - url = $state.raw(new URL('a:')); -})(); - -export const navigating = new (class Navigating { - /** @type {Navigation | null} */ - current = $state.raw(null); -})(); - -export const updated = new (class Updated { - current = $state.raw(false); - // eslint-disable-next-line @typescript-eslint/require-await - check = async () => false; -})(); - -/** - * Internal: mark `updated.current` as `true` if the given version differs. - * Called from the server response header path. No-op unless version checks - * are enabled (assigned below). Not exported on the public `updated` object. - * @type {(new_version: string | null) => void} - */ -export let notify_version = () => {}; - -if (!DEV && BROWSER) { - const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__; - - /** @type {number | undefined} */ - let timeout; - - /** @type {Promise | undefined} */ - let checking; - - if (__SVELTEKIT_APP_VERSION_CHECKS_ENABLED__) { - /** - * Mark `updated.current` as `true` if the given version differs from the one - * the app was hydrated with. Called from the server response header path. - * Does NOT reset the poll timer — unlike `check()`, this is a passive observation - * from a single server instance's response, not an explicit version check. The - * poll timer continues on its original schedule as a backstop. This is important - * for platforms that implement skew protection, where `x-sveltekit-version` - * may be out of date — in this case we still need to poll for `version.json`. - * @param {string | null} new_version - */ - notify_version = (new_version) => { - if (new_version && new_version !== version) { - updated.current = true; - } - }; - } - - /** @type {() => Promise} */ - updated.check = function check() { - window.clearTimeout(timeout); - - if (updated.current) { - return Promise.resolve(true); - } - - return (checking ??= (async () => { - try { - const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, { - headers: { - 'cache-control': 'no-cache' - } - }); - - if (!res.ok) { - return false; - } - - const data = await res.json(); - return (updated.current ||= data.version !== version); - } catch { - return false; - } finally { - checking = undefined; - if (interval && !updated.current) timeout = window.setTimeout(check, interval); - } - })()); - }; - - if (interval) timeout = window.setTimeout(updated.check, interval); -} diff --git a/packages/kit/vitest.kit.config.js b/packages/kit/vitest.kit.config.js index 50319602c7a8..a1b21a4cf8aa 100644 --- a/packages/kit/vitest.kit.config.js +++ b/packages/kit/vitest.kit.config.js @@ -15,7 +15,9 @@ const exclude = [ export default /** @satisfies {import('vitest/config').ViteUserConfig} */ ({ plugins: [svelte({ compilerOptions: { hmr: false, experimental: { async: true } } })], define: { - __SVELTEKIT_SERVER_TRACING_ENABLED__: false + __SVELTEKIT_SERVER_TRACING_ENABLED__: false, + __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: 0, + __SVELTEKIT_APP_VERSION_CHECKS_ENABLED__: false }, server: { watch: { @@ -41,7 +43,7 @@ export default /** @satisfies {import('vitest/config').ViteUserConfig} */ ({ name: 'kit-server-dev', environment: 'node', include: ['src/**/*.spec.js'], - exclude: [...exclude, 'src/**/*.svelte.spec.js'] + exclude: [...exclude, 'src/**/*.svelte.spec.js', 'src/runtime/client/**/*.spec.js'] } }, { @@ -58,10 +60,13 @@ export default /** @satisfies {import('vitest/config').ViteUserConfig} */ ({ }, { extends: true, + resolve: { + conditions: ['browser'] + }, test: { name: 'kit-client-runtime', environment: 'jsdom', - include: ['src/**/*.svelte.spec.js'], + include: ['src/**/*.svelte.spec.js', 'src/runtime/client/**/*.spec.js'], exclude, // `forks` (child_process) accepts `--expose-gc`; `threads` (worker_threads) does not. pool: 'forks',