Flatten nested set to name value pairs, named after the old path

I dislike having code in a string as it disables editor LSP. I am setting up my Firefox config and have found that the convention for its settings is kinda weird:

      settings = {
        # Visuals
        "browser.download.autohideButton" = true;
        "browser.toolbars.bookmarks.visibility" = "tab";
        "browser.urlbar.suggest.bookmark" = false;
        "browser.urlbar.suggest.engines" = false;
        "browser.urlbar.suggest.history" = false;
        "browser.urlbar.suggest.openpage" = false;
        "browser.urlbar.suggest.topsites" = false;
        "browser.urlbar.trimHttps" = true;
        "browser.compactmode.show" = true;
        "browser.uidensity" = 1;
       };

I’d far rather use nix syntax:

        browser = {
          download.autohideButton = true;
          toolbars.bookmarks.visibility = "tab";
          urlbar = {
            trimHttps = true;
            suggest = {
              bookmark = false;
              engines = false;
              history = false;
              openpage = false;
              topsites = false;
            };
          };
          compactmode.show = true;
          uidensity = 1;
        };

I couldn’t find any references online of people trying it, so after many hours of learning nix syntax and lib functions I came up with this:

  flatten =
    set:
    let
      recurse =
        path:
        lib.concatMapAttrs (
          name: value:
          if builtins.isAttrs value then
            recurse (path ++ [ name ]) value
          else
            { ${builtins.concatStringsSep "." (path ++ [ name ])} = value; }
        );
    in
    recurse [ ] set;

This flattens a set into name value pairs with the path transformed into a string as the name.

nix-repl> example = { lvl1 = { lvl2 = true; lvl2uwu = "meow"; lvltwo = { lvl3 = "running out of names";};}; lvlone = "1337";}

nix-repl> flatten example
{
  "lvl1.lvl2" = true;
  "lvl1.lvl2uwu" = "meow";
  "lvl1.lvltwo.lvl3" = "running out of names";
  lvlone = "1337"; # Yes I know this name isnt a string but it works anyway and it took me hours to get this far
}

It is a modified version of mapAttrsRecursiveCond and while it’s okay, I think, I wanted to know if there is a better way of doing it?

The Home Manager documentation states

‹name› only supports int, bool, and string types for preferences, but home-manager will automatically convert all other JSON-compatible values into strings.

but trying to read the official documentation is unironically blank, so I am struggling to understand the link between JSON and Nix. This dot separated path in a string JSON syntax is not something I have come across so I am rather stumped.

And before anyone calls me out on it, yes, I know you can put nix formatted preferences directly into settings, but it turns the user.js into this unreadable mess. Am I being pedantic? Maybe, but I’m a nix user

user_pref("browser", "{\"compactmode\":{\"show\":true},\"download\":{\"autohideButton\":true},\"toolbars\":{\"bookmarks\":{\"visibility\":\"tab\"}},\"uidensity\":1,\"urlbar\":{\"suggest\":{\"bookmark\":false,\"engines\":false,\"history\":false,\"openpage\":false,\"topsites\":false},\"trimHttps\":true}}");