@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.iife.js CHANGED
@@ -34,6 +34,7 @@ var owl = (() => {
34
34
  Suspense: () => Suspense,
35
35
  TemplateSet: () => TemplateSet,
36
36
  __info__: () => __info__,
37
+ applyDefaults: () => applyDefaults,
37
38
  assertType: () => assertType,
38
39
  asyncComputed: () => asyncComputed,
39
40
  batched: () => batched,
@@ -41,6 +42,7 @@ var owl = (() => {
41
42
  computed: () => computed,
42
43
  config: () => config,
43
44
  effect: () => effect,
45
+ getDefault: () => getDefault,
44
46
  getScope: () => getScope,
45
47
  globalTemplates: () => globalTemplates,
46
48
  htmlEscape: () => htmlEscape,
@@ -56,12 +58,12 @@ var owl = (() => {
56
58
  onWillUnmount: () => onWillUnmount,
57
59
  onWillUpdateProps: () => onWillUpdateProps,
58
60
  plugin: () => plugin,
59
- prop: () => prop,
60
61
  props: () => props,
61
62
  providePlugins: () => providePlugins,
62
63
  proxy: () => proxy,
63
64
  signal: () => signal,
64
65
  status: () => status,
66
+ t: () => types2,
65
67
  toRaw: () => toRaw,
66
68
  types: () => types2,
67
69
  untrack: () => untrack,
@@ -645,6 +647,9 @@ var owl = (() => {
645
647
  }
646
648
  onWriteAtom(signal2[atomSymbol]);
647
649
  }
650
+ function signalRef() {
651
+ return buildSignal(null, (atom) => atom.value);
652
+ }
648
653
  function signalArray(initialValue) {
649
654
  return buildSignal(initialValue, (atom) => proxifyTarget(atom.value, atom));
650
655
  }
@@ -661,6 +666,7 @@ var owl = (() => {
661
666
  return buildSignal(value, (atom) => atom.value);
662
667
  }
663
668
  signal.trigger = triggerSignal;
669
+ signal.ref = signalRef;
664
670
  signal.Array = signalArray;
665
671
  signal.Map = signalMap;
666
672
  signal.Object = signalObject;
@@ -828,7 +834,7 @@ ${issueStrings}`);
828
834
  addIssue(issue) {
829
835
  issues.push({
830
836
  received: this.value,
831
- path: this.path,
837
+ path: this.path.join(" > "),
832
838
  ...issue
833
839
  });
834
840
  },
@@ -854,33 +860,123 @@ ${issueStrings}`);
854
860
  validation(createContext(issues, value, []));
855
861
  return issues;
856
862
  }
857
- function anyType() {
858
- return function validateAny() {
863
+ var defaultSymbol = /* @__PURE__ */ Symbol("default");
864
+ var innerTypeSymbol = /* @__PURE__ */ Symbol("innerType");
865
+ var shapeSymbol = /* @__PURE__ */ Symbol("shape");
866
+ var elementTypeSymbol = /* @__PURE__ */ Symbol("elementType");
867
+ var optionalSymbol = /* @__PURE__ */ Symbol("optional");
868
+ function getDefault(type) {
869
+ return typeof type === "function" ? type[defaultSymbol] : void 0;
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);
859
880
  };
881
+ validate[defaultSymbol] = typeof value === "function" ? value : () => value;
882
+ validate[innerTypeSymbol] = type;
883
+ return validate;
884
+ }
885
+ function makeOptional(type) {
886
+ const validate = function validateOptional(context) {
887
+ if (context.value === void 0) {
888
+ return;
889
+ }
890
+ context.validate(type);
891
+ };
892
+ validate[optionalSymbol] = true;
893
+ validate[innerTypeSymbol] = type;
894
+ return validate;
895
+ }
896
+ function isOptionalType(type) {
897
+ return typeof type === "function" && optionalSymbol in type;
898
+ }
899
+ function makeType(validate) {
900
+ validate.default = (value) => withDefault(validate, value);
901
+ validate.optional = () => makeOptional(validate);
902
+ return validate;
903
+ }
904
+ function applyDefaults(value, type) {
905
+ return applyDefaultsRec(value, type);
906
+ }
907
+ function applyDefaultsRec(value, type) {
908
+ if (typeof type !== "function") {
909
+ return value;
910
+ }
911
+ if (value === void 0) {
912
+ const factory = type[defaultSymbol];
913
+ if (!factory) {
914
+ return value;
915
+ }
916
+ value = factory();
917
+ }
918
+ const inner = type[innerTypeSymbol] || type;
919
+ if (typeof inner !== "function" || !value || typeof value !== "object") {
920
+ return value;
921
+ }
922
+ const elementType = inner[elementTypeSymbol];
923
+ if (elementType && Array.isArray(value)) {
924
+ let result2 = value;
925
+ for (let index = 0; index < value.length; index++) {
926
+ const newValue = applyDefaultsRec(value[index], elementType);
927
+ if (newValue !== value[index]) {
928
+ if (result2 === value) {
929
+ result2 = [...value];
930
+ }
931
+ result2[index] = newValue;
932
+ }
933
+ }
934
+ return result2;
935
+ }
936
+ const shape = inner[shapeSymbol];
937
+ if (!shape) {
938
+ return value;
939
+ }
940
+ let result = value;
941
+ for (const key in shape) {
942
+ const subValue = result[key];
943
+ const newValue = applyDefaultsRec(subValue, shape[key]);
944
+ if (newValue !== subValue) {
945
+ if (result === value) {
946
+ result = Array.isArray(value) ? [...value] : { ...value };
947
+ }
948
+ result[key] = newValue;
949
+ }
950
+ }
951
+ return result;
952
+ }
953
+ function anyType() {
954
+ return makeType(function validateAny() {
955
+ });
860
956
  }
861
957
  function booleanType() {
862
- return function validateBoolean(context) {
958
+ return makeType(function validateBoolean(context) {
863
959
  if (typeof context.value !== "boolean") {
864
960
  context.addIssue({ message: "value is not a boolean" });
865
961
  }
866
- };
962
+ });
867
963
  }
868
964
  function numberType() {
869
- return function validateNumber(context) {
965
+ return makeType(function validateNumber(context) {
870
966
  if (typeof context.value !== "number") {
871
967
  context.addIssue({ message: "value is not a number" });
872
968
  }
873
- };
969
+ });
874
970
  }
875
971
  function stringType() {
876
- return function validateString(context) {
972
+ return makeType(function validateString(context) {
877
973
  if (typeof context.value !== "string" && !(context.value instanceof String)) {
878
974
  context.addIssue({ message: "value is not a string" });
879
975
  }
880
- };
976
+ });
881
977
  }
882
978
  function arrayType(elementType) {
883
- return function validateArray(context) {
979
+ const validate = makeType(function validateArray(context) {
884
980
  if (!Array.isArray(context.value)) {
885
981
  context.addIssue({ message: "value is not an array" });
886
982
  return;
@@ -891,17 +987,21 @@ ${issueStrings}`);
891
987
  for (let index = 0; index < context.value.length; index++) {
892
988
  context.withKey(index).validate(elementType);
893
989
  }
894
- };
990
+ });
991
+ if (elementType) {
992
+ validate[elementTypeSymbol] = elementType;
993
+ }
994
+ return validate;
895
995
  }
896
996
  function constructorType(constructor) {
897
- return function validateConstructor(context) {
997
+ return makeType(function validateConstructor(context) {
898
998
  if (!(typeof context.value === "function") || !(context.value === constructor || context.value.prototype instanceof constructor)) {
899
999
  context.addIssue({ message: `value is not '${constructor.name}' or an extension` });
900
1000
  }
901
- };
1001
+ });
902
1002
  }
903
1003
  function customValidator(type, validator, errorMessage = "value does not match custom validation") {
904
- return function validateCustom(context) {
1004
+ return makeType(function validateCustom(context) {
905
1005
  context.validate(type);
906
1006
  if (!context.isValid) {
907
1007
  return;
@@ -909,37 +1009,37 @@ ${issueStrings}`);
909
1009
  if (!validator(context.value)) {
910
1010
  context.addIssue({ message: errorMessage });
911
1011
  }
912
- };
1012
+ });
913
1013
  }
914
1014
  function functionType(parameters = [], result = void 0) {
915
- return function validateFunction(context) {
1015
+ return makeType(function validateFunction(context) {
916
1016
  if (typeof context.value !== "function") {
917
1017
  context.addIssue({ message: "value is not a function" });
918
1018
  }
919
- };
1019
+ });
920
1020
  }
921
1021
  function instanceType(constructor) {
922
- return function validateInstanceType(context) {
1022
+ return makeType(function validateInstanceType(context) {
923
1023
  if (!(context.value instanceof constructor)) {
924
1024
  context.addIssue({ message: `value is not an instance of '${constructor.name}'` });
925
1025
  }
926
- };
1026
+ });
927
1027
  }
928
1028
  function intersection(types22) {
929
- return function validateIntersection(context) {
1029
+ return makeType(function validateIntersection(context) {
930
1030
  for (const type of types22) {
931
1031
  context.validate(type);
932
1032
  }
933
- };
1033
+ });
934
1034
  }
935
1035
  function literalType(literal) {
936
- return function validateLiteral(context) {
1036
+ return makeType(function validateLiteral(context) {
937
1037
  if (context.value !== literal) {
938
1038
  context.addIssue({
939
1039
  message: `value is not equal to ${typeof literal === "string" ? `'${literal}'` : literal}`
940
1040
  });
941
1041
  }
942
- };
1042
+ });
943
1043
  }
944
1044
  function literalSelection(literals) {
945
1045
  return union(literals.map(literalType));
@@ -967,15 +1067,14 @@ ${issueStrings}`);
967
1067
  }
968
1068
  const missingKeys = [];
969
1069
  for (const key of keys) {
970
- const property = key.endsWith("?") ? key.slice(0, -1) : key;
971
- if (context.value[property] === void 0) {
972
- if (!key.endsWith("?")) {
973
- missingKeys.push(property);
1070
+ if (context.value[key] === void 0) {
1071
+ if (!isOptionalType(shape[key]) && !hasDefaultValue(shape[key])) {
1072
+ missingKeys.push(key);
974
1073
  }
975
1074
  continue;
976
1075
  }
977
1076
  if (isShape) {
978
- context.withKey(property).validate(shape[key]);
1077
+ context.withKey(key).validate(shape[key]);
979
1078
  }
980
1079
  }
981
1080
  if (missingKeys.length) {
@@ -988,7 +1087,7 @@ ${issueStrings}`);
988
1087
  if (isStrict) {
989
1088
  const unknownKeys = [];
990
1089
  for (const key in context.value) {
991
- if (!keys.includes(key) && !(`${key}?` in shape)) {
1090
+ if (!keys.includes(key)) {
992
1091
  unknownKeys.push(key);
993
1092
  }
994
1093
  }
@@ -1002,24 +1101,32 @@ ${issueStrings}`);
1002
1101
  }
1003
1102
  }
1004
1103
  function objectType(schema = {}) {
1005
- return function validateLooseObject(context) {
1104
+ const validate = makeType(function validateLooseObject(context) {
1006
1105
  validateObject(context, schema, false);
1007
- };
1106
+ });
1107
+ if (!Array.isArray(schema)) {
1108
+ validate[shapeSymbol] = schema;
1109
+ }
1110
+ return validate;
1008
1111
  }
1009
1112
  function strictObjectType(schema) {
1010
- return function validateStrictObject(context) {
1113
+ const validate = makeType(function validateStrictObject(context) {
1011
1114
  validateObject(context, schema, true);
1012
- };
1115
+ });
1116
+ if (!Array.isArray(schema)) {
1117
+ validate[shapeSymbol] = schema;
1118
+ }
1119
+ return validate;
1013
1120
  }
1014
1121
  function promiseType(type) {
1015
- return function validatePromise(context) {
1122
+ return makeType(function validatePromise(context) {
1016
1123
  if (!(context.value instanceof Promise)) {
1017
1124
  context.addIssue({ message: "value is not a promise" });
1018
1125
  }
1019
- };
1126
+ });
1020
1127
  }
1021
1128
  function recordType(valueType) {
1022
- return function validateRecord(context) {
1129
+ return makeType(function validateRecord(context) {
1023
1130
  if (typeof context.value !== "object" || Array.isArray(context.value) || context.value === null) {
1024
1131
  context.addIssue({ message: "value is not an object" });
1025
1132
  return;
@@ -1030,10 +1137,10 @@ ${issueStrings}`);
1030
1137
  for (const key in context.value) {
1031
1138
  context.withKey(key).validate(valueType);
1032
1139
  }
1033
- };
1140
+ });
1034
1141
  }
1035
1142
  function tuple(types22) {
1036
- return function validateTuple(context) {
1143
+ const validate = makeType(function validateTuple(context) {
1037
1144
  if (!Array.isArray(context.value)) {
1038
1145
  context.addIssue({ message: "value is not an array" });
1039
1146
  return;
@@ -1045,10 +1152,12 @@ ${issueStrings}`);
1045
1152
  for (let index = 0; index < types22.length; index++) {
1046
1153
  context.withKey(index).validate(types22[index]);
1047
1154
  }
1048
- };
1155
+ });
1156
+ validate[shapeSymbol] = types22;
1157
+ return validate;
1049
1158
  }
1050
1159
  function union(types22) {
1051
- return function validateUnion(context) {
1160
+ return makeType(function validateUnion(context) {
1052
1161
  let firstIssueIndex = 0;
1053
1162
  const subIssues = [];
1054
1163
  for (const type of types22) {
@@ -1064,17 +1173,20 @@ ${issueStrings}`);
1064
1173
  message: "value does not match union type",
1065
1174
  subIssues
1066
1175
  });
1067
- };
1176
+ });
1068
1177
  }
1069
1178
  function reactiveValueType(type) {
1070
- return function validateReactiveValue(context) {
1179
+ return makeType(function validateReactiveValue(context) {
1071
1180
  if (typeof context.value !== "function" || !context.value[atomSymbol]) {
1072
1181
  context.addIssue({ message: "value is not a reactive value" });
1073
1182
  }
1074
- };
1183
+ });
1075
1184
  }
1076
1185
  function ref(type) {
1077
- return union([literalType(null), instanceType(type)]);
1186
+ if (typeof HTMLElement === "undefined") {
1187
+ throw new Error("Cannot use ref in a non-DOM environment");
1188
+ }
1189
+ return union([literalType(null), instanceType(type || HTMLElement)]);
1078
1190
  }
1079
1191
  var types = {
1080
1192
  and: intersection,
@@ -1202,6 +1314,13 @@ ${issueStrings}`);
1202
1314
  static set id(shadowId) {
1203
1315
  this._shadowId = shadowId;
1204
1316
  }
1317
+ // Plugins passed to `startPlugins` are started in batches of equal sequence,
1318
+ // ascending (lower first), like Resource/Registry. Each batch's onWillStart
1319
+ // callbacks fully settle before the next batch is instantiated, so
1320
+ // foundational plugins (low sequence) are ready before later plugins even
1321
+ // run their setup. Explicit `plugin(X)` dependencies bypass batching and
1322
+ // start immediately.
1323
+ static sequence = 50;
1205
1324
  __owl__;
1206
1325
  constructor(manager) {
1207
1326
  this.__owl__ = manager;
@@ -1212,11 +1331,13 @@ ${issueStrings}`);
1212
1331
  var PluginManager = class extends Scope {
1213
1332
  config;
1214
1333
  plugins;
1215
- // Resolves once all pending plugin willStart callbacks have settled. The
1216
- // scope transitions to MOUNTED as the last step of this chain. Consumers
1217
- // (the root's mount(), providePlugins) await this before treating the
1218
- // manager as ready. `willStart` itself is inherited from Scope.
1334
+ // Resolves once all batches of plugins have started and their willStart
1335
+ // callbacks have settled. The scope transitions to MOUNTED as the last step
1336
+ // of this chain. Consumers (the root's mount(), providePlugins) await this
1337
+ // before treating the manager as ready. `willStart` itself is inherited
1338
+ // from Scope.
1219
1339
  ready = Promise.resolve();
1340
+ hasPendingReady = false;
1220
1341
  constructor(app, options = {}) {
1221
1342
  super(app);
1222
1343
  this.config = options.config ?? {};
@@ -1257,24 +1378,61 @@ ${issueStrings}`);
1257
1378
  return plugin2;
1258
1379
  }
1259
1380
  startPlugins(pluginConstructors) {
1260
- scopeStack.push(this);
1261
- try {
1262
- for (const pluginConstructor of pluginConstructors) {
1263
- this.startPlugin(pluginConstructor);
1381
+ const fresh = pluginConstructors.filter((ctor) => {
1382
+ if (!ctor.id || this.plugins.hasOwnProperty(ctor.id)) {
1383
+ this.startPlugin(ctor);
1384
+ return false;
1264
1385
  }
1265
- } finally {
1266
- scopeStack.pop();
1386
+ return true;
1387
+ });
1388
+ if (!fresh.length) {
1389
+ return;
1267
1390
  }
1268
- const pending = this.willStart.splice(0);
1269
- if (pending.length) {
1270
- this.ready = Promise.all(pending.map((fn) => fn())).then(() => {
1271
- if (this.status < STATUS.MOUNTED) {
1272
- this.status = STATUS.MOUNTED;
1391
+ fresh.sort((p1, p2) => p1.sequence - p2.sequence);
1392
+ const batches = [];
1393
+ for (const ctor of fresh) {
1394
+ const batch = batches[batches.length - 1];
1395
+ if (batch && batch[0].sequence === ctor.sequence) {
1396
+ batch.push(ctor);
1397
+ } else {
1398
+ batches.push([ctor]);
1399
+ }
1400
+ }
1401
+ const startBatch = (batch) => {
1402
+ scopeStack.push(this);
1403
+ try {
1404
+ for (const ctor of batch) {
1405
+ this.startPlugin(ctor);
1273
1406
  }
1274
- });
1275
- } else if (this.status < STATUS.MOUNTED) {
1276
- this.status = STATUS.MOUNTED;
1407
+ } finally {
1408
+ scopeStack.pop();
1409
+ }
1410
+ const pending = this.willStart.splice(0);
1411
+ return pending.length ? Promise.all(pending.map((fn) => fn())) : null;
1412
+ };
1413
+ let chain = this.hasPendingReady ? this.ready : null;
1414
+ for (const batch of batches) {
1415
+ if (chain) {
1416
+ chain = chain.then(() => startBatch(batch));
1417
+ } else {
1418
+ chain = startBatch(batch);
1419
+ }
1277
1420
  }
1421
+ if (!chain) {
1422
+ if (this.status < STATUS.MOUNTED) {
1423
+ this.status = STATUS.MOUNTED;
1424
+ }
1425
+ return;
1426
+ }
1427
+ this.hasPendingReady = true;
1428
+ const ready = this.ready = chain.then(() => {
1429
+ if (this.status < STATUS.MOUNTED) {
1430
+ this.status = STATUS.MOUNTED;
1431
+ }
1432
+ if (this.ready === ready) {
1433
+ this.hasPendingReady = false;
1434
+ }
1435
+ });
1278
1436
  }
1279
1437
  };
1280
1438
  function startPlugins(manager, plugins) {
@@ -1330,7 +1488,7 @@ ${issueStrings}`);
1330
1488
  }
1331
1489
  return plugin2;
1332
1490
  }
1333
- function config(key, type, defaultValue) {
1491
+ function config(key, type) {
1334
1492
  const scope = useScope();
1335
1493
  if (!(scope instanceof PluginManager)) {
1336
1494
  throw new OwlError("Expected to be in a plugin scope");
@@ -1338,8 +1496,8 @@ ${issueStrings}`);
1338
1496
  if (scope.app.dev && type) {
1339
1497
  assertType(scope.config, types.object({ [key]: type }), "Config does not match the type");
1340
1498
  }
1341
- const configValue = scope.config[key.endsWith("?") ? key.slice(0, -1) : key];
1342
- return configValue === void 0 ? defaultValue : configValue;
1499
+ const configValue = scope.config[key];
1500
+ return configValue === void 0 ? getDefault(type)?.() : configValue;
1343
1501
  }
1344
1502
  var EventBus = class extends EventTarget {
1345
1503
  trigger(name, payload) {
@@ -1385,7 +1543,7 @@ ${issueStrings}`);
1385
1543
  }
1386
1544
 
1387
1545
  // ../owl-runtime/dist/owl-runtime.es.js
1388
- var version = "3.0.0-alpha.33";
1546
+ var version = "3.0.0-alpha.35";
1389
1547
  var fibersInError = /* @__PURE__ */ new WeakMap();
1390
1548
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1391
1549
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -1665,20 +1823,20 @@ ${issueStrings}`);
1665
1823
  }
1666
1824
  }
1667
1825
  var CSS_PROP_CACHE = {};
1668
- function toKebabCase(prop2) {
1669
- if (prop2 in CSS_PROP_CACHE) {
1670
- return CSS_PROP_CACHE[prop2];
1826
+ function toKebabCase(prop) {
1827
+ if (prop in CSS_PROP_CACHE) {
1828
+ return CSS_PROP_CACHE[prop];
1671
1829
  }
1672
- const result = prop2.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1673
- CSS_PROP_CACHE[prop2] = result;
1830
+ const result = prop.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
1831
+ CSS_PROP_CACHE[prop] = result;
1674
1832
  return result;
1675
1833
  }
1676
1834
  var IMPORTANT_RE = /\s*!\s*important\s*$/i;
1677
- function setStyleProp(style, prop2, value) {
1835
+ function setStyleProp(style, prop, value) {
1678
1836
  if (IMPORTANT_RE.test(value)) {
1679
- style.setProperty(prop2, value.replace(IMPORTANT_RE, ""), "important");
1837
+ style.setProperty(prop, value.replace(IMPORTANT_RE, ""), "important");
1680
1838
  } else {
1681
- style.setProperty(prop2, value);
1839
+ style.setProperty(prop, value);
1682
1840
  }
1683
1841
  }
1684
1842
  function toStyleObj(expr) {
@@ -1722,19 +1880,19 @@ ${issueStrings}`);
1722
1880
  if (colonIdx === -1) {
1723
1881
  continue;
1724
1882
  }
1725
- const prop2 = trim.call(part.slice(0, colonIdx));
1883
+ const prop = trim.call(part.slice(0, colonIdx));
1726
1884
  const value = trim.call(part.slice(colonIdx + 1));
1727
- if (prop2 && value && value !== "undefined") {
1728
- result[prop2] = value;
1885
+ if (prop && value && value !== "undefined") {
1886
+ result[prop] = value;
1729
1887
  }
1730
1888
  }
1731
1889
  return result;
1732
1890
  }
1733
1891
  case "object":
1734
- for (let prop2 in expr) {
1735
- const value = expr[prop2];
1892
+ for (let prop in expr) {
1893
+ const value = expr[prop];
1736
1894
  if (value || value === 0) {
1737
- result[toKebabCase(prop2)] = String(value);
1895
+ result[toKebabCase(prop)] = String(value);
1738
1896
  }
1739
1897
  }
1740
1898
  return result;
@@ -1765,22 +1923,22 @@ ${issueStrings}`);
1765
1923
  function setStyle(val) {
1766
1924
  val = val === "" ? {} : toStyleObj(val);
1767
1925
  const style = this.style;
1768
- for (let prop2 in val) {
1769
- setStyleProp(style, prop2, val[prop2]);
1926
+ for (let prop in val) {
1927
+ setStyleProp(style, prop, val[prop]);
1770
1928
  }
1771
1929
  }
1772
1930
  function updateStyle(val, oldVal) {
1773
1931
  oldVal = oldVal === "" ? {} : toStyleObj(oldVal);
1774
1932
  val = val === "" ? {} : toStyleObj(val);
1775
1933
  const style = this.style;
1776
- for (let prop2 in oldVal) {
1777
- if (!(prop2 in val)) {
1778
- style.removeProperty(prop2);
1934
+ for (let prop in oldVal) {
1935
+ if (!(prop in val)) {
1936
+ style.removeProperty(prop);
1779
1937
  }
1780
1938
  }
1781
- for (let prop2 in val) {
1782
- if (val[prop2] !== oldVal[prop2]) {
1783
- setStyleProp(style, prop2, val[prop2]);
1939
+ for (let prop in val) {
1940
+ if (val[prop] !== oldVal[prop]) {
1941
+ setStyleProp(style, prop, val[prop]);
1784
1942
  }
1785
1943
  }
1786
1944
  if (!style.cssText) {
@@ -3272,6 +3430,10 @@ ${issueStrings}`);
3272
3430
  parent;
3273
3431
  children = /* @__PURE__ */ Object.create(null);
3274
3432
  willUpdateProps = [];
3433
+ // Fired right after `props` is applied to `node.props` on a parent re-render,
3434
+ // so reactive prop notifications happen once the new values are observable
3435
+ // (after user `onWillUpdateProps` hooks, including async ones, have run).
3436
+ propsUpdated = [];
3275
3437
  willUnmount = [];
3276
3438
  mounted = [];
3277
3439
  willPatch = [];
@@ -3572,7 +3734,7 @@ ${issueStrings}`);
3572
3734
  }
3573
3735
  };
3574
3736
  var ObjectCreate = Object.create;
3575
- function withDefault(value, defaultValue) {
3737
+ function withDefault2(value, defaultValue) {
3576
3738
  return value === void 0 || value === null || value === false ? defaultValue : value;
3577
3739
  }
3578
3740
  function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
@@ -3806,6 +3968,7 @@ ${issueStrings}`);
3806
3968
  () => {
3807
3969
  if (fiber !== node.fiber) return;
3808
3970
  node.props = props2;
3971
+ for (const f of node.propsUpdated) f();
3809
3972
  fiber.render();
3810
3973
  },
3811
3974
  (error) => {
@@ -3814,6 +3977,7 @@ ${issueStrings}`);
3814
3977
  );
3815
3978
  } else {
3816
3979
  node.props = props2;
3980
+ for (const f of node.propsUpdated) f();
3817
3981
  fiber.render();
3818
3982
  }
3819
3983
  }
@@ -3856,7 +4020,7 @@ ${issueStrings}`);
3856
4020
  return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
3857
4021
  }
3858
4022
  var helpers = {
3859
- withDefault,
4023
+ withDefault: withDefault2,
3860
4024
  zero: /* @__PURE__ */ Symbol("zero"),
3861
4025
  callSlot,
3862
4026
  withKey,
@@ -4186,56 +4350,89 @@ ${issueStrings}`);
4186
4350
  }
4187
4351
  handlers.push(callback.bind(scope.component));
4188
4352
  }
4189
- function componentType() {
4190
- return constructorType(Component);
4191
- }
4192
- var types2 = { ...types, component: componentType };
4193
- function validateDefaults(schema) {
4194
- const validation = {};
4195
- if (Array.isArray(schema)) {
4196
- for (const key of schema) {
4197
- if (key.endsWith("?")) {
4198
- validation[key] = types2.any();
4199
- }
4353
+ function staticProp(key, type) {
4354
+ const node = getComponentScope();
4355
+ const defaultFactory = getDefault(type);
4356
+ const propValue = node.props[key];
4357
+ if (node.app.dev) {
4358
+ if (type !== void 0 && (!defaultFactory || propValue !== void 0)) {
4359
+ assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4200
4360
  }
4201
- } else {
4202
- for (const key in schema) {
4203
- if (key.endsWith("?")) {
4204
- validation[key] = schema[key];
4361
+ node.willUpdateProps.push((nextProps) => {
4362
+ if (nextProps[key] !== node.props[key]) {
4363
+ throw new OwlError(
4364
+ `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).`
4365
+ );
4205
4366
  }
4206
- }
4367
+ });
4207
4368
  }
4208
- return types2.strictObject(validation);
4369
+ return propValue === void 0 && defaultFactory ? defaultFactory() : propValue;
4209
4370
  }
4210
- function props(type, defaults) {
4371
+ function componentType() {
4372
+ return constructorType(Component);
4373
+ }
4374
+ var types2 = {
4375
+ ...types,
4376
+ component: componentType
4377
+ };
4378
+ function makeProps(type) {
4211
4379
  const node = getComponentScope();
4212
4380
  const { app, componentName } = node;
4381
+ let defaults = null;
4382
+ if (type && !Array.isArray(type)) {
4383
+ for (const key in type) {
4384
+ const factory = getDefault(type[key]);
4385
+ if (factory) {
4386
+ (defaults ||= {})[key] = factory();
4387
+ }
4388
+ }
4389
+ }
4213
4390
  if (defaults) {
4214
4391
  node.defaultProps = Object.assign(node.defaultProps || {}, defaults);
4215
4392
  }
4216
- function getProp(key) {
4217
- if (node.props[key] === void 0 && defaults) {
4393
+ function resolveValue(props2, key) {
4394
+ if (props2[key] === void 0 && defaults && key in defaults) {
4218
4395
  return defaults[key];
4219
4396
  }
4220
- return node.props[key];
4397
+ return props2[key];
4221
4398
  }
4399
+ const signals = /* @__PURE__ */ Object.create(null);
4222
4400
  const result = /* @__PURE__ */ Object.create(null);
4223
- function applyPropGetters(keys) {
4401
+ function defineProp(key) {
4402
+ signals[key] = signal(resolveValue(node.props, key));
4403
+ Reflect.defineProperty(result, key, {
4404
+ enumerable: true,
4405
+ configurable: true,
4406
+ get: signals[key]
4407
+ });
4408
+ }
4409
+ function defineProps(keys) {
4224
4410
  for (const key of keys) {
4225
- Reflect.defineProperty(result, key, {
4226
- enumerable: true,
4227
- get: getProp.bind(null, key)
4228
- });
4411
+ defineProp(key);
4412
+ }
4413
+ }
4414
+ function updateSignals(keys) {
4415
+ for (const key of keys) {
4416
+ signals[key].set(resolveValue(node.props, key));
4229
4417
  }
4230
4418
  }
4231
4419
  if (type) {
4232
- const keys = (Array.isArray(type) ? type : Object.keys(type)).map(
4233
- (key) => key.endsWith("?") ? key.slice(0, -1) : key
4234
- );
4235
- applyPropGetters(keys);
4420
+ const keys = Array.isArray(type) ? type : Object.keys(type);
4421
+ defineProps(keys);
4422
+ node.propsUpdated.push(() => updateSignals(keys));
4236
4423
  if (app.dev) {
4237
4424
  if (defaults) {
4238
- assertType(defaults, validateDefaults(type), `Invalid component default props (${componentName})`);
4425
+ const defaultedShape = {};
4426
+ for (const key in type) {
4427
+ if (key in defaults) {
4428
+ defaultedShape[key] = type[key];
4429
+ }
4430
+ }
4431
+ assertType(
4432
+ defaults,
4433
+ types2.object(defaultedShape),
4434
+ `Invalid component default props (${componentName})`
4435
+ );
4239
4436
  }
4240
4437
  const validation = types2.object(type);
4241
4438
  assertType(node.props, validation, `Invalid component props (${componentName})`);
@@ -4251,27 +4448,31 @@ ${issueStrings}`);
4251
4448
  keys2.push(k);
4252
4449
  }
4253
4450
  }
4254
- if (defaults) {
4255
- for (const k in defaults) {
4256
- if (!(k in props2)) {
4257
- keys2.push(k);
4258
- }
4259
- }
4260
- }
4261
4451
  return keys2;
4262
4452
  };
4263
4453
  let keys = getKeys(node.props);
4264
- applyPropGetters(keys);
4265
- node.willUpdateProps.push((np) => {
4454
+ defineProps(keys);
4455
+ node.propsUpdated.push(() => {
4456
+ const nextKeys = getKeys(node.props);
4457
+ const nextKeySet = new Set(nextKeys);
4266
4458
  for (const key of keys) {
4267
- Reflect.deleteProperty(result, key);
4459
+ if (!nextKeySet.has(key)) {
4460
+ Reflect.deleteProperty(result, key);
4461
+ delete signals[key];
4462
+ }
4463
+ }
4464
+ for (const key of nextKeys) {
4465
+ if (!(key in signals)) {
4466
+ defineProp(key);
4467
+ }
4268
4468
  }
4269
- keys = getKeys(np);
4270
- applyPropGetters(keys);
4469
+ updateSignals(nextKeys);
4470
+ keys = nextKeys;
4271
4471
  });
4272
4472
  }
4273
4473
  return result;
4274
4474
  }
4475
+ var props = Object.assign(makeProps, { static: staticProp });
4275
4476
  var ErrorBoundary = class extends Component {
4276
4477
  static template = xml`
4277
4478
  <t t-if="this.props.error()">
@@ -4281,7 +4482,7 @@ ${issueStrings}`);
4281
4482
  <t t-call-slot="default"/>
4282
4483
  </t>
4283
4484
  `;
4284
- props = props({ "error?": types2.signal() }, { error: signal(null) });
4485
+ props = props({ error: types2.signal().default(() => signal(null)) });
4285
4486
  setup() {
4286
4487
  onError((e) => this.props.error.set(e));
4287
4488
  }
@@ -4342,7 +4543,7 @@ ${issueStrings}`);
4342
4543
  <t t-call-slot="fallback"/>
4343
4544
  </t>
4344
4545
  `;
4345
- props = props({ slots: types2.object(["default", "fallback?"]) });
4546
+ props = props({ slots: types2.object({ default: types2.any(), fallback: types2.any().optional() }) });
4346
4547
  prepared = signal(false);
4347
4548
  mounted = signal(false);
4348
4549
  subRootMounted = false;
@@ -4370,24 +4571,6 @@ ${issueStrings}`);
4370
4571
  onWillDestroy(() => root.destroy());
4371
4572
  }
4372
4573
  };
4373
- function prop(key, type, ...args) {
4374
- const node = getComponentScope();
4375
- const hasDefault = args.length > 0;
4376
- const propValue = node.props[key];
4377
- if (node.app.dev) {
4378
- if (type !== void 0 && (!hasDefault || propValue !== void 0)) {
4379
- assertType(propValue, type, `Invalid prop '${key}' in '${node.componentName}'`);
4380
- }
4381
- node.willUpdateProps.push((nextProps) => {
4382
- if (nextProps[key] !== node.props[key]) {
4383
- throw new OwlError(
4384
- `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).`
4385
- );
4386
- }
4387
- });
4388
- }
4389
- return propValue === void 0 && hasDefault ? args[0] : propValue;
4390
- }
4391
4574
  function providePlugins(pluginConstructors, config3) {
4392
4575
  const node = getComponentScope();
4393
4576
  const manager = new PluginManager(node.app, { parent: node.pluginManager, config: config3 });
@@ -4416,8 +4599,8 @@ ${issueStrings}`);
4416
4599
  };
4417
4600
  var __info__ = {
4418
4601
  version: App.version,
4419
- date: "2026-05-28T13:29:02.340Z",
4420
- hash: "fadc22b7",
4602
+ date: "2026-06-11T09:51:14.980Z",
4603
+ hash: "739fc964",
4421
4604
  url: "https://github.com/odoo/owl"
4422
4605
  };
4423
4606