Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .roc-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly-2026-09-19-d025939
nightly-2026-09-27-a3ce7f1
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ incomplete skips and platform-specific expected results.

Build an individual example (the server binary lands in the repo root):
```
roc build examples/hello-web.roc
roc build examples/health.roc
```

Files with a `.todoroc` extension are intentionally skipped migration backlog.
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ python scripts/build.py
Then run an example:

```sh
roc examples/hello-web.roc
roc examples/health.roc
```

The server listens on <http://127.0.0.1:8000> by default. Build one explicit
Expand Down Expand Up @@ -97,7 +97,7 @@ Active `.roc` examples must pass the suite on every supported operating system.
For a focused application build after building the host:

```sh
roc build examples/hello-web.roc
roc build examples/health.roc
```

CI also checks every Linux example under Valgrind Memcheck. On x86-64 Linux
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ Context : {}

program = { init!, respond!, shutdown! }

init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..])
init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)])
init! = || Ok({ config: Server.default_config, context: {} })

respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..])
respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)])
respond! = |_request, _context|
Ok(
Server.respond(
Expand All @@ -62,7 +62,7 @@ respond! = |_request, _context|
),
)

shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..])
shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)])
shutdown! = |_reason, _context| Ok({})
```

Expand Down Expand Up @@ -180,6 +180,9 @@ systems, time zones, and text formatting are deliberately left to the Roc
package ecosystem. The time-using examples demonstrate converting
`UnixTime.Timestamp` values with
[`roc-gregorian`](https://git.sr.ht/~jwoudenberg/roc/tree/main/item/gregorian).
Those six examples are temporarily kept as `.todoroc` files until the package
publishes a release compatible with the pinned compiler; each records a TODO
to restore the example and its HTTP specification cases.

Outbound HTTP calls default to a 30-second total deadline and an 8 MiB response
body. At most 64 calls run and 256 wait for admission. The shared client pools
Expand Down
12 changes: 8 additions & 4 deletions examples/command.roc → examples/command.todoroc
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# TODO: Restore this .roc example and its scripts/test_spec.json cases after
# roc-gregorian publishes a release without redundant return-position `..` unions.
# The pinned compiler rejects five such unions in 1.0.0-rc.3/Util.roc.
# Upstream: https://git.sr.ht/~jwoudenberg/roc/tree/main/item/gregorian
## Demonstrates command execution, captured output, environment variables, timeouts, and output limits.
app [Context, program] {
pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst",
http: "https://github.com/roc-lang/http/releases/download/1.0.0/6ZUwqYhCS8PU9Mo6MF7oV82ET2o7KYb57CLKDq4cq4sS.tar.zst",
gregorian: "https://cdn.jasperwoudenberg.com/roc-gregorian-v1.0.0-rc.3/3R8EMBQy6rYy3vbLY3u4CLcT8qwAPAyxaaGTA18Gknbe.tar.zst",
roc: "nightly-2026-09-19-d025939",
roc: "nightly-2026-09-27-a3ce7f1",
}

import pf.Server
Expand Down Expand Up @@ -66,7 +70,7 @@ init! = || {
Ok({ config: Server.default_config, context: { python, helper, examples_dir, scripts_dir } })
}

respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..])
respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)])
respond! = |req, { python, helper, examples_dir, scripts_dir }|
match req.target() {
Resource({ raw_path: "/cwd-examples", .. }) =>
Expand Down Expand Up @@ -104,7 +108,7 @@ respond! = |req, { python, helper, examples_dir, scripts_dir }|
}
}

command_cwd_response! : Str, Str, Path.Path => Try(Server.Outcome, [ServerErr(Str), ..])
command_cwd_response! : Str, Str, Path.Path => Try(Server.Outcome, [ServerErr(Str)])
command_cwd_response! = |python, helper, working_dir| {
output = Cmd.new_str(python)
.args_str([helper, "cwd", "0.2"])
Expand All @@ -120,5 +124,5 @@ command_cwd_response! = |python, helper, working_dir| {
text_response : Str -> Server.Outcome
text_response = |body| Server.respond(Response.from_status(200).with_body(Str.to_utf8(body)))

shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..])
shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)])
shutdown! = |_reason, _context| Ok({})
4 changes: 2 additions & 2 deletions examples/datastar/components/Animations.roc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Animations :: {

## Handle the page, finite view transition, and three retained timer stream
## routes owned by this component.
respond! : Animations, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : Animations, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |component, request, raw_path| {
method = request.method()
if component.page_target.matches(method, raw_path) {
Expand Down Expand Up @@ -117,7 +117,7 @@ Animations :: {
)
}

view_transition! : Animations, Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
view_transition! : Animations, Server.Request => Try(Server.Outcome, [ServerErr(Str)])
view_transition! = |component, request| {
parsed : Try(AnimationsSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down
4 changes: 2 additions & 2 deletions examples/datastar/components/BulkUpdate.roc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ BulkUpdate :: {
## Handle this component's page and action routes. The transition is selected
## only after its corresponding target matches, so callers cannot pair an
## activate request with deactivation behavior or vice versa.
respond! : BulkUpdate, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : BulkUpdate, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |component, request, raw_path| {
method = request.method()
if component.page_target.matches(method, raw_path) {
Expand Down Expand Up @@ -147,7 +147,7 @@ BulkUpdate :: {
}
}

apply_status! : BulkUpdate, Server.Request, Bool => Try(Server.Outcome, [ServerErr(Str), ..])
apply_status! : BulkUpdate, Server.Request, Bool => Try(Server.Outcome, [ServerErr(Str)])
apply_status! = |component, request, next_status| {
parsed : Try(BulkUpdateSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down
6 changes: 3 additions & 3 deletions examples/datastar/components/ClickToEdit.roc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ ClickToEdit :: {
}

## Handle every page and action route owned by this component.
respond! : ClickToEdit, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : ClickToEdit, Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |component, request, raw_path| {
method = request.method()
if component.page_target.matches(method, raw_path) {
Expand Down Expand Up @@ -212,7 +212,7 @@ definitions_for = |contact| {
}.Signals
}

save! : ClickToEdit, Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
save! : ClickToEdit, Server.Request => Try(Server.Outcome, [ServerErr(Str)])
save! = |component, request| {
parsed = read_signals!(request)
signals =
Expand All @@ -236,7 +236,7 @@ save! = |component, request| {
)
}

cancel! : ClickToEdit, Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
cancel! : ClickToEdit, Server.Request => Try(Server.Outcome, [ServerErr(Str)])
cancel! = |component, request| {
parsed = read_signals!(request)
signals =
Expand Down
2 changes: 1 addition & 1 deletion examples/datastar/components/ClickToLoad.roc
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ ClickToLoad :: {
)
}

more! : ClickToLoad, Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
more! : ClickToLoad, Server.Request => Try(Server.Outcome, [ServerErr(Str)])
more! = |component, request| {
parsed : Try(ClickToLoadSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down
10 changes: 5 additions & 5 deletions examples/datastar/components/CrudExamples.roc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ FileUploadSignals : { files : List(UploadedFile) }
## process-local storage.
CrudExamples :: [].{

respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |request, path|
match (request.method(), path) {
(GET, "/examples/delete_row") => Ok(Handled(page("Delete Row", "Delete table rows after confirmation, then restore the original rows.", delete_row_demo, "DELETE responses remove one selected row; reset returns the complete table body.")))
Expand Down Expand Up @@ -216,7 +216,7 @@ edit_row = |index| {
cancel_edit : U64 -> Server.Outcome
cancel_edit = |index| Datastar.respond([Datastar.patch_elements(Html.render_without_doc_type(display_edit_row(index)))])

save_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
save_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
save_edit! = |request, index| {
parsed : Try(EditSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand All @@ -240,7 +240,7 @@ file_upload_demo =
\\ <div id="file-upload" hidden></div>
\\</div>

upload_files! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
upload_files! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
upload_files! = |request| {
parsed : Try(FileUploadSignals, Datastar.SignalsError)
parsed = Datastar.read_signals_with_limit!(request, 2 * 1024 * 1024)
Expand Down Expand Up @@ -272,7 +272,7 @@ form_data_demo =
\\ <div id="form-data-result"></div>
\\</div>

form_data! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
form_data! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
form_data! = |request| {
bytes =
match request.method() {
Expand Down Expand Up @@ -303,7 +303,7 @@ inline_validation_demo =
\\ <div id="inline-validation-status"><p>Enter test@test.com and both names.</p><button id="validation-submit" aria-disabled="true">Sign Up</button></div>
\\</div>

validate_inline! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
validate_inline! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
validate_inline! = |request| {
parsed : Try(ValidationSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down
10 changes: 5 additions & 5 deletions examples/datastar/components/LoadingExamples.roc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ CounterSignals : { globalCount : U64, userCount : U64 }
## Server-driven loading, pagination, progress, morphing, and counter probes.
LoadingExamples :: [].{

respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |request, path|
match (request.method(), path) {
(GET, "/examples/dbmon") => Ok(Handled(page("DBmon", "Render a rapidly changing database activity table.", dbmon_demo, "A finite retained stream updates stable table and timing elements without retaining application state.")))
Expand Down Expand Up @@ -109,7 +109,7 @@ dbmon_transition! = |frame| {
Ok(Emit({ event: Datastar.patch_elements(elements), state: next, wake: After(40) }))
}

dbmon_inputs! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
dbmon_inputs! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
dbmon_inputs! = |request| {
parsed : Try(DbmonSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down Expand Up @@ -137,7 +137,7 @@ agent_rows = |start, count|
"<tr data-infinite-agent=\"${start_str}\"><td>Agent Smith ${start_str}</td><td>void${start_str}@null.org</td><td>agent-${start_str}</td></tr>${agent_rows(start + 1, count - 1)}"
}

infinite_scroll_more! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
infinite_scroll_more! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
infinite_scroll_more! = |request| {
parsed : Try(InfiniteScrollSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down Expand Up @@ -223,7 +223,7 @@ svg_morphing_demo : Str
svg_morphing_demo =
\\<div id="svg-morphing-demo" data-signals:circle-blue="false"><svg viewBox="0 0 100 100" width="160" height="160"><circle id="morph-circle" cx="50" cy="50" r="40" fill="red"></circle></svg><button data-action="morph-circle" data-on:click="@get('/examples/svg_morphing/circle_color')">Change Color</button></div>

svg_color! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
svg_color! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
svg_color! = |request| {
parsed : Try(SvgSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down Expand Up @@ -251,7 +251,7 @@ templ_counter_demo =

CounterKind := [Global, User]

increment_counter! : Server.Request, CounterKind => Try(Server.Outcome, [ServerErr(Str), ..])
increment_counter! : Server.Request, CounterKind => Try(Server.Outcome, [ServerErr(Str)])
increment_counter! = |request, kind| {
parsed : Try(CounterSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand Down
28 changes: 14 additions & 14 deletions examples/datastar/components/TodoMvc.roc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ TodoSignals : {
## stable application-root patch.
TodoMvc :: [].{

respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str), ..])
respond! : Server.Request, Str => Try([Handled(Server.Outcome), NotHandled], [ServerErr(Str)])
respond! = |request, path| {
parts = Str.split_on(path, "/")
match (request.method(), parts) {
Expand Down Expand Up @@ -291,7 +291,7 @@ respond_state = |state|

ParsedState : [Parsed(TodoSignals), Rejected(Server.Outcome)]

read_state! : Server.Request => Try(ParsedState, [ServerErr(Str), ..])
read_state! : Server.Request => Try(ParsedState, [ServerErr(Str)])
read_state! = |request| {
parsed : Try(TodoSignals, Datastar.SignalsError)
parsed = Datastar.read_signals!(request)
Expand All @@ -303,14 +303,14 @@ read_state! = |request| {
)
}

mutate! : Server.Request, (TodoSignals -> TodoSignals) => Try(Server.Outcome, [ServerErr(Str), ..])
mutate! : Server.Request, (TodoSignals -> TodoSignals) => Try(Server.Outcome, [ServerErr(Str)])
mutate! = |request, transform|
match read_state!(request)? {
Rejected(outcome) => Ok(outcome)
Parsed(state) => Ok(respond_state(transform(state)))
}

add! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
add! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
add! = |request|
match read_state!(request)? {
Rejected(outcome) => Ok(outcome)
Expand All @@ -333,7 +333,7 @@ add! = |request|
}
}

toggle_all! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
toggle_all! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
toggle_all! = |request|
mutate!(
request,
Expand All @@ -343,20 +343,20 @@ toggle_all! = |request|
},
)

set_mode! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
set_mode! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
set_mode! = |request, mode| mutate!(request, |state| { ..state, mode })

delete_completed! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
delete_completed! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
delete_completed! = |request|
mutate!(request, |state| { ..state, todos: state.todos.keep_if(|todo| Bool.not(todo.completed)) })

cancel_edit! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
cancel_edit! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
cancel_edit! = |request| mutate!(request, |state| { ..state, editTitle: "", editingId: -1 })

reset! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..])
reset! : Server.Request => Try(Server.Outcome, [ServerErr(Str)])
reset! = |request| mutate!(request, |_state| default_state)

route_id_action! : Server.Request, Str, TodoAction => Try(Server.Outcome, [ServerErr(Str), ..])
route_id_action! : Server.Request, Str, TodoAction => Try(Server.Outcome, [ServerErr(Str)])
route_id_action! = |request, raw_id, action|
match parse_id(raw_id) {
Err(_) => Ok(not_found)
Expand All @@ -369,7 +369,7 @@ route_id_action! = |request, raw_id, action|
}
}

toggle! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
toggle! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
toggle! = |request, id|
mutate!(
request,
Expand All @@ -385,7 +385,7 @@ toggle! = |request, id|
},
)

start_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
start_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
start_edit! = |request, id|
match read_state!(request)? {
Rejected(outcome) => Ok(outcome)
Expand All @@ -406,7 +406,7 @@ start_edit! = |request, id|
}
}

save_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
save_edit! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
save_edit! = |request, id|
match read_state!(request)? {
Rejected(outcome) => Ok(outcome)
Expand All @@ -428,7 +428,7 @@ save_edit! = |request, id|
}
}

delete! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str), ..])
delete! : Server.Request, U64 => Try(Server.Outcome, [ServerErr(Str)])
delete! = |request, id|
mutate!(request, |state| { ..state, todos: state.todos.keep_if(|todo| todo.id != id) })

Expand Down
Loading
Loading