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