anchor.diff:        query, searchPage.packageVersions, links));
   }
   
  -/// Handles requests for /packages
  -///   TODO: handle ...?format=json URIs
  +/// Handles requests for /packages - multiplexes to JSON/HTML handler.
   packagesHandler(shelf.Request request) async {
     int page = _pageFromUrl(request.url);
  -
  +  var path = request.url.path;
  +  if (path.endsWith('.json')) {
  +    path = path.substring(0, path.length - '.json'.length);
  +    return packagesHandlerJson(request, page, true);
  +  } else if (request.url.queryParameters['format'] == 'json') {
  +    return packagesHandlerJson(request, page, false);
  +  } else {
  +    packagesHandlerHtml(request, page);
  +  }
  +}
  +
  +/// Handles requests for /packages - JSON
  +packagesHandlerJson(request, page, dotJsonResponse) async {
  +  final PageSize = 50;
  +
  +  var db = dbService;
  +  var offset = PageSize * (page - 1);
  +  var limit = PageSize + 1;
  +
  +  var query = db.query(Package)
  +      ..offset(offset)
  +      ..limit(limit)
  +      ..order('-updated');
  +
  +  var packages = await query.run().toList();
  +  bool lastPage = packages.length < limit;
  +
  +  var nextPageUrl;
  +  if (!lastPage) {
  +    nextPageUrl =
  +        request.requestedUri.resolve('/packages.json?page=${page + 1}');
  +  }
  +
  +  String toUrl(Package package) {
  +    var postfix = dotJsonResponse ? '.json' : '';
  +    return request.requestedUri.resolve(
  +        '/packages/${Uri.encodeComponent(package.name)}$postfix').toString();
  +  }
  +  var json = {
  +    'packages' : packages.take(PageSize).map(toUrl).toList(),
  +    'next' : nextPageUrl != null ? '$nextPageUrl' : null,
  +
  +    // NOTE: We're not returning the following entry:
  +    //   - 'prev'
  +    //   - 'pages'
  +  };
  +
  +  return _jsonResponse(json);
  +}
  +
  +/// Handles requests for /packages - HTML
  +packagesHandlerHtml(shelf.Request request, int page) async {
     var db = dbService;
     var offset = PackageLinks.RESULTS_PER_PAGE * (page - 1);
     var limit = PackageLinks.MAX_PAGES * PackageLinks.RESULTS_PER_PAGE + 1;
  
positive.code: 
  
  /// Sorts [versions] according to the semantic versioning specification.
  void _sortVersionsDesc(List<PackageVersion> versions) {
    versions.sort((PackageVersion a, PackageVersion b) {
      var av = new Version.parse(a.version);
      var bv = new Version.parse(b.version);
      return bv.compareTo(av);
    });
  }
  
  
negative.code:         '${request.requestedUri.resolve('/api/packages?page=${page+1}')}';
    }
  
    return _jsonResponse(json);
  }
  
  
  