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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* call this, so neither can answer what the other refuses. */
|
||||
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") {
|
||||
const fn = Reflect.get(target, prop, target);
|
||||
if (typeof fn !== "function") return fn;
|
||||
return (...args: unknown[]): unknown => {
|
||||
const out = (fn as (...a: unknown[]) => unknown).apply(target, args);
|
||||
if (out === target) return viewOf(prop); // chaining lands on the view
|
||||
if (cannotReachAnItem(out)) return out;
|
||||
throw refusalOf(prop, "the collection view");
|
||||
};
|
||||
// `add` chains, so its result is the collection and must become the view.
|
||||
return (...args: unknown[]): unknown =>
|
||||
handBack((fn as (...a: unknown[]) => unknown).apply(target, args), target, prop);
|
||||
}
|
||||
if (prop === "delete") {
|
||||
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
|
||||
// two disagreed: `has(item)` said no while `delete(item)` removed it.
|
||||
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") {
|
||||
const del = Reflect.get(target, "delete", target);
|
||||
|
||||
Reference in New Issue
Block a user