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

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.es.js CHANGED
@@ -569,6 +569,9 @@ function triggerSignal(signal2) {
569
569
  }
570
570
  onWriteAtom(signal2[atomSymbol]);
571
571
  }
572
+ function signalRef() {
573
+ return buildSignal(null, (atom) => atom.value);
574
+ }
572
575
  function signalArray(initialValue) {
573
576
  return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
574
577
  }
@@ -585,6 +588,7 @@ function signal(value) {
585
588
  return buildSignal(value, (atom) => atom.value);
586
589
  }
587
590
  signal.trigger = triggerSignal;
591
+ signal.ref = signalRef;
588
592
  signal.Array = signalArray;
589
593
  signal.Map = signalMap;
590
594
  signal.Object = signalObject;
@@ -752,7 +756,7 @@ function createContext(issues, value, path, parent) {
752
756
  addIssue(issue) {
753
757
  issues.push({
754
758
  received: this.value,
755
- path: this.path,
759
+ path: this.path.join(" > "),
756
760
  ...issue
757
761
  });
758
762
  },
@@ -778,33 +782,123 @@ function validateType(value, validation) {
778
782
  validation(createContext(issues, value, []));
779
783
  return issues;
780
784
  }
781
- function anyType() {
782
- return function validateAny() {
785
+ var defaultSymbol = /* @__PURE__ */ Symbol("default");
786
+ var innerTypeSymbol = /* @__PURE__ */ Symbol("innerType");
787
+ var shapeSymbol = /* @__PURE__ */ Symbol("shape");
788
+ var elementTypeSymbol = /* @__PURE__ */ Symbol("elementType");
789
+ var optionalSymbol = /* @__PURE__ */ Symbol("optional");
790
+ function getDefault(type) {
791
+ return typeof type === "function" ? type[defaultSymbol] : void 0;
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);
783
802
  };
803
+ validate[defaultSymbol] = typeof value === "function" ? value : () => value;
804
+ validate[innerTypeSymbol] = type;
805
+ return validate;
806
+ }
807
+ function makeOptional(type) {
808
+ const validate = function validateOptional(context) {
809
+ if (context.value === void 0) {
810
+ return;
811
+ }
812
+ context.validate(type);
813
+ };
814
+ validate[optionalSymbol] = true;
815
+ validate[innerTypeSymbol] = type;
816
+ return validate;
817
+ }
818
+ function isOptionalType(type) {
819
+ return typeof type === "function" && optionalSymbol in type;
820
+ }
821
+ function makeType(validate) {
822
+ validate.default = (value) => withDefault(validate, value);
823
+ validate.optional = () => makeOptional(validate);
824
+ return validate;
825
+ }
826
+ function applyDefaults(value, type) {
827
+ return applyDefaultsRec(value, type);
828
+ }
829
+ function applyDefaultsRec(value, type) {
830
+ if (typeof type !== "function") {
831
+ return value;
832
+ }
833
+ if (value === void 0) {
834
+ const factory = type[defaultSymbol];
835
+ if (!factory) {
836
+ return value;
837
+ }
838
+ value = factory();
839
+ }
840
+ const inner = type[innerTypeSymbol] || type;
841
+ if (typeof inner !== "function" || !value || typeof value !== "object") {
842
+ return value;
843
+ }
844
+ const elementType = inner[elementTypeSymbol];
845
+ if (elementType && Array.isArray(value)) {
846
+ let result2 = value;
847
+ for (let index = 0; index < value.length; index++) {
848
+ const newValue = applyDefaultsRec(value[index], elementType);
849
+ if (newValue !== value[index]) {
850
+ if (result2 === value) {
851
+ result2 = [...value];
852
+ }
853
+ result2[index] = newValue;
854
+ }
855
+ }
856
+ return result2;
857
+ }
858
+ const shape = inner[shapeSymbol];
859
+ if (!shape) {
860
+ return value;
861
+ }
862
+ let result = value;
863
+ for (const key in shape) {
864
+ const subValue = result[key];
865
+ const newValue = applyDefaultsRec(subValue, shape[key]);
866
+ if (newValue !== subValue) {
867
+ if (result === value) {
868
+ result = Array.isArray(value) ? [...value] : { ...value };
869
+ }
870
+ result[key] = newValue;
871
+ }
872
+ }
873
+ return result;
874
+ }
875
+ function anyType() {
876
+ return makeType(function validateAny() {
877
+ });
784
878
  }
785
879
  function booleanType() {
786
- return function validateBoolean(context) {
880
+ return makeType(function validateBoolean(context) {
787
881
  if (typeof context.value !== "boolean") {
788
882
  context.addIssue({ message: "value is not a boolean" });
789
883
  }
790
- };
884
+ });
791
885
  }
792
886
  function numberType() {
793
- return function validateNumber(context) {
887
+ return makeType(function validateNumber(context) {
794
888
  if (typeof context.value !== "number") {
795
889
  context.addIssue({ message: "value is not a number" });
796
890
  }
797
- };
891
+ });
798
892
  }
799
893
  function stringType() {
800
- return function validateString(context) {
894
+ return makeType(function validateString(context) {
801
895
  if (typeof context.value !== "string" && !(context.value instanceof String)) {
802
896
  context.addIssue({ message: "value is not a string" });
803
897
  }
804
- };
898
+ });
805
899
  }
806
900
  function arrayType(elementType) {
807
- return function validateArray(context) {
901
+ const validate = makeType(function validateArray(context) {
808
902
  if (!Array.isArray(context.value)) {
809
903
  context.addIssue({ message: "value is not an array" });
810
904
  return;
@@ -815,17 +909,21 @@ function arrayType(elementType) {
815
909
  for (let index = 0; index < context.value.length; index++) {
816
910
  context.withKey(index).validate(elementType);
817
911
  }
818
- };
912
+ });
913
+ if (elementType) {
914
+ validate[elementTypeSymbol] = elementType;
915
+ }
916
+ return validate;
819
917
  }
820
918
  function constructorType(constructor) {
821
- return function validateConstructor(context) {
919
+ return makeType(function validateConstructor(context) {
822
920
  if (!(typeof context.value === "function") || !(context.value === constructor || context.value.prototype instanceof constructor)) {
823
921
  context.addIssue({ message: `value is not '${constructor.name}' or an extension` });
824
922
  }
825
- };
923
+ });
826
924
  }
827
925
  function customValidator(type, validator, errorMessage = "value does not match custom validation") {
828
- return function validateCustom(context) {
926
+ return makeType(function validateCustom(context) {
829
927
  context.validate(type);
830
928
  if (!context.isValid) {
831
929
  return;
@@ -833,37 +931,37 @@ function customValidator(type, validator, errorMessage = "value does not match c
833
931
  if (!validator(context.value)) {
834
932
  context.addIssue({ message: errorMessage });
835
933
  }
836
- };
934
+ });
837
935
  }
838
936
  function functionType(parameters = [], result = void 0) {
839
- return function validateFunction(context) {
937
+ return makeType(function validateFunction(context) {
840
938
  if (typeof context.value !== "function") {
841
939
  context.addIssue({ message: "value is not a function" });
842
940
  }
843
- };
941
+ });
844
942
  }
845
943
  function instanceType(constructor) {
846
- return function validateInstanceType(context) {
944
+ return makeType(function validateInstanceType(context) {
847
945
  if (!(context.value instanceof constructor)) {
848
946
  context.addIssue({ message: `value is not an instance of '${constructor.name}'` });
849
947
  }
850
- };
948
+ });
851
949
  }
852
950
  function intersection(types22) {
853
- return function validateIntersection(context) {
951
+ return makeType(function validateIntersection(context) {
854
952
  for (const type of types22) {
855
953
  context.validate(type);
856
954
  }
857
- };
955
+ });
858
956
  }
859
957
  function literalType(literal) {
860
- return function validateLiteral(context) {
958
+ return makeType(function validateLiteral(context) {
861
959
  if (context.value !== literal) {
862
960
  context.addIssue({
863
961
  message: `value is not equal to ${typeof literal === "string" ? `'${literal}'` : literal}`
864
962
  });
865
963
  }
866
- };
964
+ });
867
965
  }
868
966
  function literalSelection(literals) {
869
967
  return union(literals.map(literalType));
@@ -891,15 +989,14 @@ function validateObject(context, schema, isStrict) {
891
989
  }
892
990
  const missingKeys = [];
893
991
  for (const key of keys) {
894
- const property = key.endsWith("?") ? key.slice(0, -1) : key;
895
- if (context.value[property] === void 0) {
896
- if (!key.endsWith("?")) {
897
- missingKeys.push(property);
992
+ if (context.value[key] === void 0) {
993
+ if (!isOptionalType(shape[key]) && !hasDefaultValue(shape[key])) {
994
+ missingKeys.push(key);
898
995
  }
899
996
  continue;
900
997
  }
901
998
  if (isShape) {
902
- context.withKey(property).validate(shape[key]);
999
+ context.withKey(key).validate(shape[key]);
903
1000
  }
904
1001
  }
905
1002
  if (missingKeys.length) {
@@ -912,7 +1009,7 @@ function validateObject(context, schema, isStrict) {
912
1009
  if (isStrict) {
913
1010
  const unknownKeys = [];
914
1011
  for (const key in context.value) {
915
- if (!keys.includes(key) && !(`${key}?` in shape)) {
1012
+ if (!keys.includes(key)) {
916
1013
  unknownKeys.push(key);
917
1014
  }
918
1015
  }
@@ -926,24 +1023,32 @@ function validateObject(context, schema, isStrict) {
926
1023
  }
927
1024
  }
928
1025
  function objectType(schema = {}) {
929
- return function validateLooseObject(context) {
1026
+ const validate = makeType(function validateLooseObject(context) {
930
1027
  validateObject(context, schema, false);
931
- };
1028
+ });
1029
+ if (!Array.isArray(schema)) {
1030
+ validate[shapeSymbol] = schema;
1031
+ }
1032
+ return validate;
932
1033
  }
933
1034
  function strictObjectType(schema) {
934
- return function validateStrictObject(context) {
1035
+ const validate = makeType(function validateStrictObject(context) {
935
1036
  validateObject(context, schema, true);
936
- };
1037
+ });
1038
+ if (!Array.isArray(schema)) {
1039
+ validate[shapeSymbol] = schema;
1040
+ }
1041
+ return validate;
937
1042
  }
938
1043
  function promiseType(type) {
939
- return function validatePromise(context) {
1044
+ return makeType(function validatePromise(context) {
940
1045
  if (!(context.value instanceof Promise)) {
941
1046
  context.addIssue({ message: "value is not a promise" });
942
1047
  }
943
- };
1048
+ });
944
1049
  }
945
1050
  function recordType(valueType) {
946
- return function validateRecord(context) {
1051
+ return makeType(function validateRecord(context) {
947
1052
  if (typeof context.value !== "object" || Array.isArray(context.value) || context.value === null) {
948
1053
  context.addIssue({ message: "value is not an object" });
949
1054
  return;
@@ -954,10 +1059,10 @@ function recordType(valueType) {
954
1059
  for (const key in context.value) {
955
1060
  context.withKey(key).validate(valueType);
956
1061
  }
957
- };
1062
+ });
958
1063
  }
959
1064
  function tuple(types22) {
960
- return function validateTuple(context) {
1065
+ const validate = makeType(function validateTuple(context) {
961
1066
  if (!Array.isArray(context.value)) {
962
1067
  context.addIssue({ message: "value is not an array" });
963
1068
  return;
@@ -969,10 +1074,12 @@ function tuple(types22) {
969
1074
  for (let index = 0; index < types22.length; index++) {
970
1075
  context.withKey(index).validate(types22[index]);
971
1076
  }
972
- };
1077
+ });
1078
+ validate[shapeSymbol] = types22;
1079
+ return validate;
973
1080
  }
974
1081
  function union(types22) {
975
- return function validateUnion(context) {
1082
+ return makeType(function validateUnion(context) {
976
1083
  let firstIssueIndex = 0;
977
1084
  const subIssues = [];
978
1085
  for (const type of types22) {
@@ -988,17 +1095,20 @@ function union(types22) {
988
1095
  message: "value does not match union type",
989
1096
  subIssues
990
1097
  });
991
- };
1098
+ });
992
1099
  }
993
1100
  function reactiveValueType(type) {
994
- return function validateReactiveValue(context) {
1101
+ return makeType(function validateReactiveValue(context) {
995
1102
  if (typeof context.value !== "function" || !context.value[atomSymbol]) {
996
1103
  context.addIssue({ message: "value is not a reactive value" });
997
1104
  }
998
- };
1105
+ });
999
1106
  }
1000
1107
  function ref(type) {
1001
- return union([literalType(null), instanceType(type)]);
1108
+ if (typeof HTMLElement === "undefined") {
1109
+ throw new Error("Cannot use ref in a non-DOM environment");
1110
+ }
1111
+ return union([literalType(null), instanceType(type || HTMLElement)]);
1002
1112
  }
1003
1113
  var types = {
1004
1114
  and: intersection,
@@ -1126,6 +1236,13 @@ var Plugin = class {
1126
1236
  static set id(shadowId) {
1127
1237
  this._shadowId = shadowId;
1128
1238
  }
1239
+ // Plugins passed to `startPlugins` are started in batches of equal sequence,
1240
+ // ascending (lower first), like Resource/Registry. Each batch's onWillStart
1241
+ // callbacks fully settle before the next batch is instantiated, so
1242
+ // foundational plugins (low sequence) are ready before later plugins even
1243
+ // run their setup. Explicit `plugin(X)` dependencies bypass batching and
1244
+ // start immediately.
1245
+ static sequence = 50;
1129
1246
  __owl__;
1130
1247
  constructor(manager) {
1131
1248
  this.__owl__ = manager;
@@ -1136,11 +1253,13 @@ var Plugin = class {
1136
1253
  var PluginManager = class extends Scope {
1137
1254
  config;
1138
1255
  plugins;
1139
- // Resolves once all pending plugin willStart callbacks have settled. The
1140
- // scope transitions to MOUNTED as the last step of this chain. Consumers
1141
- // (the root's mount(), providePlugins) await this before treating the
1142
- // manager as ready. `willStart` itself is inherited from Scope.
1256
+ // Resolves once all batches of plugins have started and their willStart
1257
+ // callbacks have settled. The scope transitions to MOUNTED as the last step
1258
+ // of this chain. Consumers (the root's mount(), providePlugins) await this
1259
+ // before treating the manager as ready. `willStart` itself is inherited
1260
+ // from Scope.
1143
1261
  ready = Promise.resolve();
1262
+ hasPendingReady = false;
1144
1263
  constructor(app, options = {}) {
1145
1264
  super(app);
1146
1265
  this.config = options.config ?? {};
@@ -1181,24 +1300,61 @@ var PluginManager = class extends Scope {
1181
1300
  return plugin2;
1182
1301
  }
1183
1302
  startPlugins(pluginConstructors) {
1184
- scopeStack.push(this);
1185
- try {
1186
- for (const pluginConstructor of pluginConstructors) {
1187
- this.startPlugin(pluginConstructor);
1303
+ const fresh = pluginConstructors.filter((ctor) => {
1304
+ if (!ctor.id || this.plugins.hasOwnProperty(ctor.id)) {
1305
+ this.startPlugin(ctor);
1306
+ return false;
1188
1307
  }
1189
- } finally {
1190
- scopeStack.pop();
1308
+ return true;
1309
+ });
1310
+ if (!fresh.length) {
1311
+ return;
1191
1312
  }
1192
- const pending = this.willStart.splice(0);
1193
- if (pending.length) {
1194
- this.ready = Promise.all(pending.map((fn) => fn())).then(() => {
1195
- if (this.status < STATUS.MOUNTED) {
1196
- this.status = STATUS.MOUNTED;
1313
+ fresh.sort((p1, p2) => p1.sequence - p2.sequence);
1314
+ const batches = [];
1315
+ for (const ctor of fresh) {
1316
+ const batch = batches[batches.length - 1];
1317
+ if (batch && batch[0].sequence === ctor.sequence) {
1318
+ batch.push(ctor);
1319
+ } else {
1320
+ batches.push([ctor]);
1321
+ }
1322
+ }
1323
+ const startBatch = (batch) => {
1324
+ scopeStack.push(this);
1325
+ try {
1326
+ for (const ctor of batch) {
1327
+ this.startPlugin(ctor);
1197
1328
  }
1198
- });
1199
- } else if (this.status < STATUS.MOUNTED) {
1200
- this.status = STATUS.MOUNTED;
1329
+ } finally {
1330
+ scopeStack.pop();
1331
+ }
1332
+ const pending = this.willStart.splice(0);
1333
+ return pending.length ? Promise.all(pending.map((fn) => fn())) : null;
1334
+ };
1335
+ let chain = this.hasPendingReady ? this.ready : null;
1336
+ for (const batch of batches) {
1337
+ if (chain) {
1338
+ chain = chain.then(() => startBatch(batch));
1339
+ } else {
1340
+ chain = startBatch(batch);
1341
+ }
1201
1342
  }
1343
+ if (!chain) {
1344
+ if (this.status < STATUS.MOUNTED) {
1345
+ this.status = STATUS.MOUNTED;
1346
+ }
1347
+ return;
1348
+ }
1349
+ this.hasPendingReady = true;
1350
+ const ready = this.ready = chain.then(() => {
1351
+ if (this.status < STATUS.MOUNTED) {
1352
+ this.status = STATUS.MOUNTED;
1353
+ }
1354
+ if (this.ready === ready) {
1355
+ this.hasPendingReady = false;
1356
+ }
1357
+ });
1202
1358
  }
1203
1359
  };
1204
1360
  function startPlugins(manager, plugins) {
@@ -1254,7 +1410,7 @@ function plugin(pluginType) {
1254
1410
  }
1255
1411
  return plugin2;
1256
1412
  }
1257
- function config(key, type, defaultValue) {
1413
+ function config(key, type) {
1258
1414
  const scope = useScope();
1259
1415
  if (!(scope instanceof PluginManager)) {
1260
1416
  throw new OwlError("Expected to be in a plugin scope");
@@ -1262,8 +1418,8 @@ function config(key, type, defaultValue) {
1262
1418
  if (scope.app.dev && type) {
1263
1419
  assertType(scope.config, types.object({ [key]: type }), "Config does not match the type");
1264
1420
  }
1265
- const configValue = scope.config[key.endsWith("?") ? key.slice(0, -1) : key];
1266
- return configValue === void 0 ? defaultValue : configValue;
1421
+ const configValue = scope.config[key];
1422
+ return configValue === void 0 ? getDefault(type)?.() : configValue;
1267
1423
  }
1268
1424
  var EventBus = class extends EventTarget {
1269
1425
  trigger(name, payload) {
@@ -1309,7 +1465,7 @@ function markup(valueOrStrings, ...placeholders) {
1309
1465
  }
1310
1466
 
1311
1467
  // ../owl-runtime/dist/owl-runtime.es.js
1312
- var version = "3.0.0-alpha.33";
1468
+ var version = "3.0.0-alpha.35";
1313
1469
  var fibersInError = /* @__PURE__ */ new WeakMap();
1314
1470
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1315
1471
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -1589,20 +1745,20 @@ function toClassObj(expr) {
1589
1745
  }
1590
1746
  }
1591
1747
  var CSS_PROP_CACHE = {};
1592
- function toKebabCase(prop2) {
1593
- if (prop2 in CSS_PROP_CACHE) {
1594
- return CSS_PROP_CACHE[prop2];
1748
+ function toKebabCase(prop) {
1749
+ if (prop in CSS_PROP_CACHE) {
1750
+ return CSS_PROP_CACHE[prop];
1595
1751
  }
1596
- const result = prop2.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1597
- CSS_PROP_CACHE[prop2] = result;
1752
+ const result = prop.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1753
+ CSS_PROP_CACHE[prop] = result;
1598
1754
  return result;
1599
1755
  }
1600
1756
  var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1601
- function setStyleProp(style, prop2, value) {
1757
+ function setStyleProp(style, prop, value) {
1602
1758
  if (IMPORTANT_RE.test(value)) {
1603
- style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1759
+ style.setProperty(prop, value.replace(IMPORTANT_RE, ""), "important");
1604
1760
  } else {
1605
- style.setProperty(prop2, value);
1761
+ style.setProperty(prop, value);
1606
1762
  }
1607
1763
  }
1608
1764
  function toStyleObj(expr) {
@@ -1646,19 +1802,19 @@ function toStyleObj(expr) {
1646
1802
  if (colonIdx === -1) {
1647
1803
  continue;
1648
1804
  }
1649
- const prop2 = trim.call(part.slice(0, colonIdx));
1805
+ const prop = trim.call(part.slice(0, colonIdx));
1650
1806
  const value = trim.call(part.slice(colonIdx + 1));
1651
- if (prop2 && value && value !== "undefined") {
1652
- result[prop2] = value;
1807
+ if (prop && value && value !== "undefined") {
1808
+ result[prop] = value;
1653
1809
  }
1654
1810
  }
1655
1811
  return result;
1656
1812
  }
1657
1813
  case "object":
1658
- for (let prop2 in expr) {
1659
- const value = expr[prop2];
1814
+ for (let prop in expr) {
1815
+ const value = expr[prop];
1660
1816
  if (value || value === 0) {
1661
- result[toKebabCase(prop2)] = String(value);
1817
+ result[toKebabCase(prop)] = String(value);
1662
1818
  }
1663
1819
  }
1664
1820
  return result;
@@ -1689,22 +1845,22 @@ function updateClass(val, oldVal) {
1689
1845
  function setStyle(val) {
1690
1846
  val = val === "" ? {} : toStyleObj(val);
1691
1847
  const style = this.style;
1692
- for (let prop2 in val) {
1693
- setStyleProp(style, prop2, val[prop2]);
1848
+ for (let prop in val) {
1849
+ setStyleProp(style, prop, val[prop]);
1694
1850
  }
1695
1851
  }
1696
1852
  function updateStyle(val, oldVal) {
1697
1853
  oldVal = oldVal === "" ? {} : toStyleObj(oldVal);
1698
1854
  val = val === "" ? {} : toStyleObj(val);
1699
1855
  const style = this.style;
1700
- for (let prop2 in oldVal) {
1701
- if (!(prop2 in val)) {
1702
- style.removeProperty(prop2);
1856
+ for (let prop in oldVal) {
1857
+ if (!(prop in val)) {
1858
+ style.removeProperty(prop);
1703
1859
  }
1704
1860
  }
1705
- for (let prop2 in val) {
1706
- if (val[prop2] !== oldVal[prop2]) {
1707
- setStyleProp(style, prop2, val[prop2]);
1861
+ for (let prop in val) {
1862
+ if (val[prop] !== oldVal[prop]) {
1863
+ setStyleProp(style, prop, val[prop]);
1708
1864
  }
1709
1865
  }
1710
1866
  if (!style.cssText) {
@@ -3196,6 +3352,10 @@ var ComponentNode = class extends Scope {
3196
3352
  parent;
3197
3353
  children = /* @__PURE__ */ Object.create(null);
3198
3354
  willUpdateProps = [];
3355
+ // Fired right after `props` is applied to `node.props` on a parent re-render,
3356
+ // so reactive prop notifications happen once the new values are observable
3357
+ // (after user `onWillUpdateProps` hooks, including async ones, have run).
3358
+ propsUpdated = [];
3199
3359
  willUnmount = [];
3200
3360
  mounted = [];
3201
3361
  willPatch = [];
@@ -3496,7 +3656,7 @@ var Component = class {
3496
3656
  }
3497
3657
  };
3498
3658
  var ObjectCreate = Object.create;
3499
- function withDefault(value, defaultValue) {
3659
+ function withDefault2(value, defaultValue) {
3500
3660
  return value === void 0 || value === null || value === false ? defaultValue : value;
3501
3661
  }
3502
3662
  function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
@@ -3730,6 +3890,7 @@ function createComponent(app, name, isStatic, hasSlotsProp, hasDynamicPropList,
3730
3890
  () => {
3731
3891
  if (fiber !== node.fiber) return;
3732
3892
  node.props = props2;
3893
+ for (const f of node.propsUpdated) f();
3733
3894
  fiber.render();
3734
3895
  },
3735
3896
  (error) => {
@@ -3738,6 +3899,7 @@ function createComponent(app, name, isStatic, hasSlotsProp, hasDynamicPropList,
3738
3899
  );
3739
3900
  } else {
3740
3901
  node.props = props2;
3902
+ for (const f of node.propsUpdated) f();
3741
3903
  fiber.render();
3742
3904
  }
3743
3905
  }
@@ -3780,7 +3942,7 @@ function callTemplate(subTemplate, owner, app, ctx, parent, key) {
3780
3942
  return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
3781
3943
  }
3782
3944
  var helpers = {
3783
- withDefault,
3945
+ withDefault: withDefault2,
3784
3946
  zero: /* @__PURE__ */ Symbol("zero"),
3785
3947
  callSlot,
3786
3948
  withKey,
@@ -4110,56 +4272,89 @@ function onError(callback) {
4110
4272
  }
4111
4273
  handlers.push(callback.bind(scope.component));
4112
4274
  }
4113
- function componentType() {
4114
- return constructorType(Component);
4115
- }
4116
- var types2 = { ...types, component: componentType };
4117
- function validateDefaults(schema) {
4118
- const validation = {};
4119
- if (Array.isArray(schema)) {
4120
- for (const key of schema) {
4121
- if (key.endsWith("?")) {
4122
- validation[key] = types2.any();
4123
- }
4275
+ function staticProp(key, type) {
4276
+ const node = getComponentScope();
4277
+ const defaultFactory = getDefault(type);
4278
+ const propValue = node.props[key];
4279
+ if (node.app.dev) {
4280
+ if (type !== void 0 && (!defaultFactory || propValue !== void 0)) {
4281
+ assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4124
4282
  }
4125
- } else {
4126
- for (const key in schema) {
4127
- if (key.endsWith("?")) {
4128
- validation[key] = schema[key];
4283
+ node.willUpdateProps.push((nextProps) => {
4284
+ if (nextProps[key] !== node.props[key]) {
4285
+ throw new OwlError(
4286
+ `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).`
4287
+ );
4129
4288
  }
4130
- }
4289
+ });
4131
4290
  }
4132
- return types2.strictObject(validation);
4291
+ return propValue === void 0 && defaultFactory ? defaultFactory() : propValue;
4133
4292
  }
4134
- function props(type, defaults) {
4293
+ function componentType() {
4294
+ return constructorType(Component);
4295
+ }
4296
+ var types2 = {
4297
+ ...types,
4298
+ component: componentType
4299
+ };
4300
+ function makeProps(type) {
4135
4301
  const node = getComponentScope();
4136
4302
  const { app, componentName } = node;
4303
+ let defaults = null;
4304
+ if (type && !Array.isArray(type)) {
4305
+ for (const key in type) {
4306
+ const factory = getDefault(type[key]);
4307
+ if (factory) {
4308
+ (defaults ||= {})[key] = factory();
4309
+ }
4310
+ }
4311
+ }
4137
4312
  if (defaults) {
4138
4313
  node.defaultProps = Object.assign(node.defaultProps || {}, defaults);
4139
4314
  }
4140
- function getProp(key) {
4141
- if (node.props[key] === void 0 && defaults) {
4315
+ function resolveValue(props2, key) {
4316
+ if (props2[key] === void 0 && defaults && key in defaults) {
4142
4317
  return defaults[key];
4143
4318
  }
4144
- return node.props[key];
4319
+ return props2[key];
4145
4320
  }
4321
+ const signals = /* @__PURE__ */ Object.create(null);
4146
4322
  const result = /* @__PURE__ */ Object.create(null);
4147
- function applyPropGetters(keys) {
4323
+ function defineProp(key) {
4324
+ signals[key] = signal(resolveValue(node.props, key));
4325
+ Reflect.defineProperty(result, key, {
4326
+ enumerable: true,
4327
+ configurable: true,
4328
+ get: signals[key]
4329
+ });
4330
+ }
4331
+ function defineProps(keys) {
4148
4332
  for (const key of keys) {
4149
- Reflect.defineProperty(result, key, {
4150
- enumerable: true,
4151
- get: getProp.bind(null, key)
4152
- });
4333
+ defineProp(key);
4334
+ }
4335
+ }
4336
+ function updateSignals(keys) {
4337
+ for (const key of keys) {
4338
+ signals[key].set(resolveValue(node.props, key));
4153
4339
  }
4154
4340
  }
4155
4341
  if (type) {
4156
- const keys = (Array.isArray(type) ? type : Object.keys(type)).map(
4157
- (key) => key.endsWith("?") ? key.slice(0, -1) : key
4158
- );
4159
- applyPropGetters(keys);
4342
+ const keys = Array.isArray(type) ? type : Object.keys(type);
4343
+ defineProps(keys);
4344
+ node.propsUpdated.push(() => updateSignals(keys));
4160
4345
  if (app.dev) {
4161
4346
  if (defaults) {
4162
- assertType(defaults, validateDefaults(type), `Invalid component default props (${componentName})`);
4347
+ const defaultedShape = {};
4348
+ for (const key in type) {
4349
+ if (key in defaults) {
4350
+ defaultedShape[key] = type[key];
4351
+ }
4352
+ }
4353
+ assertType(
4354
+ defaults,
4355
+ types2.object(defaultedShape),
4356
+ `Invalid component default props (${componentName})`
4357
+ );
4163
4358
  }
4164
4359
  const validation = types2.object(type);
4165
4360
  assertType(node.props, validation, `Invalid component props (${componentName})`);
@@ -4175,27 +4370,31 @@ function props(type, defaults) {
4175
4370
  keys2.push(k);
4176
4371
  }
4177
4372
  }
4178
- if (defaults) {
4179
- for (const k in defaults) {
4180
- if (!(k in props2)) {
4181
- keys2.push(k);
4182
- }
4183
- }
4184
- }
4185
4373
  return keys2;
4186
4374
  };
4187
4375
  let keys = getKeys(node.props);
4188
- applyPropGetters(keys);
4189
- node.willUpdateProps.push((np) => {
4376
+ defineProps(keys);
4377
+ node.propsUpdated.push(() => {
4378
+ const nextKeys = getKeys(node.props);
4379
+ const nextKeySet = new Set(nextKeys);
4190
4380
  for (const key of keys) {
4191
- Reflect.deleteProperty(result, key);
4381
+ if (!nextKeySet.has(key)) {
4382
+ Reflect.deleteProperty(result, key);
4383
+ delete signals[key];
4384
+ }
4385
+ }
4386
+ for (const key of nextKeys) {
4387
+ if (!(key in signals)) {
4388
+ defineProp(key);
4389
+ }
4192
4390
  }
4193
- keys = getKeys(np);
4194
- applyPropGetters(keys);
4391
+ updateSignals(nextKeys);
4392
+ keys = nextKeys;
4195
4393
  });
4196
4394
  }
4197
4395
  return result;
4198
4396
  }
4397
+ var props = Object.assign(makeProps, { static: staticProp });
4199
4398
  var ErrorBoundary = class extends Component {
4200
4399
  static template = xml`
4201
4400
  <t t-if="this.props.error()">
@@ -4205,7 +4404,7 @@ var ErrorBoundary = class extends Component {
4205
4404
  <t t-call-slot="default"/>
4206
4405
  </t>
4207
4406
  `;
4208
- props = props({ "error?": types2.signal() }, { error: signal(null) });
4407
+ props = props({ error: types2.signal().default(() => signal(null)) });
4209
4408
  setup() {
4210
4409
  onError((e) => this.props.error.set(e));
4211
4410
  }
@@ -4266,7 +4465,7 @@ var Suspense = class extends Component {
4266
4465
  <t t-call-slot="fallback"/>
4267
4466
  </t>
4268
4467
  `;
4269
- props = props({ slots: types2.object(["default", "fallback?"]) });
4468
+ props = props({ slots: types2.object({ default: types2.any(), fallback: types2.any().optional() }) });
4270
4469
  prepared = signal(false);
4271
4470
  mounted = signal(false);
4272
4471
  subRootMounted = false;
@@ -4294,24 +4493,6 @@ var Suspense = class extends Component {
4294
4493
  onWillDestroy(() => root.destroy());
4295
4494
  }
4296
4495
  };
4297
- function prop(key, type, ...args) {
4298
- const node = getComponentScope();
4299
- const hasDefault = args.length > 0;
4300
- const propValue = node.props[key];
4301
- if (node.app.dev) {
4302
- if (type !== void 0 && (!hasDefault || propValue !== void 0)) {
4303
- assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4304
- }
4305
- node.willUpdateProps.push((nextProps) => {
4306
- if (nextProps[key] !== node.props[key]) {
4307
- throw new OwlError(
4308
- `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).`
4309
- );
4310
- }
4311
- });
4312
- }
4313
- return propValue === void 0 && hasDefault ? args[0] : propValue;
4314
- }
4315
4496
  function providePlugins(pluginConstructors, config3) {
4316
4497
  const node = getComponentScope();
4317
4498
  const manager = new PluginManager(node.app, { parent: node.pluginManager, config: config3 });
@@ -4340,8 +4521,8 @@ var blockDom = {
4340
4521
  };
4341
4522
  var __info__ = {
4342
4523
  version: App.version,
4343
- date: "2026-05-28T13:29:02.340Z",
4344
- hash: "fadc22b7",
4524
+ date: "2026-06-11T09:51:14.980Z",
4525
+ hash: "739fc964",
4345
4526
  url: "https://github.com/odoo/owl"
4346
4527
  };
4347
4528
 
@@ -6530,6 +6711,7 @@ export {
6530
6711
  Suspense,
6531
6712
  TemplateSet,
6532
6713
  __info__,
6714
+ applyDefaults,
6533
6715
  assertType,
6534
6716
  asyncComputed,
6535
6717
  batched,
@@ -6537,6 +6719,7 @@ export {
6537
6719
  computed,
6538
6720
  config,
6539
6721
  effect,
6722
+ getDefault,
6540
6723
  getScope,
6541
6724
  globalTemplates,
6542
6725
  htmlEscape,
@@ -6552,12 +6735,12 @@ export {
6552
6735
  onWillUnmount,
6553
6736
  onWillUpdateProps,
6554
6737
  plugin,
6555
- prop,
6556
6738
  props,
6557
6739
  providePlugins,
6558
6740
  proxy,
6559
6741
  signal,
6560
6742
  status,
6743
+ types2 as t,
6561
6744
  toRaw,
6562
6745
  types2 as types,
6563
6746
  untrack,