@odoo/owl 2.8.1 → 3.0.0-alpha.1

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.
Files changed (37) hide show
  1. package/dist/owl-devtools.zip +0 -0
  2. package/dist/owl.cjs.js +579 -182
  3. package/dist/owl.es.js +572 -183
  4. package/dist/owl.iife.js +579 -182
  5. package/dist/owl.iife.min.js +1 -1
  6. package/dist/types/common/types.d.ts +28 -0
  7. package/dist/types/owl.d.ts +162 -74
  8. package/dist/types/runtime/app.d.ts +3 -0
  9. package/dist/types/runtime/cancellableContext.d.ts +15 -0
  10. package/dist/types/runtime/cancellablePromise.d.ts +15 -0
  11. package/dist/types/runtime/component.d.ts +5 -0
  12. package/dist/types/runtime/component_node.d.ts +4 -2
  13. package/dist/types/runtime/executionContext.d.ts +0 -0
  14. package/dist/types/runtime/index.d.ts +3 -0
  15. package/dist/types/runtime/listOperation.d.ts +1 -0
  16. package/dist/types/runtime/plugins.d.ts +39 -0
  17. package/dist/types/runtime/reactivity.d.ts +1 -12
  18. package/dist/types/runtime/registry.d.ts +15 -0
  19. package/dist/types/runtime/relationalModel/discussModel.d.ts +19 -0
  20. package/dist/types/runtime/relationalModel/discussModelTypes.d.ts +22 -0
  21. package/dist/types/runtime/relationalModel/field.d.ts +20 -0
  22. package/dist/types/runtime/relationalModel/model.d.ts +59 -0
  23. package/dist/types/runtime/relationalModel/modelData.d.ts +18 -0
  24. package/dist/types/runtime/relationalModel/modelRegistry.d.ts +3 -0
  25. package/dist/types/runtime/relationalModel/modelUtils.d.ts +4 -0
  26. package/dist/types/runtime/relationalModel/store.d.ts +16 -0
  27. package/dist/types/runtime/relationalModel/types.d.ts +83 -0
  28. package/dist/types/runtime/relationalModel/util.d.ts +1 -0
  29. package/dist/types/runtime/relationalModel/web/WebDataPoint.d.ts +25 -0
  30. package/dist/types/runtime/relationalModel/web/WebRecord.d.ts +131 -0
  31. package/dist/types/runtime/relationalModel/web/WebStaticList.d.ts +63 -0
  32. package/dist/types/runtime/relationalModel/web/webModel.d.ts +5 -0
  33. package/dist/types/runtime/relationalModel/web/webModelTypes.d.ts +139 -0
  34. package/dist/types/runtime/signals.d.ts +17 -0
  35. package/dist/types/runtime/task.d.ts +12 -0
  36. package/dist/types/utils/registry.d.ts +15 -0
  37. package/package.json +1 -1
package/dist/owl.es.js CHANGED
@@ -1592,6 +1592,14 @@ function remove(vnode, withBeforeRemove = false) {
1592
1592
  vnode.remove();
1593
1593
  }
1594
1594
 
1595
+ // Reactivity system
1596
+ var ComputationState;
1597
+ (function (ComputationState) {
1598
+ ComputationState[ComputationState["EXECUTED"] = 0] = "EXECUTED";
1599
+ ComputationState[ComputationState["STALE"] = 1] = "STALE";
1600
+ ComputationState[ComputationState["PENDING"] = 2] = "PENDING";
1601
+ })(ComputationState || (ComputationState = {}));
1602
+
1595
1603
  // Maps fibers to thrown errors
1596
1604
  const fibersInError = new WeakMap();
1597
1605
  const nodeErrorHandlers = new WeakMap();
@@ -1654,6 +1662,233 @@ function handleError(params) {
1654
1662
  }
1655
1663
  }
1656
1664
 
1665
+ let Effects;
1666
+ let CurrentComputation;
1667
+ function signal(value, opts) {
1668
+ const atom = {
1669
+ value,
1670
+ observers: new Set(),
1671
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
1672
+ };
1673
+ const read = () => {
1674
+ onReadAtom(atom);
1675
+ return atom.value;
1676
+ };
1677
+ const write = (newValue) => {
1678
+ if (typeof newValue === "function") {
1679
+ newValue = newValue(atom.value);
1680
+ }
1681
+ if (Object.is(atom.value, newValue))
1682
+ return;
1683
+ atom.value = newValue;
1684
+ onWriteAtom(atom);
1685
+ };
1686
+ return {
1687
+ get: read,
1688
+ set: write,
1689
+ };
1690
+ }
1691
+ function effect(fn, opts) {
1692
+ var _a, _b;
1693
+ const effectComputation = {
1694
+ state: ComputationState.STALE,
1695
+ value: undefined,
1696
+ compute() {
1697
+ // In case the cleanup read an atom.
1698
+ // todo: test it
1699
+ CurrentComputation = undefined;
1700
+ // `removeSources` is made by `runComputation`.
1701
+ unsubscribeEffect(effectComputation);
1702
+ CurrentComputation = effectComputation;
1703
+ return fn();
1704
+ },
1705
+ sources: new Set(),
1706
+ childrenEffect: [],
1707
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
1708
+ };
1709
+ (_b = (_a = CurrentComputation === null || CurrentComputation === void 0 ? void 0 : CurrentComputation.childrenEffect) === null || _a === void 0 ? void 0 : _a.push) === null || _b === void 0 ? void 0 : _b.call(_a, effectComputation);
1710
+ updateComputation(effectComputation);
1711
+ // Remove sources and unsubscribe
1712
+ return () => {
1713
+ // In case the cleanup read an atom.
1714
+ // todo: test it
1715
+ const previousComputation = CurrentComputation;
1716
+ CurrentComputation = undefined;
1717
+ unsubscribeEffect(effectComputation);
1718
+ CurrentComputation = previousComputation;
1719
+ };
1720
+ }
1721
+ // export function computed<T>(fn: () => T, opts?: Opts) {
1722
+ // // todo: handle cleanup
1723
+ // let computedComputation: Computation = {
1724
+ // state: ComputationState.STALE,
1725
+ // sources: new Set(),
1726
+ // isEager: true,
1727
+ // compute: () => {
1728
+ // return fn();
1729
+ // },
1730
+ // value: undefined,
1731
+ // name: opts?.name,
1732
+ // };
1733
+ // updateComputation(computedComputation);
1734
+ // }
1735
+ function derived(fn, opts) {
1736
+ // todo: handle cleanup
1737
+ let derivedComputation;
1738
+ return () => {
1739
+ derivedComputation !== null && derivedComputation !== void 0 ? derivedComputation : (derivedComputation = {
1740
+ state: ComputationState.STALE,
1741
+ sources: new Set(),
1742
+ compute: () => {
1743
+ onWriteAtom(derivedComputation);
1744
+ return fn();
1745
+ },
1746
+ isDerived: true,
1747
+ value: undefined,
1748
+ observers: new Set(),
1749
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
1750
+ });
1751
+ updateComputation(derivedComputation);
1752
+ return derivedComputation.value;
1753
+ };
1754
+ }
1755
+ function onReadAtom(atom) {
1756
+ if (!CurrentComputation)
1757
+ return;
1758
+ CurrentComputation.sources.add(atom);
1759
+ atom.observers.add(CurrentComputation);
1760
+ }
1761
+ function onWriteAtom(atom) {
1762
+ collectEffects(() => {
1763
+ for (const ctx of atom.observers) {
1764
+ if (ctx.state === ComputationState.EXECUTED) {
1765
+ if (ctx.isDerived)
1766
+ markDownstream(ctx);
1767
+ else
1768
+ Effects.push(ctx);
1769
+ }
1770
+ ctx.state = ComputationState.STALE;
1771
+ }
1772
+ });
1773
+ batchProcessEffects();
1774
+ }
1775
+ function collectEffects(fn) {
1776
+ if (Effects)
1777
+ return fn();
1778
+ Effects = [];
1779
+ try {
1780
+ return fn();
1781
+ }
1782
+ finally {
1783
+ }
1784
+ }
1785
+ const batchProcessEffects = batched(processEffects);
1786
+ function processEffects() {
1787
+ if (!Effects)
1788
+ return;
1789
+ for (const computation of Effects) {
1790
+ updateComputation(computation);
1791
+ }
1792
+ Effects = undefined;
1793
+ }
1794
+ function withoutReactivity(fn) {
1795
+ return runWithComputation(undefined, fn);
1796
+ }
1797
+ function getCurrentComputation() {
1798
+ return CurrentComputation;
1799
+ }
1800
+ function setComputation(computation) {
1801
+ CurrentComputation = computation;
1802
+ }
1803
+ // todo: should probably use updateComputation instead.
1804
+ function runWithComputation(computation, fn) {
1805
+ const previousComputation = CurrentComputation;
1806
+ CurrentComputation = computation;
1807
+ let result;
1808
+ try {
1809
+ result = fn();
1810
+ }
1811
+ finally {
1812
+ CurrentComputation = previousComputation;
1813
+ }
1814
+ return result;
1815
+ }
1816
+ function updateComputation(computation) {
1817
+ var _a;
1818
+ const state = computation.state;
1819
+ if (computation.isDerived)
1820
+ onReadAtom(computation);
1821
+ if (state === ComputationState.EXECUTED)
1822
+ return;
1823
+ if (state === ComputationState.PENDING) {
1824
+ computeSources(computation);
1825
+ // If the state is still not stale after processing the sources, it means
1826
+ // none of the dependencies have changed.
1827
+ // todo: test it
1828
+ if (computation.state !== ComputationState.STALE) {
1829
+ computation.state = ComputationState.EXECUTED;
1830
+ return;
1831
+ }
1832
+ }
1833
+ // todo: test performance. We might want to avoid removing the atoms to
1834
+ // directly re-add them at compute. Especially as we are making them stale.
1835
+ removeSources(computation);
1836
+ const previousComputation = CurrentComputation;
1837
+ CurrentComputation = computation;
1838
+ computation.value = (_a = computation.compute) === null || _a === void 0 ? void 0 : _a.call(computation);
1839
+ computation.state = ComputationState.EXECUTED;
1840
+ CurrentComputation = previousComputation;
1841
+ }
1842
+ function removeSources(computation) {
1843
+ const sources = computation.sources;
1844
+ for (const source of sources) {
1845
+ const observers = source.observers;
1846
+ observers.delete(computation);
1847
+ // todo: if source has no effect observer anymore, remove its sources too
1848
+ // todo: test it
1849
+ }
1850
+ sources.clear();
1851
+ }
1852
+ function unsubscribeEffect(effectComputation) {
1853
+ removeSources(effectComputation);
1854
+ cleanupEffect(effectComputation);
1855
+ for (const children of effectComputation.childrenEffect) {
1856
+ // Consider it executed to avoid it's re-execution
1857
+ // todo: make a test for it
1858
+ children.state = ComputationState.EXECUTED;
1859
+ removeSources(children);
1860
+ unsubscribeEffect(children);
1861
+ }
1862
+ effectComputation.childrenEffect.length = 0;
1863
+ }
1864
+ function cleanupEffect(computation) {
1865
+ // the computation.value of an effect is a cleanup function
1866
+ const cleanupFn = computation.value;
1867
+ if (cleanupFn && typeof cleanupFn === "function") {
1868
+ cleanupFn();
1869
+ computation.value = undefined;
1870
+ }
1871
+ }
1872
+ function markDownstream(derived) {
1873
+ for (const observer of derived.observers) {
1874
+ // if the state has already been marked, skip it
1875
+ if (observer.state)
1876
+ continue;
1877
+ observer.state = ComputationState.PENDING;
1878
+ if (observer.isDerived)
1879
+ markDownstream(observer);
1880
+ else
1881
+ Effects.push(observer);
1882
+ }
1883
+ }
1884
+ function computeSources(derived) {
1885
+ for (const source of derived.sources) {
1886
+ if (!("compute" in source))
1887
+ continue;
1888
+ updateComputation(source);
1889
+ }
1890
+ }
1891
+
1657
1892
  function makeChildFiber(node, parent) {
1658
1893
  let current = node.fiber;
1659
1894
  if (current) {
@@ -1775,13 +2010,16 @@ class Fiber {
1775
2010
  const node = this.node;
1776
2011
  const root = this.root;
1777
2012
  if (root) {
1778
- try {
1779
- this.bdom = true;
1780
- this.bdom = node.renderFn();
1781
- }
1782
- catch (e) {
1783
- node.app.handleError({ node, error: e });
1784
- }
2013
+ // todo: should use updateComputation somewhere else.
2014
+ runWithComputation(node.signalComputation, () => {
2015
+ try {
2016
+ this.bdom = true;
2017
+ this.bdom = node.renderFn();
2018
+ }
2019
+ catch (e) {
2020
+ node.app.handleError({ node, error: e });
2021
+ }
2022
+ });
1785
2023
  root.setCounter(root.counter - 1);
1786
2024
  }
1787
2025
  }
@@ -1913,11 +2151,6 @@ class MountFiber extends RootFiber {
1913
2151
 
1914
2152
  // Special key to subscribe to, to be notified of key creation/deletion
1915
2153
  const KEYCHANGES = Symbol("Key changes");
1916
- // Used to specify the absence of a callback, can be used as WeakMap key but
1917
- // should only be used as a sentinel value and never called.
1918
- const NO_CALLBACK = () => {
1919
- throw new Error("Called NO_CALLBACK. Owl is broken, please report this to the maintainers.");
1920
- };
1921
2154
  const objectToString = Object.prototype.toString;
1922
2155
  const objectHasOwnProperty = Object.prototype.hasOwnProperty;
1923
2156
  // Use arrays because Array.includes is faster than Set.has for small arrays
@@ -1954,8 +2187,8 @@ function canBeMadeReactive(value) {
1954
2187
  * @param value the value make reactive
1955
2188
  * @returns a reactive for the given object when possible, the original otherwise
1956
2189
  */
1957
- function possiblyReactive(val, cb) {
1958
- return canBeMadeReactive(val) ? reactive(val, cb) : val;
2190
+ function possiblyReactive(val) {
2191
+ return canBeMadeReactive(val) ? reactive(val) : val;
1959
2192
  }
1960
2193
  const skipped = new WeakSet();
1961
2194
  /**
@@ -1977,7 +2210,23 @@ function markRaw(value) {
1977
2210
  function toRaw(value) {
1978
2211
  return targets.has(value) ? targets.get(value) : value;
1979
2212
  }
1980
- const targetToKeysToCallbacks = new WeakMap();
2213
+ const targetToKeysToAtomItem = new WeakMap();
2214
+ function getTargetKeyAtom(target, key) {
2215
+ let keyToAtomItem = targetToKeysToAtomItem.get(target);
2216
+ if (!keyToAtomItem) {
2217
+ keyToAtomItem = new Map();
2218
+ targetToKeysToAtomItem.set(target, keyToAtomItem);
2219
+ }
2220
+ let atom = keyToAtomItem.get(key);
2221
+ if (!atom) {
2222
+ atom = {
2223
+ value: undefined,
2224
+ observers: new Set(),
2225
+ };
2226
+ keyToAtomItem.set(key, atom);
2227
+ }
2228
+ return atom;
2229
+ }
1981
2230
  /**
1982
2231
  * Observes a given key on a target with an callback. The callback will be
1983
2232
  * called when the given key changes on the target.
@@ -1987,22 +2236,8 @@ const targetToKeysToCallbacks = new WeakMap();
1987
2236
  * or deletion)
1988
2237
  * @param callback the function to call when the key changes
1989
2238
  */
1990
- function observeTargetKey(target, key, callback) {
1991
- if (callback === NO_CALLBACK) {
1992
- return;
1993
- }
1994
- if (!targetToKeysToCallbacks.get(target)) {
1995
- targetToKeysToCallbacks.set(target, new Map());
1996
- }
1997
- const keyToCallbacks = targetToKeysToCallbacks.get(target);
1998
- if (!keyToCallbacks.get(key)) {
1999
- keyToCallbacks.set(key, new Set());
2000
- }
2001
- keyToCallbacks.get(key).add(callback);
2002
- if (!callbacksToTargets.has(callback)) {
2003
- callbacksToTargets.set(callback, new Set());
2004
- }
2005
- callbacksToTargets.get(callback).add(target);
2239
+ function onReadTargetKey(target, key) {
2240
+ onReadAtom(getTargetKeyAtom(target, key));
2006
2241
  }
2007
2242
  /**
2008
2243
  * Notify Reactives that are observing a given target that a key has changed on
@@ -2013,60 +2248,16 @@ function observeTargetKey(target, key, callback) {
2013
2248
  * @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
2014
2249
  * or deleted)
2015
2250
  */
2016
- function notifyReactives(target, key) {
2017
- const keyToCallbacks = targetToKeysToCallbacks.get(target);
2018
- if (!keyToCallbacks) {
2251
+ function onWriteTargetKey(target, key) {
2252
+ const keyToAtomItem = targetToKeysToAtomItem.get(target);
2253
+ if (!keyToAtomItem) {
2019
2254
  return;
2020
2255
  }
2021
- const callbacks = keyToCallbacks.get(key);
2022
- if (!callbacks) {
2256
+ const atom = keyToAtomItem.get(key);
2257
+ if (!atom) {
2023
2258
  return;
2024
2259
  }
2025
- // Loop on copy because clearReactivesForCallback will modify the set in place
2026
- for (const callback of [...callbacks]) {
2027
- clearReactivesForCallback(callback);
2028
- callback();
2029
- }
2030
- }
2031
- const callbacksToTargets = new WeakMap();
2032
- /**
2033
- * Clears all subscriptions of the Reactives associated with a given callback.
2034
- *
2035
- * @param callback the callback for which the reactives need to be cleared
2036
- */
2037
- function clearReactivesForCallback(callback) {
2038
- const targetsToClear = callbacksToTargets.get(callback);
2039
- if (!targetsToClear) {
2040
- return;
2041
- }
2042
- for (const target of targetsToClear) {
2043
- const observedKeys = targetToKeysToCallbacks.get(target);
2044
- if (!observedKeys) {
2045
- continue;
2046
- }
2047
- for (const [key, callbacks] of observedKeys.entries()) {
2048
- callbacks.delete(callback);
2049
- if (!callbacks.size) {
2050
- observedKeys.delete(key);
2051
- }
2052
- }
2053
- }
2054
- targetsToClear.clear();
2055
- }
2056
- function getSubscriptions(callback) {
2057
- const targets = callbacksToTargets.get(callback) || [];
2058
- return [...targets].map((target) => {
2059
- const keysToCallbacks = targetToKeysToCallbacks.get(target);
2060
- let keys = [];
2061
- if (keysToCallbacks) {
2062
- for (const [key, cbs] of keysToCallbacks) {
2063
- if (cbs.has(callback)) {
2064
- keys.push(key);
2065
- }
2066
- }
2067
- }
2068
- return { target, keys };
2069
- });
2260
+ onWriteAtom(atom);
2070
2261
  }
2071
2262
  // Maps reactive objects to the underlying target
2072
2263
  const targets = new WeakMap();
@@ -2098,7 +2289,7 @@ const reactiveCache = new WeakMap();
2098
2289
  * reactive has changed
2099
2290
  * @returns a proxy that tracks changes to it
2100
2291
  */
2101
- function reactive(target, callback = NO_CALLBACK) {
2292
+ function reactive(target) {
2102
2293
  if (!canBeMadeReactive(target)) {
2103
2294
  throw new OwlError(`Cannot make the given value reactive`);
2104
2295
  }
@@ -2107,22 +2298,19 @@ function reactive(target, callback = NO_CALLBACK) {
2107
2298
  }
2108
2299
  if (targets.has(target)) {
2109
2300
  // target is reactive, create a reactive on the underlying object instead
2110
- return reactive(targets.get(target), callback);
2111
- }
2112
- if (!reactiveCache.has(target)) {
2113
- reactiveCache.set(target, new WeakMap());
2114
- }
2115
- const reactivesForTarget = reactiveCache.get(target);
2116
- if (!reactivesForTarget.has(callback)) {
2117
- const targetRawType = rawType(target);
2118
- const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
2119
- ? collectionsProxyHandler(target, callback, targetRawType)
2120
- : basicProxyHandler(callback);
2121
- const proxy = new Proxy(target, handler);
2122
- reactivesForTarget.set(callback, proxy);
2123
- targets.set(proxy, target);
2301
+ return target;
2124
2302
  }
2125
- return reactivesForTarget.get(callback);
2303
+ const reactive = reactiveCache.get(target);
2304
+ if (reactive)
2305
+ return reactive;
2306
+ const targetRawType = rawType(target);
2307
+ const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
2308
+ ? collectionsProxyHandler(target, targetRawType)
2309
+ : basicProxyHandler();
2310
+ const proxy = new Proxy(target, handler);
2311
+ reactiveCache.set(target, proxy);
2312
+ targets.set(proxy, target);
2313
+ return proxy;
2126
2314
  }
2127
2315
  /**
2128
2316
  * Creates a basic proxy handler for regular objects and arrays.
@@ -2130,7 +2318,7 @@ function reactive(target, callback = NO_CALLBACK) {
2130
2318
  * @param callback @see reactive
2131
2319
  * @returns a proxy handler object
2132
2320
  */
2133
- function basicProxyHandler(callback) {
2321
+ function basicProxyHandler() {
2134
2322
  return {
2135
2323
  get(target, key, receiver) {
2136
2324
  // non-writable non-configurable properties cannot be made reactive
@@ -2138,41 +2326,41 @@ function basicProxyHandler(callback) {
2138
2326
  if (desc && !desc.writable && !desc.configurable) {
2139
2327
  return Reflect.get(target, key, receiver);
2140
2328
  }
2141
- observeTargetKey(target, key, callback);
2142
- return possiblyReactive(Reflect.get(target, key, receiver), callback);
2329
+ onReadTargetKey(target, key);
2330
+ return possiblyReactive(Reflect.get(target, key, receiver));
2143
2331
  },
2144
2332
  set(target, key, value, receiver) {
2145
2333
  const hadKey = objectHasOwnProperty.call(target, key);
2146
2334
  const originalValue = Reflect.get(target, key, receiver);
2147
2335
  const ret = Reflect.set(target, key, toRaw(value), receiver);
2148
2336
  if (!hadKey && objectHasOwnProperty.call(target, key)) {
2149
- notifyReactives(target, KEYCHANGES);
2337
+ onWriteTargetKey(target, KEYCHANGES);
2150
2338
  }
2151
2339
  // While Array length may trigger the set trap, it's not actually set by this
2152
2340
  // method but is updated behind the scenes, and the trap is not called with the
2153
2341
  // new value. We disable the "same-value-optimization" for it because of that.
2154
2342
  if (originalValue !== Reflect.get(target, key, receiver) ||
2155
2343
  (key === "length" && Array.isArray(target))) {
2156
- notifyReactives(target, key);
2344
+ onWriteTargetKey(target, key);
2157
2345
  }
2158
2346
  return ret;
2159
2347
  },
2160
2348
  deleteProperty(target, key) {
2161
2349
  const ret = Reflect.deleteProperty(target, key);
2162
2350
  // TODO: only notify when something was actually deleted
2163
- notifyReactives(target, KEYCHANGES);
2164
- notifyReactives(target, key);
2351
+ onWriteTargetKey(target, KEYCHANGES);
2352
+ onWriteTargetKey(target, key);
2165
2353
  return ret;
2166
2354
  },
2167
2355
  ownKeys(target) {
2168
- observeTargetKey(target, KEYCHANGES, callback);
2356
+ onReadTargetKey(target, KEYCHANGES);
2169
2357
  return Reflect.ownKeys(target);
2170
2358
  },
2171
2359
  has(target, key) {
2172
2360
  // TODO: this observes all key changes instead of only the presence of the argument key
2173
2361
  // observing the key itself would observe value changes instead of presence changes
2174
2362
  // so we may need a finer grained system to distinguish observing value vs presence.
2175
- observeTargetKey(target, KEYCHANGES, callback);
2363
+ onReadTargetKey(target, KEYCHANGES);
2176
2364
  return Reflect.has(target, key);
2177
2365
  },
2178
2366
  };
@@ -2185,11 +2373,11 @@ function basicProxyHandler(callback) {
2185
2373
  * @param target @see reactive
2186
2374
  * @param callback @see reactive
2187
2375
  */
2188
- function makeKeyObserver(methodName, target, callback) {
2376
+ function makeKeyObserver(methodName, target) {
2189
2377
  return (key) => {
2190
2378
  key = toRaw(key);
2191
- observeTargetKey(target, key, callback);
2192
- return possiblyReactive(target[methodName](key), callback);
2379
+ onReadTargetKey(target, key);
2380
+ return possiblyReactive(target[methodName](key));
2193
2381
  };
2194
2382
  }
2195
2383
  /**
@@ -2200,14 +2388,14 @@ function makeKeyObserver(methodName, target, callback) {
2200
2388
  * @param target @see reactive
2201
2389
  * @param callback @see reactive
2202
2390
  */
2203
- function makeIteratorObserver(methodName, target, callback) {
2391
+ function makeIteratorObserver(methodName, target) {
2204
2392
  return function* () {
2205
- observeTargetKey(target, KEYCHANGES, callback);
2393
+ onReadTargetKey(target, KEYCHANGES);
2206
2394
  const keys = target.keys();
2207
2395
  for (const item of target[methodName]()) {
2208
2396
  const key = keys.next().value;
2209
- observeTargetKey(target, key, callback);
2210
- yield possiblyReactive(item, callback);
2397
+ onReadTargetKey(target, key);
2398
+ yield possiblyReactive(item);
2211
2399
  }
2212
2400
  };
2213
2401
  }
@@ -2219,12 +2407,12 @@ function makeIteratorObserver(methodName, target, callback) {
2219
2407
  * @param target @see reactive
2220
2408
  * @param callback @see reactive
2221
2409
  */
2222
- function makeForEachObserver(target, callback) {
2410
+ function makeForEachObserver(target) {
2223
2411
  return function forEach(forEachCb, thisArg) {
2224
- observeTargetKey(target, KEYCHANGES, callback);
2412
+ onReadTargetKey(target, KEYCHANGES);
2225
2413
  target.forEach(function (val, key, targetObj) {
2226
- observeTargetKey(target, key, callback);
2227
- forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
2414
+ onReadTargetKey(target, key);
2415
+ forEachCb.call(thisArg, possiblyReactive(val), possiblyReactive(key), possiblyReactive(targetObj));
2228
2416
  }, thisArg);
2229
2417
  };
2230
2418
  }
@@ -2246,10 +2434,10 @@ function delegateAndNotify(setterName, getterName, target) {
2246
2434
  const ret = target[setterName](key, value);
2247
2435
  const hasKey = target.has(key);
2248
2436
  if (hadKey !== hasKey) {
2249
- notifyReactives(target, KEYCHANGES);
2437
+ onWriteTargetKey(target, KEYCHANGES);
2250
2438
  }
2251
2439
  if (originalValue !== target[getterName](key)) {
2252
- notifyReactives(target, key);
2440
+ onWriteTargetKey(target, key);
2253
2441
  }
2254
2442
  return ret;
2255
2443
  };
@@ -2264,9 +2452,9 @@ function makeClearNotifier(target) {
2264
2452
  return () => {
2265
2453
  const allKeys = [...target.keys()];
2266
2454
  target.clear();
2267
- notifyReactives(target, KEYCHANGES);
2455
+ onWriteTargetKey(target, KEYCHANGES);
2268
2456
  for (const key of allKeys) {
2269
- notifyReactives(target, key);
2457
+ onWriteTargetKey(target, key);
2270
2458
  }
2271
2459
  };
2272
2460
  }
@@ -2278,40 +2466,40 @@ function makeClearNotifier(target) {
2278
2466
  * reactives that the key which is being added or deleted has been modified.
2279
2467
  */
2280
2468
  const rawTypeToFuncHandlers = {
2281
- Set: (target, callback) => ({
2282
- has: makeKeyObserver("has", target, callback),
2469
+ Set: (target) => ({
2470
+ has: makeKeyObserver("has", target),
2283
2471
  add: delegateAndNotify("add", "has", target),
2284
2472
  delete: delegateAndNotify("delete", "has", target),
2285
- keys: makeIteratorObserver("keys", target, callback),
2286
- values: makeIteratorObserver("values", target, callback),
2287
- entries: makeIteratorObserver("entries", target, callback),
2288
- [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
2289
- forEach: makeForEachObserver(target, callback),
2473
+ keys: makeIteratorObserver("keys", target),
2474
+ values: makeIteratorObserver("values", target),
2475
+ entries: makeIteratorObserver("entries", target),
2476
+ [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
2477
+ forEach: makeForEachObserver(target),
2290
2478
  clear: makeClearNotifier(target),
2291
2479
  get size() {
2292
- observeTargetKey(target, KEYCHANGES, callback);
2480
+ onReadTargetKey(target, KEYCHANGES);
2293
2481
  return target.size;
2294
2482
  },
2295
2483
  }),
2296
- Map: (target, callback) => ({
2297
- has: makeKeyObserver("has", target, callback),
2298
- get: makeKeyObserver("get", target, callback),
2484
+ Map: (target) => ({
2485
+ has: makeKeyObserver("has", target),
2486
+ get: makeKeyObserver("get", target),
2299
2487
  set: delegateAndNotify("set", "get", target),
2300
2488
  delete: delegateAndNotify("delete", "has", target),
2301
- keys: makeIteratorObserver("keys", target, callback),
2302
- values: makeIteratorObserver("values", target, callback),
2303
- entries: makeIteratorObserver("entries", target, callback),
2304
- [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
2305
- forEach: makeForEachObserver(target, callback),
2489
+ keys: makeIteratorObserver("keys", target),
2490
+ values: makeIteratorObserver("values", target),
2491
+ entries: makeIteratorObserver("entries", target),
2492
+ [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
2493
+ forEach: makeForEachObserver(target),
2306
2494
  clear: makeClearNotifier(target),
2307
2495
  get size() {
2308
- observeTargetKey(target, KEYCHANGES, callback);
2496
+ onReadTargetKey(target, KEYCHANGES);
2309
2497
  return target.size;
2310
2498
  },
2311
2499
  }),
2312
- WeakMap: (target, callback) => ({
2313
- has: makeKeyObserver("has", target, callback),
2314
- get: makeKeyObserver("get", target, callback),
2500
+ WeakMap: (target) => ({
2501
+ has: makeKeyObserver("has", target),
2502
+ get: makeKeyObserver("get", target),
2315
2503
  set: delegateAndNotify("set", "get", target),
2316
2504
  delete: delegateAndNotify("delete", "has", target),
2317
2505
  }),
@@ -2323,18 +2511,18 @@ const rawTypeToFuncHandlers = {
2323
2511
  * @param target @see reactive
2324
2512
  * @returns a proxy handler object
2325
2513
  */
2326
- function collectionsProxyHandler(target, callback, targetRawType) {
2514
+ function collectionsProxyHandler(target, targetRawType) {
2327
2515
  // TODO: if performance is an issue we can create the special handlers lazily when each
2328
2516
  // property is read.
2329
- const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
2330
- return Object.assign(basicProxyHandler(callback), {
2517
+ const specialHandlers = rawTypeToFuncHandlers[targetRawType](target);
2518
+ return Object.assign(basicProxyHandler(), {
2331
2519
  // FIXME: probably broken when part of prototype chain since we ignore the receiver
2332
2520
  get(target, key) {
2333
2521
  if (objectHasOwnProperty.call(specialHandlers, key)) {
2334
2522
  return specialHandlers[key];
2335
2523
  }
2336
- observeTargetKey(target, key, callback);
2337
- return possiblyReactive(target[key], callback);
2524
+ onReadTargetKey(target, key);
2525
+ return possiblyReactive(target[key]);
2338
2526
  },
2339
2527
  });
2340
2528
  }
@@ -2368,7 +2556,6 @@ function applyDefaultProps(props, defaultProps) {
2368
2556
  // -----------------------------------------------------------------------------
2369
2557
  // Integration with reactivity system (useState)
2370
2558
  // -----------------------------------------------------------------------------
2371
- const batchedRenderFunctions = new WeakMap();
2372
2559
  /**
2373
2560
  * Creates a reactive object that will be observed by the current component.
2374
2561
  * Reading data from the returned object (eg during rendering) will cause the
@@ -2380,15 +2567,7 @@ const batchedRenderFunctions = new WeakMap();
2380
2567
  * @see reactive
2381
2568
  */
2382
2569
  function useState(state) {
2383
- const node = getCurrent();
2384
- let render = batchedRenderFunctions.get(node);
2385
- if (!render) {
2386
- render = batched(node.render.bind(node, false));
2387
- batchedRenderFunctions.set(node, render);
2388
- // manual implementation of onWillDestroy to break cyclic dependency
2389
- node.willDestroy.push(clearReactivesForCallback.bind(null, render));
2390
- }
2391
- return reactive(state, render);
2570
+ return reactive(state);
2392
2571
  }
2393
2572
  class ComponentNode {
2394
2573
  constructor(C, props, app, parent, parentKey) {
@@ -2411,6 +2590,13 @@ class ComponentNode {
2411
2590
  this.parent = parent;
2412
2591
  this.props = props;
2413
2592
  this.parentKey = parentKey;
2593
+ this.pluginManager = parent ? parent.pluginManager : app.pluginManager;
2594
+ this.signalComputation = {
2595
+ value: undefined,
2596
+ compute: () => this.render(false),
2597
+ sources: new Set(),
2598
+ state: ComputationState.EXECUTED,
2599
+ };
2414
2600
  const defaultProps = C.defaultProps;
2415
2601
  props = Object.assign({}, props);
2416
2602
  if (defaultProps) {
@@ -2418,16 +2604,13 @@ class ComponentNode {
2418
2604
  }
2419
2605
  const env = (parent && parent.childEnv) || app.env;
2420
2606
  this.childEnv = env;
2421
- for (const key in props) {
2422
- const prop = props[key];
2423
- if (prop && typeof prop === "object" && targets.has(prop)) {
2424
- props[key] = useState(prop);
2425
- }
2426
- }
2607
+ const previousComputation = getCurrentComputation();
2608
+ setComputation(this.signalComputation);
2427
2609
  this.component = new C(props, env, this);
2428
2610
  const ctx = Object.assign(Object.create(this.component), { this: this.component });
2429
2611
  this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
2430
2612
  this.component.setup();
2613
+ setComputation(previousComputation);
2431
2614
  currentNode = null;
2432
2615
  }
2433
2616
  mountComponent(target, options) {
@@ -2442,7 +2625,11 @@ class ComponentNode {
2442
2625
  }
2443
2626
  const component = this.component;
2444
2627
  try {
2445
- await Promise.all(this.willStart.map((f) => f.call(component)));
2628
+ let prom;
2629
+ withoutReactivity(() => {
2630
+ prom = Promise.all(this.willStart.map((f) => f.call(component)));
2631
+ });
2632
+ await prom;
2446
2633
  }
2447
2634
  catch (e) {
2448
2635
  this.app.handleError({ node: this, error: e });
@@ -2551,15 +2738,10 @@ class ComponentNode {
2551
2738
  if (defaultProps) {
2552
2739
  applyDefaultProps(props, defaultProps);
2553
2740
  }
2554
- currentNode = this;
2555
- for (const key in props) {
2556
- const prop = props[key];
2557
- if (prop && typeof prop === "object" && targets.has(prop)) {
2558
- props[key] = useState(prop);
2559
- }
2560
- }
2561
- currentNode = null;
2562
- const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
2741
+ let prom;
2742
+ withoutReactivity(() => {
2743
+ prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
2744
+ });
2563
2745
  await prom;
2564
2746
  if (fiber !== this.fiber) {
2565
2747
  return;
@@ -2667,10 +2849,6 @@ class ComponentNode {
2667
2849
  get name() {
2668
2850
  return this.component.constructor.name;
2669
2851
  }
2670
- get subscriptions() {
2671
- const render = batchedRenderFunctions.get(this);
2672
- return render ? getSubscriptions(render) : [];
2673
- }
2674
2852
  }
2675
2853
 
2676
2854
  const TIMEOUT = Symbol("timeout");
@@ -2792,6 +2970,9 @@ class Component {
2792
2970
  this.env = env;
2793
2971
  this.__owl__ = node;
2794
2972
  }
2973
+ get plugins() {
2974
+ return this.__owl__.pluginManager.plugins;
2975
+ }
2795
2976
  setup() { }
2796
2977
  render(deep = false) {
2797
2978
  this.__owl__.render(deep === true);
@@ -5907,6 +6088,171 @@ class Scheduler {
5907
6088
  // interactions with other code, such as test frameworks that override them
5908
6089
  Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window);
5909
6090
 
6091
+ // import { PluginCtor } from "./component";
6092
+ class Plugin {
6093
+ constructor() {
6094
+ this.plugins = {};
6095
+ this.resources = {};
6096
+ this.__meta__ = { isDestroyed: false };
6097
+ // getResource(name: string) {
6098
+ // // todo
6099
+ // }
6100
+ // dispatchTo(resourceName, ...args) {
6101
+ // for (let handler of this.getResource(name)) {
6102
+ // if (typeof handler === "function") {
6103
+ // handler(...args);
6104
+ // } else {
6105
+ // throw new Error("resource value should be a function")
6106
+ // }
6107
+ // }
6108
+ // }
6109
+ }
6110
+ setup() { }
6111
+ destroy() { }
6112
+ get isDestroyed() {
6113
+ return this.__meta__.isDestroyed;
6114
+ }
6115
+ }
6116
+ Plugin.id = "";
6117
+ Plugin.dependencies = [];
6118
+ // can act and replace another plugin
6119
+ // static replaceOtherPlugin: null | string = null;
6120
+ // can define the type of resources, and some information, such as, is the
6121
+ // resource global or not
6122
+ Plugin.resources = {};
6123
+ class PluginManager {
6124
+ constructor(parent, Plugins) {
6125
+ this._children = [];
6126
+ this._parent = parent;
6127
+ parent === null || parent === void 0 ? void 0 : parent._children.push(this);
6128
+ this.plugins = parent ? Object.create(parent.plugins) : {};
6129
+ this.resources = parent ? Object.create(parent.resources) : {};
6130
+ // instantiate all plugins
6131
+ const plugins = [];
6132
+ const PLUGINS = Array.isArray(Plugins) ? Plugins : Plugins();
6133
+ for (let P of toposort(PLUGINS, this.plugins)) {
6134
+ if (P.resources) {
6135
+ for (let r in P.resources) {
6136
+ const sources = reactive({});
6137
+ const fn = derived(() => {
6138
+ const result = [];
6139
+ for (let name in sources) {
6140
+ const plugin = sources[name];
6141
+ const value = plugin.resources[r];
6142
+ if (Array.isArray(value)) {
6143
+ result.push(...value);
6144
+ }
6145
+ else {
6146
+ result.push(value);
6147
+ }
6148
+ }
6149
+ return result;
6150
+ });
6151
+ this.resources[r] = { sources, fn };
6152
+ }
6153
+ }
6154
+ const p = new P();
6155
+ plugins.push(p);
6156
+ this.plugins[P.id] = p;
6157
+ for (let dep of P.dependencies) {
6158
+ p.plugins[dep] = this.plugins[dep];
6159
+ }
6160
+ }
6161
+ // aggregate resources
6162
+ for (let name in this.plugins) {
6163
+ const p = this.plugins[name];
6164
+ for (let r in p.resources) {
6165
+ this.resources[r].sources[name] = p;
6166
+ // const value = p.resources[r];
6167
+ // if (Array.isArray(value)) {
6168
+ // this.resources[r].push(...value);
6169
+ // } else {
6170
+ // this.resources[r].push(value);
6171
+ // }
6172
+ }
6173
+ }
6174
+ // setup phase
6175
+ for (let p of plugins) {
6176
+ p.setup();
6177
+ }
6178
+ }
6179
+ destroy() {
6180
+ for (let children of this._children) {
6181
+ children.destroy();
6182
+ }
6183
+ const plugins = [];
6184
+ for (let id in this.plugins) {
6185
+ if (this.plugins.hasOwnProperty(id)) {
6186
+ const plugin = this.plugins[id];
6187
+ // resources
6188
+ for (let r in plugin.resources) {
6189
+ delete this.resources[r].sources[id];
6190
+ }
6191
+ plugins.push(this.plugins[id]);
6192
+ delete this.plugins[id];
6193
+ }
6194
+ }
6195
+ while (plugins.length) {
6196
+ const plugin = plugins.pop();
6197
+ plugin.destroy();
6198
+ plugin.__meta__.isDestroyed = true;
6199
+ }
6200
+ }
6201
+ getPlugin(name) {
6202
+ return this.plugins[name] || null;
6203
+ }
6204
+ getResource(name) {
6205
+ return this.resources[name].fn();
6206
+ }
6207
+ }
6208
+ function toposort(Plugins, plugins) {
6209
+ const visited = new Set();
6210
+ const temp = new Set();
6211
+ const sorted = [];
6212
+ const mapping = {};
6213
+ for (const P of Plugins) {
6214
+ if (!P.id.length) {
6215
+ throw new Error(`Plugin ${P.name} has no id`);
6216
+ }
6217
+ if (P.id in mapping) {
6218
+ throw new Error("A plugin with the same ID is already defined");
6219
+ }
6220
+ mapping[P.id] = P;
6221
+ }
6222
+ const visit = (P) => {
6223
+ if (visited.has(P.id))
6224
+ return;
6225
+ if (temp.has(P.id)) {
6226
+ throw new Error(`Circular dependency: ${P.id}`);
6227
+ }
6228
+ temp.add(P.id);
6229
+ for (const dep of P.dependencies || []) {
6230
+ const Dep = mapping[dep];
6231
+ if (Dep) {
6232
+ visit(Dep);
6233
+ }
6234
+ else {
6235
+ if (!(dep in plugins)) {
6236
+ throw new Error(`Missing dependency "${dep}" for plugin "${P.id}"`);
6237
+ }
6238
+ }
6239
+ }
6240
+ temp.delete(P.id);
6241
+ visited.add(P.id);
6242
+ sorted.push(P);
6243
+ };
6244
+ for (const P of Plugins) {
6245
+ visit(P);
6246
+ }
6247
+ return sorted;
6248
+ }
6249
+ function usePlugins(Plugins) {
6250
+ const node = getCurrent();
6251
+ const manager = new PluginManager(node.pluginManager, Plugins);
6252
+ node.pluginManager = manager;
6253
+ onWillDestroy(() => manager.destroy());
6254
+ }
6255
+
5910
6256
  let hasBeenLogged = false;
5911
6257
  const apps = new Set();
5912
6258
  window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, reactive });
@@ -5919,6 +6265,7 @@ class App extends TemplateSet {
5919
6265
  this.name = config.name || "";
5920
6266
  this.Root = Root;
5921
6267
  apps.add(this);
6268
+ this.pluginManager = new PluginManager(null, config.Plugins || []);
5922
6269
  if (config.test) {
5923
6270
  this.dev = true;
5924
6271
  }
@@ -6134,6 +6481,43 @@ const mainEventHandler = (data, ev, currentTarget) => {
6134
6481
  return stopped;
6135
6482
  };
6136
6483
 
6484
+ class Registry {
6485
+ constructor(name, schema) {
6486
+ this._map = reactive(Object.create(null));
6487
+ this._name = name || "registry";
6488
+ this._schema = schema;
6489
+ const entries = derived(() => {
6490
+ return Object.entries(this._map)
6491
+ .sort((el1, el2) => el1[1][0] - el2[1][0])
6492
+ .map(([str, elem]) => [str, elem[1]]);
6493
+ });
6494
+ const items = derived(() => entries().map((e) => e[1]));
6495
+ Object.defineProperty(this, "items", {
6496
+ get() {
6497
+ return items;
6498
+ },
6499
+ });
6500
+ Object.defineProperty(this, "entries", {
6501
+ get() {
6502
+ return entries;
6503
+ },
6504
+ });
6505
+ }
6506
+ set(key, value, sequence = 50) {
6507
+ if (this._schema) {
6508
+ validate(value, this._schema);
6509
+ }
6510
+ this._map[key] = [sequence, value];
6511
+ }
6512
+ get(key, defaultValue) {
6513
+ const hasKey = key in this._map;
6514
+ if (!hasKey && arguments.length < 2) {
6515
+ throw new Error(`KeyNotFoundError: Cannot find key "${key}" in this registry`);
6516
+ }
6517
+ return hasKey ? this._map[key][1] : defaultValue;
6518
+ }
6519
+ }
6520
+
6137
6521
  function status(component) {
6138
6522
  switch (component.__owl__.status) {
6139
6523
  case 0 /* NEW */:
@@ -6207,21 +6591,26 @@ function useChildSubEnv(envExtension) {
6207
6591
  * NaN !== NaN, which will cause the effect to rerun on every patch.
6208
6592
  */
6209
6593
  function useEffect(effect, computeDependencies = () => [NaN]) {
6594
+ const context = getCurrent().component.__owl__.signalComputation;
6210
6595
  let cleanup;
6211
6596
  let dependencies;
6212
- onMounted(() => {
6213
- dependencies = computeDependencies();
6597
+ const runEffect = () => runWithComputation(context, () => {
6214
6598
  cleanup = effect(...dependencies);
6215
6599
  });
6600
+ const computeDependenciesWithContext = () => runWithComputation(context, computeDependencies);
6601
+ onMounted(() => {
6602
+ dependencies = computeDependenciesWithContext();
6603
+ runEffect();
6604
+ });
6216
6605
  onPatched(() => {
6217
- const newDeps = computeDependencies();
6606
+ const newDeps = computeDependenciesWithContext();
6218
6607
  const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
6219
6608
  if (shouldReapply) {
6220
6609
  dependencies = newDeps;
6221
6610
  if (cleanup) {
6222
6611
  cleanup();
6223
6612
  }
6224
- cleanup = effect(...dependencies);
6613
+ runEffect();
6225
6614
  }
6226
6615
  });
6227
6616
  onWillUnmount(() => cleanup && cleanup());
@@ -6281,9 +6670,9 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
6281
6670
  });
6282
6671
  };
6283
6672
 
6284
- export { App, Component, EventBus, OwlError, __info__, batched, blockDom, htmlEscape, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
6673
+ export { App, Component, EventBus, OwlError, Plugin, PluginManager, Registry, __info__, batched, blockDom, derived, effect, htmlEscape, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, signal, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, usePlugins, useRef, useState, useSubEnv, validate, validateType, whenReady, withoutReactivity, xml };
6285
6674
 
6286
6675
 
6287
- __info__.date = '2025-09-23T07:17:45.055Z';
6288
- __info__.hash = '5211116';
6676
+ __info__.date = '2025-11-21T12:26:21.756Z';
6677
+ __info__.hash = '938f3f6';
6289
6678
  __info__.url = 'https://github.com/odoo/owl';