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

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
@@ -115,13 +115,15 @@ function removeSources(computation) {
115
115
  sources.clear();
116
116
  }
117
117
  function disposeComputation(computation) {
118
- for (const source of computation.sources) {
118
+ const sources = computation.sources;
119
+ for (const source of sources) {
119
120
  source.observers.delete(computation);
120
- if ("compute" in source && source.isDerived && source.observers.size === 0) {
121
- disposeComputation(source);
121
+ const derived = source;
122
+ if (derived.isDerived && derived.observers.size === 0) {
123
+ disposeComputation(derived);
122
124
  }
123
125
  }
124
- computation.sources.clear();
126
+ sources.clear();
125
127
  computation.state = 1;
126
128
  }
127
129
  function markDownstream(computation) {
@@ -162,6 +164,7 @@ function useScope() {
162
164
  }
163
165
  var Scope = class {
164
166
  app;
167
+ pluginManager;
165
168
  status = STATUS.NEW;
166
169
  computations = [];
167
170
  willStart = [];
@@ -169,6 +172,7 @@ var Scope = class {
169
172
  _destroyCbs = null;
170
173
  constructor(app) {
171
174
  this.app = app;
175
+ this.pluginManager = app.pluginManager;
172
176
  }
173
177
  /**
174
178
  * Pushes this scope on the stack for the duration of `callback`. Any code
@@ -612,14 +616,19 @@ function computed(getter, options = {}) {
612
616
  }
613
617
  function effect(fn) {
614
618
  const computation = createComputation(() => {
615
- setComputation(void 0);
616
- unsubscribeEffect(computation);
617
- setComputation(computation);
619
+ if (computation.value || computation.observers.size) {
620
+ setComputation(void 0);
621
+ unsubscribeEffect(computation);
622
+ setComputation(computation);
623
+ } else {
624
+ removeSources(computation);
625
+ }
618
626
  return fn();
619
627
  }, false);
620
628
  getCurrentComputation()?.observers.add(computation);
621
629
  updateComputation(computation);
622
630
  return function cleanupEffect2() {
631
+ computation.state = 0;
623
632
  const previousComputation = getCurrentComputation();
624
633
  setComputation(void 0);
625
634
  unsubscribeEffect(computation);
@@ -631,7 +640,6 @@ function unsubscribeEffect(effect2) {
631
640
  cleanupEffect(effect2);
632
641
  for (const childEffect of effect2.observers) {
633
642
  childEffect.state = 0;
634
- removeSources(childEffect);
635
643
  unsubscribeEffect(childEffect);
636
644
  }
637
645
  effect2.observers.clear();
@@ -708,19 +716,27 @@ function asyncComputed(fetcher, options = {}) {
708
716
  read.dispose = dispose;
709
717
  return read;
710
718
  }
719
+ function safeReplacer(knownObjects, _key, value) {
720
+ if (typeof value === "function") {
721
+ return value.name || "[Function]";
722
+ }
723
+ if (value && typeof value === "object") {
724
+ const ctor = value.constructor;
725
+ if (ctor && ctor !== Object && ctor !== Array) {
726
+ return `[Instance of ${ctor.name || "anonymous"}]`;
727
+ }
728
+ if (knownObjects.includes(value)) {
729
+ return `[Known object]`;
730
+ }
731
+ knownObjects.push(value);
732
+ }
733
+ return value;
734
+ }
711
735
  function assertType(value, validation, errorMessage = "Value does not match the type") {
712
736
  const issues = validateType(value, validation);
713
737
  if (issues.length) {
714
- const issueStrings = JSON.stringify(
715
- issues,
716
- (key, value2) => {
717
- if (typeof value2 === "function") {
718
- return value2.name;
719
- }
720
- return value2;
721
- },
722
- 2
723
- );
738
+ const knownObjects = [];
739
+ const issueStrings = JSON.stringify(issues, safeReplacer.bind(null, knownObjects), 2);
724
740
  throw new OwlError(`${errorMessage}
725
741
  ${issueStrings}`);
726
742
  }
@@ -782,7 +798,7 @@ function numberType() {
782
798
  }
783
799
  function stringType() {
784
800
  return function validateString(context) {
785
- if (typeof context.value !== "string") {
801
+ if (typeof context.value !== "string" && !(context.value instanceof String)) {
786
802
  context.addIssue({ message: "value is not a string" });
787
803
  }
788
804
  };
@@ -861,15 +877,20 @@ function validateObject(context, schema, isStrict) {
861
877
  return;
862
878
  }
863
879
  const isShape = !Array.isArray(schema);
864
- let shape = schema;
865
- if (Array.isArray(schema)) {
880
+ let shape;
881
+ let keys;
882
+ if (isShape) {
883
+ keys = Object.keys(schema);
884
+ shape = schema;
885
+ } else {
886
+ keys = schema;
866
887
  shape = {};
867
- for (const key of schema) {
888
+ for (const key of keys) {
868
889
  shape[key] = null;
869
890
  }
870
891
  }
871
892
  const missingKeys = [];
872
- for (const key in shape) {
893
+ for (const key of keys) {
873
894
  const property = key.endsWith("?") ? key.slice(0, -1) : key;
874
895
  if (context.value[property] === void 0) {
875
896
  if (!key.endsWith("?")) {
@@ -884,20 +905,22 @@ function validateObject(context, schema, isStrict) {
884
905
  if (missingKeys.length) {
885
906
  context.addIssue({
886
907
  message: "object value has missing keys",
887
- missingKeys
908
+ missingKeys,
909
+ expectedKeys: keys
888
910
  });
889
911
  }
890
912
  if (isStrict) {
891
913
  const unknownKeys = [];
892
914
  for (const key in context.value) {
893
- if (!(key in shape) && !(`${key}?` in shape)) {
915
+ if (!keys.includes(key) && !(`${key}?` in shape)) {
894
916
  unknownKeys.push(key);
895
917
  }
896
918
  }
897
919
  if (unknownKeys.length) {
898
920
  context.addIssue({
899
921
  message: "object value has unknown keys",
900
- unknownKeys
922
+ unknownKeys,
923
+ expectedKeys: keys
901
924
  });
902
925
  }
903
926
  }
@@ -1115,12 +1138,13 @@ var PluginManager = class extends Scope {
1115
1138
  plugins;
1116
1139
  // Resolves once all pending plugin willStart callbacks have settled. The
1117
1140
  // scope transitions to MOUNTED as the last step of this chain. Consumers
1118
- // (App.mount, providePlugins) await this before treating the manager as
1119
- // ready. `willStart` itself is inherited from Scope.
1141
+ // (the root's mount(), providePlugins) await this before treating the
1142
+ // manager as ready. `willStart` itself is inherited from Scope.
1120
1143
  ready = Promise.resolve();
1121
1144
  constructor(app, options = {}) {
1122
1145
  super(app);
1123
1146
  this.config = options.config ?? {};
1147
+ this.pluginManager = this;
1124
1148
  if (options.parent) {
1125
1149
  const parent = options.parent;
1126
1150
  parent.onDestroy(() => this.destroy());
@@ -1189,9 +1213,103 @@ function startPlugins(manager, plugins) {
1189
1213
  );
1190
1214
  }
1191
1215
  }
1216
+ function onWillStart(fn) {
1217
+ const scope = useScope();
1218
+ scope.willStart.push(scope.decorate(fn, "onWillStart"));
1219
+ }
1220
+ function onWillDestroy(fn) {
1221
+ const scope = useScope();
1222
+ scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
1223
+ }
1224
+ function useEffect(fn) {
1225
+ onWillDestroy(effect(fn));
1226
+ }
1227
+ function useListener(target, eventName, handler, eventParams) {
1228
+ if (typeof target === "function") {
1229
+ useEffect(() => {
1230
+ const el = target();
1231
+ if (el) {
1232
+ el.addEventListener(eventName, handler, eventParams);
1233
+ return () => el.removeEventListener(eventName, handler, eventParams);
1234
+ }
1235
+ return;
1236
+ });
1237
+ } else {
1238
+ target.addEventListener(eventName, handler, eventParams);
1239
+ onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
1240
+ }
1241
+ }
1242
+ function useApp() {
1243
+ return useScope().app;
1244
+ }
1245
+ function plugin(pluginType) {
1246
+ const scope = useScope();
1247
+ let plugin2 = scope.pluginManager.getPluginById(pluginType.id);
1248
+ if (!plugin2) {
1249
+ if (scope instanceof PluginManager) {
1250
+ plugin2 = scope.pluginManager.startPlugin(pluginType);
1251
+ } else {
1252
+ throw new OwlError(`Unknown plugin "${pluginType.id}"`);
1253
+ }
1254
+ }
1255
+ return plugin2;
1256
+ }
1257
+ function config(key, type, defaultValue) {
1258
+ const scope = useScope();
1259
+ if (!(scope instanceof PluginManager)) {
1260
+ throw new OwlError("Expected to be in a plugin scope");
1261
+ }
1262
+ if (scope.app.dev && type) {
1263
+ assertType(scope.config, types.object({ [key]: type }), "Config does not match the type");
1264
+ }
1265
+ const configValue = scope.config[key.endsWith("?") ? key.slice(0, -1) : key];
1266
+ return configValue === void 0 ? defaultValue : configValue;
1267
+ }
1268
+ var EventBus = class extends EventTarget {
1269
+ trigger(name, payload) {
1270
+ this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1271
+ }
1272
+ };
1273
+ var Markup = class extends String {
1274
+ };
1275
+ function htmlEscape(str) {
1276
+ if (str instanceof Markup) {
1277
+ return str;
1278
+ }
1279
+ if (str === void 0) {
1280
+ return markup("");
1281
+ }
1282
+ if (typeof str === "number") {
1283
+ return markup(String(str));
1284
+ }
1285
+ [
1286
+ ["&", "&"],
1287
+ ["<", "&lt;"],
1288
+ [">", "&gt;"],
1289
+ ["'", "&#x27;"],
1290
+ ['"', "&quot;"],
1291
+ ["`", "&#x60;"]
1292
+ ].forEach((pairs) => {
1293
+ str = String(str).replace(new RegExp(pairs[0], "g"), pairs[1]);
1294
+ });
1295
+ return markup(str);
1296
+ }
1297
+ function markup(valueOrStrings, ...placeholders) {
1298
+ if (!Array.isArray(valueOrStrings)) {
1299
+ return new Markup(valueOrStrings);
1300
+ }
1301
+ const strings = valueOrStrings;
1302
+ let acc = "";
1303
+ let i = 0;
1304
+ for (; i < placeholders.length; ++i) {
1305
+ acc += strings[i] + htmlEscape(placeholders[i]);
1306
+ }
1307
+ acc += strings[i];
1308
+ return new Markup(acc);
1309
+ }
1192
1310
 
1193
1311
  // ../owl-runtime/dist/owl-runtime.es.js
1194
- var version = "3.0.0-alpha.31";
1312
+ var version = "3.0.0-alpha.33";
1195
1313
  var fibersInError = /* @__PURE__ */ new WeakMap();
1196
1314
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1197
1315
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -1264,7 +1382,7 @@ function filterOutModifiersFromData(dataList) {
1264
1382
  }
1265
1383
  return { modifiers, data: dataList };
1266
1384
  }
1267
- var config = {
1385
+ var config2 = {
1268
1386
  // whether or not blockdom should normalize DOM whenever a block is created.
1269
1387
  // Normalizing dom mean removing empty text nodes (or containing only spaces)
1270
1388
  shouldNormalizeDom: true,
@@ -1638,11 +1756,6 @@ function validateTarget(target) {
1638
1756
  }
1639
1757
  throw new OwlError("Cannot mount component: the target is not a valid DOM element");
1640
1758
  }
1641
- var EventBus = class extends EventTarget {
1642
- trigger(name, payload) {
1643
- this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1644
- }
1645
- };
1646
1759
  function whenReady(fn) {
1647
1760
  return new Promise(function(resolve) {
1648
1761
  if (document.readyState !== "loading") {
@@ -1653,43 +1766,6 @@ function whenReady(fn) {
1653
1766
  }).then(fn || function() {
1654
1767
  });
1655
1768
  }
1656
- var Markup = class extends String {
1657
- };
1658
- function htmlEscape(str) {
1659
- if (str instanceof Markup) {
1660
- return str;
1661
- }
1662
- if (str === void 0) {
1663
- return markup("");
1664
- }
1665
- if (typeof str === "number") {
1666
- return markup(String(str));
1667
- }
1668
- [
1669
- ["&", "&amp;"],
1670
- ["<", "&lt;"],
1671
- [">", "&gt;"],
1672
- ["'", "&#x27;"],
1673
- ['"', "&quot;"],
1674
- ["`", "&#x60;"]
1675
- ].forEach((pairs) => {
1676
- str = String(str).replace(new RegExp(pairs[0], "g"), pairs[1]);
1677
- });
1678
- return markup(str);
1679
- }
1680
- function markup(valueOrStrings, ...placeholders) {
1681
- if (!Array.isArray(valueOrStrings)) {
1682
- return new Markup(valueOrStrings);
1683
- }
1684
- const strings = valueOrStrings;
1685
- let acc = "";
1686
- let i = 0;
1687
- for (; i < placeholders.length; ++i) {
1688
- acc += strings[i] + htmlEscape(placeholders[i]);
1689
- }
1690
- acc += strings[i];
1691
- return new Markup(acc);
1692
- }
1693
1769
  function createEventHandler(rawEvent) {
1694
1770
  const eventName = rawEvent.split(".")[0];
1695
1771
  const capture = rawEvent.includes(".capture");
@@ -1711,7 +1787,7 @@ function createElementHandler(evName, capture = false, passive = false) {
1711
1787
  if (!currentTarget || !inOwnerDocument(currentTarget)) return;
1712
1788
  const data = currentTarget[eventKey];
1713
1789
  if (!data) return;
1714
- config.mainEventHandler(data, ev, currentTarget);
1790
+ config2.mainEventHandler(data, ev, currentTarget);
1715
1791
  }
1716
1792
  const options = { capture, passive };
1717
1793
  function setup(data) {
@@ -1751,7 +1827,7 @@ function nativeToSyntheticEvent(eventKey, event) {
1751
1827
  const _data = dom[eventKey];
1752
1828
  if (_data) {
1753
1829
  for (const data of Object.values(_data)) {
1754
- const stopped = config.mainEventHandler(data, event, dom);
1830
+ const stopped = config2.mainEventHandler(data, event, dom);
1755
1831
  if (stopped) return;
1756
1832
  }
1757
1833
  }
@@ -1997,7 +2073,7 @@ function createBlock(str) {
1997
2073
  }
1998
2074
  const doc = new DOMParser().parseFromString(`<t>${str}</t>`, "text/xml");
1999
2075
  const node = doc.firstChild.firstChild;
2000
- if (config.shouldNormalizeDom) {
2076
+ if (config2.shouldNormalizeDom) {
2001
2077
  normalizeNode(node);
2002
2078
  }
2003
2079
  const tree = buildTree(node);
@@ -2908,6 +2984,9 @@ function cancelFibers(fibers) {
2908
2984
  node.forceNextRender = true;
2909
2985
  } else {
2910
2986
  result++;
2987
+ if (node.bdom) {
2988
+ node.forceNextRender = true;
2989
+ }
2911
2990
  }
2912
2991
  result += cancelFibers(fiber.children);
2913
2992
  }
@@ -3122,7 +3201,6 @@ var ComponentNode = class extends Scope {
3122
3201
  willPatch = [];
3123
3202
  patched = [];
3124
3203
  signalComputation;
3125
- pluginManager;
3126
3204
  constructor(C, props2, app, parent, parentKey) {
3127
3205
  super(app);
3128
3206
  this.parent = parent;
@@ -4000,10 +4078,6 @@ var mainEventHandler = (data, ev, currentTarget) => {
4000
4078
  }
4001
4079
  return stopped;
4002
4080
  };
4003
- function onWillStart(fn) {
4004
- const scope = useScope();
4005
- scope.willStart.push(scope.decorate(fn, "onWillStart"));
4006
- }
4007
4081
  function onWillUpdateProps(fn) {
4008
4082
  const scope = getComponentScope();
4009
4083
  function swapped(s, nextProps) {
@@ -4027,10 +4101,6 @@ function onWillUnmount(fn) {
4027
4101
  const scope = getComponentScope();
4028
4102
  scope.willUnmount.unshift(scope.decorate(fn, "onWillUnmount"));
4029
4103
  }
4030
- function onWillDestroy(fn) {
4031
- const scope = useScope();
4032
- scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
4033
- }
4034
4104
  function onError(callback) {
4035
4105
  const scope = getComponentScope();
4036
4106
  let handlers = nodeErrorHandlers.get(scope);
@@ -4140,27 +4210,7 @@ var ErrorBoundary = class extends Component {
4140
4210
  onError((e) => this.props.error.set(e));
4141
4211
  }
4142
4212
  };
4143
- function useEffect(fn) {
4144
- onWillDestroy(effect(fn));
4145
- }
4146
- function useListener(target, eventName, handler, eventParams) {
4147
- if (typeof target === "function") {
4148
- useEffect(() => {
4149
- const el = target();
4150
- if (el) {
4151
- el.addEventListener(eventName, handler, eventParams);
4152
- return () => el.removeEventListener(eventName, handler, eventParams);
4153
- }
4154
- return;
4155
- });
4156
- } else {
4157
- target.addEventListener(eventName, handler, eventParams);
4158
- onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
4159
- }
4160
- }
4161
- function useApp() {
4162
- return useScope().app;
4163
- }
4213
+ var useApp2 = useApp;
4164
4214
  var PortalContent = class extends Component {
4165
4215
  static template = xml`<t t-call-slot="default"/>`;
4166
4216
  };
@@ -4262,29 +4312,6 @@ function prop(key, type, ...args) {
4262
4312
  }
4263
4313
  return propValue === void 0 && hasDefault ? args[0] : propValue;
4264
4314
  }
4265
- function plugin(pluginType) {
4266
- const scope = useScope();
4267
- const manager = scope instanceof ComponentNode ? scope.pluginManager : scope;
4268
- let plugin2 = manager.getPluginById(pluginType.id);
4269
- if (!plugin2) {
4270
- if (scope instanceof PluginManager) {
4271
- plugin2 = manager.startPlugin(pluginType);
4272
- } else {
4273
- throw new OwlError(`Unknown plugin "${pluginType.id}"`);
4274
- }
4275
- }
4276
- return plugin2;
4277
- }
4278
- function config2(name, type) {
4279
- const scope = useScope();
4280
- if (!(scope instanceof PluginManager)) {
4281
- throw new OwlError("Expected to be in a plugin scope");
4282
- }
4283
- if (scope.app.dev && type) {
4284
- assertType(scope.config, types2.object({ [name]: type }), "Config does not match the type");
4285
- }
4286
- return scope.config[name.endsWith("?") ? name.slice(0, -1) : name];
4287
- }
4288
4315
  function providePlugins(pluginConstructors, config3) {
4289
4316
  const node = getComponentScope();
4290
4317
  const manager = new PluginManager(node.app, { parent: node.pluginManager, config: config3 });
@@ -4295,10 +4322,10 @@ function providePlugins(pluginConstructors, config3) {
4295
4322
  onWillStart(() => manager.ready);
4296
4323
  }
4297
4324
  }
4298
- config.shouldNormalizeDom = false;
4299
- config.mainEventHandler = mainEventHandler;
4325
+ config2.shouldNormalizeDom = false;
4326
+ config2.mainEventHandler = mainEventHandler;
4300
4327
  var blockDom = {
4301
- config,
4328
+ config: config2,
4302
4329
  // bdom entry points
4303
4330
  mount,
4304
4331
  patch,
@@ -4313,8 +4340,8 @@ var blockDom = {
4313
4340
  };
4314
4341
  var __info__ = {
4315
4342
  version: App.version,
4316
- date: "2026-05-20T14:11:32.436Z",
4317
- hash: "25eaac4e",
4343
+ date: "2026-05-28T13:29:02.340Z",
4344
+ hash: "fadc22b7",
4318
4345
  url: "https://github.com/odoo/owl"
4319
4346
  };
4320
4347
 
@@ -6508,7 +6535,7 @@ export {
6508
6535
  batched,
6509
6536
  blockDom,
6510
6537
  computed,
6511
- config2 as config,
6538
+ config,
6512
6539
  effect,
6513
6540
  getScope,
6514
6541
  globalTemplates,
@@ -6534,7 +6561,7 @@ export {
6534
6561
  toRaw,
6535
6562
  types2 as types,
6536
6563
  untrack,
6537
- useApp,
6564
+ useApp2 as useApp,
6538
6565
  useEffect,
6539
6566
  useListener,
6540
6567
  useScope,