@sveltejs/kit 2.57.1 → 2.59.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 (35) hide show
  1. package/package.json +2 -2
  2. package/src/exports/internal/remote-functions.js +1 -1
  3. package/src/exports/public.d.ts +107 -16
  4. package/src/runtime/app/paths/client.js +7 -0
  5. package/src/runtime/app/paths/server.js +7 -0
  6. package/src/runtime/app/server/remote/command.js +7 -6
  7. package/src/runtime/app/server/remote/form.js +2 -1
  8. package/src/runtime/app/server/remote/prerender.js +3 -4
  9. package/src/runtime/app/server/remote/query.js +327 -113
  10. package/src/runtime/app/server/remote/requested.js +127 -32
  11. package/src/runtime/app/server/remote/shared.js +89 -20
  12. package/src/runtime/client/client.js +92 -62
  13. package/src/runtime/client/ndjson.js +42 -0
  14. package/src/runtime/client/remote-functions/command.svelte.js +8 -3
  15. package/src/runtime/client/remote-functions/form.svelte.js +24 -6
  16. package/src/runtime/client/remote-functions/index.js +3 -1
  17. package/src/runtime/client/remote-functions/query-batch.svelte.js +105 -0
  18. package/src/runtime/client/remote-functions/query-live.svelte.js +636 -0
  19. package/src/runtime/client/remote-functions/query.svelte.js +48 -148
  20. package/src/runtime/client/remote-functions/shared.svelte.js +76 -23
  21. package/src/runtime/form-utils.js +33 -12
  22. package/src/runtime/server/page/index.js +26 -16
  23. package/src/runtime/server/page/load_data.js +4 -2
  24. package/src/runtime/server/page/render.js +21 -18
  25. package/src/runtime/server/remote.js +117 -9
  26. package/src/runtime/server/respond.js +11 -8
  27. package/src/runtime/server/utils.js +10 -0
  28. package/src/runtime/shared.js +3 -3
  29. package/src/runtime/utils.js +0 -1
  30. package/src/types/internal.d.ts +54 -14
  31. package/src/utils/page_nodes.js +1 -0
  32. package/src/utils/url.js +3 -3
  33. package/src/version.js +1 -1
  34. package/types/index.d.ts +182 -30
  35. package/types/index.d.ts.map +8 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.57.1",
3
+ "version": "2.59.0",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -42,7 +42,7 @@
42
42
  "rollup": "^4.59.0",
43
43
  "svelte": "^5.53.12",
44
44
  "typescript": "^5.3.3",
45
- "vite": "^6.3.5",
45
+ "vite": "^6.4.2",
46
46
  "vitest": "^4.0.0"
47
47
  },
48
48
  "peerDependencies": {
@@ -1,7 +1,7 @@
1
1
  /** @import { RemoteInternals } from 'types' */
2
2
 
3
3
  /** @type {RemoteInternals['type'][]} */
4
- const types = ['command', 'form', 'prerender', 'query', 'query_batch'];
4
+ const types = ['command', 'form', 'prerender', 'query', 'query_batch', 'query_live'];
5
5
 
6
6
  /**
7
7
  * @param {Record<string, any>} module
@@ -52,7 +52,7 @@ export interface Adapter {
52
52
  supports?: {
53
53
  /**
54
54
  * Test support for `read` from `$app/server`.
55
- * @param details.config The merged route config
55
+ * @param details.config The merged adapter-specific route config exported from the route with `export const config`
56
56
  */
57
57
  read?: (details: { config: any; route: { id: string } }) => boolean;
58
58
 
@@ -1452,22 +1452,67 @@ export interface Page<
1452
1452
  */
1453
1453
  export type ParamMatcher = (param: string) => boolean;
1454
1454
 
1455
- export type RequestedResult<T> = Iterable<T> &
1456
- AsyncIterable<T> & {
1455
+ /**
1456
+ * A single entry yielded by [`requested`](https://svelte.dev/docs/kit/$app-server#requested)
1457
+ * when called with a regular `query`. `arg` is the validated argument (the input *after*
1458
+ * the query's schema validated and transformed it, if applicable); `query` is a
1459
+ * `RemoteQuery` bound to the client's original cache key, so `refresh()` / `set()` will
1460
+ * update the correct client entry.
1461
+ */
1462
+ export type RequestedEntry<Validated, Output> = {
1463
+ arg: Validated;
1464
+ query: RemoteQuery<Output>;
1465
+ };
1466
+
1467
+ /**
1468
+ * A single entry yielded by [`requested`](https://svelte.dev/docs/kit/$app-server#requested)
1469
+ * when called with a `query.live`. `arg` is the validated argument; `query` is a
1470
+ * `RemoteLiveQuery` bound to the client's original cache key, so `reconnect()` targets
1471
+ * the correct client subscription.
1472
+ */
1473
+ export type LiveRequestedEntry<Validated, Output> = {
1474
+ arg: Validated;
1475
+ query: RemoteLiveQuery<Output>;
1476
+ };
1477
+
1478
+ export type QueryRequestedResult<Validated, Output> = Iterable<RequestedEntry<Validated, Output>> &
1479
+ AsyncIterable<RequestedEntry<Validated, Output>> & {
1457
1480
  /**
1458
1481
  * Call `refresh` on all queries selected by this `requested` invocation.
1459
1482
  * This is identical to:
1460
1483
  * ```ts
1461
1484
  * import { requested } from '$app/server';
1462
1485
  *
1463
- * for await (const arg of requested(query, ...) {
1464
- * void query(arg).refresh();
1486
+ * for await (const { query } of requested(getPost, ...)) {
1487
+ * void query.refresh();
1465
1488
  * }
1466
1489
  * ```
1467
1490
  */
1468
1491
  refreshAll: () => Promise<void>;
1469
1492
  };
1470
1493
 
1494
+ export type LiveQueryRequestedResult<Validated, Output> = Iterable<
1495
+ LiveRequestedEntry<Validated, Output>
1496
+ > &
1497
+ AsyncIterable<LiveRequestedEntry<Validated, Output>> & {
1498
+ /**
1499
+ * Call `reconnect` on all live queries selected by this `requested` invocation.
1500
+ * This is identical to:
1501
+ * ```ts
1502
+ * import { requested } from '$app/server';
1503
+ *
1504
+ * for await (const { query } of requested(liveQuery, ...)) {
1505
+ * void query.reconnect();
1506
+ * }
1507
+ * ```
1508
+ */
1509
+ reconnectAll: () => Promise<void>;
1510
+ };
1511
+
1512
+ export type RequestedResult<Validated, Output> =
1513
+ | QueryRequestedResult<Validated, Output>
1514
+ | LiveQueryRequestedResult<Validated, Output>;
1515
+
1471
1516
  export interface RequestEvent<
1472
1517
  Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
1473
1518
  RouteId extends AppRouteId | null = AppRouteId | null
@@ -1884,6 +1929,7 @@ type InputElementProps<T extends keyof InputTypeMap> = T extends 'checkbox' | 'r
1884
1929
  'aria-invalid': boolean | 'false' | 'true' | undefined;
1885
1930
  get checked(): boolean;
1886
1931
  set checked(value: boolean);
1932
+ readonly defaultChecked?: boolean;
1887
1933
  }
1888
1934
  : T extends 'file'
1889
1935
  ? {
@@ -1914,6 +1960,7 @@ type InputElementProps<T extends keyof InputTypeMap> = T extends 'checkbox' | 'r
1914
1960
  'aria-invalid': boolean | 'false' | 'true' | undefined;
1915
1961
  get value(): string | number;
1916
1962
  set value(v: string | number);
1963
+ readonly defaultValue?: string | number;
1917
1964
  }
1918
1965
  : {
1919
1966
  name: string;
@@ -1921,6 +1968,7 @@ type InputElementProps<T extends keyof InputTypeMap> = T extends 'checkbox' | 'r
1921
1968
  'aria-invalid': boolean | 'false' | 'true' | undefined;
1922
1969
  get value(): string | number;
1923
1970
  set value(v: string | number);
1971
+ readonly defaultValue?: string | number;
1924
1972
  };
1925
1973
 
1926
1974
  type RemoteFormFieldMethods<T> = {
@@ -1946,7 +1994,9 @@ export type RemoteFormFieldValue = string | string[] | number | boolean | File |
1946
1994
  type AsArgs<Type extends keyof InputTypeMap, Value> = Type extends 'checkbox'
1947
1995
  ? Value extends string[]
1948
1996
  ? [type: Type, value: Value[number] | (string & {})]
1949
- : [type: Type] | [type: Type, value: Value | (string & {})]
1997
+ : Value extends boolean
1998
+ ? [type: Type] | [type: Type, value: boolean]
1999
+ : [type: Type] | [type: Type, value: Value | (string & {})]
1950
2000
  : Type extends 'radio' | 'submit' | 'hidden'
1951
2001
  ? [type: Type, value: Value | (string & {})]
1952
2002
  : Type extends 'file' | 'file multiple'
@@ -2013,11 +2063,15 @@ export type RemoteFormFields<T> =
2013
2063
  ? RecursiveFormFields
2014
2064
  : NonNullable<T> extends string | number | boolean | File
2015
2065
  ? RemoteFormField<NonNullable<T>>
2016
- : // [T] is used to prevent distributing over union, only the last condition should distribute over unions
2017
- [T] extends [string[] | File[]]
2018
- ? RemoteFormField<T> & { [K in number]: RemoteFormField<T[number]> }
2019
- : [T] extends [Array<infer U>]
2020
- ? RemoteFormFieldContainer<T> & {
2066
+ : // [NonNullable<T>] is used to prevent distributing over union while still allowing
2067
+ // nullable wrappers (e.g. `string[] | undefined` from a schema with `.default([])`)
2068
+ // to be treated as arrays; only the last condition should distribute over unions
2069
+ [NonNullable<T>] extends [string[] | File[]]
2070
+ ? RemoteFormField<NonNullable<T>> & {
2071
+ [K in number]: RemoteFormField<NonNullable<T>[number]>;
2072
+ }
2073
+ : [NonNullable<T>] extends [Array<infer U>]
2074
+ ? RemoteFormFieldContainer<NonNullable<T>> & {
2021
2075
  [K in number]: RemoteFormFields<U>;
2022
2076
  }
2023
2077
  : RemoteFormFieldContainer<T> & {
@@ -2079,7 +2133,7 @@ export interface ValidationError {
2079
2133
  }
2080
2134
 
2081
2135
  /**
2082
- * The return value of a remote `form` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
2136
+ * The type of a remote `form` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#form) for full documentation.
2083
2137
  */
2084
2138
  export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
2085
2139
  /** Attachment that sets up an event handler that intercepts the form submission on the client to prevent a full page reload */
@@ -2095,7 +2149,7 @@ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
2095
2149
  submit: () => Promise<boolean> & {
2096
2150
  updates: (...updates: RemoteQueryUpdate[]) => Promise<boolean>;
2097
2151
  };
2098
- }) => void
2152
+ }) => MaybePromise<void>
2099
2153
  ): {
2100
2154
  method: 'POST';
2101
2155
  action: string;
@@ -2134,7 +2188,7 @@ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
2134
2188
  };
2135
2189
 
2136
2190
  /**
2137
- * The return value of a remote `command` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
2191
+ * The type of a remote `command` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
2138
2192
  */
2139
2193
  export type RemoteCommand<Input, Output> = {
2140
2194
  (arg: undefined extends Input ? Input | void : Input): Promise<Output> & {
@@ -2146,7 +2200,9 @@ export type RemoteCommand<Input, Output> = {
2146
2200
 
2147
2201
  export type RemoteQueryUpdate =
2148
2202
  | RemoteQuery<any>
2203
+ | RemoteLiveQuery<any>
2149
2204
  | RemoteQueryFunction<any, any>
2205
+ | RemoteLiveQueryFunction<any, any>
2150
2206
  | RemoteQueryOverride;
2151
2207
 
2152
2208
  export type RemoteResource<T> = Promise<T> & {
@@ -2210,10 +2266,25 @@ export type RemoteQuery<T> = RemoteResource<T> & {
2210
2266
  withOverride(update: (current: T) => T): RemoteQueryOverride;
2211
2267
  };
2212
2268
 
2269
+ export type RemoteLiveQuery<T> = RemoteResource<T> & {
2270
+ /**
2271
+ * Returns an async iterator with live updates.
2272
+ * Unlike awaiting the resource directly, this can only be used _outside_ render
2273
+ * (i.e. in load functions, event handlers and so on)
2274
+ */
2275
+ run(): AsyncGenerator<T>;
2276
+ /** `true` if the live stream is currently connected. */
2277
+ readonly connected: boolean;
2278
+ /** `true` once the current live stream iterator is done. */
2279
+ readonly done: boolean;
2280
+ /** Reconnects the live stream immediately. */
2281
+ reconnect(): Promise<void>;
2282
+ };
2283
+
2213
2284
  export type RemoteQueryOverride = () => void;
2214
2285
 
2215
2286
  /**
2216
- * The return value of a remote `prerender` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#prerender) for full documentation.
2287
+ * The type of a remote `prerender` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#prerender) for full documentation.
2217
2288
  */
2218
2289
  export type RemotePrerenderFunction<Input, Output> = (
2219
2290
  arg: undefined extends Input ? Input | void : Input
@@ -2221,9 +2292,29 @@ export type RemotePrerenderFunction<Input, Output> = (
2221
2292
 
2222
2293
  /**
2223
2294
  * The return value of a remote `query` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query) for full documentation.
2295
+ *
2296
+ * The optional `Validated` generic parameter represents the argument type *after* the
2297
+ * query's schema has validated and (optionally) transformed it — this is the type the
2298
+ * query's implementation function receives on the server, and the type yielded by
2299
+ * [`requested`](https://svelte.dev/docs/kit/$app-server#requested). For queries declared
2300
+ * with [Standard Schema](https://standardschema.dev/) it differs from `Input` when the
2301
+ * schema contains a transform (e.g. `v.pipe(v.number(), v.transform(String))` has
2302
+ * `Input = number` but `Validated = string`). For `'unchecked'` validators and queries
2303
+ * without arguments it defaults to `Input`.
2224
2304
  */
2225
- export type RemoteQueryFunction<Input, Output> = (
2305
+ export type RemoteQueryFunction<Input, Output, _Validated = Input> = (
2226
2306
  arg: undefined extends Input ? Input | void : Input
2227
2307
  ) => RemoteQuery<Output>;
2228
2308
 
2309
+ /**
2310
+ * The type of a remote `query.live` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.live) for full documentation.
2311
+ *
2312
+ * The optional `Validated` generic parameter represents the argument type *after* the
2313
+ * query's schema has validated and (optionally) transformed it, and matches the type
2314
+ * yielded by [`requested`](https://svelte.dev/docs/kit/$app-server#requested).
2315
+ */
2316
+ export type RemoteLiveQueryFunction<Input, Output, _Validated = Input> = (
2317
+ arg: undefined extends Input ? Input | void : Input
2318
+ ) => RemoteLiveQuery<Output>;
2319
+
2229
2320
  export * from './index.js';
@@ -52,6 +52,13 @@ const pathname_prefix = hash_routing ? '#' : '';
52
52
  * @returns {ResolvedPathname}
53
53
  */
54
54
  export function resolve(...args) {
55
+ if (!args[0].startsWith('/')) {
56
+ throw new Error(
57
+ `Cannot use \`resolve(...)\` with a non-absolute pathname or route ID (got "${args[0]}"). ` +
58
+ '`resolve` is only for internal pathnames and route IDs; external URLs should be used directly.'
59
+ );
60
+ }
61
+
55
62
  // The type error is correct here, and if someone doesn't pass params when they should there's a runtime error,
56
63
  // but we don't want to adjust the internal resolve_route function to accept `undefined`, hence the type cast.
57
64
  return (
@@ -13,6 +13,13 @@ export function asset(file) {
13
13
 
14
14
  /** @type {import('./client.js').resolve} */
15
15
  export function resolve(id, params) {
16
+ if (!id.startsWith('/')) {
17
+ throw new Error(
18
+ `Cannot use \`resolve(...)\` with a non-absolute pathname or route ID (got "${id}"). ` +
19
+ '`resolve` is only for internal pathnames and route IDs; external URLs should be used directly.'
20
+ );
21
+ }
22
+
16
23
  const resolved = resolve_route(id, /** @type {Record<string, string>} */ (params));
17
24
 
18
25
  if (relative) {
@@ -12,7 +12,7 @@ import { MUTATIVE_METHODS } from '../../../../constants.js';
12
12
  *
13
13
  * @template Output
14
14
  * @overload
15
- * @param {() => Output} fn
15
+ * @param {() => MaybePromise<Output>} fn
16
16
  * @returns {RemoteCommand<void, Output>}
17
17
  * @since 2.27
18
18
  */
@@ -25,7 +25,7 @@ import { MUTATIVE_METHODS } from '../../../../constants.js';
25
25
  * @template Output
26
26
  * @overload
27
27
  * @param {'unchecked'} validate
28
- * @param {(arg: Input) => Output} fn
28
+ * @param {(arg: Input) => MaybePromise<Output>} fn
29
29
  * @returns {RemoteCommand<Input, Output>}
30
30
  * @since 2.27
31
31
  */
@@ -38,7 +38,7 @@ import { MUTATIVE_METHODS } from '../../../../constants.js';
38
38
  * @template Output
39
39
  * @overload
40
40
  * @param {Schema} validate
41
- * @param {(arg: StandardSchemaV1.InferOutput<Schema>) => Output} fn
41
+ * @param {(arg: StandardSchemaV1.InferOutput<Schema>) => MaybePromise<Output>} fn
42
42
  * @returns {RemoteCommand<StandardSchemaV1.InferInput<Schema>, Output>}
43
43
  * @since 2.27
44
44
  */
@@ -46,13 +46,13 @@ import { MUTATIVE_METHODS } from '../../../../constants.js';
46
46
  * @template Input
47
47
  * @template Output
48
48
  * @param {any} validate_or_fn
49
- * @param {(arg?: Input) => Output} [maybe_fn]
49
+ * @param {(arg?: Input) => MaybePromise<Output>} [maybe_fn]
50
50
  * @returns {RemoteCommand<Input, Output>}
51
51
  * @since 2.27
52
52
  */
53
53
  /*@__NO_SIDE_EFFECTS__*/
54
54
  export function command(validate_or_fn, maybe_fn) {
55
- /** @type {(arg?: Input) => Output} */
55
+ /** @type {(arg?: Input) => MaybePromise<Output>} */
56
56
  const fn = maybe_fn ?? validate_or_fn;
57
57
 
58
58
  /** @type {(arg?: any) => MaybePromise<Input>} */
@@ -77,7 +77,8 @@ export function command(validate_or_fn, maybe_fn) {
77
77
  );
78
78
  }
79
79
 
80
- state.remote.refreshes ??= {};
80
+ state.remote.refreshes ??= new Map();
81
+ state.remote.reconnects ??= new Map();
81
82
 
82
83
  const promise = Promise.resolve(
83
84
  run_remote_function(event, state, true, () => validate(arg), fn)
@@ -136,7 +136,8 @@ export function form(validate_or_fn, maybe_fn) {
136
136
  data = validated.value;
137
137
  }
138
138
 
139
- state.remote.refreshes ??= {};
139
+ state.remote.refreshes ??= new Map();
140
+ state.remote.reconnects ??= new Map();
140
141
 
141
142
  const issue = create_issues();
142
143
 
@@ -98,13 +98,12 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
98
98
 
99
99
  if (!state.prerendering && !DEV && !event.isRemoteRequest) {
100
100
  try {
101
- return await get_response(__, arg, state, async () => {
102
- const key = stringify_remote_arg(arg, state.transport);
101
+ return await get_response(__, payload, state, async () => {
103
102
  const cache = get_cache(__, state);
104
103
 
105
104
  // TODO adapters can provide prerendered data more efficiently than
106
105
  // fetching from the public internet
107
- const promise = (cache[key] ??= {
106
+ const promise = (cache[payload] ??= {
108
107
  serialize: true,
109
108
  data: fetch(new URL(url, event.url.origin).href).then(async (response) => {
110
109
  if (!response.ok) {
@@ -132,7 +131,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
132
131
  return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
133
132
  }
134
133
 
135
- const promise = get_response(__, arg, state, () =>
134
+ const promise = get_response(__, payload, state, () =>
136
135
  run_remote_function(event, state, false, () => validate(arg), fn)
137
136
  );
138
137