After a value gets garbage collected but before the FinalizationRegistry callback has fired the table entry continues to exist according to has(), size, and iteration, but with undefined value. This is unlikely to be what any user wants, I would expect map entries to simply get deleted when values get garbage-collected.
It's easy enough to make the has() method and iteration methods deref the WeakRef to verify the entry is still valid and pretend it doesn't exist otherwise. It's a bit annoying though that a correct implementation for size would require iterating the entire map, although it would also be a rather ugly wart if it sometimes returns an erroneously high value. Perhaps if there's a use-case for a fast size estimate (upper bound) a separate accessor could be added for it.
Test case using node --expose-gc:
import { WeakValueMap } from 'weakref';
import assert from 'node:assert';
function next_job() {
return new Promise( resolve => setImmediate( resolve ) );
}
let map = new WeakValueMap;
map.set( 42, {} );
await next_job(); // release strong ref on new/dereferenced weakrefs
gc(); // garbage-collect the now-unreferenced value
assert.strictEqual( map.get( 42 ), undefined ); // passes
assert( ! map.has( 42 ) ); // FAILS
assert.strictEqual( map.size, 0 ); // FAILS