@sveltejs/kit 2.36.3 → 2.37.1

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.36.3",
3
+ "version": "2.37.1",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -437,7 +437,9 @@ export interface KitConfig {
437
437
  *
438
438
  * If the array contains `'*'`, all origins will be trusted. This is generally not recommended!
439
439
  *
440
- * **Warning**: Only add origins you completely trust, as this bypasses CSRF protection for those origins.
440
+ * > [!NOTE] Only add origins you completely trust, as this bypasses CSRF protection for those origins.
441
+ *
442
+ * CSRF checks only apply in production, not in local development.
441
443
  * @default []
442
444
  * @example ['https://checkout.stripe.com', 'https://accounts.google.com']
443
445
  */
@@ -1802,6 +1804,13 @@ export type RemoteResource<T> = Promise<Awaited<T>> & {
1802
1804
  );
1803
1805
 
1804
1806
  export type RemoteQuery<T> = RemoteResource<T> & {
1807
+ /**
1808
+ * On the client, this function will update the value of the query without re-fetching it.
1809
+ *
1810
+ * On the server, this can be called in the context of a `command` or `form` and the specified data will accompany the action response back to the client.
1811
+ * This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.
1812
+ */
1813
+ set(value: T): void;
1805
1814
  /**
1806
1815
  * On the client, this function will re-fetch the query from the server.
1807
1816
  *
@@ -108,14 +108,15 @@ export function form(fn) {
108
108
  /** @type {RemoteForm<any>['for']} */
109
109
  value: (key) => {
110
110
  const { state } = get_request_store();
111
- let instance = (state.form_instances ??= new Map()).get(key);
111
+ const cache_key = __.id + '|' + JSON.stringify(key);
112
+ let instance = (state.form_instances ??= new Map()).get(cache_key);
112
113
 
113
114
  if (!instance) {
114
115
  instance = create_instance(key);
115
116
  instance.__.id = `${__.id}/${encodeURIComponent(JSON.stringify(key))}`;
116
117
  instance.__.name = __.name;
117
118
 
118
- state.form_instances.set(key, instance);
119
+ state.form_instances.set(cache_key, instance);
119
120
  }
120
121
 
121
122
  return instance;
@@ -79,7 +79,22 @@ export function query(validate_or_fn, maybe_fn) {
79
79
 
80
80
  promise.catch(() => {});
81
81
 
82
- promise.refresh = async () => {
82
+ /** @param {Output} value */
83
+ promise.set = (value) => {
84
+ const { state } = get_request_store();
85
+ const refreshes = state.refreshes;
86
+
87
+ if (!refreshes) {
88
+ throw new Error(
89
+ `Cannot call set on query '${__.name}' because it is not executed in the context of a command/form remote function`
90
+ );
91
+ }
92
+
93
+ const cache_key = create_remote_cache_key(__.id, stringify_remote_arg(arg, state.transport));
94
+ refreshes[cache_key] = (state.remote_data ??= {})[cache_key] = Promise.resolve(value);
95
+ };
96
+
97
+ promise.refresh = () => {
83
98
  const { state } = get_request_store();
84
99
  const refreshes = state.refreshes;
85
100
 
@@ -90,7 +105,10 @@ export function query(validate_or_fn, maybe_fn) {
90
105
  }
91
106
 
92
107
  const cache_key = create_remote_cache_key(__.id, stringify_remote_arg(arg, state.transport));
93
- refreshes[cache_key] = await /** @type {Promise<any>} */ (promise);
108
+ refreshes[cache_key] = promise;
109
+
110
+ // TODO we could probably just return promise here, but would need to update the types
111
+ return promise.then(() => {});
94
112
  };
95
113
 
96
114
  promise.withOverride = () => {
@@ -68,9 +68,12 @@ export function create_validator(validate_or_fn, maybe_fn) {
68
68
  * @param {() => Promise<T>} get_result
69
69
  * @returns {Promise<T>}
70
70
  */
71
- export function get_response(id, arg, state, get_result) {
72
- const cache_key = create_remote_cache_key(id, stringify_remote_arg(arg, state.transport));
71
+ export async function get_response(id, arg, state, get_result) {
72
+ // wait a beat, in case `myQuery().set(...)` is immediately called
73
+ // eslint-disable-next-line @typescript-eslint/await-thenable
74
+ await 0;
73
75
 
76
+ const cache_key = create_remote_cache_key(id, stringify_remote_arg(arg, state.transport));
74
77
  return ((state.remote_data ??= {})[cache_key] ??= get_result());
75
78
  }
76
79
 
@@ -440,7 +440,7 @@ function persist_state() {
440
440
  * @param {number} redirect_count
441
441
  * @param {{}} [nav_token]
442
442
  */
443
- async function _goto(url, options, redirect_count, nav_token) {
443
+ export async function _goto(url, options, redirect_count, nav_token) {
444
444
  /** @type {string[]} */
445
445
  let query_keys;
446
446
  const result = await navigate({
@@ -9,7 +9,7 @@ import {
9
9
  app,
10
10
  remote_responses,
11
11
  started,
12
- goto,
12
+ _goto,
13
13
  set_nearest_error_page,
14
14
  invalidateAll
15
15
  } from '../client.js';
@@ -32,7 +32,7 @@ export function form(id) {
32
32
  const action = '?/remote=' + encodeURIComponent(action_id);
33
33
 
34
34
  /** @type {any} */
35
- let result = $state(started ? undefined : remote_responses[action_id]);
35
+ let result = $state.raw(started ? undefined : remote_responses[action_id]);
36
36
 
37
37
  /** @type {number} */
38
38
  let pending_count = $state(0);
@@ -82,7 +82,6 @@ export function form(id) {
82
82
  if (!response.ok) {
83
83
  // We only end up here in case of a network error or if the server has an internal error
84
84
  // (which shouldn't happen because we handle errors on the server and always send a 200 response)
85
- result = undefined;
86
85
  throw new Error('Failed to execute remote function');
87
86
  }
88
87
 
@@ -102,12 +101,14 @@ export function form(id) {
102
101
  if (!invalidateAll) {
103
102
  refresh_queries(refreshes, updates);
104
103
  }
105
- void goto(form_result.location, { invalidateAll });
104
+ // Use internal version to allow redirects to external URLs
105
+ void _goto(form_result.location, { invalidateAll }, 0);
106
106
  } else {
107
107
  result = undefined;
108
- throw new HttpError(500, form_result.error);
108
+ throw new HttpError(form_result.status ?? 500, form_result.error);
109
109
  }
110
110
  } catch (e) {
111
+ result = undefined;
111
112
  release_overrides(updates);
112
113
  throw e;
113
114
  } finally {
@@ -2,11 +2,10 @@ import { text } from '@sveltejs/kit';
2
2
  import { HttpError, SvelteKitError, Redirect } from '@sveltejs/kit/internal';
3
3
  import { normalize_error } from '../../../utils/error.js';
4
4
  import { once } from '../../../utils/functions.js';
5
+ import { server_data_serializer_json } from '../page/data_serializer.js';
5
6
  import { load_server_data } from '../page/load_data.js';
6
- import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from '../utils.js';
7
+ import { handle_error_and_jsonify } from '../utils.js';
7
8
  import { normalize_path } from '../../../utils/url.js';
8
- import * as devalue from 'devalue';
9
- import { create_async_iterator } from '../../../utils/streaming.js';
10
9
  import { text_encoder } from '../../utils.js';
11
10
 
12
11
  /**
@@ -120,7 +119,9 @@ export async function render_data(
120
119
  )
121
120
  );
122
121
 
123
- const { data, chunks } = get_data_json(event, event_state, options, nodes);
122
+ const data_serializer = server_data_serializer_json(event, event_state, options);
123
+ for (let i = 0; i < nodes.length; i++) data_serializer.add_node(i, nodes[i]);
124
+ const { data, chunks } = data_serializer.get_data();
124
125
 
125
126
  if (!chunks) {
126
127
  // use a normal JSON response where possible, so we get `content-length`
@@ -185,92 +186,3 @@ export function redirect_json_response(redirect) {
185
186
  })
186
187
  );
187
188
  }
188
-
189
- /**
190
- * If the serialized data contains promises, `chunks` will be an
191
- * async iterable containing their resolutions
192
- * @param {import('@sveltejs/kit').RequestEvent} event
193
- * @param {import('types').RequestState} event_state
194
- * @param {import('types').SSROptions} options
195
- * @param {Array<import('types').ServerDataSkippedNode | import('types').ServerDataNode | import('types').ServerErrorNode | null | undefined>} nodes
196
- * @returns {{ data: string, chunks: AsyncIterable<string> | null }}
197
- */
198
- export function get_data_json(event, event_state, options, nodes) {
199
- let promise_id = 1;
200
- let count = 0;
201
-
202
- const { iterator, push, done } = create_async_iterator();
203
-
204
- const reducers = {
205
- ...Object.fromEntries(
206
- Object.entries(options.hooks.transport).map(([key, value]) => [key, value.encode])
207
- ),
208
- /** @param {any} thing */
209
- Promise: (thing) => {
210
- if (typeof thing?.then === 'function') {
211
- const id = promise_id++;
212
- count += 1;
213
-
214
- /** @type {'data' | 'error'} */
215
- let key = 'data';
216
-
217
- thing
218
- .catch(
219
- /** @param {any} e */ async (e) => {
220
- key = 'error';
221
- return handle_error_and_jsonify(event, event_state, options, /** @type {any} */ (e));
222
- }
223
- )
224
- .then(
225
- /** @param {any} value */
226
- async (value) => {
227
- let str;
228
- try {
229
- str = devalue.stringify(value, reducers);
230
- } catch {
231
- const error = await handle_error_and_jsonify(
232
- event,
233
- event_state,
234
- options,
235
- new Error(`Failed to serialize promise while rendering ${event.route.id}`)
236
- );
237
-
238
- key = 'error';
239
- str = devalue.stringify(error, reducers);
240
- }
241
-
242
- count -= 1;
243
-
244
- push(`{"type":"chunk","id":${id},"${key}":${str}}\n`);
245
- if (count === 0) done();
246
- }
247
- );
248
-
249
- return id;
250
- }
251
- }
252
- };
253
-
254
- try {
255
- const strings = nodes.map((node) => {
256
- if (!node) return 'null';
257
-
258
- if (node.type === 'error' || node.type === 'skip') {
259
- return JSON.stringify(node);
260
- }
261
-
262
- return `{"type":"data","data":${devalue.stringify(node.data, reducers)},"uses":${JSON.stringify(
263
- serialize_uses(node)
264
- )}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''}}`;
265
- });
266
-
267
- return {
268
- data: `{"type":"data","nodes":[${strings.join(',')}]}\n`,
269
- chunks: count > 0 ? iterator : null
270
- };
271
- } catch (e) {
272
- // @ts-expect-error
273
- e.path = 'data' + e.path;
274
- throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
275
- }
276
- }
@@ -0,0 +1,202 @@
1
+ import * as devalue from 'devalue';
2
+ import { create_async_iterator } from '../../../utils/streaming.js';
3
+ import {
4
+ clarify_devalue_error,
5
+ get_global_name,
6
+ handle_error_and_jsonify,
7
+ serialize_uses
8
+ } from '../utils.js';
9
+
10
+ /**
11
+ * If the serialized data contains promises, `chunks` will be an
12
+ * async iterable containing their resolutions
13
+ * @param {import('@sveltejs/kit').RequestEvent} event
14
+ * @param {import('types').RequestState} event_state
15
+ * @param {import('types').SSROptions} options
16
+ * @returns {import('./types.js').ServerDataSerializer}
17
+ */
18
+ export function server_data_serializer(event, event_state, options) {
19
+ let promise_id = 1;
20
+
21
+ const iterator = create_async_iterator();
22
+ const global = get_global_name(options);
23
+
24
+ /** @param {any} thing */
25
+ function replacer(thing) {
26
+ if (typeof thing?.then === 'function') {
27
+ const id = promise_id++;
28
+
29
+ const promise = thing
30
+ .then(/** @param {any} data */ (data) => ({ data }))
31
+ .catch(
32
+ /** @param {any} error */ async (error) => ({
33
+ error: await handle_error_and_jsonify(event, event_state, options, error)
34
+ })
35
+ )
36
+ .then(
37
+ /**
38
+ * @param {{data: any; error: any}} result
39
+ */
40
+ async ({ data, error }) => {
41
+ let str;
42
+ try {
43
+ str = devalue.uneval(error ? [, error] : [data], replacer);
44
+ } catch {
45
+ error = await handle_error_and_jsonify(
46
+ event,
47
+ event_state,
48
+ options,
49
+ new Error(`Failed to serialize promise while rendering ${event.route.id}`)
50
+ );
51
+ data = undefined;
52
+ str = devalue.uneval([, error], replacer);
53
+ }
54
+
55
+ return `${global}.resolve(${id}, ${str.includes('app.decode') ? `(app) => ${str}` : `() => ${str}`})`;
56
+ }
57
+ );
58
+
59
+ iterator.add(promise);
60
+
61
+ return `${global}.defer(${id})`;
62
+ } else {
63
+ for (const key in options.hooks.transport) {
64
+ const encoded = options.hooks.transport[key].encode(thing);
65
+ if (encoded) {
66
+ return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ const strings = /** @type {string[]} */ ([]);
73
+
74
+ return {
75
+ add_node(i, node) {
76
+ try {
77
+ if (!node) {
78
+ strings[i] = 'null';
79
+ return;
80
+ }
81
+
82
+ /** @type {any} */
83
+ const payload = { type: 'data', data: node.data, uses: serialize_uses(node) };
84
+ if (node.slash) payload.slash = node.slash;
85
+
86
+ strings[i] = devalue.uneval(payload, replacer);
87
+ } catch (e) {
88
+ // @ts-expect-error
89
+ e.path = e.path.slice(1);
90
+ throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
91
+ }
92
+ },
93
+
94
+ get_data(csp) {
95
+ const open = `<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>`;
96
+ const close = `</script>\n`;
97
+
98
+ return {
99
+ data: `[${strings.join(',')}]`,
100
+ chunks: promise_id > 1 ? iterator.iterate((str) => open + str + close) : null
101
+ };
102
+ }
103
+ };
104
+ }
105
+
106
+ /**
107
+ * If the serialized data contains promises, `chunks` will be an
108
+ * async iterable containing their resolutions
109
+ * @param {import('@sveltejs/kit').RequestEvent} event
110
+ * @param {import('types').RequestState} event_state
111
+ * @param {import('types').SSROptions} options
112
+ * @returns {import('./types.js').ServerDataSerializerJson}
113
+ */
114
+ export function server_data_serializer_json(event, event_state, options) {
115
+ let promise_id = 1;
116
+
117
+ const iterator = create_async_iterator();
118
+
119
+ const reducers = {
120
+ ...Object.fromEntries(
121
+ Object.entries(options.hooks.transport).map(([key, value]) => [key, value.encode])
122
+ ),
123
+ /** @param {any} thing */
124
+ Promise: (thing) => {
125
+ if (typeof thing?.then !== 'function') {
126
+ return;
127
+ }
128
+
129
+ const id = promise_id++;
130
+
131
+ /** @type {'data' | 'error'} */
132
+ let key = 'data';
133
+
134
+ const promise = thing
135
+ .catch(
136
+ /** @param {any} e */ async (e) => {
137
+ key = 'error';
138
+ return handle_error_and_jsonify(event, event_state, options, /** @type {any} */ (e));
139
+ }
140
+ )
141
+ .then(
142
+ /** @param {any} value */
143
+ async (value) => {
144
+ let str;
145
+ try {
146
+ str = devalue.stringify(value, reducers);
147
+ } catch {
148
+ const error = await handle_error_and_jsonify(
149
+ event,
150
+ event_state,
151
+ options,
152
+ new Error(`Failed to serialize promise while rendering ${event.route.id}`)
153
+ );
154
+
155
+ key = 'error';
156
+ str = devalue.stringify(error, reducers);
157
+ }
158
+
159
+ return `{"type":"chunk","id":${id},"${key}":${str}}\n`;
160
+ }
161
+ );
162
+
163
+ iterator.add(promise);
164
+
165
+ return id;
166
+ }
167
+ };
168
+
169
+ const strings = /** @type {string[]} */ ([]);
170
+
171
+ return {
172
+ add_node(i, node) {
173
+ try {
174
+ if (!node) {
175
+ strings[i] = 'null';
176
+ return;
177
+ }
178
+
179
+ if (node.type === 'error' || node.type === 'skip') {
180
+ strings[i] = JSON.stringify(node);
181
+ return;
182
+ }
183
+
184
+ strings[i] =
185
+ `{"type":"data","data":${devalue.stringify(node.data, reducers)},"uses":${JSON.stringify(
186
+ serialize_uses(node)
187
+ )}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''}}`;
188
+ } catch (e) {
189
+ // @ts-expect-error
190
+ e.path = 'data' + e.path;
191
+ throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
192
+ }
193
+ },
194
+
195
+ get_data() {
196
+ return {
197
+ data: `{"type":"data","nodes":[${strings.join(',')}]}\n`,
198
+ chunks: promise_id > 1 ? iterator.iterate() : null
199
+ };
200
+ }
201
+ };
202
+ }
@@ -10,10 +10,10 @@ import {
10
10
  is_action_json_request,
11
11
  is_action_request
12
12
  } from './actions.js';
13
+ import { server_data_serializer, server_data_serializer_json } from './data_serializer.js';
13
14
  import { load_data, load_server_data } from './load_data.js';
14
15
  import { render_response } from './render.js';
15
16
  import { respond_with_error } from './respond_with_error.js';
16
- import { get_data_json } from '../data/index.js';
17
17
  import { DEV } from 'esm-env';
18
18
  import { get_remote_action, handle_remote_form_post } from '../remote.js';
19
19
  import { PageNodes } from '../../../utils/page_nodes.js';
@@ -147,7 +147,8 @@ export async function render_page(
147
147
  options,
148
148
  manifest,
149
149
  state,
150
- resolve_opts
150
+ resolve_opts,
151
+ data_serializer: server_data_serializer(event, event_state, options)
151
152
  });
152
153
  }
153
154
 
@@ -157,6 +158,12 @@ export async function render_page(
157
158
  /** @type {Error | null} */
158
159
  let load_error = null;
159
160
 
161
+ const data_serializer = server_data_serializer(event, event_state, options);
162
+ const data_serializer_json =
163
+ state.prerendering && should_prerender_data
164
+ ? server_data_serializer_json(event, event_state, options)
165
+ : null;
166
+
160
167
  /** @type {Array<Promise<import('types').ServerDataNode | null>>} */
161
168
  const server_promises = nodes.data.map((node, i) => {
162
169
  if (load_error) {
@@ -172,7 +179,7 @@ export async function render_page(
172
179
  throw action_result.error;
173
180
  }
174
181
 
175
- return await load_server_data({
182
+ const server_data = await load_server_data({
176
183
  event,
177
184
  event_state,
178
185
  state,
@@ -187,6 +194,11 @@ export async function render_page(
187
194
  return data;
188
195
  }
189
196
  });
197
+
198
+ data_serializer.add_node(i, server_data);
199
+ data_serializer_json?.add_node(i, server_data);
200
+
201
+ return server_data;
190
202
  } catch (e) {
191
203
  load_error = /** @type {Error} */ (e);
192
204
  throw load_error;
@@ -287,7 +299,8 @@ export async function render_page(
287
299
  data: null,
288
300
  server_data: null
289
301
  }),
290
- fetched
302
+ fetched,
303
+ data_serializer: server_data_serializer(event, event_state, options)
291
304
  });
292
305
  }
293
306
  }
@@ -303,14 +316,9 @@ export async function render_page(
303
316
  }
304
317
  }
305
318
 
306
- if (state.prerendering && should_prerender_data) {
319
+ if (state.prerendering && data_serializer_json) {
307
320
  // ndjson format
308
- let { data, chunks } = get_data_json(
309
- event,
310
- event_state,
311
- options,
312
- branch.map((node) => node?.server_data)
313
- );
321
+ let { data, chunks } = data_serializer_json.get_data();
314
322
 
315
323
  if (chunks) {
316
324
  for await (const chunk of chunks) {
@@ -339,7 +347,8 @@ export async function render_page(
339
347
  error: null,
340
348
  branch: ssr === false ? [] : compact(branch),
341
349
  action_result,
342
- fetched
350
+ fetched,
351
+ data_serializer
343
352
  });
344
353
  } catch (e) {
345
354
  // if we end up here, it means the data loaded successfully
@@ -1,12 +1,11 @@
1
1
  import { DEV } from 'esm-env';
2
- import * as devalue from 'devalue';
3
2
  import { disable_search, make_trackable } from '../../../utils/url.js';
4
3
  import { validate_depends, validate_load_response } from '../../shared.js';
5
4
  import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
6
5
  import { record_span } from '../../telemetry/record_span.js';
7
- import { clarify_devalue_error, get_node_type } from '../utils.js';
8
6
  import { base64_encode, text_decoder } from '../../utils.js';
9
7
  import { NULL_BODY_STATUS } from '../constants.js';
8
+ import { get_node_type } from '../utils.js';
10
9
 
11
10
  /**
12
11
  * Calls the user's server `load` function.
@@ -234,38 +233,11 @@ export async function load_data({
234
233
  },
235
234
  fn: async (current) => {
236
235
  const traced_event = merge_tracing(event, current);
237
- return await with_request_store({ event: traced_event, state: event_state }, () => {
238
- /** @type {Record<string, any> | null} */
239
- let data = null;
240
-
241
- return load.call(null, {
236
+ return await with_request_store({ event: traced_event, state: event_state }, () =>
237
+ load.call(null, {
242
238
  url: event.url,
243
239
  params: event.params,
244
- get data() {
245
- if (data === null && server_data_node?.data != null) {
246
- /** @type {Record<string, (value: any) => any>} */
247
- const reducers = {};
248
-
249
- /** @type {Record<string, (value: any) => any>} */
250
- const revivers = {};
251
-
252
- for (const key in event_state.transport) {
253
- reducers[key] = event_state.transport[key].encode;
254
- revivers[key] = event_state.transport[key].decode;
255
- }
256
-
257
- // run it through devalue so that the developer can't accidentally mutate it
258
- try {
259
- data = devalue.parse(devalue.stringify(server_data_node.data, reducers), revivers);
260
- } catch (e) {
261
- // @ts-expect-error
262
- e.path = e.path.slice(1);
263
- throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
264
- }
265
- }
266
-
267
- return data;
268
- },
240
+ data: server_data_node?.data ?? null,
269
241
  route: event.route,
270
242
  fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts),
271
243
  setHeaders: event.setHeaders,
@@ -273,8 +245,8 @@ export async function load_data({
273
245
  parent,
274
246
  untrack: (fn) => fn(),
275
247
  tracing: traced_event.tracing
276
- });
277
- });
248
+ })
249
+ );
278
250
  }
279
251
  });
280
252
 
@@ -8,15 +8,14 @@ import { serialize_data } from './serialize_data.js';
8
8
  import { s } from '../../../utils/misc.js';
9
9
  import { Csp } from './csp.js';
10
10
  import { uneval_action_response } from './actions.js';
11
- import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from '../utils.js';
12
11
  import { public_env } from '../../shared-server.js';
13
- import { create_async_iterator } from '../../../utils/streaming.js';
14
12
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
15
13
  import { SCHEME } from '../../../utils/url.js';
16
14
  import { create_server_routing_response, generate_route_object } from './server_routing.js';
17
15
  import { add_resolution_suffix } from '../../pathname.js';
18
16
  import { with_request_store } from '@sveltejs/kit/internal/server';
19
17
  import { text_encoder } from '../../utils.js';
18
+ import { get_global_name } from '../utils.js';
20
19
 
21
20
  // TODO rename this function/module
22
21
 
@@ -40,6 +39,7 @@ const updated = {
40
39
  * event_state: import('types').RequestState;
41
40
  * resolve_opts: import('types').RequiredResolveOptions;
42
41
  * action_result?: import('@sveltejs/kit').ActionResult;
42
+ * data_serializer: import('./types.js').ServerDataSerializer
43
43
  * }} opts
44
44
  */
45
45
  export async function render_response({
@@ -54,7 +54,8 @@ export async function render_response({
54
54
  event,
55
55
  event_state,
56
56
  resolve_opts,
57
- action_result
57
+ action_result,
58
+ data_serializer
58
59
  }) {
59
60
  if (state.prerendering) {
60
61
  if (options.csp.mode === 'nonce') {
@@ -295,16 +296,8 @@ export async function render_response({
295
296
  }
296
297
  }
297
298
 
298
- const global = DEV ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;
299
-
300
- const { data, chunks } = get_data(
301
- event,
302
- event_state,
303
- options,
304
- branch.map((b) => b.server_data),
305
- csp,
306
- global
307
- );
299
+ const global = get_global_name(options);
300
+ const { data, chunks } = data_serializer.get_data(csp);
308
301
 
309
302
  if (page_config.ssr && page_config.csr) {
310
303
  body += `\n\t\t\t${fetched
@@ -644,95 +637,3 @@ export async function render_response({
644
637
  }
645
638
  );
646
639
  }
647
-
648
- /**
649
- * If the serialized data contains promises, `chunks` will be an
650
- * async iterable containing their resolutions
651
- * @param {import('@sveltejs/kit').RequestEvent} event
652
- * @param {import('types').RequestState} event_state
653
- * @param {import('types').SSROptions} options
654
- * @param {Array<import('types').ServerDataNode | null>} nodes
655
- * @param {import('./csp.js').Csp} csp
656
- * @param {string} global
657
- * @returns {{ data: string, chunks: AsyncIterable<string> | null }}
658
- */
659
- function get_data(event, event_state, options, nodes, csp, global) {
660
- let promise_id = 1;
661
- let count = 0;
662
-
663
- const { iterator, push, done } = create_async_iterator();
664
-
665
- /** @param {any} thing */
666
- function replacer(thing) {
667
- if (typeof thing?.then === 'function') {
668
- const id = promise_id++;
669
- count += 1;
670
-
671
- thing
672
- .then(/** @param {any} data */ (data) => ({ data }))
673
- .catch(
674
- /** @param {any} error */ async (error) => ({
675
- error: await handle_error_and_jsonify(event, event_state, options, error)
676
- })
677
- )
678
- .then(
679
- /**
680
- * @param {{data: any; error: any}} result
681
- */
682
- async ({ data, error }) => {
683
- count -= 1;
684
-
685
- let str;
686
- try {
687
- str = devalue.uneval(error ? [, error] : [data], replacer);
688
- } catch {
689
- error = await handle_error_and_jsonify(
690
- event,
691
- event_state,
692
- options,
693
- new Error(`Failed to serialize promise while rendering ${event.route.id}`)
694
- );
695
- data = undefined;
696
- str = devalue.uneval([, error], replacer);
697
- }
698
-
699
- const nonce = csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : '';
700
- push(
701
- `<script${nonce}>${global}.resolve(${id}, ${str.includes('app.decode') ? `(app) => ${str}` : `() => ${str}`})</script>\n`
702
- );
703
- if (count === 0) done();
704
- }
705
- );
706
-
707
- return `${global}.defer(${id})`;
708
- } else {
709
- for (const key in options.hooks.transport) {
710
- const encoded = options.hooks.transport[key].encode(thing);
711
- if (encoded) {
712
- return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
713
- }
714
- }
715
- }
716
- }
717
-
718
- try {
719
- const strings = nodes.map((node) => {
720
- if (!node) return 'null';
721
-
722
- /** @type {any} */
723
- const payload = { type: 'data', data: node.data, uses: serialize_uses(node) };
724
- if (node.slash) payload.slash = node.slash;
725
-
726
- return devalue.uneval(payload, replacer);
727
- });
728
-
729
- return {
730
- data: `[${strings.join(',')}]`,
731
- chunks: count > 0 ? iterator : null
732
- };
733
- } catch (e) {
734
- // @ts-expect-error
735
- e.path = e.path.slice(1);
736
- throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
737
- }
738
- }
@@ -4,6 +4,7 @@ import { load_data, load_server_data } from './load_data.js';
4
4
  import { handle_error_and_jsonify, static_error_page, redirect_response } from '../utils.js';
5
5
  import { get_status } from '../../../utils/error.js';
6
6
  import { PageNodes } from '../../../utils/page_nodes.js';
7
+ import { server_data_serializer } from './data_serializer.js';
7
8
 
8
9
  /**
9
10
  * @typedef {import('./types.js').Loaded} Loaded
@@ -45,6 +46,7 @@ export async function respond_with_error({
45
46
  const nodes = new PageNodes([default_layout]);
46
47
  const ssr = nodes.ssr();
47
48
  const csr = nodes.csr();
49
+ const data_serializer = server_data_serializer(event, event_state, options);
48
50
 
49
51
  if (ssr) {
50
52
  state.error = true;
@@ -59,6 +61,7 @@ export async function respond_with_error({
59
61
  });
60
62
 
61
63
  const server_data = await server_data_promise;
64
+ data_serializer.add_node(0, server_data);
62
65
 
63
66
  const data = await load_data({
64
67
  event,
@@ -101,7 +104,8 @@ export async function respond_with_error({
101
104
  fetched,
102
105
  event,
103
106
  event_state,
104
- resolve_opts
107
+ resolve_opts,
108
+ data_serializer
105
109
  });
106
110
  } catch (e) {
107
111
  // Edge case: If route is a 404 and the user redirects to somewhere from the root layout,
@@ -1,5 +1,12 @@
1
1
  import { CookieSerializeOptions } from 'cookie';
2
- import { SSRNode, CspDirectives, ServerDataNode } from 'types';
2
+ import {
3
+ CspDirectives,
4
+ ServerDataNode,
5
+ SSRNode,
6
+ ServerDataSkippedNode,
7
+ ServerErrorNode
8
+ } from 'types';
9
+ import { Csp } from './csp.js';
3
10
 
4
11
  export interface Fetched {
5
12
  url: string;
@@ -34,3 +41,16 @@ export interface Cookie {
34
41
  value: string;
35
42
  options: CookieSerializeOptions & { path: string };
36
43
  }
44
+
45
+ export type ServerDataSerializer = {
46
+ add_node(i: number, node: ServerDataNode | null): void;
47
+ get_data(csp: Csp): { data: string; chunks: AsyncIterable<string> | null };
48
+ };
49
+
50
+ export type ServerDataSerializerJson = {
51
+ add_node(
52
+ i: number,
53
+ node: ServerDataSkippedNode | ServerDataNode | ServerErrorNode | null | undefined
54
+ ): void;
55
+ get_data(): { data: string; chunks: AsyncIterable<string> | null };
56
+ };
@@ -149,34 +149,35 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
149
149
  * @param {string[]} client_refreshes
150
150
  */
151
151
  async function serialize_refreshes(client_refreshes) {
152
- const refreshes = {
153
- ...state.refreshes,
154
- ...Object.fromEntries(
155
- await Promise.all(
156
- client_refreshes.map(async (key) => {
157
- const [hash, name, payload] = key.split('/');
158
- const loader = manifest._.remotes[hash];
152
+ const refreshes = /** @type {Record<string, Promise<any>>} */ (state.refreshes);
159
153
 
160
- // TODO what do we do in this case? erroring after the mutation has happened is not great
161
- if (!loader) error(400, 'Bad Request');
154
+ for (const key of client_refreshes) {
155
+ if (refreshes[key] !== undefined) continue;
162
156
 
163
- const module = await loader();
164
- const fn = module[name];
157
+ const [hash, name, payload] = key.split('/');
165
158
 
166
- if (!fn) error(400, 'Bad Request');
159
+ const loader = manifest._.remotes[hash];
160
+ const fn = (await loader?.())?.[name];
167
161
 
168
- return [
169
- key,
170
- await with_request_store({ event, state }, () =>
171
- fn(parse_remote_arg(payload, transport))
172
- )
173
- ];
174
- })
175
- )
176
- )
177
- };
162
+ if (!fn) error(400, 'Bad Request');
163
+
164
+ refreshes[key] = with_request_store({ event, state }, () =>
165
+ fn(parse_remote_arg(payload, transport))
166
+ );
167
+ }
168
+
169
+ if (Object.keys(refreshes).length === 0) {
170
+ return undefined;
171
+ }
178
172
 
179
- return Object.keys(refreshes).length > 0 ? stringify(refreshes, transport) : undefined;
173
+ return stringify(
174
+ Object.fromEntries(
175
+ await Promise.all(
176
+ Object.entries(refreshes).map(async ([key, promise]) => [key, await promise])
177
+ )
178
+ ),
179
+ transport
180
+ );
180
181
  }
181
182
  }
182
183
 
@@ -35,6 +35,7 @@ import {
35
35
  strip_data_suffix,
36
36
  strip_resolution_suffix
37
37
  } from '../pathname.js';
38
+ import { server_data_serializer } from './page/data_serializer.js';
38
39
  import { get_remote_id, handle_remote_call } from './remote.js';
39
40
  import { record_span } from '../telemetry/record_span.js';
40
41
  import { otel } from '../telemetry/otel.js';
@@ -73,32 +74,34 @@ export async function internal_respond(request, options, manifest, state) {
73
74
  const is_data_request = has_data_suffix(url.pathname);
74
75
  const remote_id = get_remote_id(url);
75
76
 
76
- const request_origin = request.headers.get('origin');
77
+ if (!DEV) {
78
+ const request_origin = request.headers.get('origin');
77
79
 
78
- if (remote_id) {
79
- if (request.method !== 'GET' && request_origin !== url.origin) {
80
- const message = 'Cross-site remote requests are forbidden';
81
- return json({ message }, { status: 403 });
82
- }
83
- } else if (options.csrf_check_origin) {
84
- const forbidden =
85
- is_form_content_type(request) &&
86
- (request.method === 'POST' ||
87
- request.method === 'PUT' ||
88
- request.method === 'PATCH' ||
89
- request.method === 'DELETE') &&
90
- request_origin !== url.origin &&
91
- (!request_origin || !options.csrf_trusted_origins.includes(request_origin));
92
-
93
- if (forbidden) {
94
- const message = `Cross-site ${request.method} form submissions are forbidden`;
95
- const opts = { status: 403 };
96
-
97
- if (request.headers.get('accept') === 'application/json') {
98
- return json({ message }, opts);
80
+ if (remote_id) {
81
+ if (request.method !== 'GET' && request_origin !== url.origin) {
82
+ const message = 'Cross-site remote requests are forbidden';
83
+ return json({ message }, { status: 403 });
99
84
  }
85
+ } else if (options.csrf_check_origin) {
86
+ const forbidden =
87
+ is_form_content_type(request) &&
88
+ (request.method === 'POST' ||
89
+ request.method === 'PUT' ||
90
+ request.method === 'PATCH' ||
91
+ request.method === 'DELETE') &&
92
+ request_origin !== url.origin &&
93
+ (!request_origin || !options.csrf_trusted_origins.includes(request_origin));
94
+
95
+ if (forbidden) {
96
+ const message = `Cross-site ${request.method} form submissions are forbidden`;
97
+ const opts = { status: 403 };
98
+
99
+ if (request.headers.get('accept') === 'application/json') {
100
+ return json({ message }, opts);
101
+ }
100
102
 
101
- return text(message, opts);
103
+ return text(message, opts);
104
+ }
102
105
  }
103
106
  }
104
107
 
@@ -540,7 +543,8 @@ export async function internal_respond(request, options, manifest, state) {
540
543
  error: null,
541
544
  branch: [],
542
545
  fetched: [],
543
- resolve_opts
546
+ resolve_opts,
547
+ data_serializer: server_data_serializer(event, event_state, options)
544
548
  });
545
549
  }
546
550
 
@@ -44,6 +44,13 @@ export function allowed_methods(mod) {
44
44
  return allowed;
45
45
  }
46
46
 
47
+ /**
48
+ * @param {import('types').SSROptions} options
49
+ */
50
+ export function get_global_name(options) {
51
+ return DEV ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;
52
+ }
53
+
47
54
  /**
48
55
  * Return as a response that renders the error.html
49
56
  *
@@ -587,7 +587,7 @@ export interface RequestState {
587
587
  };
588
588
  form_instances?: Map<any, any>;
589
589
  remote_data?: Record<string, MaybePromise<any>>;
590
- refreshes?: Record<string, any>;
590
+ refreshes?: Record<string, Promise<any>>;
591
591
  }
592
592
 
593
593
  export interface RequestStore {
@@ -240,6 +240,8 @@ const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
240
240
  */
241
241
  export function resolve_route(id, params) {
242
242
  const segments = get_route_segments(id);
243
+ const has_id_trailing_slash = id != '/' && id.endsWith('/');
244
+
243
245
  return (
244
246
  '/' +
245
247
  segments
@@ -262,7 +264,8 @@ export function resolve_route(id, params) {
262
264
  })
263
265
  )
264
266
  .filter(Boolean)
265
- .join('/')
267
+ .join('/') +
268
+ (has_id_trailing_slash ? '/' : '')
266
269
  );
267
270
  }
268
271
 
@@ -16,36 +16,50 @@ function defer() {
16
16
 
17
17
  /**
18
18
  * Create an async iterator and a function to push values into it
19
+ * @template T
19
20
  * @returns {{
20
- * iterator: AsyncIterable<any>;
21
- * push: (value: any) => void;
22
- * done: () => void;
21
+ * iterate: (transform?: (input: T) => T) => AsyncIterable<T>;
22
+ * add: (promise: Promise<T>) => void;
23
23
  * }}
24
24
  */
25
25
  export function create_async_iterator() {
26
+ let count = 0;
27
+
26
28
  const deferred = [defer()];
27
29
 
28
30
  return {
29
- iterator: {
30
- [Symbol.asyncIterator]() {
31
- return {
32
- next: async () => {
33
- const next = await deferred[0].promise;
34
- if (!next.done) deferred.shift();
35
- return next;
36
- }
37
- };
38
- }
31
+ iterate: (transform = (x) => x) => {
32
+ return {
33
+ [Symbol.asyncIterator]() {
34
+ return {
35
+ next: async () => {
36
+ const next = await deferred[0].promise;
37
+
38
+ if (!next.done) {
39
+ deferred.shift();
40
+ return { value: transform(next.value), done: false };
41
+ }
42
+
43
+ return next;
44
+ }
45
+ };
46
+ }
47
+ };
39
48
  },
40
- push: (value) => {
41
- deferred[deferred.length - 1].fulfil({
42
- value,
43
- done: false
49
+ add: (promise) => {
50
+ count += 1;
51
+
52
+ void promise.then((value) => {
53
+ deferred[deferred.length - 1].fulfil({
54
+ value,
55
+ done: false
56
+ });
57
+ deferred.push(defer());
58
+
59
+ if (--count === 0) {
60
+ deferred[deferred.length - 1].fulfil({ done: true });
61
+ }
44
62
  });
45
- deferred.push(defer());
46
- },
47
- done: () => {
48
- deferred[deferred.length - 1].fulfil({ done: true });
49
63
  }
50
64
  };
51
65
  }
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.36.3';
4
+ export const VERSION = '2.37.1';
package/types/index.d.ts CHANGED
@@ -413,7 +413,9 @@ declare module '@sveltejs/kit' {
413
413
  *
414
414
  * If the array contains `'*'`, all origins will be trusted. This is generally not recommended!
415
415
  *
416
- * **Warning**: Only add origins you completely trust, as this bypasses CSRF protection for those origins.
416
+ * > [!NOTE] Only add origins you completely trust, as this bypasses CSRF protection for those origins.
417
+ *
418
+ * CSRF checks only apply in production, not in local development.
417
419
  * @default []
418
420
  * @example ['https://checkout.stripe.com', 'https://accounts.google.com']
419
421
  */
@@ -1778,6 +1780,13 @@ declare module '@sveltejs/kit' {
1778
1780
  );
1779
1781
 
1780
1782
  export type RemoteQuery<T> = RemoteResource<T> & {
1783
+ /**
1784
+ * On the client, this function will update the value of the query without re-fetching it.
1785
+ *
1786
+ * On the server, this can be called in the context of a `command` or `form` and the specified data will accompany the action response back to the client.
1787
+ * This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.
1788
+ */
1789
+ set(value: T): void;
1781
1790
  /**
1782
1791
  * On the client, this function will re-fetch the query from the server.
1783
1792
  *
@@ -187,6 +187,6 @@
187
187
  null,
188
188
  null
189
189
  ],
190
- "mappings": ";;;;;;;;;;;kBAkCiBA,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAiCZC,cAAcA;;;;;;aAMdC,cAAcA;;;;;;;;MAQrBC,aAAaA;;;;;OAKJC,YAAYA;;kBAETC,aAAaA;;;;;;MAMzBC,qBAAqBA;;;;;;;;;;;kBAWTC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA8IPC,MAAMA;;;;;;;;;;;kBAWNC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA4DPC,QAAQA;;;;;;;;kBAQRC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAmkBdC,MAAMA;;;;;;;;;;;aAWNC,iBAAiBA;;;;;;;;;;;;aAYjBC,qBAAqBA;;;;;;;;;aASrBC,iBAAiBA;;;;;;;;;;aAUjBC,WAAWA;;;;;;;;;;aAUXC,UAAUA;;;;;;aAMVC,UAAUA;;;;;;aAMVC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;aA0BPC,SAASA;;;;;kBAKJC,WAAWA;;;;;;;;;;;;aAYhBC,IAAIA;;;;;;;;;;;;kBAYCC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAyHTC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0BfC,gBAAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAgCrBC,cAAcA;;kBAETC,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAoCVC,cAAcA;;;;;;;;;;kBAUdC,UAAUA;;;;;;;;;;;;;;;;;;kBAkBVC,aAAaA;;;;;;;;;;;;;;;;;;;kBAmBbC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA8CTC,YAAYA;;kBAEPC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA+GjBC,cAAcA;;;;;kBAKTC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;kBAuBdC,eAAeA;;;;;;;;;;;;;;;cAenBC,MAAMA;;;;;;kBAMFC,iBAAiBA;;;;;;;kBAOjBC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;aAyBhBC,UAAUA;;;;;;;kBAOLC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAkFpBC,MAAMA;;;;;;;;;;aAUNC,OAAOA;;;;;;;;;;;;;;;;aAgBPC,YAAYA;;;;;;;;;;;;kBC3mDXC,SAASA;;;;;;;;;;kBAqBTC,QAAQA;;;;;;;aDmnDTC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BTC,QAAQA;;;;;;;;aAQbC,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAoEVC,aAAaA;;;;;;;;aAQbC,cAAcA;;;;;;;;;;;;;;;;;;aAkBdC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA8BNC,mBAAmBA;;;;;;;;aAQxBC,uBAAuBA;;;;;aAKvBC,mBAAmBA;WEhzDdC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAkDZC,GAAGA;;;;;;;;;;;;;;;;;;;;;WAqBHC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmElBC,UAAUA;;WAELC,MAAMA;;;;;;;;;MASXC,YAAYA;;WAEPC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAmCXC,yBAAyBA;;;;;;;;;;WAUzBC,yBAAyBA;;;;WAIzBC,sCAAsCA;;;;WAItCC,4BAA4BA;;;;MAIjCC,8BAA8BA;MAC9BC,8BAA8BA;MAC9BC,iCAAiCA;;;;;MAKjCC,2CAA2CA;;;;;;aAM3CC,eAAeA;;WAIVC,cAAcA;;;;;WAKdC,YAAYA;;;;;;MAMjBC,aAAaA;WC/LRC,KAAKA;;;;;;WAeLC,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuHTC,YAAYA;;;;;;;;;;;;;;;;;WAiBZC,QAAQA;;;;;;;;;;;;;;MAgCbC,iBAAiBA;;;;;;;;;WAWZC,UAAUA;;;;;;;;;;;;;WAaVC,SAASA;;;;;;;;;;;;;;;;;;;;;;;WAsHTC,YAAYA;;;;;;;;;;;;;;;;MAgBjBC,kBAAkBA;;WAEbC,aAAaA;;;;;;;;;;WAUbC,UAAUA;;;;;;;;;;;WAWVC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;MAuBZC,aAAaA;;WA6BRC,eAAeA;;;;;;MAMpBC,uBAAuBA;;MAGvBC,WAAWA;;;;;;;;WAQNC,QAAQA;;;;;;;;;WASRC,cAAcA;;;;;;;;;MA+CnBC,eAAeA;;;;;MAKfC,kBAAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC1cdC,WAAWA;;;;;;;;;;;;;;;;;;;iBAsBXC,QAAQA;;;;;iBAiBRC,UAAUA;;;;;;iBASVC,IAAIA;;;;;;iBA4BJC,IAAIA;;;;;;;;;;;;;;;;iBAkDJC,eAAeA;;;;;;;;;;;;;;iBAmBfC,YAAYA;;;;;;;cCrOfC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC4EJC,QAAQA;;;;;;iBC4BFC,UAAUA;;;;;;iBAkCVC,WAAWA;;;;;iBAgFjBC,oBAAoBA;;;;;;;;;;;iBC3MpBC,gBAAgBA;;;;;;;;;iBCuHVC,SAASA;;;;;;;;;cCtIlBC,OAAOA;;;;;cAKPC,GAAGA;;;;;cAKHC,QAAQA;;;;;cAKRC,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;iBCYJC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;iBAgDXC,OAAOA;;;;;;;iBCsmEDC,WAAWA;;;;;;;;;;;iBA9UjBC,aAAaA;;;;;;;;;;;;iBAiBbC,cAAcA;;;;;;;;;;iBAedC,UAAUA;;;;;iBASVC,qBAAqBA;;;;;;;;;;iBA8BrBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCJC,UAAUA;;;;iBA0BVC,aAAaA;;;;;iBAebC,UAAUA;;;;;;;;;;;;;;iBAqBJC,WAAWA;;;;;;;;;;;;;;;;;;iBAoCXC,WAAWA;;;;;iBAsCjBC,SAASA;;;;;iBA+CTC,YAAYA;MV/+DhBlE,YAAYA;;;;;;;;;;;;;;YWlJbmE,IAAIA;;;;;;;;;YASJC,MAAMA;;MAEZC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBAC,OAAOA;;;;;;;;;;;;;;;;;iBAiBPC,KAAKA;;;;;iBAKLC,YAAYA;;;;;;;;;;;;;;;;;;;;;;iBChDZC,IAAIA;;;;;;;;iBCOJC,eAAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCTfC,IAAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MbycRC,8BAA8BA;MD/T9B5E,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ce1GX6E,IAAIA;;;;;cAQJC,UAAUA;;;;;;;;;;;cAMVC,OAAOA;;;;;;;;;iBCrDPC,SAASA;;;;;;;;;;;;;;;cAyBTH,IAAIA;;;;;;;;;;cAiBJC,UAAUA;;;;;;;;cAeVC,OAAOA",
190
+ "mappings": "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",
191
191
  "ignoreList": []
192
192
  }