diff --git a/packages/dataverse/docs/GET_STARTED.md b/packages/dataverse/docs/GET_STARTED.md
index 4ab313d..1b6618d 100644
--- a/packages/dataverse/docs/GET_STARTED.md
+++ b/packages/dataverse/docs/GET_STARTED.md
@@ -1,725 +1,3 @@
# Into the dataverse - Get started with `@theatre/dataverse`
-This guide will help you to get started with `dataverse`, the reactive dataflow
-library that [Theatre.js](https://www.theatrejs.com) is built on. It is inspired
-by ideas in
-[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming)
-and it is optimised for interactivity and animation.
-
-## Main concepts
-
-A good analogy for `dataverse` would be a spreadsheet editor application. In a
-spreadsheet editor you have cells that store values, cells that store functions,
-that manipulate the values of other cells. The cells have identifiers (e.g.
-`A1`, `B3`, etc...) that are used to reference them in the functions. These are
-similar to the set of tools that `dataverse` provides for manipulating data.
-Here's a quick comparison:
-
-| `dataverse` | Spreadsheet editor analogy | role |
-| ----------------------- | ----------------------------------- | ---------------------------------------------------------------------------------- |
-| sources (`Box`, `Atom`) | a cell that holds a value | `Box`: holds a simple value, `Atom`: holds an object with (sub)props to be tracked |
-| derivations | functions | changes recorded on the value of an `Box` or `Atom` |
-| pointers | addresses of the cells (`A1`, `B3`) | they point to a (sub)prop of an `Atom` |
-
-Note that some concepts in `dataverse` go beyond the spreadsheet analogy.
-
-## Practical Introduction
-
-Here we collected a few examples that introduce the main concepts/tools in
-`dataverse` through practical examples. We strongly recommend running the
-examples on your local machine (see the [Setup](#setup) section to see how to
-configure your local environment before running the examples).
-
-0. [Setup your local environment for running the examples](#setup)
-1. [`Box`](#box-store-simple-values)
-2. [Observe values](#observe-values)
-3. [`map()`](#map)
-4. [`prism()`](#prism)
- - [A basic example](#a-basic-example)
- - [`prism.state()` and `prism.effect()`](#prismstate-and-prismeffect)
- - [Other methods of `prism()`](#other-methods-of-prism)
-5. [`usePrism()` (from `@theatre/react`)](#useprism)
-6. [`Atom`](#atom)
- - [`Atom` vs `Box`](#atom-vs-box)
- - [`Pointers`](#pointers)
- - [Update the value of an `Atom`](#update-the-value-of-an-atom)
-7. [`Ticker`](#ticker-and-studioticker)
-
-### Setup
-
-You are encouraged to follow the examples on your machine by cloning the
-[`theatre-js/theatre`](https://github.com/theatre-js/theatre/) repo and creating
-a new directory and file called `dataverse/index.tsx` in
-`theatre/packages/playground/src/personal/` (this directory is already added to
-`.gitignore`, so you don't have to worry about that).
-
-### `Box`: store simple values
-
-Let's start with creating a variable that holds a simple value, which we can
-change and observe later:
-
-```typescript
-import {Box} from '@theatre/dataverse'
-
-// `theatre/packages/playground/src/personal/dataverse/index.tsx`
-
-const variableB = new Box('some value')
-console.log(variableB.get()) // prints 'some value' in the console
-```
-
-> As you can see there's a naming convention here for boxes (`variableB`),
-> pointers (`variableP`), derivations (`variableD`), etc...
-
-Now we can change the value:
-
-```typescript
-variableB.set('some new value')
-console.log(variableB.get()) // prints 'some new value' in the console
-```
-
-### Observe values
-
-Let's say you want to watch the value of `variableB` for changes and execute a
-callback when it does change.
-
-```typescript
-import {Box} from '@theatre/dataverse'
-
-const variableB = new Box('some value')
-// Change the value of variableB to a random number in every 1000 ms
-const interval = setInterval(() => {
- variableB.set(Math.random().toString())
- console.log('isHot?', variableB.derivation.isHot)
-}, 1000)
-
-// Watch `variableB` changes and print a message to the console when the value of
-// `variableB` changes
-const untap = variableB.derivation.changesWithoutValues().tap(() => {
- console.log('value of variableB changed', variableB.derivation.getValue())
-})
-
-// Stop observing `variableB` after 5000 ms
-setTimeout(untap, 5000)
-
-// Clear the interval after 7000 ms
-setTimeout(() => {
- clearInterval(interval)
- console.log('Interval cleared.')
-}, 7000)
-```
-
-A few notes about the example above:
-
-- `variableB.derivation.changesWithoutValues()` returns a derivation / tappable that we can
- tap into (observe).
-- The `tap()` method returns the `untap()` function which unsubscribes the observer function
-- As long as `variableB` is tapped (observed) `variableB.derivation.isHot` will
- bet set to `true` automatically
-
-## Hotness
-As we saw above, derivations may or may not be "hot"
-(the same concept as "hot" Observables[ref](https://medium.com/codingthesmartway-com-blog/getting-started-with-rxjs-part-3-hot-and-cold-observables-4713757c9a88)). A derivation
-is hot if and only if it is being tapped.
-
-If you want to keep a derivation hot manually even if there's no tappable
-attached to it anymore, you can use the `keepHot()` method. Why would you
-want to keep a derivation hot? Check out this example:
-
-
-
-
-
-without `keepHot()` 🥶
-
-
-
-
-with `keepHot()` 🥵
-
-
-
-
-
-
-```typescript
-const variableD = prism(() => {
- return performance.now()
-})
-console.log(variableD.getValue()) // e.g. 113.5
-console.log(variableD.getValue()) // e.g. 114
-// Notice they give different values
-```
-
-
-
-
-```typescript
-const variableD = prism(() => {
- return performance.now()
-})
-variableD.keepHot()
-console.log(variableD.getValue()) // e.g. 113
-console.log(variableD.getValue()) // e.g. 113
-// Notice they give the same value!
-```
-
-
-
-
-
-To see a full example of `keepHot`, check out this modified version
-of the example from the section above:
-
-```typescript
-variableB.set('some new value')
-console.log(variableB.get()) // prints 'some new value' in the console
-
-// Change the value of variableB to a random number in every 1000 ms
-const interval = setInterval(() => {
- variableB.set(Math.random().toString())
- // This will print 'isHot? true' every time, since we kept
- // the derivation hot by calling the 'keepHot()' method
- console.log('isHot?', variableB.derivation.isHot)
-}, 1000)
-
-// Watch `variableB` changes and print a message to the console when the value of
-// `variableB` changes
-const untap = variableB.derivation.changesWithoutValues().tap(() => {
- console.log('value of variableB changed', variableB.derivation.getValue())
-})
-
-// Stop observing `variableB` after 5000 ms
-setTimeout(untap, 5000)
-
-// Keep the derivation hot
-variableB.derivation.keepHot()
-
-// Clear the interval after 7000 ms
-setTimeout(() => {
- clearInterval(interval)
- console.log('Interval cleared.')
-}, 7000)
-```
-
-How does `keepHot` work? It's super simple, it just adds a tap to the derivation ([source](https://github.com/theatre-js/theatre/blob/aec6b2a25135e6264e7529e7d3800c4bc3badee6/packages/dataverse/src/derivations/AbstractDerivation.ts#L103-L105)).
-
-### `map()`
-
-It is also possible to create a derivation based on an existing derivation:
-
-```typescript
-const niceNumberB = new Box(5)
-const isNiceNumberEvenD = niceNumberB.derivation.map((v) => v % 2 === 0)
-
-// the following line will print '5, false' to the console
-console.log(niceNumberB.get(), isNiceNumberEvenD.getValue())
-```
-
-The new derivation will be always up to date with the value of the original
-derivation:
-
-```typescript
-import {Box} from '@theatre/dataverse'
-
-const niceNumberB = new Box(5)
-const isNiceNumberEvenD = niceNumberB.derivation.map((v) =>
- v % 2 === 0 ? 'even' : 'odd',
-)
-
-const untap = isNiceNumberEvenD.changesWithoutValues().tap(() => {})
-
-const interval1 = setInterval(untap, 5000)
-const interval2 = setInterval(() => {
- niceNumberB.set(niceNumberB.get() + 1)
- console.log(
- `${niceNumberB.get()} is an ${isNiceNumberEvenD.getValue()} number.`,
- )
-}, 1000)
-
-// clear the intervals
-setTimeout(() => {
- clearInterval(interval1)
- console.log('interval1 is cleared.')
-}, 7000)
-
-setTimeout(() => {
- clearInterval(interval2)
- console.log('interval2 is cleared.')
-}, 7000)
-```
-
-### `prism()`
-
-At this point we can make derivations that track the value of an other
-derivation with [the `.map()` method](#map), but what if we want to track the
-value of multiple derivations at once for the new derivation? This is where the
-`prism()` function comes into play.
-
-#### A basic example
-
-Let's say that we have two derivations and we want to create a derivation that
-returns the product of their values. In the spreadsheet analogy it would be like
-having two cells with two functions and third cell that contains a function that
-calculates the product of the previous two cells. Whenever the first two cells
-recalculate their value, the third cell will also do the same.
-
-Here's how we would solve this problem in `dataverse`:
-
-```typescript
-import {Box, prism} from '@theatre/dataverse'
-
-const widthB = new Box(1)
-const heightB = new Box(2)
-const padding = 5
-
-const widthWithPaddingD = widthB.derivation.map((w) => w + padding)
-const heightWidthPaddingD = heightB.derivation.map((h) => h + padding)
-
-const areaD = prism(() => {
- return widthWithPaddingD.getValue() * heightWidthPaddingD.getValue()
-})
-
-console.log('area: ', areaD.getValue())
-widthB.set(10)
-console.log('new area: ', areaD.getValue())
-```
-
-#### `prism.state()` and `prism.effect()`
-
-Prisms don't always follow the rules of functional programming: they can have
-internal states and perform side effects using the `prism.state()` and
-`prism.effect()` methods. Their concept and API is very similar to React's
-`useState()` and `useEffect()` hooks.
-
-The important thing to know about them is that:
-
-- `prism.state()` returns a state variable and a function that updates it.
-- `prism.effect()` receives two arguments:
- 1. The first one is a key (a string), which should be unique to this effect
- inside the prism
- 2. The second one is a callback function as an argument that gets executed
- when the derivation is created (or the dependencies in the dependency array
- change). The callback function may return a clean up function that runs
- when the derivation gets updated or removed.
-
-Let's say you want to create a derivation that tracks the position of the mouse.
-This would require the derivation to do the following steps:
-
-1. Create an internal state where the position of the mouse is stored
-2. Attach an event listener that listens to `mousemove` events to the `document`
-3. Update the internal state of the position whenever the `mousemove` event is
- fired
-4. Remove the event listener once the derivation is gone (clean up)
-
-This is how this derivation would look like in code:
-
-```typescript
-import {prism} from '@theatre/dataverse'
-
-const mousePositionD = prism(() => {
- // Create an internal state (`pos`) where the position of the mouse
- // will be stored, and a function that updates it (`setPos`)
- const [pos, setPos] = prism.state('pos', {x: 0, y: 0})
-
- // Create a side effect that attaches the `mousemove` event listeners
- // to the `document`
- prism.effect(
- 'setupListeners',
- () => {
- const handleMouseMove = (e: MouseEvent) => {
- setPos({x: e.screenX, y: e.screenY})
- }
- document.addEventListener('mousemove', handleMouseMove)
-
- // Clean up after the derivation is gone (remove the event
- // listener)
- return () => {
- document.removeEventListener('mousemove', handleMouseMove)
- }
- },
- [],
- )
-
- return pos
-})
-
-// Display the current position of the mouse using a `h2` element
-const p = document.createElement('h2')
-const {x, y} = mousePositionD.getValue()
-p.textContent = `Position of the cursor: [${x}, ${y}]`
-document.querySelector('body')?.append(p)
-
-// Update the element's content when the position of the mouse
-// changes
-mousePositionD.changesWithoutValues().tap(() => {
- const {x, y} = mousePositionD.getValue()
- p.textContent = `Position of the cursor: [${x}, ${y}]`
-})
-```
-
-#### Other methods of `prism`
-
-Prism has other methods (`prism.memo()`, `prism.scope()`, `prism.ref()`, etc)
-inspired by React hooks, but they aren't used that much in `@theatre/core` and
-`@theatre/studio`. You can check out the
-[tests](../src/derivations/prism/prism.test.ts) or the
-[source code](../src/derivations/prism/prism.ts) to get more familiar with them.
-
-### `usePrism()`
-
-You can also use derivations inside of React components with the `usePrism()`
-hook from the `@theatre/react` package, which accepts a dependency array for the
-second argument. If the prism uses a value that is not a derivation (such as a
-simple number, or a pointer), then you need to provide that value to the
-dependency array.
-
-#### A simple example
-
-Here's a simple example: we have a Box that contains the width and height of a
-div (let's call it `panel`). Imagine that we want to have a button that changes
-the width of the `panel` to a random number when clicked.
-
-```typescript
-import {Box} from '@theatre/dataverse'
-import {usePrism} from '@theatre/react'
-import React from 'react'
-import ReactDOM from 'react-dom'
-
-// Set the original width and height
-const panelB = new Box({
- dims: {width: 200, height: 100},
-})
-
-function changePanelWidth() {
- const oldValue = panelB.get()
- // Change `width` to a random number between 0 and 200
- panelB.set({dims: {...oldValue.dims, width: Math.round(Math.random() * 200)}})
-}
-
-const Comp = () => {
- const render = usePrism(() => {
- const {dims} = panelB.derivation.getValue()
- return (
- <>
-
-
- >
- )
- }, [panelB]) // Note that `panelB` is in the dependency array
-
- return render
-}
-
-ReactDOM.render(
-
-
-
,
- document.querySelector('body'),
-)
-```
-
-#### The dependency array
-
-If you remove `panelB` from the dependency array in the previous example you
-might see that there's no change in the functionality of the `Change the width`
-button. It surprisingly still works:
-
-```typescript
-// ...
-const Comp = () => {
- const render = usePrism(() => {
- // ...
- }, []) // Here we removed `panelB` from the dependency array
-
- return render
-}
-// ...
-```
-
-The reason behind this behavior is that even though the value of `panelB` - the
-`Box` instance - is cached, the cached `Box` instance's value is still tracked
-inside the callback function (which uses `prism()` under the hood, and handles
-every derivation inside as its dependency). However, if you change the value of
-the `panelB` variable to another `Box` instance, then that change won't be
-recognized inside the callback function if `panelB` is not included in the
-dependency array of `usePrism()`. Let's look at another example to make things a
-bit more clear:
-
-```typescript
-// ...
-
-// Set the original width and height
-const panelB = new Box({
- dims: {width: 200, height: 100},
-})
-
-// Create two new `Box` instances
-const theme1B = new Box({backgroundColor: '#bd6888', opacity: 1})
-const theme2B = new Box({backgroundColor: '#5ac777', opacity: 1})
-
-function changePanelWidthAndThemeOpacity() {
- const oldValue = panelB.get()
- // Change `width` to a random number between 0 and 200
- const width = Math.round(Math.random() * 200)
- panelB.set({dims: {...oldValue.dims, width}})
- // Change opacity in the themes:
- const opacity = width > 100 ? width / 200 : width / 100
- theme1B.set({...theme1B.get(), opacity})
- theme2B.set({...theme2B.get(), opacity})
-}
-
-// DEPENDENCY ARRAYS DEMO
-const Comp = () => {
- // Get the width of the panel
- const {width} = panelB.derivation.getValue().dims
- // If the width of the panel is greater than 100, then
- // set the value of the `theme` variable to `theme1B`,
- // otherwise use `theme2B`
- const theme = width > 100 ? theme1B : theme2B
-
- const render = usePrism(() => {
- const {dims} = panelB.derivation.getValue()
- const {backgroundColor, opacity} = theme.get()
- return (
- <>
-
-
- >
- )
- // Note that if the `theme` variable weren't included in the
- // dependency array, then the background color of the div
- // wouldn't be updated (the opacity still would).
- // (Feel free to try it out.)
- }, [theme])
-
- return render
-}
-
-// ...
-```
-
-If you omit the `theme` variable from the previous example, then the background
-color of the `div` element will not be updated when the value of the `theme`
-variable does, while the opacity would track the changes of the width. This
-happens, because in that case the callback function in `usePrism()` caches the
-value of `theme`, which is `theme1B` when `usePrims()` is called for the first
-time, and updates whenever `theme1B` changes. If you pass down `theme` as a
-dependency to `usePrism()`, then the callback function will always use new new
-value of `theme` (which is set to `theme2B` if the `div`'s width is smaller than
-or equal to `100`), whenever it changes.
-
-### `Atom`
-
-Remember how we compared `Box`-es to cells in the spreadsheet-analogy? `Atom`-s
-are also like cells in the sense that they also hold a value (although they only
-work with objects), but there's a huge difference in how their value gets
-updated.
-
-#### `Atom` vs `Box`
-
-`Box` uses strict equality for comparing new and old values, while `Atom` tracks
-the individual properties and nested properties of an object. The following
-example illustrates this difference between the two pretty well:
-
-```typescript
-import {Atom, Box, val, valueDerivation} from '@theatre/dataverse'
-
-const originalValue = {width: 200, height: 100}
-
-// Create a `Box` that holds an object
-const panelB = new Box(originalValue)
-
-console.log('old value (Box): ', panelB.derivation.getValue())
-// Print the new value of `panelB` to the console
-// every time it changes
-panelB.derivation
- .changesWithoutValues()
- .tap(() => console.log('new value: (Box) ', panelB.derivation.getValue()))
-
-// Set the value of the `panelB` to a new object that has
-// the same properties with the same values as `panelB`.
-// Note that this will get recognized as a change, since
-// the two objects are not strictly equal.
-panelB.set({...panelB.get()})
-
-// Create an `Atom` that holds an object
-const panelA = new Atom({width: 200, height: 100})
-
-console.log('old value (Atom):', val(panelA.pointer))
-
-// Create a derivation to track the value of `panelA`
-// There are a lot of new information here, we'll come back
-// to this line later.
-const panelFromAtomD = valueDerivation(panelA.pointer)
-
-// Print the new value of `panelA` to the console
-// every time it changes
-panelFromAtomD
- .changesWithoutValues()
- .tap(() => console.log('new value (Atom):', val(panelA.pointer)))
-
-// Since the next line sets changes the value of `panelA` to what it
-// already holds, it does not get recognized as a change.
-// The `.setIn()` method is also new, we'll cover it later.
-panelA.setIn(['width'], 200)
-
-// The next line will trigger a change as expected
-panelA.setIn(['width'], 400)
-```
-
-#### Pointers
-
-You might have wondered what `val(panelA.pointer)` meant when you read this
-line:
-
-```typescript
-console.log('old value (Atom):', val(panelA.pointer))
-```
-
-`dataverse` uses pointers that point to the properties and nested properties of
-the object that the `Atom` instance holds as its value.
-
-You can use the pointers to get the value of the property they point to, or to
-convert them to a derivation using the `val()` and `useDerivation()` functions:
-
-```typescript
-const panelA = new Atom({width: 200, height: 100})
-
-// Create a derivation
-const panelFromAtomD = valueDerivation(panelA.pointer)
-
-// Print the value of the property that belongs to the pointer
-// Note that `panelA.pointer` and `panelA.pointer.width` are both
-// pointers.
-console.log(val(panelA.pointer)) // prints `{width: 200, height: 100}`
-console.log(val(panelA.pointer.width)) // prints `100`
-console.log(val(panelA.pointer.height)) // prints `200`
-```
-
-#### Update the value of an `Atom`
-
-If you want to update the value of an `Atom`, you have first choose the
-property/nested property that you want to update. Then you can use the names of
-its ancestor properties in an array to define the path to the property for the
-`setIn()` method:
-
-```typescript
-const panelA = new Atom({dims: {width: 200, height: 100}})
-
-// Sets the value of panelA to `{dims: {width: 400, height: 100}}`
-panelA.setIn(['dims', 'width'], 400)
-```
-
-### `Ticker` and `studioTicker`
-
-The `Ticker` class helps us to schedule callbacks using a strategy. One such
-strategy could by synchronizing the execution of certain actions with the
-browser's repaint schedule to avoid changes that are invisible for the user and
-would only worsen the performance. This could be implemented using the
-`studioTicker` from the `@theatre/studio` package.
-
-Here's an example: we want to increase the value of a counter variable by 1 in
-every 10 ms and print the current value on every repaint for 1 s:
-
-```typescript
-import {Box} from '@theatre/dataverse'
-import studioTicker from '@theatre/studio/studioTicker'
-
-// Clear the console to make tracking the relevant logs easier
-console.clear()
-
-// Create a counter variable
-const counterB = new Box(0)
-// Create a variable to track the number of repaints
-let numberOfRepaints = 0
-
-// Increase the value of the counter variable by 1
-// in every 10 ms
-const interval = setInterval(() => {
- counterB.set(counterB.get() + 1)
- console.log(counterB.get())
-}, 10)
-
-// Increase the number of repaints by one every time
-// a repaint happens
-const untap = counterB.derivation
- .changes(studioTicker)
- .tap((newCounterValue) => {
- ++numberOfRepaints
- console.log(`VALUE ON REPAINT: ${newCounterValue}`)
- })
-
-// Clean up everything after 1 s
-setTimeout(() => {
- clearInterval(interval)
- untap()
- console.log('interval is cleared.')
- console.log(`Number of times when the counter got updated: ${counterB.get()}`)
- console.log(`Number of repaints: ${numberOfRepaints}`)
-}, 1000)
-```
-
-When I run the example above using a 60 Hz refresh rate monitor (or when the
-browser itself limits the repaints to 60 times per second), I see something like
-this in the console:
-
-```
-Number of times when the counter got updated: 98
-Number of repaints: 60
-```
-
-What happens is that the counter gets updated about `1000ms / 10ms = 100` times,
-but only 60 of these changes can be shown on screen due to the refresh rate of
-my monitor. The values of the counter when the repaints happen are also printed
-to the console:
-
-```
-...
-94
-VALUE ON REPAINT: 94
-95
-96
-VALUE ON REPAINT: 96
-97
-98
-...
-```
-
-## Summary
-
-We only covered the basics, there are much more to `Box`-es, `Atom`-s and
-everything else in `dataverse`. You can always check the source code for more
-information.
+> TODO: Dataverse has changed quite a bit since this guide was written, so let's write a new one.
\ No newline at end of file
diff --git a/packages/dataverse/src/derivations/IDerivation.ts b/packages/dataverse/src/derivations/IDerivation.ts
index dfa72d6..ac10668 100644
--- a/packages/dataverse/src/derivations/IDerivation.ts
+++ b/packages/dataverse/src/derivations/IDerivation.ts
@@ -23,13 +23,7 @@ export interface IDerivation {
*/
changes(ticker: Ticker): Tappable
- /**
- * Like {@link changes} but with a different performance model. `changesWithoutValues` returns a {@link Tappable} that
- * updates every time the derivation is updated, even if the value didn't change, and the callback is called without
- * the value. The advantage of this is that you have control over when the derivation is freshened, it won't
- * automatically be kept fresh.
- */
- changesWithoutValues(): Tappable
+ onStale(cb: () => void): VoidFn
/**
* Keep the derivation hot, even if there are no tappers (subscribers).
diff --git a/packages/dataverse/src/derivations/flatMap.ts b/packages/dataverse/src/derivations/flatMap.ts
deleted file mode 100644
index d4fa2ba..0000000
--- a/packages/dataverse/src/derivations/flatMap.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import type {$FixMe} from '../types'
-import AbstractDerivation from './AbstractDerivation'
-import type {IDerivation} from './IDerivation'
-import {isDerivation} from './IDerivation'
-
-enum UPDATE_NEEDED_FROM {
- none = 0,
- dep = 1,
- inner = 2,
-}
-
-const makeFlatMapDerivationClass = () => {
- // TODO once prism and AbstractDerivation are merged into one, we should delete this file
- class FlatMapDerivation extends AbstractDerivation {
- private _innerDerivation: undefined | null | IDerivation
- private _staleDependency: UPDATE_NEEDED_FROM
-
- static displayName = 'flatMap'
-
- constructor(
- readonly _depDerivation: IDerivation,
- readonly _fn: (v: DepType) => IDerivation | V,
- ) {
- super()
- this._innerDerivation = undefined
- this._staleDependency = UPDATE_NEEDED_FROM.dep
-
- this._addDependency(_depDerivation)
-
- return this
- }
-
- _recalculateHot() {
- const updateNeededFrom = this._staleDependency
- this._staleDependency = UPDATE_NEEDED_FROM.none
-
- if (updateNeededFrom === UPDATE_NEEDED_FROM.inner) {
- // @ts-ignore
- return this._innerDerivation.getValue()
- }
-
- const possibleInnerDerivation = this._fn(this._depDerivation.getValue())
-
- if (isDerivation(possibleInnerDerivation)) {
- this._innerDerivation = possibleInnerDerivation
- this._addDependency(possibleInnerDerivation)
- return possibleInnerDerivation.getValue()
- } else {
- return possibleInnerDerivation
- }
- }
-
- protected _recalculateCold() {
- const possibleInnerDerivation = this._fn(this._depDerivation.getValue())
-
- if (isDerivation(possibleInnerDerivation)) {
- return possibleInnerDerivation.getValue()
- } else {
- return possibleInnerDerivation
- }
- }
-
- protected _recalculate() {
- return this.isHot ? this._recalculateHot() : this._recalculateCold()
- }
-
- protected _reactToDependencyBecomingStale(
- msgComingFrom: IDerivation,
- ) {
- const updateNeededFrom =
- msgComingFrom === this._depDerivation
- ? UPDATE_NEEDED_FROM.dep
- : UPDATE_NEEDED_FROM.inner
-
- if (
- updateNeededFrom === UPDATE_NEEDED_FROM.inner &&
- msgComingFrom !== this._innerDerivation
- ) {
- throw Error(
- `got a _reactToDependencyBecomingStale() from neither the dep nor the inner derivation`,
- )
- }
-
- if (this._staleDependency === UPDATE_NEEDED_FROM.none) {
- this._staleDependency = updateNeededFrom
-
- if (updateNeededFrom === UPDATE_NEEDED_FROM.dep) {
- this._removeInnerDerivation()
- }
- } else if (this._staleDependency === UPDATE_NEEDED_FROM.dep) {
- } else {
- if (updateNeededFrom === UPDATE_NEEDED_FROM.dep) {
- this._staleDependency = UPDATE_NEEDED_FROM.dep
- this._removeInnerDerivation()
- }
- }
- }
-
- private _removeInnerDerivation() {
- if (this._innerDerivation) {
- this._removeDependency(this._innerDerivation)
- this._innerDerivation = undefined
- }
- }
-
- protected _keepHot() {
- this._staleDependency = UPDATE_NEEDED_FROM.dep
- this.getValue()
- }
-
- protected _becomeCold() {
- this._staleDependency = UPDATE_NEEDED_FROM.dep
- this._removeInnerDerivation()
- }
- }
- return FlatMapDerivation
-}
-
-let cls: ReturnType | undefined = undefined
-
-export default function flatMap(
- dep: IDerivation,
- fn: (v: V) => R,
-): IDerivation ? T : R> {
- if (!cls) {
- cls = makeFlatMapDerivationClass()
- }
- return new cls(dep, fn) as $FixMe
-}
diff --git a/packages/dataverse/src/derivations/iterateAndCountTicks.ts b/packages/dataverse/src/derivations/iterateAndCountTicks.ts
index 37030fb..6e8e54c 100644
--- a/packages/dataverse/src/derivations/iterateAndCountTicks.ts
+++ b/packages/dataverse/src/derivations/iterateAndCountTicks.ts
@@ -17,7 +17,7 @@ export default function* iterateAndCountTicks(
}
let ticksCountedSinceLastYield = 0
- const untap = d.changesWithoutValues().tap(() => {
+ const untap = d.onStale(() => {
ticksCountedSinceLastYield++
})
diff --git a/packages/dataverse/src/derivations/map.ts b/packages/dataverse/src/derivations/map.ts
deleted file mode 100644
index 78c661e..0000000
--- a/packages/dataverse/src/derivations/map.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import AbstractDerivation from './AbstractDerivation'
-import type {IDerivation} from './IDerivation'
-
-// Exporting from a function because of the circular dependency with AbstractDerivation
-const makeMapDerivationClass = () =>
- // TODO once prism and AbstractDerivation are merged into one, we should delete this file
- class MapDerivation extends AbstractDerivation {
- constructor(
- private readonly _dep: IDerivation,
- private readonly _fn: (t: T) => V,
- ) {
- super()
- this._addDependency(_dep)
- }
-
- _recalculate() {
- return this._fn(this._dep.getValue())
- }
-
- _reactToDependencyBecomingStale() {}
- }
-
-let cls: ReturnType | undefined = undefined
-
-export default function map(
- dep: IDerivation,
- fn: (v: V) => R,
-): IDerivation {
- if (!cls) {
- cls = makeMapDerivationClass()
- }
- return new cls(dep, fn)
-}
diff --git a/packages/dataverse/src/derivations/prism/prism.ts b/packages/dataverse/src/derivations/prism/prism.ts
index 89f3d7b..d3cd2fe 100644
--- a/packages/dataverse/src/derivations/prism/prism.ts
+++ b/packages/dataverse/src/derivations/prism/prism.ts
@@ -231,25 +231,18 @@ class PrismDerivation implements IDerivation {
return new DerivationEmitter(this, ticker).tappable()
}
- /**
- * @deprecated This is renamed to {@link PrismDerivation.onStale}.
- */
- changesWithoutValues(): Tappable {
- return this.onStale()
- }
-
/**
* Returns a tappable that fires every time the prism's state goes from `fresh-\>stale.`
*/
- onStale(): Tappable {
- return new DerivationValuelessEmitter(this).tappable()
+ onStale(callback: () => void): VoidFn {
+ return new DerivationValuelessEmitter(this).tappable().tap(callback)
}
/**
* Keep the derivation hot, even if there are no tappers (subscribers).
*/
keepHot() {
- return this.onStale().tap(() => {})
+ return this.onStale(() => {})
}
/**
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 1eb8877..86777fb 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -132,9 +132,9 @@ type QueueItem = {
*/
| `queueUpdate()`
/**
- * `cb` in `item.der.changesWithoutValues(cb)` was called
+ * `cb` in `item.der.onStale(cb)` was called
*/
- | `changesWithoutValues(cb)`
+ | `onStale(cb)`
/**
* Item was rendered
*/
@@ -158,7 +158,7 @@ type QueueItem = {
*/
queueUpdate: () => void
/**
- * Untaps from `this.der.changesWithoutValues()`
+ * Untaps from `this.der.unStale()`
*/
untap: () => void
}
@@ -327,9 +327,9 @@ export function useDerivation(der: IDerivation, debugLabel?: string): T {
}
pushToQueue(ref.current)
},
- untap: der.changesWithoutValues().tap(() => {
+ untap: der.onStale(() => {
if (TRACE) {
- ref.current.debug!.history.push(`changesWithoutValues(cb)`)
+ ref.current.debug!.history.push(`onStale(cb)`)
}
ref.current!.queueUpdate()
}),
diff --git a/theatre/core/src/sequences/playbackControllers/AudioPlaybackController.ts b/theatre/core/src/sequences/playbackControllers/AudioPlaybackController.ts
index 771abfc..53f6138 100644
--- a/theatre/core/src/sequences/playbackControllers/AudioPlaybackController.ts
+++ b/theatre/core/src/sequences/playbackControllers/AudioPlaybackController.ts
@@ -49,7 +49,7 @@ export default class AudioPlaybackController implements IPlaybackController {
// We're keeping the rangeD hot, so we can read from it on every tick without
// causing unnecessary recalculations
- const untapFromRangeD = rangeD.changesWithoutValues().tap(play)
+ const untapFromRangeD = rangeD.onStale(play)
play()
this._stopPlayCallback = () => {
diff --git a/theatre/core/src/sheetObjects/SheetObject.ts b/theatre/core/src/sheetObjects/SheetObject.ts
index 750204d..74b5c93 100644
--- a/theatre/core/src/sheetObjects/SheetObject.ts
+++ b/theatre/core/src/sheetObjects/SheetObject.ts
@@ -283,9 +283,9 @@ export default class SheetObject implements IdentityDerivationProvider {
interpolate(left, right, triple.progression),
)
}
- const untap = derivation
- .changesWithoutValues()
- .tap(updateSequenceValueFromItsDerivation)
+ const untap = derivation.onStale(
+ updateSequenceValueFromItsDerivation,
+ )
updateSequenceValueFromItsDerivation()
untaps.push(untap)
diff --git a/theatre/studio/src/PaneManager.ts b/theatre/studio/src/PaneManager.ts
index af7071f..86f4478 100644
--- a/theatre/studio/src/PaneManager.ts
+++ b/theatre/studio/src/PaneManager.ts
@@ -15,7 +15,7 @@ export default class PaneManager {
private _instantiatePanesAsTheyComeIn() {
const allPanesD = this._getAllPanes()
- allPanesD.changesWithoutValues().tap(() => {
+ allPanesD.onStale(() => {
allPanesD.getValue()
})
}
diff --git a/theatre/studio/src/Studio.ts b/theatre/studio/src/Studio.ts
index 05ff8c0..6a4d3b5 100644
--- a/theatre/studio/src/Studio.ts
+++ b/theatre/studio/src/Studio.ts
@@ -180,7 +180,7 @@ export class Studio {
}
}
}
- projectsD.changesWithoutValues().tap(() => {
+ projectsD.onStale(() => {
attachToProjects(projectsD.getValue())
})
attachToProjects(projectsD.getValue())
diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
index 73c105d..c770bb8 100644
--- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
+++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
@@ -311,7 +311,7 @@ function useUpdateScrollFromClippedSpaceRange(
const rangeStartInScaledSpace = d.getValue()
node.scrollLeft = rangeStartInScaledSpace
}
- const untap = d.changesWithoutValues().tap(update)
+ const untap = d.onStale(update)
update()
const timeout = setTimeout(update, 100)
diff --git a/theatre/studio/src/uiComponents/Popover/TooltipContext.tsx b/theatre/studio/src/uiComponents/Popover/TooltipContext.tsx
index 8f0330e..1fc2450 100644
--- a/theatre/studio/src/uiComponents/Popover/TooltipContext.tsx
+++ b/theatre/studio/src/uiComponents/Popover/TooltipContext.tsx
@@ -29,7 +29,7 @@ export const useTooltipOpenState = (): [
}, [])
useEffect(() => {
- return cur.changesWithoutValues().tap(() => {
+ return cur.onStale(() => {
const flag = cur.getValue() === id
if (isOpenRef.current !== flag) isOpenRef.current = flag