diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..70a9f52
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,77 @@
+name: build
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  builds:
+    name: Earliest Supported Version
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - ubuntu-latest
+        ocaml-version:
+          - 4.04.0
+
+    runs-on: ${{ matrix.os }}
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+
+      - name: Use OCaml ${{ matrix.ocaml-version }}
+        uses: avsm/setup-ocaml@v1
+        with:
+          ocaml-version: ${{ matrix.ocaml-version }}
+
+      - name: Deps
+        run: |
+          opam pin add -n angstrom .
+          opam install --deps-only angstrom
+
+      - name: Build
+        run: opam exec -- dune build -p angstrom
+
+  tests:
+    name: Tests
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - ubuntu-latest
+        ocaml-version:
+          - 4.08.1
+          - 4.10.2
+          - 4.11.2
+          - 4.12.0
+
+    runs-on: ${{ matrix.os }}
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+
+      - name: Use OCaml ${{ matrix.ocaml-version }}
+        uses: avsm/setup-ocaml@v1
+        with:
+          ocaml-version: ${{ matrix.ocaml-version }}
+
+      - name: Deps
+        run: |
+          opam pin add -n angstrom .
+          opam pin add -n angstrom-async .
+          opam pin add -n angstrom-lwt-unix .
+          opam install -t --deps-only .
+
+      - name: Build
+        run: opam exec -- dune build
+
+      - name: Test
+        run: opam exec -- dune runtest
+
+      - name: Examples
+        run: |
+          opam install -t angstrom-async angstrom-lwt-unix
+          opam exec -- make examples
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 359fa6b..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-language: c
-sudo: false
-services:
-  - docker
-install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/master/.travis-docker.sh
-script: bash -ex ./.travis-docker.sh
-env:
-  global:
-  - DISTRO="ubuntu"
-  - PINS="angstrom-async:. angstrom-lwt-unix:. angstrom:."
-  - PACKAGE="angstrom"
-  - TESTS=true
-  - EXTRA_DEPS="ppx_let"
-  - POST_INSTALL_HOOK="opam install --with-test angstrom-async angstrom-lwt-unix && opam exec -- make examples"
-  matrix:
-  - OCAML_VERSION="4.08"
-  - OCAML_VERSION="4.07"
-  - OCAML_VERSION="4.06"
diff --git a/README.md b/README.md
index 2ba20a8..612fff4 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ give the user total control over the blocking behavior of their application,
 with the unbuffered interface enabling zero-copy IO. Parsers are backtracking
 by default and support unbounded lookahead.
 
-[![Build Status](https://travis-ci.com/inhabitedtype/angstrom.svg?branch=master)](https://travis-ci.com/inhabitedtype/angstrom)
+[![Build Status](https://github.com/inhabitedtype/angstrom/workflows/build/badge.svg)](https://github.com/inhabitedtype/angstrom/actions?query=workflow%3A%22build%22)
 
 
 ## Installation
diff --git a/angstrom-async.opam b/angstrom-async.opam
index 5e32f3a..22460af 100644
--- a/angstrom-async.opam
+++ b/angstrom-async.opam
@@ -1,4 +1,3 @@
-version: "0.15.0"
 opam-version: "2.0"
 maintainer: "Spiros Eliopoulos <spiros@inhabitedtype.com>"
 authors: [ "Spiros Eliopoulos <spiros@inhabitedtype.com>" ]
@@ -13,7 +12,7 @@ build: [
 ]
 depends: [
   "ocaml" {>= "4.04.1"}
-  "dune" {>= "1.0"}
+  "dune" {>= "1.8"}
   "angstrom" {>= "0.7.0"}
   "async" {>= "v0.10.0"}
 ]
diff --git a/angstrom-lwt-unix.opam b/angstrom-lwt-unix.opam
index ba81d95..b956947 100644
--- a/angstrom-lwt-unix.opam
+++ b/angstrom-lwt-unix.opam
@@ -1,4 +1,3 @@
-version: "0.15.0"
 opam-version: "2.0"
 maintainer: "Spiros Eliopoulos <spiros@inhabitedtype.com>"
 authors: [ "Spiros Eliopoulos <spiros@inhabitedtype.com>" ]
@@ -13,7 +12,7 @@ build: [
 ]
 depends: [
   "ocaml" {>= "4.03.0"}
-  "dune" {>= "1.0"}
+  "dune" {>= "1.8"}
   "angstrom"
   "lwt"
   "base-unix"
diff --git a/angstrom-unix.opam b/angstrom-unix.opam
index fe3ecbb..3ae9b22 100644
--- a/angstrom-unix.opam
+++ b/angstrom-unix.opam
@@ -1,4 +1,3 @@
-version: "0.15.0"
 opam-version: "2.0"
 maintainer: "Spiros Eliopoulos <spiros@inhabitedtype.com>"
 authors: [ "Spiros Eliopoulos <spiros@inhabitedtype.com>" ]
@@ -13,7 +12,7 @@ build: [
 ]
 depends: [
   "ocaml" {>= "4.03.0"}
-  "dune" {>= "1.0"}
+  "dune" {>= "1.8"}
   "angstrom"
   "base-unix"
 ]
diff --git a/angstrom.opam b/angstrom.opam
index 57ef601..b121227 100644
--- a/angstrom.opam
+++ b/angstrom.opam
@@ -1,4 +1,3 @@
-version: "0.15.0"
 opam-version: "2.0"
 maintainer: "Spiros Eliopoulos <spiros@inhabitedtype.com>"
 authors: [ "Spiros Eliopoulos <spiros@inhabitedtype.com>" ]
@@ -16,8 +15,7 @@ depends: [
   "dune" {>= "1.8"}
   "alcotest" {with-test & >= "0.8.1"}
   "bigstringaf"
-  "result"
-  "ppx_let" {with-test & >= "0.14.0"}
+  "ppx_let" {with-test & >= "v0.14.0"}
   "ocaml-syntax-shims" {build}
 ]
 synopsis: "Parser combinators built for speed and memory-efficiency"
diff --git a/benchmarks/pure_benchmark.ml b/benchmarks/pure_benchmark.ml
index 295e6f2..b7c54d9 100644
--- a/benchmarks/pure_benchmark.ml
+++ b/benchmarks/pure_benchmark.ml
@@ -21,7 +21,7 @@ let zero =
 
 let make_bench name parser contents =
   Bench.Test.create ~name (fun () ->
-    match Angstrom.(parse_bigstring parser contents) with
+    match Angstrom.(parse_bigstring ~consume:Consume.Prefix parser contents) with
     | Ok _ -> ()
     | Error err -> failwith err)
 ;;
diff --git a/debian/changelog b/debian/changelog
index 6c70aa7..20da24f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ocaml-angstrom (0.15.0+git20210921.1.5536d1d-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 31 Mar 2022 15:48:48 -0000
+
 ocaml-angstrom (0.15.0-1) unstable; urgency=medium
 
   * Initial release (Closes: #1000748)
diff --git a/lib/angstrom.ml b/lib/angstrom.ml
index b025121..241d052 100644
--- a/lib/angstrom.ml
+++ b/lib/angstrom.ml
@@ -461,8 +461,7 @@ let fix_direct f =
   in
   r
 
-let fix_lazy f =
-  let max_steps = 20 in
+let fix_lazy ~max_steps f =
   let steps = ref max_steps in
   let rec p = lazy (f r)
   and r = { run = fun buf pos more fail succ ->
@@ -480,7 +479,7 @@ let fix_lazy f =
 let fix = match Sys.backend_type with
   | Native -> fix_direct
   | Bytecode -> fix_direct
-  | Other _ -> fix_lazy
+  | Other _ -> fun f -> fix_lazy ~max_steps:20 f
 
 let option x p =
   p <|> return x
@@ -493,9 +492,9 @@ let rec list ps =
   | p::ps -> lift2 cons p (list ps)
 
 let count n p =
-  if n < 0 
+  if n < 0
   then fail "count: n < 0"
-  else 
+  else
     let rec loop = function
       | 0 -> return []
       | n -> lift2 cons p (loop (n - 1))
diff --git a/lib/angstrom.mli b/lib/angstrom.mli
index b9fd964..d669595 100644
--- a/lib/angstrom.mli
+++ b/lib/angstrom.mli
@@ -48,8 +48,7 @@ type +'a t
 (** A parser for values of type ['a]. *)
 
 
-type bigstring =
-  (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
+type bigstring = Bigstringaf.t
 
 (** {2 Basic parsers} *)
 
@@ -349,6 +348,15 @@ val fix : ('a t -> 'a t) -> 'a t
     let obj = char '{' *> ... json ... <* char '}' in
     choice [str; num; arr json, ...])]} *)
 
+(** [fix_lazy] is like [fix], but after the function reaches [max_steps]
+    deep, it wraps up the remaining computation and yields
+    back to the root of the parsing loop where it continues from there.
+
+    This is an effective way to break up the stack trace into more managable
+    chunks, which is important for Js_of_ocaml due to the lack of tailrec
+    optimizations for CPS-style tail calls. When compiling for Js_of_ocaml,
+    [fix] itself is defined as [fix_lazy ~max_steps:20]. *)
+val fix_lazy : max_steps:int -> ('a t -> 'a t) -> 'a t
 
 (** {2 Alternatives} *)
 
diff --git a/lib_test/test_json.ml b/lib_test/test_json.ml
index 98b8a8c..96d720e 100644
--- a/lib_test/test_json.ml
+++ b/lib_test/test_json.ml
@@ -14,6 +14,6 @@ let read f =
 
 let () =
   let twitter_big = read Sys.argv.(1) in
-  match Angstrom.(parse_bigstring RFC7159.json twitter_big) with
+  match Angstrom.(parse_bigstring ~consume:Consume.Prefix RFC7159.json twitter_big) with
   | Ok _ -> ()
   | Error err -> failwith err
diff --git a/lib_test/test_let_syntax_ppx.ml b/lib_test/test_let_syntax_ppx.ml
index 8640452..a21173c 100644
--- a/lib_test/test_let_syntax_ppx.ml
+++ b/lib_test/test_let_syntax_ppx.ml
@@ -11,12 +11,8 @@ let (_ : int t) =
   in
   2
 
-(* [mapn] support was introduced in ppx_let.v0.14.0, which CI does not reliably
-  install. *)
-(*
 let (_ : int t) =
   let%mapn (_ : char) = any_char
   and (_ : string) = string "foo"
   in
   2
-*)