tilia
Version:
🍃 State management library, domain-driven.
970 lines (908 loc) • 21.5 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
let raise = (function (message) {
throw new Error(message)
});
let reraise = (function (e) {
console.error("Reraising exception after flush");
throw e
});
let callCb = (function(cb, k, v) { cb(k, v) });
function cleanTrace(stack) {
if (typeof stack !== "string") return stack;
const cleaned = ["Exception thrown in computed or observe"];
let collapsing = false;
for (const line of stack.split("\n")) {
if (/src\/Tilia\.mjs:\d+:\d+/.test(line)) {
if (!collapsing) {
cleaned.push(" [... tilia internals]");
collapsing = true;
}
} else {
cleaned.push(line);
collapsing = false;
}
}
return cleaned.join("\n");
}
;
let symbol = (function(s) {
return Symbol.for('tilia:' + s);
});
let indexKey = symbol("indexKey");
let metaKey = symbol("metaKey");
let dynamicKey = symbol("dynamicKey");
let ctxKey = symbol("ctx");
let proxiable = (function(v) {
if ( typeof v === 'object' && v !== null) {
const proto = Object.getPrototypeOf(v)
return proto === Object.prototype || proto === Array.prototype || proto === null
}
return false;
});
let dynamic = (function(v) {
return typeof v === 'object' && v !== null && v[dynamicKey] ? v : undefined;
});
function readonly(o, k) {
let d = Object.getOwnPropertyDescriptor(o, k);
if (d == null) {
return false;
} else {
return d.writable === false;
}
}
let setError = (function (root, e) {
if (typeof e === "object" && e !== null) {
if (e.stack) {
console.error(cleanTrace(e.stack));
} else {
console.error(e);
}
root.error = e;
}
});
function observeKey(observed, key) {
let w = observed.get(key);
if (w !== null && w !== undefined) {
return w;
}
w === null;
let w$1 = {
state: "Pristine",
key: key,
observed: observed,
observers: new Set()
};
observed.set(key, w$1);
return w$1;
}
function flush(root) {
if (root.expired.size > 0) {
while (root.expired.size > 0) {
let expired = root.expired;
root.expired = new Set();
expired.forEach(observer => observer.notify());
};
}
let gc = root.gc;
if (gc.active.size >= gc.threshold) {
gc.quarantine.forEach(w => {
if (w.state === "Pristine" && w.observers.size === 0) {
w.state = "Cleared";
w.observed.delete(w.key);
return;
}
});
gc.quarantine = gc.active;
gc.active = new Set();
}
if (root.error === undefined) {
return;
}
let e = root.error;
root.error = undefined;
reraise(e);
}
function _clear(observer) {
let root = observer.root;
observer.observing.forEach(watchers => {
if (watchers.state === "Pristine" && watchers.observers.delete(observer) && watchers.observers.size === 0) {
root.gc.active.add(watchers);
return;
}
});
let o = root.observer;
if ((o == null) || o !== observer) {
return;
} else {
root.observer = undefined;
if (!root.lock) {
return flush(root);
} else {
return;
}
}
}
function _ready(observer, notifyIfChanged) {
observer.observing.find((w, idx) => {
let match = w.state;
switch (match) {
case "Pristine" :
w.observers.add(observer);
return false;
case "Changed" :
case "Cleared" :
break;
}
if (notifyIfChanged) {
_clear(observer);
observer.notify();
return true;
}
let w$1 = observeKey(w.observed, w.key);
w$1.observers.add(observer);
observer.observing[idx] = w$1;
return false;
});
let root = observer.root;
let o = root.observer;
if ((o == null) || o !== observer) {
return;
} else {
root.observer = undefined;
if (!root.lock) {
return flush(root);
} else {
return;
}
}
}
function notify(root, observed, key) {
let watchers = observed.get(key);
if (watchers == null) {
return;
}
observed.delete(key);
watchers.state = "Changed";
let expired = root.expired;
watchers.observers.forEach(observer => {
_clear(observer);
expired.add(observer);
});
let match = root.observer;
if (match !== null && match !== undefined) {
return;
}
match === null;
if (!root.lock) {
return flush(root);
}
}
function set(node, isArray, _fromComputed, target, _key, _value) {
while (true) {
let value = _value;
let key = _key;
let fromComputed = _fromComputed;
let hadKey = Reflect.has(target, key);
let prev = Reflect.get(target, key);
let proxiable$1 = proxiable(value);
let same;
if (proxiable$1) {
let m = Reflect.get(value, metaKey);
same = (m == null) ? prev === value : m.target === prev;
} else {
same = prev === value;
}
if (same) {
return true;
}
if (!Reflect.set(target, key, value)) {
return false;
}
let key$1 = isArray && key === "length" ? indexKey : key;
if (proxiable$1) {
node.proxied.delete(key$1);
}
if (!fromComputed) {
let clear = node.computes.get(key$1);
if (clear == null) {
clear === null;
} else {
node.computes.delete(key$1);
clear();
}
}
let match = dynamic(value);
if (match == null) {
match === null;
} else {
let w = node.observed.get(key$1);
if (w == null) {
return true;
}
if (!(w.state === "Pristine" && w.observers.size > 0)) {
return true;
}
Reflect.set(target, key$1, prev);
let compile$1 = callback => compile(node, isArray, target, key$1, callback);
let setter = v => {
set(node, isArray, true, target, key$1, v);
};
let get = _value => {
while (true) {
let value = _value;
let dynamic$1 = dynamic(value);
if (dynamic$1 == null) {
return value;
}
let v;
switch (dynamic$1.TAG) {
case "Computed" :
v = compile$1(dynamic$1._0);
break;
case "Source" :
let source = dynamic$1._0;
let v$1 = source.value;
let val = {
contents: v$1
};
let set = v => {
val.contents = v;
setter(v);
};
v = compile$1((set(v$1), () => {
let callback = source.source;
callback(val.contents, set);
return val.contents;
}));
break;
case "Store" :
let store = dynamic$1._0;
v = compile$1(() => store.store(setter));
break;
case "Compiled" :
let rebuild = dynamic$1._0.rebuild;
v = rebuild();
break;
}
if (!proxiable(v)) {
return v;
}
_value = v;
continue;
};
};
let v = get(value);
_value = v;
_key = key$1;
_fromComputed = true;
continue;
}
if (!fromComputed) {
let cbs = node.changes;
if (cbs == null) {
cbs === null;
} else {
cbs.forEach(cb => callCb(cb, key, value));
}
}
notify(node.root, node.observed, key$1);
if (!hadKey) {
notify(node.root, node.observed, indexKey);
}
return true;
};
}
function compile(node, isArray, target, key, callback) {
let lastValue = {
v: undefined
};
let observer = {
o: undefined
};
let compute = {
rebuild: undefined
};
let compiled = {
TAG: "Compiled",
_0: compute
};
Reflect.set(compiled, dynamicKey, true);
let notify = () => {
let v = Reflect.get(target, key);
if (v !== compiled) {
lastValue.v = v;
}
let w = node.observed.get(key);
if (w !== null && w !== undefined) {
if (w.observers.size > 0) {
set(node, isArray, true, target, key, rebuild());
} else {
w.state = "Changed";
node.observed.delete(key);
node.proxied.delete(key);
Reflect.set(target, key, compiled);
}
return;
}
w === null;
node.proxied.delete(key);
Reflect.set(target, key, compiled);
};
let rebuild = () => {
let curr = Reflect.get(target, key);
if (curr === compiled) {
Reflect.set(target, key, lastValue.v);
} else {
lastValue.v = curr;
}
let o_root = node.root;
let o_observing = [];
let o = {
root: o_root,
notify: notify,
observing: o_observing
};
observer.o = o;
let previous = node.root.observer;
node.root.observer = o;
let v;
try {
v = callback();
} catch (_e) {
setError(node.root, _e);
_clear(o);
if (previous == null) {
previous === null;
} else {
_clear(previous);
}
v = lastValue.v;
}
node.root.observer = previous;
if (o_observing.length === 0) {
_clear(o);
node.computes.delete(key);
} else {
_ready(o, false);
}
return v;
};
compute.rebuild = rebuild;
let clear = () => {
let o = observer.o;
if (o == null) {
return;
}
_clear(o);
((o.observing.length = 0));
};
node.computes.set(key, clear);
return rebuild();
}
function proxify(root, _target) {
while (true) {
let target = _target;
let m = Reflect.get(target, metaKey);
if (m == null) {
m === null;
} else {
if (m.root === root) {
return m;
}
_target = m.target;
continue;
}
let node = {
root: root,
observed: new Map(),
proxied: new Map(),
computes: new Map(),
changes: undefined
};
let meta = ((node.target = target, node));
let isArray = Array.isArray(target);
let proxy = new Proxy(target, {
set: (extra, extra$1, extra$2) => set(node, isArray, false, extra, extra$1, extra$2),
deleteProperty: (extra, extra$1) => {
let res = Reflect.deleteProperty(extra, extra$1);
node.proxied.delete(extra$1);
let clear = node.computes.get(extra$1);
if (clear == null) {
clear === null;
} else {
node.computes.delete(extra$1);
clear();
}
let cbs = node.changes;
if (cbs == null) {
cbs === null;
} else {
cbs.forEach(cb => callCb(cb, extra$1, undefined));
}
notify(node.root, node.observed, extra$1);
return res;
},
get: (extra, extra$1) => {
if (extra$1 === metaKey) {
return meta;
}
if (extra$1 === dynamicKey) {
return undefined;
}
let value = Reflect.get(extra, extra$1);
let own = Object.hasOwn(extra, extra$1);
if (!(value === undefined || own)) {
return value;
}
let o = node.root.observer;
if (o == null) {
o === null;
} else if (isArray && extra$1 === "length") {
let w = observeKey(node.observed, indexKey);
o.observing.push(w);
} else {
let w$1 = observeKey(node.observed, extra$1);
o.observing.push(w$1);
}
if (!(proxiable(value) && !readonly(extra, extra$1))) {
return value;
}
let m = node.proxied.get(extra$1);
if (m !== null && m !== undefined) {
return m.proxy;
}
m === null;
let compile$1 = callback => compile(node, isArray, extra, extra$1, callback);
let setter = v => {
set(node, isArray, true, extra, extra$1, v);
};
let get = _value => {
while (true) {
let value = _value;
let dynamic$1 = dynamic(value);
if (dynamic$1 == null) {
return value;
}
let v;
switch (dynamic$1.TAG) {
case "Computed" :
v = compile$1(dynamic$1._0);
break;
case "Source" :
let source = dynamic$1._0;
let v$1 = source.value;
let val = {
contents: v$1
};
let set = v => {
val.contents = v;
setter(v);
};
v = compile$1((set(v$1), () => {
let callback = source.source;
callback(val.contents, set);
return val.contents;
}));
break;
case "Store" :
let store = dynamic$1._0;
v = compile$1(() => store.store(setter));
break;
case "Compiled" :
let rebuild = dynamic$1._0.rebuild;
v = rebuild();
break;
}
if (!proxiable(v)) {
return v;
}
_value = v;
continue;
};
};
let v = get(value);
Reflect.set(extra, extra$1, v);
if (!proxiable(v)) {
return v;
}
let m$1 = proxify(node.root, v);
node.proxied.set(extra$1, m$1);
return m$1.proxy;
},
ownKeys: extra => {
let root = node.root;
let observed = node.observed;
let keys = Reflect.ownKeys(extra);
let o = root.observer;
if (o == null) {
o === null;
} else {
let w = observeKey(observed, indexKey);
o.observing.push(w);
}
return keys;
}
});
meta.proxy = proxy;
return meta;
};
}
function makeTilia(root) {
return value => {
if (!proxiable(value)) {
raise("tilia: value is not an object or array");
}
return proxify(root, value).proxy;
};
}
function makeDerived(p) {
return fn => {
let v = {
TAG: "Computed",
_0: () => fn(p.contents)
};
Reflect.set(v, dynamicKey, true);
return v;
};
}
function makeReactive(derived) {
return { derived }
}
;
function makeCarve(root) {
return fn => {
let p = {
contents: {}
};
let ctx = makeReactive(makeDerived(p));
let value = fn(ctx);
if (!proxiable(value)) {
raise("tilia: value is not an object or array");
}
let value$1 = proxify(root, value).proxy;
p.contents = value$1;
return value$1;
};
}
function makeObserve(root) {
return callback => {
let notify = () => {
let observer_observing = [];
let observer = {
root: root,
notify: notify,
observing: observer_observing
};
root.observer = observer;
let o = observer;
try {
callback();
return _ready(o, true);
} catch (_e) {
setError(root, _e);
return _clear(o);
}
};
notify();
};
}
function makeWatch(root, observe_) {
return (callback, effect) => {
let notify = () => {
let o = observe_(notify);
let v = callback();
o.root.observer = undefined;
if (root.lock) {
effect(v);
} else {
root.lock = true;
effect(v);
root.lock = false;
}
_ready(o, false);
};
let o = observe_(notify);
callback();
_ready(o, false);
};
}
function makeBatch(root) {
return callback => {
if (root.lock) {
return callback();
} else {
root.lock = true;
callback();
root.lock = false;
return flush(root);
}
};
}
let orphanError = "Cannot modify or access the value of an orphan computation. See https://tiliajs.com/errors#orphan";
function warningHandler() {
return {
set: (param, _key, _value) => raise(orphanError),
deleteProperty: (param, _key) => raise(orphanError),
get: (target, key) => {
if (key === dynamicKey || key === metaKey || key === "TAG" || key === "_0") {
return Reflect.get(target, key);
} else {
return raise(orphanError);
}
},
ownKeys: target => Reflect.ownKeys(target)
};
}
function computed(fn) {
let v = {
TAG: "Computed",
_0: fn
};
Reflect.set(v, dynamicKey, true);
return new Proxy(v, warningHandler());
}
function makeSource(tilia) {
return (value, source) => {
let d = dynamic(source);
let s;
s = (d == null) ? ({
source: source,
value: value
}) : (tilia({value, source: d}));
let v = {
TAG: "Source",
_0: s
};
Reflect.set(v, dynamicKey, true);
return v;
};
}
function makeStore(tilia) {
return callback => {
let d = dynamic(callback);
let s;
s = (d == null) ? ({
store: callback
}) : (tilia({store: d}));
let v = {
TAG: "Store",
_0: s
};
Reflect.set(v, dynamicKey, true);
return v;
};
}
function makeDerived$1(tilia) {
return fn => tilia({
value: computed(() => fn())
});
}
function _done(o) {
o.root.observer = undefined;
}
function connector(tilia, carve, observe, watch, batch, signal, derived, source, store, _observe) {
return {
tilia,
carve,
observe,
watch,
batch,
// extra
signal,
derived,
source,
store,
// internal
_observe,
};
}
;
function make(gcOpt) {
let gc = gcOpt !== undefined ? gcOpt : 50;
let gc$1 = {
active: new Set(),
quarantine: new Set(),
threshold: gc
};
let root = {
observer: undefined,
expired: new Set(),
lock: false,
error: undefined,
id: Math.random(),
gc: gc$1
};
let tilia = makeTilia(root);
let _observe = extra => {
let observer_observing = [];
let observer = {
root: root,
notify: extra,
observing: observer_observing
};
root.observer = observer;
return observer;
};
return connector(tilia, makeCarve(root), makeObserve(root), makeWatch(root, _observe), makeBatch(root), value => {
let s = tilia({
value: value
});
let set = v => {
s.value = v;
};
return [
s,
set
];
}, makeDerived$1(tilia), makeSource(tilia), makeStore(tilia), _observe);
}
let ctx = Reflect.get(globalThis, ctxKey);
let _ctx;
let exit = 0;
if (ctx == null) {
exit = 1;
} else {
_ctx = ctx;
}
if (exit === 1) {
let ctx$1 = make(undefined);
Reflect.set(globalThis, ctxKey, ctx$1);
_ctx = ctx$1;
}
function readonly$1(data) {
let obj = {};
Object.defineProperty(obj, "data", {
writable: false,
enumerable: true,
configurable: false,
value: data
});
return obj;
}
function lift(s) {
return computed(() => s.value);
}
function changing(accessor, guard) {
let empty_upsert = [];
let empty_remove = [];
let empty = {
upsert: empty_upsert,
remove: empty_remove
};
let m = Reflect.get(accessor(), metaKey);
let meta;
meta = (m == null) ? raise("changing: argument is not a tilia proxy") : m;
let root = meta.root;
let pending = new Map();
let counter = proxify(root, {
changed: 0
}).proxy;
let currentMeta = {
contents: meta
};
let register = (m, cb) => {
let cbs = m.changes;
let cbs$1;
let exit = 0;
if (cbs == null) {
exit = 1;
} else {
cbs$1 = cbs;
}
if (exit === 1) {
let cbs$2 = new Set();
m.changes = cbs$2;
cbs$1 = cbs$2;
}
cbs$1.add(cb);
return cbs$1;
};
let drain = () => {
let upsert = [];
let remove = [];
pending.forEach((v, k) => {
if (v == null) {
remove.push(k);
} else {
upsert.push(v);
}
pending.delete(k);
});
return {
upsert: upsert,
remove: remove
};
};
let cb = (key, value) => {
pending.set(key, value);
counter.changed = counter.changed + 1 | 0;
};
let currentCbs = {
contents: register(meta, cb)
};
let reregister = () => {
let m = Reflect.get(accessor(), metaKey);
if ((m == null) || m === currentMeta.contents) {
return;
} else {
currentCbs.contents.delete(cb);
currentCbs.contents = register(m, cb);
currentMeta.contents = m;
return;
}
};
let capture = guard !== undefined ? () => {
reregister();
if (guard()) {
Reflect.get(counter, "changed");
if (pending.size > 0) {
return drain();
} else {
return empty;
}
} else {
return empty;
}
} : () => {
reregister();
Reflect.get(counter, "changed");
if (pending.size > 0) {
return drain();
} else {
return empty;
}
};
let mute = fn => {
reregister();
currentCbs.contents.delete(cb);
if (root.lock) {
fn();
} else {
root.lock = true;
fn();
root.lock = false;
flush(root);
}
currentCbs.contents.add(cb);
};
return {
changes: capture,
mute: mute
};
}
let tilia = _ctx.tilia;
let carve = _ctx.carve;
let observe = _ctx.observe;
let watch = _ctx.watch;
let batch = _ctx.batch;
let signal = _ctx.signal;
let derived = _ctx.derived;
let source = _ctx.source;
let store = _ctx.store;
let _observe = _ctx._observe;
function _meta(p) {
return Reflect.get(p, metaKey);
}
export {
make,
tilia,
carve,
observe,
watch,
batch,
signal,
derived,
readonly$1 as readonly,
computed,
lift,
source,
store,
changing,
_observe,
_done,
_ready,
_clear,
_meta,
_ctx,
}
/* Not a pure module */