async function parseScript(src) {
  const { meta, errors } = src.meta ? src : parseMetaWithErrors(src);
  if (!meta.name) throw `${i18n('msgInvalidScript')}\n${i18n('labelNoName')}`;
  const update = {
    message: src.message == null ? i18n('msgUpdated') : src.message || '',
  };
  const result = { errors, update };
  const { [S_CODE]: code, update: srcUpdate } = src;
  const now = Date.now();
  let { id } = src;
  let script;
  let oldScript = getScript({ id, meta });
  if (oldScript) {
    script = oldScript;
    id = script.props.id;
  } else {
    ({ script } = newScript());
    maxScriptId++;
    id = script.props.id = maxScriptId;
    result.isNew = true;
    update.message = i18n('msgInstalled');
    aliveScripts.push(script);
  }
  const { config, custom, props } = script;
  const uri = getNameURI({ meta, props: {id} });
  if (oldScript) {
    // Do not allow script with same name and namespace
    if (src.isNew || id && aliveScripts.some(({ props: p }) => uri === p.uri && id !== p.id)) {
      throw i18n('msgNamespaceConflict');
    }
    delete script[INFERRED];
  }
  props.lastModified = now;
  props.uuid = props.uuid || getUUID();
  // Overwriting inner data by `src`, deleting keys for which `src` specifies `null`
  for (const key of ['config', 'custom', 'props']) {
    const dst = script[key];
    src[key]::forEachEntry(([srcKey, srcVal]) => {
      if (srcVal == null) delete dst[srcKey];
      else dst[srcKey] = srcVal;
    });
  }
  const pos = +src.position;
  if (pos) {
    props.position = pos;
    maxScriptPosition = Math.max(maxScriptPosition, pos);
  } else if (!oldScript) {
    maxScriptPosition++;
    props.position = maxScriptPosition;
  }
  config.enabled = getInt(config.enabled);
  config.removed = 0; // force-resetting `removed` since this is an installation
  config.shouldUpdate = getInt(config.shouldUpdate);
  script.meta = meta;
  props.uri = getNameURI(script); // DANGER! Must be after props.position and meta assignments.
  delete custom.from; // remove the old installation URL if any
  if (!getScriptHome(script) && isRemote(src.from)) {
    custom.from = src.from; // to avoid overriding script's `meta` for homepage in a future version
  }
  // Allowing any http url including localhost as the user may keep multiple scripts there
  if (isValidHttpUrl(src.url)) custom.lastInstallURL = src.url;
  custom.tags = custom.tags?.split(/\s+/).map(normalizeTag).filter(Boolean).join(' ').toLowerCase();
  if (!srcUpdate) storage.mod.remove(getScriptUpdateUrl(script, { all: true }) || []);
  buildPathMap(script, src.url);
  const depsPromise = fetchResources(script, src);
  // DANGER! Awaiting here when all props are set to avoid modifications made by a "concurrent" call
  const codeChanged = !oldScript || code !== await storage[S_CODE].getOne(id);
  if (codeChanged && src.bumpDate) props.lastUpdated = now;
  // Installer has all the deps, so we'll put them in storage first
  if (src.cache) await depsPromise;
  await storage.base.set({
    [S_SCRIPT_PRE + id]: script,
    ...codeChanged && { [S_CODE_PRE + id]: code },
  });
  Object.assign(update, script, srcUpdate);
  result.where = { id };
  result[S_CODE] = src[S_CODE];
  sendCmd('UpdateScript', result);
  pluginEvents.emit('scriptChanged', result);
  if (src.reloadTab) reloadTabForScript(script);
  return result;
}
@param {VMScriptSourceOptions} src
@return {Promise<{
  errors: string[],
  isNew: boolean,
  update: VMScript & { message: string },
  where: { id: number },
}>}