Skip to content

Commit b62f13d

Browse files
Sync kit docs (#1955)
sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com>
1 parent ec9c086 commit b62f13d

6 files changed

Lines changed: 248 additions & 38 deletions

File tree

apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,47 @@ export const getWeather = query.batch(v.string(), async (cityIds) => {
275275
{/if}
276276
```
277277
278+
## query.live
279+
280+
`query.live` is for accessing real-time data from the server. It behaves similarly to `query`, but the callback — typically an async [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function*) — returns an `AsyncIterable`:
281+
282+
```js
283+
import { query } from '$app/server';
284+
285+
export const getTime = query.live(async function* () {
286+
while (true) {
287+
yield new Date();
288+
await new Promise((f) => setTimeout(f, 1000));
289+
}
290+
});
291+
```
292+
293+
During server-side rendering, `await getTime()` returns the first yielded value then closes the iterator. This initial value is serialized and reused during hydration.
294+
295+
On the client, the query stays connected while it's actively used in a component. Multiple instances share a connection. When there are no active uses left, the stream disconnects and server-side iteration is stopped.
296+
297+
Live queries expose a `connected` property and `reconnect()` method:
298+
299+
```svelte
300+
<script>
301+
import { getTime } from './time.remote.js';
302+
303+
const time = getTime();
304+
</script>
305+
306+
<p>{await time}</p>
307+
<p>connected: {time.connected}</p>
308+
<button onclick={() => time.reconnect()}>Reconnect</button>
309+
```
310+
311+
If the connection drops, `connected` becomes `false`. SvelteKit will attempt to reconnect passively, with exponential backoff, and actively if `navigator.onLine` goes from `false` to `true`.
312+
313+
Unlike `query`, live queries do not have a `refresh()` method, as they are self-updating.
314+
315+
As with `query` and `query.batch`, call `.run()` outside render when you need imperative access. For live queries, `run()` returns a `Promise<AsyncGenerator<T>>`.
316+
317+
> [!NOTE] It's essential that you don't cache live query responses in a service worker, since the cloned response will continue streaming long after the page is closed. Make sure that your caching logic excludes any responses with a `Cache-Control` header that includes `no-store`.
318+
278319
## form
279320
280321
The `form` function makes it easy to write data to the server. It takes a callback that receives `data` constructed from the submitted [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)...
@@ -982,6 +1023,29 @@ export const updatePost = form(
9821023
9831024
Because queries are keyed based on their arguments, `await getPost(post.id).set(result)` on the server knows to look up the matching `getPost(id)` on the client to update it. The same goes for `getPosts().refresh()` -- it knows to look up `getPosts()` with no argument on the client.
9841025
1026+
### Reconnecting live queries in mutations
1027+
1028+
Single-flight mutations can also reconnect `query.live` instances. In a `form`/`command` handler, call `.reconnect()` on the live query resource you want to reconnect:
1029+
1030+
```js
1031+
import * as v from 'valibot';
1032+
import { form, query } from '$app/server';
1033+
1034+
export const getNotifications = query.live(v.string(), async function* (userId) {
1035+
while (true) {
1036+
yield await db.notifications(userId);
1037+
await wait(1000);
1038+
}
1039+
});
1040+
1041+
export const markAllRead = form(v.object({ userId: v.string() }), async ({ userId }) => {
1042+
// mutation logic...
1043+
+++getNotifications(userId).reconnect();+++
1044+
});
1045+
```
1046+
1047+
This schedules a reconnect for the matching active client instances and applies it as part of the mutation response (i.e. in the same flight as the form/command result). You might need this if, for example, the command modifies a cookie that the live query needs to restart in order to capture.
1048+
9851049
### Client-requested refreshes
9861050
9871051
Unfortunately, life isn't always as simple as the preceding example. The server always knows which query _functions_ to update, but it may not know which specific query _instances_ to update. For example, if `getPosts({ filter: 'author:santa' })` is rendered on the client, calling `getPosts().refresh()` in the server handler won't update it. You'd need to call `getPosts({ filter: 'author:santa' }).refresh()` instead — but how could you know which specific combinations of filters are currently rendered on the client, especially if your query argument is more complicated than an object with just one key?
@@ -1199,7 +1263,7 @@ export const getStuff = query('unchecked', async ({ id }: { id: string }) => {
11991263
Inside `query`, `form` and `command` you can use [`getRequestEvent`]($app-server#getRequestEvent) to get the current [`RequestEvent`](@sveltejs-kit#RequestEvent) object. This makes it easy to build abstractions for interacting with cookies, for example:
12001264
12011265
```ts
1202-
/// file: user.remote.ts
1266+
/// file: user.remote.js
12031267
// @filename: ambient.d.ts
12041268
interface User {
12051269
name: string;

apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ The directory to write static assets (the contents of `static`, plus client-side
8585

8686
To create a [single page app (SPA)](single-page-apps) you must specify the name of the fallback page to be generated by SvelteKit, which is used as the entry point for URLs that have not been prerendered. This is commonly `200.html`, but can vary depending on your deployment platform. You should avoid `index.html` where possible to avoid conflicting with a prerendered homepage.
8787

88-
> This option has large negative performance and SEO impacts. It is only recommended in certain circumstances such as wrapping the site in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives.
88+
> [!NOTE] This option has large negative performance and SEO impacts. It is only recommended in certain circumstances such as wrapping the site in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives.
8989
9090
### precompress
9191

apps/svelte.dev/content/docs/kit/30-advanced/40-service-workers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ self.addEventListener('fetch', (event) => {
9999
throw new Error('invalid response from fetch');
100100
}
101101

102-
if (response.status === 200) {
102+
if (response.status === 200 && !response.headers.get('cache-control')?.includes('no-store')) {
103103
cache.put(event.request, response.clone());
104104
}
105105

apps/svelte.dev/content/docs/kit/60-appendix/10-faq.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,7 @@ export function GET({ params, url }) {
155155
`adapter-node` builds a middleware that you can use with your own server for production mode. In dev, you can add middleware to Vite by using a Vite plugin. For example:
156156

157157
```js
158-
// @errors: 2322
159-
// @filename: ambient.d.ts
160-
declare module '@sveltejs/kit/vite'; // TODO this feels unnecessary, why can't it 'see' the declarations?
161-
162-
// @filename: index.js
163-
// ---cut---
158+
/// file: vite.config.js
164159
import { sveltekit } from '@sveltejs/kit/vite';
165160

166161
/** @type {import('vite').Plugin} */

apps/svelte.dev/content/docs/kit/98-reference/[email protected]

Lines changed: 129 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ read?: (details: { config: any; route: { id: string } }) => boolean;
505505

506506
<div class="ts-block-property-bullets">
507507

508-
- `details.config` The merged route config
508+
- `details.config` The merged adapter-specific route config exported from the route with `export const config`
509509

510510
</div>
511511

@@ -1361,6 +1361,50 @@ type LessThan<
13611361

13621362
</div>
13631363

1364+
## LiveQueryRequestedResult
1365+
1366+
<div class="ts-block">
1367+
1368+
```dts
1369+
type LiveQueryRequestedResult<Validated, Output> = Iterable<
1370+
LiveRequestedEntry<Validated, Output>
1371+
> &
1372+
AsyncIterable<LiveRequestedEntry<Validated, Output>> & {
1373+
/**
1374+
* Call `reconnect` on all live queries selected by this `requested` invocation.
1375+
* This is identical to:
1376+
* ```ts
1377+
* import { requested } from '$app/server';
1378+
*
1379+
* for await (const { query } of requested(liveQuery, ...)) {
1380+
* void query.reconnect();
1381+
* }
1382+
* ```
1383+
*/
1384+
reconnectAll: () => Promise<void>;
1385+
};
1386+
```
1387+
1388+
</div>
1389+
1390+
## LiveRequestedEntry
1391+
1392+
A single entry yielded by [`requested`](/docs/kit/$app-server#requested)
1393+
when called with a `query.live`. `arg` is the validated argument; `query` is a
1394+
`RemoteLiveQuery` bound to the client's original cache key, so `reconnect()` targets
1395+
the correct client subscription.
1396+
1397+
<div class="ts-block">
1398+
1399+
```dts
1400+
type LiveRequestedEntry<Validated, Output> = {
1401+
arg: Validated;
1402+
query: RemoteLiveQuery<Output>;
1403+
};
1404+
```
1405+
1406+
</div>
1407+
13641408
## Load
13651409

13661410
The generic form of `PageLoad` and `LayoutLoad`. You should import those from `./$types` (see [generated types](/docs/kit/types#Generated-types))
@@ -2349,6 +2393,32 @@ type PrerenderOption = boolean | 'auto';
23492393

23502394
</div>
23512395

2396+
## QueryRequestedResult
2397+
2398+
<div class="ts-block">
2399+
2400+
```dts
2401+
type QueryRequestedResult<Validated, Output> = Iterable<
2402+
RequestedEntry<Validated, Output>
2403+
> &
2404+
AsyncIterable<RequestedEntry<Validated, Output>> & {
2405+
/**
2406+
* Call `refresh` on all queries selected by this `requested` invocation.
2407+
* This is identical to:
2408+
* ```ts
2409+
* import { requested } from '$app/server';
2410+
*
2411+
* for await (const { query } of requested(getPost, ...)) {
2412+
* void query.refresh();
2413+
* }
2414+
* ```
2415+
*/
2416+
refreshAll: () => Promise<void>;
2417+
};
2418+
```
2419+
2420+
</div>
2421+
23522422
## Redirect
23532423

23542424
The object returned by the [`redirect`](/docs/kit/@sveltejs-kit#redirect) function.
@@ -2387,7 +2457,7 @@ The location to redirect to.
23872457

23882458
## RemoteCommand
23892459

2390-
The return value of a remote `command` function. See [Remote functions](/docs/kit/remote-functions#command) for full documentation.
2460+
The type of a remote `command` function. See [Remote functions](/docs/kit/remote-functions#command) for full documentation.
23912461

23922462
<div class="ts-block">
23932463

@@ -2409,7 +2479,7 @@ type RemoteCommand<Input, Output> = {
24092479

24102480
## RemoteForm
24112481

2412-
The return value of a remote `form` function. See [Remote functions](/docs/kit/remote-functions#form) for full documentation.
2482+
The type of a remote `form` function. See [Remote functions](/docs/kit/remote-functions#form) for full documentation.
24132483

24142484
<div class="ts-block">
24152485

@@ -2616,9 +2686,54 @@ path: Array<string | number>;
26162686
<div class="ts-block-property-details"></div>
26172687
</div></div>
26182688

2689+
## RemoteLiveQuery
2690+
2691+
<div class="ts-block">
2692+
2693+
```dts
2694+
type RemoteLiveQuery<T> = RemoteResource<T> & {
2695+
/**
2696+
* Returns an async iterator with live updates.
2697+
* Unlike awaiting the resource directly, this can only be used _outside_ render
2698+
* (i.e. in load functions, event handlers and so on)
2699+
*/
2700+
run(): AsyncGenerator<T>;
2701+
/** `true` if the live stream is currently connected. */
2702+
readonly connected: boolean;
2703+
/** `true` once the current live stream iterator is done. */
2704+
readonly done: boolean;
2705+
/** Reconnects the live stream immediately. */
2706+
reconnect(): Promise<void>;
2707+
};
2708+
```
2709+
2710+
</div>
2711+
2712+
## RemoteLiveQueryFunction
2713+
2714+
The type of a remote `query.live` function. See [Remote functions](/docs/kit/remote-functions#query.live) for full documentation.
2715+
2716+
The optional `Validated` generic parameter represents the argument type *after* the
2717+
query's schema has validated and (optionally) transformed it, and matches the type
2718+
yielded by [`requested`](/docs/kit/$app-server#requested).
2719+
2720+
<div class="ts-block">
2721+
2722+
```dts
2723+
type RemoteLiveQueryFunction<
2724+
Input,
2725+
Output,
2726+
_Validated = Input
2727+
> = (
2728+
arg: undefined extends Input ? Input | void : Input
2729+
) => RemoteLiveQuery<Output>;
2730+
```
2731+
2732+
</div>
2733+
26192734
## RemotePrerenderFunction
26202735

2621-
The return value of a remote `prerender` function. See [Remote functions](/docs/kit/remote-functions#prerender) for full documentation.
2736+
The type of a remote `prerender` function. See [Remote functions](/docs/kit/remote-functions#prerender) for full documentation.
26222737

26232738
<div class="ts-block">
26242739

@@ -2727,7 +2842,9 @@ type RemoteQueryOverride = () => void;
27272842
```dts
27282843
type RemoteQueryUpdate =
27292844
| RemoteQuery<any>
2845+
| RemoteLiveQuery<any>
27302846
| RemoteQueryFunction<any, any>
2847+
| RemoteLiveQueryFunction<any, any>
27312848
| RemoteQueryOverride;
27322849
```
27332850

@@ -3061,10 +3178,11 @@ type RequestHandler<
30613178

30623179
## RequestedEntry
30633180

3064-
A single entry yielded by [`requested`](/docs/kit/$app-server#requested).
3065-
`arg` is the validated argument (the input *after* the query's schema validated and
3066-
transformed it, if applicable); `query` is a `RemoteQuery` bound to the client's
3067-
original cache key, so `refresh()` / `set()` will update the correct client entry.
3181+
A single entry yielded by [`requested`](/docs/kit/$app-server#requested)
3182+
when called with a regular `query`. `arg` is the validated argument (the input *after*
3183+
the query's schema validated and transformed it, if applicable); `query` is a
3184+
`RemoteQuery` bound to the client's original cache key, so `refresh()` / `set()` will
3185+
update the correct client entry.
30683186

30693187
<div class="ts-block">
30703188

@@ -3082,23 +3200,9 @@ type RequestedEntry<Validated, Output> = {
30823200
<div class="ts-block">
30833201

30843202
```dts
3085-
type RequestedResult<Validated, Output> = Iterable<
3086-
RequestedEntry<Validated, Output>
3087-
> &
3088-
AsyncIterable<RequestedEntry<Validated, Output>> & {
3089-
/**
3090-
* Call `refresh` on all queries selected by this `requested` invocation.
3091-
* This is identical to:
3092-
* ```ts
3093-
* import { requested } from '$app/server';
3094-
*
3095-
* for await (const { query } of requested(getPost, ...)) {
3096-
* void query.refresh();
3097-
* }
3098-
* ```
3099-
*/
3100-
refreshAll: () => Promise<void>;
3101-
};
3203+
type RequestedResult<Validated, Output> =
3204+
| QueryRequestedResult<Validated, Output>
3205+
| LiveQueryRequestedResult<Validated, Output>;
31023206
```
31033207

31043208
</div>

0 commit comments

Comments
 (0)