@sveltejs/kit 2.27.2 → 2.28.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.27.2",
3
+ "version": "2.28.0",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/sveltejs/kit",
14
+ "url": "git+https://github.com/sveltejs/kit.git",
15
15
  "directory": "packages/kit"
16
16
  },
17
17
  "license": "MIT",
@@ -242,6 +242,11 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
242
242
  const filepath = saved.get(file);
243
243
  if (filepath) return readFileSync(filepath);
244
244
 
245
+ // Static assets emitted during build
246
+ if (file.startsWith(config.appDir)) {
247
+ return readFileSync(`${out}/server/${file}`);
248
+ }
249
+
245
250
  // stuff in `static`
246
251
  return readFileSync(join(config.files.assets, file));
247
252
  },
@@ -8,6 +8,7 @@ import {
8
8
  strip_data_suffix,
9
9
  strip_resolution_suffix
10
10
  } from '../runtime/pathname.js';
11
+ import { text_encoder } from '../runtime/utils.js';
11
12
 
12
13
  export { VERSION } from '../version.js';
13
14
 
@@ -142,7 +143,7 @@ export function json(data, init) {
142
143
  // means less duplicated work
143
144
  const headers = new Headers(init?.headers);
144
145
  if (!headers.has('content-length')) {
145
- headers.set('content-length', encoder.encode(body).byteLength.toString());
146
+ headers.set('content-length', text_encoder.encode(body).byteLength.toString());
146
147
  }
147
148
 
148
149
  if (!headers.has('content-type')) {
@@ -155,8 +156,6 @@ export function json(data, init) {
155
156
  });
156
157
  }
157
158
 
158
- const encoder = new TextEncoder();
159
-
160
159
  /**
161
160
  * Create a `Response` object from the supplied body.
162
161
  * @param {string} body The value that will be used as-is.
@@ -165,7 +164,7 @@ const encoder = new TextEncoder();
165
164
  export function text(body, init) {
166
165
  const headers = new Headers(init?.headers);
167
166
  if (!headers.has('content-length')) {
168
- const encoded = encoder.encode(body);
167
+ const encoded = text_encoder.encode(body);
169
168
  headers.set('content-length', encoded.byteLength.toString());
170
169
  return new Response(encoded, {
171
170
  ...init,
@@ -1020,12 +1020,15 @@ export interface NavigationEvent<
1020
1020
  /**
1021
1021
  * Information about the target of a specific navigation.
1022
1022
  */
1023
- export interface NavigationTarget {
1023
+ export interface NavigationTarget<
1024
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
1025
+ RouteId extends AppRouteId | null = AppRouteId | null
1026
+ > {
1024
1027
  /**
1025
1028
  * Parameters of the target page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1026
1029
  * Is `null` if the target is not part of the SvelteKit app (could not be resolved to a route).
1027
1030
  */
1028
- params: Record<string, string> | null;
1031
+ params: Params | null;
1029
1032
  /**
1030
1033
  * Info about the target route
1031
1034
  */
@@ -1033,7 +1036,7 @@ export interface NavigationTarget {
1033
1036
  /**
1034
1037
  * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. It is `null` when no route is matched.
1035
1038
  */
1036
- id: string | null;
1039
+ id: RouteId | null;
1037
1040
  };
1038
1041
  /**
1039
1042
  * The URL that is navigated to
@@ -1565,6 +1568,8 @@ export type RemoteForm<Result> = {
1565
1568
  for(key: string | number | boolean): Omit<RemoteForm<Result>, 'for'>;
1566
1569
  /** The result of the form submission */
1567
1570
  get result(): Result | undefined;
1571
+ /** The number of pending submissions */
1572
+ get pending(): number;
1568
1573
  /** Spread this onto a `<button>` or `<input type="submit">` */
1569
1574
  buttonProps: {
1570
1575
  type: 'submit';
@@ -1586,14 +1591,20 @@ export type RemoteForm<Result> = {
1586
1591
  formaction: string;
1587
1592
  onclick: (event: Event) => void;
1588
1593
  };
1594
+ /** The number of pending submissions */
1595
+ get pending(): number;
1589
1596
  };
1590
1597
  };
1591
1598
 
1592
1599
  /**
1593
1600
  * The return value of a remote `command` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
1594
1601
  */
1595
- export type RemoteCommand<Input, Output> = (arg: Input) => Promise<Awaited<Output>> & {
1596
- updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<Awaited<Output>>;
1602
+ export type RemoteCommand<Input, Output> = {
1603
+ (arg: Input): Promise<Awaited<Output>> & {
1604
+ updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<Awaited<Output>>;
1605
+ };
1606
+ /** The number of pending command executions */
1607
+ get pending(): number;
1597
1608
  };
1598
1609
 
1599
1610
  export type RemoteResource<T> = Promise<Awaited<T>> & {
@@ -1623,7 +1634,7 @@ export type RemoteQuery<T> = RemoteResource<T> & {
1623
1634
  */
1624
1635
  refresh(): Promise<void>;
1625
1636
  /**
1626
- * Temporarily override the value of a query. This is used with the `updates` method of a [command](https://svelte.dev/docs/kit/remote-functions#command-Single-flight-mutations) or [enhanced form submission](https://svelte.dev/docs/kit/remote-functions#form-enhance) to provide optimistic updates.
1637
+ * Temporarily override the value of a query. This is used with the `updates` method of a [command](https://svelte.dev/docs/kit/remote-functions#command-Updating-queries) or [enhanced form submission](https://svelte.dev/docs/kit/remote-functions#form-enhance) to provide optimistic updates.
1627
1638
  *
1628
1639
  * ```svelte
1629
1640
  * <script>
@@ -102,9 +102,10 @@ export async function treeshake_prerendered_remotes(out, manifest_data, metadata
102
102
  input[prefix + remote.hash] = remote_file;
103
103
  }
104
104
 
105
- const bundle = await vite.build({
105
+ const bundle = /** @type {import('vite').Rollup.RollupOutput} */ (await vite.build({
106
106
  configFile: false,
107
107
  build: {
108
+ write: false,
108
109
  ssr: true,
109
110
  rollupOptions: {
110
111
  external: (id) => {
@@ -114,11 +115,10 @@ export async function treeshake_prerendered_remotes(out, manifest_data, metadata
114
115
  input
115
116
  }
116
117
  }
117
- });
118
+ }));
118
119
 
119
- // @ts-expect-error TypeScript doesn't know what type `bundle` is
120
120
  for (const chunk of bundle.output) {
121
- if (chunk.name.startsWith(prefix)) {
121
+ if (chunk.type === 'chunk' && chunk.name.startsWith(prefix)) {
122
122
  fs.writeFileSync(`${dir}/${chunk.fileName.slice(prefix.length)}`, chunk.code);
123
123
  }
124
124
  }
@@ -1,7 +1,7 @@
1
1
  import { read_implementation, manifest } from '__sveltekit/server';
2
2
  import { base } from '__sveltekit/paths';
3
3
  import { DEV } from 'esm-env';
4
- import { b64_decode } from '../../utils.js';
4
+ import { base64_decode } from '../../utils.js';
5
5
 
6
6
  /**
7
7
  * Read the contents of an imported asset from the filesystem
@@ -33,8 +33,9 @@ export function read(asset) {
33
33
  const data = asset.slice(match[0].length);
34
34
 
35
35
  if (match[2] !== undefined) {
36
- const decoded = b64_decode(data);
36
+ const decoded = base64_decode(data);
37
37
 
38
+ // @ts-ignore passing a Uint8Array to `new Response(...)` is fine
38
39
  return new Response(decoded, {
39
40
  headers: {
40
41
  'Content-Length': decoded.byteLength.toString(),
@@ -87,5 +87,10 @@ export function command(validate_or_fn, maybe_fn) {
87
87
 
88
88
  Object.defineProperty(wrapper, '__', { value: __ });
89
89
 
90
+ // On the server, pending is always 0
91
+ Object.defineProperty(wrapper, 'pending', {
92
+ get: () => 0
93
+ });
94
+
90
95
  return wrapper;
91
96
  }
@@ -97,6 +97,16 @@ export function form(fn) {
97
97
  }
98
98
  });
99
99
 
100
+ // On the server, pending is always 0
101
+ Object.defineProperty(instance, 'pending', {
102
+ get: () => 0
103
+ });
104
+
105
+ // On the server, buttonProps.pending is always 0
106
+ Object.defineProperty(button_props, 'pending', {
107
+ get: () => 0
108
+ });
109
+
100
110
  if (key == undefined) {
101
111
  Object.defineProperty(instance, 'for', {
102
112
  /** @type {RemoteForm<any>['for']} */
@@ -44,6 +44,7 @@ import { get_message, get_status } from '../../utils/error.js';
44
44
  import { writable } from 'svelte/store';
45
45
  import { page, update, navigating } from './state.svelte.js';
46
46
  import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
47
+ import { text_decoder } from '../utils.js';
47
48
 
48
49
  export { load_css };
49
50
  const ICON_REL_ATTRIBUTES = new Set(['icon', 'shortcut icon', 'apple-touch-icon']);
@@ -2781,7 +2782,6 @@ async function load_data(url, invalid) {
2781
2782
  */
2782
2783
  const deferreds = new Map();
2783
2784
  const reader = /** @type {ReadableStream<Uint8Array>} */ (res.body).getReader();
2784
- const decoder = new TextDecoder();
2785
2785
 
2786
2786
  /**
2787
2787
  * @param {any} data
@@ -2804,7 +2804,7 @@ async function load_data(url, invalid) {
2804
2804
  const { done, value } = await reader.read();
2805
2805
  if (done && !text) break;
2806
2806
 
2807
- text += !value && text ? '\n' : decoder.decode(value, { stream: true }); // no value -> final chunk -> add a new line to trigger the last parse
2807
+ text += !value && text ? '\n' : text_decoder.decode(value, { stream: true }); // no value -> final chunk -> add a new line to trigger the last parse
2808
2808
 
2809
2809
  while (true) {
2810
2810
  const split = text.indexOf('\n');
@@ -1,6 +1,6 @@
1
1
  import { BROWSER, DEV } from 'esm-env';
2
2
  import { hash } from '../../utils/hash.js';
3
- import { b64_decode } from '../utils.js';
3
+ import { base64_decode } from '../utils.js';
4
4
 
5
5
  let loading = 0;
6
6
 
@@ -90,6 +90,7 @@ export function initial_fetch(resource, opts) {
90
90
 
91
91
  const script = document.querySelector(selector);
92
92
  if (script?.textContent) {
93
+ script.remove(); // In case multiple script tags match the same selector
93
94
  let { body, ...init } = JSON.parse(script.textContent);
94
95
 
95
96
  const ttl = script.getAttribute('data-ttl');
@@ -98,7 +99,7 @@ export function initial_fetch(resource, opts) {
98
99
  if (b64 !== null) {
99
100
  // Can't use native_fetch('data:...;base64,${body}')
100
101
  // csp can block the request
101
- body = b64_decode(body);
102
+ body = base64_decode(body);
102
103
  }
103
104
 
104
105
  return Promise.resolve(new Response(body, init));
@@ -0,0 +1,91 @@
1
+ /** @import { RemoteCommand, RemoteQueryOverride } from '@sveltejs/kit' */
2
+ /** @import { RemoteFunctionResponse } from 'types' */
3
+ /** @import { Query } from './query.svelte.js' */
4
+ import { app_dir, base } from '__sveltekit/paths';
5
+ import * as devalue from 'devalue';
6
+ import { HttpError } from '@sveltejs/kit/internal';
7
+ import { app } from '../client.js';
8
+ import { stringify_remote_arg } from '../../shared.js';
9
+ import { refresh_queries, release_overrides } from './shared.svelte.js';
10
+
11
+ /**
12
+ * Client-version of the `command` function from `$app/server`.
13
+ * @param {string} id
14
+ * @returns {RemoteCommand<any, any>}
15
+ */
16
+ export function command(id) {
17
+ /** @type {number} */
18
+ let pending_count = $state(0);
19
+
20
+ // Careful: This function MUST be synchronous (can't use the async keyword) because the return type has to be a promise with an updates() method.
21
+ // If we make it async, the return type will be a promise that resolves to a promise with an updates() method, which is not what we want.
22
+ /** @type {RemoteCommand<any, any>} */
23
+ const command_function = (arg) => {
24
+ /** @type {Array<Query<any> | RemoteQueryOverride>} */
25
+ let updates = [];
26
+
27
+ // Increment pending count when command starts
28
+ pending_count++;
29
+
30
+ /** @type {Promise<any> & { updates: (...args: any[]) => any }} */
31
+ const promise = (async () => {
32
+ try {
33
+ // Wait a tick to give room for the `updates` method to be called
34
+ await Promise.resolve();
35
+
36
+ const response = await fetch(`${base}/${app_dir}/remote/${id}`, {
37
+ method: 'POST',
38
+ body: JSON.stringify({
39
+ payload: stringify_remote_arg(arg, app.hooks.transport),
40
+ refreshes: updates.map((u) => u._key)
41
+ }),
42
+ headers: {
43
+ 'Content-Type': 'application/json'
44
+ }
45
+ });
46
+
47
+ if (!response.ok) {
48
+ release_overrides(updates);
49
+ // We only end up here in case of a network error or if the server has an internal error
50
+ // (which shouldn't happen because we handle errors on the server and always send a 200 response)
51
+ throw new Error('Failed to execute remote function');
52
+ }
53
+
54
+ const result = /** @type {RemoteFunctionResponse} */ (await response.json());
55
+ if (result.type === 'redirect') {
56
+ release_overrides(updates);
57
+ throw new Error(
58
+ 'Redirects are not allowed in commands. Return a result instead and use goto on the client'
59
+ );
60
+ } else if (result.type === 'error') {
61
+ release_overrides(updates);
62
+ throw new HttpError(result.status ?? 500, result.error);
63
+ } else {
64
+ if (result.refreshes) {
65
+ refresh_queries(result.refreshes, updates);
66
+ }
67
+
68
+ return devalue.parse(result.result, app.decoders);
69
+ }
70
+ } finally {
71
+ // Decrement pending count when command completes
72
+ pending_count--;
73
+ }
74
+ })();
75
+
76
+ promise.updates = (/** @type {any} */ ...args) => {
77
+ updates = args;
78
+ // @ts-expect-error Don't allow updates to be called multiple times
79
+ delete promise.updates;
80
+ return promise;
81
+ };
82
+
83
+ return promise;
84
+ };
85
+
86
+ Object.defineProperty(command_function, 'pending', {
87
+ get: () => pending_count
88
+ });
89
+
90
+ return command_function;
91
+ }
@@ -5,7 +5,14 @@ import { app_dir, base } from '__sveltekit/paths';
5
5
  import * as devalue from 'devalue';
6
6
  import { DEV } from 'esm-env';
7
7
  import { HttpError } from '@sveltejs/kit/internal';
8
- import { app, remote_responses, started, goto, set_nearest_error_page } from '../client.js';
8
+ import {
9
+ app,
10
+ remote_responses,
11
+ started,
12
+ goto,
13
+ set_nearest_error_page,
14
+ invalidateAll
15
+ } from '../client.js';
9
16
  import { tick } from 'svelte';
10
17
  import { refresh_queries, release_overrides } from './shared.svelte.js';
11
18
 
@@ -27,6 +34,9 @@ export function form(id) {
27
34
  /** @type {any} */
28
35
  let result = $state(started ? undefined : remote_responses[action_id]);
29
36
 
37
+ /** @type {number} */
38
+ let pending_count = $state(0);
39
+
30
40
  /**
31
41
  * @param {FormData} data
32
42
  * @returns {Promise<any> & { updates: (...args: any[]) => any }}
@@ -42,6 +52,9 @@ export function form(id) {
42
52
  entry.count++;
43
53
  }
44
54
 
55
+ // Increment pending count when submission starts
56
+ pending_count++;
57
+
45
58
  /** @type {Array<Query<any> | RemoteQueryOverride>} */
46
59
  let updates = [];
47
60
 
@@ -78,7 +91,11 @@ export function form(id) {
78
91
  if (form_result.type === 'result') {
79
92
  result = devalue.parse(form_result.result, app.decoders);
80
93
 
81
- refresh_queries(form_result.refreshes, updates);
94
+ if (form_result.refreshes) {
95
+ refresh_queries(form_result.refreshes, updates);
96
+ } else {
97
+ void invalidateAll();
98
+ }
82
99
  } else if (form_result.type === 'redirect') {
83
100
  const refreshes = form_result.refreshes ?? '';
84
101
  const invalidateAll = !refreshes && updates.length === 0;
@@ -94,6 +111,9 @@ export function form(id) {
94
111
  release_overrides(updates);
95
112
  throw e;
96
113
  } finally {
114
+ // Decrement pending count when submission completes
115
+ pending_count--;
116
+
97
117
  void tick().then(() => {
98
118
  if (entry) {
99
119
  entry.count--;
@@ -242,6 +262,10 @@ export function form(id) {
242
262
  }
243
263
  });
244
264
 
265
+ Object.defineProperty(button_props, 'pending', {
266
+ get: () => pending_count
267
+ });
268
+
245
269
  Object.defineProperties(instance, {
246
270
  buttonProps: {
247
271
  value: button_props
@@ -249,6 +273,9 @@ export function form(id) {
249
273
  result: {
250
274
  get: () => result
251
275
  },
276
+ pending: {
277
+ get: () => pending_count
278
+ },
252
279
  enhance: {
253
280
  /** @type {RemoteForm<any>['enhance']} */
254
281
  value: (callback) => {
@@ -1,4 +1,4 @@
1
- export { command } from './command.js';
1
+ export { command } from './command.svelte.js';
2
2
  export { form } from './form.svelte.js';
3
3
  export { prerender } from './prerender.svelte.js';
4
4
  export { query } from './query.svelte.js';
@@ -2,7 +2,7 @@
2
2
  /** @import { RemoteFunctionResponse } from 'types' */
3
3
  /** @import { Query } from './query.svelte.js' */
4
4
  import * as devalue from 'devalue';
5
- import { app, goto, invalidateAll, query_map } from '../client.js';
5
+ import { app, goto, query_map } from '../client.js';
6
6
  import { HttpError, Redirect } from '@sveltejs/kit/internal';
7
7
  import { tick } from 'svelte';
8
8
  import { create_remote_cache_key, stringify_remote_arg } from '../../shared.js';
@@ -125,19 +125,16 @@ export function release_overrides(updates) {
125
125
  */
126
126
  export function refresh_queries(stringified_refreshes, updates = []) {
127
127
  const refreshes = Object.entries(devalue.parse(stringified_refreshes, app.decoders));
128
- if (refreshes.length > 0) {
129
- // `refreshes` is a superset of `updates`
130
- for (const [key, value] of refreshes) {
131
- // If there was an optimistic update, release it right before we update the query
132
- const update = updates.find((u) => u._key === key);
133
- if (update && 'release' in update) {
134
- update.release();
135
- }
136
- // Update the query with the new value
137
- const entry = query_map.get(key);
138
- entry?.resource.set(value);
128
+
129
+ // `refreshes` is a superset of `updates`
130
+ for (const [key, value] of refreshes) {
131
+ // If there was an optimistic update, release it right before we update the query
132
+ const update = updates.find((u) => u._key === key);
133
+ if (update && 'release' in update) {
134
+ update.release();
139
135
  }
140
- } else {
141
- void invalidateAll();
136
+ // Update the query with the new value
137
+ const entry = query_map.get(key);
138
+ entry?.resource.set(value);
142
139
  }
143
140
  }
@@ -1,6 +1,7 @@
1
1
  import { parse, serialize } from 'cookie';
2
2
  import { normalize_path, resolve } from '../../utils/url.js';
3
3
  import { add_data_suffix } from '../pathname.js';
4
+ import { text_encoder } from '../utils.js';
4
5
 
5
6
  // eslint-disable-next-line no-control-regex -- control characters are invalid in cookie names
6
7
  const INVALID_COOKIE_CHARACTER_REGEX = /[\x00-\x1F\x7F()<>@,;:"/[\]?={} \t]/;
@@ -217,7 +218,7 @@ export function get_cookies(request, url) {
217
218
 
218
219
  if (__SVELTEKIT_DEV__) {
219
220
  const serialized = serialize(name, value, new_cookies[name].options);
220
- if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
221
+ if (text_encoder.encode(serialized).byteLength > MAX_COOKIE_SIZE) {
221
222
  throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
222
223
  }
223
224
 
@@ -7,8 +7,7 @@ import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from
7
7
  import { normalize_path } from '../../../utils/url.js';
8
8
  import * as devalue from 'devalue';
9
9
  import { create_async_iterator } from '../../../utils/streaming.js';
10
-
11
- const encoder = new TextEncoder();
10
+ import { text_encoder } from '../../utils.js';
12
11
 
13
12
  /**
14
13
  * @param {import('@sveltejs/kit').RequestEvent} event
@@ -129,9 +128,9 @@ export async function render_data(
129
128
  return new Response(
130
129
  new ReadableStream({
131
130
  async start(controller) {
132
- controller.enqueue(encoder.encode(data));
131
+ controller.enqueue(text_encoder.encode(data));
133
132
  for await (const chunk of chunks) {
134
- controller.enqueue(encoder.encode(chunk));
133
+ controller.enqueue(text_encoder.encode(chunk));
135
134
  }
136
135
  controller.close();
137
136
  },
@@ -1,4 +1,4 @@
1
- const encoder = new TextEncoder();
1
+ import { text_encoder } from '../../utils.js';
2
2
 
3
3
  /**
4
4
  * SHA-256 hashing function adapted from https://bitwiseshiftleft.github.io/sjcl
@@ -102,7 +102,7 @@ export function sha256(data) {
102
102
  const bytes = new Uint8Array(out.buffer);
103
103
  reverse_endianness(bytes);
104
104
 
105
- return base64(bytes);
105
+ return btoa(String.fromCharCode(...bytes));
106
106
  }
107
107
 
108
108
  /** The SHA-256 initialization vector */
@@ -160,7 +160,7 @@ function reverse_endianness(bytes) {
160
160
 
161
161
  /** @param {string} str */
162
162
  function encode(str) {
163
- const encoded = encoder.encode(str);
163
+ const encoded = text_encoder.encode(str);
164
164
  const length = encoded.length * 8;
165
165
 
166
166
  // result should be a multiple of 512 bits in length,
@@ -182,58 +182,3 @@ function encode(str) {
182
182
 
183
183
  return words;
184
184
  }
185
-
186
- /*
187
- Based on https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
188
-
189
- MIT License
190
- Copyright (c) 2020 Egor Nepomnyaschih
191
- Permission is hereby granted, free of charge, to any person obtaining a copy
192
- of this software and associated documentation files (the "Software"), to deal
193
- in the Software without restriction, including without limitation the rights
194
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
195
- copies of the Software, and to permit persons to whom the Software is
196
- furnished to do so, subject to the following conditions:
197
- The above copyright notice and this permission notice shall be included in all
198
- copies or substantial portions of the Software.
199
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
200
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
201
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
202
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
204
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205
- SOFTWARE.
206
- */
207
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
208
-
209
- /** @param {Uint8Array} bytes */
210
- export function base64(bytes) {
211
- const l = bytes.length;
212
-
213
- let result = '';
214
- let i;
215
-
216
- for (i = 2; i < l; i += 3) {
217
- result += chars[bytes[i - 2] >> 2];
218
- result += chars[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
219
- result += chars[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)];
220
- result += chars[bytes[i] & 0x3f];
221
- }
222
-
223
- if (i === l + 1) {
224
- // 1 octet yet to write
225
- result += chars[bytes[i - 2] >> 2];
226
- result += chars[(bytes[i - 2] & 0x03) << 4];
227
- result += '==';
228
- }
229
-
230
- if (i === l) {
231
- // 2 octets yet to write
232
- result += chars[bytes[i - 2] >> 2];
233
- result += chars[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
234
- result += chars[(bytes[i - 1] & 0x0f) << 2];
235
- result += '=';
236
- }
237
-
238
- return result;
239
- }
@@ -1,11 +1,11 @@
1
1
  import { escape_html } from '../../../utils/escape.js';
2
- import { base64, sha256 } from './crypto.js';
2
+ import { sha256 } from './crypto.js';
3
3
 
4
4
  const array = new Uint8Array(16);
5
5
 
6
6
  function generate_nonce() {
7
7
  crypto.getRandomValues(array);
8
- return base64(array);
8
+ return btoa(String.fromCharCode(...array));
9
9
  }
10
10
 
11
11
  const quoted = new Set([
@@ -1,7 +1,7 @@
1
1
  import { DEV } from 'esm-env';
2
2
  import { disable_search, make_trackable } from '../../../utils/url.js';
3
3
  import { validate_depends } from '../../shared.js';
4
- import { b64_encode } from '../../utils.js';
4
+ import { base64_encode, text_decoder } from '../../utils.js';
5
5
  import { with_event } from '../../app/server/event.js';
6
6
 
7
7
  /**
@@ -316,12 +316,14 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
316
316
  return async () => {
317
317
  const buffer = await response.arrayBuffer();
318
318
 
319
+ const bytes = new Uint8Array(buffer);
320
+
319
321
  if (dependency) {
320
- dependency.body = new Uint8Array(buffer);
322
+ dependency.body = bytes;
321
323
  }
322
324
 
323
325
  if (buffer instanceof ArrayBuffer) {
324
- await push_fetched(b64_encode(buffer), true);
326
+ await push_fetched(base64_encode(bytes), true);
325
327
  }
326
328
 
327
329
  return buffer;
@@ -394,13 +396,12 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
394
396
  async function stream_to_string(stream) {
395
397
  let result = '';
396
398
  const reader = stream.getReader();
397
- const decoder = new TextDecoder();
398
399
  while (true) {
399
400
  const { done, value } = await reader.read();
400
401
  if (done) {
401
402
  break;
402
403
  }
403
- result += decoder.decode(value);
404
+ result += text_decoder.decode(value);
404
405
  }
405
406
  return result;
406
407
  }
@@ -17,6 +17,7 @@ import { create_server_routing_response, generate_route_object } from './server_
17
17
  import { add_resolution_suffix } from '../../pathname.js';
18
18
  import { with_event } from '../../app/server/event.js';
19
19
  import { get_event_state } from '../event-state.js';
20
+ import { text_encoder } from '../../utils.js';
20
21
 
21
22
  // TODO rename this function/module
22
23
 
@@ -25,8 +26,6 @@ const updated = {
25
26
  check: () => false
26
27
  };
27
28
 
28
- const encoder = new TextEncoder();
29
-
30
29
  /**
31
30
  * Creates the HTML response.
32
31
  * @param {{
@@ -586,9 +585,9 @@ export async function render_response({
586
585
  : new Response(
587
586
  new ReadableStream({
588
587
  async start(controller) {
589
- controller.enqueue(encoder.encode(transformed + '\n'));
588
+ controller.enqueue(text_encoder.encode(transformed + '\n'));
590
589
  for await (const chunk of chunks) {
591
- controller.enqueue(encoder.encode(chunk));
590
+ controller.enqueue(text_encoder.encode(chunk));
592
591
  }
593
592
  controller.close();
594
593
  },
@@ -62,13 +62,7 @@ export async function handle_remote_call(event, options, manifest, id) {
62
62
  /** @type {RemoteFunctionResponse} */ ({
63
63
  type: 'result',
64
64
  result: stringify(data, transport),
65
- refreshes: stringify(
66
- {
67
- ...get_event_state(event).refreshes,
68
- ...(await apply_client_refreshes(/** @type {string[]} */ (form_client_refreshes)))
69
- },
70
- transport
71
- )
65
+ refreshes: await serialize_refreshes(/** @type {string[]} */ (form_client_refreshes))
72
66
  })
73
67
  );
74
68
  }
@@ -78,13 +72,12 @@ export async function handle_remote_call(event, options, manifest, id) {
78
72
  const { payload, refreshes } = await event.request.json();
79
73
  const arg = parse_remote_arg(payload, transport);
80
74
  const data = await with_event(event, () => fn(arg));
81
- const refreshed = await apply_client_refreshes(refreshes);
82
75
 
83
76
  return json(
84
77
  /** @type {RemoteFunctionResponse} */ ({
85
78
  type: 'result',
86
79
  result: stringify(data, transport),
87
- refreshes: stringify({ ...get_event_state(event).refreshes, ...refreshed }, transport)
80
+ refreshes: await serialize_refreshes(refreshes)
88
81
  })
89
82
  );
90
83
  }
@@ -107,14 +100,10 @@ export async function handle_remote_call(event, options, manifest, id) {
107
100
  );
108
101
  } catch (error) {
109
102
  if (error instanceof Redirect) {
110
- const refreshes = {
111
- ...(get_event_state(event).refreshes ?? {}), // could be set by form actions
112
- ...(await apply_client_refreshes(form_client_refreshes ?? []))
113
- };
114
103
  return json({
115
104
  type: 'redirect',
116
105
  location: error.location,
117
- refreshes: Object.keys(refreshes).length > 0 ? stringify(refreshes, transport) : undefined
106
+ refreshes: await serialize_refreshes(form_client_refreshes ?? [])
118
107
  });
119
108
  }
120
109
 
@@ -132,26 +121,33 @@ export async function handle_remote_call(event, options, manifest, id) {
132
121
  );
133
122
  }
134
123
 
135
- /** @param {string[]} refreshes */
136
- async function apply_client_refreshes(refreshes) {
137
- return Object.fromEntries(
138
- await Promise.all(
139
- refreshes.map(async (key) => {
140
- const [hash, name, payload] = key.split('/');
141
- const loader = manifest._.remotes[hash];
142
-
143
- // TODO what do we do in this case? erroring after the mutation has happened is not great
144
- if (!loader) error(400, 'Bad Request');
145
-
146
- const module = await loader();
147
- const fn = module[name];
148
-
149
- if (!fn) error(400, 'Bad Request');
150
-
151
- return [key, await with_event(event, () => fn(parse_remote_arg(payload, transport)))];
152
- })
124
+ /**
125
+ * @param {string[]} client_refreshes
126
+ */
127
+ async function serialize_refreshes(client_refreshes) {
128
+ const refreshes = {
129
+ ...get_event_state(event).refreshes,
130
+ ...Object.fromEntries(
131
+ await Promise.all(
132
+ client_refreshes.map(async (key) => {
133
+ const [hash, name, payload] = key.split('/');
134
+ const loader = manifest._.remotes[hash];
135
+
136
+ // TODO what do we do in this case? erroring after the mutation has happened is not great
137
+ if (!loader) error(400, 'Bad Request');
138
+
139
+ const module = await loader();
140
+ const fn = module[name];
141
+
142
+ if (!fn) error(400, 'Bad Request');
143
+
144
+ return [key, await with_event(event, () => fn(parse_remote_arg(payload, transport)))];
145
+ })
146
+ )
153
147
  )
154
- );
148
+ };
149
+
150
+ return Object.keys(refreshes).length > 0 ? stringify(refreshes, transport) : undefined;
155
151
  }
156
152
  }
157
153
 
@@ -1,5 +1,6 @@
1
1
  /** @import { Transport } from '@sveltejs/kit' */
2
2
  import * as devalue from 'devalue';
3
+ import { base64_decode, base64_encode, text_decoder } from './utils.js';
3
4
 
4
5
  /**
5
6
  * @param {string} route_id
@@ -41,12 +42,8 @@ export function stringify_remote_arg(value, transport) {
41
42
  // If people hit file/url size limits, we can look into using something like compress_and_encode_text from svelte.dev beyond a certain size
42
43
  const json_string = stringify(value, transport);
43
44
 
44
- // Convert to UTF-8 bytes, then base64 - handles all Unicode properly (btoa would fail on exotic characters)
45
- const utf8_bytes = new TextEncoder().encode(json_string);
46
- return btoa(String.fromCharCode(...utf8_bytes))
47
- .replace(/=/g, '')
48
- .replace(/\+/g, '-')
49
- .replace(/\//g, '_');
45
+ const bytes = new TextEncoder().encode(json_string);
46
+ return base64_encode(bytes).replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_');
50
47
  }
51
48
 
52
49
  /**
@@ -57,13 +54,12 @@ export function stringify_remote_arg(value, transport) {
57
54
  export function parse_remote_arg(string, transport) {
58
55
  if (!string) return undefined;
59
56
 
60
- const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode]));
57
+ const json_string = text_decoder.decode(
58
+ // no need to add back `=` characters, atob can handle it
59
+ base64_decode(string.replaceAll('-', '+').replaceAll('_', '/'))
60
+ );
61
61
 
62
- // We don't need to add back the `=`-padding because atob can handle it
63
- const base64_restored = string.replace(/-/g, '+').replace(/_/g, '/');
64
- const binary_string = atob(base64_restored);
65
- const utf8_bytes = new Uint8Array([...binary_string].map((char) => char.charCodeAt(0)));
66
- const json_string = new TextDecoder().decode(utf8_bytes);
62
+ const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode]));
67
63
 
68
64
  return devalue.parse(json_string, decoders);
69
65
  }
@@ -1,37 +1,7 @@
1
- /**
2
- * @param {string} text
3
- * @returns {ArrayBufferLike}
4
- */
5
- export function b64_decode(text) {
6
- const d = atob(text);
1
+ import { BROWSER } from 'esm-env';
7
2
 
8
- const u8 = new Uint8Array(d.length);
9
-
10
- for (let i = 0; i < d.length; i++) {
11
- u8[i] = d.charCodeAt(i);
12
- }
13
-
14
- return u8.buffer;
15
- }
16
-
17
- /**
18
- * @param {ArrayBuffer} buffer
19
- * @returns {string}
20
- */
21
- export function b64_encode(buffer) {
22
- if (globalThis.Buffer) {
23
- return Buffer.from(buffer).toString('base64');
24
- }
25
-
26
- const little_endian = new Uint8Array(new Uint16Array([1]).buffer)[0] > 0;
27
-
28
- // The Uint16Array(Uint8Array(...)) ensures the code points are padded with 0's
29
- return btoa(
30
- new TextDecoder(little_endian ? 'utf-16le' : 'utf-16be').decode(
31
- new Uint16Array(new Uint8Array(buffer))
32
- )
33
- );
34
- }
3
+ export const text_encoder = new TextEncoder();
4
+ export const text_decoder = new TextDecoder();
35
5
 
36
6
  /**
37
7
  * Like node's path.relative, but without using node
@@ -53,3 +23,43 @@ export function get_relative_path(from, to) {
53
23
 
54
24
  return from_parts.concat(to_parts).join('/');
55
25
  }
26
+
27
+ /**
28
+ * @param {Uint8Array} bytes
29
+ * @returns {string}
30
+ */
31
+ export function base64_encode(bytes) {
32
+ // Using `Buffer` is faster than iterating
33
+ if (!BROWSER && globalThis.Buffer) {
34
+ return globalThis.Buffer.from(bytes).toString('base64');
35
+ }
36
+
37
+ let binary = '';
38
+
39
+ for (let i = 0; i < bytes.length; i++) {
40
+ binary += String.fromCharCode(bytes[i]);
41
+ }
42
+
43
+ return btoa(binary);
44
+ }
45
+
46
+ /**
47
+ * @param {string} encoded
48
+ * @returns {Uint8Array}
49
+ */
50
+ export function base64_decode(encoded) {
51
+ // Using `Buffer` is faster than iterating
52
+ if (!BROWSER && globalThis.Buffer) {
53
+ const buffer = globalThis.Buffer.from(encoded, 'base64');
54
+ return new Uint8Array(buffer);
55
+ }
56
+
57
+ const binary = atob(encoded);
58
+ const bytes = new Uint8Array(binary.length);
59
+
60
+ for (let i = 0; i < binary.length; i++) {
61
+ bytes[i] = binary.charCodeAt(i);
62
+ }
63
+
64
+ return bytes;
65
+ }
@@ -302,7 +302,7 @@ export type RemoteFunctionResponse =
302
302
  type: 'result';
303
303
  result: string;
304
304
  /** devalue'd Record<string, any> */
305
- refreshes: string;
305
+ refreshes: string | undefined;
306
306
  };
307
307
 
308
308
  /**
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.27.2';
4
+ export const VERSION = '2.28.0';
package/types/index.d.ts CHANGED
@@ -997,12 +997,15 @@ declare module '@sveltejs/kit' {
997
997
  /**
998
998
  * Information about the target of a specific navigation.
999
999
  */
1000
- export interface NavigationTarget {
1000
+ export interface NavigationTarget<
1001
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
1002
+ RouteId extends AppRouteId | null = AppRouteId | null
1003
+ > {
1001
1004
  /**
1002
1005
  * Parameters of the target page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1003
1006
  * Is `null` if the target is not part of the SvelteKit app (could not be resolved to a route).
1004
1007
  */
1005
- params: Record<string, string> | null;
1008
+ params: Params | null;
1006
1009
  /**
1007
1010
  * Info about the target route
1008
1011
  */
@@ -1010,7 +1013,7 @@ declare module '@sveltejs/kit' {
1010
1013
  /**
1011
1014
  * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. It is `null` when no route is matched.
1012
1015
  */
1013
- id: string | null;
1016
+ id: RouteId | null;
1014
1017
  };
1015
1018
  /**
1016
1019
  * The URL that is navigated to
@@ -1542,6 +1545,8 @@ declare module '@sveltejs/kit' {
1542
1545
  for(key: string | number | boolean): Omit<RemoteForm<Result>, 'for'>;
1543
1546
  /** The result of the form submission */
1544
1547
  get result(): Result | undefined;
1548
+ /** The number of pending submissions */
1549
+ get pending(): number;
1545
1550
  /** Spread this onto a `<button>` or `<input type="submit">` */
1546
1551
  buttonProps: {
1547
1552
  type: 'submit';
@@ -1563,14 +1568,20 @@ declare module '@sveltejs/kit' {
1563
1568
  formaction: string;
1564
1569
  onclick: (event: Event) => void;
1565
1570
  };
1571
+ /** The number of pending submissions */
1572
+ get pending(): number;
1566
1573
  };
1567
1574
  };
1568
1575
 
1569
1576
  /**
1570
1577
  * The return value of a remote `command` function. See [Remote functions](https://svelte.dev/docs/kit/remote-functions#command) for full documentation.
1571
1578
  */
1572
- export type RemoteCommand<Input, Output> = (arg: Input) => Promise<Awaited<Output>> & {
1573
- updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<Awaited<Output>>;
1579
+ export type RemoteCommand<Input, Output> = {
1580
+ (arg: Input): Promise<Awaited<Output>> & {
1581
+ updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<Awaited<Output>>;
1582
+ };
1583
+ /** The number of pending command executions */
1584
+ get pending(): number;
1574
1585
  };
1575
1586
 
1576
1587
  export type RemoteResource<T> = Promise<Awaited<T>> & {
@@ -1600,7 +1611,7 @@ declare module '@sveltejs/kit' {
1600
1611
  */
1601
1612
  refresh(): Promise<void>;
1602
1613
  /**
1603
- * Temporarily override the value of a query. This is used with the `updates` method of a [command](https://svelte.dev/docs/kit/remote-functions#command-Single-flight-mutations) or [enhanced form submission](https://svelte.dev/docs/kit/remote-functions#form-enhance) to provide optimistic updates.
1614
+ * Temporarily override the value of a query. This is used with the `updates` method of a [command](https://svelte.dev/docs/kit/remote-functions#command-Updating-queries) or [enhanced form submission](https://svelte.dev/docs/kit/remote-functions#form-enhance) to provide optimistic updates.
1604
1615
  *
1605
1616
  * ```svelte
1606
1617
  * <script>
@@ -185,6 +185,6 @@
185
185
  null,
186
186
  null
187
187
  ],
188
- "mappings": "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",
188
+ "mappings": "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",
189
189
  "ignoreList": []
190
190
  }
@@ -1,71 +0,0 @@
1
- /** @import { RemoteCommand, RemoteQueryOverride } from '@sveltejs/kit' */
2
- /** @import { RemoteFunctionResponse } from 'types' */
3
- /** @import { Query } from './query.svelte.js' */
4
- import { app_dir, base } from '__sveltekit/paths';
5
- import * as devalue from 'devalue';
6
- import { HttpError } from '@sveltejs/kit/internal';
7
- import { app } from '../client.js';
8
- import { stringify_remote_arg } from '../../shared.js';
9
- import { refresh_queries, release_overrides } from './shared.svelte.js';
10
-
11
- /**
12
- * Client-version of the `command` function from `$app/server`.
13
- * @param {string} id
14
- * @returns {RemoteCommand<any, any>}
15
- */
16
- export function command(id) {
17
- // Careful: This function MUST be synchronous (can't use the async keyword) because the return type has to be a promise with an updates() method.
18
- // If we make it async, the return type will be a promise that resolves to a promise with an updates() method, which is not what we want.
19
- return (arg) => {
20
- /** @type {Array<Query<any> | RemoteQueryOverride>} */
21
- let updates = [];
22
-
23
- /** @type {Promise<any> & { updates: (...args: any[]) => any }} */
24
- const promise = (async () => {
25
- // Wait a tick to give room for the `updates` method to be called
26
- await Promise.resolve();
27
-
28
- const response = await fetch(`${base}/${app_dir}/remote/${id}`, {
29
- method: 'POST',
30
- body: JSON.stringify({
31
- payload: stringify_remote_arg(arg, app.hooks.transport),
32
- refreshes: updates.map((u) => u._key)
33
- }),
34
- headers: {
35
- 'Content-Type': 'application/json'
36
- }
37
- });
38
-
39
- if (!response.ok) {
40
- release_overrides(updates);
41
- // We only end up here in case of a network error or if the server has an internal error
42
- // (which shouldn't happen because we handle errors on the server and always send a 200 response)
43
- throw new Error('Failed to execute remote function');
44
- }
45
-
46
- const result = /** @type {RemoteFunctionResponse} */ (await response.json());
47
- if (result.type === 'redirect') {
48
- release_overrides(updates);
49
- throw new Error(
50
- 'Redirects are not allowed in commands. Return a result instead and use goto on the client'
51
- );
52
- } else if (result.type === 'error') {
53
- release_overrides(updates);
54
- throw new HttpError(result.status ?? 500, result.error);
55
- } else {
56
- refresh_queries(result.refreshes, updates);
57
-
58
- return devalue.parse(result.result, app.decoders);
59
- }
60
- })();
61
-
62
- promise.updates = (/** @type {any} */ ...args) => {
63
- updates = args;
64
- // @ts-expect-error Don't allow updates to be called multiple times
65
- delete promise.updates;
66
- return promise;
67
- };
68
-
69
- return promise;
70
- };
71
- }