UNPKG

tilia

Version:

🍃 State management library, domain-driven.

970 lines (908 loc) 21.5 kB
// 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 */