@odoo/owl 3.0.0-alpha.35 → 3.0.0-alpha.37

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/dist/owl.cjs.js CHANGED
@@ -823,7 +823,7 @@ function assertType(value, validation, errorMessage = "Value does not match the
823
823
  ${issueStrings}`);
824
824
  }
825
825
  }
826
- function createContext(issues, value, path, parent) {
826
+ function createContext(issues, value, path, parent, depthOffset = 1) {
827
827
  return {
828
828
  issueDepth: 0,
829
829
  path,
@@ -844,11 +844,11 @@ function createContext(issues, value, path, parent) {
844
844
  validate(type) {
845
845
  type(this);
846
846
  if (!this.isValid && parent) {
847
- parent.issueDepth = this.issueDepth + 1;
847
+ parent.issueDepth = this.issueDepth + depthOffset;
848
848
  }
849
849
  },
850
850
  withIssues(issues2) {
851
- return createContext(issues2, this.value, this.path, this);
851
+ return createContext(issues2, this.value, this.path, this, 0);
852
852
  },
853
853
  withKey(key) {
854
854
  return createContext(issues, this.value[key], this.path.concat(key), this);
@@ -868,21 +868,7 @@ var optionalSymbol = /* @__PURE__ */ Symbol("optional");
868
868
  function getDefault(type) {
869
869
  return typeof type === "function" ? type[defaultSymbol] : void 0;
870
870
  }
871
- function hasDefaultValue(type) {
872
- return typeof type === "function" && defaultSymbol in type;
873
- }
874
- function withDefault(type, value) {
875
- const validate = function validateWithDefault(context) {
876
- if (context.value === void 0) {
877
- return;
878
- }
879
- context.validate(type);
880
- };
881
- validate[defaultSymbol] = typeof value === "function" ? value : () => value;
882
- validate[innerTypeSymbol] = type;
883
- return validate;
884
- }
885
- function makeOptional(type) {
871
+ function makeOptional(type, value) {
886
872
  const validate = function validateOptional(context) {
887
873
  if (context.value === void 0) {
888
874
  return;
@@ -891,14 +877,16 @@ function makeOptional(type) {
891
877
  };
892
878
  validate[optionalSymbol] = true;
893
879
  validate[innerTypeSymbol] = type;
880
+ if (value !== void 0) {
881
+ validate[defaultSymbol] = typeof value === "function" ? value : () => value;
882
+ }
894
883
  return validate;
895
884
  }
896
885
  function isOptionalType(type) {
897
886
  return typeof type === "function" && optionalSymbol in type;
898
887
  }
899
888
  function makeType(validate) {
900
- validate.default = (value) => withDefault(validate, value);
901
- validate.optional = () => makeOptional(validate);
889
+ validate.optional = (value) => makeOptional(validate, value);
902
890
  return validate;
903
891
  }
904
892
  function applyDefaults(value, type) {
@@ -1068,7 +1056,7 @@ function validateObject(context, schema, isStrict) {
1068
1056
  const missingKeys = [];
1069
1057
  for (const key of keys) {
1070
1058
  if (context.value[key] === void 0) {
1071
- if (!isOptionalType(shape[key]) && !hasDefaultValue(shape[key])) {
1059
+ if (!isOptionalType(shape[key])) {
1072
1060
  missingKeys.push(key);
1073
1061
  }
1074
1062
  continue;
@@ -1080,8 +1068,7 @@ function validateObject(context, schema, isStrict) {
1080
1068
  if (missingKeys.length) {
1081
1069
  context.addIssue({
1082
1070
  message: "object value has missing keys",
1083
- missingKeys,
1084
- expectedKeys: keys
1071
+ missingKeys
1085
1072
  });
1086
1073
  }
1087
1074
  if (isStrict) {
@@ -1094,8 +1081,7 @@ function validateObject(context, schema, isStrict) {
1094
1081
  if (unknownKeys.length) {
1095
1082
  context.addIssue({
1096
1083
  message: "object value has unknown keys",
1097
- unknownKeys,
1098
- expectedKeys: keys
1084
+ unknownKeys
1099
1085
  });
1100
1086
  }
1101
1087
  }
@@ -1543,7 +1529,7 @@ function markup(valueOrStrings, ...placeholders) {
1543
1529
  }
1544
1530
 
1545
1531
  // ../owl-runtime/dist/owl-runtime.es.js
1546
- var version = "3.0.0-alpha.35";
1532
+ var version = "3.0.0-alpha.37";
1547
1533
  var fibersInError = /* @__PURE__ */ new WeakMap();
1548
1534
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1549
1535
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -3439,6 +3425,11 @@ var ComponentNode = class extends Scope {
3439
3425
  willPatch = [];
3440
3426
  patched = [];
3441
3427
  signalComputation;
3428
+ // t-ref signals bound to an element hosted by this component, mapped to their
3429
+ // atom (so the element can be read without subscribing). Swept by isConnected
3430
+ // after each patch and after this subtree is removed, to unset a ref pointing
3431
+ // at a bulk-removed element (slot host, enclosing t-if) — see sweepRefs.
3432
+ trackedRefs = null;
3442
3433
  constructor(C, props2, app, parent, parentKey) {
3443
3434
  super(app);
3444
3435
  this.parent = parent;
@@ -3547,9 +3538,15 @@ var ComponentNode = class extends Scope {
3547
3538
  }
3548
3539
  destroy() {
3549
3540
  let shouldRemove = this.status === STATUS.MOUNTED;
3550
- this._destroy();
3541
+ removalDepth++;
3542
+ try {
3543
+ this._destroy();
3544
+ } finally {
3545
+ removalDepth--;
3546
+ }
3551
3547
  if (shouldRemove) {
3552
3548
  this.bdom.remove();
3549
+ sweepRemovedRefs();
3553
3550
  }
3554
3551
  }
3555
3552
  _destroy() {
@@ -3559,12 +3556,42 @@ var ComponentNode = class extends Scope {
3559
3556
  cb.call(component);
3560
3557
  }
3561
3558
  }
3559
+ if (removalDepth && this.trackedRefs) {
3560
+ (removed ||= []).push(this);
3561
+ }
3562
3562
  for (let childKey in this.children) {
3563
3563
  this.children[childKey]._destroy();
3564
3564
  }
3565
3565
  this.finalize((e) => handleError({ error: e, node: this }));
3566
3566
  disposeComputation(this.signalComputation);
3567
3567
  }
3568
+ /**
3569
+ * Unset any tracked t-ref whose element is no longer in the document, and stop
3570
+ * tracking it (createRef re-registers it on the next render if the element
3571
+ * comes back). `isConnected` is the discriminator: a ref the block's own
3572
+ * remove() failed to clear (bulk removal) points at a detached element and is
3573
+ * cleared, while a ref a surviving sibling just took over (t-if/t-else with a
3574
+ * shared signal) points at a still-connected element and is left alone.
3575
+ *
3576
+ * Called after this component's dom settles: at the tail of `_patch` (before
3577
+ * user `onPatched`), so an element removed in place is caught, and — for the
3578
+ * nodes collected during `_destroy` — after a removed subtree is detached.
3579
+ */
3580
+ sweepRefs() {
3581
+ const refs = this.trackedRefs;
3582
+ if (!refs) {
3583
+ return;
3584
+ }
3585
+ for (const [ref2, atom] of refs) {
3586
+ const el = atom.value;
3587
+ if (!el) {
3588
+ refs.delete(ref2);
3589
+ } else if (!el.isConnected) {
3590
+ ref2.set(null);
3591
+ refs.delete(ref2);
3592
+ }
3593
+ }
3594
+ }
3568
3595
  /**
3569
3596
  * Finds a child that has dom that is not yet updated, and update it. This
3570
3597
  * method is meant to be used only in the context of repatching the dom after
@@ -3580,7 +3607,14 @@ var ComponentNode = class extends Scope {
3580
3607
  child.updateDom();
3581
3608
  }
3582
3609
  } else {
3583
- this.bdom.patch(this.fiber.bdom, false);
3610
+ removalDepth++;
3611
+ try {
3612
+ this.bdom.patch(this.fiber.bdom, false);
3613
+ } finally {
3614
+ removalDepth--;
3615
+ }
3616
+ this.sweepRefs();
3617
+ sweepRemovedRefs();
3584
3618
  this.fiber.appliedToDom = true;
3585
3619
  this.fiber = null;
3586
3620
  }
@@ -3607,6 +3641,14 @@ var ComponentNode = class extends Scope {
3607
3641
  moveBeforeVNode(other, afterNode) {
3608
3642
  this.bdom.moveBeforeVNode(other ? other.bdom : null, afterNode);
3609
3643
  }
3644
+ /**
3645
+ * Register a t-ref signal bound to an element this component hosts, so its
3646
+ * lifecycle can clear it (see sweepRefs / _destroy). Idempotent — re-tracking
3647
+ * the same signal on each render just refreshes its atom.
3648
+ */
3649
+ trackRef(ref2, atom) {
3650
+ (this.trackedRefs ||= /* @__PURE__ */ new Map()).set(ref2, atom);
3651
+ }
3610
3652
  patch() {
3611
3653
  if (this.fiber && this.fiber.parent) {
3612
3654
  this._patch();
@@ -3620,7 +3662,14 @@ var ComponentNode = class extends Scope {
3620
3662
  }
3621
3663
  const fiber = this.fiber;
3622
3664
  this.children = fiber.childrenMap;
3623
- this.bdom.patch(fiber.bdom, hasChildren);
3665
+ removalDepth++;
3666
+ try {
3667
+ this.bdom.patch(fiber.bdom, hasChildren);
3668
+ } finally {
3669
+ removalDepth--;
3670
+ }
3671
+ this.sweepRefs();
3672
+ sweepRemovedRefs();
3624
3673
  fiber.appliedToDom = true;
3625
3674
  this.fiber = null;
3626
3675
  }
@@ -3631,6 +3680,17 @@ var ComponentNode = class extends Scope {
3631
3680
  this.bdom.remove();
3632
3681
  }
3633
3682
  };
3683
+ var removalDepth = 0;
3684
+ var removed = null;
3685
+ function sweepRemovedRefs() {
3686
+ if (removalDepth === 0 && removed) {
3687
+ const nodes = removed;
3688
+ removed = null;
3689
+ for (let i = 0; i < nodes.length; i++) {
3690
+ nodes[i].sweepRefs();
3691
+ }
3692
+ }
3693
+ }
3634
3694
  function getComponentScope() {
3635
3695
  const scope = useScope();
3636
3696
  if (!(scope instanceof ComponentNode)) {
@@ -3734,7 +3794,7 @@ var Component = class {
3734
3794
  }
3735
3795
  };
3736
3796
  var ObjectCreate = Object.create;
3737
- function withDefault2(value, defaultValue) {
3797
+ function withDefault(value, defaultValue) {
3738
3798
  return value === void 0 || value === null || value === false ? defaultValue : value;
3739
3799
  }
3740
3800
  function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
@@ -3833,7 +3893,7 @@ function safeOutput(value, defaultValue) {
3833
3893
  }
3834
3894
  return toggler(safeKey, block);
3835
3895
  }
3836
- function createRef(ref2) {
3896
+ function createRef(ref2, node) {
3837
3897
  if (!ref2) {
3838
3898
  throw new OwlError(`Ref is undefined or null`);
3839
3899
  }
@@ -3848,6 +3908,9 @@ function createRef(ref2) {
3848
3908
  remove2 = atom ? (prevEl) => {
3849
3909
  if (atom.value === prevEl) ref2.set(null);
3850
3910
  } : () => ref2.set(null);
3911
+ if (atom) {
3912
+ node.trackRef(ref2, atom);
3913
+ }
3851
3914
  } else {
3852
3915
  throw new OwlError(
3853
3916
  `Ref should implement either a 'set' function or 'add' and 'delete' functions`
@@ -4020,7 +4083,7 @@ function callTemplate(subTemplate, owner, app, ctx, parent, key) {
4020
4083
  return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
4021
4084
  }
4022
4085
  var helpers = {
4023
- withDefault: withDefault2,
4086
+ withDefault,
4024
4087
  zero: /* @__PURE__ */ Symbol("zero"),
4025
4088
  callSlot,
4026
4089
  withKey,
@@ -4482,7 +4545,7 @@ var ErrorBoundary = class extends Component {
4482
4545
  <t t-call-slot="default"/>
4483
4546
  </t>
4484
4547
  `;
4485
- props = props({ error: types2.signal().default(() => signal(null)) });
4548
+ props = props({ error: types2.signal().optional(() => signal(null)) });
4486
4549
  setup() {
4487
4550
  onError((e) => this.props.error.set(e));
4488
4551
  }
@@ -4599,8 +4662,8 @@ var blockDom = {
4599
4662
  };
4600
4663
  var __info__ = {
4601
4664
  version: App.version,
4602
- date: "2026-06-11T09:51:14.980Z",
4603
- hash: "739fc964",
4665
+ date: "2026-06-19T13:15:04.793Z",
4666
+ hash: "18550141",
4604
4667
  url: "https://github.com/odoo/owl"
4605
4668
  };
4606
4669
 
@@ -6131,7 +6194,7 @@ ${code}`;
6131
6194
  if (ast.ref) {
6132
6195
  const refExpr = compileExpr(ast.ref);
6133
6196
  this.helpers.add("createRef");
6134
- const setRefStr = `createRef(${refExpr})`;
6197
+ const setRefStr = `createRef(${refExpr}, node)`;
6135
6198
  const idx = block.insertData(setRefStr, "ref");
6136
6199
  attrs["block-ref"] = String(idx);
6137
6200
  }
package/dist/owl.es.js CHANGED
@@ -745,7 +745,7 @@ function assertType(value, validation, errorMessage = "Value does not match the
745
745
  ${issueStrings}`);
746
746
  }
747
747
  }
748
- function createContext(issues, value, path, parent) {
748
+ function createContext(issues, value, path, parent, depthOffset = 1) {
749
749
  return {
750
750
  issueDepth: 0,
751
751
  path,
@@ -766,11 +766,11 @@ function createContext(issues, value, path, parent) {
766
766
  validate(type) {
767
767
  type(this);
768
768
  if (!this.isValid && parent) {
769
- parent.issueDepth = this.issueDepth + 1;
769
+ parent.issueDepth = this.issueDepth + depthOffset;
770
770
  }
771
771
  },
772
772
  withIssues(issues2) {
773
- return createContext(issues2, this.value, this.path, this);
773
+ return createContext(issues2, this.value, this.path, this, 0);
774
774
  },
775
775
  withKey(key) {
776
776
  return createContext(issues, this.value[key], this.path.concat(key), this);
@@ -790,21 +790,7 @@ var optionalSymbol = /* @__PURE__ */ Symbol("optional");
790
790
  function getDefault(type) {
791
791
  return typeof type === "function" ? type[defaultSymbol] : void 0;
792
792
  }
793
- function hasDefaultValue(type) {
794
- return typeof type === "function" && defaultSymbol in type;
795
- }
796
- function withDefault(type, value) {
797
- const validate = function validateWithDefault(context) {
798
- if (context.value === void 0) {
799
- return;
800
- }
801
- context.validate(type);
802
- };
803
- validate[defaultSymbol] = typeof value === "function" ? value : () => value;
804
- validate[innerTypeSymbol] = type;
805
- return validate;
806
- }
807
- function makeOptional(type) {
793
+ function makeOptional(type, value) {
808
794
  const validate = function validateOptional(context) {
809
795
  if (context.value === void 0) {
810
796
  return;
@@ -813,14 +799,16 @@ function makeOptional(type) {
813
799
  };
814
800
  validate[optionalSymbol] = true;
815
801
  validate[innerTypeSymbol] = type;
802
+ if (value !== void 0) {
803
+ validate[defaultSymbol] = typeof value === "function" ? value : () => value;
804
+ }
816
805
  return validate;
817
806
  }
818
807
  function isOptionalType(type) {
819
808
  return typeof type === "function" && optionalSymbol in type;
820
809
  }
821
810
  function makeType(validate) {
822
- validate.default = (value) => withDefault(validate, value);
823
- validate.optional = () => makeOptional(validate);
811
+ validate.optional = (value) => makeOptional(validate, value);
824
812
  return validate;
825
813
  }
826
814
  function applyDefaults(value, type) {
@@ -990,7 +978,7 @@ function validateObject(context, schema, isStrict) {
990
978
  const missingKeys = [];
991
979
  for (const key of keys) {
992
980
  if (context.value[key] === void 0) {
993
- if (!isOptionalType(shape[key]) && !hasDefaultValue(shape[key])) {
981
+ if (!isOptionalType(shape[key])) {
994
982
  missingKeys.push(key);
995
983
  }
996
984
  continue;
@@ -1002,8 +990,7 @@ function validateObject(context, schema, isStrict) {
1002
990
  if (missingKeys.length) {
1003
991
  context.addIssue({
1004
992
  message: "object value has missing keys",
1005
- missingKeys,
1006
- expectedKeys: keys
993
+ missingKeys
1007
994
  });
1008
995
  }
1009
996
  if (isStrict) {
@@ -1016,8 +1003,7 @@ function validateObject(context, schema, isStrict) {
1016
1003
  if (unknownKeys.length) {
1017
1004
  context.addIssue({
1018
1005
  message: "object value has unknown keys",
1019
- unknownKeys,
1020
- expectedKeys: keys
1006
+ unknownKeys
1021
1007
  });
1022
1008
  }
1023
1009
  }
@@ -1465,7 +1451,7 @@ function markup(valueOrStrings, ...placeholders) {
1465
1451
  }
1466
1452
 
1467
1453
  // ../owl-runtime/dist/owl-runtime.es.js
1468
- var version = "3.0.0-alpha.35";
1454
+ var version = "3.0.0-alpha.37";
1469
1455
  var fibersInError = /* @__PURE__ */ new WeakMap();
1470
1456
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1471
1457
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -3361,6 +3347,11 @@ var ComponentNode = class extends Scope {
3361
3347
  willPatch = [];
3362
3348
  patched = [];
3363
3349
  signalComputation;
3350
+ // t-ref signals bound to an element hosted by this component, mapped to their
3351
+ // atom (so the element can be read without subscribing). Swept by isConnected
3352
+ // after each patch and after this subtree is removed, to unset a ref pointing
3353
+ // at a bulk-removed element (slot host, enclosing t-if) — see sweepRefs.
3354
+ trackedRefs = null;
3364
3355
  constructor(C, props2, app, parent, parentKey) {
3365
3356
  super(app);
3366
3357
  this.parent = parent;
@@ -3469,9 +3460,15 @@ var ComponentNode = class extends Scope {
3469
3460
  }
3470
3461
  destroy() {
3471
3462
  let shouldRemove = this.status === STATUS.MOUNTED;
3472
- this._destroy();
3463
+ removalDepth++;
3464
+ try {
3465
+ this._destroy();
3466
+ } finally {
3467
+ removalDepth--;
3468
+ }
3473
3469
  if (shouldRemove) {
3474
3470
  this.bdom.remove();
3471
+ sweepRemovedRefs();
3475
3472
  }
3476
3473
  }
3477
3474
  _destroy() {
@@ -3481,12 +3478,42 @@ var ComponentNode = class extends Scope {
3481
3478
  cb.call(component);
3482
3479
  }
3483
3480
  }
3481
+ if (removalDepth && this.trackedRefs) {
3482
+ (removed ||= []).push(this);
3483
+ }
3484
3484
  for (let childKey in this.children) {
3485
3485
  this.children[childKey]._destroy();
3486
3486
  }
3487
3487
  this.finalize((e) => handleError({ error: e, node: this }));
3488
3488
  disposeComputation(this.signalComputation);
3489
3489
  }
3490
+ /**
3491
+ * Unset any tracked t-ref whose element is no longer in the document, and stop
3492
+ * tracking it (createRef re-registers it on the next render if the element
3493
+ * comes back). `isConnected` is the discriminator: a ref the block's own
3494
+ * remove() failed to clear (bulk removal) points at a detached element and is
3495
+ * cleared, while a ref a surviving sibling just took over (t-if/t-else with a
3496
+ * shared signal) points at a still-connected element and is left alone.
3497
+ *
3498
+ * Called after this component's dom settles: at the tail of `_patch` (before
3499
+ * user `onPatched`), so an element removed in place is caught, and — for the
3500
+ * nodes collected during `_destroy` — after a removed subtree is detached.
3501
+ */
3502
+ sweepRefs() {
3503
+ const refs = this.trackedRefs;
3504
+ if (!refs) {
3505
+ return;
3506
+ }
3507
+ for (const [ref2, atom] of refs) {
3508
+ const el = atom.value;
3509
+ if (!el) {
3510
+ refs.delete(ref2);
3511
+ } else if (!el.isConnected) {
3512
+ ref2.set(null);
3513
+ refs.delete(ref2);
3514
+ }
3515
+ }
3516
+ }
3490
3517
  /**
3491
3518
  * Finds a child that has dom that is not yet updated, and update it. This
3492
3519
  * method is meant to be used only in the context of repatching the dom after
@@ -3502,7 +3529,14 @@ var ComponentNode = class extends Scope {
3502
3529
  child.updateDom();
3503
3530
  }
3504
3531
  } else {
3505
- this.bdom.patch(this.fiber.bdom, false);
3532
+ removalDepth++;
3533
+ try {
3534
+ this.bdom.patch(this.fiber.bdom, false);
3535
+ } finally {
3536
+ removalDepth--;
3537
+ }
3538
+ this.sweepRefs();
3539
+ sweepRemovedRefs();
3506
3540
  this.fiber.appliedToDom = true;
3507
3541
  this.fiber = null;
3508
3542
  }
@@ -3529,6 +3563,14 @@ var ComponentNode = class extends Scope {
3529
3563
  moveBeforeVNode(other, afterNode) {
3530
3564
  this.bdom.moveBeforeVNode(other ? other.bdom : null, afterNode);
3531
3565
  }
3566
+ /**
3567
+ * Register a t-ref signal bound to an element this component hosts, so its
3568
+ * lifecycle can clear it (see sweepRefs / _destroy). Idempotent — re-tracking
3569
+ * the same signal on each render just refreshes its atom.
3570
+ */
3571
+ trackRef(ref2, atom) {
3572
+ (this.trackedRefs ||= /* @__PURE__ */ new Map()).set(ref2, atom);
3573
+ }
3532
3574
  patch() {
3533
3575
  if (this.fiber && this.fiber.parent) {
3534
3576
  this._patch();
@@ -3542,7 +3584,14 @@ var ComponentNode = class extends Scope {
3542
3584
  }
3543
3585
  const fiber = this.fiber;
3544
3586
  this.children = fiber.childrenMap;
3545
- this.bdom.patch(fiber.bdom, hasChildren);
3587
+ removalDepth++;
3588
+ try {
3589
+ this.bdom.patch(fiber.bdom, hasChildren);
3590
+ } finally {
3591
+ removalDepth--;
3592
+ }
3593
+ this.sweepRefs();
3594
+ sweepRemovedRefs();
3546
3595
  fiber.appliedToDom = true;
3547
3596
  this.fiber = null;
3548
3597
  }
@@ -3553,6 +3602,17 @@ var ComponentNode = class extends Scope {
3553
3602
  this.bdom.remove();
3554
3603
  }
3555
3604
  };
3605
+ var removalDepth = 0;
3606
+ var removed = null;
3607
+ function sweepRemovedRefs() {
3608
+ if (removalDepth === 0 && removed) {
3609
+ const nodes = removed;
3610
+ removed = null;
3611
+ for (let i = 0; i < nodes.length; i++) {
3612
+ nodes[i].sweepRefs();
3613
+ }
3614
+ }
3615
+ }
3556
3616
  function getComponentScope() {
3557
3617
  const scope = useScope();
3558
3618
  if (!(scope instanceof ComponentNode)) {
@@ -3656,7 +3716,7 @@ var Component = class {
3656
3716
  }
3657
3717
  };
3658
3718
  var ObjectCreate = Object.create;
3659
- function withDefault2(value, defaultValue) {
3719
+ function withDefault(value, defaultValue) {
3660
3720
  return value === void 0 || value === null || value === false ? defaultValue : value;
3661
3721
  }
3662
3722
  function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
@@ -3755,7 +3815,7 @@ function safeOutput(value, defaultValue) {
3755
3815
  }
3756
3816
  return toggler(safeKey, block);
3757
3817
  }
3758
- function createRef(ref2) {
3818
+ function createRef(ref2, node) {
3759
3819
  if (!ref2) {
3760
3820
  throw new OwlError(`Ref is undefined or null`);
3761
3821
  }
@@ -3770,6 +3830,9 @@ function createRef(ref2) {
3770
3830
  remove2 = atom ? (prevEl) => {
3771
3831
  if (atom.value === prevEl) ref2.set(null);
3772
3832
  } : () => ref2.set(null);
3833
+ if (atom) {
3834
+ node.trackRef(ref2, atom);
3835
+ }
3773
3836
  } else {
3774
3837
  throw new OwlError(
3775
3838
  `Ref should implement either a 'set' function or 'add' and 'delete' functions`
@@ -3942,7 +4005,7 @@ function callTemplate(subTemplate, owner, app, ctx, parent, key) {
3942
4005
  return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
3943
4006
  }
3944
4007
  var helpers = {
3945
- withDefault: withDefault2,
4008
+ withDefault,
3946
4009
  zero: /* @__PURE__ */ Symbol("zero"),
3947
4010
  callSlot,
3948
4011
  withKey,
@@ -4404,7 +4467,7 @@ var ErrorBoundary = class extends Component {
4404
4467
  <t t-call-slot="default"/>
4405
4468
  </t>
4406
4469
  `;
4407
- props = props({ error: types2.signal().default(() => signal(null)) });
4470
+ props = props({ error: types2.signal().optional(() => signal(null)) });
4408
4471
  setup() {
4409
4472
  onError((e) => this.props.error.set(e));
4410
4473
  }
@@ -4521,8 +4584,8 @@ var blockDom = {
4521
4584
  };
4522
4585
  var __info__ = {
4523
4586
  version: App.version,
4524
- date: "2026-06-11T09:51:14.980Z",
4525
- hash: "739fc964",
4587
+ date: "2026-06-19T13:15:04.793Z",
4588
+ hash: "18550141",
4526
4589
  url: "https://github.com/odoo/owl"
4527
4590
  };
4528
4591
 
@@ -6053,7 +6116,7 @@ ${code}`;
6053
6116
  if (ast.ref) {
6054
6117
  const refExpr = compileExpr(ast.ref);
6055
6118
  this.helpers.add("createRef");
6056
- const setRefStr = `createRef(${refExpr})`;
6119
+ const setRefStr = `createRef(${refExpr}, node)`;
6057
6120
  const idx = block.insertData(setRefStr, "ref");
6058
6121
  attrs["block-ref"] = String(idx);
6059
6122
  }