diff --git a/library/Salve.hs b/library/Salve.hs
--- a/library/Salve.hs
+++ b/library/Salve.hs
@@ -1,7 +1,7 @@
 -- | This module defines types and functions for working with versions as
 -- defined by [Semantic Versioning](http://semver.org/spec/v2.0.0.html). It
 -- also provides types and functions for working with version constraints as
--- described by [npm](https://docs.npmjs.com/misc/semver#ranges).
+-- described by [npm](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#ranges).
 module Salve (
 -- | This module doesn't export anything that conflicts with the "Prelude", so
 -- you can import it unqualified.
@@ -9,7 +9,8 @@
 -- >>> import Salve
 --
 -- This module provides lenses for modifying versions. If you want to modify
--- versions, consider importing a lens library like "Lens.Micro".
+-- versions, consider importing a lens library like
+-- [microlens](https://www.stackage.org/lts-9.3/package/microlens-0.4.8.1).
 --
 -- The 'Version' data type is the core of this module. Use 'parseVersion' to
 -- make versions and 'renderVersion' to convert them into strings.
@@ -56,8 +57,9 @@
 -- * Rationale
 
 -- ** The PVP
--- | Haskell's <https://pvp.haskell.org/ Package Versioning Policy> (PVP)
--- defines three things:
+-- | Haskell's
+-- [Package Versioning Policy](https://github.com/haskell/pvp/blob/176bb14/pvp-specification.md)
+-- (PVP) defines three things:
 --
 -- 1.  A spec for versioning your package, which includes how version numbers
 --     look and how to encode breaking changes.
@@ -72,7 +74,7 @@
 --
 -- Looking at the first point, why might you want to use SemVer instead of the
 -- PVP? The PVP has many problems, as described by the
--- <http://taylor.fausak.me/2016/12/28/problematic-versioning-policy/ Problematic versioning policy>
+-- [Problematic versioning policy](http://taylor.fausak.me/2016/12/28/problematic-versioning-policy/)
 -- blog post. In short, the PVP is too flexible and it's unique to Haskell,
 -- which causes unnecessary friction with developers from other languages.
 --
@@ -86,7 +88,7 @@
 -- | There are already a few modules that provide version numbers. Why do we
 -- need another one? Let's take a look at the options.
 --
--- -   <https://www.stackage.org/haddock/lts-9.3/base-4.9.1.0/Data-Version.html Data.Version>
+-- -   [Data.Version](https://www.stackage.org/haddock/lts-9.3/base-4.9.1.0/Data-Version.html)
 --     from the @base@ package:
 --
 --     -   Exposes constructors, which allows creating versions that cannot be
@@ -96,7 +98,7 @@
 --     -   Does not support build metadata on versions.
 --     -   Does not support constraints.
 --
--- -   <https://www.stackage.org/haddock/lts-9.3/Cabal-1.24.2.0/Distribution-Version.html Distribution.Version>
+-- -   [Distribution.Version](https://www.stackage.org/haddock/lts-9.3/Cabal-1.24.2.0/Distribution-Version.html)
 --     from the @Cabal@ package:
 --
 --     -   Has the same problems as Data.Version because it re-uses that
@@ -105,13 +107,13 @@
 --         @deepseq@, @directory@, @filepath@, @pretty@, @process@, @time@, and
 --         @unix@ packages.
 --
--- -   <https://www.stackage.org/haddock/lts-9.3/semver-0.3.3.1/Data-SemVer.html Data.SemVer>
+-- -   [Data.SemVer](https://www.stackage.org/haddock/lts-9.3/semver-0.3.3.1/Data-SemVer.html)
 --     from the @semver@ package:
 --
 --     -   Depends on the @attoparsec@, @deepseq@, and @text@ packages.
 --     -   Does not support version constraints.
 --
--- -   <https://hackage.haskell.org/package/semver-range-0.2.2/docs/Data-SemVer.html Data.SemVer>
+-- -   [Data.SemVer](https://hackage.haskell.org/package/semver-range-0.2.2/docs/Data-SemVer.html)
 --     from the @semver-range@ package:
 --
 --     -   Depends on the @classy-prelude@, @parsec@, @text@, and
@@ -119,7 +121,7 @@
 --     -   Module name collides with the @semver@ package.
 --     -   Supports constraints, but does not provide a way to render them.
 --
--- -   <https://www.stackage.org/haddock/lts-9.3/versions-3.1.1/Data-Versions.html Data.Versions>
+-- -   [Data.Versions](https://www.stackage.org/haddock/lts-9.3/versions-3.1.1/Data-Versions.html)
 --     from the @versions@ package:
 --
 --     -   Depends on the @deepseq@, @hashable@, @megaparsec@, and @text@
@@ -192,8 +194,7 @@
 -- Don't be scared by these type signatures. They are provided in full to avoid
 -- the @RankNTypes@ language extension. The type signature
 -- @'Functor' f => (a -> f a) -> 'Version' -> f 'Version'@ is the same as
--- @'Lens.Micro.Lens'' 'Version' a@ (from "Lens.Micro"), which you may already
--- be familiar with.
+-- @Lens' 'Version' a@, which you may already be familiar with.
 majorLens,
 minorLens,
 patchLens,
@@ -253,6 +254,27 @@
 -- Nothing
 -- >>> parseVersion "0.0.0 "
 -- Nothing
+--
+-- Each version component cannot be larger than a 64-bit unsigned integer.
+--
+-- >>> parseVersion "18446744073709551615.0.0"
+-- Just (Version {versionMajor = 18446744073709551615, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})
+-- >>> parseVersion "18446744073709551616.0.0"
+-- Nothing
+--
+-- Numeric pre-releases tags cannot be larger than a 64-bit unsigned integer.
+--
+-- >>> parseVersion "0.0.0-18446744073709551615"
+-- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [PreReleaseNumeric 18446744073709551615], versionBuilds = []})
+-- >>> parseVersion "0.0.0-18446744073709551616"
+-- Nothing
+--
+-- Build metadata is not numeric so it does not have any limit.
+--
+-- >>> parseVersion "0.0.0+18446744073709551615"
+-- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551615"]})
+-- >>> parseVersion "0.0.0+18446744073709551616"
+-- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551616"]})
 
 -- ** Constraints
 -- | Partial version numbers are not allowed.
@@ -576,3 +598,6 @@
 ) where
 
 import Salve.Internal
+
+-- $setup
+-- >>> import Control.Applicative
diff --git a/library/Salve/Internal.hs b/library/Salve/Internal.hs
--- a/library/Salve/Internal.hs
+++ b/library/Salve/Internal.hs
@@ -7,13 +7,15 @@
 import qualified Data.Char as Char
 import qualified Data.List as List
 import qualified Data.Maybe as Maybe
+import qualified Data.Monoid as Monoid
 import qualified Data.Ord as Ord
+import qualified Data.Word as Word
 import qualified Text.ParserCombinators.ReadP as ReadP
-import qualified Text.Read as Read
 
 -- $setup
+-- >>> import Control.Applicative
 -- >>> import Lens.Micro
--- >>> import Lens.Micro.Extras
+-- >>> let view l x = x ^. l
 
 -- * Public
 
@@ -27,14 +29,14 @@
 --
 -- Use 'parseVersion' to create versions.
 data Version = Version
-  { versionMajor :: Word
-  , versionMinor :: Word
-  , versionPatch :: Word
+  { versionMajor :: Word.Word64
+  , versionMinor :: Word.Word64
+  , versionPatch :: Word.Word64
   , versionPreReleases :: [PreRelease]
   , versionBuilds :: [Build]
   } deriving (Eq, Show)
 
--- | In general, 'Version's compare in the way that you would expect. First the
+-- | In general, versions compare in the way that you would expect. First the
 -- major version numbers are compared, then the minors, then the patches.
 --
 -- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "2.0.0"
@@ -69,7 +71,7 @@
 -- >>> (==) <$> parseVersion "1.2.3+a" <*> parseVersion "1.2.3+b"
 -- Just False
 instance Ord Version where
-  compare x y = mconcat
+  compare x y = Monoid.mconcat
     [ Ord.comparing versionMajor x y
     , Ord.comparing versionMinor x y
     , Ord.comparing versionPatch x y
@@ -93,7 +95,7 @@
 --
 -- Use 'parsePreRelease' to create pre-releases.
 data PreRelease
-  = PreReleaseNumeric Word
+  = PreReleaseNumeric Word.Word64
   | PreReleaseTextual String
   deriving (Eq, Show)
 
@@ -132,7 +134,7 @@
 -- | Constrains allowable version numbers.
 --
 -- Use 'parseConstraint' to create constraints and 'satisfiesConstraint' to see
--- if a version number satisfiesConstraint a constraint.
+-- if a version number satisfies a constraint.
 data Constraint
   = ConstraintOperator Operator Version
   | ConstraintHyphen Version Version
@@ -143,12 +145,12 @@
 
 -- | Makes a new version number.
 --
--- >>> makeVersion 0 0 0 [] []
--- Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []}
+-- >>> makeVersion 1 2 3 [unsafeParsePreRelease "pre"] [unsafeParseBuild "build"]
+-- Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "pre"], versionBuilds = [Build "build"]}
 --
 -- This can be a useful alternative to 'parseVersion' if you want a total way
 -- to create a version.
-makeVersion :: Word -> Word -> Word -> [PreRelease] -> [Build] -> Version
+makeVersion :: Word.Word64 -> Word.Word64 -> Word.Word64 -> [PreRelease] -> [Build] -> Version
 makeVersion major minor patch preReleases builds = Version
   { versionMajor = major
   , versionMinor = minor
@@ -164,8 +166,8 @@
 initialVersion :: Version
 initialVersion = makeVersion 0 0 0 [] []
 
--- | Attempts to parse a version. This parser follows [SemVer's
--- BNF](https://github.com/mojombo/semver/blob/eb9aac5/semver.md#backusnaur-form-grammar-for-valid-semver-versions).
+-- | Attempts to parse a version. This parser follows
+-- [SemVer's BNF](https://github.com/mojombo/semver/blob/eb9aac5/semver.md#backusnaur-form-grammar-for-valid-semver-versions).
 --
 -- >>> parseVersion "1.2.3-p.4+b.5"
 -- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "p",PreReleaseNumeric 4], versionBuilds = [Build "b",Build "5"]})
@@ -220,10 +222,8 @@
 parseBuild :: String -> Maybe Build
 parseBuild s = parse buildP s
 
--- | Attempts to parse a constraint. This parser follows [npm's
--- BNF](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#range-grammar),
--- except that neither the so-called "x-ranges" nor partial version numbers are
--- not supported. So you cannot use @1.2.x@ or @>1.2@ as version constraints.
+-- | Attempts to parse a constraint. This parser mostly follows
+-- [npm's BNF](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#range-grammar).
 --
 -- >>> parseConstraint ">1.2.3"
 -- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))
@@ -232,6 +232,19 @@
 --
 -- >>> parseConstraint "wrong"
 -- Nothing
+--
+-- The two departures from npm's BNF are that x-ranges cannot be used with
+-- other operators and partial version numbers are not allowed.
+--
+-- >>> parseConstraint "1.2.x"
+-- Just (ConstraintWildcard (WildcardPatch 1 2))
+-- >>> parseConstraint ">=1.2.x"
+-- Nothing
+--
+-- >>> parseConstraint "1.2"
+-- Nothing
+-- >>> parseConstraint ">=1.2"
+-- Nothing
 parseConstraint :: String -> Maybe Constraint
 parseConstraint s = parse constraintsP s
 
@@ -308,7 +321,7 @@
 -- >>> renderVersion <$> parseVersion "1.2.3-p.4+b.5"
 -- Just "1.2.3-p.4+b.5"
 renderVersion :: Version -> String
-renderVersion v = mconcat
+renderVersion v = concat
   [ show (versionMajor v)
   , "."
   , show (versionMinor v)
@@ -460,7 +473,7 @@
 -- Just 1
 -- >>> set majorLens 4 <$> parseVersion "1.2.3"
 -- Just (Version {versionMajor = 4, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})
-majorLens :: Functor f => (Word -> f Word) -> Version -> f Version
+majorLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version
 majorLens f v = fmap
   (\ m -> v { versionMajor = m })
   (f (versionMajor v))
@@ -471,7 +484,7 @@
 -- Just 2
 -- >>> set minorLens 4 <$> parseVersion "1.2.3"
 -- Just (Version {versionMajor = 1, versionMinor = 4, versionPatch = 3, versionPreReleases = [], versionBuilds = []})
-minorLens :: Functor f => (Word -> f Word) -> Version -> f Version
+minorLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version
 minorLens f v = fmap
   (\ n -> v { versionMinor = n })
   (f (versionMinor v))
@@ -482,7 +495,7 @@
 -- Just 3
 -- >>> set patchLens 4 <$> parseVersion "1.2.3"
 -- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 4, versionPreReleases = [], versionBuilds = []})
-patchLens :: Functor f => (Word -> f Word) -> Version -> f Version
+patchLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version
 patchLens f v = fmap
   (\ p -> v { versionPatch = p })
   (f (versionPatch v))
@@ -525,8 +538,8 @@
 
 data Wildcard
   = WildcardMajor
-  | WildcardMinor Word
-  | WildcardPatch Word Word
+  | WildcardMinor Word.Word64
+  | WildcardPatch Word.Word64 Word.Word64
   deriving (Eq, Show)
 
 -- ** Constructors
@@ -635,7 +648,7 @@
   patch <- numberP
   preReleases <- preReleasesP
   builds <- buildsP
-  pure (makeVersion major minor patch preReleases builds)
+  return (makeVersion major minor patch preReleases builds)
 
 preReleasesP :: ReadP.ReadP [PreRelease]
 preReleasesP = ReadP.option [] (do
@@ -648,14 +661,14 @@
 preReleaseNumberP :: ReadP.ReadP PreRelease
 preReleaseNumberP = do
   n <- numberP
-  pure (PreReleaseNumeric n)
+  return (PreReleaseNumeric n)
 
 preReleaseStringP :: ReadP.ReadP PreRelease
 preReleaseStringP = do
   s <- ReadP.munch1 isIdentifier
   if all Char.isDigit s
     then ReadP.pfail
-    else pure (PreReleaseTextual s)
+    else return (PreReleaseTextual s)
 
 buildsP :: ReadP.ReadP [Build]
 buildsP = ReadP.option [] (do
@@ -665,21 +678,21 @@
 buildP :: ReadP.ReadP Build
 buildP = do
   b <- ReadP.munch1 isIdentifier
-  pure (Build b)
+  return (Build b)
 
-numberP :: ReadP.ReadP Word
-numberP = nonZeroP ReadP.<++ zeroP
+numberP :: ReadP.ReadP Word.Word64
+numberP = zeroP ReadP.<++ nonZeroP
 
-zeroP :: ReadP.ReadP Word
+zeroP :: ReadP.ReadP Word.Word64
 zeroP = do
   Monad.void (ReadP.char '0')
-  pure 0
+  return 0
 
-nonZeroP :: ReadP.ReadP Word
+nonZeroP :: ReadP.ReadP Word.Word64
 nonZeroP = do
   x <- ReadP.satisfy isAsciiDigitNonZero
   ys <- ReadP.munch Char.isDigit
-  case Read.readMaybe (x : ys) of
+  case toWord64 (stringToIntegral (x : ys)) of
     Nothing -> ReadP.pfail
     Just n -> pure n
 
@@ -688,19 +701,19 @@
   spacesP
   cs <- ReadP.sepBy1 constraintP orP
   spacesP
-  pure (foldr1 constraintOr cs)
+  return (foldr1 constraintOr cs)
 
 constraintP :: ReadP.ReadP Constraint
 constraintP = do
   cs <- ReadP.sepBy1 simpleP spaces1P
-  pure (foldr1 constraintAnd cs)
+  return (foldr1 constraintAnd cs)
 
 hyphenatedP :: ReadP.ReadP Constraint
 hyphenatedP = do
   v <- versionP
   hyphenP
   w <- versionP
-  pure (constraintHyphen v w)
+  return (constraintHyphen v w)
 
 simpleP :: ReadP.ReadP Constraint
 simpleP = ReadP.choice [hyphenatedP, wildcardConstraintP, primitiveP]
@@ -709,7 +722,7 @@
 wildcardConstraintP = do
   ReadP.optional (ReadP.char '=')
   w <- wildcardP
-  pure (ConstraintWildcard w)
+  return (ConstraintWildcard w)
 
 wildcardP :: ReadP.ReadP Wildcard
 wildcardP = ReadP.choice [wildcardPatchP, wildcardMinorP, wildcardMajorP]
@@ -721,7 +734,7 @@
   n <- numberP
   Monad.void (ReadP.char '.')
   Monad.void (ReadP.satisfy isWildcard)
-  pure (WildcardPatch m n)
+  return (WildcardPatch m n)
 
 wildcardMinorP :: ReadP.ReadP Wildcard
 wildcardMinorP = do
@@ -730,7 +743,7 @@
   Monad.void (ReadP.satisfy isWildcard)
   Monad.void (ReadP.char '.')
   Monad.void (ReadP.satisfy isWildcard)
-  pure (WildcardMinor m)
+  return (WildcardMinor m)
 
 wildcardMajorP :: ReadP.ReadP Wildcard
 wildcardMajorP = do
@@ -739,25 +752,25 @@
   Monad.void (ReadP.satisfy isWildcard)
   Monad.void (ReadP.char '.')
   Monad.void (ReadP.satisfy isWildcard)
-  pure WildcardMajor
+  return WildcardMajor
 
 primitiveP :: ReadP.ReadP Constraint
 primitiveP = do
   o <- operatorP
   spacesP
   v <- versionP
-  pure (ConstraintOperator o v)
+  return (ConstraintOperator o v)
 
 operatorP :: ReadP.ReadP Operator
 operatorP = ReadP.choice
-  [ ReadP.string "<=" *> pure OperatorLE
-  , ReadP.string ">=" *> pure OperatorGE
-  , ReadP.char '<' *> pure OperatorLT
-  , ReadP.char '>' *> pure OperatorGT
-  , ReadP.char '=' *> pure OperatorEQ
-  , ReadP.char '^' *> pure OperatorCaret
-  , ReadP.char '~' *> pure OperatorTilde
-  , pure OperatorEQ
+  [ ReadP.string "<=" >> return OperatorLE
+  , ReadP.string ">=" >> return OperatorGE
+  , ReadP.char '<' >> return OperatorLT
+  , ReadP.char '>' >> return OperatorGT
+  , ReadP.char '=' >> return OperatorEQ
+  , ReadP.char '^' >> return OperatorCaret
+  , ReadP.char '~' >> return OperatorTilde
+  , return OperatorEQ
   ]
 
 hyphenP :: ReadP.ReadP ()
@@ -809,8 +822,20 @@
   let p' = ReadP.readP_to_S p
   in Maybe.listToMaybe (do
     (x, "") <- p' s
-    pure x)
+    return x)
 
+stringToIntegral :: Integral a => String -> a
+stringToIntegral s = foldl
+  (\ n d -> (n * 10) + (fromIntegral (fromEnum d) - 48)) 0 s
+
+toWord64 :: Integer -> Maybe Word.Word64
+toWord64 n =
+  if n < 0
+  then Nothing
+  else if n > fromIntegral (maxBound :: Word.Word64)
+  then Nothing
+  else Just (fromIntegral n)
+
 -- * Simple constraints
 -- | Simple constraints are just as expressive as 'Constraint's, but they are
 -- easier to reason about. You can think of them as the desugared version of
@@ -824,7 +849,7 @@
   | SCOr SimpleConstraint SimpleConstraint
   deriving (Eq, Show)
 
-mkV :: Word -> Word -> Word -> Version
+mkV :: Word.Word64 -> Word.Word64 -> Word.Word64 -> Version
 mkV m n p = makeVersion m n p [] []
 
 satisfiesSC :: SimpleConstraint -> Version -> Bool
diff --git a/salve.cabal b/salve.cabal
--- a/salve.cabal
+++ b/salve.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           salve
-version:        0.0.6
+version:        0.0.7
 synopsis:       Semantic version numbers and constraints.
 description:    Salve provides semantic version (SemVer) numbers and constraints (ranges).
 category:       Distribution
@@ -28,7 +28,7 @@
       library
   ghc-options: -Wall
   build-depends:
-      base >=4.8.2 && <4.11
+      base >=4.7.0 && <4.11
   exposed-modules:
       Salve
       Salve.Internal
@@ -41,9 +41,9 @@
       tests
   ghc-options: -Wall
   build-depends:
-      base >=4.8.2 && <4.11
-    , doctest >=0.10.1 && <0.14
-    , microlens >=0.3.5 && <0.5
+      base >=4.7.0 && <4.11
+    , doctest >=0.9.11 && <0.14
+    , microlens >=0.2.0 && <0.5
   default-language: Haskell2010
 
 benchmark benchmark
@@ -53,7 +53,7 @@
       benchmarks
   ghc-options: -Wall
   build-depends:
-      base >=4.8.2 && <4.11
+      base >=4.7.0 && <4.11
     , criterion >=1.1.0 && <1.3
     , deepseq >=1.4.1 && <1.5
     , salve
diff --git a/tests/doctest.hs b/tests/doctest.hs
--- a/tests/doctest.hs
+++ b/tests/doctest.hs
@@ -1,4 +1,7 @@
 import Test.DocTest
 
 main :: IO ()
-main = doctest ["library"]
+main = doctest
+  [ "library/Salve/Internal.hs"
+  , "library/Salve.hs"
+  ]
