626 lines
17 KiB
JavaScript
626 lines
17 KiB
JavaScript
var N = Object.defineProperty;
|
|
var P = (s, t, e) => t in s ? N(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
|
|
var p = (s, t, e) => P(s, typeof t != "symbol" ? t + "" : t, e);
|
|
import * as g from "react";
|
|
import { jsx as j } from "react/jsx-runtime";
|
|
class S {
|
|
constructor(t = /* @__PURE__ */ new Map()) {
|
|
this.map = t;
|
|
}
|
|
clone() {
|
|
return new S(new Map(this.map));
|
|
}
|
|
increment(t) {
|
|
const e = this.map.get(t) ?? 0;
|
|
this.map.set(t, e + 1);
|
|
}
|
|
decrement(t, e) {
|
|
let n = this.map.get(t);
|
|
n !== void 0 && (n -= 1, this.map.set(t, n), n === 0 && e());
|
|
}
|
|
}
|
|
class k {
|
|
constructor() {
|
|
p(this, "map", /* @__PURE__ */ new Map());
|
|
}
|
|
getOrCreate(t) {
|
|
let e = this.map.get(t);
|
|
return e === void 0 && (e = /* @__PURE__ */ new Set(), this.map.set(t, e)), e;
|
|
}
|
|
get(t) {
|
|
return this.map.get(t);
|
|
}
|
|
use(t, e) {
|
|
const n = this.get(t);
|
|
n !== void 0 && e(n);
|
|
}
|
|
delete(t) {
|
|
return this.map.delete(t);
|
|
}
|
|
}
|
|
function d(s, t) {
|
|
return t(s), s;
|
|
}
|
|
function x() {
|
|
}
|
|
const I = "cell";
|
|
function O(s, t) {
|
|
return s === t;
|
|
}
|
|
const C = /* @__PURE__ */ new Map();
|
|
let w;
|
|
class T {
|
|
/**
|
|
* Creates a new realm.
|
|
* @param initialValues - the initial cell values that will populate the realm.
|
|
* Those values will not trigger a recomputation cycle, and will overwrite the initial values specified for each cell.
|
|
*/
|
|
constructor(t = {}) {
|
|
p(this, "subscriptions", new k());
|
|
p(this, "singletonSubscriptions", /* @__PURE__ */ new Map());
|
|
p(this, "graph", new k());
|
|
p(this, "state", /* @__PURE__ */ new Map());
|
|
p(this, "distinctNodes", /* @__PURE__ */ new Map());
|
|
p(this, "executionMaps", /* @__PURE__ */ new Map());
|
|
p(this, "definitionRegistry", /* @__PURE__ */ new Set());
|
|
p(this, "combinedCells", []);
|
|
for (const e of Object.getOwnPropertySymbols(t))
|
|
this.state.set(e, t[e]);
|
|
}
|
|
/**
|
|
* Creates or resolves an existing cell instance in the realm. Useful as a joint point when building your own operators.
|
|
* @returns a reference to the cell.
|
|
* @param value - the initial value of the cell
|
|
* @param distinct - true by default. Pass false to mark the signal as a non-distinct one, meaning that publishing the same value multiple times will re-trigger a recomputation cycle.
|
|
* @param node - optional, a reference to a cell. If the cell has not been touched in the realm before, the realm will instantiate a reference to it. If it's registered already, the function will return the reference.
|
|
*/
|
|
cellInstance(t, e = !0, n = Symbol()) {
|
|
return this.state.has(n) || this.state.set(n, t), e !== !1 && !this.distinctNodes.has(n) && this.distinctNodes.set(n, e === !0 ? O : e), n;
|
|
}
|
|
/**
|
|
* Creates or resolves an existing signal instance in the realm. Useful as a joint point when building your own operators.
|
|
* @returns a reference to the signal.
|
|
* @param distinct - true by default. Pass false to mark the signal as a non-distinct one, meaning that publishing the same value multiple times will re-trigger a recomputation cycle.
|
|
* @param node - optional, a reference to a signal. If the signal has not been touched in the realm before, the realm will instantiate a reference to it. If it's registered already, the function will return the reference.
|
|
*/
|
|
signalInstance(t = !0, e = Symbol()) {
|
|
return t !== !1 && this.distinctNodes.set(e, t === !0 ? O : t), e;
|
|
}
|
|
/**
|
|
* Subscribes to the values published in the referred node.
|
|
* @param node - the cell/signal to subscribe to.
|
|
* @param subscription - the callback to execute when the node receives a new value.
|
|
* @returns a function that, when called, will cancel the subscription.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const signal$ = Signal<number>()
|
|
* const r = new Realm()
|
|
* const unsub = r.sub(signal$, console.log)
|
|
* r.pub(signal$, 2)
|
|
* unsub()
|
|
* r.pub(signal$, 3)
|
|
* ```
|
|
*/
|
|
sub(t, e) {
|
|
this.register(t);
|
|
const n = this.subscriptions.getOrCreate(t);
|
|
return n.add(e), () => n.delete(e);
|
|
}
|
|
/**
|
|
* Subscribes exclusively to values in the referred node.
|
|
* Calling this multiple times on a single node will remove the previous subscription created through `singletonSub`.
|
|
* Subscriptions created through `sub` are not affected.
|
|
* @returns a function that, when called, will cancel the subscription.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const signal$ = Signal<number>()
|
|
* const r = new Realm()
|
|
* // console.log will run only once.
|
|
* r.singletonSub(signal$, console.log)
|
|
* r.singletonSub(signal$, console.log)
|
|
* r.singletonSub(signal$, console.log)
|
|
* r.pub(signal$, 2)
|
|
* ```
|
|
*/
|
|
singletonSub(t, e) {
|
|
return this.register(t), e === void 0 ? this.singletonSubscriptions.delete(t) : this.singletonSubscriptions.set(t, e), () => this.singletonSubscriptions.delete(t);
|
|
}
|
|
/**
|
|
* Clears all exclusive subscriptions.
|
|
*/
|
|
resetSingletonSubs() {
|
|
this.singletonSubscriptions.clear();
|
|
}
|
|
// biome-ignore lint/suspicious/noExplicitAny: I know why we need any here
|
|
subMultiple(t, e) {
|
|
const n = this.signalInstance();
|
|
return this.connect({
|
|
map: (i) => (...r) => {
|
|
i(r);
|
|
},
|
|
sink: n,
|
|
sources: t
|
|
}), this.sub(n, e);
|
|
}
|
|
/**
|
|
* Publishes into multiple nodes simultaneously, triggering a single re-computation cycle.
|
|
* @param values - a record of node references and their values.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const foo$ = Cell('foo')
|
|
* const bar$ = Cell('bar')
|
|
*
|
|
* const r = new Realm()
|
|
* r.pubIn({[foo$]: 'foo1', [bar$]: 'bar1'})
|
|
* ```
|
|
*/
|
|
pubIn(t) {
|
|
var a;
|
|
const e = Reflect.ownKeys(t), n = this.getExecutionMap(e), i = n.refCount.clone(), r = n.participatingNodes.slice(), o = new Map(this.state), l = (h) => {
|
|
this.graph.use(h, (c) => {
|
|
for (const { sources: u, sink: y } of c)
|
|
u.has(h) && i.decrement(y, () => {
|
|
r.splice(r.indexOf(y), 1), l(y);
|
|
});
|
|
});
|
|
};
|
|
for (; ; ) {
|
|
const h = r.shift();
|
|
if (h === void 0)
|
|
break;
|
|
const c = h;
|
|
let u = !1;
|
|
const y = (m) => {
|
|
const f = this.distinctNodes.get(c);
|
|
if (f != null && f(o.get(c), m)) {
|
|
u = !1;
|
|
return;
|
|
}
|
|
u = !0, o.set(c, m), this.state.has(c) && this.state.set(c, m);
|
|
};
|
|
if (Object.prototype.hasOwnProperty.call(t, c) ? y(t[c]) : n.projections.use(c, (m) => {
|
|
for (const f of m) {
|
|
const M = [...Array.from(f.sources), ...Array.from(f.pulls)].map((v) => o.get(v));
|
|
f.map(y)(...M);
|
|
}
|
|
}), u) {
|
|
const m = o.get(c);
|
|
this.inContext(() => {
|
|
this.subscriptions.use(c, (f) => {
|
|
for (const M of f)
|
|
M(m);
|
|
});
|
|
}), (a = this.singletonSubscriptions.get(c)) == null || a(m);
|
|
} else
|
|
l(c);
|
|
}
|
|
}
|
|
/**
|
|
* A low-level utility that connects multiple nodes to a sink node with a map function. Used as a foundation for the higher-level operators.
|
|
* The nodes can be active (sources) or passive (pulls).
|
|
*/
|
|
connect({
|
|
sources: t,
|
|
pulls: e = [],
|
|
map: n,
|
|
sink: i
|
|
}) {
|
|
const r = {
|
|
map: n,
|
|
pulls: new Set(e),
|
|
sink: this.register(i),
|
|
sources: new Set(t)
|
|
};
|
|
for (const o of [...t, ...e])
|
|
this.register(o), this.graph.getOrCreate(o).add(r);
|
|
this.executionMaps.clear();
|
|
}
|
|
pub(t, e) {
|
|
this.pubIn({ [t]: e });
|
|
}
|
|
pipe(t, ...e) {
|
|
return this.combineOperators(...e)(t);
|
|
}
|
|
transformer(...t) {
|
|
return (e) => d(this.signalInstance(), (n) => (this.link(this.pipe(n, ...t), e), n));
|
|
}
|
|
/**
|
|
* Links the output of a node to the input of another node.
|
|
*/
|
|
link(t, e) {
|
|
this.connect({
|
|
map: (n) => (i) => {
|
|
n(i);
|
|
},
|
|
sink: e,
|
|
sources: [t]
|
|
});
|
|
}
|
|
// prettier-ignore
|
|
combine(...t) {
|
|
return d(this.signalInstance(), (e) => {
|
|
this.connect({
|
|
map: (n) => (...i) => {
|
|
n(i);
|
|
},
|
|
sink: e,
|
|
sources: t
|
|
});
|
|
});
|
|
}
|
|
// prettier-ignore
|
|
combineCells(...t) {
|
|
const e = this.combinedCells.find((i) => t.length === i.sources.length && t.every((r, o) => r === i.sources[o]));
|
|
if (e)
|
|
return e.cell;
|
|
const n = this.cellInstance(
|
|
t.map((i) => this.getValue(i)),
|
|
!0
|
|
);
|
|
return this.connect({
|
|
map: (i) => (...r) => {
|
|
i(r);
|
|
},
|
|
sink: n,
|
|
sources: t
|
|
}), this.combinedCells.push({ sources: t, cell: n }), n;
|
|
}
|
|
/**
|
|
* Gets the current value of a node. The node must be stateful.
|
|
* @remark if possible, use {@link withLatestFrom} or {@link combine}, as getValue will not create a dependency to the passed node,
|
|
* which means that if you call it within a computational cycle, you may not get the correct value.
|
|
* @param node - the node instance.
|
|
* @example
|
|
* ```ts
|
|
* const foo$ = Cell('foo')
|
|
*
|
|
* const r = new Realm()
|
|
* r.getValue(foo$) // 'foo'
|
|
* r.pub(foo$, 'bar')
|
|
* //...
|
|
* r.getValue(foo$) // 'bar'
|
|
* ```
|
|
*/
|
|
getValue(t) {
|
|
return this.register(t), this.state.get(t);
|
|
}
|
|
getValues(t) {
|
|
return t.map((e) => this.getValue(e));
|
|
}
|
|
/**
|
|
* Explicitly includes the specified cell/signal reference in the realm.
|
|
* Most of the time you don't need to do that, since any interaction with the node through a realm will register it.
|
|
* The only exception of that rule should be when the interaction is conditional, and the node definition includes an init function that needs to be eagerly evaluated.
|
|
*/
|
|
register(t) {
|
|
const e = C.get(t);
|
|
return e === void 0 || this.definitionRegistry.has(t) ? t : (this.definitionRegistry.add(t), d(
|
|
e.type === I ? this.cellInstance(e.initial, e.distinct, t) : this.signalInstance(e.distinct, t),
|
|
(n) => {
|
|
this.inContext(() => {
|
|
e.init(this, n);
|
|
});
|
|
}
|
|
));
|
|
}
|
|
inContext(t) {
|
|
const e = w;
|
|
w = this;
|
|
const n = t();
|
|
return w = e, n;
|
|
}
|
|
/**
|
|
* Convenient for mutation of cells that contian non-primitive values (e.g. arrays, or objects).
|
|
* Specifies that the cell value should be changed when source emits, with the result of the map callback parameter.
|
|
* the map parameter gets called with the current value of the cell and the value published through the source.
|
|
* @typeParam T - the type of the cell value.
|
|
* @typeParam K - the type of the value published through the source.
|
|
* @example
|
|
* ```ts
|
|
* const items$ = Cell<string[]([])
|
|
* const addItem$ = Signal<string>(false, (r) => {
|
|
* r.changeWith(items$, addItem$, (items, item) => [...items, item])
|
|
* })
|
|
* const r = new Realm()
|
|
* r.pub(addItem$, 'foo')
|
|
* r.pub(addItem$, 'bar')
|
|
* r.getValue(items$) // ['foo', 'bar']
|
|
* ```
|
|
*/
|
|
changeWith(t, e, n) {
|
|
this.connect({
|
|
sources: [e],
|
|
pulls: [t],
|
|
sink: t,
|
|
map: (i) => (r, o) => {
|
|
i(n(o, r));
|
|
}
|
|
});
|
|
}
|
|
calculateExecutionMap(t) {
|
|
const e = [], n = /* @__PURE__ */ new Set(), i = new k(), r = new S(), o = new k(), l = (a, h = 0) => {
|
|
r.increment(a), !n.has(a) && (this.register(a), i.use(a, (c) => {
|
|
h = Math.max(...Array.from(c).map((u) => e.indexOf(u))) + 1;
|
|
}), this.graph.use(a, (c) => {
|
|
for (const u of c)
|
|
u.sources.has(a) ? (o.getOrCreate(u.sink).add(u), l(u.sink, h)) : i.getOrCreate(u.sink).add(a);
|
|
}), n.add(a), e.splice(h, 0, a));
|
|
};
|
|
return t.forEach(l), { participatingNodes: e, pendingPulls: i, projections: o, refCount: r };
|
|
}
|
|
getExecutionMap(t) {
|
|
let e = t;
|
|
if (t.length === 1) {
|
|
e = t[0];
|
|
const i = this.executionMaps.get(e);
|
|
if (i !== void 0)
|
|
return i;
|
|
} else
|
|
for (const [i, r] of this.executionMaps.entries())
|
|
if (Array.isArray(i) && i.length === t.length && i.every((o) => t.includes(o)))
|
|
return r;
|
|
const n = this.calculateExecutionMap(t);
|
|
return this.executionMaps.set(e, n), n;
|
|
}
|
|
combineOperators(...t) {
|
|
return (e) => {
|
|
for (const n of t)
|
|
e = n(e, this);
|
|
return e;
|
|
};
|
|
}
|
|
}
|
|
function $(s, t = x, e = !0) {
|
|
return d(Symbol(), (n) => {
|
|
C.set(n, { type: I, distinct: e, initial: s, init: t });
|
|
});
|
|
}
|
|
function D(s, t, e = !0) {
|
|
return d(Symbol(), (n) => {
|
|
C.set(n, {
|
|
type: I,
|
|
distinct: e,
|
|
initial: s,
|
|
init: (i, r) => {
|
|
i.link(t(i, r), r);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
function K(s = x, t = !1) {
|
|
return d(Symbol(), (e) => {
|
|
C.set(e, { type: "signal", distinct: t, init: s });
|
|
});
|
|
}
|
|
function q(s = x) {
|
|
return d(Symbol(), (t) => {
|
|
C.set(t, { type: "signal", distinct: !1, init: s });
|
|
});
|
|
}
|
|
function b() {
|
|
if (!w)
|
|
throw new Error("This function must be called within a realm instance");
|
|
return w;
|
|
}
|
|
const F = (s, t) => {
|
|
b().link(s, t);
|
|
}, Y = (...s) => {
|
|
b().pub(...s);
|
|
}, z = (...s) => b().sub(...s), B = (...s) => {
|
|
b().pubIn(...s);
|
|
}, G = (...s) => b().pipe(...s), H = (...s) => {
|
|
b().changeWith(...s);
|
|
}, J = (...s) => b().combine(...s), Q = (s) => b().getValue(s), V = g.createContext(null);
|
|
function U({
|
|
children: s,
|
|
initWith: t,
|
|
updateWith: e = {}
|
|
}) {
|
|
const n = g.useMemo(() => new T(t), []);
|
|
return g.useEffect(() => {
|
|
n.pubIn(e);
|
|
}, [e, n]), /* @__PURE__ */ j(V.Provider, { value: n, children: s });
|
|
}
|
|
function R() {
|
|
const s = g.useContext(V);
|
|
if (s === null)
|
|
throw new Error("useRealm must be used within a RealmContextProvider");
|
|
return s;
|
|
}
|
|
function E(s) {
|
|
const t = R();
|
|
t.register(s);
|
|
const e = g.useCallback((n) => t.sub(s, n), [t, s]);
|
|
return g.useSyncExternalStore(
|
|
e,
|
|
() => t.getValue(s),
|
|
() => t.getValue(s)
|
|
);
|
|
}
|
|
function X(...s) {
|
|
const t = R(), e = g.useMemo(() => t.combineCells.apply(t, s), [t, ...s]);
|
|
return E(e);
|
|
}
|
|
function A(s) {
|
|
const t = R();
|
|
return t.register(s), g.useCallback(
|
|
(e) => {
|
|
t.pub(s, e);
|
|
},
|
|
[t, s]
|
|
);
|
|
}
|
|
function Z(s) {
|
|
return [E(s), A(s)];
|
|
}
|
|
function _(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
return e.connect({
|
|
map: (i) => (r) => {
|
|
i(s(r));
|
|
},
|
|
sink: n,
|
|
sources: [t]
|
|
}), n;
|
|
};
|
|
}
|
|
function tt(...s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
return e.connect({
|
|
map: (i) => (...r) => {
|
|
i(r);
|
|
},
|
|
pulls: s,
|
|
sink: n,
|
|
sources: [t]
|
|
}), n;
|
|
};
|
|
}
|
|
function et(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
return e.connect({
|
|
map: (i) => () => {
|
|
i(s);
|
|
},
|
|
sink: n,
|
|
sources: [t]
|
|
}), n;
|
|
};
|
|
}
|
|
function nt(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
return e.connect({
|
|
map: (i) => (r) => {
|
|
s(r) && i(r);
|
|
},
|
|
sink: n,
|
|
sources: [t]
|
|
}), n;
|
|
};
|
|
}
|
|
function st() {
|
|
return (s, t) => {
|
|
const e = t.signalInstance();
|
|
let n = !1;
|
|
return t.connect({
|
|
map: (i) => (r) => {
|
|
n || (n = !0, i(r));
|
|
},
|
|
sink: e,
|
|
sources: [s]
|
|
}), e;
|
|
};
|
|
}
|
|
function it(s, t) {
|
|
return (e, n) => {
|
|
const i = n.signalInstance();
|
|
return n.connect({
|
|
map: (r) => (o) => {
|
|
r(t = s(t, o));
|
|
},
|
|
sink: i,
|
|
sources: [e]
|
|
}), i;
|
|
};
|
|
}
|
|
function rt(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
let i, r = null;
|
|
return e.sub(t, (o) => {
|
|
i = o, r === null && (r = setTimeout(() => {
|
|
r = null, e.pub(n, i);
|
|
}, s));
|
|
}), n;
|
|
};
|
|
}
|
|
function ot(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance();
|
|
let i, r = null;
|
|
return e.sub(t, (o) => {
|
|
i = o, r !== null && clearTimeout(r), r = setTimeout(() => {
|
|
e.pub(n, i);
|
|
}, s);
|
|
}), n;
|
|
};
|
|
}
|
|
function ct() {
|
|
return (s, t) => {
|
|
const e = t.signalInstance();
|
|
return t.sub(s, (n) => {
|
|
queueMicrotask(() => {
|
|
t.pub(e, n);
|
|
});
|
|
}), e;
|
|
};
|
|
}
|
|
function ut(s) {
|
|
return (t, e) => {
|
|
const n = e.signalInstance(), i = Symbol();
|
|
let r = i;
|
|
return e.connect({
|
|
map: (o) => (l) => {
|
|
r !== i && (o([r, l]), r = i);
|
|
},
|
|
sink: n,
|
|
sources: [s]
|
|
}), e.sub(t, (o) => {
|
|
r = o;
|
|
}), n;
|
|
};
|
|
}
|
|
function at(s, t, e) {
|
|
return (n, i) => {
|
|
const r = i.signalInstance();
|
|
return i.sub(n, (o) => {
|
|
o !== null && typeof o == "object" && "then" in o ? (i.pub(r, s()), o.then((l) => {
|
|
i.pub(r, t(l));
|
|
}).catch((l) => {
|
|
i.pub(r, e(l));
|
|
})) : i.pub(r, t(o));
|
|
}), r;
|
|
};
|
|
}
|
|
export {
|
|
q as Action,
|
|
$ as Cell,
|
|
D as DerivedCell,
|
|
T as Realm,
|
|
V as RealmContext,
|
|
U as RealmProvider,
|
|
K as Signal,
|
|
H as changeWith,
|
|
J as combine,
|
|
ot as debounceTime,
|
|
O as defaultComparator,
|
|
ct as delayWithMicrotask,
|
|
nt as filter,
|
|
Q as getValue,
|
|
at as handlePromise,
|
|
F as link,
|
|
_ as map,
|
|
et as mapTo,
|
|
ut as onNext,
|
|
st as once,
|
|
G as pipe,
|
|
Y as pub,
|
|
B as pubIn,
|
|
it as scan,
|
|
z as sub,
|
|
rt as throttleTime,
|
|
Z as useCell,
|
|
E as useCellValue,
|
|
X as useCellValues,
|
|
A as usePublisher,
|
|
R as useRealm,
|
|
tt as withLatestFrom
|
|
};
|