function dumpValue(context, add, what) {
  let res;
  const { id, async } = context;
  const values = storages[id];
  const keyHooks = changeHooks[id];
  for (const key of add ? objectKeys(what) : what) {
    let val, raw, oldRaw, tmp;
    if (add) {
      val = what[key];
      raw = dumpScriptValue(val, jsonDump) || null;
    } else raw = null; // val is `undefined` to match GM_addValueChangeListener docs
    oldRaw = values[key];
    if (add) values[key] = raw;
    else delete values[key];
    if (raw !== oldRaw) {
      (res || (res = uploadBuf[id] || (uploadBuf[id] = createNullObj())))[key] = raw;
      if ((tmp = keyHooks?.[key])) notifyChange(tmp, key, val, raw, oldRaw);
    }
  }
  if (res) {
    res = uploadThrottle || (uploadThrottle = promiseResolve()::then(upload));
    if (async) uploadAsync = true;
  }
  if (async) {
    return res || promiseResolve();
  }
}
@param {GMContext} context
@param {boolean} add
@param {string[]|Object} what
@return {void|Promise<void>}