refactor: une seule fonction decide ce qu-une mutation peut rendre
Trois copies d-un raisonnement sur une valeur de retour, c-est trois occasions de se tromper et c-est ce qui a produit le defaut. `handBack` porte la regle une fois : la collection devient la vue, un scalaire passe, le reste refuse.
This commit is contained in:
@@ -243,6 +243,19 @@ export function makeReadFilteredView<S extends object>(set: S, caps: CapRegistry
|
|||||||
return self.view;
|
return self.view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What a MUTATION may hand back - the same question this file asks of any other member,
|
||||||
|
* asked of a RESULT rather than of a property. Written once and called from each of
|
||||||
|
* them, because the defect it answers was three copies of a reasoned-about return value:
|
||||||
|
* the collection becomes the view, a scalar passes, and anything else refuses rather
|
||||||
|
* than being guessed at.
|
||||||
|
*/
|
||||||
|
const handBack = (out: unknown, target: object, prop: string | symbol): unknown => {
|
||||||
|
if (out === target) return viewOf(prop);
|
||||||
|
if (cannotReachAnItem(out)) return out;
|
||||||
|
throw refusalOf(prop, "the collection view");
|
||||||
|
};
|
||||||
|
|
||||||
/** What the view answers for one member. The `get` trap and the descriptor trap BOTH
|
/** What the view answers for one member. The `get` trap and the descriptor trap BOTH
|
||||||
* call this, so neither can answer what the other refuses. */
|
* call this, so neither can answer what the other refuses. */
|
||||||
const read = (target: object, prop: string | symbol): unknown => {
|
const read = (target: object, prop: string | symbol): unknown => {
|
||||||
@@ -334,12 +347,9 @@ export function makeReadFilteredView<S extends object>(set: S, caps: CapRegistry
|
|||||||
if (prop === "add") {
|
if (prop === "add") {
|
||||||
const fn = Reflect.get(target, prop, target);
|
const fn = Reflect.get(target, prop, target);
|
||||||
if (typeof fn !== "function") return fn;
|
if (typeof fn !== "function") return fn;
|
||||||
return (...args: unknown[]): unknown => {
|
// `add` chains, so its result is the collection and must become the view.
|
||||||
const out = (fn as (...a: unknown[]) => unknown).apply(target, args);
|
return (...args: unknown[]): unknown =>
|
||||||
if (out === target) return viewOf(prop); // chaining lands on the view
|
handBack((fn as (...a: unknown[]) => unknown).apply(target, args), target, prop);
|
||||||
if (cannotReachAnItem(out)) return out;
|
|
||||||
throw refusalOf(prop, "the collection view");
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (prop === "delete") {
|
if (prop === "delete") {
|
||||||
const fn = Reflect.get(target, prop, target);
|
const fn = Reflect.get(target, prop, target);
|
||||||
@@ -347,7 +357,7 @@ export function makeReadFilteredView<S extends object>(set: S, caps: CapRegistry
|
|||||||
// The same question `has` already refuses to answer, with a worse consequence. The
|
// The same question `has` already refuses to answer, with a worse consequence. The
|
||||||
// two disagreed: `has(item)` said no while `delete(item)` removed it.
|
// two disagreed: `has(item)` said no while `delete(item)` removed it.
|
||||||
return (item: unknown): unknown =>
|
return (item: unknown): unknown =>
|
||||||
keep(item) ? (fn as (i: unknown) => unknown).call(target, item) : false;
|
keep(item) ? handBack((fn as (i: unknown) => unknown).call(target, item), target, prop) : false;
|
||||||
}
|
}
|
||||||
if (prop === "clear") {
|
if (prop === "clear") {
|
||||||
const del = Reflect.get(target, "delete", target);
|
const del = Reflect.get(target, "delete", target);
|
||||||
|
|||||||
Reference in New Issue
Block a user