@odoo/owl 3.0.0-alpha.32 → 3.0.0-alpha.34

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.iife.js CHANGED
@@ -39,7 +39,7 @@ var owl = (() => {
39
39
  batched: () => batched,
40
40
  blockDom: () => blockDom,
41
41
  computed: () => computed,
42
- config: () => config2,
42
+ config: () => config,
43
43
  effect: () => effect,
44
44
  getScope: () => getScope,
45
45
  globalTemplates: () => globalTemplates,
@@ -56,7 +56,6 @@ var owl = (() => {
56
56
  onWillUnmount: () => onWillUnmount,
57
57
  onWillUpdateProps: () => onWillUpdateProps,
58
58
  plugin: () => plugin,
59
- prop: () => prop,
60
59
  props: () => props,
61
60
  providePlugins: () => providePlugins,
62
61
  proxy: () => proxy,
@@ -65,7 +64,7 @@ var owl = (() => {
65
64
  toRaw: () => toRaw,
66
65
  types: () => types2,
67
66
  untrack: () => untrack,
68
- useApp: () => useApp,
67
+ useApp: () => useApp2,
69
68
  useEffect: () => useEffect,
70
69
  useListener: () => useListener,
71
70
  useScope: () => useScope,
@@ -191,13 +190,15 @@ var owl = (() => {
191
190
  sources.clear();
192
191
  }
193
192
  function disposeComputation(computation) {
194
- for (const source of computation.sources) {
193
+ const sources = computation.sources;
194
+ for (const source of sources) {
195
195
  source.observers.delete(computation);
196
- if ("compute" in source && source.isDerived && source.observers.size === 0) {
197
- disposeComputation(source);
196
+ const derived = source;
197
+ if (derived.isDerived && derived.observers.size === 0) {
198
+ disposeComputation(derived);
198
199
  }
199
200
  }
200
- computation.sources.clear();
201
+ sources.clear();
201
202
  computation.state = 1;
202
203
  }
203
204
  function markDownstream(computation) {
@@ -238,6 +239,7 @@ var owl = (() => {
238
239
  }
239
240
  var Scope = class {
240
241
  app;
242
+ pluginManager;
241
243
  status = STATUS.NEW;
242
244
  computations = [];
243
245
  willStart = [];
@@ -245,6 +247,7 @@ var owl = (() => {
245
247
  _destroyCbs = null;
246
248
  constructor(app) {
247
249
  this.app = app;
250
+ this.pluginManager = app.pluginManager;
248
251
  }
249
252
  /**
250
253
  * Pushes this scope on the stack for the duration of `callback`. Any code
@@ -688,14 +691,19 @@ var owl = (() => {
688
691
  }
689
692
  function effect(fn) {
690
693
  const computation = createComputation(() => {
691
- setComputation(void 0);
692
- unsubscribeEffect(computation);
693
- setComputation(computation);
694
+ if (computation.value || computation.observers.size) {
695
+ setComputation(void 0);
696
+ unsubscribeEffect(computation);
697
+ setComputation(computation);
698
+ } else {
699
+ removeSources(computation);
700
+ }
694
701
  return fn();
695
702
  }, false);
696
703
  getCurrentComputation()?.observers.add(computation);
697
704
  updateComputation(computation);
698
705
  return function cleanupEffect2() {
706
+ computation.state = 0;
699
707
  const previousComputation = getCurrentComputation();
700
708
  setComputation(void 0);
701
709
  unsubscribeEffect(computation);
@@ -707,7 +715,6 @@ var owl = (() => {
707
715
  cleanupEffect(effect2);
708
716
  for (const childEffect of effect2.observers) {
709
717
  childEffect.state = 0;
710
- removeSources(childEffect);
711
718
  unsubscribeEffect(childEffect);
712
719
  }
713
720
  effect2.observers.clear();
@@ -784,19 +791,27 @@ var owl = (() => {
784
791
  read.dispose = dispose;
785
792
  return read;
786
793
  }
794
+ function safeReplacer(knownObjects, _key, value) {
795
+ if (typeof value === "function") {
796
+ return value.name || "[Function]";
797
+ }
798
+ if (value && typeof value === "object") {
799
+ const ctor = value.constructor;
800
+ if (ctor && ctor !== Object && ctor !== Array) {
801
+ return `[Instance of ${ctor.name || "anonymous"}]`;
802
+ }
803
+ if (knownObjects.includes(value)) {
804
+ return `[Known object]`;
805
+ }
806
+ knownObjects.push(value);
807
+ }
808
+ return value;
809
+ }
787
810
  function assertType(value, validation, errorMessage = "Value does not match the type") {
788
811
  const issues = validateType(value, validation);
789
812
  if (issues.length) {
790
- const issueStrings = JSON.stringify(
791
- issues,
792
- (key, value2) => {
793
- if (typeof value2 === "function") {
794
- return value2.name;
795
- }
796
- return value2;
797
- },
798
- 2
799
- );
813
+ const knownObjects = [];
814
+ const issueStrings = JSON.stringify(issues, safeReplacer.bind(null, knownObjects), 2);
800
815
  throw new OwlError(`${errorMessage}
801
816
  ${issueStrings}`);
802
817
  }
@@ -812,7 +827,7 @@ ${issueStrings}`);
812
827
  addIssue(issue) {
813
828
  issues.push({
814
829
  received: this.value,
815
- path: this.path,
830
+ path: this.path.join(" > "),
816
831
  ...issue
817
832
  });
818
833
  },
@@ -937,15 +952,20 @@ ${issueStrings}`);
937
952
  return;
938
953
  }
939
954
  const isShape = !Array.isArray(schema);
940
- let shape = schema;
941
- if (Array.isArray(schema)) {
955
+ let shape;
956
+ let keys;
957
+ if (isShape) {
958
+ keys = Object.keys(schema);
959
+ shape = schema;
960
+ } else {
961
+ keys = schema;
942
962
  shape = {};
943
- for (const key of schema) {
963
+ for (const key of keys) {
944
964
  shape[key] = null;
945
965
  }
946
966
  }
947
967
  const missingKeys = [];
948
- for (const key in shape) {
968
+ for (const key of keys) {
949
969
  const property = key.endsWith("?") ? key.slice(0, -1) : key;
950
970
  if (context.value[property] === void 0) {
951
971
  if (!key.endsWith("?")) {
@@ -960,20 +980,22 @@ ${issueStrings}`);
960
980
  if (missingKeys.length) {
961
981
  context.addIssue({
962
982
  message: "object value has missing keys",
963
- missingKeys
983
+ missingKeys,
984
+ expectedKeys: keys
964
985
  });
965
986
  }
966
987
  if (isStrict) {
967
988
  const unknownKeys = [];
968
989
  for (const key in context.value) {
969
- if (!(key in shape) && !(`${key}?` in shape)) {
990
+ if (!keys.includes(key) && !(`${key}?` in shape)) {
970
991
  unknownKeys.push(key);
971
992
  }
972
993
  }
973
994
  if (unknownKeys.length) {
974
995
  context.addIssue({
975
996
  message: "object value has unknown keys",
976
- unknownKeys
997
+ unknownKeys,
998
+ expectedKeys: keys
977
999
  });
978
1000
  }
979
1001
  }
@@ -1179,6 +1201,13 @@ ${issueStrings}`);
1179
1201
  static set id(shadowId) {
1180
1202
  this._shadowId = shadowId;
1181
1203
  }
1204
+ // Plugins passed to `startPlugins` are started in batches of equal sequence,
1205
+ // ascending (lower first), like Resource/Registry. Each batch's onWillStart
1206
+ // callbacks fully settle before the next batch is instantiated, so
1207
+ // foundational plugins (low sequence) are ready before later plugins even
1208
+ // run their setup. Explicit `plugin(X)` dependencies bypass batching and
1209
+ // start immediately.
1210
+ static sequence = 50;
1182
1211
  __owl__;
1183
1212
  constructor(manager) {
1184
1213
  this.__owl__ = manager;
@@ -1189,14 +1218,17 @@ ${issueStrings}`);
1189
1218
  var PluginManager = class extends Scope {
1190
1219
  config;
1191
1220
  plugins;
1192
- // Resolves once all pending plugin willStart callbacks have settled. The
1193
- // scope transitions to MOUNTED as the last step of this chain. Consumers
1194
- // (App.mount, providePlugins) await this before treating the manager as
1195
- // ready. `willStart` itself is inherited from Scope.
1221
+ // Resolves once all batches of plugins have started and their willStart
1222
+ // callbacks have settled. The scope transitions to MOUNTED as the last step
1223
+ // of this chain. Consumers (the root's mount(), providePlugins) await this
1224
+ // before treating the manager as ready. `willStart` itself is inherited
1225
+ // from Scope.
1196
1226
  ready = Promise.resolve();
1227
+ hasPendingReady = false;
1197
1228
  constructor(app, options = {}) {
1198
1229
  super(app);
1199
1230
  this.config = options.config ?? {};
1231
+ this.pluginManager = this;
1200
1232
  if (options.parent) {
1201
1233
  const parent = options.parent;
1202
1234
  parent.onDestroy(() => this.destroy());
@@ -1233,24 +1265,61 @@ ${issueStrings}`);
1233
1265
  return plugin2;
1234
1266
  }
1235
1267
  startPlugins(pluginConstructors) {
1236
- scopeStack.push(this);
1237
- try {
1238
- for (const pluginConstructor of pluginConstructors) {
1239
- this.startPlugin(pluginConstructor);
1268
+ const fresh = pluginConstructors.filter((ctor) => {
1269
+ if (!ctor.id || this.plugins.hasOwnProperty(ctor.id)) {
1270
+ this.startPlugin(ctor);
1271
+ return false;
1240
1272
  }
1241
- } finally {
1242
- scopeStack.pop();
1273
+ return true;
1274
+ });
1275
+ if (!fresh.length) {
1276
+ return;
1243
1277
  }
1244
- const pending = this.willStart.splice(0);
1245
- if (pending.length) {
1246
- this.ready = Promise.all(pending.map((fn) => fn())).then(() => {
1247
- if (this.status < STATUS.MOUNTED) {
1248
- this.status = STATUS.MOUNTED;
1278
+ fresh.sort((p1, p2) => p1.sequence - p2.sequence);
1279
+ const batches = [];
1280
+ for (const ctor of fresh) {
1281
+ const batch = batches[batches.length - 1];
1282
+ if (batch && batch[0].sequence === ctor.sequence) {
1283
+ batch.push(ctor);
1284
+ } else {
1285
+ batches.push([ctor]);
1286
+ }
1287
+ }
1288
+ const startBatch = (batch) => {
1289
+ scopeStack.push(this);
1290
+ try {
1291
+ for (const ctor of batch) {
1292
+ this.startPlugin(ctor);
1249
1293
  }
1250
- });
1251
- } else if (this.status < STATUS.MOUNTED) {
1252
- this.status = STATUS.MOUNTED;
1294
+ } finally {
1295
+ scopeStack.pop();
1296
+ }
1297
+ const pending = this.willStart.splice(0);
1298
+ return pending.length ? Promise.all(pending.map((fn) => fn())) : null;
1299
+ };
1300
+ let chain = this.hasPendingReady ? this.ready : null;
1301
+ for (const batch of batches) {
1302
+ if (chain) {
1303
+ chain = chain.then(() => startBatch(batch));
1304
+ } else {
1305
+ chain = startBatch(batch);
1306
+ }
1307
+ }
1308
+ if (!chain) {
1309
+ if (this.status < STATUS.MOUNTED) {
1310
+ this.status = STATUS.MOUNTED;
1311
+ }
1312
+ return;
1253
1313
  }
1314
+ this.hasPendingReady = true;
1315
+ const ready = this.ready = chain.then(() => {
1316
+ if (this.status < STATUS.MOUNTED) {
1317
+ this.status = STATUS.MOUNTED;
1318
+ }
1319
+ if (this.ready === ready) {
1320
+ this.hasPendingReady = false;
1321
+ }
1322
+ });
1254
1323
  }
1255
1324
  };
1256
1325
  function startPlugins(manager, plugins) {
@@ -1265,9 +1334,103 @@ ${issueStrings}`);
1265
1334
  );
1266
1335
  }
1267
1336
  }
1337
+ function onWillStart(fn) {
1338
+ const scope = useScope();
1339
+ scope.willStart.push(scope.decorate(fn, "onWillStart"));
1340
+ }
1341
+ function onWillDestroy(fn) {
1342
+ const scope = useScope();
1343
+ scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
1344
+ }
1345
+ function useEffect(fn) {
1346
+ onWillDestroy(effect(fn));
1347
+ }
1348
+ function useListener(target, eventName, handler, eventParams) {
1349
+ if (typeof target === "function") {
1350
+ useEffect(() => {
1351
+ const el = target();
1352
+ if (el) {
1353
+ el.addEventListener(eventName, handler, eventParams);
1354
+ return () => el.removeEventListener(eventName, handler, eventParams);
1355
+ }
1356
+ return;
1357
+ });
1358
+ } else {
1359
+ target.addEventListener(eventName, handler, eventParams);
1360
+ onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
1361
+ }
1362
+ }
1363
+ function useApp() {
1364
+ return useScope().app;
1365
+ }
1366
+ function plugin(pluginType) {
1367
+ const scope = useScope();
1368
+ let plugin2 = scope.pluginManager.getPluginById(pluginType.id);
1369
+ if (!plugin2) {
1370
+ if (scope instanceof PluginManager) {
1371
+ plugin2 = scope.pluginManager.startPlugin(pluginType);
1372
+ } else {
1373
+ throw new OwlError(`Unknown plugin "${pluginType.id}"`);
1374
+ }
1375
+ }
1376
+ return plugin2;
1377
+ }
1378
+ function config(key, type, defaultValue) {
1379
+ const scope = useScope();
1380
+ if (!(scope instanceof PluginManager)) {
1381
+ throw new OwlError("Expected to be in a plugin scope");
1382
+ }
1383
+ if (scope.app.dev && type) {
1384
+ assertType(scope.config, types.object({ [key]: type }), "Config does not match the type");
1385
+ }
1386
+ const configValue = scope.config[key.endsWith("?") ? key.slice(0, -1) : key];
1387
+ return configValue === void 0 ? defaultValue : configValue;
1388
+ }
1389
+ var EventBus = class extends EventTarget {
1390
+ trigger(name, payload) {
1391
+ this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1392
+ }
1393
+ };
1394
+ var Markup = class extends String {
1395
+ };
1396
+ function htmlEscape(str) {
1397
+ if (str instanceof Markup) {
1398
+ return str;
1399
+ }
1400
+ if (str === void 0) {
1401
+ return markup("");
1402
+ }
1403
+ if (typeof str === "number") {
1404
+ return markup(String(str));
1405
+ }
1406
+ [
1407
+ ["&", "&amp;"],
1408
+ ["<", "&lt;"],
1409
+ [">", "&gt;"],
1410
+ ["'", "&#x27;"],
1411
+ ['"', "&quot;"],
1412
+ ["`", "&#x60;"]
1413
+ ].forEach((pairs) => {
1414
+ str = String(str).replace(new RegExp(pairs[0], "g"), pairs[1]);
1415
+ });
1416
+ return markup(str);
1417
+ }
1418
+ function markup(valueOrStrings, ...placeholders) {
1419
+ if (!Array.isArray(valueOrStrings)) {
1420
+ return new Markup(valueOrStrings);
1421
+ }
1422
+ const strings = valueOrStrings;
1423
+ let acc = "";
1424
+ let i = 0;
1425
+ for (; i < placeholders.length; ++i) {
1426
+ acc += strings[i] + htmlEscape(placeholders[i]);
1427
+ }
1428
+ acc += strings[i];
1429
+ return new Markup(acc);
1430
+ }
1268
1431
 
1269
1432
  // ../owl-runtime/dist/owl-runtime.es.js
1270
- var version = "3.0.0-alpha.32";
1433
+ var version = "3.0.0-alpha.34";
1271
1434
  var fibersInError = /* @__PURE__ */ new WeakMap();
1272
1435
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1273
1436
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -1340,7 +1503,7 @@ ${issueStrings}`);
1340
1503
  }
1341
1504
  return { modifiers, data: dataList };
1342
1505
  }
1343
- var config = {
1506
+ var config2 = {
1344
1507
  // whether or not blockdom should normalize DOM whenever a block is created.
1345
1508
  // Normalizing dom mean removing empty text nodes (or containing only spaces)
1346
1509
  shouldNormalizeDom: true,
@@ -1547,20 +1710,20 @@ ${issueStrings}`);
1547
1710
  }
1548
1711
  }
1549
1712
  var CSS_PROP_CACHE = {};
1550
- function toKebabCase(prop2) {
1551
- if (prop2 in CSS_PROP_CACHE) {
1552
- return CSS_PROP_CACHE[prop2];
1713
+ function toKebabCase(prop) {
1714
+ if (prop in CSS_PROP_CACHE) {
1715
+ return CSS_PROP_CACHE[prop];
1553
1716
  }
1554
- const result = prop2.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1555
- CSS_PROP_CACHE[prop2] = result;
1717
+ const result = prop.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1718
+ CSS_PROP_CACHE[prop] = result;
1556
1719
  return result;
1557
1720
  }
1558
1721
  var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1559
- function setStyleProp(style, prop2, value) {
1722
+ function setStyleProp(style, prop, value) {
1560
1723
  if (IMPORTANT_RE.test(value)) {
1561
- style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1724
+ style.setProperty(prop, value.replace(IMPORTANT_RE, ""), "important");
1562
1725
  } else {
1563
- style.setProperty(prop2, value);
1726
+ style.setProperty(prop, value);
1564
1727
  }
1565
1728
  }
1566
1729
  function toStyleObj(expr) {
@@ -1604,19 +1767,19 @@ ${issueStrings}`);
1604
1767
  if (colonIdx === -1) {
1605
1768
  continue;
1606
1769
  }
1607
- const prop2 = trim.call(part.slice(0, colonIdx));
1770
+ const prop = trim.call(part.slice(0, colonIdx));
1608
1771
  const value = trim.call(part.slice(colonIdx + 1));
1609
- if (prop2 && value && value !== "undefined") {
1610
- result[prop2] = value;
1772
+ if (prop && value && value !== "undefined") {
1773
+ result[prop] = value;
1611
1774
  }
1612
1775
  }
1613
1776
  return result;
1614
1777
  }
1615
1778
  case "object":
1616
- for (let prop2 in expr) {
1617
- const value = expr[prop2];
1779
+ for (let prop in expr) {
1780
+ const value = expr[prop];
1618
1781
  if (value || value === 0) {
1619
- result[toKebabCase(prop2)] = String(value);
1782
+ result[toKebabCase(prop)] = String(value);
1620
1783
  }
1621
1784
  }
1622
1785
  return result;
@@ -1647,22 +1810,22 @@ ${issueStrings}`);
1647
1810
  function setStyle(val) {
1648
1811
  val = val === "" ? {} : toStyleObj(val);
1649
1812
  const style = this.style;
1650
- for (let prop2 in val) {
1651
- setStyleProp(style, prop2, val[prop2]);
1813
+ for (let prop in val) {
1814
+ setStyleProp(style, prop, val[prop]);
1652
1815
  }
1653
1816
  }
1654
1817
  function updateStyle(val, oldVal) {
1655
1818
  oldVal = oldVal === "" ? {} : toStyleObj(oldVal);
1656
1819
  val = val === "" ? {} : toStyleObj(val);
1657
1820
  const style = this.style;
1658
- for (let prop2 in oldVal) {
1659
- if (!(prop2 in val)) {
1660
- style.removeProperty(prop2);
1821
+ for (let prop in oldVal) {
1822
+ if (!(prop in val)) {
1823
+ style.removeProperty(prop);
1661
1824
  }
1662
1825
  }
1663
- for (let prop2 in val) {
1664
- if (val[prop2] !== oldVal[prop2]) {
1665
- setStyleProp(style, prop2, val[prop2]);
1826
+ for (let prop in val) {
1827
+ if (val[prop] !== oldVal[prop]) {
1828
+ setStyleProp(style, prop, val[prop]);
1666
1829
  }
1667
1830
  }
1668
1831
  if (!style.cssText) {
@@ -1714,11 +1877,6 @@ ${issueStrings}`);
1714
1877
  }
1715
1878
  throw new OwlError("Cannot mount component: the target is not a valid DOM element");
1716
1879
  }
1717
- var EventBus = class extends EventTarget {
1718
- trigger(name, payload) {
1719
- this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1720
- }
1721
- };
1722
1880
  function whenReady(fn) {
1723
1881
  return new Promise(function(resolve) {
1724
1882
  if (document.readyState !== "loading") {
@@ -1729,43 +1887,6 @@ ${issueStrings}`);
1729
1887
  }).then(fn || function() {
1730
1888
  });
1731
1889
  }
1732
- var Markup = class extends String {
1733
- };
1734
- function htmlEscape(str) {
1735
- if (str instanceof Markup) {
1736
- return str;
1737
- }
1738
- if (str === void 0) {
1739
- return markup("");
1740
- }
1741
- if (typeof str === "number") {
1742
- return markup(String(str));
1743
- }
1744
- [
1745
- ["&", "&amp;"],
1746
- ["<", "&lt;"],
1747
- [">", "&gt;"],
1748
- ["'", "&#x27;"],
1749
- ['"', "&quot;"],
1750
- ["`", "&#x60;"]
1751
- ].forEach((pairs) => {
1752
- str = String(str).replace(new RegExp(pairs[0], "g"), pairs[1]);
1753
- });
1754
- return markup(str);
1755
- }
1756
- function markup(valueOrStrings, ...placeholders) {
1757
- if (!Array.isArray(valueOrStrings)) {
1758
- return new Markup(valueOrStrings);
1759
- }
1760
- const strings = valueOrStrings;
1761
- let acc = "";
1762
- let i = 0;
1763
- for (; i < placeholders.length; ++i) {
1764
- acc += strings[i] + htmlEscape(placeholders[i]);
1765
- }
1766
- acc += strings[i];
1767
- return new Markup(acc);
1768
- }
1769
1890
  function createEventHandler(rawEvent) {
1770
1891
  const eventName = rawEvent.split(".")[0];
1771
1892
  const capture = rawEvent.includes(".capture");
@@ -1787,7 +1908,7 @@ ${issueStrings}`);
1787
1908
  if (!currentTarget || !inOwnerDocument(currentTarget)) return;
1788
1909
  const data = currentTarget[eventKey];
1789
1910
  if (!data) return;
1790
- config.mainEventHandler(data, ev, currentTarget);
1911
+ config2.mainEventHandler(data, ev, currentTarget);
1791
1912
  }
1792
1913
  const options = { capture, passive };
1793
1914
  function setup(data) {
@@ -1827,7 +1948,7 @@ ${issueStrings}`);
1827
1948
  const _data = dom[eventKey];
1828
1949
  if (_data) {
1829
1950
  for (const data of Object.values(_data)) {
1830
- const stopped = config.mainEventHandler(data, event, dom);
1951
+ const stopped = config2.mainEventHandler(data, event, dom);
1831
1952
  if (stopped) return;
1832
1953
  }
1833
1954
  }
@@ -2073,7 +2194,7 @@ ${issueStrings}`);
2073
2194
  }
2074
2195
  const doc = new DOMParser().parseFromString(`<t>${str}</t>`, "text/xml");
2075
2196
  const node = doc.firstChild.firstChild;
2076
- if (config.shouldNormalizeDom) {
2197
+ if (config2.shouldNormalizeDom) {
2077
2198
  normalizeNode(node);
2078
2199
  }
2079
2200
  const tree = buildTree(node);
@@ -3196,12 +3317,15 @@ ${issueStrings}`);
3196
3317
  parent;
3197
3318
  children = /* @__PURE__ */ Object.create(null);
3198
3319
  willUpdateProps = [];
3320
+ // Fired right after `props` is applied to `node.props` on a parent re-render,
3321
+ // so reactive prop notifications happen once the new values are observable
3322
+ // (after user `onWillUpdateProps` hooks, including async ones, have run).
3323
+ propsUpdated = [];
3199
3324
  willUnmount = [];
3200
3325
  mounted = [];
3201
3326
  willPatch = [];
3202
3327
  patched = [];
3203
3328
  signalComputation;
3204
- pluginManager;
3205
3329
  constructor(C, props2, app, parent, parentKey) {
3206
3330
  super(app);
3207
3331
  this.parent = parent;
@@ -3731,6 +3855,7 @@ ${issueStrings}`);
3731
3855
  () => {
3732
3856
  if (fiber !== node.fiber) return;
3733
3857
  node.props = props2;
3858
+ for (const f of node.propsUpdated) f();
3734
3859
  fiber.render();
3735
3860
  },
3736
3861
  (error) => {
@@ -3739,6 +3864,7 @@ ${issueStrings}`);
3739
3864
  );
3740
3865
  } else {
3741
3866
  node.props = props2;
3867
+ for (const f of node.propsUpdated) f();
3742
3868
  fiber.render();
3743
3869
  }
3744
3870
  }
@@ -4079,10 +4205,6 @@ ${issueStrings}`);
4079
4205
  }
4080
4206
  return stopped;
4081
4207
  };
4082
- function onWillStart(fn) {
4083
- const scope = useScope();
4084
- scope.willStart.push(scope.decorate(fn, "onWillStart"));
4085
- }
4086
4208
  function onWillUpdateProps(fn) {
4087
4209
  const scope = getComponentScope();
4088
4210
  function swapped(s, nextProps) {
@@ -4106,10 +4228,6 @@ ${issueStrings}`);
4106
4228
  const scope = getComponentScope();
4107
4229
  scope.willUnmount.unshift(scope.decorate(fn, "onWillUnmount"));
4108
4230
  }
4109
- function onWillDestroy(fn) {
4110
- const scope = useScope();
4111
- scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
4112
- }
4113
4231
  function onError(callback) {
4114
4232
  const scope = getComponentScope();
4115
4233
  let handlers = nodeErrorHandlers.get(scope);
@@ -4119,6 +4237,24 @@ ${issueStrings}`);
4119
4237
  }
4120
4238
  handlers.push(callback.bind(scope.component));
4121
4239
  }
4240
+ function staticProp(key, type, ...args) {
4241
+ const node = getComponentScope();
4242
+ const hasDefault = args.length > 0;
4243
+ const propValue = node.props[key];
4244
+ if (node.app.dev) {
4245
+ if (type !== void 0 && (!hasDefault || propValue !== void 0)) {
4246
+ assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4247
+ }
4248
+ node.willUpdateProps.push((nextProps) => {
4249
+ if (nextProps[key] !== node.props[key]) {
4250
+ throw new OwlError(
4251
+ `Prop '${key}' changed in component '${node.componentName}'. Props declared with \`props.static()\` are static and should not change. If the prop is a signal, pass the same signal reference (its inner value may change).`
4252
+ );
4253
+ }
4254
+ });
4255
+ }
4256
+ return propValue === void 0 && hasDefault ? args[0] : propValue;
4257
+ }
4122
4258
  function componentType() {
4123
4259
  return constructorType(Component);
4124
4260
  }
@@ -4140,35 +4276,51 @@ ${issueStrings}`);
4140
4276
  }
4141
4277
  return types2.strictObject(validation);
4142
4278
  }
4143
- function props(type, defaults) {
4279
+ function makeProps(type, defaults) {
4144
4280
  const node = getComponentScope();
4145
4281
  const { app, componentName } = node;
4146
4282
  if (defaults) {
4147
4283
  node.defaultProps = Object.assign(node.defaultProps || {}, defaults);
4148
4284
  }
4149
- function getProp(key) {
4150
- if (node.props[key] === void 0 && defaults) {
4285
+ function resolveValue(props2, key) {
4286
+ if (props2[key] === void 0 && defaults) {
4151
4287
  return defaults[key];
4152
4288
  }
4153
- return node.props[key];
4289
+ return props2[key];
4154
4290
  }
4291
+ const signals = /* @__PURE__ */ Object.create(null);
4155
4292
  const result = /* @__PURE__ */ Object.create(null);
4156
- function applyPropGetters(keys) {
4293
+ function defineProp(key) {
4294
+ signals[key] = signal(resolveValue(node.props, key));
4295
+ Reflect.defineProperty(result, key, {
4296
+ enumerable: true,
4297
+ configurable: true,
4298
+ get: signals[key]
4299
+ });
4300
+ }
4301
+ function defineProps(keys) {
4157
4302
  for (const key of keys) {
4158
- Reflect.defineProperty(result, key, {
4159
- enumerable: true,
4160
- get: getProp.bind(null, key)
4161
- });
4303
+ defineProp(key);
4304
+ }
4305
+ }
4306
+ function updateSignals(keys) {
4307
+ for (const key of keys) {
4308
+ signals[key].set(resolveValue(node.props, key));
4162
4309
  }
4163
4310
  }
4164
4311
  if (type) {
4165
4312
  const keys = (Array.isArray(type) ? type : Object.keys(type)).map(
4166
4313
  (key) => key.endsWith("?") ? key.slice(0, -1) : key
4167
4314
  );
4168
- applyPropGetters(keys);
4315
+ defineProps(keys);
4316
+ node.propsUpdated.push(() => updateSignals(keys));
4169
4317
  if (app.dev) {
4170
4318
  if (defaults) {
4171
- assertType(defaults, validateDefaults(type), `Invalid component default props (${componentName})`);
4319
+ assertType(
4320
+ defaults,
4321
+ validateDefaults(type),
4322
+ `Invalid component default props (${componentName})`
4323
+ );
4172
4324
  }
4173
4325
  const validation = types2.object(type);
4174
4326
  assertType(node.props, validation, `Invalid component props (${componentName})`);
@@ -4194,17 +4346,28 @@ ${issueStrings}`);
4194
4346
  return keys2;
4195
4347
  };
4196
4348
  let keys = getKeys(node.props);
4197
- applyPropGetters(keys);
4198
- node.willUpdateProps.push((np) => {
4349
+ defineProps(keys);
4350
+ node.propsUpdated.push(() => {
4351
+ const nextKeys = getKeys(node.props);
4352
+ const nextKeySet = new Set(nextKeys);
4199
4353
  for (const key of keys) {
4200
- Reflect.deleteProperty(result, key);
4354
+ if (!nextKeySet.has(key)) {
4355
+ Reflect.deleteProperty(result, key);
4356
+ delete signals[key];
4357
+ }
4358
+ }
4359
+ for (const key of nextKeys) {
4360
+ if (!(key in signals)) {
4361
+ defineProp(key);
4362
+ }
4201
4363
  }
4202
- keys = getKeys(np);
4203
- applyPropGetters(keys);
4364
+ updateSignals(nextKeys);
4365
+ keys = nextKeys;
4204
4366
  });
4205
4367
  }
4206
4368
  return result;
4207
4369
  }
4370
+ var props = Object.assign(makeProps, { static: staticProp });
4208
4371
  var ErrorBoundary = class extends Component {
4209
4372
  static template = xml`
4210
4373
  <t t-if="this.props.error()">
@@ -4219,27 +4382,7 @@ ${issueStrings}`);
4219
4382
  onError((e) => this.props.error.set(e));
4220
4383
  }
4221
4384
  };
4222
- function useEffect(fn) {
4223
- onWillDestroy(effect(fn));
4224
- }
4225
- function useListener(target, eventName, handler, eventParams) {
4226
- if (typeof target === "function") {
4227
- useEffect(() => {
4228
- const el = target();
4229
- if (el) {
4230
- el.addEventListener(eventName, handler, eventParams);
4231
- return () => el.removeEventListener(eventName, handler, eventParams);
4232
- }
4233
- return;
4234
- });
4235
- } else {
4236
- target.addEventListener(eventName, handler, eventParams);
4237
- onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
4238
- }
4239
- }
4240
- function useApp() {
4241
- return useScope().app;
4242
- }
4385
+ var useApp2 = useApp;
4243
4386
  var PortalContent = class extends Component {
4244
4387
  static template = xml`<t t-call-slot="default"/>`;
4245
4388
  };
@@ -4323,47 +4466,6 @@ ${issueStrings}`);
4323
4466
  onWillDestroy(() => root.destroy());
4324
4467
  }
4325
4468
  };
4326
- function prop(key, type, ...args) {
4327
- const node = getComponentScope();
4328
- const hasDefault = args.length > 0;
4329
- const propValue = node.props[key];
4330
- if (node.app.dev) {
4331
- if (type !== void 0 && (!hasDefault || propValue !== void 0)) {
4332
- assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4333
- }
4334
- node.willUpdateProps.push((nextProps) => {
4335
- if (nextProps[key] !== node.props[key]) {
4336
- throw new OwlError(
4337
- `Prop '${key}' changed in component '${node.componentName}'. Props declared with \`prop()\` are static and should not change. If the prop is a signal, pass the same signal reference (its inner value may change).`
4338
- );
4339
- }
4340
- });
4341
- }
4342
- return propValue === void 0 && hasDefault ? args[0] : propValue;
4343
- }
4344
- function plugin(pluginType) {
4345
- const scope = useScope();
4346
- const manager = scope instanceof ComponentNode ? scope.pluginManager : scope;
4347
- let plugin2 = manager.getPluginById(pluginType.id);
4348
- if (!plugin2) {
4349
- if (scope instanceof PluginManager) {
4350
- plugin2 = manager.startPlugin(pluginType);
4351
- } else {
4352
- throw new OwlError(`Unknown plugin "${pluginType.id}"`);
4353
- }
4354
- }
4355
- return plugin2;
4356
- }
4357
- function config2(name, type) {
4358
- const scope = useScope();
4359
- if (!(scope instanceof PluginManager)) {
4360
- throw new OwlError("Expected to be in a plugin scope");
4361
- }
4362
- if (scope.app.dev && type) {
4363
- assertType(scope.config, types2.object({ [name]: type }), "Config does not match the type");
4364
- }
4365
- return scope.config[name.endsWith("?") ? name.slice(0, -1) : name];
4366
- }
4367
4469
  function providePlugins(pluginConstructors, config3) {
4368
4470
  const node = getComponentScope();
4369
4471
  const manager = new PluginManager(node.app, { parent: node.pluginManager, config: config3 });
@@ -4374,10 +4476,10 @@ ${issueStrings}`);
4374
4476
  onWillStart(() => manager.ready);
4375
4477
  }
4376
4478
  }
4377
- config.shouldNormalizeDom = false;
4378
- config.mainEventHandler = mainEventHandler;
4479
+ config2.shouldNormalizeDom = false;
4480
+ config2.mainEventHandler = mainEventHandler;
4379
4481
  var blockDom = {
4380
- config,
4482
+ config: config2,
4381
4483
  // bdom entry points
4382
4484
  mount,
4383
4485
  patch,
@@ -4392,8 +4494,8 @@ ${issueStrings}`);
4392
4494
  };
4393
4495
  var __info__ = {
4394
4496
  version: App.version,
4395
- date: "2026-05-28T07:14:22.717Z",
4396
- hash: "578ed435",
4497
+ date: "2026-06-05T08:44:38.319Z",
4498
+ hash: "3206cf27",
4397
4499
  url: "https://github.com/odoo/owl"
4398
4500
  };
4399
4501