Skip to content

Commit 12374dc

Browse files
committed
0 parents  commit 12374dc

File tree

17 files changed

+1179
-0
lines changed

17 files changed

+1179
-0
lines changed

.github/workflows/test.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: haskell-actions/setup@v2
17+
with:
18+
ghc-version: "9.6.7"
19+
20+
- name: Cache
21+
uses: actions/cache@v4
22+
with:
23+
path: |
24+
~/.cabal/store
25+
dist-newstyle
26+
key: ${{ runner.os }}-cabal-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/*.hs') }}
27+
restore-keys: |
28+
${{ runner.os }}-cabal-${{ hashFiles('**/*.cabal') }}-
29+
${{ runner.os }}-cabal-
30+
31+
- name: Install dependencies
32+
run: cabal update && cabal build --only-dependencies
33+
34+
- name: Build
35+
run: cabal build
36+
37+
- name: Run tests
38+
run: cabal test

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
dist
2+
dist-*
3+
cabal-dev
4+
*.o
5+
*.hi
6+
*.hie
7+
*.chi
8+
*.chs.h
9+
*.dyn_o
10+
*.dyn_hi
11+
.hpc
12+
.hsenv
13+
.cabal-sandbox/
14+
cabal.sandbox.config
15+
*.prof
16+
*.aux
17+
*.hp
18+
*.eventlog
19+
.stack-work/
20+
cabal.project.local
21+
cabal.project.local~
22+
.HTF/
23+
.ghc.environment.*
24+
.DS_Store
25+
26+
static-build/

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## 0.1.0.0
4+
5+
* Initial release.

LICENSE

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright © Star Federation
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<p align="center"><img width="150" height="150" src="https://data-star.dev/static/images/rocket-512x512.png"></p>
2+
3+
# Datastar Haskell SDK
4+
5+
[![Test](https://github.com/carlohamalainen/datastar-hs/actions/workflows/test.yml/badge.svg)](https://github.com/carlohamalainen/datastar-hs/actions/workflows/test.yml)
6+
7+
A Haskell implementation of the [Datastar](https://data-star.dev/) SDK for building real-time hypermedia applications with server-sent events (SSE).
8+
9+
Live examples: <https://hamalainen.dev>
10+
11+
To minimise dependencies for the library, examples are hosted in a separate repo: <https://github.com/carlohamalainen/datastar-hs-examples>
12+
13+
## License
14+
15+
This package is licensed for free under the [MIT License](LICENSE).
16+
17+
## Design
18+
19+
The SDK is built on [WAI](https://github.com/yesodweb/wai) (Web Application
20+
Interface), Haskell's standard interface for HTTP servers. This means it works
21+
with any WAI-compatible server (Warp, etc.) and any framework built on WAI
22+
(Yesod, Scotty, Servant, etc.) without framework-specific adapters.
23+
24+
Key design decisions:
25+
26+
- **Minimal dependencies** -- the library depends only on `aeson`, `bytestring`,
27+
`http-types`, `text`, and `wai`.
28+
- **WAI streaming** -- SSE responses use WAI's native `responseStream`, giving
29+
you a `ServerSentEventGenerator` callback with `sendPatchElements`,
30+
`sendPatchSignals`, and `sendExecuteScript`.
31+
- **No routing opinion** -- the SDK provides request helpers (`readSignals`,
32+
`isDatastarRequest`) but doesn't impose a routing framework. The examples use
33+
simple pattern matching on `(requestMethod, pathInfo)`.
34+
35+
## API Overview
36+
37+
```haskell
38+
import Hypermedia.Datastar
39+
40+
-- Create an SSE response
41+
sseResponse :: (ServerSentEventGenerator -> IO ()) -> Response
42+
43+
-- Send events
44+
sendPatchElements :: ServerSentEventGenerator -> PatchElements -> IO ()
45+
sendPatchSignals :: ServerSentEventGenerator -> PatchSignals -> IO ()
46+
sendExecuteScript :: ServerSentEventGenerator -> ExecuteScript -> IO ()
47+
48+
-- Read signals from a request (query string for GET, body for POST)
49+
readSignals :: FromJSON a => Request -> IO (Either String a)
50+
```
51+
52+
## Quick Start
53+
54+
Add `datastar-hs` to your `build-depends`, then:
55+
56+
```haskell
57+
import Hypermedia.Datastar
58+
import Network.Wai
59+
import Network.Wai.Handler.Warp qualified as Warp
60+
61+
app :: Application
62+
app req respond =
63+
case (requestMethod req, pathInfo req) of
64+
("GET", ["hello"]) -> do
65+
Right signals <- readSignals req
66+
respond $ sseResponse $ \gen -> do
67+
sendPatchElements gen (patchElements "<div id=\"message\">Hello!</div>")
68+
_ ->
69+
respond $ responseLBS status404 [] "Not found"
70+
71+
main :: IO ()
72+
main = Warp.run 3000 app
73+
```

datastar-hs.cabal

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
cabal-version: 3.0
2+
name: datastar-hs
3+
version: 0.1.0.0
4+
synopsis: Haskell bindings for Datastar
5+
description:
6+
Server-side SDK for building real-time hypermedia applications with
7+
<https://data-star.dev/ Datastar>. Stream HTML fragments, reactive
8+
signal updates, and scripts to the browser over server-sent events
9+
(SSE). Built on WAI so it works with Warp, Scotty, Servant, Yesod,
10+
and any other WAI-compatible framework.
11+
homepage: https://github.com/carlohamalainen/datastar-hs
12+
bug-reports: https://github.com/carlohamalainen/datastar-hs/issues
13+
license: MIT
14+
license-file: LICENSE
15+
author: Carlo Hamalainen
16+
maintainer: carlo@carlo-hamalainen.net
17+
category: Web, Hypermedia
18+
build-type: Simple
19+
extra-doc-files: CHANGELOG.md
20+
21+
library
22+
exposed-modules:
23+
Hypermedia.Datastar
24+
Hypermedia.Datastar.ExecuteScript
25+
Hypermedia.Datastar.Logger
26+
Hypermedia.Datastar.PatchElements
27+
Hypermedia.Datastar.PatchSignals
28+
Hypermedia.Datastar.Types
29+
Hypermedia.Datastar.WAI
30+
hs-source-dirs: src
31+
default-language: Haskell2010
32+
default-extensions:
33+
ImportQualifiedPost
34+
OverloadedStrings
35+
build-depends:
36+
, base >= 4.14 && < 5
37+
, aeson >= 2.0 && < 3
38+
, bytestring >= 0.10 && < 1
39+
, http-types >= 0.12 && < 1
40+
, text >= 1.2 && < 3
41+
, wai >= 3.2 && < 4
42+
43+
test-suite datastar-hs-test
44+
type: exitcode-stdio-1.0
45+
hs-source-dirs: test
46+
main-is: Main.hs
47+
other-modules:
48+
Hypermedia.Datastar.PatchElementsSpec
49+
Hypermedia.Datastar.SSESpec
50+
build-depends:
51+
, base
52+
, bytestring
53+
, datastar-hs
54+
, hspec
55+
, text
56+
, wai
57+
default-language: Haskell2010
58+
default-extensions:
59+
ImportQualifiedPost
60+
OverloadedStrings

fourmolu.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
indentation: 2
2+
function-arrows: leading
3+
comma-style: leading
4+
import-export-style: leading

src/Hypermedia/Datastar.hs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
-- |
2+
-- Module : Hypermedia.Datastar
3+
-- Description : Haskell SDK for building real-time hypermedia apps with Datastar
4+
--
5+
-- <https://data-star.dev/ Datastar> is a hypermedia framework: instead of
6+
-- building a JSON API and a JavaScript SPA, you write HTML on the server and
7+
-- let Datastar handle the interactivity. The browser sends requests, the
8+
-- server holds the connection open as a server-sent event (SSE) stream, and
9+
-- pushes HTML fragments, signal updates, or scripts back to the browser as
10+
-- things change.
11+
--
12+
-- This SDK provides the server-side Haskell API. It builds on
13+
-- <https://hackage.haskell.org/package/wai WAI> so it works with Warp, Scotty,
14+
-- Servant, Yesod, or any other WAI-compatible framework.
15+
--
16+
-- === Minimal example
17+
--
18+
-- @
19+
-- import Hypermedia.Datastar
20+
-- import Network.Wai (Application, responseLBS, requestMethod, pathInfo)
21+
-- import Network.Wai.Handler.Warp qualified as Warp
22+
-- import Network.HTTP.Types (status404)
23+
--
24+
-- app :: Application
25+
-- app req respond =
26+
-- case (requestMethod req, pathInfo req) of
27+
-- (\"GET\", [\"hello\"]) ->
28+
-- respond $ sseResponse $ \\gen ->
29+
-- sendPatchElements gen (patchElements \"\<div id=\\\"msg\\\"\>Hello!\<\/div\>\")
30+
-- _ ->
31+
-- respond $ responseLBS status404 [] \"Not found\"
32+
--
33+
-- main :: IO ()
34+
-- main = Warp.run 3000 app
35+
-- @
36+
--
37+
-- === Module guide
38+
--
39+
-- * "Hypermedia.Datastar.PatchElements" — send HTML to morph into the DOM
40+
-- * "Hypermedia.Datastar.PatchSignals" — update the browser's reactive signals
41+
-- * "Hypermedia.Datastar.ExecuteScript" — run JavaScript in the browser
42+
-- * "Hypermedia.Datastar.WAI" — SSE streaming, signal decoding, request helpers
43+
-- * "Hypermedia.Datastar.Types" — protocol types and defaults
44+
--
45+
-- === Further reading
46+
--
47+
-- * <https://data-star.dev/ Datastar homepage> — guides, reference, and examples
48+
-- * <https://github.com/carlohamalainen/datastar-hs-examples Examples repository> — full working Haskell examples
49+
-- * <https://cljdoc.org/d/dev.data-star.clojure/http-kit/1.0.0-RC7/doc/sdk-docs/using-datastar Clojure SDK docs> — excellent Datastar walkthrough that applies across SDKs
50+
module Hypermedia.Datastar
51+
( -- * Types
52+
EventType (..)
53+
, ElementPatchMode (..)
54+
, ElementNamespace (..)
55+
56+
-- * Patch Elements
57+
, PatchElements (..)
58+
, patchElements
59+
, removeElements
60+
61+
-- * Patch Signals
62+
, PatchSignals (..)
63+
, patchSignals
64+
65+
-- * Execute Script
66+
, ExecuteScript (..)
67+
, executeScript
68+
69+
-- * WAI
70+
, ServerSentEventGenerator
71+
, sseResponse
72+
, sendPatchElements
73+
, sendPatchSignals
74+
, sendExecuteScript
75+
, readSignals
76+
, isDatastarRequest
77+
)
78+
where
79+
80+
import Hypermedia.Datastar.ExecuteScript (ExecuteScript (..), executeScript)
81+
import Hypermedia.Datastar.PatchElements (PatchElements (..), patchElements, removeElements)
82+
import Hypermedia.Datastar.PatchSignals (PatchSignals (..), patchSignals)
83+
import Hypermedia.Datastar.Types (ElementNamespace (..), ElementPatchMode (..), EventType (..))
84+
import Hypermedia.Datastar.WAI
85+
( ServerSentEventGenerator
86+
, isDatastarRequest
87+
, readSignals
88+
, sendExecuteScript
89+
, sendPatchElements
90+
, sendPatchSignals
91+
, sseResponse
92+
)

0 commit comments

Comments
 (0)