wardens
Version:
A framework for resource management
155 lines (154 loc) • 4.57 kB
JavaScript
var O = Object.defineProperty;
var R = (r, e, t) => e in r ? O(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
var f = (r, e, t) => (R(r, typeof e != "symbol" ? e + "" : e, t), t), S = (r, e, t) => {
if (!e.has(r))
throw TypeError("Cannot " + t);
};
var n = (r, e, t) => (S(r, e, "read from private field"), t ? t.call(r) : e.get(r)), l = (r, e, t) => {
if (e.has(r))
throw TypeError("Cannot add the same private member more than once");
e instanceof WeakSet ? e.add(r) : e.set(r, t);
}, h = (r, e, t, s) => (S(r, e, "write to private field"), s ? s.call(r, t) : e.set(r, t), t);
function T(r) {
const e = /* @__PURE__ */ new WeakMap();
return new Proxy(r, {
get(t, s) {
const o = Reflect.get(t, s, t);
if (typeof o == "function") {
if (e.has(o) === !1) {
const a = o.bind(t);
Object.defineProperties(
a,
Object.getOwnPropertyDescriptors(o)
), e.set(o, a);
}
return e.get(o);
}
return o;
},
set(t, s, o) {
return Reflect.set(t, s, o, t);
}
});
}
const C = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakSet();
function B(r) {
return Proxy.revocable(r, {});
}
var g, w;
class x {
constructor(e) {
l(this, g, Symbol("Context ID"));
l(this, w, void 0);
h(this, w, e);
}
static getId(e) {
return n(e, g);
}
static getDefaultValue(e) {
var t;
return n(t = e, w).call(t);
}
}
g = new WeakMap(), w = new WeakMap();
const U = (r) => new x(r);
var y, u, p, i;
class A {
constructor(e, t, s) {
l(this, y, /* @__PURE__ */ new WeakSet());
l(this, u, void 0);
l(this, p, void 0);
l(this, i, void 0);
/** Provision an owned resource and make sure it doesn't outlive us. */
f(this, "create", async (e, ...t) => {
if (n(this, p).enforced)
throw new Error("Cannot create new resources after teardown.");
const s = Object.create(n(this, i)), o = await k(s, e, ...t);
return n(this, u).add(o), o;
});
/**
* Tear down a resource. Happens automatically when resource owners are
* deallocated.
*/
f(this, "destroy", async (e) => {
if (n(this, y).has(e))
throw new Error("Resource already destroyed.");
if (!n(this, u).has(e))
throw new Error("You do not own this resource.");
n(this, u).delete(e), n(this, y).add(e), await D(e);
});
/** Store a value in context. Anything down the chain can read it. */
f(this, "setContext", (e, t) => {
n(this, i)[x.getId(e)] = t;
});
/** Retrieve a value from context, or a default if it is unset. */
f(this, "getContext", (e) => {
const t = x.getId(e);
return t in n(this, i) ? n(this, i)[t] : x.getDefaultValue(e);
});
h(this, i, e), h(this, u, t), h(this, p, s);
}
}
y = new WeakMap(), u = new WeakMap(), p = new WeakMap(), i = new WeakMap();
const k = async (r, e, ...t) => {
const s = { enforced: !1 }, o = /* @__PURE__ */ new Set(), a = new A(r, o, s);
let c;
try {
c = await e(a, ...t);
} catch (b) {
const I = Array.from(o).reverse(), v = (await Promise.allSettled(
I.map((d) => a.destroy(d))
)).filter(
(d) => d.status === "rejected"
);
throw v.length ? E(
v.map((d) => d.reason),
{ cause: b }
) : b;
}
const P = c.value, { proxy: m, revoke: W } = B(P);
return j.add(m), C.set(m, {
curfew: s,
resource: c,
children: o,
revoke: W
}), m;
}, D = async (r) => {
if (!j.has(r))
throw new Error("Cannot destroy object. It is not a resource.");
const e = C.get(r);
if (e) {
C.delete(r), e.revoke();
let t = {
status: "fulfilled",
value: void 0
};
if (e.resource.destroy)
try {
await e.resource.destroy();
} catch (c) {
t = { status: "rejected", reason: c };
}
e.curfew.enforced = !0;
const s = Array.from(e.children).reverse().map(D), o = await Promise.allSettled(s), a = [t].concat(o).filter(
(c) => c.status === "rejected"
);
if (a.length)
throw E(a.map((c) => c.reason));
}
}, E = (r, e) => r.length === 1 ? r[0] : new M(r, e);
class M extends Error {
constructor(e, t) {
super(
"Some resources could not be destroyed. See the `failures` property for details.",
t
), this.failures = e;
}
}
const Y = async (r, ...e) => k(/* @__PURE__ */ Object.create(null), r, ...e);
export {
T as bindContext,
Y as create,
U as createContext,
D as destroy
};