diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
+## [0.3.2] - 2017-10-02
+
+### Fixed
+- Support parsing packages with `@` in the package name 
+
 ## [0.3.1] - 2017-08-16
 
 ### Added
diff --git a/src/Data/MultiKeyedMap.hs b/src/Data/MultiKeyedMap.hs
--- a/src/Data/MultiKeyedMap.hs
+++ b/src/Data/MultiKeyedMap.hs
@@ -23,6 +23,7 @@
 , mkMKMap, fromList, toList
 , insert
 , flattenKeys, keys, values
+, mapKeys
 ) where
 
 import qualified Data.Map.Strict as M
@@ -149,7 +150,15 @@
     ins = newVal [k] v m
     upd ik = MKMap { keyMap, highestIk, valMap = M.insert ik v valMap }
 
+-- | Map function over all keys. See 'M.mapKeys'.
+--
+-- TODO: Might leak space if a k is mapped to an
+-- already existing k', because k’s value might not be
+-- referenced anymore.
+mapKeys :: (Ord k') => (k -> k') -> MKMap k v -> MKMap k' v
+mapKeys f MKMap{..} = MKMap { keyMap = M.mapKeys f keyMap, .. }
 
+
 -- | Helper, assumes there is no such value already.
 -- Will leak space otherwise!
 --
@@ -163,3 +172,4 @@
         , highestIk = next
         , valMap = M.insert next v valMap }
   where next = succ highestIk
+
diff --git a/src/Yarn/Lock/Parse.hs b/src/Yarn/Lock/Parse.hs
--- a/src/Yarn/Lock/Parse.hs
+++ b/src/Yarn/Lock/Parse.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE NoImplicitPrelude, GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NoImplicitPrelude, GeneralizedNewtypeDeriving, OverloadedStrings #-}
 {-|
 Module : Yarn.Lock.Parse
 Description : Parser for yarn.lock files
@@ -15,14 +15,17 @@
 -- ** Parsers
 , packageList
 , packageEntry
--- = internal parsers
-, field, packageKeys, packageKey
+-- * Internal Parsers
+, field, nestedField, simpleField
+, packageKeys, packageKey
 ) where
 
 import Protolude hiding (try)
 import qualified Data.Char as Ch
-import qualified Data.Text as Text
+import qualified Data.Text as T
 import qualified Data.Map.Strict as M
+import Control.Monad (fail)
+
 import Text.Megaparsec as MP hiding (space)
 import Text.Megaparsec.Text
 import qualified Text.Megaparsec.Lexer as MPL
@@ -30,7 +33,7 @@
 -- import qualified Data.MultiKeyedMap as MKM
 -- import Data.Proxy (Proxy(..))
 
-import qualified Yarn.Lock.Types as T
+import qualified Yarn.Lock.Types as YLT
 
 
 -- | The @yarn.lock@ format doesn’t specifically include a fixed scheme,
@@ -44,7 +47,7 @@
 
 -- | A parsed 'Package' AST has one or more keys, a position in the original files
 -- and a collection of fields.
-type Package = T.Keyed (SourcePos, PackageFields)
+type Package = YLT.Keyed (SourcePos, PackageFields)
 
 
 -- | Parse a complete yarn.lock into an abstract syntax tree,
@@ -67,8 +70,9 @@
 --     source-map "^0.4.4"
 --   optionalDependencies:
 --     uglify-js "^2.6"
+--     "
 -- @
-packageEntry :: Parser (T.Keyed (SourcePos, PackageFields))
+packageEntry :: Parser (YLT.Keyed (SourcePos, PackageFields))
 packageEntry = label "package entry" $ do
   pos <- getPosition
   -- A package entry is a non-indented
@@ -76,14 +80,14 @@
             -- block that has a header of package keys
             -- and an indented part that contains fields
             $ indentedFieldsWithHeader packageKeys
-  pure $ T.Keyed keys (pos, pkgs)
+  pure $ YLT.Keyed keys (pos, pkgs)
 
 -- | The list of PackageKeys that index the same Package
 --
 -- @
 -- align-text@^0.1.1, align-text@^0.1.3:\\n
 -- @
-packageKeys :: Parser [T.PackageKey]
+packageKeys :: Parser [YLT.PackageKey]
 packageKeys = label "package keys" $ do
   firstEls <- many (try $ lexeme $ packageKey ":," <* char ',')
   lastEl   <-                      packageKey ":"  <* char ':'
@@ -92,21 +96,26 @@
 -- | A packageKey is @\<package-name\>\@\<semver\>@;
 --
 -- If the semver contains spaces, it is also quoted with @"@.
-packageKey :: [Char] -> Parser T.PackageKey
+packageKey :: [Char] -> Parser YLT.PackageKey
 packageKey separators = inString (pkgKey "\"")
          -- if no string delimiters is used we need to check for the separators
          -- this file format is shit :<
          <|> pkgKey separators
          <?> "package key"
   where
-    pkgKey valueChars = do
-      pkgName <- someTextOf (noneOf "@") <?> "package name part of package key"
-      _ <- char '@'
-      semver <- (someTextOf (noneOf valueChars))
-                <|> (pure Text.empty <?> "an empty semver")
-                <?> "semver part of package key"
-      pure $ T.PackageKey pkgName semver
-
+    pkgKey :: [Char] -> Parser YLT.PackageKey
+    pkgKey valueChars = label "package key" $ do
+      key <- someTextOf (noneOf valueChars)
+      -- okay, here’s the rub:
+      -- `@` is used for separation, but package names containing `@`
+      -- are totally a thing. As first character, as well.
+      -- As are package keys without any semver whatsoever, thus ending
+      -- with `@`.
+      -- Did I mention this file format is big chunks of elephant shit?
+      case (\(n, v) -> (T.dropEnd 1 n, v)) $ T.breakOnEnd "@" key of
+        ("", _) -> fail "packageKey: package name can not be empty"
+        (n, "") -> pure $ YLT.PackageKey n ""
+        (n,  v) -> pure $ YLT.PackageKey n v
 
 -- | Either a simple or a nested field.
 field :: Parser (Text, Either Text PackageFields)
@@ -115,20 +124,22 @@
     simple = fmap Left <$> simpleField
     nested = fmap Right <$> nestedField
 
--- | A key-value pair, separated by space. The value is enclosed in "".
+-- | A key-value pair, separated by space.
+-- Key any value may be enclosed in "".
 -- Returns key and value.
 simpleField :: Parser (Text, Text)
-simpleField = (,) <$> lexeme symbolChars
+simpleField = (,) <$> lexeme (strSymbolChars <|> symbolChars)
                   -- valueChars may be in Strings or maybe not >:
                   -- this file format is absolute garbage
                   <*> (strValueChars <|> valueChars)
                   <?> "simple field"
   where
     valueChars, strValueChars :: Parser Text
-    valueChars = someTextOf (noneOf "\n\r\"")
+    valueChars = someTextOf (noneOf ("\n\r\"" :: [Char]))
+    strSymbolChars = inString $ symbolChars
     strValueChars = inString $ valueChars
       -- as with packageKey semvers, this can be empty
-      <|> (pure Text.empty <?> "an empty value field")
+      <|> (pure T.empty <?> "an empty value field")
 
 -- | Similar to a @simpleField@, but instead of a string
 -- we get another block with deeper indentation.
@@ -162,13 +173,14 @@
 symbolChars :: Parser Text
 symbolChars = label "key symbol" $ someTextOf $ satisfy
   (\c -> Ch.isAscii c &&
-     (Ch.isLower c || Ch.isUpper c || Ch.isNumber c || c `elem` "-_."))
+     (Ch.isLower c || Ch.isUpper c || Ch.isNumber c || c `elem` special))
+  where special = "-_.@/" :: [Char]
 
 
 -- text versions of parsers & helpers
 
 someTextOf :: Parser Char -> Parser Text
-someTextOf c = Text.pack <$> some c
+someTextOf c = T.pack <$> some c
 
 -- | parse everything as inside a string
 inString :: Parser a -> Parser a
diff --git a/tests/TestParse.hs b/tests/TestParse.hs
--- a/tests/TestParse.hs
+++ b/tests/TestParse.hs
@@ -46,10 +46,23 @@
     dependencies:
       core-util-is "~1.0.0"
       is.array ""
-      string_decoder "~0.10.x"
+      "@types/string_decoder" "~0.10.x"
       johnny-dep 2.3.4
   |]
 
+nestedFieldExample :: Text
+nestedFieldExample = [text|
+  dependencies:
+    core-util-is "~1.0.0"
+    is.array ""
+    "@types/string_decoder" "~0.10.x"
+    johnny-dep 2.3.4
+  |]
+
+case_nestedField :: Assertion
+case_nestedField = do
+  void $ parseSuccess nestedField nestedFieldExample
+
 case_NestedPackage :: Assertion
 case_NestedPackage = do
   assertBool "there is unicode" (all Ch.isAscii (toS nestedPackage :: [Char]))
@@ -62,7 +75,9 @@
         (Just (Right (PackageFields nested))) -> do
           assertEqual "nested keys" 4 $ length nested
           assertEqual "dep exists" (Just (Left "2.3.4"))
-            $ Map.lookup "johnny-dep" nested 
+            $ Map.lookup "johnny-dep" nested
+          assertEqual "there can be @" (Just (Left "~0.10.x"))
+            $ Map.lookup "@types/string_decoder" nested
 
 case_PackageField :: IO ()
 case_PackageField = do
@@ -81,13 +96,16 @@
 
 case_PackageKey :: Assertion
 case_PackageKey = do
-  let key = "foo@^1.3.4, bar@blafoo234, xnu@:\n"
+  let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@@:\n"
   parseSuccess packageKeys key
     >>= \keys -> do
-      keys @=? [ PackageKey "foo" "^1.3.4"
+      keys @?= [ PackageKey "foo" "^1.3.4"
                , PackageKey "bar" "blafoo234"
                -- yes, the version can be empty …
-               , PackageKey "xnu" ""]
+               , PackageKey "xnu" ""
+               -- and yes, package names can contain `@`
+               , PackageKey "@types/foo@" ""
+               ]
 
 parseSuccess :: Parser a -> Text -> IO a
 parseSuccess parser string = do
diff --git a/yarn-lock.cabal b/yarn-lock.cabal
--- a/yarn-lock.cabal
+++ b/yarn-lock.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           yarn-lock
-version:        0.3.1
+version:        0.3.2
 synopsis:       Represent and parse yarn.lock files
 description:    Types and parser for the lock file format of the npm successor yarn. All modules should be imported qualified.
 category:       Data
