svelte 5.57.1 → 5.57.2

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 (30) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/errors.js +10 -0
  4. package/src/compiler/migrate/index.js +15 -4
  5. package/src/compiler/phases/1-parse/state/tag.js +2 -0
  6. package/src/compiler/phases/2-analyze/visitors/LetDirective.js +22 -0
  7. package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +2 -2
  8. package/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js +2 -1
  9. package/src/compiler/phases/3-transform/client/visitors/shared/events.js +3 -2
  10. package/src/compiler/phases/scope.js +8 -0
  11. package/src/compiler/print/index.js +3 -1
  12. package/src/index-client.js +1 -1
  13. package/src/internal/client/dev/tracing.js +3 -1
  14. package/src/internal/client/dom/blocks/boundary.js +39 -8
  15. package/src/internal/client/dom/blocks/each.js +8 -6
  16. package/src/internal/client/dom/blocks/svelte-element.js +3 -1
  17. package/src/internal/client/dom/css.js +1 -1
  18. package/src/internal/client/dom/elements/attributes.js +168 -148
  19. package/src/internal/client/dom/elements/bindings/document.js +3 -2
  20. package/src/internal/client/dom/elements/transitions.js +3 -3
  21. package/src/internal/client/error-handling.js +5 -1
  22. package/src/internal/client/reactivity/async.js +5 -0
  23. package/src/internal/client/reactivity/batch.js +12 -4
  24. package/src/internal/client/reactivity/sources.js +3 -3
  25. package/src/internal/client/runtime.js +13 -16
  26. package/src/internal/server/index.js +1 -1
  27. package/src/reactivity/date.js +2 -2
  28. package/src/reactivity/url-search-params.js +1 -1
  29. package/src/version.js +1 -1
  30. package/types/index.d.ts.map +1 -1
@@ -298,182 +298,202 @@ function set_attributes(
298
298
  var is_option_element = element.nodeName === OPTION_TAG;
299
299
  var is_select_element = element.nodeName === SELECT_TAG;
300
300
 
301
- for (var key in prev) {
302
- // don't null our internal $$onX listeners
303
- if (!(key in next) && key[0] + key[1] !== '$$') {
304
- next[key] = null;
305
- }
306
- }
307
-
308
- if (next.class) {
309
- next.class = clsx(next.class);
310
- } else if (css_hash || next[CLASS]) {
311
- next.class = null; /* force call to set_class() */
312
- }
313
-
314
- if (next[STYLE]) {
315
- next.style ??= null; /* force call to set_style() */
316
- }
317
-
318
- var setters = get_setters(element);
319
-
320
- if (element.nodeName === INPUT_TAG && 'type' in next && ('value' in next || '__value' in next)) {
321
- var type = next.type;
322
-
323
- if (type !== current.type || (type === undefined && element.hasAttribute('type'))) {
324
- current.type = type;
325
- set_attribute(element, 'type', type, skip_warning);
301
+ try {
302
+ for (var key in prev) {
303
+ // don't null our internal $$onX listeners
304
+ if (!(key in next) && key[0] + key[1] !== '$$') {
305
+ next[key] = null;
306
+ }
326
307
  }
327
- }
328
308
 
329
- // since key is captured we use const
330
- for (const key in next) {
331
- // let instead of var because referenced in a closure
332
- let value = next[key];
333
-
334
- // Up here because we want to do this for the initial value, too, even if it's undefined,
335
- // and this wouldn't be reached in case of undefined because of the equality check below
336
- if (is_option_element && key === 'value' && value == null) {
337
- // The <option> element is a special case because removing the value attribute means
338
- // the value is set to the text content of the option element, and setting the value
339
- // to null or undefined means the value is set to the string "null" or "undefined".
340
- // To align with how we handle this case in non-spread-scenarios, this logic is needed.
341
- // There's a super-edge-case bug here that is left in in favor of smaller code size:
342
- // Because of the "set missing props to null" logic above, we can't differentiate
343
- // between a missing value and an explicitly set value of null or undefined. That means
344
- // that once set, the value attribute of an <option> element can't be removed. This is
345
- // a very rare edge case, and removing the attribute altogether isn't possible either
346
- // for the <option value={undefined}> case, so we're not losing any functionality here.
347
- // @ts-ignore
348
- element.value = element.__value = '';
349
- current[key] = value;
350
- continue;
309
+ if (next.class) {
310
+ next.class = clsx(next.class);
311
+ } else if (css_hash || next[CLASS]) {
312
+ next.class = null; /* force call to set_class() */
351
313
  }
352
314
 
353
- if (key === 'class') {
354
- var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
355
- set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]);
356
- current[key] = value;
357
- current[CLASS] = next[CLASS];
358
- continue;
315
+ if (next[STYLE]) {
316
+ next.style ??= null; /* force call to set_style() */
359
317
  }
360
318
 
361
- if (key === 'style') {
362
- set_style(element, value, prev?.[STYLE], next[STYLE]);
363
- current[key] = value;
364
- current[STYLE] = next[STYLE];
365
- continue;
366
- }
319
+ var setters = get_setters(element);
367
320
 
368
- var prev_value = current[key];
321
+ if (
322
+ element.nodeName === INPUT_TAG &&
323
+ 'type' in next &&
324
+ ('value' in next || '__value' in next)
325
+ ) {
326
+ var type = next.type;
369
327
 
370
- // Skip if value is unchanged, unless it's `undefined` and the element still has the attribute
371
- if (value === prev_value && !(value === undefined && element.hasAttribute(key))) {
372
- continue;
328
+ if (type !== current.type || (type === undefined && element.hasAttribute('type'))) {
329
+ current.type = type;
330
+ set_attribute(element, 'type', type, skip_warning);
331
+ }
373
332
  }
374
333
 
375
- current[key] = value;
376
-
377
- var prefix = key[0] + key[1]; // this is faster than key.slice(0, 2)
378
- if (prefix === '$$') continue;
334
+ // since key is captured we use const
335
+ for (const key in next) {
336
+ // let instead of var because referenced in a closure
337
+ let value = next[key];
338
+
339
+ // Up here because we want to do this for the initial value, too, even if it's undefined,
340
+ // and this wouldn't be reached in case of undefined because of the equality check below
341
+ if (is_option_element && key === 'value' && value == null) {
342
+ // The <option> element is a special case because removing the value attribute means
343
+ // the value is set to the text content of the option element, and setting the value
344
+ // to null or undefined means the value is set to the string "null" or "undefined".
345
+ // To align with how we handle this case in non-spread-scenarios, this logic is needed.
346
+ // There's a super-edge-case bug here that is left in in favor of smaller code size:
347
+ // Because of the "set missing props to null" logic above, we can't differentiate
348
+ // between a missing value and an explicitly set value of null or undefined. That means
349
+ // that once set, the value attribute of an <option> element can't be removed. This is
350
+ // a very rare edge case, and removing the attribute altogether isn't possible either
351
+ // for the <option value={undefined}> case, so we're not losing any functionality here.
352
+ // @ts-ignore
353
+ element.value = element.__value = '';
354
+ current[key] = value;
355
+ continue;
356
+ }
379
357
 
380
- if (prefix === 'on') {
381
- /** @type {{ capture?: true }} */
382
- const opts = {};
383
- const event_handle_key = '$$' + key;
384
- let event_name = key.slice(2);
385
- var is_delegated = can_delegate_event(event_name);
358
+ if (key === 'class') {
359
+ var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
360
+ set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]);
361
+ current[key] = value;
362
+ current[CLASS] = next[CLASS];
363
+ continue;
364
+ }
386
365
 
387
- if (is_capture_event(event_name)) {
388
- event_name = event_name.slice(0, -7);
389
- opts.capture = true;
366
+ if (key === 'style') {
367
+ set_style(element, value, prev?.[STYLE], next[STYLE]);
368
+ current[key] = value;
369
+ current[STYLE] = next[STYLE];
370
+ continue;
390
371
  }
391
372
 
392
- if (!is_delegated && prev_value) {
393
- // Listening to same event but different handler -> our handle function below takes care of this
394
- // If we were to remove and add listeners in this case, it could happen that the event is "swallowed"
395
- // (the browser seems to not know yet that a new one exists now) and doesn't reach the handler
396
- // https://github.com/sveltejs/svelte/issues/11903
397
- if (value != null) continue;
373
+ var prev_value = current[key];
398
374
 
399
- element.removeEventListener(event_name, current[event_handle_key], opts);
400
- current[event_handle_key] = null;
375
+ // Skip if value is unchanged, unless it's `undefined` and the element still has the attribute
376
+ if (value === prev_value && !(value === undefined && element.hasAttribute(key))) {
377
+ continue;
401
378
  }
402
379
 
403
- if (is_delegated) {
404
- delegated(event_name, element, value);
405
- delegate([event_name]);
406
- } else if (value != null) {
407
- /**
408
- * @this {any}
409
- * @param {Event} evt
410
- */
411
- function handle(evt) {
412
- current[key].call(this, evt);
380
+ current[key] = value;
381
+
382
+ var prefix = key[0] + key[1]; // this is faster than key.slice(0, 2)
383
+ if (prefix === '$$') continue;
384
+
385
+ if (prefix === 'on') {
386
+ /** @type {{ capture?: true }} */
387
+ const opts = {};
388
+ const event_handle_key = '$$' + key;
389
+ let event_name = key.slice(2);
390
+ var is_delegated = can_delegate_event(event_name);
391
+
392
+ if (is_capture_event(event_name)) {
393
+ event_name = event_name.slice(0, -7);
394
+ opts.capture = true;
413
395
  }
414
396
 
415
- current[event_handle_key] = create_event(event_name, element, handle, opts);
416
- }
417
- } else if (key === 'style') {
418
- // avoid using the setter
419
- set_attribute(element, key, value);
420
- } else if (key === 'autofocus') {
421
- autofocus(/** @type {HTMLElement} */ (element), Boolean(value));
422
- } else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) {
423
- // @ts-ignore We're not running this for custom elements because __value is actually
424
- // how Lit stores the current value on the element, and messing with that would break things.
425
- element.value = element.__value = value;
426
- } else if (key === 'selected' && is_option_element) {
427
- set_selected(/** @type {HTMLOptionElement} */ (element), value);
428
- } else {
429
- var name = key;
430
- if (!preserve_attribute_case) {
431
- name = normalize_attribute(name);
432
- }
397
+ if (!is_delegated && prev_value) {
398
+ // Listening to same event but different handler -> our handle function below takes care of this
399
+ // If we were to remove and add listeners in this case, it could happen that the event is "swallowed"
400
+ // (the browser seems to not know yet that a new one exists now) and doesn't reach the handler
401
+ // https://github.com/sveltejs/svelte/issues/11903
402
+ if (value != null) continue;
433
403
 
434
- var is_default = name === 'defaultValue' || name === 'defaultChecked';
404
+ element.removeEventListener(event_name, current[event_handle_key], opts);
405
+ current[event_handle_key] = null;
406
+ }
435
407
 
436
- // A select's default value is represented by selected options, not a property.
437
- if (is_select_element && name === 'defaultValue') continue;
408
+ if (is_delegated) {
409
+ delegated(event_name, element, value);
410
+ delegate([event_name]);
411
+ } else if (value != null) {
412
+ /**
413
+ * @this {any}
414
+ * @param {Event} evt
415
+ */
416
+ function handle(evt) {
417
+ current[key].call(this, evt);
418
+ }
438
419
 
439
- if (value == null && !is_custom_element && !is_default) {
440
- attributes[key] = null;
420
+ current[event_handle_key] = create_event(event_name, element, handle, opts);
421
+ }
422
+ } else if (key === 'style') {
423
+ // avoid using the setter
424
+ set_attribute(element, key, value);
425
+ } else if (key === 'autofocus') {
426
+ autofocus(/** @type {HTMLElement} */ (element), Boolean(value));
427
+ } else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) {
428
+ // @ts-ignore We're not running this for custom elements because __value is actually
429
+ // how Lit stores the current value on the element, and messing with that would break things.
430
+ element.__value = value;
431
+ // we don't set the value if it hasn't changed. This supports invalid number inputs like `1e` because
432
+ // 1. user types 1e
433
+ // 2. the state is updated reading e.target.value which is ''
434
+ // 3. the spreaded value is ''
435
+ // 4. updating input.value would thus, clear the user value
436
+ if (
437
+ prev_value == null ||
438
+ // @ts-ignore
439
+ element.value !== value ||
440
+ (value === 0 && element.nodeName === PROGRESS_TAG)
441
+ ) {
442
+ // @ts-ignore
443
+ element.value = value;
444
+ }
445
+ } else if (key === 'selected' && is_option_element) {
446
+ set_selected(/** @type {HTMLOptionElement} */ (element), value);
447
+ } else {
448
+ var name = key;
449
+ if (!preserve_attribute_case) {
450
+ name = normalize_attribute(name);
451
+ }
441
452
 
442
- if (name === 'value' || name === 'checked') {
443
- // removing value/checked also removes defaultValue/defaultChecked — preserve
444
- let input = /** @type {HTMLInputElement} */ (element);
445
- const use_default = prev === undefined;
446
- if (name === 'value') {
447
- let previous = input.defaultValue;
448
- input.removeAttribute(name);
449
- input.defaultValue = previous;
450
- // @ts-ignore
451
- input.value = input.__value = use_default ? previous : null;
453
+ var is_default = name === 'defaultValue' || name === 'defaultChecked';
454
+
455
+ // A select's default value is represented by selected options, not a property.
456
+ if (is_select_element && name === 'defaultValue') continue;
457
+
458
+ if (value == null && !is_custom_element && !is_default) {
459
+ attributes[key] = null;
460
+
461
+ if (name === 'value' || name === 'checked') {
462
+ // removing value/checked also removes defaultValue/defaultChecked — preserve
463
+ let input = /** @type {HTMLInputElement} */ (element);
464
+ const use_default = prev === undefined;
465
+ if (name === 'value') {
466
+ let previous = input.defaultValue;
467
+ input.removeAttribute(name);
468
+ input.defaultValue = previous;
469
+ // @ts-ignore
470
+ input.value = input.__value = use_default ? previous : null;
471
+ } else {
472
+ let previous = input.defaultChecked;
473
+ input.removeAttribute(name);
474
+ input.defaultChecked = previous;
475
+ input.checked = use_default ? previous : false;
476
+ }
452
477
  } else {
453
- let previous = input.defaultChecked;
454
- input.removeAttribute(name);
455
- input.defaultChecked = previous;
456
- input.checked = use_default ? previous : false;
478
+ element.removeAttribute(key);
457
479
  }
458
- } else {
459
- element.removeAttribute(key);
480
+ } else if (
481
+ is_default ||
482
+ ((is_custom_element || typeof value !== 'string') && setters.has(name))
483
+ ) {
484
+ // @ts-ignore
485
+ element[name] = value;
486
+ // remove it from attributes's cache
487
+ if (name in attributes) attributes[name] = UNINITIALIZED;
488
+ } else if (typeof value !== 'function') {
489
+ set_attribute(element, name, value, skip_warning);
460
490
  }
461
- } else if (
462
- is_default ||
463
- ((is_custom_element || typeof value !== 'string') && setters.has(name))
464
- ) {
465
- // @ts-ignore
466
- element[name] = value;
467
- // remove it from attributes's cache
468
- if (name in attributes) attributes[name] = UNINITIALIZED;
469
- } else if (typeof value !== 'function') {
470
- set_attribute(element, name, value, skip_warning);
471
491
  }
472
492
  }
473
- }
474
-
475
- if (is_hydrating_custom_element) {
476
- set_hydrating(true);
493
+ } finally {
494
+ if (is_hydrating_custom_element) {
495
+ set_hydrating(true);
496
+ }
477
497
  }
478
498
 
479
499
  return current;
@@ -1,4 +1,4 @@
1
- import { listen } from './shared.js';
1
+ import { listen, without_reactive_context } from './shared.js';
2
2
 
3
3
  /**
4
4
  * @param {(activeElement: Element | null) => void} update
@@ -12,6 +12,7 @@ export function bind_active_element(update) {
12
12
  return;
13
13
  }
14
14
 
15
- update(document.activeElement);
15
+ // Removing the focused element fires `focusout` while an effect is running
16
+ without_reactive_context(() => update(document.activeElement));
16
17
  });
17
18
  }
@@ -359,9 +359,9 @@ function animate(element, options, counterpart, t2, on_begin, on_finish) {
359
359
  aborted = true;
360
360
  a?.abort();
361
361
  },
362
- deactivate: () => a.deactivate(),
363
- reset: () => a.reset(),
364
- t: () => a.t()
362
+ deactivate: () => a?.deactivate(),
363
+ reset: () => a?.reset(),
364
+ t: () => a?.t() ?? 1 - t2
365
365
  };
366
366
  }
367
367
 
@@ -1,7 +1,7 @@
1
1
  /** @import { Derived, Effect } from '#client' */
2
2
  /** @import { Boundary } from './dom/blocks/boundary.js' */
3
3
  import { DEV } from 'esm-env';
4
- import { FILENAME } from '../../constants.js';
4
+ import { FILENAME, HYDRATION_ERROR } from '../../constants.js';
5
5
  import { is_firefox } from './dom/operations.js';
6
6
  import {
7
7
  ERROR_VALUE,
@@ -52,6 +52,10 @@ export function handle_error(error) {
52
52
  * @param {Effect | null} effect
53
53
  */
54
54
  export function invoke_error_boundary(error, effect) {
55
+ if (error === HYDRATION_ERROR) {
56
+ throw error;
57
+ }
58
+
55
59
  if (effect !== null && (effect.f & DESTROYED) !== 0) {
56
60
  return;
57
61
  }
@@ -103,6 +103,11 @@ export function flatten(blockers, sync, async, fn) {
103
103
 
104
104
  if (blocker_promise) {
105
105
  blocker_promise.then(() => {
106
+ if ((parent.f & DESTROYED) !== 0) {
107
+ decrement_pending();
108
+ return;
109
+ }
110
+
106
111
  restore();
107
112
  run();
108
113
  unset_context();
@@ -1032,15 +1032,15 @@ export class Batch {
1032
1032
  */
1033
1033
  export function flushSync(fn) {
1034
1034
  var was_flushing_sync = is_flushing_sync;
1035
+ var prev_previous_batch = previous_batch;
1036
+ previous_batch = null;
1035
1037
  is_flushing_sync = true;
1036
1038
 
1037
1039
  try {
1038
1040
  var result;
1039
1041
 
1040
1042
  if (fn) {
1041
- if (current_batch !== null && !current_batch.is_fork) {
1042
- current_batch.flush();
1043
- }
1043
+ flushSync(); // flush anything pending through the while loop below
1044
1044
 
1045
1045
  result = fn();
1046
1046
  }
@@ -1056,6 +1056,7 @@ export function flushSync(fn) {
1056
1056
  }
1057
1057
  } finally {
1058
1058
  is_flushing_sync = was_flushing_sync;
1059
+ previous_batch = prev_previous_batch;
1059
1060
  }
1060
1061
  }
1061
1062
 
@@ -1295,6 +1296,10 @@ export function eager(fn) {
1295
1296
  let version = version_map.get(parent) ?? source(0);
1296
1297
  version_map.set(parent, version);
1297
1298
 
1299
+ if (DEV) {
1300
+ version.label ??= '$state.eager version';
1301
+ }
1302
+
1298
1303
  teardown(() => {
1299
1304
  if (parent.f & DESTROYING) version_map.delete(parent);
1300
1305
  });
@@ -1402,6 +1407,8 @@ export function fork(fn) {
1402
1407
  e.fork_timing();
1403
1408
  }
1404
1409
 
1410
+ flushSync();
1411
+
1405
1412
  var batch = Batch.ensure();
1406
1413
  batch.is_fork = true;
1407
1414
  batch_values = new Map();
@@ -1409,7 +1416,8 @@ export function fork(fn) {
1409
1416
  var committed = false;
1410
1417
  var settled = batch.settled();
1411
1418
 
1412
- flushSync(fn);
1419
+ fn();
1420
+ flushSync();
1413
1421
 
1414
1422
  return {
1415
1423
  commit: async () => {
@@ -46,7 +46,7 @@ import { set_signal_status, update_derived_status } from './status.js';
46
46
  /** @type {Set<Effect>} */
47
47
  export let eager_effects = new Set();
48
48
 
49
- /** @type {Map<Source, any>} */
49
+ /** @type {Map<Value, any>} */
50
50
  export const old_values = new Map();
51
51
 
52
52
  /**
@@ -141,7 +141,7 @@ export function mutate(source, value) {
141
141
 
142
142
  /**
143
143
  * @template V
144
- * @param {Source<V>} source
144
+ * @param {Value<V>} source
145
145
  * @param {V} value
146
146
  * @param {boolean} [should_proxy]
147
147
  * @returns {V}
@@ -181,7 +181,7 @@ var count_deps = 0;
181
181
 
182
182
  /**
183
183
  * @template V
184
- * @param {Source<V>} source
184
+ * @param {Value<V>} source
185
185
  * @param {V} value
186
186
  * @param {Effect[] | null} [updated_during_traversal]
187
187
  * @returns {V}
@@ -60,11 +60,6 @@ import { without_reactive_context } from './dom/elements/bindings/shared.js';
60
60
  import { set_signal_status, update_derived_status } from './reactivity/status.js';
61
61
  import * as w from './warnings.js';
62
62
 
63
- /**
64
- * True if updating in an effect context that is reactive (i.e. not branch/root effects)
65
- */
66
- let is_updating_effect = false;
67
-
68
63
  export let is_destroying_effect = false;
69
64
 
70
65
  /** @param {boolean} value */
@@ -93,7 +88,7 @@ export function set_active_effect(effect) {
93
88
  /**
94
89
  * When sources are created within a reaction, reading and writing
95
90
  * them within that reaction should not cause a re-run
96
- * @type {null | Set<Source>}
91
+ * @type {null | Set<Value>}
97
92
  */
98
93
  export let current_sources = null;
99
94
 
@@ -121,11 +116,11 @@ export let skipped_deps = 0;
121
116
  /**
122
117
  * Tracks writes that the effect it's executed in doesn't listen to yet,
123
118
  * so that the dependency can be added to the effect later on if it then reads it
124
- * @type {null | Source[]}
119
+ * @type {null | Value[]}
125
120
  */
126
121
  export let untracked_writes = null;
127
122
 
128
- /** @param {null | Source[]} value */
123
+ /** @param {null | Value[]} value */
129
124
  export function set_untracked_writes(value) {
130
125
  untracked_writes = value;
131
126
  }
@@ -410,7 +405,7 @@ function remove_reaction(signal, dependency) {
410
405
  // In a fork it's possible that a derived is executed and gets reactions, then commits, but is
411
406
  // never re-executed. This is possible when the derived is only executed once in the context
412
407
  // of a new branch which happens before fork.commit() runs. In this case, the derived still has
413
- // UNINITIALIZED as its value, and then when it's loosing its reactions we need to ensure it stays
408
+ // UNINITIALIZED as its value, and then when it's losing its reactions we need to ensure it stays
414
409
  // DIRTY so it is reexecuted once someone wants its value again.
415
410
  if (derived.v !== UNINITIALIZED) {
416
411
  update_derived_status(derived);
@@ -462,10 +457,8 @@ export function update_effect(effect) {
462
457
  set_signal_status(effect, CLEAN);
463
458
 
464
459
  var previous_effect = active_effect;
465
- var was_updating_effect = is_updating_effect;
466
460
 
467
461
  active_effect = effect;
468
- is_updating_effect = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0; // Branch/root effects are not reactive contexts
469
462
 
470
463
  if (DEV) {
471
464
  var previous_component_fn = dev_current_component_function;
@@ -498,7 +491,6 @@ export function update_effect(effect) {
498
491
  }
499
492
  }
500
493
  } finally {
501
- is_updating_effect = was_updating_effect;
502
494
  active_effect = previous_effect;
503
495
 
504
496
  if (DEV) {
@@ -680,13 +672,12 @@ export function get(signal) {
680
672
  return value;
681
673
  }
682
674
 
683
- // connect disconnected deriveds if we are reading them inside an effect,
684
- // or inside another derived that is already connected
675
+ // connect disconnected deriveds when reading them inside a connected reaction
685
676
  var should_connect =
686
677
  (derived.f & CONNECTED) === 0 &&
687
678
  !untracking &&
688
679
  active_reaction !== null &&
689
- (is_updating_effect || (active_reaction.f & CONNECTED) !== 0);
680
+ (active_reaction.f & CONNECTED) !== 0;
690
681
 
691
682
  var is_new = (derived.f & REACTION_RAN) === 0;
692
683
 
@@ -728,7 +719,13 @@ function reconnect(derived) {
728
719
  if (derived.deps === null) return;
729
720
 
730
721
  for (const dep of derived.deps) {
731
- (dep.reactions ??= []).push(derived);
722
+ var reactions = dep.reactions;
723
+
724
+ if (reactions === null) {
725
+ dep.reactions = [derived];
726
+ } else if (!includes.call(reactions, derived)) {
727
+ reactions.push(derived);
728
+ }
732
729
 
733
730
  if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) {
734
731
  unfreeze_derived_effects(/** @type {Derived} */ (dep));
@@ -168,7 +168,7 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) {
168
168
  // value/checked takes precedence over defaultValue/defaultChecked
169
169
  name = name === 'defaultvalue' ? 'value' : 'checked';
170
170
  if (name in attrs) continue;
171
- // We're checking prior entries aswell because "name in attrs" is not enough as the attributes may have different casing
171
+ // We're checking prior entries as well because "name in attrs" is not enough as the attributes may have different casing
172
172
  for (let j = 0; j < names.length; j++) {
173
173
  if (names[j].toLowerCase() === name) continue outer;
174
174
  }
@@ -1,4 +1,4 @@
1
- /** @import { Source } from '#client' */
1
+ /** @import { Derived } from '#client' */
2
2
  import { derived } from '../internal/client/index.js';
3
3
  import { set, state } from '../internal/client/reactivity/sources.js';
4
4
  import { tag } from '../internal/client/dev/tracing.js';
@@ -42,7 +42,7 @@ var inited = false;
42
42
  export class SvelteDate extends Date {
43
43
  #time = state(super.getTime());
44
44
 
45
- /** @type {Map<keyof Date, Source<unknown>>} */
45
+ /** @type {Map<keyof Date, Derived<unknown>>} */
46
46
  #deriveds = new Map();
47
47
 
48
48
  #reaction = active_reaction;
@@ -42,7 +42,7 @@ export class SvelteURLSearchParams extends URLSearchParams {
42
42
  if (!this.#url || this.#updating) return;
43
43
  this.#updating = true;
44
44
 
45
- const search = this.toString();
45
+ const search = super.toString();
46
46
  this.#url.search = search && `?${search}`;
47
47
 
48
48
  this.#updating = false;
package/src/version.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * The current version, as set in package.json.
5
5
  * @type {string}
6
6
  */
7
- export const VERSION = '5.57.1';
7
+ export const VERSION = '5.57.2';
8
8
  export const PUBLIC_VERSION = '5';