tilia
Version:
🍃 State management library, domain-driven.
913 lines (912 loc) • 21.6 kB
JavaScript
// src/Tilia.mjs
var raise = (function(message) {
throw new Error(message);
});
var reraise = (function(e) {
console.error("Reraising exception after flush");
throw e;
});
var 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");
}
var symbol = (function(s) {
return Symbol.for("tilia:" + s);
});
var indexKey = symbol("indexKey");
var metaKey = symbol("metaKey");
var dynamicKey = symbol("dynamicKey");
var ctxKey = symbol("ctx");
var 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;
});
var dynamic = (function(v) {
return typeof v === "object" && v !== null && v[dynamicKey] ? v : void 0;
});
function readonly(o, k) {
let d = Object.getOwnPropertyDescriptor(o, k);
if (d == null) {
return false;
} else {
return d.writable === false;
}
}
var 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 !== void 0) {
return w;
}
w === null;
let w$1 = {
state: "Pristine",
key,
observed,
observers: /* @__PURE__ */ 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ new Set();
}
if (root.error === void 0) {
return;
}
let e = root.error;
root.error = void 0;
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 = void 0;
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 = void 0;
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 !== void 0) {
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 = (v2) => {
set(node, isArray, true, target, key$1, v2);
};
let get = (_value2) => {
while (true) {
let value2 = _value2;
let dynamic$1 = dynamic(value2);
if (dynamic$1 == null) {
return value2;
}
let v2;
switch (dynamic$1.TAG) {
case "Computed":
v2 = compile$1(dynamic$1._0);
break;
case "Source":
let source2 = dynamic$1._0;
let v$1 = source2.value;
let val = {
contents: v$1
};
let set2 = (v3) => {
val.contents = v3;
setter(v3);
};
v2 = compile$1((set2(v$1), () => {
let callback = source2.source;
callback(val.contents, set2);
return val.contents;
}));
break;
case "Store":
let store2 = dynamic$1._0;
v2 = compile$1(() => store2.store(setter));
break;
case "Compiled":
let rebuild = dynamic$1._0.rebuild;
v2 = rebuild();
break;
}
if (!proxiable(v2)) {
return v2;
}
_value2 = v2;
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: void 0
};
let observer = {
o: void 0
};
let compute = {
rebuild: void 0
};
let compiled = {
TAG: "Compiled",
_0: compute
};
Reflect.set(compiled, dynamicKey, true);
let notify2 = () => {
let v = Reflect.get(target, key);
if (v !== compiled) {
lastValue.v = v;
}
let w = node.observed.get(key);
if (w !== null && w !== void 0) {
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: notify2,
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,
observed: /* @__PURE__ */ new Map(),
proxied: /* @__PURE__ */ new Map(),
computes: /* @__PURE__ */ new Map(),
changes: void 0
};
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, void 0));
}
notify(node.root, node.observed, extra$1);
return res;
},
get: (extra, extra$1) => {
if (extra$1 === metaKey) {
return meta;
}
if (extra$1 === dynamicKey) {
return void 0;
}
let value = Reflect.get(extra, extra$1);
let own = Object.hasOwn(extra, extra$1);
if (!(value === void 0 || 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 m2 = node.proxied.get(extra$1);
if (m2 !== null && m2 !== void 0) {
return m2.proxy;
}
m2 === null;
let compile$1 = (callback) => compile(node, isArray, extra, extra$1, callback);
let setter = (v2) => {
set(node, isArray, true, extra, extra$1, v2);
};
let get = (_value) => {
while (true) {
let value2 = _value;
let dynamic$1 = dynamic(value2);
if (dynamic$1 == null) {
return value2;
}
let v2;
switch (dynamic$1.TAG) {
case "Computed":
v2 = compile$1(dynamic$1._0);
break;
case "Source":
let source2 = dynamic$1._0;
let v$1 = source2.value;
let val = {
contents: v$1
};
let set2 = (v3) => {
val.contents = v3;
setter(v3);
};
v2 = compile$1((set2(v$1), () => {
let callback = source2.source;
callback(val.contents, set2);
return val.contents;
}));
break;
case "Store":
let store2 = dynamic$1._0;
v2 = compile$1(() => store2.store(setter));
break;
case "Compiled":
let rebuild = dynamic$1._0.rebuild;
v2 = rebuild();
break;
}
if (!proxiable(v2)) {
return v2;
}
_value = v2;
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 root2 = node.root;
let observed = node.observed;
let keys = Reflect.ownKeys(extra);
let o = root2.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(derived2) {
return { derived: derived2 };
}
function makeCarve(root) {
return (fn) => {
let p = {
contents: {}
};
let ctx2 = makeReactive(makeDerived(p));
let value = fn(ctx2);
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 notify2 = () => {
let observer_observing = [];
let observer = {
root,
notify: notify2,
observing: observer_observing
};
root.observer = observer;
let o = observer;
try {
callback();
return _ready(o, true);
} catch (_e) {
setError(root, _e);
return _clear(o);
}
};
notify2();
};
}
function makeWatch(root, observe_) {
return (callback, effect) => {
let notify2 = () => {
let o2 = observe_(notify2);
let v = callback();
o2.root.observer = void 0;
if (root.lock) {
effect(v);
} else {
root.lock = true;
effect(v);
root.lock = false;
}
_ready(o2, false);
};
let o = observe_(notify2);
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);
}
};
}
var 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(tilia2) {
return (value, source2) => {
let d = dynamic(source2);
let s;
s = d == null ? {
source: source2,
value
} : tilia2({ value, source: d });
let v = {
TAG: "Source",
_0: s
};
Reflect.set(v, dynamicKey, true);
return v;
};
}
function makeStore(tilia2) {
return (callback) => {
let d = dynamic(callback);
let s;
s = d == null ? {
store: callback
} : tilia2({ store: d });
let v = {
TAG: "Store",
_0: s
};
Reflect.set(v, dynamicKey, true);
return v;
};
}
function makeDerived$1(tilia2) {
return (fn) => tilia2({
value: computed(() => fn())
});
}
function _done(o) {
o.root.observer = void 0;
}
function connector(tilia2, carve2, observe2, watch2, batch2, signal2, derived2, source2, store2, _observe2) {
return {
tilia: tilia2,
carve: carve2,
observe: observe2,
watch: watch2,
batch: batch2,
// extra
signal: signal2,
derived: derived2,
source: source2,
store: store2,
// internal
_observe: _observe2
};
}
function make(gcOpt) {
let gc = gcOpt !== void 0 ? gcOpt : 50;
let gc$1 = {
active: /* @__PURE__ */ new Set(),
quarantine: /* @__PURE__ */ new Set(),
threshold: gc
};
let root = {
observer: void 0,
expired: /* @__PURE__ */ new Set(),
lock: false,
error: void 0,
id: Math.random(),
gc: gc$1
};
let tilia2 = makeTilia(root);
let _observe2 = (extra) => {
let observer_observing = [];
let observer = {
root,
notify: extra,
observing: observer_observing
};
root.observer = observer;
return observer;
};
return connector(tilia2, makeCarve(root), makeObserve(root), makeWatch(root, _observe2), makeBatch(root), (value) => {
let s = tilia2({
value
});
let set2 = (v) => {
s.value = v;
};
return [
s,
set2
];
}, makeDerived$1(tilia2), makeSource(tilia2), makeStore(tilia2), _observe2);
}
var ctx = Reflect.get(globalThis, ctxKey);
var _ctx;
var exit = 0;
if (ctx == null) {
exit = 1;
} else {
_ctx = ctx;
}
if (exit === 1) {
let ctx$1 = make(void 0);
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 = /* @__PURE__ */ new Map();
let counter = proxify(root, {
changed: 0
}).proxy;
let currentMeta = {
contents: meta
};
let register = (m2, cb2) => {
let cbs = m2.changes;
let cbs$1;
let exit2 = 0;
if (cbs == null) {
exit2 = 1;
} else {
cbs$1 = cbs;
}
if (exit2 === 1) {
let cbs$2 = /* @__PURE__ */ new Set();
m2.changes = cbs$2;
cbs$1 = cbs$2;
}
cbs$1.add(cb2);
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,
remove
};
};
let cb = (key, value) => {
pending.set(key, value);
counter.changed = counter.changed + 1 | 0;
};
let currentCbs = {
contents: register(meta, cb)
};
let reregister = () => {
let m2 = Reflect.get(accessor(), metaKey);
if (m2 == null || m2 === currentMeta.contents) {
return;
} else {
currentCbs.contents.delete(cb);
currentCbs.contents = register(m2, cb);
currentMeta.contents = m2;
return;
}
};
let capture = guard !== void 0 ? () => {
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
};
}
var tilia = _ctx.tilia;
var carve = _ctx.carve;
var observe = _ctx.observe;
var watch = _ctx.watch;
var batch = _ctx.batch;
var signal = _ctx.signal;
var derived = _ctx.derived;
var source = _ctx.source;
var store = _ctx.store;
var _observe = _ctx._observe;
function _meta(p) {
return Reflect.get(p, metaKey);
}
export {
_clear,
_ctx,
_done,
_meta,
_observe,
_ready,
batch,
carve,
changing,
computed,
derived,
lift,
make,
observe,
readonly$1 as readonly,
signal,
source,
store,
tilia,
watch
};
//# sourceMappingURL=index.mjs.map