@qtpy/state-management-observable
Version:
1. [Основная идея и архитектура](#основная-идея-и-архитектура) 1.1. [Прозрачная реактивность](#прозрачная-реактивность) 1.2. [Поддержка типизированных строковых ключей](#поддержка-типизированных-строковых-ключей) 1.3. [Подписки с «гранул
383 lines (382 loc) • 12.4 kB
JavaScript
var J = Object.defineProperty;
var X = (P, s, a) => s in P ? J(P, s, { enumerable: !0, configurable: !0, writable: !0, value: a }) : P[s] = a;
var $ = (P, s, a) => X(P, typeof s != "symbol" ? s + "" : s, a);
import { getStringPath as Y, validatePath as M, detRandomId as Z, withMetaSupport as q, calculateSnapshotHash as z, setMetaData as N, normalizeCacheKey as B, isArrayMethod as I, splitPath as W, getMetaData as O, shallowEqual as F } from "./utils.mjs";
class ee {
constructor(s = [], a) {
$(this, "undoStack", /* @__PURE__ */ new Map());
$(this, "redoStack", /* @__PURE__ */ new Map());
$(this, "perPathMaxLength");
this.perPathMaxLength = /* @__PURE__ */ new Map();
for (const [i, d] of s) {
const g = a(i);
this.perPathMaxLength.set(g, d);
}
}
getMaxLength(s) {
return this.perPathMaxLength.get(s) ?? 0;
}
push(s, a) {
if (!this.perPathMaxLength.get(s)) return;
const i = this.undoStack.get(s) ?? [];
i.push(a);
const d = this.getMaxLength(s);
i.length > d && i.shift(), this.undoStack.set(s, i), this.redoStack.set(s, []);
}
getUndo(s, a) {
const i = this.undoStack.get(s) ?? [];
if (!(a < 0 || a >= i.length))
return i[i.length - 1 - a];
}
getRedo(s, a) {
const i = this.redoStack.get(s) ?? [];
if (!(a < 0 || a >= i.length))
return i[i.length - 1 - a];
}
getHistory(s) {
const a = this.undoStack.get(s) ?? [], i = this.redoStack.get(s) ?? [];
return {
undo: [...a],
redo: [...i]
};
}
spliceStack(s, a, i) {
if (i) {
const [d, g] = i;
if (d < 0 || d >= s.length || g <= 0)
return;
const l = s.splice(d, g);
return a.push(...l), l[l.length - 1];
}
}
undo(s, a) {
const i = this.undoStack.get(s) ?? [], d = this.redoStack.get(s) ?? [];
if (i.length <= 1 && !a) return;
if (a) {
const l = this.spliceStack(i, d, a);
return this.undoStack.set(s, i), this.redoStack.set(s, d), l ?? (i.length > 0 ? i[i.length - 1] : void 0);
}
const g = i.pop();
return d.push(g), this.undoStack.set(s, i), this.redoStack.set(s, d), i[i.length - 1];
}
redo(s, a) {
const i = this.undoStack.get(s) ?? [], d = this.redoStack.get(s) ?? [];
if (d.length === 0 && !a) return;
if (a) {
const l = this.spliceStack(d, i, a);
return this.undoStack.set(s, i), this.redoStack.set(s, d), l;
}
const g = d.pop();
return i.push(g), this.undoStack.set(s, i), this.redoStack.set(s, d), g;
}
getCurrent(s) {
const a = this.undoStack.get(s) ?? [];
return a[a.length - 1];
}
clear(s, a = "all", i) {
const d = this.undoStack.get(s) ?? [], g = this.redoStack.get(s) ?? [];
if (i) {
const [l, k] = i;
if (l < 0 || l >= (a === "redo" ? g : d).length || k <= 0)
return !1;
if (a === "redo")
return g.splice(l, k), this.redoStack.set(s, g), !0;
if (a === "undo")
return d.splice(l, k), this.undoStack.set(s, d), !0;
if (a === "all") {
const x = d.length > 0 && l >= 0 && l < d.length && k > 0, p = g.length > 0 && l >= 0 && l < g.length && k > 0;
return x && d.splice(l, k), p && g.splice(l, k), this.undoStack.set(s, d), this.redoStack.set(s, g), x || p;
}
}
switch (a) {
case "redo":
return this.redoStack.delete(s);
case "undo":
return this.undoStack.delete(s);
default:
const l = this.undoStack.delete(s), k = this.redoStack.delete(s);
return l || k;
}
}
pruneUnused(s) {
for (const a of [...this.undoStack.keys()])
s.has(a) || this.clear(a);
}
getEntries() {
return Array.from(this.undoStack.entries()).map(([s, a]) => {
const i = this.redoStack.get(s) ?? [];
return {
path: s,
current: a[a.length - 1],
undoLength: a.length,
redoLength: i.length,
undo: a,
redo: i,
maxLength: this.getMaxLength(s)
};
});
}
}
function re(P, s = [], a = {}) {
let i = { ...P }, d = !1, g = "user", l = null;
const k = /* @__PURE__ */ new WeakMap(), x = [], p = /* @__PURE__ */ new Set(), b = /* @__PURE__ */ new Map(), w = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), E = /* @__PURE__ */ new Map(), C = () => x[x.length - 1], U = (e) => W(e).reduce((n, r) => {
if (n != null)
return n[r];
}, i), D = (e, t) => {
const n = W(e), r = n.pop(), c = n.reduce((u, h) => {
if (u == null) throw new Error(`Невозможно установить по пути "${e}" — промежуточное значение undefined`);
return u[h];
}, i);
c[r] = t;
};
let H = null;
function K(e, t = "") {
return typeof e != "object" || e === null ? e : new Proxy(e, {
get(r, c, u) {
const h = typeof c == "string" ? c : String(c), f = t ? `${t}.${h}` : h;
l == null || l.trackedPaths.add(f);
const y = Reflect.get(r, c, u);
return Array.isArray(r) && typeof y == "function" && I(h) ? (...R) => {
H = { name: h };
let m;
return g = "proxy", o.batch(() => {
m = y.apply(u, R);
}), d && t ? _.add(t) : t && o.invalidate(t), H = null, m;
} : y === r ? u : y !== null && typeof y == "object" ? K(y, f) : y;
},
set(r, c, u, h) {
const f = typeof c == "string" ? c : String(c), y = t ? `${t}.${f}` : f, R = Reflect.get(r, c, h);
return Object.is(R, u) || (d ? C().set(y, u) : (o.update(y, u), !H && t && o.invalidate(t))), !0;
},
deleteProperty(r, c) {
const u = typeof c == "string" ? c : String(c), h = t ? `${t}.${u}` : u, f = Reflect.deleteProperty(r, c);
return f && o.update(h, void 0), f;
},
ownKeys(r) {
if (l) {
const c = t ? `${t}.` : "";
for (const u of Reflect.ownKeys(r))
l.trackedPaths.add(`${c}${String(u)}`);
}
return Reflect.ownKeys(r);
}
});
}
function V(e, t, n) {
let r = !1;
const c = q(t, () => {
const u = O(n, t), h = u == null ? void 0 : u._prevSignature, f = z(t);
return h && f && h === f ? (r = !0, !0) : (f && N(n, t, { _prevSignature: f }), !0);
});
return {
bool: r || !c && F(e, t),
isSupportedMetaData: c
};
}
const o = {};
let L = K(i);
Object.defineProperty(o, "$", {
get: () => L,
enumerable: !0,
configurable: !0
});
const S = (e) => Y(o == null ? void 0 : o.$, e), v = new ee((a == null ? void 0 : a.customLimitsHistory) ?? [], S);
function A(e) {
p.forEach((n) => {
const r = n.__meta;
if (d ? r.cacheKeys && r.cacheKeys.has(e) : !r.cacheKeys || r.cacheKeys.has(e)) {
l = r;
try {
n(L);
} finally {
l = null;
}
}
});
const t = b.get(e);
if (t) {
const n = U(e);
t.forEach((r) => r(n));
}
}
o.clearHistoryPath = (e, t = "all", n) => {
const r = S(e);
v.clear(r, t, n);
}, o.clearAllHistory = () => {
const e = new Set(b.keys());
v.pruneUnused(e);
};
const Q = (e, t, n = !1, r = !1) => {
const c = U(e), u = V(c, t, k);
u.bool || (n || (u.isSupportedMetaData && t !== c && v.push(e, t), u.isSupportedMetaData || v.push(e, t)), D(e, t), !r && A(e));
};
o.update = (e, t, n) => {
M(e);
const r = S(e);
let c = o.resolveValue(e, t);
d && !(n != null && n.keepQuiet) ? C().set(r, c) : Q(r, c, !1, n == null ? void 0 : n.keepQuiet);
}, o.update.quiet = (e, t) => {
o.update(e, t, { keepQuiet: !0 });
}, o.debounced = (e, t) => {
const n = Z();
let r = null;
const c = (...u) => {
r && clearTimeout(r), r = setTimeout(() => {
e(...u), E.delete(n);
}, t), E.set(n, r);
};
return c.cancel = () => {
r && (clearTimeout(r), E.delete(n));
}, c;
};
function G(e) {
const t = [];
for (const [n, r] of e) {
const c = U(n);
V(c, r, k).bool || (g === "user" && v.push(n, c), D(n, r), A(n), t.push(n));
}
t.length && p.forEach((n) => {
n.__meta.cacheKeys || n(L);
});
}
o.resolveValue = (e, t) => {
const n = S(e), r = U(n), c = typeof t == "function" ? t(r) : t;
return q(c, () => {
const u = z(r);
u || N(k, c, {
_prevSignature: u
});
}), c;
}, o.asyncUpdate = async (e, t, n) => {
const { abortPrevious: r = !1, keepQuiet: c = !1 } = n ?? {};
M(e);
const u = S(e);
if (r) {
const f = w.get(u);
f && (f.abort(), w.delete(u));
}
const h = new AbortController();
h.signal.addEventListener(
"abort",
() => {
w.delete(u);
},
{ once: !0 }
), w.set(u, h);
try {
const f = U(u), y = await t(f, h.signal);
h.signal.aborted || o.update(u, y, { keepQuiet: c });
} catch (f) {
f.name !== "AbortError" && console.error(f);
} finally {
w.delete(u);
}
}, o.asyncUpdate.quiet = async (e, t, n = {}) => o.asyncUpdate(e, t, {
abortPrevious: n.abortPrevious ?? !1,
keepQuiet: !0
}), o.batch = (e) => {
x.push(/* @__PURE__ */ new Map()), d = !0;
try {
e();
} finally {
const t = x.pop();
if (x.length > 0) {
const n = C();
for (const [r, c] of t)
n.set(r, c);
} else {
G(t), d = !1, g = "user";
for (const n of _)
o.invalidate(n);
_.clear();
}
}
return Promise.resolve();
}, o.getUndo = (e, t) => {
M(e);
const n = S(e);
return v.getUndo(n, t);
}, o.getRedo = (e, t) => {
M(e);
const n = S(e);
return v.getRedo(n, t);
}, o.getHistory = (e) => {
M(e);
const t = S(e);
return v.getHistory(t);
}, o.undo = (e, t) => {
M(e);
const n = S(e), r = v.undo(n, t);
return r !== void 0 ? (Q(n, r, !0), o.update(n, r, { skipHistory: !0, keepQuiet: !0 }), !0) : (console.warn(`No undo history for path: ${n}`), !1);
}, o.redo = (e, t) => {
M(e);
const n = S(e), r = v.redo(n, t);
return r !== void 0 ? (Q(n, r, !0), o.update(n, r, { skipHistory: !0, keepQuiet: !0 }), !0) : (console.warn(`No redo history for path: ${n}`), !1);
}, o.subscribe = (e, t) => {
const n = t ? new Set(t.map((u) => B(u, o))) : void 0, r = {
active: !0,
trackedPaths: /* @__PURE__ */ new Set(),
cacheKeys: n
}, c = (u) => {
r.active && e(u);
};
return c.__meta = r, p.add(c), () => {
r.active = !1, p.delete(c);
};
}, o.subscribeToPath = (e, t, n = {}) => {
M(e);
const { immediate: r = !1, cacheKeys: c } = n, u = S(e), h = c ? c.map((m) => B(m, o)) : [], f = [u, ...h], y = (m) => t(m), R = {
active: !0,
trackedPaths: new Set(f),
cacheKeys: h.length > 0 ? new Set(h) : void 0
};
return y.__meta = R, f.forEach((m) => {
b.has(m) || b.set(m, /* @__PURE__ */ new Set()), b.get(m).add(y);
}), r && y(U(u)), () => {
f.forEach((m) => {
var j;
(j = b.get(m)) == null || j.delete(y);
});
};
}, o.invalidateAll = () => {
p.forEach((e) => {
l = e.__meta;
try {
e(L);
} finally {
l = null;
}
}), b.forEach((e, t) => {
const n = U(t);
e.forEach((r) => r(n));
});
}, o.invalidate = (e) => {
const t = S(e);
A(t);
}, o.cancelAsyncUpdates = (e) => {
if (e) {
const t = S(e), n = w.get(t);
n && (n.abort(), w.delete(t));
} else
w.forEach((t, n) => {
t.abort(), w.delete(n);
});
}, o.clearStore = () => {
p.clear(), b.clear(), w.clear(), E.forEach((e) => clearTimeout(e)), E.clear(), o.clearAllHistory();
}, o.get = (e) => (M(e), U(S(e))), o.resolvePath = S, o.getRawStore = () => i, o.setRawStore = async (e, t) => {
o.cancelAsyncUpdates(), o.clearAllHistory(), E.forEach((n) => clearTimeout(n)), E.clear(), i = { ...e }, L = K(i), t != null && t.keepQuiet || o.invalidateAll();
}, o.getMemoryStats = () => ({
subscribersCount: p.size,
pathSubscribersCount: b.size,
historyEntries: v.getEntries(),
activePathsCount: b.size,
debounceTimersCount: E.size
});
let T = o.update;
return s.reverse().forEach((e) => {
T = e(o, T);
}), o.update = T, o;
}
export {
re as createObservableStore
};