Smart operator looks promising, but I have not tried it so I can't speak for it.
A prebuilt solution would be ideal, but if none or sufficient, it would be very easy to wrap write this functionality and wrap it in a minor mode though.

Here's my go:

<!-- language: lang-lisp -->

 (defvar auto-punc-punctuation-members
 (string-to-list ".,:;?!")
 "List of charactesr to insert spaces after")
 
 (defvar auto-punc-ignore-members
 (string-to-list " \t"))
 
 (defun auto-punc-maybe-do ()
 "If the last entered character is not in `auto-punc-punctuation-members' or `auto-punc-ignore-members',
 and the prior character is in `auto-punc-punctuation-members',
 insert a space between the two characters. "
 (when (and (not (member (char-before) (append auto-punc-punctuation-members auto-punc-ignore-members)))
 (member (char-before (1- (point))) auto-punc-punctuation-members))
 (backward-char 1)
 (insert " ")
 (forward-char 1)))
 
 (define-minor-mode auto-space-punctuation-mode
 "Automatically inserts spaces between some punctuation and other characters."
 :init-value nil
 :lighter "._a"
 :keymap nil
 (if auto-space-punctuation-mode
 (add-hook 'post-self-insert-hook 'auto-punc-maybe-do)
 (remove-hook 'post-self-insert-hook 'auto-punc-maybe-do)))

You could simply add it to your init and auto enable it when you want like

 (add-hook 'text-mode-hook 'auto-space-punctuation-mode)