@odoo/owl 3.0.0-alpha.28 → 3.0.0-alpha.29

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/README.md ADDED
@@ -0,0 +1,106 @@
1
+ <h1 align="center">🦉 <a href="https://odoo.github.io/owl/">Owl</a> 🦉</h1>
2
+
3
+ <p align="center">
4
+ <strong>A modern, lightweight UI framework for applications that scale</strong>
5
+ </p>
6
+
7
+ [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
8
+ [![npm version](https://badge.fury.io/js/@odoo%2Fowl.svg)](https://badge.fury.io/js/@odoo%2Fowl)
9
+ [![Downloads](https://img.shields.io/npm/dm/@odoo%2Fowl.svg)](https://www.npmjs.com/package/@odoo/owl)
10
+
11
+ ---
12
+
13
+ > **Owl 3.0.0 Alpha:** This is an alpha release. The API and features are subject to change without notice.
14
+
15
+ ---
16
+
17
+ ## Try it now
18
+
19
+ The fastest way to discover Owl is the **[online playground](https://odoo.github.io/owl/playground)**.
20
+ It features interactive examples, a live editor, and showcases all major features:
21
+ reactivity, components, plugins, and more. It also includes **guided tutorials**
22
+ and is the recommended way to learn about Owl.
23
+
24
+ ## What is Owl?
25
+
26
+ Owl is a modern UI framework (~30kb gzipped, zero dependencies) written in TypeScript,
27
+ built by [Odoo](https://www.odoo.com/). It powers Odoo's web client, one of the largest
28
+ open-source business applications, but is equally suited for small projects and prototypes.
29
+
30
+ Key features:
31
+
32
+ - **Signal-based reactivity** — Explicit, composable, and debuggable state management
33
+ - **Plugin system** — Type-safe, composable sharing of state and services
34
+ - **Class-based components** — Familiar OOP patterns with ES6 classes
35
+ - **Declarative templates** — XML templates with a clean syntax
36
+ - **Async rendering** — Concurrent mode for smooth user experiences
37
+
38
+ ## Quick Example
39
+
40
+ ```javascript
41
+ import { Component, signal, computed, mount, xml } from "@odoo/owl";
42
+
43
+ class TodoList extends Component {
44
+ static template = xml`
45
+ <input placeholder="Add todo..." t-on-keydown="this.onKeydown"/>
46
+ <ul>
47
+ <t t-foreach="this.todos()" t-as="todo" t-key="todo.id">
48
+ <li t-att-class="{ done: todo.done }">
49
+ <input type="checkbox" t-model="todo.done"/>
50
+ <t t-out="todo.text"/>
51
+ </li>
52
+ </t>
53
+ </ul>
54
+ <p t-if="this.remaining() > 0">
55
+ <t t-out="this.remaining()"/> item(s) remaining
56
+ </p>`;
57
+
58
+ todos = signal.Array([
59
+ { id: 1, text: "Learn Owl", done: false },
60
+ { id: 2, text: "Build something", done: false },
61
+ ]);
62
+
63
+ remaining = computed(() => this.todos().filter((t) => !t.done).length);
64
+
65
+ onKeydown(ev) {
66
+ if (ev.key === "Enter" && ev.target.value) {
67
+ this.todos.push({
68
+ id: Date.now(),
69
+ text: ev.target.value,
70
+ done: false,
71
+ });
72
+ ev.target.value = "";
73
+ }
74
+ }
75
+ }
76
+
77
+ mount(TodoList, document.body);
78
+ ```
79
+
80
+ This example demonstrates Owl's reactivity: `todos` is a signal, `remaining`
81
+ is a computed value that updates automatically, and the UI reacts to changes
82
+ without manual subscription management.
83
+
84
+ ## Documentation
85
+
86
+ The full documentation is available at **[odoo.github.io/owl/documentation](https://odoo.github.io/owl/documentation/)**.
87
+
88
+ For the Owl 2 documentation, see the [owl-2.x branch](https://github.com/odoo/owl/tree/owl-2.x).
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ npm install @odoo/owl
94
+ ```
95
+
96
+ Or download directly: [latest release](https://github.com/odoo/owl/releases/latest)
97
+
98
+ ## Devtools
99
+
100
+ The Owl devtools extension helps debug your applications with component tree
101
+ inspection, state visualization, and performance profiling. Download it from
102
+ the [releases page](https://github.com/odoo/owl/releases/latest).
103
+
104
+ ## License
105
+
106
+ Owl is released under the [LGPL v3](https://www.gnu.org/licenses/lgpl-3.0) license.
package/dist/owl.cjs.js CHANGED
@@ -75,7 +75,7 @@ __export(index_exports, {
75
75
  module.exports = __toCommonJS(index_exports);
76
76
 
77
77
  // ../owl-runtime/dist/owl-runtime.es.js
78
- var version = "3.0.0-alpha.28";
78
+ var version = "3.0.0-alpha.29";
79
79
  var OwlError = class extends Error {
80
80
  cause;
81
81
  };
@@ -629,7 +629,7 @@ function buildSignal(value, set) {
629
629
  };
630
630
  return readSignal;
631
631
  }
632
- function invalidateSignal(signal22) {
632
+ function triggerSignal(signal22) {
633
633
  if (typeof signal22 !== "function" || signal22[atomSymbol]?.type !== "signal") {
634
634
  throw new OwlError(`Value is not a signal (${signal22})`);
635
635
  }
@@ -650,7 +650,7 @@ function signalSet(initialValue) {
650
650
  function signal(value) {
651
651
  return buildSignal(value, (atom) => atom.value);
652
652
  }
653
- signal.invalidate = invalidateSignal;
653
+ signal.trigger = triggerSignal;
654
654
  signal.Array = signalArray;
655
655
  signal.Map = signalMap;
656
656
  signal.Object = signalObject;
@@ -1283,6 +1283,9 @@ function invokeErrorHandlers(node, error, finalize, markFibers) {
1283
1283
  }
1284
1284
  function forwardErrorToParent(boundary) {
1285
1285
  return (error, finalize) => {
1286
+ if (boundary.app.destroyed) {
1287
+ throw error;
1288
+ }
1286
1289
  const { handled } = invokeErrorHandlers(boundary, error, finalize, false);
1287
1290
  if (!handled) {
1288
1291
  boundary.app._handleError(finalize());
@@ -1294,6 +1297,9 @@ function handleError(params) {
1294
1297
  let node = "node" in params ? params.node : params.fiber.node;
1295
1298
  const fiber = "fiber" in params ? params.fiber : node.fiber;
1296
1299
  const app = node.app;
1300
+ if (app.destroyed) {
1301
+ throw error;
1302
+ }
1297
1303
  if (fiber) {
1298
1304
  let current = fiber;
1299
1305
  do {
@@ -1308,6 +1314,9 @@ function handleError(params) {
1308
1314
  app.destroy();
1309
1315
  } catch (e) {
1310
1316
  }
1317
+ if (error instanceof OwlError) {
1318
+ return error;
1319
+ }
1311
1320
  return Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
1312
1321
  cause: error
1313
1322
  });
@@ -1542,17 +1551,48 @@ function toKebabCase(prop2) {
1542
1551
  CSS_PROP_CACHE[prop2] = result;
1543
1552
  return result;
1544
1553
  }
1554
+ var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1555
+ function setStyleProp(style, prop2, value) {
1556
+ if (IMPORTANT_RE.test(value)) {
1557
+ style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1558
+ } else {
1559
+ style.setProperty(prop2, value);
1560
+ }
1561
+ }
1545
1562
  function toStyleObj(expr) {
1546
1563
  const result = {};
1547
1564
  switch (typeof expr) {
1548
1565
  case "string": {
1549
- const str = trim.call(expr);
1550
- if (!str) {
1551
- return {};
1552
- }
1553
- const parts = str.split(";");
1554
- for (let part of parts) {
1555
- part = trim.call(part);
1566
+ const str = expr;
1567
+ const len = str.length;
1568
+ let i = 0;
1569
+ while (i < len) {
1570
+ const start = i;
1571
+ let depth = 0;
1572
+ let quote = 0;
1573
+ while (i < len) {
1574
+ const c = str.charCodeAt(i);
1575
+ if (quote) {
1576
+ if (c === 92) {
1577
+ i += 2;
1578
+ continue;
1579
+ }
1580
+ if (c === quote) {
1581
+ quote = 0;
1582
+ }
1583
+ } else if (c === 34 || c === 39) {
1584
+ quote = c;
1585
+ } else if (c === 40) {
1586
+ depth++;
1587
+ } else if (c === 41) {
1588
+ if (depth > 0) depth--;
1589
+ } else if (c === 59 && depth === 0) {
1590
+ break;
1591
+ }
1592
+ i++;
1593
+ }
1594
+ const part = trim.call(str.slice(start, i));
1595
+ i++;
1556
1596
  if (!part) {
1557
1597
  continue;
1558
1598
  }
@@ -1604,7 +1644,7 @@ function setStyle(val) {
1604
1644
  val = val === "" ? {} : toStyleObj(val);
1605
1645
  const style = this.style;
1606
1646
  for (let prop2 in val) {
1607
- style.setProperty(prop2, val[prop2]);
1647
+ setStyleProp(style, prop2, val[prop2]);
1608
1648
  }
1609
1649
  }
1610
1650
  function updateStyle(val, oldVal) {
@@ -1618,7 +1658,7 @@ function updateStyle(val, oldVal) {
1618
1658
  }
1619
1659
  for (let prop2 in val) {
1620
1660
  if (val[prop2] !== oldVal[prop2]) {
1621
- style.setProperty(prop2, val[prop2]);
1661
+ setStyleProp(style, prop2, val[prop2]);
1622
1662
  }
1623
1663
  }
1624
1664
  if (!style.cssText) {
@@ -3859,6 +3899,7 @@ var App = class _App extends TemplateSet {
3859
3899
  scheduler = new Scheduler();
3860
3900
  roots = /* @__PURE__ */ new Set();
3861
3901
  pluginManager;
3902
+ destroyed = false;
3862
3903
  constructor(config3 = {}) {
3863
3904
  super(config3);
3864
3905
  this.name = config3.name || "";
@@ -3969,6 +4010,7 @@ var App = class _App extends TemplateSet {
3969
4010
  this.pluginManager.destroy();
3970
4011
  this.scheduler.processTasks();
3971
4012
  apps.delete(this);
4013
+ this.destroyed = true;
3972
4014
  }
3973
4015
  _handleError(error) {
3974
4016
  throw error;
@@ -4323,8 +4365,8 @@ var blockDom = {
4323
4365
  };
4324
4366
  var __info__ = {
4325
4367
  version: App.version,
4326
- date: "2026-04-22T14:24:13.298Z",
4327
- hash: "914a106c",
4368
+ date: "2026-04-23T11:09:22.692Z",
4369
+ hash: "2220e1dd",
4328
4370
  url: "https://github.com/odoo/owl"
4329
4371
  };
4330
4372
 
@@ -4687,7 +4729,7 @@ function buildSignal2(value, set) {
4687
4729
  };
4688
4730
  return readSignal;
4689
4731
  }
4690
- function invalidateSignal2(signal22) {
4732
+ function triggerSignal2(signal22) {
4691
4733
  if (typeof signal22 !== "function" || signal22[atomSymbol2]?.type !== "signal") {
4692
4734
  throw new OwlError2(`Value is not a signal (${signal22})`);
4693
4735
  }
@@ -4708,7 +4750,7 @@ function signalSet2(initialValue) {
4708
4750
  function signal2(value) {
4709
4751
  return buildSignal2(value, (atom) => atom.value);
4710
4752
  }
4711
- signal2.invalidate = invalidateSignal2;
4753
+ signal2.trigger = triggerSignal2;
4712
4754
  signal2.Array = signalArray2;
4713
4755
  signal2.Map = signalMap2;
4714
4756
  signal2.Object = signalObject2;
package/dist/owl.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // ../owl-runtime/dist/owl-runtime.es.js
2
- var version = "3.0.0-alpha.28";
2
+ var version = "3.0.0-alpha.29";
3
3
  var OwlError = class extends Error {
4
4
  cause;
5
5
  };
@@ -553,7 +553,7 @@ function buildSignal(value, set) {
553
553
  };
554
554
  return readSignal;
555
555
  }
556
- function invalidateSignal(signal22) {
556
+ function triggerSignal(signal22) {
557
557
  if (typeof signal22 !== "function" || signal22[atomSymbol]?.type !== "signal") {
558
558
  throw new OwlError(`Value is not a signal (${signal22})`);
559
559
  }
@@ -574,7 +574,7 @@ function signalSet(initialValue) {
574
574
  function signal(value) {
575
575
  return buildSignal(value, (atom) => atom.value);
576
576
  }
577
- signal.invalidate = invalidateSignal;
577
+ signal.trigger = triggerSignal;
578
578
  signal.Array = signalArray;
579
579
  signal.Map = signalMap;
580
580
  signal.Object = signalObject;
@@ -1207,6 +1207,9 @@ function invokeErrorHandlers(node, error, finalize, markFibers) {
1207
1207
  }
1208
1208
  function forwardErrorToParent(boundary) {
1209
1209
  return (error, finalize) => {
1210
+ if (boundary.app.destroyed) {
1211
+ throw error;
1212
+ }
1210
1213
  const { handled } = invokeErrorHandlers(boundary, error, finalize, false);
1211
1214
  if (!handled) {
1212
1215
  boundary.app._handleError(finalize());
@@ -1218,6 +1221,9 @@ function handleError(params) {
1218
1221
  let node = "node" in params ? params.node : params.fiber.node;
1219
1222
  const fiber = "fiber" in params ? params.fiber : node.fiber;
1220
1223
  const app = node.app;
1224
+ if (app.destroyed) {
1225
+ throw error;
1226
+ }
1221
1227
  if (fiber) {
1222
1228
  let current = fiber;
1223
1229
  do {
@@ -1232,6 +1238,9 @@ function handleError(params) {
1232
1238
  app.destroy();
1233
1239
  } catch (e) {
1234
1240
  }
1241
+ if (error instanceof OwlError) {
1242
+ return error;
1243
+ }
1235
1244
  return Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
1236
1245
  cause: error
1237
1246
  });
@@ -1466,17 +1475,48 @@ function toKebabCase(prop2) {
1466
1475
  CSS_PROP_CACHE[prop2] = result;
1467
1476
  return result;
1468
1477
  }
1478
+ var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1479
+ function setStyleProp(style, prop2, value) {
1480
+ if (IMPORTANT_RE.test(value)) {
1481
+ style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1482
+ } else {
1483
+ style.setProperty(prop2, value);
1484
+ }
1485
+ }
1469
1486
  function toStyleObj(expr) {
1470
1487
  const result = {};
1471
1488
  switch (typeof expr) {
1472
1489
  case "string": {
1473
- const str = trim.call(expr);
1474
- if (!str) {
1475
- return {};
1476
- }
1477
- const parts = str.split(";");
1478
- for (let part of parts) {
1479
- part = trim.call(part);
1490
+ const str = expr;
1491
+ const len = str.length;
1492
+ let i = 0;
1493
+ while (i < len) {
1494
+ const start = i;
1495
+ let depth = 0;
1496
+ let quote = 0;
1497
+ while (i < len) {
1498
+ const c = str.charCodeAt(i);
1499
+ if (quote) {
1500
+ if (c === 92) {
1501
+ i += 2;
1502
+ continue;
1503
+ }
1504
+ if (c === quote) {
1505
+ quote = 0;
1506
+ }
1507
+ } else if (c === 34 || c === 39) {
1508
+ quote = c;
1509
+ } else if (c === 40) {
1510
+ depth++;
1511
+ } else if (c === 41) {
1512
+ if (depth > 0) depth--;
1513
+ } else if (c === 59 && depth === 0) {
1514
+ break;
1515
+ }
1516
+ i++;
1517
+ }
1518
+ const part = trim.call(str.slice(start, i));
1519
+ i++;
1480
1520
  if (!part) {
1481
1521
  continue;
1482
1522
  }
@@ -1528,7 +1568,7 @@ function setStyle(val) {
1528
1568
  val = val === "" ? {} : toStyleObj(val);
1529
1569
  const style = this.style;
1530
1570
  for (let prop2 in val) {
1531
- style.setProperty(prop2, val[prop2]);
1571
+ setStyleProp(style, prop2, val[prop2]);
1532
1572
  }
1533
1573
  }
1534
1574
  function updateStyle(val, oldVal) {
@@ -1542,7 +1582,7 @@ function updateStyle(val, oldVal) {
1542
1582
  }
1543
1583
  for (let prop2 in val) {
1544
1584
  if (val[prop2] !== oldVal[prop2]) {
1545
- style.setProperty(prop2, val[prop2]);
1585
+ setStyleProp(style, prop2, val[prop2]);
1546
1586
  }
1547
1587
  }
1548
1588
  if (!style.cssText) {
@@ -3783,6 +3823,7 @@ var App = class _App extends TemplateSet {
3783
3823
  scheduler = new Scheduler();
3784
3824
  roots = /* @__PURE__ */ new Set();
3785
3825
  pluginManager;
3826
+ destroyed = false;
3786
3827
  constructor(config3 = {}) {
3787
3828
  super(config3);
3788
3829
  this.name = config3.name || "";
@@ -3893,6 +3934,7 @@ var App = class _App extends TemplateSet {
3893
3934
  this.pluginManager.destroy();
3894
3935
  this.scheduler.processTasks();
3895
3936
  apps.delete(this);
3937
+ this.destroyed = true;
3896
3938
  }
3897
3939
  _handleError(error) {
3898
3940
  throw error;
@@ -4247,8 +4289,8 @@ var blockDom = {
4247
4289
  };
4248
4290
  var __info__ = {
4249
4291
  version: App.version,
4250
- date: "2026-04-22T14:24:13.298Z",
4251
- hash: "914a106c",
4292
+ date: "2026-04-23T11:09:22.692Z",
4293
+ hash: "2220e1dd",
4252
4294
  url: "https://github.com/odoo/owl"
4253
4295
  };
4254
4296
 
@@ -4611,7 +4653,7 @@ function buildSignal2(value, set) {
4611
4653
  };
4612
4654
  return readSignal;
4613
4655
  }
4614
- function invalidateSignal2(signal22) {
4656
+ function triggerSignal2(signal22) {
4615
4657
  if (typeof signal22 !== "function" || signal22[atomSymbol2]?.type !== "signal") {
4616
4658
  throw new OwlError2(`Value is not a signal (${signal22})`);
4617
4659
  }
@@ -4632,7 +4674,7 @@ function signalSet2(initialValue) {
4632
4674
  function signal2(value) {
4633
4675
  return buildSignal2(value, (atom) => atom.value);
4634
4676
  }
4635
- signal2.invalidate = invalidateSignal2;
4677
+ signal2.trigger = triggerSignal2;
4636
4678
  signal2.Array = signalArray2;
4637
4679
  signal2.Map = signalMap2;
4638
4680
  signal2.Object = signalObject2;
package/dist/owl.iife.js CHANGED
@@ -75,7 +75,7 @@ var owl = (() => {
75
75
  });
76
76
 
77
77
  // ../owl-runtime/dist/owl-runtime.es.js
78
- var version = "3.0.0-alpha.28";
78
+ var version = "3.0.0-alpha.29";
79
79
  var OwlError = class extends Error {
80
80
  cause;
81
81
  };
@@ -629,7 +629,7 @@ var owl = (() => {
629
629
  };
630
630
  return readSignal;
631
631
  }
632
- function invalidateSignal(signal22) {
632
+ function triggerSignal(signal22) {
633
633
  if (typeof signal22 !== "function" || signal22[atomSymbol]?.type !== "signal") {
634
634
  throw new OwlError(`Value is not a signal (${signal22})`);
635
635
  }
@@ -650,7 +650,7 @@ var owl = (() => {
650
650
  function signal(value) {
651
651
  return buildSignal(value, (atom) => atom.value);
652
652
  }
653
- signal.invalidate = invalidateSignal;
653
+ signal.trigger = triggerSignal;
654
654
  signal.Array = signalArray;
655
655
  signal.Map = signalMap;
656
656
  signal.Object = signalObject;
@@ -1283,6 +1283,9 @@ ${issueStrings}`);
1283
1283
  }
1284
1284
  function forwardErrorToParent(boundary) {
1285
1285
  return (error, finalize) => {
1286
+ if (boundary.app.destroyed) {
1287
+ throw error;
1288
+ }
1286
1289
  const { handled } = invokeErrorHandlers(boundary, error, finalize, false);
1287
1290
  if (!handled) {
1288
1291
  boundary.app._handleError(finalize());
@@ -1294,6 +1297,9 @@ ${issueStrings}`);
1294
1297
  let node = "node" in params ? params.node : params.fiber.node;
1295
1298
  const fiber = "fiber" in params ? params.fiber : node.fiber;
1296
1299
  const app = node.app;
1300
+ if (app.destroyed) {
1301
+ throw error;
1302
+ }
1297
1303
  if (fiber) {
1298
1304
  let current = fiber;
1299
1305
  do {
@@ -1308,6 +1314,9 @@ ${issueStrings}`);
1308
1314
  app.destroy();
1309
1315
  } catch (e) {
1310
1316
  }
1317
+ if (error instanceof OwlError) {
1318
+ return error;
1319
+ }
1311
1320
  return Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
1312
1321
  cause: error
1313
1322
  });
@@ -1542,17 +1551,48 @@ ${issueStrings}`);
1542
1551
  CSS_PROP_CACHE[prop2] = result;
1543
1552
  return result;
1544
1553
  }
1554
+ var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1555
+ function setStyleProp(style, prop2, value) {
1556
+ if (IMPORTANT_RE.test(value)) {
1557
+ style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1558
+ } else {
1559
+ style.setProperty(prop2, value);
1560
+ }
1561
+ }
1545
1562
  function toStyleObj(expr) {
1546
1563
  const result = {};
1547
1564
  switch (typeof expr) {
1548
1565
  case "string": {
1549
- const str = trim.call(expr);
1550
- if (!str) {
1551
- return {};
1552
- }
1553
- const parts = str.split(";");
1554
- for (let part of parts) {
1555
- part = trim.call(part);
1566
+ const str = expr;
1567
+ const len = str.length;
1568
+ let i = 0;
1569
+ while (i < len) {
1570
+ const start = i;
1571
+ let depth = 0;
1572
+ let quote = 0;
1573
+ while (i < len) {
1574
+ const c = str.charCodeAt(i);
1575
+ if (quote) {
1576
+ if (c === 92) {
1577
+ i += 2;
1578
+ continue;
1579
+ }
1580
+ if (c === quote) {
1581
+ quote = 0;
1582
+ }
1583
+ } else if (c === 34 || c === 39) {
1584
+ quote = c;
1585
+ } else if (c === 40) {
1586
+ depth++;
1587
+ } else if (c === 41) {
1588
+ if (depth > 0) depth--;
1589
+ } else if (c === 59 && depth === 0) {
1590
+ break;
1591
+ }
1592
+ i++;
1593
+ }
1594
+ const part = trim.call(str.slice(start, i));
1595
+ i++;
1556
1596
  if (!part) {
1557
1597
  continue;
1558
1598
  }
@@ -1604,7 +1644,7 @@ ${issueStrings}`);
1604
1644
  val = val === "" ? {} : toStyleObj(val);
1605
1645
  const style = this.style;
1606
1646
  for (let prop2 in val) {
1607
- style.setProperty(prop2, val[prop2]);
1647
+ setStyleProp(style, prop2, val[prop2]);
1608
1648
  }
1609
1649
  }
1610
1650
  function updateStyle(val, oldVal) {
@@ -1618,7 +1658,7 @@ ${issueStrings}`);
1618
1658
  }
1619
1659
  for (let prop2 in val) {
1620
1660
  if (val[prop2] !== oldVal[prop2]) {
1621
- style.setProperty(prop2, val[prop2]);
1661
+ setStyleProp(style, prop2, val[prop2]);
1622
1662
  }
1623
1663
  }
1624
1664
  if (!style.cssText) {
@@ -3859,6 +3899,7 @@ ${issueStrings}`);
3859
3899
  scheduler = new Scheduler();
3860
3900
  roots = /* @__PURE__ */ new Set();
3861
3901
  pluginManager;
3902
+ destroyed = false;
3862
3903
  constructor(config3 = {}) {
3863
3904
  super(config3);
3864
3905
  this.name = config3.name || "";
@@ -3969,6 +4010,7 @@ ${issueStrings}`);
3969
4010
  this.pluginManager.destroy();
3970
4011
  this.scheduler.processTasks();
3971
4012
  apps.delete(this);
4013
+ this.destroyed = true;
3972
4014
  }
3973
4015
  _handleError(error) {
3974
4016
  throw error;
@@ -4323,8 +4365,8 @@ ${issueStrings}`);
4323
4365
  };
4324
4366
  var __info__ = {
4325
4367
  version: App.version,
4326
- date: "2026-04-22T14:24:13.298Z",
4327
- hash: "914a106c",
4368
+ date: "2026-04-23T11:09:22.692Z",
4369
+ hash: "2220e1dd",
4328
4370
  url: "https://github.com/odoo/owl"
4329
4371
  };
4330
4372
 
@@ -4687,7 +4729,7 @@ ${issueStrings}`);
4687
4729
  };
4688
4730
  return readSignal;
4689
4731
  }
4690
- function invalidateSignal2(signal22) {
4732
+ function triggerSignal2(signal22) {
4691
4733
  if (typeof signal22 !== "function" || signal22[atomSymbol2]?.type !== "signal") {
4692
4734
  throw new OwlError2(`Value is not a signal (${signal22})`);
4693
4735
  }
@@ -4708,7 +4750,7 @@ ${issueStrings}`);
4708
4750
  function signal2(value) {
4709
4751
  return buildSignal2(value, (atom) => atom.value);
4710
4752
  }
4711
- signal2.invalidate = invalidateSignal2;
4753
+ signal2.trigger = triggerSignal2;
4712
4754
  signal2.Array = signalArray2;
4713
4755
  signal2.Map = signalMap2;
4714
4756
  signal2.Object = signalObject2;