It's easy! Just do manual box mapping and manual normal map calculation and then mix the normals by blend factor!
That should be perfectly clear, right? Right?
So our basic plan here is to lookup our normal map from generated coordinates, depending on the direction of the particular sample's normal, then create three modified normal vectors from those lookups, and then to combine those with weights, on the basis of how much our base normal agrees with our 3 basis object vectors and on an arbitrary, 0-1 blend factor.
Let's go through our node groups. We'll start with the idea of a triplanar projection, in our "triplanar lookup" node group:
Here, I'm doing three lookups on a texture, using generated coordinates. Each translates a particular pair of our XYZ coordinates into two UV coordinates. We'll manage these three lookups separately, by moving on to manual normal mapping, in our "NormalMapNode" node group:
This node group is sent a world-space normal and an arbitrary, object space tangent. We repair the tangent to be orthogonal to our normal with a pair of cross products and generate a binormal vector in our "repair tangent" node group:
Returning to our NormalMapNode group, each of our lookups is going to be associated with a different object space tangent. We will remap the normal map color from the 0,1 space to the -1,1 space, then we'll scale our normal, tangent, and binormal vectors by the amounts given to us by our normal map and add those vectors back up to generate a normal in an arbitrary, object space tangent.
We now have three normals for every sample. We need to know how much of each to use, so lets look at the normDotObj node group:
We start by checking our object space normals against our three object space basis vectors. To get the same value regardless of which direction the normal is pointing-- eg, to get the same value on +x and -x faces-- we take the absolute value of the dot product.
We'll then use those in our "weightTriplanar" node group to mix with our arbitrary "blend" value. We want this value to emulate Blender's, so we'll use a 0-1 range, although 0 (and other very low numbers) will be buggy, because division by zero. (For more work, we could check for very low numbers and have a different path, but I don't think it's necessary, not compared to just living with very tiny amounts of blending.)
We'll move on to our weightedMix3 node group:
Our weights aren't normalized, so we'll start by normalizing them, so that everything adds up. To mix three, we're basically multiplying each by its normalized weight and adding them up.
It's okay that we're using color nodes to deal with vectors. Blender doesn't know the difference. We can vector math/scale colors, we can mixRGB vectors, it's fine.
At the end, to deal with imprecisions, we do a final normalization operation on our normal and send it into a shader.
There is something to take note of here, which is this is not exactly how Blender computes normals from normal maps. This treats normals maps as existing in an orthonormal tangent space, whereas Blender does not. (I know, that's total Greek, but you don't have to understand if you don't want to.) However, it's better than how Blender computes normals, and it's how almost everybody else computes normals. If your plan is to normal map from popular texture packs, this normal mapping technique is appropriate, but you will notice differences between this and Blender's default.
It is possible to bake orthonormal normal maps in Blender, despite Blender's defaults, but I don't think it's worth getting into that for this question, where you are most likely interested in using normal maps that were baked as orthonormal to begin with.
Addendum based on comments: Well, I said it was a first draft :) You're absolutely right that it doesn't handle the negative faces correctly. That's because the binormal is going the wrong direction for those faces.
Let's fix that by first editing our normDotObj node group to output facing with regards to our basis vecs:
We're checking whether these are positive or negative faces by getting Greater Than 0 before we absolute value the dot product, then we're remapping that to a -1, 1 value: -1 if the values are opposite, 1 if the values are the same direction.
We can then edit our normalMapNode to have an option to flip the binormal from a new Scale input:
We can plug the facing into the scale like so:
After that, our binormals are pointing the right direction on our normal map node.
As for editing three different image texture nodes being cumbersome, I feel that this is the least cumbersome issue here. This is a complicated structure; we're not really supposed to use normal maps like this. (We're not really supposed to use normal maps independent of meshes to begin with, but this is the world we've found ourselves in. Maybe in some parallel world, texture artists are distributing packs with 16bpc .png bump maps rather than normal maps, and it's my own bad karma that landed me in this world.) However, you can link changes to a texture node by using linked node groups:
Here, I've just put the image texture node into a node group, then duplicated that node group and used it in lieu of the image texture node. I've also put an unconnected copy into my base node tree. Now, to change the texture image used, I only have to edit it once, in any instance of those linked groups. Because they're linked, editing the group affects all instances of that group, changing it everywhere.
Edit 2: Again, you're correct in comments that the +Y face is wrong (backwards tangent.) Actually, both Y faces are wrong. I was designing things around the +Y face, but the +Y face is actually pointing in -Y, so we're flipping that tangent instead of the face opposite it.
We can fix this easily just by reversing the sign of our tangent for the +-Y lookups. Instead of sending that middle normal map node a 1,0,0 tangent, we can send it a -1,0,0 tangent:
In our viewport, we can see a box-blended Suzanne and bevelled, subdivided cube, and a basic cube on the right (well, UVs rotated/scale to provide appropriate tangents) getting the normal map via Blender's basic normal mapping. I've set the view to display material normals.
Images aren't packed, so you'll have to provide your own normal map.