fix(i18n): copy missing-key snapshots safely
All checks were successful
Security Scan / security (push) Successful in 18s
Test / test (push) Successful in 2m31s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 13:13:49 +00:00
parent 82164940d7
commit 6e202e8230
3 changed files with 37 additions and 3 deletions

View file

@ -126,7 +126,7 @@ func (s *CoreService) handleMissingKey(mk MissingKey) {
}
s.missingKeysMu.Lock()
defer s.missingKeysMu.Unlock()
s.missingKeys = append(s.missingKeys, mk)
s.missingKeys = append(s.missingKeys, cloneMissingKey(mk))
}
// MissingKeys returns all missing keys collected in collect mode.
@ -137,7 +137,9 @@ func (s *CoreService) MissingKeys() []MissingKey {
s.missingKeysMu.Lock()
defer s.missingKeysMu.Unlock()
result := make([]MissingKey, len(s.missingKeys))
copy(result, s.missingKeys)
for i, mk := range s.missingKeys {
result[i] = cloneMissingKey(mk)
}
return result
}

View file

@ -49,3 +49,22 @@ func TestCoreServiceNilSafe(t *testing.T) {
require.ErrorIs(t, svc.AddLoader(nil), ErrServiceNotInitialised)
require.ErrorIs(t, svc.LoadFS(nil, "locales"), ErrServiceNotInitialised)
}
func TestCoreServiceMissingKeysReturnsCopies(t *testing.T) {
svc, err := New()
require.NoError(t, err)
coreSvc := &CoreService{svc: svc}
coreSvc.SetMode(ModeCollect)
_ = svc.T("missing.copy.key", map[string]any{"foo": "bar"})
missing := coreSvc.MissingKeys()
require.Len(t, missing, 1)
require.Equal(t, "bar", missing[0].Args["foo"])
missing[0].Args["foo"] = "mutated"
again := coreSvc.MissingKeys()
require.Len(t, again, 1)
assert.Equal(t, "bar", again[0].Args["foo"])
}

View file

@ -201,7 +201,7 @@ func dispatchMissingKey(key string, args map[string]any) {
return
}
file, line := missingKeyCaller()
mk := MissingKey{Key: key, Args: args, CallerFile: file, CallerLine: line}
mk := cloneMissingKey(MissingKey{Key: key, Args: args, CallerFile: file, CallerLine: line})
for _, h := range state.handlers {
if h != nil {
h(mk)
@ -209,6 +209,19 @@ func dispatchMissingKey(key string, args map[string]any) {
}
}
func cloneMissingKey(mk MissingKey) MissingKey {
if len(mk.Args) == 0 {
mk.Args = nil
return mk
}
args := make(map[string]any, len(mk.Args))
for key, value := range mk.Args {
args[key] = value
}
mk.Args = args
return mk
}
func missingKeyCaller() (string, int) {
const packagePrefix = "dappco.re/go/core/i18n."