Compare commits

..

No commits in common. "master" and "1.0.0-alpha4" have entirely different histories.

328 changed files with 4372 additions and 11621 deletions

View File

@ -1,9 +1,9 @@
name: docker-esy name: docker-esy
on: workflow_call on:
# push: push:
# paths: paths:
# - 'example/z-docker-esy/**' - 'example/z-docker-esy/**'
# - .github/workflows/docker-esy.yml - .github/workflows/docker-esy.yml
jobs: jobs:
deploy: deploy:

View File

@ -1,9 +1,9 @@
name: docker-opam name: docker-opam
on: workflow_call on:
# push: push:
# paths: paths:
# - 'example/z-docker-opam/**' - 'example/z-docker-opam/**'
# - .github/workflows/docker-opam.yml - .github/workflows/docker-opam.yml
jobs: jobs:
deploy: deploy:

View File

@ -1,9 +1,9 @@
name: systemd name: systemd
on: workflow_call on:
# push: push:
# paths: paths:
# - 'example/z-systemd/**' - 'example/z-systemd/**'
# - .github/workflows/systemd.yml - .github/workflows/systemd.yml
jobs: jobs:
deploy: deploy:

View File

@ -7,71 +7,46 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: os:
# Until https://github.com/ocaml/setup-ocaml/issues/872. - ubuntu-latest
# When fixing, search for other instances of this string in this file.
- ubuntu-22.04
ocaml: ocaml:
- 5.2.x - 4.13.x
- 4.14.x - 4.12.x
- 4.11.x
- 4.10.x
- 4.09.x
- 4.08.x
include: include:
- os: macos-latest - os: macos-latest
ocaml: 4.14.x ocaml: 4.12.x
- os: windows-latest # Windows is blocked until we no longer require libev; Dream still works
ocaml: 4.14.x # on Windows, but testing it is awkward at the moment.
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- uses: ocaml/setup-ocaml@v3 - uses: avsm/setup-ocaml@v2
with: with:
ocaml-compiler: ${{matrix.ocaml}} ocaml-compiler: ${{matrix.ocaml}}
dune-cache: true
# For Caqti PostgreSQL examples. opam does actually install PostgreSQL for - run: opam depext --yes conf-postgresql
# us. However, Homebrew doesn't link it by default, so we have to install - run: opam depext --yes conf-libev
# and link it manually. # Needed until https://github.com/reasonml/reason/pull/2660 is in opam.
- run: brew install postgresql@15 && brew link --overwrite postgresql@15 - run: opam pin add reason --yes --no-action --dev-repo
if: runner.os == 'macOS' - run: opam install --yes --deps-only --with-test ./dream-pure.opam ./dream-httpaf.opam ./dream.opam
- run: opam exec -- dune runtest
# Workaround https://github.com/savonet/ocaml-ssl/issues/155 and/or - run: |
# https://github.com/ocaml/setup-ocaml/issues/856.
- run: opam pin add ssl 0.6.0 --no-action
if: runner.os == 'Windows'
- run: opam exec -- make deps
- run: opam exec -- make
# Tests on Windows are disabled because of a difference in ppx_expect
# output. See https://github.com/aantron/dream/pull/282. This difference
# remains as of ppx_expect 0.16.
- run: opam exec -- make test
if: runner.os != 'Windows'
- run: opam lint --recursive example
- name: Build examples
if: runner.os != 'Windows'
run: |
set -e set -e
set -x set -x
EXCLUDED_EXAMPLES='w-mirage*|r-tyxml|w-dream-html'
EXAMPLES=$(find example -maxdepth 1 -type d | grep -Ev $EXCLUDED_EXAMPLES | grep -v "^example/0" | grep -v "^example$" | sort) EXAMPLES=$(find example -maxdepth 1 -type d -not -name "w-mirage*"| grep -v "^example/0" | grep -v "^example$" | sort)
shopt -s nullglob shopt -s nullglob
for EXAMPLE in $EXAMPLES for EXAMPLE in $EXAMPLES
do do
FILE=$(find $EXAMPLE -maxdepth 1 -type f -and -path "${EXAMPLE}/*.ml") FILE=$(ls $EXAMPLE/*.ml $EXAMPLE/*.re $EXAMPLE/server/*.ml $EXAMPLE/server/*.re)
FILE+=$(find $EXAMPLE -maxdepth 1 -type f -and -path "${EXAMPLE}/*.re")
FILE+=$(find $EXAMPLE -maxdepth 2 -type f -and -path "${EXAMPLE}/server/*.ml")
FILE+=$(find $EXAMPLE -maxdepth 2 -type f -and -path "${EXAMPLE}/server/*.re")
if [[ "$FILE" == "" ]]; then
continue
fi
EXE=$(echo $FILE | sed 's/\..*$/.exe/g') EXE=$(echo $FILE | sed 's/\..*$/.exe/g')
echo dune build $EXE echo dune build $EXE
opam exec -- dune build $EXE opam exec -- dune build $EXE
@ -82,23 +57,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: os:
- ubuntu-22.04 - ubuntu-latest
ocaml: - macos-latest
- 5.2.x
- 4.14.x
include:
- os: macos-latest
ocaml: 4.14.x
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: ocaml/setup-ocaml@v3 - name: Quick start
with:
ocaml-compiler: ${{matrix.ocaml}}
dune-cache: true
- name: Run quickstart.sh
shell: bash
run: | run: |
set -x set -x
touch output touch output
@ -112,33 +76,3 @@ jobs:
else else
exit 1 exit 1
fi fi
mirage:
if: false
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: mkdir ../repo-copy
- run: cp -r * ../repo-copy/
- uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 4.14.x
dune-cache: true
# Needed until https://github.com/robur-coop/ocaml-letsencrypt/pull/34.
- run: opam pin add letsencrypt git+https://github.com/hannesm/ocaml-letsencrypt.git#no-cstruct --no-action
- run: opam install --yes --deps-only ./dream-pure.opam ./dream-httpaf.opam ./dream.opam ./dream-mirage.opam
- run: opam install --yes mirage mirage-clock-unix mirage-crypto-rng-mirage
- run: cd example/w-mirage && mv config.ml config.ml.backup
- run: cd example/w-mirage && sed -e 's/package "dream-mirage"//' < config.ml.backup > config.ml
- run: cd example/w-mirage && opam exec -- mirage configure -t unix
- run: cd example/w-mirage && opam exec -- make depends
- run: cd example/w-mirage && ls duniverse
- run: cp -r ../repo-copy example/w-mirage/duniverse/dream
- run: cd example/w-mirage/duniverse && rm -rf ocaml-cstruct logs ke fmt lwt bytes seq mirage-flow sexplib0 ptime tls domain-name ocaml-ipaddr mirage-clock ocplib-endian digestif eqaf mirage-crypto mirage-runtime
- run: cd example/w-mirage && mv config.ml.backup config.ml
- run: cd example/w-mirage && sed -e 's/(libraries/(libraries dream-mirage/' < dune.build > dune.build.2
- run: cd example/w-mirage && mv dune.build.2 dune.build
- run: cd example/w-mirage && opam exec -- dune build
- run: file example/w-mirage/_build/default/main.exe

1
.gitignore vendored
View File

@ -26,4 +26,3 @@ scratch/
# Editors # Editors
.vscode/ .vscode/
*.swp

16
.gitmodules vendored Normal file
View File

@ -0,0 +1,16 @@
[submodule "src/vendor/httpaf"]
path = src/vendor/httpaf
url = https://github.com/aantron/httpaf.git
[submodule "src/vendor/gluten"]
path = src/vendor/gluten
url = https://github.com/aantron/gluten.git
[submodule "src/vendor/websocketaf"]
path = src/vendor/websocketaf
url = https://github.com/aantron/websocketaf.git
[submodule "src/vendor/h2"]
path = src/vendor/h2
url = https://github.com/aantron/ocaml-h2.git
[submodule "src/vendor/paf"]
path = src/vendor/paf
url = https://github.com/dinosaure/paf-le-chien.git
branch = dream

View File

@ -1,4 +1,4 @@
version = 0.25.1 version = 0.20.1
profile = conventional profile = conventional
leading-nested-match-parens = false leading-nested-match-parens = false
@ -8,6 +8,7 @@ space-around-lists = false
space-around-records = false space-around-records = false
break-infix = fit-or-vertical break-infix = fit-or-vertical
break-separators = after break-separators = after
space-around-records = true
break-cases = fit-or-vertical break-cases = fit-or-vertical
cases-exp-indent = 2 cases-exp-indent = 2
exp-grouping = preserve exp-grouping = preserve

View File

@ -1,4 +1,4 @@
Copyright (c) 2021-2024, Anton Bachin Copyright (c) 2021, Anton Bachin
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,17 +1,10 @@
PACKAGES := dream-pure,dream-httpaf,dream
.PHONY : build .PHONY : build
build : build :
@dune build --only-packages $(PACKAGES) --no-print-directory @install @dune build -p dream-pure,dream-httpaf,dream --no-print-directory @install
.PHONY : watch .PHONY : watch
watch : watch :
@dune build --only-packages $(PACKAGES) --no-print-directory @install -w @dune build -p dream-pure,dream-httpaf,dream --no-print-directory -w
.PHONY : deps
deps :
opam install --deps-only --with-test --with-doc \
./dream-pure.opam ./dream-httpaf.opam ./dream.opam
TEST ?= test TEST ?= test
ROOT := $(shell [ -f ../dune-workspace ] && echo .. || echo .) ROOT := $(shell [ -f ../dune-workspace ] && echo .. || echo .)
@ -31,7 +24,7 @@ test-watch :
.PHONY : coverage-serve .PHONY : coverage-serve
coverage-serve : coverage-serve :
cd _coverage && dune exec -- dream-serve -p 8082 cd _coverage && dune exec -- serve -p 8082
.PHONY : promote .PHONY : promote
promote : promote :
@ -113,14 +106,12 @@ release : clean
cp -r $(FILES) $(RELEASE) cp -r $(FILES) $(RELEASE)
rm -rf $(RELEASE)/src/vendor/gluten/.github rm -rf $(RELEASE)/src/vendor/gluten/.github
rm -rf $(RELEASE)/src/vendor/gluten/async rm -rf $(RELEASE)/src/vendor/gluten/async
rm -rf $(RELEASE)/src/vendor/gluten/eio
rm -rf $(RELEASE)/src/vendor/gluten/mirage rm -rf $(RELEASE)/src/vendor/gluten/mirage
rm -rf $(RELEASE)/src/vendor/gluten/nix rm -rf $(RELEASE)/src/vendor/gluten/nix
rm -rf $(RELEASE)/src/vendor/httpaf/.github rm -rf $(RELEASE)/src/vendor/httpaf/.github
rm -rf $(RELEASE)/src/vendor/httpaf/async rm -rf $(RELEASE)/src/vendor/httpaf/async
rm -rf $(RELEASE)/src/vendor/httpaf/benchmarks rm -rf $(RELEASE)/src/vendor/httpaf/benchmarks
rm -rf $(RELEASE)/src/vendor/httpaf/certificates rm -rf $(RELEASE)/src/vendor/httpaf/certificates
rm -rf $(RELEASE)/src/vendor/httpaf/eio
rm -rf $(RELEASE)/src/vendor/httpaf/examples rm -rf $(RELEASE)/src/vendor/httpaf/examples
rm -rf $(RELEASE)/src/vendor/httpaf/images rm -rf $(RELEASE)/src/vendor/httpaf/images
rm -rf $(RELEASE)/src/vendor/httpaf/lib_test rm -rf $(RELEASE)/src/vendor/httpaf/lib_test
@ -129,7 +120,6 @@ release : clean
rm -rf $(RELEASE)/src/vendor/h2/.github rm -rf $(RELEASE)/src/vendor/h2/.github
rm -rf $(RELEASE)/src/vendor/h2/async rm -rf $(RELEASE)/src/vendor/h2/async
rm -rf $(RELEASE)/src/vendor/h2/certificates rm -rf $(RELEASE)/src/vendor/h2/certificates
rm -rf $(RELEASE)/src/vendor/h2/eio
rm -rf $(RELEASE)/src/vendor/h2/examples rm -rf $(RELEASE)/src/vendor/h2/examples
rm -rf $(RELEASE)/src/vendor/h2/lib_test rm -rf $(RELEASE)/src/vendor/h2/lib_test
rm -rf $(RELEASE)/src/vendor/h2/mirage rm -rf $(RELEASE)/src/vendor/h2/mirage
@ -138,34 +128,24 @@ release : clean
rm -rf $(RELEASE)/src/vendor/h2/vegeta-plot.png rm -rf $(RELEASE)/src/vendor/h2/vegeta-plot.png
rm -rf $(RELEASE)/src/vendor/websocketaf/.github rm -rf $(RELEASE)/src/vendor/websocketaf/.github
rm -rf $(RELEASE)/src/vendor/websocketaf/async rm -rf $(RELEASE)/src/vendor/websocketaf/async
rm -rf $(RELEASE)/src/vendor/websocketaf/eio
rm -rf $(RELEASE)/src/vendor/websocketaf/examples rm -rf $(RELEASE)/src/vendor/websocketaf/examples
rm -rf $(RELEASE)/src/vendor/websocketaf/lib_test rm -rf $(RELEASE)/src/vendor/websocketaf/lib_test
rm -rf $(RELEASE)/src/vendor/websocketaf/mirage rm -rf $(RELEASE)/src/vendor/websocketaf/mirage
rm -rf $(RELEASE)/src/vendor/websocketaf/nix rm -rf $(RELEASE)/src/vendor/websocketaf/nix
rm -rf $(RELEASE)/src/vendor/paf
tar cf $(RELEASE).tar $(RELEASE) tar cf $(RELEASE).tar $(RELEASE)
ls -l $(RELEASE).tar ls -l $(RELEASE).tar
gzip -9 $(RELEASE).tar gzip -9 $(RELEASE).tar
mkdir -p _release mkdir -p _release
cp $(RELEASE).tar.gz _release cp $(RELEASE).tar.gz _release
(cd _release && tar xf $(RELEASE).tar.gz) (cd _release && tar xf $(RELEASE).tar.gz)
opam remove -y dream-pure dream-httpaf dream gluten httpaf h2 websocketaf paf
opam pin remove -y dream-pure dream-httpaf dream
opam pin add -y --no-action dream-pure.dev _release/$(RELEASE) --kind=path
opam pin add -y --no-action dream-httpaf.dev _release/$(RELEASE) --kind=path
opam pin add -y --no-action dream.dev _release/$(RELEASE) --kind=path
opam reinstall -y --verbose dream
@echo Run make release-finish to complete after killing the server
cd example/1-hello && dune exec --root . ./hello.exe || true
.PHONY : release-finish
release-finish :
opam remove -y dream-pure dream-httpaf dream opam remove -y dream-pure dream-httpaf dream
opam pin remove -y dream-pure dream-httpaf dream opam pin remove -y dream-pure dream-httpaf dream
sha256sum $(RELEASE).tar.gz opam pin add -y --no-action dream-pure _release/$(RELEASE) --kind=path
opam pin add -y --no-action dream-httpaf _release/$(RELEASE) --kind=path
opam pin add -y --no-action dream _release/$(RELEASE) --kind=path
opam reinstall -y --verbose dream
cd example/1-hello && dune exec --root . ./hello.exe || true
opam remove -y dream-pure dream-httpaf dream
opam pin remove -y dream-pure dream-httpaf dream
md5sum $(RELEASE).tar.gz
ls -l $(RELEASE).tar.gz ls -l $(RELEASE).tar.gz
.PHONY : release-clean
release-clean :
rm -rf $(RELEASE) $(RELEASE).tar.gz _release

View File

@ -12,6 +12,7 @@ Easy-to-use, feature-complete Web framework without boilerplate.
<p align="center"> <p align="center">
<a href="#quick-start">Quick Start</a> | <a href="#quick-start">Quick Start</a> |
<a href="http://dream.as">Playground</a> |
<a href="https://github.com/aantron/dream/tree/master/example#readme"> <a href="https://github.com/aantron/dream/tree/master/example#readme">
Tutorial</a> | Tutorial</a> |
<a href="https://aantron.github.io/dream/">Reference</a> <a href="https://aantron.github.io/dream/">Reference</a>
@ -77,24 +78,24 @@ Dream binary][one-binary], or use Dream in a subcommand. Dream tries to be as
functional as possible, touching global runtime state only lazily, when called functional as possible, touching global runtime state only lazily, when called
into. into.
[https]: https://github.com/aantron/dream/tree/master/example/l-https#folders-and-files [https]: https://github.com/aantron/dream/tree/master/example/l-https#files
[websocket]: https://github.com/aantron/dream/tree/master/example/k-websocket#folders-and-files [websocket]: https://github.com/aantron/dream/tree/master/example/k-websocket#files
[graphql]: https://github.com/aantron/dream/tree/master/example/w-graphql-subscription#folders-and-files [graphql]: https://github.com/aantron/dream/tree/master/example/w-graphql-subscription#files
[templates]: https://github.com/aantron/dream/tree/master/example/7-template#folders-and-files [templates]: https://github.com/aantron/dream/tree/master/example/7-template#files
[reason-templates]: https://github.com/aantron/dream/tree/master/example/r-template#folders-and-files [reason-templates]: https://github.com/aantron/dream/tree/master/example/r-template#files
[middleware]: https://github.com/aantron/dream/tree/master/example/2-middleware#folders-and-files [middleware]: https://github.com/aantron/dream/tree/master/example/2-middleware#files
[handler]: https://aantron.github.io/dream/#type-handler [handler]: https://aantron.github.io/dream/#type-handler
[routing]: https://github.com/aantron/dream/tree/master/example/3-router#folders-and-files [routing]: https://github.com/aantron/dream/tree/master/example/3-router#files
[cookies]: https://aantron.github.io/dream/#cookies [cookies]: https://aantron.github.io/dream/#cookies
[forms]: https://aantron.github.io/dream/#forms [forms]: https://aantron.github.io/dream/#forms
[sessions]: https://github.com/aantron/dream/tree/master/example/b-session#folders-and-files [sessions]: https://github.com/aantron/dream/tree/master/example/b-session#files
[back-ends]: https://aantron.github.io/dream/#back-ends [back-ends]: https://aantron.github.io/dream/#back-ends
[errors]: https://github.com/aantron/dream/tree/master/example/9-error#folders-and-files [errors]: https://github.com/aantron/dream/tree/master/example/9-error#files
[crypto]: https://aantron.github.io/dream/#cryptography [crypto]: https://aantron.github.io/dream/#cryptography
[logging]: https://github.com/aantron/dream/tree/master/example/2-middleware#folders-and-files [logging]: https://github.com/aantron/dream/tree/master/example/2-middleware#files
[melange]: https://github.com/aantron/dream/tree/master/example/r-fullstack-melange#folders-and-files [melange]: https://github.com/aantron/dream/tree/master/example/r-fullstack-melange#files
[rescript]: https://github.com/aantron/dream/tree/master/example/w-fullstack-rescript#folders-and-files [rescript]: https://github.com/aantron/dream/tree/master/example/w-fullstack-rescript#files
[jsoo]: https://github.com/aantron/dream/tree/master/example/w-fullstack-jsoo#folders-and-files [jsoo]: https://github.com/aantron/dream/tree/master/example/w-fullstack-jsoo#files
[types]: https://aantron.github.io/dream/#types [types]: https://aantron.github.io/dream/#types
[basic-read]: https://aantron.github.io/dream/#val-body [basic-read]: https://aantron.github.io/dream/#val-body
[streaming]: https://aantron.github.io/dream/#streaming [streaming]: https://aantron.github.io/dream/#streaming
@ -102,32 +103,33 @@ into.
[alpn]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation [alpn]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
[libs]: https://github.com/aantron/dream/tree/master/src [libs]: https://github.com/aantron/dream/tree/master/src
[deploy]: https://github.com/aantron/dream/tree/master/example#deploying [deploy]: https://github.com/aantron/dream/tree/master/example#deploying
[jsx]: https://github.com/aantron/dream/tree/master/example/r-tyxml#folders-and-files [jsx]: https://github.com/aantron/dream/tree/master/example/r-tyxml#files
[one-binary]: https://github.com/aantron/dream/tree/master/example/w-one-binary#folders-and-files [one-binary]: https://github.com/aantron/dream/tree/master/example/w-one-binary#files
<br> <br>
## Quick start ## Quick start
You can get Visit one of the first tutorials in the [online
[one](https://github.com/aantron/dream/tree/master/example/2-middleware#folders-and-files) playground][2-middleware-playground], and read its
of the first [tutorials][tutorial] and build it locally with: [docs](https://github.com/aantron/dream/tree/master/example/2-middleware#files).
You can get and build it locally with:
<pre><b>bash -c "$(curl -fsSL https://raw.githubusercontent.com/aantron/dream/master/example/quickstart.sh)"</b></pre> <pre><b>bash -c "$(curl -fsSL https://raw.githubusercontent.com/aantron/dream/master/example/quickstart.sh)"</b></pre>
Most of the other [examples][tutorial] are also loaded in the playground. See
the links on its [home page][playground].
## esy
Visit any of the [examples][tutorial], such as
[**`2-middleware`**][2-middleware], and re-create the files locally. The file
[`esy.json`](https://github.com/aantron/dream/blob/master/example/2-middleware/esy.json)
shows how to depend on Dream. All of the examples are installed by running `npx
esy`, and started with `npx esy start`.
### opam ### opam
Create a project directory with an optional local switch:
```
mkdir project
cd project
opam switch create . 5.1.0
eval $(opam env)
```
Install Dream:
``` ```
opam install dream opam install dream
``` ```
@ -139,18 +141,12 @@ After that, go to any of the [examples][tutorial], such as
dune exec ./middleware.exe dune exec ./middleware.exe
``` ```
[esy-example]: https://github.com/aantron/dream/tree/master/example/w-esy#folders-and-files [esy-example]: https://github.com/aantron/dream/tree/master/example/w-esy#files
[quickstart.sh]: https://github.com/aantron/dream/blob/master/example/quickstart.sh [quickstart.sh]: https://github.com/aantron/dream/blob/master/example/quickstart.sh
[esy]: https://esy.sh/ [esy]: https://esy.sh/
[2-middleware]: https://github.com/aantron/dream/tree/master/example/2-middleware#folders-and-files [2-middleware]: https://github.com/aantron/dream/tree/master/example/2-middleware#files
[playground]: http://dream.as
## esy [2-middleware-playground]: http://dream.as/2-middleware
Visit any of the [examples][tutorial], such as
[**`2-middleware`**][2-middleware], and re-create the files locally. The file
[`esy.json`](https://github.com/aantron/dream/blob/master/example/2-middleware/esy.json)
shows how to depend on Dream. All of the examples are installed by running `npx
esy`, and started with `npx esy start`.
<br> <br>
@ -169,18 +165,18 @@ esy`, and started with `npx esy start`.
small-to-medium deployments. small-to-medium deployments.
- [**Examples**][examples] &mdash; These cover various HTTP scenarios. - [**Examples**][examples] &mdash; These cover various HTTP scenarios.
- [**API reference**][api-main] - [**API reference**][api-main]
- [Watching][watch] and [live reloading][reload]. - [Watching][fswatch] and [live reloading][reload].
[tutorial]: https://github.com/aantron/dream/tree/master/example#readme [tutorial]: https://github.com/aantron/dream/tree/master/example#readme
[examples]: https://github.com/aantron/dream/tree/master/example#examples [examples]: https://github.com/aantron/dream/tree/master/example#examples
[1-hello]: https://github.com/aantron/dream/tree/master/example/1-hello#folders-and-files [1-hello]: https://github.com/aantron/dream/tree/master/example/1-hello#files
[r-hello]: https://github.com/aantron/dream/tree/master/example/r-hello#folders-and-files [r-hello]: https://github.com/aantron/dream/tree/master/example/r-hello#files
[reason-examples]: https://github.com/aantron/dream/tree/master/example#reason [reason-examples]: https://github.com/aantron/dream/tree/master/example#reason
[deploying]: https://github.com/aantron/dream/tree/master/example#deploying [deploying]: https://github.com/aantron/dream/tree/master/example#deploying
[api-main]: https://aantron.github.io/dream/#types [api-main]: https://aantron.github.io/dream/#types
[fullstack]: https://github.com/aantron/dream/tree/master/example#full-stack [fullstack]: https://github.com/aantron/dream/tree/master/example#full-stack
[watch]: https://github.com/aantron/dream/tree/master/example/w-watch#folders-and-files [fswatch]: https://github.com/aantron/dream/tree/master/example/w-fswatch#files
[reload]: https://github.com/aantron/dream/tree/master/example/w-live-reload#folders-and-files [reload]: https://github.com/aantron/dream/tree/master/example/w-live-reload#files
<br> <br>
@ -217,9 +213,8 @@ Apart from the [issues](https://github.com/aantron/dream/issues), good places
to discuss Dream are... to discuss Dream are...
- #dream on the [Reason Discord](https://discord.gg/2JTYRq2rYh). - #dream on the [Reason Discord](https://discord.gg/2JTYRq2rYh).
- #webdev on the [OCaml Discord](https://discord.gg/sx45hPkkWV). - #webdev on the [OCaml Discord](https://discord.gg/sx45hPkkWV)
- The [OCaml Discuss forum](https://discuss.ocaml.org/). - The [OCaml Discuss forum](https://discuss.ocaml.org/).
- The development stream on [Twitch](https://www.twitch.tv/antron_ML).
Highlight `@antron` to poke @aantron specifically. Highlight `@antron` to poke @aantron specifically.

View File

@ -19,13 +19,13 @@ Contributions are very welcome. This includes not only code PRs, but also:
To get the version of Dream installed in a project that uses it, run To get the version of Dream installed in a project that uses it, run
``` ```
opam list dream npx esy ls-builds
``` ```
or or
``` ```
npx esy ls-builds opam list dream
``` ```
<br> <br>
@ -40,26 +40,25 @@ cd dream
``` ```
Note: the clone *must* be `--recursive`, because Dream several dependencies Note: the clone *must* be `--recursive`, because Dream several dependencies
vendored as vendored as [submodules](https://github.com/aantron/dream/tree/master/src/vendor)!
[submodules](https://github.com/aantron/dream/tree/master/src/vendor).
Later, you'll need to fork the repository on GitHub, and add your fork as a Later, you'll need to fork the repository on GitHub, and add your fork as a
remote: remote:
``` ```
git remote add fork git@github.com:my-github-name/dream.git git remote add fork git@github.com/my-github-name/dream.git
``` ```
Install Dream's dependencies: Install Dream's dependencies:
``` ```
make deps opam install --deps-only ./dream.opam --with-test
``` ```
If you don't have an opam switch ready, first create one with If you don't have an opam switch ready, first create one with
``` ```
opam switch create . 4.14.1 --no-install opam switch create . 4.12.0 --no-install
``` ```
You can now add some code that will exercise your change, so you can test it as You can now add some code that will exercise your change, so you can test it as
@ -72,10 +71,7 @@ you work. There are two main places for this:
To run tests from a single directory, for example `test/expect/pure`, run To run tests from a single directory, for example `test/expect/pure`, run
`make test TEST=test/expect/pure`. `make test TEST=test/expect/pure`.
2. The tests can also be run in watch mode using `make test-watch`. This is not 2. The examples in `example/`. I often test changes by modifying an example that
compatible with coverage reports at the moment.
3. The examples in `example/`. I often test changes by modifying an example that
is almost on topic for the code I'm changing, and then not committing the is almost on topic for the code I'm changing, and then not committing the
example. In some cases, though, it's easiest to fork or write a new example example. In some cases, though, it's easiest to fork or write a new example
for some new code, and commit it. New examples greatly appreciated! To build for some new code, and commit it. New examples greatly appreciated! To build
@ -108,10 +104,10 @@ If you want to work again later, be sure to use `--recurse-submodules` during
git pull --recurse-submodules git pull --recurse-submodules
``` ```
**Note:** Please don't force-push into a PR &mdash; it makes incremental review Please don't force-push into a PR &mdash; it makes incremental review very
very difficult, and we will squash-merge most PRs anyway! difficult, and we will squash-merge most PRs anyway!
**Note:** Please don't resolve conversations in PRs. Reviewers use resolving Also please don't resolve conversations in PRs. Reviewers use resolving
conversations to keep track of what has been addressed. conversations to keep track of what has been addressed.
<br> <br>
@ -150,8 +146,8 @@ To build the docs, go to
make deps make deps
``` ```
This will install npm and opam packages. In particular, the site currently This will install npm packages and opam packages (some of which are pinned to
requires odoc 2.0.2, Soupault, and a specific version of Highlight.js. git commits).
After that, back in the project root, After that, back in the project root,
@ -166,11 +162,3 @@ make docs
``` ```
to build the docs locally. They are output to `docs/web/build/index.html`. to build the docs locally. They are output to `docs/web/build/index.html`.
You can also use
```
make docs-watch
```
to rebuild the docs automatically as you write them.

View File

@ -39,8 +39,8 @@ b {
<body> <body>
<pre><code><b>$ cd example/2-middleware</b> <pre><code><b>$ cd example/2-middleware</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./middleware.exe</b> <b>$ npx esy start</b>
<span class="dim">08.03.21 22:19:21.126</span> Running at http://localhost:8080 <span class="dim">08.03.21 22:19:21.126</span> Running at http://localhost:8080
<span class="dim">08.03.21 22:19:21.126</span> Type Ctrl+C to stop <span class="dim">08.03.21 22:19:21.126</span> Type Ctrl+C to stop
<span class="dim">08.03.21 22:19:24.927</span> dream.log <span class="info">INFO</span> <span class="odd">REQ 1</span> GET / 127.0.0.1:58549 Mozilla/5.0 ... <span class="dim">08.03.21 22:19:24.927</span> dream.log <span class="info">INFO</span> <span class="odd">REQ 1</span> GET / 127.0.0.1:58549 Mozilla/5.0 ...

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,4 +1,4 @@
ROOT := ../.. ROOT := ../..
ODOC := odoc/default/_doc/_html ODOC := odoc/default/_doc/_html
.PHONY : build .PHONY : build

View File

@ -26,6 +26,6 @@ lockfile.
Useful links: Useful links:
- OCaml [*Syntax of documentation comments*](https://v2.ocaml.org/manual/ocamldoc.html#ss:ocamldoc-syntax) - OCaml [*Syntax of documentation comments*](http://caml.inria.fr/pub/docs/manual-ocaml/ocamldoc.html#ss:ocamldoc-syntax)
- Lambda Soup [*Module Soup*](https://aantron.github.io/lambdasoup/) - Lambda Soup [*Module Soup*](https://aantron.github.io/lambdasoup/)
- Soupault [*Tips and tricks*](https://soupault.app/tips-and-tricks/) - Soupault [*Tips and tricks*](https://soupault.app/tips-and-tricks/)

View File

@ -13,35 +13,8 @@ let if_expected expected test f =
f () f ()
else begin else begin
Soup.write_file "actual" actual; Soup.write_file "actual" actual;
prerr_newline (); Printf.ksprintf failwith "Mismatch; wrote %s"
prerr_newline (); (Filename.concat (Sys.getcwd ()) "actual")
prerr_endline "Mismatch with expected initial HTML content.";
prerr_newline ();
prerr_endline
"The Dream docs build rewrites HTML emitted by odoc to make it neater.";
prerr_endline
"Each rewritten tag has an expected initial content for sanity checking.";
prerr_endline "The actual found content has been written to";
prerr_newline ();
prerr_endline (" " ^ (Filename.concat (Sys.getcwd ()) "actual"));
prerr_newline ();
begin match String.split_on_char '\n' actual with
| [] -> ()
| first_line::_ ->
prerr_endline "Hint:";
prerr_newline ();
prerr_endline (" " ^ first_line);
prerr_newline ()
end;
prerr_endline "Hint: make sure odoc 2.0.2 is installed.";
prerr_endline
"Other versions of odoc generate markup that doesn't match the expected.";
prerr_newline ();
Printf.ksprintf failwith "Mismatch"
end end
let add_backing_lines soup = let add_backing_lines soup =

View File

@ -1,7 +1,7 @@
(executable (executable
(name index) (name index)
(modules index) (modules index)
(libraries common lambdasoup str)) (libraries common lambdasoup))
(library (library
(name common) (name common)

File diff suppressed because it is too large Load Diff

View File

@ -60,49 +60,6 @@
src: url('tenor-sans-v12-latin-regular.woff2') format('woff2'); src: url('tenor-sans-v12-latin-regular.woff2') format('woff2');
} }
/* Theme */
/* Dark theme (default) */
:root, body:not([data-theme="light"]) {
--bg-color: #131618;
--text-color: #c9d1d9;
--code-bg-color: #2c333b;
--border-color: #282828;
--link-color: #8dc5ff;
--external-link-color: #5d7fcd;
--anchor-color: #bfcdea;
--of-color: #bec5cd;
--target-backing-color: #390022;
--hljs-keyword-color: #ff6c9b;
--hljs-identifier-color: #70df5c;
--hljs-tag-color: #c28eff;
--hljs-string-color: #e3db7a;
}
/* Light theme */
:root, body[data-theme="light"] {
--bg-color: #f5f7fa;
--text-color: #1f2937;
--code-bg-color: #eef1f6;
--header-bg-color: #f5f7fa;
--border-color: #e0e0e0;
--link-color: #1c7ed6;
--external-link-color: #1d4ed8;
--anchor-color: #888;
--of-color: #6b7280;
--target-backing-color: #f7f6f3;
--hljs-keyword-color: #d94879;
--hljs-identifier-color: #22863a;
--hljs-tag-color: #6f42c1;
--hljs-string-color: #b94e48;
}
body { body {
font-family: Lato, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue, Helvetica, Arial, sans-serif; font-family: Lato, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 16px; font-size: 16px;
@ -144,15 +101,15 @@ h6 {
/* Colors and presentation styles. */ /* Colors and presentation styles. */
body { body {
background-color: var(--bg-color); background-color: #131618;
color: var(--text-color); color: #c9d1d9;
} }
.odoc-content pre { .odoc-content pre {
background-color: var(--code-bg-color); background-color: #1a1f26;
margin-left: 1em; margin-left: 1em;
margin-right: 1em; margin-right: 1em;
border: 1px solid var(--border-color); border: 1px solid #111;
} }
.odoc-content .spec > pre { .odoc-content .spec > pre {
background: none; background: none;
@ -162,7 +119,7 @@ body {
.odoc-content code { .odoc-content code {
/* color: #ddd; */ /* color: #ddd; */
background-color: var(--code-bg-color); background-color: #2c333b;
padding: 0 5px; padding: 0 5px;
margin: 0 1px; margin: 0 1px;
white-space: nowrap; white-space: nowrap;
@ -193,40 +150,17 @@ body {
} */ } */
header { header {
background-color: var(--bg-color); background-color: #131618;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid #282828;
} }
header .topmost { header .topmost {
/* background-color: #0f131a; */ /* background-color: #0f131a; */
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid #282828;
} }
.topmost .toolbar { h1 {
float: right; text-shadow: -2px 2px black;
}
.topmost .toggle-theme-btn {
all: unset;
position: relative;
}
.topmost .toggle-theme-btn::before {
content: "\F185"; /* sun */
position: absolute;
left: calc(0% - 16px + -8px);
top: calc(0% + 4px);
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
font-family: FontAwesome, FontAwesomeBrands;
font-size: 15px;
}
body:not([data-theme="light"]) .topmost .toggle-theme-btn::before {
content: "\F186"; /* moon */
} }
header pre { header pre {
@ -257,7 +191,7 @@ footer {
} }
:target .backing { :target .backing {
background-color: var(--target-backing-color); background-color: #390022;
} }
nav ~ * a[href="#builtin"], nav ~ * a[href="#builtin"],
@ -271,16 +205,16 @@ nav ~ * a[href="#templates"] {
font-style: italic; font-style: italic;
} }
header a[href*=example], .spec-doc a[href*=example] { a[href*=example] {
font-weight: bold; font-weight: bold;
} }
header a[href^=http]::after, .spec-doc a[href^=http]::after { a[href^=http]::after {
content: "\f35d"; content: "\f35d";
font-family: FontAwesome; font-family: FontAwesome;
font-size: 10px; font-size: 10px;
line-height: 18px; line-height: 18px;
color: var(--external-link-color); color: #5d7fcd;
position: relative; position: relative;
top: -1px; top: -1px;
margin-left: 2px; margin-left: 2px;
@ -291,7 +225,7 @@ header a[href^=http]::after, .spec-doc a[href^=http]::after {
} }
a, a:visited, a:active { a, a:visited, a:active {
color: var(--link-color); color: #8dc5ff;
text-decoration: none; text-decoration: none;
} }
@ -300,27 +234,27 @@ a:hover {
} }
.odoc-content a > code { .odoc-content a > code {
color: var(--link-color); color: #8dc5ff;
} }
.hljs-module-access, .hljs-keyword, .keyword { .hljs-module-access, .hljs-keyword, .keyword {
color: var(--hljs-keyword-color); color: #ff6c9b;
} }
.hljs-identifier, .hljs-literal, .hljs-type { .hljs-identifier, .hljs-literal, .hljs-type {
color: var(--hljs-identifier-color); color: #70df5c;
} }
.hljs-tag { .hljs-tag {
color: var(--hljs-tag-color); color: #c28eff;
} }
.hljs-string { .hljs-string {
color: var(--hljs-string-color); color: #e3db7a;
} }
.of { .of {
color: var(--of-color); color: #bec5cd;
} }
.topmost ul { .topmost ul {
@ -434,6 +368,7 @@ p + .odoc-spec {
#val-origin_referrer_check + .spec-doc li + li, #val-origin_referrer_check + .spec-doc li + li,
#val-form + .spec-doc li + li, #val-form + .spec-doc li + li,
#type-part + .spec-doc li + li, #type-part + .spec-doc li + li,
#type-upload_event + .spec-doc li + li,
#val-upload + .spec-doc li + li, #val-upload + .spec-doc li + li,
#val-static + .spec-doc li + li, #val-static + .spec-doc li + li,
#val-from_path + .spec-doc li + li { #val-from_path + .spec-doc li + li {
@ -472,7 +407,7 @@ ul ul li {
height: 100%; height: 100%;
width: 43rem; width: 43rem;
/* background-color: #262626; */ /* background-color: #262626; */
border-right: 1px solid var(--border-color); border-right: 1px solid #282828;
} }
h2, h2 ~ :not(.odoc-spec):not(nav), footer { h2, h2 ~ :not(.odoc-spec):not(nav), footer {
@ -647,8 +582,8 @@ h2:first-of-type {
overflow-y: scroll; overflow-y: scroll;
scrollbar-width: none; scrollbar-width: none;
line-height: 30px; line-height: 30px;
border-right: 1px solid var(--border-color); border-right: 1px solid #262626;
background-color: var(--bg-color); background-color: #131618;
/* color: #ddd; */ /* color: #ddd; */
} }
.odoc-toc::-webkit-scrollbar { .odoc-toc::-webkit-scrollbar {
@ -739,7 +674,7 @@ h2 > .anchor, h3 > .anchor {
font-family: FontAwesome; font-family: FontAwesome;
font-size: 10px; font-size: 10px;
font-style: oblique; font-style: oblique;
color: var(--anchor-color); color: #bfcdea;
position: relative; position: relative;
top: -1.75px; top: -1.75px;
left: -4px; left: -4px;

View File

@ -4,7 +4,8 @@
// Copyright 2021 Anton Bachin *) // Copyright 2021 Anton Bachin *)
/* Scrolling */
console.log("foo");
function current_section() { function current_section() {
var threshold = window.innerHeight / 2; var threshold = window.innerHeight / 2;
@ -48,38 +49,3 @@ function scroll() {
}; };
window.onscroll = scroll; window.onscroll = scroll;
/* Theme mode */
var THEME_MODE_KEY = "dream-theme"
function apply_theme(theme) {
if (theme === "light") {
document.body.setAttribute("data-theme", "light");
} else {
document.body.removeAttribute("data-theme");
}
}
function toggle_theme() {
var current_theme = localStorage.getItem(THEME_MODE_KEY);
var new_theme = current_theme === "dark" ? "light" : "dark";
localStorage.setItem(THEME_MODE_KEY, new_theme);
apply_theme(new_theme);
}
function init_theme() {
var default_theme = "dark";
var stored_theme = localStorage.getItem(THEME_MODE_KEY) || default_theme;
apply_theme(stored_theme);
}
function prepare_button() {
var theme_toggle_button = document.querySelector(".toggle-theme-btn");
if (theme_toggle_button) {
theme_toggle_button.addEventListener("click", toggle_theme);
}
}
document.addEventListener("DOMContentLoaded", prepare_button);

View File

@ -24,10 +24,6 @@
</head> </head>
<body class="index"> <body class="index">
<script>
init_theme();
</script>
<header> <header>
<div class="topmost"> <div class="topmost">
<div class="titles"> <div class="titles">
@ -36,15 +32,11 @@ init_theme();
</div> </div>
<ul> <ul>
<li><code>1.0.0~alpha7</code></li> <li><code>1.0.0~alpha4</code></li>
<li><code>opam install dream</code></li> <li><code>opam install dream</code></li>
<li><a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream">GitHub</a></li> <li><a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream">GitHub</a></li>
<li><a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/edit/master/src/dream.mli">Edit these docs</a></li> <li><a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/blob/master/src/dream.mli">Edit these docs</a></li>
</ul> </ul>
<div class="toolbar">
<button class="toggle-theme-btn">&nbsp;</button>
</div>
</div> </div>
<pre><span class="keyword">let</span> hello who = <pre><span class="keyword">let</span> hello who =
@ -60,7 +52,8 @@ init_theme();
@@ <span class="hljs-type">Dream</span>.router <span class="hljs-string">[</span> @@ <span class="hljs-type">Dream</span>.router <span class="hljs-string">[</span>
<span class="hljs-type">Dream</span>.get <span class="hljs-string">"/"</span> (<span class="keyword">fun</span> _ -> <span class="hljs-type">Dream</span>.get <span class="hljs-string">"/"</span> (<span class="keyword">fun</span> _ ->
<span class="hljs-type">Dream</span>.html (hello <span class="hljs-string">"world"</span>)); <span class="hljs-type">Dream</span>.html (hello <span class="hljs-string">"world"</span>));
<span class="hljs-string">]</span></pre> <span class="hljs-string">]</span>
@@ <span class="hljs-type">Dream</span>.not_found</pre>
<!-- Send TLS link to HTTPS example. --> <!-- Send TLS link to HTTPS example. -->
@ -123,13 +116,18 @@ init_theme();
<li> <li>
A <a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example#readme"> A <a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example#readme">
Tutorial</a> &mdash; get started at Tutorial</a> &mdash; get started at
<a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example/1-hello#folders-and-files"> <a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example/1-hello#files">
<code>1-hello</code></a>! <code>1-hello</code></a>!
</li> </li>
<li> <li>
Many <a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example#reason"> Many <a target="_blank" rel="noreferrer noopener" href="https://github.com/aantron/dream/tree/master/example#reason">
Examples</a>, covering all kinds of scenarios. Examples</a>, covering all kinds of scenarios.
</li> </li>
<li>
An online <a target="_blank" rel="noreferrer noopener" href="http://dream.as/">
<b>Playground</b></a>, where you can try Dream without installing
anything!
</li>
</ul> </ul>
</div> </div>
</header> </header>
@ -138,7 +136,7 @@ init_theme();
<div id="api"></div> <div id="api"></div>
<footer> <footer>
Copyright © 2021-2024 Anton Bachin. Copyright © 2021 Anton Bachin.
<br> <br>
Released under the MIT license. See Released under the MIT license. See
<a href="https://github.com/aantron/dream/blob/master/LICENSE.md"> <a href="https://github.com/aantron/dream/blob/master/LICENSE.md">

View File

@ -1,9 +1,11 @@
opam-version: "2.0" opam-version: "2.0"
synopsis: "Dream docs"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"
depends: [ depends: [
"lambdasoup" "lambdasoup"
"odoc" {= "2.0.2"} "odoc"
"soupault" {>= "2.5.0"} "soupault" {>= "2.5.0"}
] ]
pin-depends: [
["odoc.2.0.0~master" "git+https://github.com/aantron/odoc.git#dbb37e20717985edfd8f734e00b9ab6f705d81a4"]
]

View File

@ -1,78 +1,71 @@
opam-version: "2.0" opam-version: "2.0"
depends: [ depends: [
"ISO8601" {= "0.2.6"}
"astring" {= "0.8.5"} "astring" {= "0.8.5"}
"base-bigarray" {= "base"} "base-bigarray" {= "base"}
"base-bytes" {= "base"} "base-bytes" {= "base"}
"base-threads" {= "base"} "base-threads" {= "base"}
"base-unix" {= "base"} "base-unix" {= "base"}
"base64" {= "3.5.1"} "base64" {= "3.5.0"}
"bigarray-compat" {= "1.1.0"} "bigarray-compat" {= "1.0.0"}
"bos" {= "0.2.1"} "cmdliner" {= "1.0.4"}
"camlp-streams" {= "5.0.1"} "conf-libev" {= "4-11"}
"camomile" {= "1.0.2"} "containers" {= "3.3"}
"cmdliner" {= "1.1.1"} "cppo" {= "1.6.7"}
"conf-libev" {= "4-12"}
"conf-pkg-config" {= "2"}
"containers" {= "3.11"}
"cppo" {= "1.6.9"}
"csexp" {= "1.5.1"} "csexp" {= "1.5.1"}
"cstruct" {= "6.1.1"} "cstruct" {= "6.0.0"}
"ctypes" {= "0.20.1"} "dune" {= "2.8.5"}
"digestif" {= "1.1.3"} "dune-configurator" {= "2.8.5"}
"dune" {= "3.7.0"} "ezjsonm" {= "1.2.0"}
"dune-configurator" {= "3.7.0"} "fileutils" {= "0.6.3"}
"either" {= "1.0.0"} "fmt" {= "0.8.9"}
"eqaf" {= "0.9"}
"ezjsonm" {= "1.3.0"}
"fileutils" {= "0.6.4"}
"fmt" {= "0.9.0"}
"fpath" {= "0.7.3"} "fpath" {= "0.7.3"}
"hex" {= "1.5.0"} "hex" {= "1.4.0"}
"integers" {= "0.7.0"} "jingoo" {= "1.4.3"}
"jingoo" {= "1.4.4"} "jsonm" {= "1.0.1"}
"jsonm" {= "1.0.2"} "lambdasoup" {= "0.7.2"}
"lambdasoup" {= "1.0.0"}
"logs" {= "0.7.0"} "logs" {= "0.7.0"}
"lua-ml" {= "0.9.4"} "lua-ml" {= "0.9.2"}
"lwt" {= "5.6.1"} "lwt" {= "5.4.0"}
"markup" {= "1.0.3"} "markup" {= "1.0.0-1"}
"menhir" {= "20211128"} "menhir" {= "20210310"}
"menhirLib" {= "20211128"} "menhirLib" {= "20210310"}
"menhirSdk" {= "20211128"} "menhirSdk" {= "20210310"}
"ocaml" {= "4.14.1"} "mmap" {= "1.1.0"}
"ocaml-base-compiler" {= "4.14.1"} "ocaml" {= "4.12.0"}
"ocaml-compiler-libs" {= "v0.12.4"} "ocaml-base-compiler" {= "4.12.0"}
"ocaml-compiler-libs" {= "v0.12.3"}
"ocaml-config" {= "2"} "ocaml-config" {= "2"}
"ocaml-migrate-parsetree" {= "2.4.0"} "ocaml-migrate-parsetree" {= "2.1.0"}
"ocaml-options-vanilla" {= "1"} "ocaml-options-vanilla" {= "1"}
"ocamlbuild" {= "0.14.2"} "ocaml-syntax-shims" {= "1.0.0"}
"ocamlfind" {= "1.9.6"} "ocamlbuild" {= "0.14.0"}
"ocplib-endian" {= "1.2"} "ocamlfind" {= "1.9.1"}
"ocplib-endian" {= "1.1"}
"odate" {= "0.6"} "odate" {= "0.6"}
"odoc" {= "2.0.2"} "odoc" {= "2.0.0~master"}
"odoc-parser" {= "1.0.1"}
"otoml" {= "1.0.4"}
"ppx_derivers" {= "1.2.1"} "ppx_derivers" {= "1.2.1"}
"ppx_deriving" {= "5.2.1"} "ppx_deriving" {= "5.2.1"}
"ppxlib" {= "0.25.1"} "ppxlib" {= "0.22.0"}
"re" {= "1.10.4"} "re" {= "1.9.0"}
"result" {= "1.5"} "result" {= "1.5"}
"rresult" {= "0.7.0"}
"seq" {= "base"} "seq" {= "base"}
"sexplib0" {= "v0.15.1"} "sexplib0" {= "v0.14.0"}
"soupault" {= "4.4.0"} "soupault" {= "2.5.0"}
"spelll" {= "0.4"} "spelll" {= "0.3"}
"stdlib-shims" {= "0.3.0"} "stdlib-shims" {= "0.3.0"}
"topkg" {= "1.0.7"} "stringext" {= "1.6.0"}
"tsort" {= "2.1.0"} "toml" {= "7.0.0"}
"tyxml" {= "4.5.0"} "topkg" {= "1.0.3"}
"tsort" {= "2.0.0"}
"tyxml" {= "4.4.0"}
"uchar" {= "0.0.2"} "uchar" {= "0.0.2"}
"uucp" {= "15.0.0"} "uucp" {= "13.0.0"}
"uutf" {= "1.0.3"} "uutf" {= "1.0.2"}
"yaml" {= "3.1.0"} ]
pin-depends: [
["odoc.2.0.0~master" "git+https://github.com/aantron/odoc.git#dbb37e20717985edfd8f734e00b9ab6f705d81a4"]
] ]
name: "web" name: "web"
version: "~dev" version: "dev"
synopsis: "Dream docs"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -15,18 +15,31 @@ maintainer: "Anton Bachin <antonbachin@yahoo.com>"
depends: [ depends: [
"dream-pure" "dream-pure"
"dune" {>= "2.7.0"} # --instrument-with. "dune" {>= "2.7.0"} # --instrument-with.
"gluten"
"gluten-lwt-unix"
"h2" {< "0.13.0"}
"h2-lwt-unix"
"httpun" {< "0.2.0"}
"httpun-lwt-unix"
"httpun-ws"
"lwt" "lwt"
"lwt_ppx" {>= "1.2.2"} "lwt_ppx" {>= "1.2.2"}
"lwt_ssl" "lwt_ssl"
"ocaml" {>= "4.08.0"} "ocaml" {>= "4.08.0"}
"ssl" {>= "0.5.8"} # Ssl.get_negotiated_alpn_protocol. "ssl" {>= "0.5.8"} # Ssl.get_negotiated_alpn_protocol.
# Currently vendored.
# "gluten"
# "gluten-lwt-unix"
# "httpaf"
# "httpaf-lwt-unix"
# "h2"
# "h2-lwt-unix"
# "hpack"
# "websocketaf"
# Dependencies of vendored packages.
"angstrom" {>= "0.14.0"}
"base64" {>= "3.0.0"}
"bigstringaf" {>= "0.5.0"} # h2.
"digestif" {>= "0.7.2"} # websocket/af, sha1, default implementation.
"faraday" {>= "0.6.1"}
"faraday-lwt-unix"
"psq" # h2.
"result" # http/af, websocket/af.
] ]
build: [ build: [

View File

@ -29,7 +29,7 @@ Within this model, Dream adds:
- Helpers for Web formats, such as Base64url, and a modern cipher. - Helpers for Web formats, such as Base64url, and a modern cipher.
Because of the simple programming model, everything is optional and Because of the simple programming model, everything is optional and
composable. It is trivially possible to strip Dream down to just a composable. It is trivailly possible to strip Dream down to just a
bare driver of the various HTTP protocols. bare driver of the various HTTP protocols.
Dream is presented as a single module, whose API is documented on one Dream is presented as a single module, whose API is documented on one
@ -47,24 +47,16 @@ author: "Anton Bachin <antonbachin@yahoo.com>"
maintainer: "Anton Bachin <antonbachin@yahoo.com>" maintainer: "Anton Bachin <antonbachin@yahoo.com>"
depends: [ depends: [
"dream"
"dune" {>= "2.7.0"}
"dream"
"bigarray-compat" "bigarray-compat"
"bigstringaf" "bigstringaf"
"digestif" {>= "1.0.0"} "digestif" {>= "1.0.0"}
"dream"
"dream-httpaf"
"dream-pure"
"dune" {>= "2.7.0"}
"duration"
"emile" {>= "1.1"}
"ke" {>= "0.4"} # paf.
"letsencrypt" {>= "0.3.0"}
"lwt" "lwt"
"lwt_ppx" {>= "1.2.2"}
"mimic" {>= "0.0.5"}
"mirage-time"
"rresult"
"tcpip"
"tls-mirage" "tls-mirage"
"lwt_ppx" {>= "1.2.2"}
"letsencrypt" {>= "0.3.0"}
] ]
build: [ build: [

View File

@ -29,7 +29,7 @@ Within this model, Dream adds:
- Helpers for Web formats, such as Base64url, and a modern cipher. - Helpers for Web formats, such as Base64url, and a modern cipher.
Because of the simple programming model, everything is optional and Because of the simple programming model, everything is optional and
composable. It is trivially possible to strip Dream down to just a composable. It is trivailly possible to strip Dream down to just a
bare driver of the various HTTP protocols. bare driver of the various HTTP protocols.
Dream is presented as a single module, whose API is documented on one Dream is presented as a single module, whose API is documented on one
@ -50,28 +50,24 @@ depends: [
"base-unix" "base-unix"
"bigarray-compat" "bigarray-compat"
"camlp-streams" "camlp-streams"
"caqti" {>= "2.0.0"} "caqti" {>= "1.6.0"} # https://github.com/aantron/dream/issues/44.
"caqti-lwt" {>= "2.0.0"} "caqti-lwt"
("conf-libev" {os != "win32"} | "ocaml" {os = "win32"}) "conf-libev" {os != "win32"}
"cstruct" {>= "6.0.0"} "cstruct" {>= "6.0.0"}
"digestif" {>= "0.7"} # to_raw_string. "dream-httpaf"
"dream-httpaf" {>= "1.0.0~alpha4"} "dream-pure"
"dream-pure" {>= "1.0.0~alpha2"}
"dune" {>= "2.7.0"} # --instrument-with. "dune" {>= "2.7.0"} # --instrument-with.
"fmt" {>= "0.8.7"} # `Italic. "fmt" {>= "0.8.7"} # `Italic.
"graphql_parser" "graphql_parser"
"graphql-lwt" "graphql-lwt"
"lambdasoup" {>= "0.6.1"}
"lwt" "lwt"
"lwt_ppx" {>= "1.2.2"} "lwt_ppx" {>= "1.2.2"}
"lwt_ssl" "lwt_ssl"
"logs" {>= "0.5.0"} "logs" {>= "0.5.0"}
"magic-mime" "magic-mime"
"markup" {>= "1.0.2"}
"mirage-clock" {>= "3.0.0"} # now_d_ps : unit -> int * int64. "mirage-clock" {>= "3.0.0"} # now_d_ps : unit -> int * int64.
"mirage-crypto" {>= "1.0.0"} "mirage-crypto" {>= "0.8.1"} # AES-256-GCM.
"mirage-crypto-rng" {>= "1.0.0"} "mirage-crypto-rng" {>= "0.8.0"} # Signature of initialize.
"mirage-crypto-rng-lwt"
"multipart_form" {>= "0.4.0"} "multipart_form" {>= "0.4.0"}
"multipart_form-lwt" "multipart_form-lwt"
"ocaml" {>= "4.08.0"} "ocaml" {>= "4.08.0"}
@ -86,14 +82,13 @@ depends: [
"caqti-driver-postgresql" {with-test} "caqti-driver-postgresql" {with-test}
"caqti-driver-sqlite3" {with-test} "caqti-driver-sqlite3" {with-test}
"crunch" {with-test} "crunch" {with-test}
"html_of_jsx" {with-test} "lambdasoup" {with-test}
"js_of_ocaml" {with-test} "ppx_expect" {with-test}
"js_of_ocaml-ppx" {with-test}
"ppx_expect" {with-test & >= "v0.15.0" & < "v0.17.0"} # Breaking changes.
"ppx_yojson_conv" {with-test} "ppx_yojson_conv" {with-test}
"reason" {with-test} "reason" {with-test}
"tyxml" {with-test & >= "4.5.0"} "tyxml" {with-test & >= "4.5.0"}
"tyxml-jsx" {with-test} "tyxml-jsx" {with-test & >= "4.5.0"}
"tyxml-ppx" {with-test & >= "4.5.0"}
] ]
build: [ build: [

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -12,25 +12,26 @@ let () =
<br> <br>
It's the minimal Dream server. It responds to all requests with the same text. It's the absolute minimum Dream server. It responds to all requests with the
At startup, Dream prints a message to the log, telling you where to point your same text. At startup, Dream prints a message to the log, telling you where to
browser. Your terminal probably makes the link clickable. point your browser. Your terminal probably makes the link clickable.
<pre><code><b>$ cd example/1-hello</b> <pre><code><b>$ cd example/1-hello</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./hello.exe</b> <b>$ npx esy start</b>
08.03.21 21:17:21.471 Running at http://localhost:8080 08.03.21 21:17:21.471 Running at http://localhost:8080
08.03.21 21:17:21.471 Type Ctrl+C to stop 08.03.21 21:17:21.471 Type Ctrl+C to stop
</code></pre> </code></pre>
If you go to [http://localhost:8080](http://localhost:8080), you will, of If you go to [http://localhost:8080](http://localhost:8080), you will, of
course, see `Good morning, world!`. course, see `Good morning, world!`. You can also try it in the [Dream
Playground](http://dream.as/1-hello).
<br> <br>
If you'd like to copy out the server binary, you can do it like this: If you'd like to copy out the server binary, you can do it like this:
<pre><code><b>$ cp _build/default/hello.exe . <pre><code><b>$ npx esy cp '#{self.target_dir}/default/hello.exe' .
</b></code></pre> </b></code></pre>
The name will change as you go through the tutorial examples. It's always the The name will change as you go through the tutorial examples. It's always the
@ -40,17 +41,19 @@ name of the `.ml` file, but with `.ml` changed to `.exe`.
**Next steps:** **Next steps:**
- The next example, [**`2-middleware`**](../2-middleware#folders-and-files), adds a logger - The next example, [**`2-middleware`**](../2-middleware#files), adds a logger
to the app. to the app.
- [**`3-router`**](../3-router#folders-and-files) sends requests to different handlers, - [**`3-router`**](../3-router#files) sends requests to different handlers,
depending on their path. depending on their path.
<br> <br>
**See also:** **See also:**
- [**`r-hello`**](../r-hello#folders-and-files) is a Reason syntax version of this example. - [**`r-hello`**](../r-hello#files) is a Reason syntax version of this example.
- [**`w-watch`**](../w-watch#folders-and-files) sets up a development watcher. - [**`w-esy`**](../w-esy#files) gives more detail on the [esy](https://esy.sh/)
packaging.
- [**`w-fswatch`**](../w-fswatch#files) sets up a primitive development watcher.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name hello) (name hello)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -1,11 +1,8 @@
{ {
"dependencies": { "dependencies": {
"@opam/conf-libssl": "3", "@opam/dream": "1.0.0~alpha3",
"@opam/dream": "1.0.0~alpha5", "@opam/dune": "^2.0",
"@opam/dune": "^3.0", "ocaml": "4.12.x"
"@opam/reason": "^3.8.0",
"@opam/html_of_jsx": "0.0.4",
"ocaml": "^4.14.0"
}, },
"devDependencies": { "devDependencies": {
"@opam/ocaml-lsp-server": "*" "@opam/ocaml-lsp-server": "*"
@ -15,6 +12,6 @@
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1" "esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
}, },
"scripts": { "scripts": {
"start": "dune exec --root . ./html_of_jsx.exe" "start": "dune exec --root . ./hello.exe"
} }
} }

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -4,7 +4,7 @@
*Middleware* is just functions that take handlers and wrap them, producing *Middleware* is just functions that take handlers and wrap them, producing
bigger handlers that do a little bit more. This example takes the handler from bigger handlers that do a little bit more. This example takes the handler from
[**`1-hello`**](../1-hello#folders-and-files) and wraps it in one of the most useful [**`1-hello`**](../1-hello#files) and wraps it in one of the most useful
middlewares, the [*logger*](https://aantron.github.io/dream/#val-logger): middlewares, the [*logger*](https://aantron.github.io/dream/#val-logger):
```ocaml ```ocaml
@ -19,7 +19,7 @@ let () =
However, as you can see, the more middlewares we stack on top of each other However, as you can see, the more middlewares we stack on top of each other
like this, the more parentheses and indentation we will end up with! To keep like this, the more parentheses and indentation we will end up with! To keep
the code tidy, we use `@@`, the the code tidy, we use `@@`, the
[standard OCaml operator](https://v2.ocaml.org/api/Stdlib.html#VAL(@@)) for calling functions without parentheses. So, the [actual [standard OCaml operator](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Stdlib.html#VAL(@@)) for calling functions without parentheses. So, the [actual
code](https://github.com/aantron/dream/blob/master/example/2-middleware/middleware.ml) code](https://github.com/aantron/dream/blob/master/example/2-middleware/middleware.ml)
in this example looks like this: in this example looks like this:
@ -33,14 +33,15 @@ let () =
<br> <br>
When you run this server and visit When you run this server and visit
[http://localhost:8080](http://localhost:8080), you get much more interesting [http://localhost:8080](http://localhost:8080)
[[playground](http://dream.as/2-middleware)], you get much more interesting
(and colorful!) output: (and colorful!) output:
![Dream log example](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/log-sanitized.png) ![Dream log example](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/log-sanitized.png)
You can write your own messages to the log using You can write your own messages to the log using
[`Dream.log`](https://aantron.github.io/dream/#val-log). See example [`Dream.log`](https://aantron.github.io/dream/#val-log). See example
[**`a-log`**](../a-log#folders-and-files) for more logging options. Now that we have the [**`a-log`**](../a-log#files) for more logging options. Now that we have the
logger, we will use it in all other examples, even though it's not really logger, we will use it in all other examples, even though it's not really
necessary &mdash; it just makes it much easier to see what is going on. necessary &mdash; it just makes it much easier to see what is going on.
@ -48,14 +49,14 @@ necessary &mdash; it just makes it much easier to see what is going on.
There's not much else to middlewares &mdash; they are really just functions There's not much else to middlewares &mdash; they are really just functions
from handlers to handlers, so you can create them anywhere. Example from handlers to handlers, so you can create them anywhere. Example
[**`4-counter`**](../4-counter#folders-and-files) already shows a simple custom middleware. [**`4-counter`**](../4-counter#files) already shows a simple custom middleware.
<!-- <!--
There are also more complicated middlewares defined in There are also more complicated middlewares defined in
- [**`m-locals`**](../m-locals#folders-and-files), - [**`m-locals`**](../m-locals/#files),
- [**`w-auto-reload`**](../w-auto-reload#folders-and-files), and - [**`w-auto-reload`**](../w-auto-reload/#files), and
- [**`w-index-html`**](../w-index-html#folders-and-files). - [**`w-index-html`**](../w-index-html/#files).
--> -->
<!-- TODO Fill out this list; probably a-promise belongs here. --> <!-- TODO Fill out this list; probably a-promise belongs here. -->
@ -64,10 +65,10 @@ There are also more complicated middlewares defined in
**Next steps:** **Next steps:**
- The next example, [**`3-router`**](../3-router#folders-and-files), shows - The next example, [**`3-router`**](../3-router#files), shows
[*routes*](https://aantron.github.io/dream/#routing), the other way to build [*routes*](https://aantron.github.io/dream/#routing), the other way to build
up handlers in Dream. up handlers in Dream.
- [**`4-counter`**](../4-counter#folders-and-files) builds the first custom middleware. - [**`4-counter`**](../4-counter#files) builds the first custom middleware.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name middleware) (name middleware)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./middleware.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -26,13 +26,15 @@ let () =
``` ```
<pre><code><b>$ cd example/3-router</b> <pre><code><b>$ cd example/3-router</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./router.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
This is also our first dynamic site! A request to `/echo/foo` gets the response This is also our first dynamic site! A request to `/echo/foo` gets the response
`foo`, and a request to `/echo/bar` gets `bar`! `foo`, and a request to `/echo/bar` gets `bar`! Try it in the
[playground](http://dream.as/3-router/echo/foo) &mdash; once the server loads,
edit the URL in the right pane to visit `/echo/bar`.
The syntax `:word` in a route creates a path parameter, which can be read with The syntax `:word` in a route creates a path parameter, which can be read with
[`Dream.param`](https://aantron.github.io/dream/#val-param). [`Dream.param`](https://aantron.github.io/dream/#val-param).
@ -42,20 +44,20 @@ The syntax `:word` in a route creates a path parameter, which can be read with
When none of the routes match, the router returns a `404 Not Found` response. When none of the routes match, the router returns a `404 Not Found` response.
Except for the status code, the `404 Not Found` response is *completely* empty, Except for the status code, the `404 Not Found` response is *completely* empty,
so it might not display well in your browser. In example so it might not display well in your browser. In example
[**`9-error`**](../9-error#folders-and-files), we will decorate all error responses with [**`9-error`**](../9-error#files), we will decorate all error responses with
an error template in one central location. an error template in one central location.
<br> <br>
The router can do more than match simple routes: The router can do more than match simple routes:
- [**`f-static`**](../f-static#folders-and-files) forwards all requests with a certain - [**`f-static`**](../f-static#files) forwards all requests with a certain
prefix to a static file handler. prefix to a static file handler.
<!-- - [**`w-scope`**](../w-scope#folders-and-files) applies middlewares to groups of routes <!-- - [**`w-scope`**](../w-scope/#files) applies middlewares to groups of routes
&mdash; but only when they match. &mdash; but only when they match.
- [**`w-subsite`**](../w-subsite#folders-and-files) attaches a handler as a complete, - [**`w-subsite`**](../w-subsite/#files) attaches a handler as a complete,
nested sub-site, which might have its own router. --> nested sub-site, which might have its own router. -->
<!-- TODO --> <!-- TODO -->
@ -63,9 +65,9 @@ The router can do more than match simple routes:
**Next steps:** **Next steps:**
- [**`4-counter`**](../4-counter#folders-and-files) counts requests, and exposes a route for - [**`4-counter`**](../4-counter#files) counts requests, and exposes a route for
getting the count. getting the count.
- [**`5-promise`**](../5-promise#folders-and-files) introduces - [**`5-promise`**](../5-promise#files) introduces
[Lwt](https://github.com/ocsigen/lwt), the promise library used by Dream. [Lwt](https://github.com/ocsigen/lwt), the promise library used by Dream.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name router) (name router)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/3-router/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./router.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -4,7 +4,8 @@
This example shows how easy it is to define a custom middleware, This example shows how easy it is to define a custom middleware,
`count_requests`. It exposes the request count at `count_requests`. It exposes the request count at
[http://localhost:8080/](http://localhost:8080/), in a sort of dashboard: [http://localhost:8080/](http://localhost:8080/)
[[playground](http://dream.as/4-counter)], in a sort of dashboard:
```ocaml ```ocaml
let count = ref 0 let count = ref 0
@ -23,8 +24,8 @@ let () =
] ]
``` ```
<pre><code><b>$ cd example/4-counter</b> <pre><code><b>$ cd example/4-counter</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./counter.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
@ -37,16 +38,16 @@ which means they usually also
This example's middleware only does something *before* calling the This example's middleware only does something *before* calling the
`inner_handler`. To do something *after*, we will need to await the response `inner_handler`. To do something *after*, we will need to await the response
promise with [Lwt](https://github.com/ocsigen/lwt#readme), the promise library promise with [Lwt](https://github.com/ocsigen/lwt#readme), the promise library
used by Dream. The next example, [**`5-promise`**](../5-promise#folders-and-files), does used by Dream. The next example, [**`5-promise`**](../5-promise#files), does
exactly that! exactly that!
<br> <br>
**Next steps:** **Next steps:**
- [**`5-promise`**](../5-promise#folders-and-files) shows a middleware that awaits - [**`5-promise`**](../5-promise#files) shows a middleware that awaits
responses using [Lwt](https://github.com/ocsigen/lwt). responses using [Lwt](https://github.com/ocsigen/lwt).
- [**`6-echo`**](../6-echo#folders-and-files) responds to `POST` requests and reads their - [**`6-echo`**](../6-echo#files) responds to `POST` requests and reads their
bodies. bodies.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name counter) (name counter)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./counter.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -2,7 +2,7 @@
<br> <br>
[**`4-counter`**](../4-counter#folders-and-files) was limited to counting requests *before* [**`4-counter`**](../4-counter#files) was limited to counting requests *before*
passing them on to the rest of the app. With the promise library passing them on to the rest of the app. With the promise library
[Lwt](https://github.com/ocsigen/lwt), we can await responses, and do something [Lwt](https://github.com/ocsigen/lwt), we can await responses, and do something
*after*. In this example, we separately count requests that were handled *after*. In this example, we separately count requests that were handled
@ -41,8 +41,10 @@ let () =
``` ```
<pre><code><b>$ cd example/5-promise</b> <pre><code><b>$ cd example/5-promise</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./promise.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
Try it in the [playground](http://dream.as/5-promise).
<br> <br>
@ -91,8 +93,8 @@ We will stick to `let%lwt` in the examples and keep things tidy.
**Next steps:** **Next steps:**
- [**`6-echo`**](../6-echo#folders-and-files) uses Dream and Lwt to read a request body. - [**`6-echo`**](../6-echo#files) uses Dream and Lwt to read a request body.
- [**`7-template`**](../7-template#folders-and-files) shows how to interleave HTML and - [**`7-template`**](../7-template#files) shows how to interleave HTML and
OCaml. OCaml.
<br> <br>

View File

@ -2,3 +2,5 @@
(name promise) (name promise)
(libraries dream) (libraries dream)
(preprocess (pps lwt_ppx))) (preprocess (pps lwt_ppx)))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./promise.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -21,8 +21,10 @@ let () =
``` ```
<pre><code><b>$ cd example/6-echo</b> <pre><code><b>$ cd example/6-echo</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./echo.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
...or run it in the [playground](http://dream.as/6-echo).
<br> <br>
@ -46,11 +48,11 @@ foo
We usually want to do something more interesting with the request body than just We usually want to do something more interesting with the request body than just
echo it, and there are several examples for that! echo it, and there are several examples for that!
- [**`d-form`**](../d-form#folders-and-files) parses request bodies as forms. - [**`d-form`**](../d-form#files) parses request bodies as forms.
- [**`e-json`**](../e-json#folders-and-files) parses bodies as JSON. - [**`e-json`**](../e-json#files) parses bodies as JSON.
- [**`g-upload`**](../g-upload#folders-and-files) receives file upload forms. - [**`g-upload`**](../g-upload#files) receives file upload forms.
- [**`i-graphql`**](../i-graphql#folders-and-files) receives GraphQL queries. - [**`i-graphql`**](../i-graphql#files) receives GraphQL queries.
- [**`j-stream`**](../j-stream#folders-and-files) streams huge bodies. - [**`j-stream`**](../j-stream#files) streams huge bodies.
We delay these examples a bit, so we can squeeze in a couple security topics We delay these examples a bit, so we can squeeze in a couple security topics
first. These examples do take client input, after all! So, it's better to first. These examples do take client input, after all! So, it's better to
@ -62,9 +64,9 @@ present them the right way.
**Next steps:** **Next steps:**
- [**`7-template`**](../7-template#folders-and-files) builds responses from templates and - [**`7-template`**](../7-template#files) builds responses from templates and
guards against injection attacks (XSS). guards against injection attacks (XSS).
- [**`8-debug`**](../8-debug#folders-and-files) renders error information in responses. - [**`8-debug`**](../8-debug#files) renders error information in responses.
<br> <br>

View File

@ -2,3 +2,5 @@
(name echo) (name echo)
(libraries dream) (libraries dream)
(preprocess (pps lwt_ppx))) (preprocess (pps lwt_ppx)))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/6-echo/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./echo.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -31,8 +31,8 @@ let () =
``` ```
<pre><code><b>$ cd example/7-template</b> <pre><code><b>$ cd example/7-template</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./template.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
@ -54,7 +54,7 @@ file to run the template preprocessor:
<br> <br>
The substitution, `<%s param %>`, uses The substitution, `<%s param %>`, uses
[`Printf` conversion specifications](https://v2.ocaml.org/api/Printf.html) [`Printf` conversion specifications](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Printf.html)
from the standard library. So, you can do things like this: from the standard library. So, you can do things like this:
- `<%i my_int %>` to print an OCaml `int`. - `<%i my_int %>` to print an OCaml `int`.
@ -84,11 +84,15 @@ already escaped, or if it is safe for some other reason. But be careful!
To show the danger, let's launch a **script injection (XSS) attack** against To show the danger, let's launch a **script injection (XSS) attack** against
this tiny Web app! First, go to this tiny Web app! First, go to
[`template.eml.ml`](https://github.com/aantron/dream/blob/master/example/7-template/template.eml.ml#L4), [`template.eml.ml`](https://github.com/aantron/dream/blob/master/example/7-template/template.eml.ml#L4),
change the substitution to `<%s! param %>`, and restart the app. Then, visit change the substitution to `<%s! param %>`, and restart the app. You can also
make the edit in the [playground](http://dream.as/7-template/foo). Then,
visit
this highly questionable URL: this highly questionable URL:
[http://localhost:8080/%3Cscript%3Ealert(%22Impossible!%22)%3C%2Fscript%3E](http://localhost:8080/%3Cscript%3Ealert(%22Impossible!%22)%3C%2Fscript%3E) [http://localhost:8080/%3Cscript%3Ealert(%22Impossible!%22)%3C%2Fscript%3E](http://localhost:8080/%3Cscript%3Ealert(%22Impossible!%22)%3C%2Fscript%3E)
If you are using the playground, change the host and port accordingly.
This URL will cause our Web app to display an alert box, which we, as the This URL will cause our Web app to display an alert box, which we, as the
developers, did not intend! developers, did not intend!
@ -125,31 +129,27 @@ and not supported by Dream.
**Next steps:** **Next steps:**
- [**`8-debug`**](../8-debug#folders-and-files) shows how to turn on debug responses, and - [**`8-debug`**](../8-debug#files) shows how to turn on debug responses, and
get more info about errors. get more info about errors.
- [**`9-error`**](../9-error#folders-and-files) sets up a central error template for all - [**`9-error`**](../9-error#files) sets up a central error template for all
errors. errors.
- [**`r-template`**](../r-template#folders-and-files) is a Reason syntax version of this - [**`r-template`**](../r-template#files) is a Reason syntax version of this
example. example.
<br> <br>
**See also:** **See also:**
- [**`w-template-files`**](../w-template-files#folders-and-files) moves the template into a - [**`w-template-files`**](../w-template-files#files) moves the template into a
separate `.eml.html` to avoid problems with editor support. separate `.eml.html` to avoid problems with editor support.
- [**`w-template-logic`**](../w-template-logic#folders-and-files) shows how to put control - [**`w-template-logic`**](../w-template-logic#files) shows how to put control
flow into templates. flow into templates.
- [**`w-tyxml`**](../w-tyxml#folders-and-files) shows how to use - [**`w-tyxml`**](../w-tyxml#files) shows how to use
[TyXML](https://github.com/ocsigen/tyxml), a different templater that uses [TyXML](https://github.com/ocsigen/tyxml), a different templater that uses
OCaml's type system to prevent emitting many kinds of invalid HTML. OCaml's type system to prevent emitting many kinds of invalid HTML.
- [**`r-tyxml`**](../r-tyxml#folders-and-files) if you are using Reason. You can use TyXML - [**`r-tyxml`**](../r-tyxml#files) if you are using Reason. You can use TyXML
with JSX syntax server-side! with JSX syntax server-side!
- [**`w-dream-html`**](../w-dream-html#folders-and-files) shows how to use - [**`w-template-stream`**](../w-template-stream#files) streams templates to
[dream-html](https://github.com/yawaramin/dream-html), another alternative
library for generating HTML from OCaml, which is more closely integrated with
Dream.
- [**`w-template-stream`**](../w-template-stream#folders-and-files) streams templates to
responses, instead of building up complete response strings. responses, instead of building up complete response strings.
<br> <br>

View File

@ -6,3 +6,5 @@
(targets template.ml) (targets template.ml)
(deps template.eml.ml) (deps template.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root}))) (action (run dream_eml %{deps} --workspace %{workspace_root})))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./template.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -23,17 +23,18 @@ let () =
``` ```
<pre><code><b>$ cd example/8-debug</b> <pre><code><b>$ cd example/8-debug</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./debug.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
The rest of the app just adds two routes for triggering two kinds of The rest of the app just adds two routes for triggering two kinds of
failures that the debugger will detail. Visit failures that the debugger will detail. Visit
[http://localhost:8080/bad](http://localhost:8080/bad) to trigger a [http://localhost:8080/bad](http://localhost:8080/bad)
`400 Bad Request` response, and [[playground](http://dream.as/8-debug/bad)] to trigger a `400 Bad Request`
[http://localhost:8080/fail](http://localhost:8080/fail) to trigger an response, and [http://localhost:8080/fail](http://localhost:8080/fail)
exception. The debugger will show reports like this: [[playground](http://dream.as/8-debug/fail)] to trigger an exception. The
debugger will show reports like this:
``` ```
Failure("The Web app failed!") Failure("The Web app failed!")
@ -94,7 +95,7 @@ work with in development.
You can have Dream show a custom error page with any information or graphics You can have Dream show a custom error page with any information or graphics
that you like &mdash; we will do this in the [very next that you like &mdash; we will do this in the [very next
example](../9-error#folders-and-files)! example](../9-error#files)!
<!-- TODO Fix after stack trace is fixed. --> <!-- TODO Fix after stack trace is fixed. -->
<!-- TODO Show the log --> <!-- TODO Show the log -->
@ -104,9 +105,9 @@ example](../9-error#folders-and-files)!
**Next steps:** **Next steps:**
- [**`9-error`**](../9-error#folders-and-files) handles all errors in one place, including - [**`9-error`**](../9-error#files) handles all errors in one place, including
displaying the debugger output. displaying the debugger output.
- [**`a-log`**](../a-log#folders-and-files) shows [log - [**`a-log`**](../a-log#files) shows [log
levels](https://aantron.github.io/dream/#type-log_level) and levels](https://aantron.github.io/dream/#type-log_level) and
[sub-logs](https://aantron.github.io/dream/#type-sub_log). [sub-logs](https://aantron.github.io/dream/#type-sub_log).

View File

@ -1,3 +1,5 @@
(executable (executable
(name debug) (name debug)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/8-debug/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./debug.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -33,8 +33,10 @@ let () =
``` ```
<pre><code><b>$ cd example/9-error</b> <pre><code><b>$ cd example/9-error</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./error.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
Try it in the [playground](http://dream.as/9-error).
<br> <br>
@ -63,7 +65,7 @@ including return a completely new response.
<br> <br>
`debug_info` is a multiline string containing the same information as in the `debug_info` is a multiline string containing the same information as in the
previous example, [**`8-debug`**](../8-debug#folders-and-files). previous example, [**`8-debug`**](../8-debug#files).
<!-- TODO Images of the generated pages. --> <!-- TODO Images of the generated pages. -->
@ -80,9 +82,9 @@ Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet
**Next steps:** **Next steps:**
- [**`a-log`**](../a-log#folders-and-files) shows how to write messages to Dream's - [**`a-log`**](../a-log#files) shows how to write messages to Dream's
[log](https://aantron.github.io/dream/#logging). [log](https://aantron.github.io/dream/#logging).
- [**`b-session`**](../b-session#folders-and-files) adds [session - [**`b-session`**](../b-session#files) adds [session
management](https://aantron.github.io/dream/#sessions) for associating state management](https://aantron.github.io/dream/#sessions) for associating state
with clients. with clients.

View File

@ -6,3 +6,5 @@
(targets error.ml) (targets error.ml)
(deps error.eml.ml) (deps error.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root}))) (action (run dream_eml %{deps} --workspace %{workspace_root})))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -17,4 +17,4 @@ let my_error_template _error debug_info suggested_response =
let () = let () =
Dream.run ~error_handler:(Dream.error_template my_error_template) Dream.run ~error_handler:(Dream.error_template my_error_template)
@@ Dream.logger @@ Dream.logger
@@ fun _ -> Dream.empty `Not_Found @@ Dream.not_found

17
example/9-error/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./error.exe"
}
}

View File

@ -2,46 +2,46 @@
Dream's first several examples make up a **tutorial**. Each example is a Dream's first several examples make up a **tutorial**. Each example is a
complete project with a helpful README, and plenty of links to next steps and complete project with a helpful README, and plenty of links to next steps and
documentation. You can begin at [**`1-hello`**](1-hello#folders-and-files), or look in the documentation. You can begin at [**`1-hello`**](1-hello#files), or look in the
list below and jump to whatever interests you! list below and jump to whatever interests you!
- [**`1-hello`**](1-hello#folders-and-files) &nbsp;&mdash;&nbsp; the simplest Dream server - [**`1-hello`**](1-hello/#files) &nbsp;&mdash;&nbsp; the simplest Dream server
responds to every request with the same friendly message. responds to every request with the same friendly message.
- [**`2-middleware`**](2-middleware#folders-and-files) &nbsp;&mdash;&nbsp; adds the first - [**`2-middleware`**](2-middleware/#files) &nbsp;&mdash;&nbsp; adds the first
Dream middleware: the *logger*. Dream middleware: the *logger*.
- [**`3-router`**](3-router#folders-and-files) &nbsp;&mdash;&nbsp; different handlers for - [**`3-router`**](3-router/#files) &nbsp;&mdash;&nbsp; different handlers for
different paths. different paths.
- [**`4-counter`**](4-counter#folders-and-files) &nbsp;&mdash;&nbsp; the first *custom* - [**`4-counter`**](4-counter/#files) &nbsp;&mdash;&nbsp; the first *custom*
middleware! middleware!
- [**`5-promise`**](5-promise#folders-and-files) &nbsp;&mdash;&nbsp; introduces Lwt, the - [**`5-promise`**](5-promise/#files) &nbsp;&mdash;&nbsp; introduces Lwt, the
promise library used by Dream. promise library used by Dream.
- [**`6-echo`**](6-echo#folders-and-files) &nbsp;&mdash;&nbsp; reads request bodies. - [**`6-echo`**](6-echo/#files) &nbsp;&mdash;&nbsp; reads request bodies.
- [**`7-template`**](7-template#folders-and-files) &nbsp;&mdash;&nbsp; renders responses - [**`7-template`**](7-template/#files) &nbsp;&mdash;&nbsp; renders responses
from inline HTML templates and guards against XSS. from inline HTML templates and guards against XSS.
- [**`8-debug`**](8-debug#folders-and-files) &nbsp;&mdash;&nbsp; includes detailed - [**`8-debug`**](8-debug/#files) &nbsp;&mdash;&nbsp; includes detailed
information about errors in responses. information about errors in responses.
- [**`9-error`**](9-error#folders-and-files) &nbsp;&mdash;&nbsp; customize all error - [**`9-error`**](9-error/#files) &nbsp;&mdash;&nbsp; customize all error
responses in one place. responses in one place.
- [**`a-log`**](a-log#folders-and-files) &nbsp;&mdash;&nbsp; writing messages to Dream's - [**`a-log`**](a-log/#files) &nbsp;&mdash;&nbsp; writing messages to Dream's
log. log.
- [**`b-session`**](b-session#folders-and-files) &nbsp;&mdash;&nbsp; associates state with - [**`b-session`**](b-session/#files) &nbsp;&mdash;&nbsp; associates state with
client sessions. client sessions.
- [**`c-cookie`**](c-cookie#folders-and-files) &nbsp;&mdash;&nbsp; sets custom cookies. - [**`c-cookie`**](c-cookie/#files) &nbsp;&mdash;&nbsp; sets custom cookies.
- [**`d-form`**](d-form#folders-and-files) &nbsp;&mdash;&nbsp; reads forms with CSRF - [**`d-form`**](d-form#files) &nbsp;&mdash;&nbsp; reads forms with CSRF
prevention. prevention.
- [**`e-json`**](e-json#folders-and-files) &nbsp;&mdash;&nbsp; sends and receives JSON - [**`e-json`**](e-json#files) &nbsp;&mdash;&nbsp; sends and receives JSON
securely. securely.
- [**`f-static`**](f-static#folders-and-files) &nbsp;&mdash;&nbsp; serves static files from - [**`f-static`**](f-static#files) &nbsp;&mdash;&nbsp; serves static files from
a local directory. a local directory.
- [**`g-upload`**](g-upload#folders-and-files) &nbsp;&mdash;&nbsp; receives file uploads. - [**`g-upload`**](g-upload#files) &nbsp;&mdash;&nbsp; receives file uploads.
- [**`h-sql`**](h-sql#folders-and-files) &nbsp;&mdash;&nbsp; queries an SQL database. - [**`h-sql`**](h-sql#files) &nbsp;&mdash;&nbsp; queries an SQL database.
- [**`i-graphql`**](i-graphql#folders-and-files) &nbsp;&mdash;&nbsp; serves a GraphQL - [**`i-graphql`**](i-graphql#files) &nbsp;&mdash;&nbsp; serves a GraphQL
schema and GraphiQL. schema and GraphiQL.
- [**`j-stream`**](j-stream#folders-and-files) &nbsp;&mdash;&nbsp; streams request and - [**`j-stream`**](j-stream#files) &nbsp;&mdash;&nbsp; streams request and
response bodies. response bodies.
- [**`k-websocket`**](k-websocket#folders-and-files) &nbsp;&mdash;&nbsp; opens a WebSocket - [**`k-websocket`**](k-websocket#files) &nbsp;&mdash;&nbsp; opens a WebSocket
between client and server. between client and server.
- [**`l-https`**](l-https#folders-and-files) &nbsp;&mdash;&nbsp; enables HTTPS and HTTP/2 - [**`l-https`**](l-https#files) &nbsp;&mdash;&nbsp; enables HTTPS and HTTP/2
upgrades. upgrades.
That's it for the tutorial! That's it for the tutorial!
@ -52,43 +52,43 @@ That's it for the tutorial!
There are several examples showing Dream with Reason syntax. There are several examples showing Dream with Reason syntax.
- [**`r-hello`**](r-hello#folders-and-files) &nbsp;&mdash;&nbsp; the simplest Dream server. - [**`r-hello`**](r-hello#files) &nbsp;&mdash;&nbsp; the simplest Dream server.
- [**`r-template`**](r-template#folders-and-files) &nbsp;&mdash;&nbsp; renders HTML - [**`r-template`**](r-template#files) &nbsp;&mdash;&nbsp; renders HTML
templates and protects against XSS. templates and protects against XSS.
- [**`r-template-files`**](r-template-files#folders-and-files) &nbsp;&mdash;&nbsp; templates - [**`r-template-files`**](r-template-files#files) &nbsp;&mdash;&nbsp; templates
in separate `.html` files for better editor support. in separate `.html` files for better editor support.
- [**`r-template-logic`**](r-template-logic#folders-and-files) &nbsp;&mdash;&nbsp; control - [**`r-template-logic`**](r-template-logic#files) &nbsp;&mdash;&nbsp; control
flow inside templates. flow inside templates.
- [**`r-template-stream`**](r-template-stream#folders-and-files) &nbsp;&mdash;&nbsp; streams - [**`r-template-stream`**](r-template-stream#files) &nbsp;&mdash;&nbsp; streams
templates as response bodies. templates as response bodies.
- [**`r-tyxml`**](r-tyxml#folders-and-files) &nbsp;&mdash;&nbsp; type-checked server-side - [**`r-tyxml`**](r-tyxml#files) &nbsp;&mdash;&nbsp; type-checked server-side
JSX templates. JSX templates.
- [**`r-graphql`**](r-graphql#folders-and-files) &nbsp;&mdash;&nbsp; serves a GraphQL - [**`r-graphql`**](r-graphql#files) &nbsp;&mdash;&nbsp; serves a GraphQL
schema. schema.
<br> <br>
# Full-stack # Full-stack
- [**`r-fullstack-melange`**](r-fullstack-melange#folders-and-files) &nbsp;&mdash;&nbsp; - [**`r-fullstack-melange`**](r-fullstack-melange#files) &nbsp;&mdash;&nbsp;
server *and* client written in Reason! server *and* client written in Reason!
- [**`w-fullstack-rescript`**](w-fullstack-rescript#folders-and-files) &nbsp;&mdash;&nbsp; - [**`w-fullstack-rescript`**](w-fullstack-rescript#files) &nbsp;&mdash;&nbsp;
shares OCaml code between server and client using ReScript. shares OCaml code between server and client using ReScript.
- [**`w-fullstack-jsoo`**](w-fullstack-jsoo#folders-and-files) &nbsp;&mdash;&nbsp; shares - [**`w-fullstack-jsoo`**](w-fullstack-jsoo#files) &nbsp;&mdash;&nbsp; shares
OCaml code between server and client using js_of_ocaml. OCaml code between server and client using js_of_ocaml.
<br> <br>
# Deploying # Deploying
- [**`z-heroku`**](z-heroku#folders-and-files) &nbsp;&mdash;&nbsp; to - [**`z-heroku`**](z-heroku#files) &nbsp;&mdash;&nbsp; to
[Heroku](https://www.heroku.com). [Heroku](https://www.heroku.com).
- [**`z-fly`**](z-fly#folders-and-files) &nbsp;&mdash;&nbsp; to [Fly.io](https://fly.io/). - [**`z-fly`**](z-fly@files) &nbsp;&mdash;&nbsp; to [Fly.io](https://fly.io/).
- [**`z-docker-esy`**](z-docker-esy#folders-and-files) &nbsp;&mdash;&nbsp; on a server, - [**`z-docker-esy`**](z-docker-esy#files) &nbsp;&mdash;&nbsp; on a server,
using Docker, with package manager esy. using Docker, with package manager esy.
- [**`z-docker-opam`**](z-docker-opam#folders-and-files) &nbsp;&mdash;&nbsp; on a server, - [**`z-docker-opam`**](z-docker-opam#files) &nbsp;&mdash;&nbsp; on a server,
using Docker, with package manager opam. using Docker, with package manager opam.
- [**`z-systemd`**](z-systemd#folders-and-files) &nbsp;&mdash;&nbsp; on a server, as a - [**`z-systemd`**](z-systemd#files) &nbsp;&mdash;&nbsp; on a server, as a
systemd daemon. systemd daemon.
<br> <br>
@ -102,51 +102,48 @@ if something is missing!
<br> <br>
- [**`w-template-files`**](w-template-files#folders-and-files) &nbsp;&mdash;&nbsp; templates - [**`w-template-files`**](w-template-files#files) &nbsp;&mdash;&nbsp; templates
in separate `.html` files for better editor support. in separate `.html` files for better editor support.
- [**`w-template-logic`**](w-template-logic#folders-and-files) &nbsp;&mdash;&nbsp; control - [**`w-template-logic`**](w-template-logic#files) &nbsp;&mdash;&nbsp; control
flow inside templates. flow inside templates.
- [**`w-graphql-subscription`**](w-graphql-subscription#folders-and-files) - [**`w-graphql-subscription`**](w-graphql-subscription#files)
&nbsp;&mdash;&nbsp; GraphQL subscriptions. &nbsp;&mdash;&nbsp; GraphQL subscriptions.
- [**`w-postgres`**](w-postgres#folders-and-files) &nbsp;&mdash;&nbsp; connects to a - [**`w-postgres`**](w-postgres#files) &nbsp;&mdash;&nbsp; connects to a
PostgreSQL database. PostgreSQL database.
- [**`w-flash`**](w-flash#folders-and-files) &nbsp;&mdash;&nbsp; using flash messages, which - [**`w-flash`**](w-flash#files) &nbsp;&mdash;&nbsp; using flash messages, which
are displayed on the next request. are displayed on the next request.
- [**`w-chat`**](w-chat#folders-and-files) &nbsp;&mdash;&nbsp; a chat room based on - [**`w-chat`**](w-chat#files) &nbsp;&mdash;&nbsp; a chat room based on
WebSockets. WebSockets.
- [**`w-content-security-policy`**](w-content-security-policy#folders-and-files) - [**`w-content-security-policy`**](w-content-security-policy#files)
&nbsp;&mdash;&nbsp; sandboxes Web pages using `Content-Security-Policy`. &nbsp;&mdash;&nbsp; sandboxes Web pages using `Content-Security-Policy`.
- [**`w-esy`**](w-esy#folders-and-files) &nbsp;&mdash;&nbsp; gives detail on packaging with - [**`w-esy`**](w-esy#files) &nbsp;&mdash;&nbsp; gives detail on packaging with
[esy](https://esy.sh/), an npm-like package manager. [esy](https://esy.sh/), an npm-like package manager.
- [**`w-one-binary`**](w-one-binary#folders-and-files) &nbsp;&mdash;&nbsp; bakes static - [**`w-one-binary`**](w-one-binary#files) &nbsp;&mdash;&nbsp; bakes static
assets into a self-contained server binary. assets into a self-contained server binary.
- [**`w-watch`**](w-watch#folders-and-files) &nbsp;&mdash;&nbsp; sets up a development - [**`w-fswatch`**](w-fswatch#files) &nbsp;&mdash;&nbsp; sets up a development
watcher. watcher using fswatch.
- [**`w-live-reload`**](w-live-reload#folders-and-files) &nbsp;&mdash;&nbsp; a simple - [**`w-live-reload`**](w-live-reload#files) &nbsp;&mdash;&nbsp; a simple
live-reloading setup. live-reloading setup.
- [**`w-nginx`**](w-nginx#folders-and-files) &nbsp;&mdash;&nbsp; uses nginx as a - [**`w-nginx`**](w-nginx#files) &nbsp;&mdash;&nbsp; uses nginx as a reverse proxy.
reverse proxy. - [**`w-tyxml`**](w-tyxml#files) &nbsp;&mdash;&nbsp; uses TyXML for type-checked
- [**`w-tyxml`**](w-tyxml#folders-and-files) &nbsp;&mdash;&nbsp; uses TyXML for type-checked
HTML templating. HTML templating.
- [**`w-dream-html`**](../w-dream-html#folders-and-files) &nbsp;&mdash;&nbsp; uses - [**`w-long-polling`**](w-long-polling#files) &nbsp;&mdash;&nbsp; old form of
dream-html for convenient HTML generation from OCaml.
- [**`w-long-polling`**](w-long-polling#folders-and-files) &nbsp;&mdash;&nbsp; old form of
asynchronous communication without WebSockets. asynchronous communication without WebSockets.
- [**`w-query`**](w-query#folders-and-files) &nbsp;&mdash;&nbsp; reads URL query parameters. - [**`w-query`**](w-query#files) &nbsp;&mdash;&nbsp; reads URL query parameters.
- [**`w-server-sent-events`**](w-server-sent-events#folders-and-files) &nbsp;&mdash;&nbsp; - [**`w-server-sent-events`**](w-server-sent-events#files) &nbsp;&mdash;&nbsp;
[`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource), [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource),
an older alternative to WebSockets. an older alternative to WebSockets.
- [**`w-template-stream`**](w-template-stream#folders-and-files) &nbsp;&mdash;&nbsp; sends - [**`w-template-stream`**](w-template-stream#files) &nbsp;&mdash;&nbsp; sends
templates asynchronously, one chunk at a time. templates asynchronously, one chunk at a time.
- [**`w-upload-stream`**](w-upload-stream#folders-and-files) &nbsp;&mdash;&nbsp; streams - [**`w-upload-stream`**](w-upload-stream#files) &nbsp;&mdash;&nbsp; streams
uploaded files. uploaded files.
- [**`w-stress-response`**](w-stress-response#folders-and-files) &nbsp;&mdash;&nbsp; - [**`w-stress-response`**](w-stress-response#files) &nbsp;&mdash;&nbsp;
benchmarks streaming very large responses. benchmarks streaming very large responses.
- [**`w-stress-websocket-send`**](w-stress-websocket-send#folders-and-files) - [**`w-stress-websocket-send`**](w-stress-websocket-send#files)
&nbsp;&mdash;&nbsp; benchmarks sending WebSocket messages quickly. &nbsp;&mdash;&nbsp; benchmarks sending WebSocket messages quickly.
- [**`w-multipart-dump`**](w-multipart-dump#folders-and-files) &nbsp;&mdash;&nbsp; echoes - [**`w-multipart-dump`**](w-multipart-dump#files) &nbsp;&mdash;&nbsp; echoes
`multipart/form-data` bodies for debugging. `multipart/form-data` bodies for debugging.
- [**`z-playground`**](z-playground#folders-and-files) &nbsp;&mdash;&nbsp; source code of - [**`z-playground`**](z-playground#files) &nbsp;&mdash;&nbsp; source code of
the Dream playground. the Dream playground.
<br> <br>
@ -169,7 +166,7 @@ Ideas:
Basics: Basics:
- `w-content-negotiation` - `w-content-negotiation`
- [**`w-query`**](w-query#folders-and-files) &nbsp;&mdash;&nbsp; done. - [**`w-query`**](w-query#files) &nbsp;&mdash;&nbsp; done.
- `w-scope` &nbsp;&mdash;&nbsp; for - `w-scope` &nbsp;&mdash;&nbsp; for
[`Dream.scope`](https://aantron.github.io/dream/#val-scope). [`Dream.scope`](https://aantron.github.io/dream/#val-scope).
- `w-subsite` &nbsp;&mdash;&nbsp; for - `w-subsite` &nbsp;&mdash;&nbsp; for

View File

@ -25,12 +25,13 @@ let () =
``` ```
<pre><code><b>$ cd example/a-log</b> <pre><code><b>$ cd example/a-log</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./log.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
If you visit [http://localhost:8080](http://localhost:8080) and then If you visit [http://localhost:8080](http://localhost:8080)
[[playground](http://dream.as/a-log)] and then
[http://localhost:8080/fail](http://localhost:8080/fail), you will find these [http://localhost:8080/fail](http://localhost:8080/fail), you will find these
messages in the log, between the others: messages in the log, between the others:
@ -40,7 +41,7 @@ messages in the log, between the others:
``` ```
Note that this is on `stderr`. As you can see, the functions take Note that this is on `stderr`. As you can see, the functions take
[`Printf`-style format strings](https://v2.ocaml.org/api/Printf.html), [`Printf`-style format strings](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Printf.html),
so you can quickly print values of various types to the log. so you can quickly print values of various types to the log.
<br> <br>
@ -76,9 +77,9 @@ let () =
**Next steps:** **Next steps:**
- [**`b-session`**](../b-session#folders-and-files) returns Web development proper with - [**`b-session`**](../b-session#files) returns Web development proper with
session management. session management.
- [**`c-cookie`**](../c-cookie#folders-and-files) shows cookie handling in Dream. - [**`c-cookie`**](../c-cookie#files) shows cookie handling in Dream.
<br> <br>

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -1,3 +1,5 @@
(executable (executable
(name log) (name log)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/a-log/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./log.exe"
}
}

View File

@ -23,13 +23,13 @@ let () =
``` ```
<pre><code><b>$ cd example/b-session</b> <pre><code><b>$ cd example/b-session</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./session.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
The first time you access the app, it “logs you in” by saving you user name in The first time you access the app [[playground](http://dream.as/b-session)], it
a session. The session manager, “logs you in” by saving you user name in a session. The session manager,
[`Dream.memory_sessions`](https://aantron.github.io/dream/#val-memory_sessions), [`Dream.memory_sessions`](https://aantron.github.io/dream/#val-memory_sessions),
a middleware, adds a `dream.session` cookie to the response, containing the a middleware, adds a `dream.session` cookie to the response, containing the
session key. The next time you access the app, the session is looked up again session key. The next time you access the app, the session is looked up again
@ -68,7 +68,7 @@ There are two other session back ends, which are persistent:
using a different random encryption key each time it starts. using a different random encryption key each time it starts.
- [`Dream.sql_sessions`](https://aantron.github.io/dream/#val-sql_sessions) - [`Dream.sql_sessions`](https://aantron.github.io/dream/#val-sql_sessions)
stores sessions in a database. It is shown in example stores sessions in a database. It is shown in example
[**`h-sql`**](../h-sql#folders-and-files). [**`h-sql`**](../h-sql#files).
<br> <br>
@ -90,7 +90,7 @@ new session will, again, be an empty pre-session.
It is best to use HTTPS when using sessions, to prevent session cookies from It is best to use HTTPS when using sessions, to prevent session cookies from
being easily observed by third parties. See being easily observed by third parties. See
[`Dream.run`](https://aantron.github.io/dream/#val-run) argument `~https`, and [`Dream.run`](https://aantron.github.io/dream/#val-run) argument `~https`, and
example [**`l-https`**](../l-https#folders-and-files). If you redirect from HTTP to HTTPS, example [**`l-https`**](../l-https#files). If you redirect from HTTP to HTTPS,
do not issue sessions for HTTP requests. If you do, don't accept them later do not issue sessions for HTTP requests. If you do, don't accept them later
from HTTPS requests. from HTTPS requests.
@ -100,8 +100,8 @@ from HTTPS requests.
**Next steps:** **Next steps:**
- Sessions already use cookies internally, but in - Sessions already use cookies internally, but in
[**`c-cookie`**](../c-cookie#folders-and-files) we set cookies for our own purposes! [**`c-cookie`**](../c-cookie#files) we set cookies for our own purposes!
- [**`d-form`**](../d-form#folders-and-files) builds secure forms on top of sessions. - [**`d-form`**](../d-form#files) builds secure forms on top of sessions.
<br> <br>

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -2,3 +2,5 @@
(name session) (name session)
(libraries dream) (libraries dream)
(preprocess (pps lwt_ppx))) (preprocess (pps lwt_ppx)))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./session.exe"
}
}

View File

@ -24,14 +24,15 @@ let () =
``` ```
<pre><code><b>$ cd example/c-cookie</b> <pre><code><b>$ cd example/c-cookie</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./cookie.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
The first time you access this app, it sets up a language preference, `ut-OP`. The first time you access this app [[playground](http://dream.as/c-cookie)], it
This string is sent to the client in a `ui.language` cookie. On the next sets up a language preference, `ut-OP`. This string is sent to the client in a
request, the client sends it back. The app retrieves and displays it. `ui.language` cookie. On the next request, the client sends it back. The app
retrieves and displays it.
<br> <br>
@ -96,9 +97,9 @@ The easiest way to do that for general data is to use
**Next steps:** **Next steps:**
- [**`d-form`**](../d-form#folders-and-files) builds secure forms on top of sessions, and - [**`d-form`**](../d-form#files) builds secure forms on top of sessions, and
introduces automatic handling of CSRF tokens. introduces automatic handling of CSRF tokens.
- [**`e-json`**](../e-json#folders-and-files) sends and receives JSON instead! - [**`e-json`**](../e-json#files) sends and receives JSON instead!
<br> <br>

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -1,3 +1,5 @@
(executable (executable
(name cookie) (name cookie)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/c-cookie/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./cookie.exe"
}
}

View File

@ -2,7 +2,7 @@
<br> <br>
With the session middleware from example [**`b-session`**](../b-session#folders-and-files), With the session middleware from example [**`b-session`**](../b-session#files),
we can build a [secure form](https://aantron.github.io/dream/#forms): we can build a [secure form](https://aantron.github.io/dream/#forms):
```ocaml ```ocaml
@ -46,8 +46,10 @@ let () =
``` ```
<pre><code><b>$ cd example/d-form</b> <pre><code><b>$ cd example/d-form</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./form.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
Try it in the [playground](http://dream.as/d-form).
<br> <br>
@ -100,14 +102,14 @@ important on login forms and other sensitive pages.
However, this server is so simple that it doesn't store the data anywhere, and However, this server is so simple that it doesn't store the data anywhere, and
the data is not sensitive, so we took a shortcut. See the data is not sensitive, so we took a shortcut. See
[**`h-sql`**](../h-sql#folders-and-files) for an example with a proper redirection. [**`h-sql`**](../h-sql#files) for an example with a proper redirection.
<br> <br>
**Next steps:** **Next steps:**
- [**`e-json`**](../e-json#folders-and-files) receives and sends JSON. - [**`e-json`**](../e-json#files) receives and sends JSON.
- [**`f-static`**](../f-static#folders-and-files) serves static files from a local - [**`f-static`**](../f-static#files) serves static files from a local
directory. directory.
<br> <br>

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -7,3 +7,5 @@
(targets form.ml) (targets form.ml)
(deps form.eml.ml) (deps form.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root}))) (action (run dream_eml %{deps} --workspace %{workspace_root})))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/d-form/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./form.exe"
}
}

View File

@ -10,8 +10,6 @@ converter between JSON and an OCaml data type. We then create a little server
that listens for JSON of the right shape, and echoes back its `message` field: that listens for JSON of the right shape, and echoes back its `message` field:
```ocaml ```ocaml
open Ppx_yojson_conv_lib.Yojson_conv.Primitives
type message_object = { type message_object = {
message : string; message : string;
} [@@deriving yojson] } [@@deriving yojson]
@ -49,21 +47,24 @@ To get this working, we have to add `ppx_yojson_conv` to our
</code></pre> </code></pre>
and to and to
[`json.opam`](https://github.com/aantron/dream/blob/master/example/e-json/e-json.opam): [`esy.json`](https://github.com/aantron/dream/blob/master/example/e-json/esy.json):
<pre><code>depends: [ <pre><code>{
"ocaml" {>= "4.08.0"} "dependencies": {
"dream" "@opam/dream": "aantron/dream:dream.opam",
"dune" {>= "2.0.0"} "@opam/dune": "^2.0",
<b>"ppx_yojson_conv"</b> <b>"@opam/ppx_yojson_conv": "*",</b>
] "ocaml": "4.12.x"
}
</code></pre> </code></pre>
The build commands, as always, are: The build commands, as always, are:
<pre><code><b>$ cd example/e-json</b> <pre><code><b>$ cd example/e-json</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./json.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
You can try this example in the [playground](http://dream.as/e-json).
<br> <br>
@ -112,9 +113,9 @@ requests!
**Next steps:** **Next steps:**
- [**`f-static`**](../f-static#folders-and-files) serves static files from the local - [**`f-static`**](../f-static#files) serves static files from the local
file system. file system.
- [**`g-upload`**](../g-upload#folders-and-files) receives files from an upload form. - [**`g-upload`**](../g-upload#files) receives files from an upload form.
<br> <br>

View File

@ -2,3 +2,5 @@
(name json) (name json)
(libraries dream) (libraries dream)
(preprocess (pps lwt_ppx ppx_yojson_conv))) (preprocess (pps lwt_ppx ppx_yojson_conv)))
(data_only_dirs _esy esy.lock lib node_modules)

View File

@ -1,15 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
"ppx_yojson_conv"
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

18
example/e-json/esy.json Normal file
View File

@ -0,0 +1,18 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"@opam/ppx_yojson_conv": "*",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./json.exe"
}
}

View File

@ -1,5 +1,3 @@
open Ppx_yojson_conv_lib.Yojson_conv.Primitives
type message_object = { type message_object = {
message : string; message : string;
} [@@deriving yojson] } [@@deriving yojson]

View File

@ -5,8 +5,8 @@
Run this example: Run this example:
<pre><code><b>$ cd example/f-static</b> <pre><code><b>$ cd example/f-static</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./static.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
...and visit ...and visit
[http://localhost:8080/static/static.ml](http://localhost:8080/static/static.ml). [http://localhost:8080/static/static.ml](http://localhost:8080/static/static.ml).
@ -56,7 +56,7 @@ You can replace the file loading behavior of
[crunch](https://github.com/mirage/ocaml-crunch) to compile a directory right [crunch](https://github.com/mirage/ocaml-crunch) to compile a directory right
into your Web app binary, and then serve that directory from memory with into your Web app binary, and then serve that directory from memory with
[`Dream.static`](https://aantron.github.io/dream/#val-static)! See example [`Dream.static`](https://aantron.github.io/dream/#val-static)! See example
[**`w-one-binary`**](../w-one-binary#folders-and-files). [**`w-one-binary`**](../w-one-binary#files).
You can also use `~loader` to set arbitrary headers on the response. You can also use `~loader` to set arbitrary headers on the response.
@ -64,9 +64,9 @@ You can also use `~loader` to set arbitrary headers on the response.
**Next steps:** **Next steps:**
- [**`g-upload`**](../g-upload#folders-and-files) receives files instead of serving them. - [**`g-upload`**](../g-upload#files) receives files instead of serving them.
- [**`h-sql`**](../h-sql#folders-and-files) runs SQL queries against a database. - [**`h-sql`**](../h-sql#files) runs SQL queries against a database.
- [**`w-one-binary`**](../w-one-binary#folders-and-files) bundles assets into a - [**`w-one-binary`**](../w-one-binary#files) bundles assets into a
self-contained binary. self-contained binary.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name static) (name static)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/f-static/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./static.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -51,12 +51,13 @@ let () =
``` ```
<pre><code><b>$ cd example/g-upload</b> <pre><code><b>$ cd example/g-upload</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./upload.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
The page shown after uploading looks like this: The page shown after uploading looks like this
[[playground](http://dream.as/g-upload)]:
``` ```
foo.png, 663959 bytes foo.png, 663959 bytes
@ -74,7 +75,7 @@ However, this is only good for rare, small uploads, such as user avatars, or for
prototyping. prototyping.
For more heavy usage, see For more heavy usage, see
[`Dream.upload`](https://aantron.github.io/dream/#val-upload) for [`Dream.upload`](https://aantron.github.io/dream/#type-upload_event) for
streaming file uploads. streaming file uploads.
<br> <br>
@ -84,7 +85,7 @@ streaming file uploads.
[`Dream.multipart`](https://aantron.github.io/dream/#val-multipart) behaves just [`Dream.multipart`](https://aantron.github.io/dream/#val-multipart) behaves just
like [`Dream.form`](https://aantron.github.io/dream/#val-form) when it comes to like [`Dream.form`](https://aantron.github.io/dream/#val-form) when it comes to
[CSRF protection](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html). [CSRF protection](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html).
See example [**`d-form`**](../d-form#folders-and-files). We use See example [**`d-form`**](../d-form#files). We use
[`Dream.csrf_tag`](https://aantron.github.io/dream/#val-csrf_tag) to generate [`Dream.csrf_tag`](https://aantron.github.io/dream/#val-csrf_tag) to generate
the CSRF token in the template, and pass the `enctype="multipart/form-data"` the CSRF token in the template, and pass the `enctype="multipart/form-data"`
attribute as needed for forms to upload files. The template output looks like attribute as needed for forms to upload files. The template output looks like
@ -109,17 +110,17 @@ for a checklist of additional security precautions.
**Next steps:** **Next steps:**
- [**`h-sql`**](../h-sql#folders-and-files) runs SQL queries against a database. - [**`h-sql`**](../h-sql#files) runs SQL queries against a database.
- [**`i-graphql`**](../i-graphql#folders-and-files) handles GraphQL queries and serves - [**`i-graphql`**](../i-graphql#files) handles GraphQL queries and serves
GraphiQL. GraphiQL.
<br> <br>
**See also:** **See also:**
- [**`w-upload-stream`**](../w-upload-stream#folders-and-files) shows the streaming - [**`w-upload-stream`**](../w-upload-stream#files) shows the streaming
interface for receiving file uploads. interface for receiving file uploads.
- [**`w-multipart-dump`**](../w-multipart-dump#folders-and-files) shows the request body - [**`w-multipart-dump`**](../w-multipart-dump#files) shows the request body
that is interpreted by that is interpreted by
[`Dream.multipart`](https://aantron.github.io/dream/#val-multipart). [`Dream.multipart`](https://aantron.github.io/dream/#val-multipart).

View File

@ -7,3 +7,5 @@
(targets upload.ml) (targets upload.ml)
(deps upload.eml.ml) (deps upload.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root}))) (action (run dream_eml %{deps} --workspace %{workspace_root})))
(data_only_dirs _esy esy.lock lib node_modules)

17
example/g-upload/esy.json Normal file
View File

@ -0,0 +1,17 @@
{
"dependencies": {
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./upload.exe"
}
}

View File

@ -1,14 +0,0 @@
opam-version: "2.0"
depends: [
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -9,22 +9,21 @@ a library for talking to SQL databases:
```ocaml ```ocaml
module type DB = Caqti_lwt.CONNECTION module type DB = Caqti_lwt.CONNECTION
module R = Caqti_request
module T = Caqti_type module T = Caqti_type
let list_comments = let list_comments =
let query = let query =
let open Caqti_request.Infix in R.collect T.unit T.(tup2 int string)
(T.unit ->* T.(t2 int string)) "SELECT id, text FROM comment" in
"SELECT id, text FROM comment" in
fun (module Db : DB) -> fun (module Db : DB) ->
let%lwt comments_or_error = Db.collect_list query () in let%lwt comments_or_error = Db.collect_list query () in
Caqti_lwt.or_fail comments_or_error Caqti_lwt.or_fail comments_or_error
let add_comment = let add_comment =
let query = let query =
let open Caqti_request.Infix in R.exec T.string
(T.string ->. T.unit) "INSERT INTO comment (text) VALUES ($1)" in
"INSERT INTO comment (text) VALUES ($1)" in
fun text (module Db : DB) -> fun text (module Db : DB) ->
let%lwt unit_or_error = Db.exec query text in let%lwt unit_or_error = Db.exec query text in
Caqti_lwt.or_fail unit_or_error Caqti_lwt.or_fail unit_or_error
@ -67,13 +66,13 @@ let () =
``` ```
<pre><code><b>$ cd example/h-sql</b> <pre><code><b>$ cd example/h-sql</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./sql.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
Try visiting [http://localhost:8080](http://localhost:8080) and leaving some Try visiting [http://localhost:8080](http://localhost:8080)
comments! [[playground](http://dream.as/h-sql)] and leaving some comments!
![Comments](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/sql.png) ![Comments](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/sql.png)
@ -82,7 +81,7 @@ comments!
We take the opportunity to try out We take the opportunity to try out
[`Dream.sql_sessions`](https://aantron.github.io/dream/#val-sql_sessions), which [`Dream.sql_sessions`](https://aantron.github.io/dream/#val-sql_sessions), which
stores session data persistently in `db.sqlite`. See example stores session data persistently in `db.sqlite`. See example
[**`b-session`**](../b-session#folders-and-files) for an introduction to session management. [**`b-session`**](../b-session#files) for an introduction to session management.
Both the comments and the sessions survive server restarts. Both the comments and the sessions survive server restarts.
<br> <br>
@ -118,21 +117,18 @@ We also had to make an addition to our
</pre> </pre>
...and to ...and to
[`sql.opam`](https://github.com/aantron/dream/blob/master/example/h-sql/esy.json): [`esy.json`](https://github.com/aantron/dream/blob/master/example/h-sql/esy.json):
<pre>depends: [ <pre>"dependencies": {
<b>"caqti-driver-sqlite3" {>= "1.7.0"}</b> <b>"@opam/caqti-driver-sqlite3": "*"</b>
"ocaml" {>= "4.08.0"} }
"dream"
"dune" {>= "2.0.0"}
]
</pre> </pre>
<br> <br>
SQLite is good for small-to-medium sites and examples. For a larger site, SQLite is good for small-to-medium sites and examples. For a larger site,
microservices, or other needs, you can switch, for example, to PostgreSQL. See microservices, or other needs, you can switch, for example, to PostgreSQL. See
[**`w-postgres`**](../w-postgres#folders-and-files). [**`w-postgres`**](../w-postgres#files).
A good program for examining databases locally is A good program for examining databases locally is
[Beekeeper Studio](https://www.beekeeperstudio.io/). Dream might also integrate [Beekeeper Studio](https://www.beekeeperstudio.io/). Dream might also integrate
@ -156,9 +152,9 @@ See
**Next steps:** **Next steps:**
- [**`i-graphql`**](../i-graphql#folders-and-files) handles GraphQL queries and serves - [**`i-graphql`**](../i-graphql#files) handles GraphQL queries and serves
GraphiQL. GraphiQL.
- [**`j-stream`**](../j-stream#folders-and-files) streams response bodies to clients. - [**`j-stream`**](../j-stream#files) streams response bodies to clients.
<br> <br>

View File

@ -7,3 +7,5 @@
(targets sql.ml) (targets sql.ml)
(deps sql.eml.ml) (deps sql.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root}))) (action (run dream_eml %{deps} --workspace %{workspace_root})))
(data_only_dirs _esy esy.lock lib node_modules)

18
example/h-sql/esy.json Normal file
View File

@ -0,0 +1,18 @@
{
"dependencies": {
"@opam/caqti-driver-sqlite3": "*",
"@opam/dream": "1.0.0~alpha3",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"devDependencies": {
"@opam/ocaml-lsp-server": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#0b5eb6685b688649045aceac55dc559f6f21b829",
"esy-openssl": "esy-packages/esy-openssl#619ae2d46ca981ec26ab3287487ad98b157a01d1"
},
"scripts": {
"start": "dune exec --root . ./sql.exe"
}
}

View File

@ -1,15 +0,0 @@
opam-version: "2.0"
depends: [
"caqti-driver-sqlite3" {>= "1.7.0"}
"ocaml" {>= "4.08.0"}
"dream"
"dune" {>= "2.0.0"}
]
synopsis: "One of the Dream examples"
homepage: "https://github.com/aantron/dream"
bug-reports: "https://github.com/aantron/dream/issues"
author: "Anton Bachin <antonbachin@yahoo.com>"
license: "MIT"
maintainer: "Anton Bachin <antonbachin@yahoo.com>"

View File

@ -1,20 +1,19 @@
module type DB = Caqti_lwt.CONNECTION module type DB = Caqti_lwt.CONNECTION
module R = Caqti_request
module T = Caqti_type module T = Caqti_type
let list_comments = let list_comments =
let query = let query =
let open Caqti_request.Infix in R.collect T.unit T.(tup2 int string)
(T.unit ->* T.(t2 int string)) "SELECT id, text FROM comment" in
"SELECT id, text FROM comment" in
fun (module Db : DB) -> fun (module Db : DB) ->
let%lwt comments_or_error = Db.collect_list query () in let%lwt comments_or_error = Db.collect_list query () in
Caqti_lwt.or_fail comments_or_error Caqti_lwt.or_fail comments_or_error
let add_comment = let add_comment =
let query = let query =
let open Caqti_request.Infix in R.exec T.string
(T.string ->. T.unit) "INSERT INTO comment (text) VALUES ($1)" in
"INSERT INTO comment (text) VALUES ($1)" in
fun text (module Db : DB) -> fun text (module Db : DB) ->
let%lwt unit_or_error = Db.exec query text in let%lwt unit_or_error = Db.exec query text in
Caqti_lwt.or_fail unit_or_error Caqti_lwt.or_fail unit_or_error

View File

@ -19,7 +19,7 @@ let hardcoded_users = [
let user = let user =
Graphql_lwt.Schema.(obj "user" Graphql_lwt.Schema.(obj "user"
~fields:[ ~fields:(fun _info -> [
field "id" field "id"
~typ:(non_null int) ~typ:(non_null int)
~args:Arg.[] ~args:Arg.[]
@ -28,7 +28,7 @@ let user =
~typ:(non_null string) ~typ:(non_null string)
~args:Arg.[] ~args:Arg.[]
~resolve:(fun _info user -> user.name); ~resolve:(fun _info user -> user.name);
]) ]))
let schema = let schema =
Graphql_lwt.Schema.(schema [ Graphql_lwt.Schema.(schema [
@ -58,13 +58,13 @@ let () =
``` ```
<pre><code><b>$ cd example/i-graphql</b> <pre><code><b>$ cd example/i-graphql</b>
<b>$ opam install --deps-only --yes .</b> <b>$ npm install esy && npx esy</b>
<b>$ dune exec --root . ./graphql.exe</b></code></pre> <b>$ npx esy start</b></code></pre>
<br> <br>
Visit [http://localhost:8080](http://localhost:8080), and you can interact with Visit [http://localhost:8080](http://localhost:8080)
the schema: [[playground](http://dream.as/i-graphql)], and you can interact with the schema:
![GraphiQL](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/graphiql.png) ![GraphiQL](https://raw.githubusercontent.com/aantron/dream/master/docs/asset/graphiql.png)
@ -89,17 +89,17 @@ GraphiQL conditionally, only during development.
**Next steps:** **Next steps:**
- [**`j-stream`**](../j-stream#folders-and-files) streams response bodies to clients. - [**`j-stream`**](../j-stream#files) streams response bodies to clients.
- [**`k-websocket`**](../k-websocket#folders-and-files) sends and receives messages over a - [**`k-websocket`**](../k-websocket#files) sends and receives messages over a
WebSocket. WebSocket.
<br> <br>
**See also:** **See also:**
- [**`r-graphql`**](../r-graphql#folders-and-files) is a version of this example in Reason - [**`r-graphql`**](../r-graphql#files) is a version of this example in Reason
syntax. syntax.
- [**`w-graphql-subscription`**](../w-graphql-subscription#folders-and-files) for GraphQL - [**`w-graphql-subscription`**](../w-graphql-subscription#files) for GraphQL
subscriptions. subscriptions.
<br> <br>

View File

@ -1,3 +1,5 @@
(executable (executable
(name graphql) (name graphql)
(libraries dream)) (libraries dream))
(data_only_dirs _esy esy.lock lib node_modules)

Some files were not shown because too many files have changed in this diff Show More