@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.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,
@@ -65,7 +65,7 @@ var owl = (() => {
65
65
  toRaw: () => toRaw,
66
66
  types: () => types2,
67
67
  untrack: () => untrack,
68
- useApp: () => useApp,
68
+ useApp: () => useApp2,
69
69
  useEffect: () => useEffect,
70
70
  useListener: () => useListener,
71
71
  useScope: () => useScope,
@@ -191,13 +191,15 @@ var owl = (() => {
191
191
  sources.clear();
192
192
  }
193
193
  function disposeComputation(computation) {
194
- for (const source of computation.sources) {
194
+ const sources = computation.sources;
195
+ for (const source of sources) {
195
196
  source.observers.delete(computation);
196
- if ("compute" in source && source.isDerived && source.observers.size === 0) {
197
- disposeComputation(source);
197
+ const derived = source;
198
+ if (derived.isDerived && derived.observers.size === 0) {
199
+ disposeComputation(derived);
198
200
  }
199
201
  }
200
- computation.sources.clear();
202
+ sources.clear();
201
203
  computation.state = 1;
202
204
  }
203
205
  function markDownstream(computation) {
@@ -238,6 +240,7 @@ var owl = (() => {
238
240
  }
239
241
  var Scope = class {
240
242
  app;
243
+ pluginManager;
241
244
  status = STATUS.NEW;
242
245
  computations = [];
243
246
  willStart = [];
@@ -245,6 +248,7 @@ var owl = (() => {
245
248
  _destroyCbs = null;
246
249
  constructor(app) {
247
250
  this.app = app;
251
+ this.pluginManager = app.pluginManager;
248
252
  }
249
253
  /**
250
254
  * Pushes this scope on the stack for the duration of `callback`. Any code
@@ -688,14 +692,19 @@ var owl = (() => {
688
692
  }
689
693
  function effect(fn) {
690
694
  const computation = createComputation(() => {
691
- setComputation(void 0);
692
- unsubscribeEffect(computation);
693
- setComputation(computation);
695
+ if (computation.value || computation.observers.size) {
696
+ setComputation(void 0);
697
+ unsubscribeEffect(computation);
698
+ setComputation(computation);
699
+ } else {
700
+ removeSources(computation);
701
+ }
694
702
  return fn();
695
703
  }, false);
696
704
  getCurrentComputation()?.observers.add(computation);
697
705
  updateComputation(computation);
698
706
  return function cleanupEffect2() {
707
+ computation.state = 0;
699
708
  const previousComputation = getCurrentComputation();
700
709
  setComputation(void 0);
701
710
  unsubscribeEffect(computation);
@@ -707,7 +716,6 @@ var owl = (() => {
707
716
  cleanupEffect(effect2);
708
717
  for (const childEffect of effect2.observers) {
709
718
  childEffect.state = 0;
710
- removeSources(childEffect);
711
719
  unsubscribeEffect(childEffect);
712
720
  }
713
721
  effect2.observers.clear();
@@ -784,19 +792,27 @@ var owl = (() => {
784
792
  read.dispose = dispose;
785
793
  return read;
786
794
  }
795
+ function safeReplacer(knownObjects, _key, value) {
796
+ if (typeof value === "function") {
797
+ return value.name || "[Function]";
798
+ }
799
+ if (value && typeof value === "object") {
800
+ const ctor = value.constructor;
801
+ if (ctor && ctor !== Object && ctor !== Array) {
802
+ return `[Instance of ${ctor.name || "anonymous"}]`;
803
+ }
804
+ if (knownObjects.includes(value)) {
805
+ return `[Known object]`;
806
+ }
807
+ knownObjects.push(value);
808
+ }
809
+ return value;
810
+ }
787
811
  function assertType(value, validation, errorMessage = "Value does not match the type") {
788
812
  const issues = validateType(value, validation);
789
813
  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
- );
814
+ const knownObjects = [];
815
+ const issueStrings = JSON.stringify(issues, safeReplacer.bind(null, knownObjects), 2);
800
816
  throw new OwlError(`${errorMessage}
801
817
  ${issueStrings}`);
802
818
  }
@@ -858,7 +874,7 @@ ${issueStrings}`);
858
874
  }
859
875
  function stringType() {
860
876
  return function validateString(context) {
861
- if (typeof context.value !== "string") {
877
+ if (typeof context.value !== "string" && !(context.value instanceof String)) {
862
878
  context.addIssue({ message: "value is not a string" });
863
879
  }
864
880
  };
@@ -937,15 +953,20 @@ ${issueStrings}`);
937
953
  return;
938
954
  }
939
955
  const isShape = !Array.isArray(schema);
940
- let shape = schema;
941
- if (Array.isArray(schema)) {
956
+ let shape;
957
+ let keys;
958
+ if (isShape) {
959
+ keys = Object.keys(schema);
960
+ shape = schema;
961
+ } else {
962
+ keys = schema;
942
963
  shape = {};
943
- for (const key of schema) {
964
+ for (const key of keys) {
944
965
  shape[key] = null;
945
966
  }
946
967
  }
947
968
  const missingKeys = [];
948
- for (const key in shape) {
969
+ for (const key of keys) {
949
970
  const property = key.endsWith("?") ? key.slice(0, -1) : key;
950
971
  if (context.value[property] === void 0) {
951
972
  if (!key.endsWith("?")) {
@@ -960,20 +981,22 @@ ${issueStrings}`);
960
981
  if (missingKeys.length) {
961
982
  context.addIssue({
962
983
  message: "object value has missing keys",
963
- missingKeys
984
+ missingKeys,
985
+ expectedKeys: keys
964
986
  });
965
987
  }
966
988
  if (isStrict) {
967
989
  const unknownKeys = [];
968
990
  for (const key in context.value) {
969
- if (!(key in shape) && !(`${key}?` in shape)) {
991
+ if (!keys.includes(key) && !(`${key}?` in shape)) {
970
992
  unknownKeys.push(key);
971
993
  }
972
994
  }
973
995
  if (unknownKeys.length) {
974
996
  context.addIssue({
975
997
  message: "object value has unknown keys",
976
- unknownKeys
998
+ unknownKeys,
999
+ expectedKeys: keys
977
1000
  });
978
1001
  }
979
1002
  }
@@ -1191,12 +1214,13 @@ ${issueStrings}`);
1191
1214
  plugins;
1192
1215
  // Resolves once all pending plugin willStart callbacks have settled. The
1193
1216
  // 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.
1217
+ // (the root's mount(), providePlugins) await this before treating the
1218
+ // manager as ready. `willStart` itself is inherited from Scope.
1196
1219
  ready = Promise.resolve();
1197
1220
  constructor(app, options = {}) {
1198
1221
  super(app);
1199
1222
  this.config = options.config ?? {};
1223
+ this.pluginManager = this;
1200
1224
  if (options.parent) {
1201
1225
  const parent = options.parent;
1202
1226
  parent.onDestroy(() => this.destroy());
@@ -1265,9 +1289,103 @@ ${issueStrings}`);
1265
1289
  );
1266
1290
  }
1267
1291
  }
1292
+ function onWillStart(fn) {
1293
+ const scope = useScope();
1294
+ scope.willStart.push(scope.decorate(fn, "onWillStart"));
1295
+ }
1296
+ function onWillDestroy(fn) {
1297
+ const scope = useScope();
1298
+ scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
1299
+ }
1300
+ function useEffect(fn) {
1301
+ onWillDestroy(effect(fn));
1302
+ }
1303
+ function useListener(target, eventName, handler, eventParams) {
1304
+ if (typeof target === "function") {
1305
+ useEffect(() => {
1306
+ const el = target();
1307
+ if (el) {
1308
+ el.addEventListener(eventName, handler, eventParams);
1309
+ return () => el.removeEventListener(eventName, handler, eventParams);
1310
+ }
1311
+ return;
1312
+ });
1313
+ } else {
1314
+ target.addEventListener(eventName, handler, eventParams);
1315
+ onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
1316
+ }
1317
+ }
1318
+ function useApp() {
1319
+ return useScope().app;
1320
+ }
1321
+ function plugin(pluginType) {
1322
+ const scope = useScope();
1323
+ let plugin2 = scope.pluginManager.getPluginById(pluginType.id);
1324
+ if (!plugin2) {
1325
+ if (scope instanceof PluginManager) {
1326
+ plugin2 = scope.pluginManager.startPlugin(pluginType);
1327
+ } else {
1328
+ throw new OwlError(`Unknown plugin "${pluginType.id}"`);
1329
+ }
1330
+ }
1331
+ return plugin2;
1332
+ }
1333
+ function config(key, type, defaultValue) {
1334
+ const scope = useScope();
1335
+ if (!(scope instanceof PluginManager)) {
1336
+ throw new OwlError("Expected to be in a plugin scope");
1337
+ }
1338
+ if (scope.app.dev && type) {
1339
+ assertType(scope.config, types.object({ [key]: type }), "Config does not match the type");
1340
+ }
1341
+ const configValue = scope.config[key.endsWith("?") ? key.slice(0, -1) : key];
1342
+ return configValue === void 0 ? defaultValue : configValue;
1343
+ }
1344
+ var EventBus = class extends EventTarget {
1345
+ trigger(name, payload) {
1346
+ this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1347
+ }
1348
+ };
1349
+ var Markup = class extends String {
1350
+ };
1351
+ function htmlEscape(str) {
1352
+ if (str instanceof Markup) {
1353
+ return str;
1354
+ }
1355
+ if (str === void 0) {
1356
+ return markup("");
1357
+ }
1358
+ if (typeof str === "number") {
1359
+ return markup(String(str));
1360
+ }
1361
+ [
1362
+ ["&", "&"],
1363
+ ["<", "&lt;"],
1364
+ [">", "&gt;"],
1365
+ ["'", "&#x27;"],
1366
+ ['"', "&quot;"],
1367
+ ["`", "&#x60;"]
1368
+ ].forEach((pairs) => {
1369
+ str = String(str).replace(new RegExp(pairs[0], "g"), pairs[1]);
1370
+ });
1371
+ return markup(str);
1372
+ }
1373
+ function markup(valueOrStrings, ...placeholders) {
1374
+ if (!Array.isArray(valueOrStrings)) {
1375
+ return new Markup(valueOrStrings);
1376
+ }
1377
+ const strings = valueOrStrings;
1378
+ let acc = "";
1379
+ let i = 0;
1380
+ for (; i < placeholders.length; ++i) {
1381
+ acc += strings[i] + htmlEscape(placeholders[i]);
1382
+ }
1383
+ acc += strings[i];
1384
+ return new Markup(acc);
1385
+ }
1268
1386
 
1269
1387
  // ../owl-runtime/dist/owl-runtime.es.js
1270
- var version = "3.0.0-alpha.31";
1388
+ var version = "3.0.0-alpha.33";
1271
1389
  var fibersInError = /* @__PURE__ */ new WeakMap();
1272
1390
  var nodeErrorHandlers = /* @__PURE__ */ new WeakMap();
1273
1391
  function invokeErrorHandlers(node, error, finalize, markFibers) {
@@ -1340,7 +1458,7 @@ ${issueStrings}`);
1340
1458
  }
1341
1459
  return { modifiers, data: dataList };
1342
1460
  }
1343
- var config = {
1461
+ var config2 = {
1344
1462
  // whether or not blockdom should normalize DOM whenever a block is created.
1345
1463
  // Normalizing dom mean removing empty text nodes (or containing only spaces)
1346
1464
  shouldNormalizeDom: true,
@@ -1714,11 +1832,6 @@ ${issueStrings}`);
1714
1832
  }
1715
1833
  throw new OwlError("Cannot mount component: the target is not a valid DOM element");
1716
1834
  }
1717
- var EventBus = class extends EventTarget {
1718
- trigger(name, payload) {
1719
- this.dispatchEvent(new CustomEvent(name, { detail: payload }));
1720
- }
1721
- };
1722
1835
  function whenReady(fn) {
1723
1836
  return new Promise(function(resolve) {
1724
1837
  if (document.readyState !== "loading") {
@@ -1729,43 +1842,6 @@ ${issueStrings}`);
1729
1842
  }).then(fn || function() {
1730
1843
  });
1731
1844
  }
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
1845
  function createEventHandler(rawEvent) {
1770
1846
  const eventName = rawEvent.split(".")[0];
1771
1847
  const capture = rawEvent.includes(".capture");
@@ -1787,7 +1863,7 @@ ${issueStrings}`);
1787
1863
  if (!currentTarget || !inOwnerDocument(currentTarget)) return;
1788
1864
  const data = currentTarget[eventKey];
1789
1865
  if (!data) return;
1790
- config.mainEventHandler(data, ev, currentTarget);
1866
+ config2.mainEventHandler(data, ev, currentTarget);
1791
1867
  }
1792
1868
  const options = { capture, passive };
1793
1869
  function setup(data) {
@@ -1827,7 +1903,7 @@ ${issueStrings}`);
1827
1903
  const _data = dom[eventKey];
1828
1904
  if (_data) {
1829
1905
  for (const data of Object.values(_data)) {
1830
- const stopped = config.mainEventHandler(data, event, dom);
1906
+ const stopped = config2.mainEventHandler(data, event, dom);
1831
1907
  if (stopped) return;
1832
1908
  }
1833
1909
  }
@@ -2073,7 +2149,7 @@ ${issueStrings}`);
2073
2149
  }
2074
2150
  const doc = new DOMParser().parseFromString(`<t>${str}</t>`, "text/xml");
2075
2151
  const node = doc.firstChild.firstChild;
2076
- if (config.shouldNormalizeDom) {
2152
+ if (config2.shouldNormalizeDom) {
2077
2153
  normalizeNode(node);
2078
2154
  }
2079
2155
  const tree = buildTree(node);
@@ -2984,6 +3060,9 @@ ${issueStrings}`);
2984
3060
  node.forceNextRender = true;
2985
3061
  } else {
2986
3062
  result++;
3063
+ if (node.bdom) {
3064
+ node.forceNextRender = true;
3065
+ }
2987
3066
  }
2988
3067
  result += cancelFibers(fiber.children);
2989
3068
  }
@@ -3198,7 +3277,6 @@ ${issueStrings}`);
3198
3277
  willPatch = [];
3199
3278
  patched = [];
3200
3279
  signalComputation;
3201
- pluginManager;
3202
3280
  constructor(C, props2, app, parent, parentKey) {
3203
3281
  super(app);
3204
3282
  this.parent = parent;
@@ -4076,10 +4154,6 @@ ${issueStrings}`);
4076
4154
  }
4077
4155
  return stopped;
4078
4156
  };
4079
- function onWillStart(fn) {
4080
- const scope = useScope();
4081
- scope.willStart.push(scope.decorate(fn, "onWillStart"));
4082
- }
4083
4157
  function onWillUpdateProps(fn) {
4084
4158
  const scope = getComponentScope();
4085
4159
  function swapped(s, nextProps) {
@@ -4103,10 +4177,6 @@ ${issueStrings}`);
4103
4177
  const scope = getComponentScope();
4104
4178
  scope.willUnmount.unshift(scope.decorate(fn, "onWillUnmount"));
4105
4179
  }
4106
- function onWillDestroy(fn) {
4107
- const scope = useScope();
4108
- scope.onDestroy(scope.decorate(fn, "onWillDestroy"));
4109
- }
4110
4180
  function onError(callback) {
4111
4181
  const scope = getComponentScope();
4112
4182
  let handlers = nodeErrorHandlers.get(scope);
@@ -4216,27 +4286,7 @@ ${issueStrings}`);
4216
4286
  onError((e) => this.props.error.set(e));
4217
4287
  }
4218
4288
  };
4219
- function useEffect(fn) {
4220
- onWillDestroy(effect(fn));
4221
- }
4222
- function useListener(target, eventName, handler, eventParams) {
4223
- if (typeof target === "function") {
4224
- useEffect(() => {
4225
- const el = target();
4226
- if (el) {
4227
- el.addEventListener(eventName, handler, eventParams);
4228
- return () => el.removeEventListener(eventName, handler, eventParams);
4229
- }
4230
- return;
4231
- });
4232
- } else {
4233
- target.addEventListener(eventName, handler, eventParams);
4234
- onWillDestroy(() => target.removeEventListener(eventName, handler, eventParams));
4235
- }
4236
- }
4237
- function useApp() {
4238
- return useScope().app;
4239
- }
4289
+ var useApp2 = useApp;
4240
4290
  var PortalContent = class extends Component {
4241
4291
  static template = xml`<t t-call-slot="default"/>`;
4242
4292
  };
@@ -4338,29 +4388,6 @@ ${issueStrings}`);
4338
4388
  }
4339
4389
  return propValue === void 0 && hasDefault ? args[0] : propValue;
4340
4390
  }
4341
- function plugin(pluginType) {
4342
- const scope = useScope();
4343
- const manager = scope instanceof ComponentNode ? scope.pluginManager : scope;
4344
- let plugin2 = manager.getPluginById(pluginType.id);
4345
- if (!plugin2) {
4346
- if (scope instanceof PluginManager) {
4347
- plugin2 = manager.startPlugin(pluginType);
4348
- } else {
4349
- throw new OwlError(`Unknown plugin "${pluginType.id}"`);
4350
- }
4351
- }
4352
- return plugin2;
4353
- }
4354
- function config2(name, type) {
4355
- const scope = useScope();
4356
- if (!(scope instanceof PluginManager)) {
4357
- throw new OwlError("Expected to be in a plugin scope");
4358
- }
4359
- if (scope.app.dev && type) {
4360
- assertType(scope.config, types2.object({ [name]: type }), "Config does not match the type");
4361
- }
4362
- return scope.config[name.endsWith("?") ? name.slice(0, -1) : name];
4363
- }
4364
4391
  function providePlugins(pluginConstructors, config3) {
4365
4392
  const node = getComponentScope();
4366
4393
  const manager = new PluginManager(node.app, { parent: node.pluginManager, config: config3 });
@@ -4371,10 +4398,10 @@ ${issueStrings}`);
4371
4398
  onWillStart(() => manager.ready);
4372
4399
  }
4373
4400
  }
4374
- config.shouldNormalizeDom = false;
4375
- config.mainEventHandler = mainEventHandler;
4401
+ config2.shouldNormalizeDom = false;
4402
+ config2.mainEventHandler = mainEventHandler;
4376
4403
  var blockDom = {
4377
- config,
4404
+ config: config2,
4378
4405
  // bdom entry points
4379
4406
  mount,
4380
4407
  patch,
@@ -4389,8 +4416,8 @@ ${issueStrings}`);
4389
4416
  };
4390
4417
  var __info__ = {
4391
4418
  version: App.version,
4392
- date: "2026-05-20T14:11:32.436Z",
4393
- hash: "25eaac4e",
4419
+ date: "2026-05-28T13:29:02.340Z",
4420
+ hash: "fadc22b7",
4394
4421
  url: "https://github.com/odoo/owl"
4395
4422
  };
4396
4423