From 181928805e65271865eabdc7aaab1ed8470f7e56 Mon Sep 17 00:00:00 2001 From: mariano Date: Wed, 30 Sep 2026 13:39:29 -0500 Subject: [PATCH 1/3] Drop redundant open tag unions in return position; pin nightly-2026-09-27 Since nightly-2026-09-23 the compiler opens a tag union in return position on its own and warns on an explicit `..` there ("redundant open tag union"). roc build exits non-zero on any warning, so an application on basic-webserver cannot build against the current nightlies: the platform's own sources raise sixty of them. This removes every explicit `..` in return position in platform/ (60 sites in nine files, driven by the compiler's warning list) and in the examples' own code, and moves the pinned nightly to nightly-2026-09-27-a3ce7f1 in .roc-version and every example manifest. No signature widens and no behaviour changes: the compiler now opens those unions itself. platform/main.roc checks with zero warnings on the new pin; the examples still see warnings from the released platform tarball and packages they pin by URL, which the next release resolves. --- .roc-version | 2 +- examples/command.roc | 8 +-- examples/datastar/components/Animations.roc | 4 +- examples/datastar/components/BulkUpdate.roc | 4 +- examples/datastar/components/ClickToEdit.roc | 6 +-- examples/datastar/components/ClickToLoad.roc | 2 +- examples/datastar/components/CrudExamples.roc | 10 ++-- .../datastar/components/LoadingExamples.roc | 10 ++-- examples/datastar/components/TodoMvc.roc | 28 +++++----- examples/datastar/main.roc | 10 ++-- examples/dir.roc | 8 +-- examples/echo.roc | 8 +-- examples/env.roc | 8 +-- examples/error-handling.roc | 14 ++--- .../file-accessed-modified-created-time.roc | 7 ++- examples/file-permissions.roc | 7 ++- examples/file-read-buffered.roc | 8 +-- examples/file-read.roc | 8 +-- examples/file-upload.roc | 8 +-- examples/form-file-upload.roc | 10 ++-- examples/form-url-encoded.roc | 10 ++-- examples/health.roc | 8 +-- examples/hello-web.roc | 8 +-- examples/http.roc | 6 +-- examples/init-basic.roc | 8 +-- examples/ndjson-ingest.roc | 8 +-- examples/request-limits.roc | 8 +-- examples/server-lifecycle.roc | 8 +-- examples/sleep.roc | 8 +-- examples/sqlite.roc | 8 +-- examples/sse.roc | 8 +-- examples/static-files.roc | 8 +-- examples/tcp.roc | 8 +-- examples/temp-dir.roc | 8 +-- examples/todos.roc | 8 +-- examples/try.roc | 8 +-- platform/Cmd.roc | 12 ++--- platform/Env.roc | 8 +-- platform/Http.roc | 16 +++--- platform/MultipartFormData.roc | 6 +-- platform/Path.roc | 52 +++++++++---------- platform/Sqlite.roc | 2 +- platform/Stderr.roc | 6 +-- platform/Stdout.roc | 6 +-- platform/Url.roc | 12 ++--- 45 files changed, 208 insertions(+), 210 deletions(-) diff --git a/.roc-version b/.roc-version index 3d3c7490..ede85fb4 100644 --- a/.roc-version +++ b/.roc-version @@ -1 +1 @@ -nightly-2026-09-19-d025939 +nightly-2026-09-27-a3ce7f1 diff --git a/examples/command.roc b/examples/command.roc index caf6bfc3..c161d540 100644 --- a/examples/command.roc +++ b/examples/command.roc @@ -3,7 +3,7 @@ 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 @@ -66,7 +66,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", .. }) => @@ -104,7 +104,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"]) @@ -120,5 +120,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({}) diff --git a/examples/datastar/components/Animations.roc b/examples/datastar/components/Animations.roc index baddd9f0..322a2964 100644 --- a/examples/datastar/components/Animations.roc +++ b/examples/datastar/components/Animations.roc @@ -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) { @@ -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) diff --git a/examples/datastar/components/BulkUpdate.roc b/examples/datastar/components/BulkUpdate.roc index 6d61fe1a..008de953 100644 --- a/examples/datastar/components/BulkUpdate.roc +++ b/examples/datastar/components/BulkUpdate.roc @@ -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) { @@ -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) diff --git a/examples/datastar/components/ClickToEdit.roc b/examples/datastar/components/ClickToEdit.roc index 12efec61..e5668a8d 100644 --- a/examples/datastar/components/ClickToEdit.roc +++ b/examples/datastar/components/ClickToEdit.roc @@ -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) { @@ -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 = @@ -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 = diff --git a/examples/datastar/components/ClickToLoad.roc b/examples/datastar/components/ClickToLoad.roc index ae15cfc0..65a98eb2 100644 --- a/examples/datastar/components/ClickToLoad.roc +++ b/examples/datastar/components/ClickToLoad.roc @@ -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) diff --git a/examples/datastar/components/CrudExamples.roc b/examples/datastar/components/CrudExamples.roc index 96551a22..b1cf9d0c 100644 --- a/examples/datastar/components/CrudExamples.roc +++ b/examples/datastar/components/CrudExamples.roc @@ -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."))) @@ -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) @@ -240,7 +240,7 @@ file_upload_demo = \\ \\ -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) @@ -272,7 +272,7 @@ form_data_demo = \\
\\ -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() { @@ -303,7 +303,7 @@ inline_validation_demo = \\

Enter test@test.com and both names.

\\ -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) diff --git a/examples/datastar/components/LoadingExamples.roc b/examples/datastar/components/LoadingExamples.roc index eb6bc24e..1a05286b 100644 --- a/examples/datastar/components/LoadingExamples.roc +++ b/examples/datastar/components/LoadingExamples.roc @@ -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."))) @@ -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) @@ -137,7 +137,7 @@ agent_rows = |start, count| "Agent Smith ${start_str}void${start_str}@null.orgagent-${start_str}${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) @@ -223,7 +223,7 @@ svg_morphing_demo : Str svg_morphing_demo = \\
-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) @@ -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) diff --git a/examples/datastar/components/TodoMvc.roc b/examples/datastar/components/TodoMvc.roc index 0e2214e9..857ce38e 100644 --- a/examples/datastar/components/TodoMvc.roc +++ b/examples/datastar/components/TodoMvc.roc @@ -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) { @@ -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) @@ -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) @@ -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, @@ -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) @@ -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, @@ -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) @@ -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) @@ -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) }) diff --git a/examples/datastar/main.roc b/examples/datastar/main.roc index 21061b97..33756552 100644 --- a/examples/datastar/main.roc +++ b/examples/datastar/main.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import ./components/ClickToLoad @@ -41,10 +41,10 @@ animations = Animations.default 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.with_request_body_limit(Server.default_config, 2 * 1024 * 1024), context: {} }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, _context| { path = match request.target() { @@ -110,7 +110,7 @@ respond! = |request, _context| { } } -active_search! : Server.Request => Try(Server.Outcome, [ServerErr(Str), ..]) +active_search! : Server.Request => Try(Server.Outcome, [ServerErr(Str)]) active_search! = |request| { signals_result : Try(ActiveSearchSignals, Datastar.SignalsError) signals_result = Datastar.read_signals!(request) @@ -344,5 +344,5 @@ text_response = |status, body| .with_headers([{ name: "Content-Type", value: "text/plain; charset=utf-8" }]) .with_body(Str.to_utf8(body)) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/dir.roc b/examples/dir.roc index 32315350..51345699 100644 --- a/examples/dir.roc +++ b/examples/dir.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Stdout @@ -16,7 +16,7 @@ program = { init!, respond!, shutdown! } init! : () => Try( { config : Server.Config, context : Context }, - [Exit(I64), FailedToListExamples(_), FailedToPrintExamples(_), ..], + [Exit(I64), FailedToListExamples(_), FailedToPrintExamples(_)], ) init! = || { @@ -28,9 +28,9 @@ init! = || { Ok({ config: Server.default_config, context: paths_str }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, paths_str| Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8(paths_str)))) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/echo.roc b/examples/echo.roc index 3a37af69..82763527 100644 --- a/examples/echo.roc +++ b/examples/echo.roc @@ -3,7 +3,7 @@ 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 @@ -16,7 +16,7 @@ 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.with_timeouts( @@ -32,7 +32,7 @@ init! = || context: {}, }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |req, _context| { time = (Time.unix_epoch + UnixTime.now!().seconds_since_epoch()).iso8601() @@ -131,5 +131,5 @@ header_value = |headers, wanted| } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/env.roc b/examples/env.roc index c4c086e9..9fa4ed62 100644 --- a/examples/env.roc +++ b/examples/env.roc @@ -5,7 +5,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Env @@ -16,14 +16,14 @@ Context : [DebugPrintMode, NonDebugMode] program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || match Env.var_str!("DEBUG") { Ok("1") => Ok({ config: Server.default_config, context: DebugPrintMode }) _ => Ok({ config: Server.default_config, context: NonDebugMode }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, mode| match mode { DebugPrintMode => { @@ -32,5 +32,5 @@ respond! = |_request, mode| NonDebugMode => Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8("DEBUG var not set")))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/error-handling.roc b/examples/error-handling.roc index f6ed6796..3c8d7c5a 100644 --- a/examples/error-handling.roc +++ b/examples/error-handling.roc @@ -3,7 +3,7 @@ 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.Stdout @@ -21,7 +21,7 @@ program = { init!, respond!, shutdown! } init! : () => Try( { config : Server.Config, context : Context }, - [Exit(I64), InvalidTargetUrl(_), MissingTargetUrl(_), ..], + [Exit(I64), InvalidTargetUrl(_), MissingTargetUrl(_)], ) init! = || { target = Env.var_str!("TARGET_URL") ? |err| MissingTargetUrl(err) @@ -32,7 +32,7 @@ init! = || { respond! : Server.Request, Context => Try( Server.Outcome, - [FailedToFetch(_), FailedToLogRequest(_), ServerErr(Str), ..], + [FailedToFetch(_), FailedToLogRequest(_), ServerErr(Str)], ) respond! = |req, target_url| { response = handle_req!(req, target_url)? @@ -42,7 +42,7 @@ respond! = |req, target_url| { ## Semantic application errors can flow directly to the platform. The platform ## logs their inspected values and returns a generic HTTP 500 without exposing ## the details to the client. -handle_req! : Server.Request, Url => Try(Response, [FailedToFetch(_), FailedToLogRequest(_), ..]) +handle_req! : Server.Request, Url => Try(Response, [FailedToFetch(_), FailedToLogRequest(_)]) handle_req! = |req, target_url| { # `?` returns early when an effect is `Err`, preserving its typed error tag. log_request!(req)? @@ -51,7 +51,7 @@ handle_req! = |req, target_url| { Ok(response_with_code(200, content)) } -log_request! : Server.Request => Try({}, [FailedToLogRequest(_), ..]) +log_request! : Server.Request => Try({}, [FailedToLogRequest(_)]) log_request! = |req| { datetime = (Time.unix_epoch + UnixTime.now!().seconds_since_epoch()).iso8601() @@ -60,7 +60,7 @@ log_request! = |req| { Ok({}) } -fetch_content! : Url => Try(Str, [FailedToFetch(_), ..]) +fetch_content! : Url => Try(Str, [FailedToFetch(_)]) fetch_content! = |url| Http.get_utf8!(url).map_err(|err| FailedToFetch(err)) ## Build an in-memory response with the given status and UTF-8 body. @@ -68,5 +68,5 @@ response_with_code : U16, Str -> Response response_with_code = |code, body| Response.from_status(code).with_body(Str.to_utf8(body)) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/file-accessed-modified-created-time.roc b/examples/file-accessed-modified-created-time.roc index 83d54719..68921978 100644 --- a/examples/file-accessed-modified-created-time.roc +++ b/examples/file-accessed-modified-created-time.roc @@ -3,7 +3,7 @@ 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.Stdout @@ -25,7 +25,6 @@ init! : () => Try( FailedToReadAccessedTime(_), FailedToReadCreatedTime(_), FailedToReadModifiedTime(_), - .., ], ) init! = || { @@ -41,11 +40,11 @@ init! = || { Ok({ config: Server.default_config, context: summary }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, summary| Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8(summary)))) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) format_timestamp : UnixTime.Timestamp -> Str diff --git a/examples/file-permissions.roc b/examples/file-permissions.roc index 3a02eafd..af55c59e 100644 --- a/examples/file-permissions.roc +++ b/examples/file-permissions.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Stdout @@ -22,7 +22,6 @@ init! : () => Try( FailedToCheckReadable(_), FailedToCheckWritable(_), FailedToPrintPermissions(_), - .., ], ) init! = || { @@ -38,9 +37,9 @@ init! = || { Ok({ config: Server.default_config, context: summary }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, summary| Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8(summary)))) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/file-read-buffered.roc b/examples/file-read-buffered.roc index 6126802d..b42e0c3c 100644 --- a/examples/file-read-buffered.roc +++ b/examples/file-read-buffered.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.File @@ -21,7 +21,7 @@ program = { init!, respond!, shutdown! } init! : () => Try( { config : Server.Config, context : Context }, - [Exit(I64), FailedToOpenLicense(_), FailedToReadLicense(_), ..], + [Exit(I64), FailedToOpenLicense(_), FailedToReadLicense(_)], ) init! = || { reader = File.open_reader!(Path.utf8("LICENSE")) ? |err| FailedToOpenLicense(err) @@ -30,7 +30,7 @@ init! = || { Ok({ config: Server.default_config, context: summary }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_, summary| Ok( Server.respond( @@ -40,7 +40,7 @@ respond! = |_, summary| ), ) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) ## Recursively read one bounded line at a time, accumulating line and byte counts. diff --git a/examples/file-read.roc b/examples/file-read.roc index 9b791882..1ea0a5ed 100644 --- a/examples/file-read.roc +++ b/examples/file-read.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Path @@ -13,15 +13,15 @@ Context : Str program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), FailedToReadSource(_), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), FailedToReadSource(_)]) init! = || { contents = Path.read_utf8!(Path.utf8("examples/file-read.roc")) ? |err| FailedToReadSource(err) Ok({ config: Server.default_config, context: "Source code of current program:\n\n${contents}" }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, contents| Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8(contents)))) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/file-upload.roc b/examples/file-upload.roc index e601bd28..f8819fde 100644 --- a/examples/file-upload.roc +++ b/examples/file-upload.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Path @@ -21,7 +21,7 @@ Context : { program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { uploads = Server.writable_root({ id: "uploads-write", @@ -68,7 +68,7 @@ upload_config = |uploads, readback| timeout_ms: 100, }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, context| match (request.method(), request.target()) { (PUT, Resource({ raw_path: "/upload/alpha", .. })) => @@ -170,5 +170,5 @@ text_response = |status, body| .with_headers([{ name: "Content-Type", value: "text/plain; charset=utf-8" }]) .with_body(Str.to_utf8(body)) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/form-file-upload.roc b/examples/form-file-upload.roc index d32f583c..d410e257 100644 --- a/examples/form-file-upload.roc +++ b/examples/form-file-upload.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -13,7 +13,7 @@ Context : {} program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { config = Server.with_request_body_limit(Server.default_config, 10 * 1024 * 1024) Ok({ config, context: {} }) @@ -43,7 +43,7 @@ upload_form = ), ) -display_uploaded_image! : Server.Request => Try(Response, [ServerErr(Str), ..]) +display_uploaded_image! : Server.Request => Try(Response, [ServerErr(Str)]) display_uploaded_image! = |req| { body = req.body().with_limit(10 * 1024 * 1024).read_all!() ? |err| ServerErr("Failed to read multipart form-data: ${Str.inspect(err)}") @@ -95,7 +95,7 @@ display_uploaded_image! = |req| { } } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |req, _context| match req.method() { GET => Ok(Server.respond(upload_form)) @@ -103,7 +103,7 @@ respond! = |req, _context| _ => Ok(Server.respond(Response.from_status(405))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) png_signature : List(U8) diff --git a/examples/form-url-encoded.roc b/examples/form-url-encoded.roc index 67f1b2b4..5507d90c 100644 --- a/examples/form-url-encoded.roc +++ b/examples/form-url-encoded.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -15,7 +15,7 @@ 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: {} }) form_page : Response @@ -52,7 +52,7 @@ form_page = { ) } -display_form_data! : Server.Request => Try(Response, [ServerErr(Str), ..]) +display_form_data! : Server.Request => Try(Response, [ServerErr(Str)]) display_form_data! = |req| { body = req.body().with_limit(64 * 1024).read_all!() ? |err| ServerErr("Failed to read URL-encoded form: ${Str.inspect(err)}") @@ -63,7 +63,7 @@ display_form_data! = |req| { } } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |req, _context| match req.method() { GET => Ok(Server.respond(form_page)) @@ -116,5 +116,5 @@ text_response = |status, body| .with_headers([{ name: "Content-Type", value: "text/plain; charset=utf-8" }]) .with_body(Str.to_utf8(body)) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/health.roc b/examples/health.roc index abfc89c7..b2607b20 100644 --- a/examples/health.roc +++ b/examples/health.roc @@ -5,7 +5,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Env @@ -18,7 +18,7 @@ Context : { readiness : Server.Readiness } program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { readiness = Server.Readiness.create!(NotReady) ? |_| Exit(1) @@ -61,7 +61,7 @@ init! = || { }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, context| match request.target() { Resource({ raw_path: "/set-ready", .. }) => { @@ -83,7 +83,7 @@ respond! = |request, context| _ => Ok(text_response(404, "not found")) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, context| { result = context.readiness.set!(Ready) Stdout.line!("shutdown readiness update: ${Str.inspect(result)}") ?? {} diff --git a/examples/hello-web.roc b/examples/hello-web.roc index cb58fbba..cb47bc72 100644 --- a/examples/hello-web.roc +++ b/examples/hello-web.roc @@ -3,7 +3,7 @@ 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 @@ -19,7 +19,7 @@ program = { init!, respond!, shutdown! } # `init!` can validate configuration, run migrations, or prepare immutable # startup data. This example has no startup data, so its context is `{}`. -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { config = Server.default_config .with_access_log( @@ -32,7 +32,7 @@ init! = || { Ok({ config, context: {} }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, _context| { datetime = (Time.unix_epoch + UnixTime.now!().seconds_since_epoch()).iso8601() @@ -42,5 +42,5 @@ respond! = |request, _context| { Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8("Hello from server
")))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/http.roc b/examples/http.roc index 65f00f86..42b6e021 100644 --- a/examples/http.roc +++ b/examples/http.roc @@ -3,7 +3,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Stdout @@ -81,7 +81,7 @@ demo! = || { } } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |server_request, _context| match server_request.target() { Resource({ raw_path: "/limit", .. }) => { @@ -113,5 +113,5 @@ text_response = |body| .with_body(Str.to_utf8(body)), ) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/init-basic.roc b/examples/init-basic.roc index f8c16841..f364ba75 100644 --- a/examples/init-basic.roc +++ b/examples/init-basic.roc @@ -3,7 +3,7 @@ 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.Stdout @@ -19,10 +19,10 @@ program = { init!, respond!, shutdown! } # `init!` can validate configuration, run migrations, or prepare immutable # startup data. Here it stores a gift string in the context. -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! = |req, gift| { datetime = (Time.unix_epoch + UnixTime.now!().seconds_since_epoch()).iso8601() @@ -32,5 +32,5 @@ respond! = |req, gift| { Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8("init gave me ${gift}")))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/ndjson-ingest.roc b/examples/ndjson-ingest.roc index 5d37dbce..065acc46 100644 --- a/examples/ndjson-ingest.roc +++ b/examples/ndjson-ingest.roc @@ -3,7 +3,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Env @@ -282,7 +282,7 @@ ingest_flush! = |ingest, db| { }) } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { db_path = match Env.var!("DB_PATH") { @@ -304,7 +304,7 @@ init! = || { Ok({ config, context: db }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, db| { response = match (request.method(), request.target()) { @@ -316,7 +316,7 @@ respond! = |request, db| { Ok(Server.respond(response)) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _db| Ok({}) ingest_events! : Sqlite.Db, Server.Request => Response diff --git a/examples/request-limits.roc b/examples/request-limits.roc index 8f6d2806..da7c6d69 100644 --- a/examples/request-limits.roc +++ b/examples/request-limits.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Env @@ -15,7 +15,7 @@ Context : {} program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { limits = match Env.var_str!("INVALID_REQUEST_LIMITS") { @@ -29,7 +29,7 @@ init! = || { }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, {}| { target = match request.target() { @@ -48,7 +48,7 @@ respond! = |request, {}| { ) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |reason, {}| match reason { StartupFailed(detail) => { diff --git a/examples/server-lifecycle.roc b/examples/server-lifecycle.roc index 9c04dd66..7165d4ff 100644 --- a/examples/server-lifecycle.roc +++ b/examples/server-lifecycle.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -13,10 +13,10 @@ Context : Str 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: "lifecycle context" }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, context| { if context != "lifecycle context" { return Err(ServerErr("Request received unexpected context")) @@ -29,7 +29,7 @@ respond! = |_request, context| { shutdown! : Server.ShutdownReason, Context => Try( {}, - [Exit(I64), FailedToLogShutdown(_), UnexpectedShutdown({ context : Str, reason : Server.ShutdownReason }), ..], + [Exit(I64), FailedToLogShutdown(_), UnexpectedShutdown({ context : Str, reason : Server.ShutdownReason })], ) shutdown! = |reason, context| match reason { diff --git a/examples/sleep.roc b/examples/sleep.roc index d2f83906..db282a75 100644 --- a/examples/sleep.roc +++ b/examples/sleep.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -14,7 +14,7 @@ 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.with_limits( @@ -28,7 +28,7 @@ init! = || context: {}, }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, _context| { target = match request.target() { @@ -55,5 +55,5 @@ respond! = |request, _context| { } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/sqlite.roc b/examples/sqlite.roc index d691a553..4db23ed8 100644 --- a/examples/sqlite.roc +++ b/examples/sqlite.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -25,7 +25,7 @@ Context : { db : Sqlite.Db } program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { db_path = match Env.var!("DB_PATH") { @@ -36,7 +36,7 @@ init! = || { Ok({ config: Server.default_config, context: { db: db } }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |_request, { db }| { match query_todos_by_status!(db, Completed) { Ok(todos) => { @@ -52,7 +52,7 @@ respond! = |_request, { db }| { } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) Todo : { id : I64, status : TodoStatus, task : Str } diff --git a/examples/sse.roc b/examples/sse.roc index 6f19a76f..16cdf26d 100644 --- a/examples/sse.roc +++ b/examples/sse.roc @@ -2,7 +2,7 @@ ## callback admission, timer scheduling, output framing, and cancellation. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -12,10 +12,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| { path = match request.target() { @@ -106,5 +106,5 @@ transition! = |state| _ => Err(StreamFailed("invalid retained source state")) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/static-files.roc b/examples/static-files.roc index 4b3af463..6ae598f5 100644 --- a/examples/static-files.roc +++ b/examples/static-files.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Path @@ -17,7 +17,7 @@ Context : { program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64)]) init! = || { assets = Server.file_root_with_cache({ id: "assets", @@ -58,7 +58,7 @@ init! = || { }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, context| { target = request.target() Stdout.line!("Roc handled ${Str.inspect(target)}") @@ -91,5 +91,5 @@ respond! = |request, context| { } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/tcp.roc b/examples/tcp.roc index 578fc580..ca95c6cd 100644 --- a/examples/tcp.roc +++ b/examples/tcp.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -16,10 +16,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| { body = request.body().with_limit(64 * 1024).read_all!() ? |err| ServerErr("Failed to read request body: ${Str.inspect(err)}") @@ -37,5 +37,5 @@ respond! = |request, _context| { Ok(Server.respond(Response.from_status(200).with_body(echoed))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/temp-dir.roc b/examples/temp-dir.roc index 1834ec7d..89957ab0 100644 --- a/examples/temp-dir.roc +++ b/examples/temp-dir.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -14,15 +14,15 @@ 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| { temp_dir_str = Path.display(Env.temp_dir!()) Ok(Server.respond(Response.from_status(200).with_body(Str.to_utf8("The temp dir path is ${temp_dir_str}")))) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/todos.roc b/examples/todos.roc index 8708378f..83c58c92 100644 --- a/examples/todos.roc +++ b/examples/todos.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Env @@ -39,7 +39,7 @@ CreateTodoBody : { task : Str, status : Str } program = { init!, respond!, shutdown! } -init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), FailedToEnsureSchema(_), ..]) +init! : () => Try({ config : Server.Config, context : Context }, [Exit(I64), FailedToEnsureSchema(_)]) init! = || { db_path = match Env.var!("DB_PATH") { @@ -53,7 +53,7 @@ init! = || { Ok({ config: Server.default_config, context: db }) } -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |req, db| match handle_req!(req, db) { Ok(response) => Ok(Server.respond(response)) @@ -218,5 +218,5 @@ text_response = |status, body| .with_headers([{ name: "Content-Type", value: "text/plain; charset=utf-8" }]) .with_body(Str.to_utf8(body)) -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) diff --git a/examples/try.roc b/examples/try.roc index 91a3f3ea..9d13e4c0 100644 --- a/examples/try.roc +++ b/examples/try.roc @@ -2,7 +2,7 @@ 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", - roc: "nightly-2026-09-19-d025939", + roc: "nightly-2026-09-27-a3ce7f1", } import pf.Server @@ -12,10 +12,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| { body = request.body().with_limit(1024).read_all!() ? |err| ServerErr("Failed to read request body: ${Str.inspect(err)}") @@ -28,7 +28,7 @@ respond! = |request, _context| { } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_reason, _context| Ok({}) ## Return `Ok(Good)` or `Ok(Bad)` for accepted input and a typed diff --git a/platform/Cmd.roc b/platform/Cmd.roc index fa093115..b8179551 100644 --- a/platform/Cmd.roc +++ b/platform/Cmd.roc @@ -45,7 +45,7 @@ Cmd := [ default_output_limit_bytes = 1024 * 1024 ## Execute a program with native arguments, inheriting standard streams. - exec! : OsStr, List(OsStr) => Try({}, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), ..]) + exec! : OsStr, List(OsStr) => Try({}, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str })]) exec! = |program, arguments| { command = "${OsStr.display(program)} ${Str.join_with(arguments.map(OsStr.display), " ")}" exit_code = new(program).args(arguments).exec_exit_code!()? @@ -58,12 +58,12 @@ Cmd := [ } ## Execute a UTF-8 program with UTF-8 arguments. - exec_str! : Str, List(Str) => Try({}, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), ..]) + exec_str! : Str, List(Str) => Try({}, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str })]) exec_str! = |program, arguments| exec!(OsStr.from_str(program), arguments.map(OsStr.from_str)) ## Execute a configured command, inheriting standard streams. - exec_cmd! : Cmd => Try({}, [ExecCmdFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), ..]) + exec_cmd! : Cmd => Try({}, [ExecCmdFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str })]) exec_cmd! = |cmd| { command = to_str(cmd) exit_code = exec_exit_code!(cmd)? @@ -77,7 +77,7 @@ Cmd := [ ## Execute a command and capture stdout as UTF-8 and stderr lossily. ## Use [exec_output_bytes!] when either stream must be preserved exactly. - exec_output! : Cmd => Try({ stdout_utf8 : Str, stderr_utf8_lossy : Str }, [StdoutContainsInvalidUtf8({ cmd_str : Str, err : [BadUtf8({ problem : _, index : U64 })] }), NonZeroExitCode({ command : Str, exit_code : I32, stdout_utf8_lossy : Str, stderr_utf8_lossy : Str }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), StdoutLimitExceeded({ command : Str, limit_bytes : U64, received_at_least : U64 }), StderrLimitExceeded({ command : Str, limit_bytes : U64, received_at_least : U64 }), ..]) + exec_output! : Cmd => Try({ stdout_utf8 : Str, stderr_utf8_lossy : Str }, [StdoutContainsInvalidUtf8({ cmd_str : Str, err : [BadUtf8({ problem : _, index : U64 })] }), NonZeroExitCode({ command : Str, exit_code : I32, stdout_utf8_lossy : Str, stderr_utf8_lossy : Str }), FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), StdoutLimitExceeded({ command : Str, limit_bytes : U64, received_at_least : U64 }), StderrLimitExceeded({ command : Str, limit_bytes : U64, received_at_least : U64 })]) exec_output! = |Cmd(cmd)| { command = Cmd(cmd) cmd_str = to_str(command) @@ -107,7 +107,7 @@ Cmd := [ } ## Execute a command and capture stdout and stderr without text conversion. - exec_output_bytes! : Cmd => Try({ stderr_bytes : List(U8), stdout_bytes : List(U8) }, [NonZeroExitCodeB({ exit_code : I32, stdout_bytes : List(U8), stderr_bytes : List(U8) }), FailedToGetExitCodeB(IOErr), CommandTimedOutB(U64), CommandSaturatedB, StdoutLimitExceededB({ limit_bytes : U64, received_at_least : U64 }), StderrLimitExceededB({ limit_bytes : U64, received_at_least : U64 }), ..]) + exec_output_bytes! : Cmd => Try({ stderr_bytes : List(U8), stdout_bytes : List(U8) }, [NonZeroExitCodeB({ exit_code : I32, stdout_bytes : List(U8), stderr_bytes : List(U8) }), FailedToGetExitCodeB(IOErr), CommandTimedOutB(U64), CommandSaturatedB, StdoutLimitExceededB({ limit_bytes : U64, received_at_least : U64 }), StderrLimitExceededB({ limit_bytes : U64, received_at_least : U64 })]) exec_output_bytes! = |Cmd(cmd)| match Host.cmd_exec_output!(to_host_cmd(Cmd(cmd)), to_host_working_dir(Cmd(cmd))) { Ok({ stderr_bytes, stdout_bytes }) => Ok({ stdout_bytes, stderr_bytes }) @@ -121,7 +121,7 @@ Cmd := [ } ## Execute a command and return its exit code. - exec_exit_code! : Cmd => Try(I32, [FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str }), ..]) + exec_exit_code! : Cmd => Try(I32, [FailedToGetExitCode({ command : Str, err : IOErr }), CommandTimedOut({ command : Str, timeout_ms : U64 }), CommandSaturated({ command : Str })]) exec_exit_code! = |Cmd(cmd)| { command = to_str(Cmd(cmd)) diff --git a/platform/Env.roc b/platform/Env.roc index f8b9e9c2..e16508fe 100644 --- a/platform/Env.roc +++ b/platform/Env.roc @@ -14,7 +14,7 @@ Env := [].{ OS : [LINUX, MACOS, WINDOWS, OTHER(Str)] ## Read an environment variable using an exact native name and value. - var! : OsStr => Try(OsStr, [VarNotFound(OsStr), EnvErr(IOErr), ..]) + var! : OsStr => Try(OsStr, [VarNotFound(OsStr), EnvErr(IOErr)]) var! = |name| match Host.env_var!(OsStr.to_raw(name)) { Ok(raw) => Ok(OsStr.from_raw(raw)) @@ -24,7 +24,7 @@ Env := [].{ ## Read an environment variable whose value must be valid Unicode text. ## The name remains native-safe; quoted names work through OsStr.from_quote. - var_str! : OsStr => Try(Str, [VarNotFound(OsStr), EnvErr(IOErr), InvalidStr(U64), ..]) + var_str! : OsStr => Try(Str, [VarNotFound(OsStr), EnvErr(IOErr), InvalidStr(U64)]) var_str! = |name| match var!(name) { Ok(value) => @@ -38,7 +38,7 @@ Env := [].{ ## Read the byte-preserving directory inherited when the platform launched. ## The platform never changes this process-global directory. - cwd! : () => Try(Path, [CwdUnavailable, ..]) + cwd! : () => Try(Path, [CwdUnavailable]) cwd! = || { if Host.env_is_windows!("") { match Host.env_cwd_windows!("") { @@ -54,7 +54,7 @@ Env := [].{ } ## Return the path to the currently running executable. - exe_path! : () => Try(Path, [ExePathUnavailable, ..]) + exe_path! : () => Try(Path, [ExePathUnavailable]) exe_path! = || { if Host.env_is_windows!("") { match Host.env_exe_path_windows!("") { diff --git a/platform/Http.roc b/platform/Http.roc index 9aa10818..b29648ac 100644 --- a/platform/Http.roc +++ b/platform/Http.roc @@ -71,12 +71,12 @@ Http :: [].{ ## request = Request.from_method(GET).with_uri("https://www.roc-lang.org") ## response = Http.send!(request)? ## ``` - send! : Request => Try(Response, [InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr), ..]) + send! : Request => Try(Response, [InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr)]) send! = |request| send_with!(request, default_config) ## Send using an explicit finite resource policy. A timeout attached to the ## shared http Request takes precedence; NoTimeout uses this config. - send_with! : Request, Config => Try(Response, [InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr), ..]) + send_with! : Request, Config => Try(Response, [InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr)]) send_with! = |request, config| { url = Url.parse(Request.uri(request)) ? InvalidUrl canonical_url = Url.without_fragment(url) @@ -89,7 +89,7 @@ Http :: [].{ ## ## This uses Roc's builtin JSON encoder, so the value's type determines the ## encoder through static dispatch. - with_json_body : Request, _ => Try(Request, [JsonErr(_), ..]) + with_json_body : Request, _ => Try(Request, [JsonErr(_)]) with_json_body = |request, value| { body = Json.to_str_try(value) ? JsonErr @@ -101,7 +101,7 @@ Http :: [].{ } ## Encode a value as JSON, attach it to the request body, and send it. - send_json! : Request, _ => Try(Response, [JsonErr(_), InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr), ..]) + send_json! : Request, _ => Try(Response, [JsonErr(_), InvalidUrl(Url.ParseErr), InvalidRequest(Str), HttpErr(TransportErr)]) send_json! = |request, value| { json_request = with_json_body(request, value)? @@ -116,7 +116,7 @@ Http :: [].{ ## ```roc ## hello_str = Http.get_utf8!("http://localhost:8000")? ## ``` - get_utf8! : Url.Url => Try(Str, [BadBody(Str), InvalidRequest(Str), HttpErr(TransportErr), ..]) + get_utf8! : Url.Url => Try(Str, [BadBody(Str), InvalidRequest(Str), HttpErr(TransportErr)]) get_utf8! = |url| { response = send_validated!(Request.from_method(GET).with_uri(Url.to_str(url)), default_config)? body = Str.from_utf8(Response.body(response)) ? |_| BadBody("get_utf8!: response body was not valid UTF-8") @@ -128,7 +128,7 @@ Http :: [].{ ## ## This uses Roc's builtin JSON parser, so the expected result type ## determines the parser through static dispatch. - decode_json_response : Response => Try(_, [BadBody(Str), JsonErr(_), ..]) + decode_json_response : Response => Try(_, [BadBody(Str), JsonErr(_)]) decode_json_response = |response| { body = Str.from_utf8(Response.body(response)) ? |_| BadBody("decode_json_response: response body was not valid UTF-8") decoded = Json.parse(body) ? JsonErr @@ -145,7 +145,7 @@ Http :: [].{ ## payload : Try({ foo : Str }, _) ## payload = Http.get!("http://localhost:8000") ## ``` - get! : Url.Url => Try(_, [BadBody(Str), InvalidRequest(Str), HttpErr(TransportErr), JsonErr(_), ..]) + get! : Url.Url => Try(_, [BadBody(Str), InvalidRequest(Str), HttpErr(TransportErr), JsonErr(_)]) get! = |url| { response = send_validated!(Request.from_method(GET).with_uri(Url.to_str(url)), default_config)? @@ -155,7 +155,7 @@ Http :: [].{ # Send a request whose URI was constructed from a validated Url. Keeping this # private prevents get!/get_utf8! from exposing an impossible InvalidUrl error. -send_validated! : Request.Request, Http.Config => Try(Response.Response, [InvalidRequest(Str), HttpErr(InternalHttp.TransportErr), ..]) +send_validated! : Request.Request, Http.Config => Try(Response.Response, [InvalidRequest(Str), HttpErr(InternalHttp.TransportErr)]) send_validated! = |request, config| { host_response = match Host.http_send_request!( diff --git a/platform/MultipartFormData.roc b/platform/MultipartFormData.roc index 432a4482..810d4fee 100644 --- a/platform/MultipartFormData.roc +++ b/platform/MultipartFormData.roc @@ -155,7 +155,7 @@ parse_all_headers = |bytes| { } ## Parses the body of a multipart/form-data request. -parse_form_data : { body : List(U8), boundary : List(U8) } -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart, ..]) +parse_form_data : { body : List(U8), boundary : List(U8) } -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart]) parse_form_data = |{ body, boundary }| { start_marker = List.concat(doubledash, boundary) end_marker = List.concat(List.concat(List.concat(List.concat(newline, doubledash), boundary), doubledash), newline) @@ -173,7 +173,7 @@ parse_form_data = |{ body, boundary }| { } } -parse_parts : List(List(U8)), List(ParsedFormData) -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart, ..]) +parse_parts : List(List(U8)), List(ParsedFormData) -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart]) parse_parts = |parts, parsed| match parts { [] => Ok(parsed) @@ -184,7 +184,7 @@ parse_parts = |parts, parsed| } } -multipart_data_error : [Boundary, Part] -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart, ..]) +multipart_data_error : [Boundary, Part] -> Try(List(ParsedFormData), [ExpectedEnclosedByBoundary, InvalidPart]) multipart_data_error = |kind| match kind { Boundary => Err(ExpectedEnclosedByBoundary) diff --git a/platform/Path.roc b/platform/Path.roc index 2f81dc0f..89569814 100644 --- a/platform/Path.roc +++ b/platform/Path.roc @@ -46,7 +46,7 @@ Path := [ ## This function does not traverse symbolic links; symbolic links (including ## broken ones) return `Bool.False`. ## Sockets, FIFOs, devices, and other special filesystem objects also return `Bool.False`. - is_file! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_file! : Path => Try(Bool, [PathErr(IOErr)]) is_file! = |path| match type!(path) { Ok(IsFile) => Ok(Bool.True) @@ -60,7 +60,7 @@ Path := [ ## This function does not traverse symbolic links; symbolic links (including ## broken ones) return `Bool.False`. ## Sockets, FIFOs, devices, and other special filesystem objects also return `Bool.False`. - is_dir! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_dir! : Path => Try(Bool, [PathErr(IOErr)]) is_dir! = |path| match type!(path) { Ok(IsDir) => Ok(Bool.True) @@ -74,7 +74,7 @@ Path := [ ## This function will not traverse symbolic links - it checks whether the path ## itself is a symlink. ## Sockets, FIFOs, devices, and other special filesystem objects return `Bool.False`. - is_sym_link! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_sym_link! : Path => Try(Bool, [PathErr(IOErr)]) is_sym_link! = |path| match type!(path) { Ok(IsSymLink) => Ok(Bool.True) @@ -84,7 +84,7 @@ Path := [ } ## Returns `True` if the path exists on disk. - exists! : Path => Try(Bool, [PathErr(IOErr), ..]) + exists! : Path => Try(Bool, [PathErr(IOErr)]) exists! = |path| match type!(path) { Ok(_) => Ok(Bool.True) @@ -97,7 +97,7 @@ Path := [ ## `IsOther` represents sockets, FIFOs, block and character devices, and any ## platform-specific object that is not a regular file, directory, or symbolic ## link. On Windows this includes unrecognized reparse-point types. - type! : Path => Try([IsFile, IsDir, IsSymLink, IsOther], [PathErr(IOErr), ..]) + type! : Path => Try([IsFile, IsDir, IsSymLink, IsOther], [PathErr(IOErr)]) type! = |path| { Host.path_type!(to_host_raw!(path)) .map_err(|err| PathErr(err)) @@ -105,86 +105,86 @@ Path := [ } ## Read all bytes from a file at this path. - read_bytes! : Path => Try(List(U8), [PathErr(IOErr), ..]) + read_bytes! : Path => Try(List(U8), [PathErr(IOErr)]) read_bytes! = |path| map_file_result(Host.file_read_bytes!(to_host_raw!(path))) ## Write bytes to a file at this path, replacing any existing contents. - write_bytes! : Path, List(U8) => Try({}, [PathErr(IOErr), ..]) + write_bytes! : Path, List(U8) => Try({}, [PathErr(IOErr)]) write_bytes! = |path, bytes| map_file_result(Host.file_write_bytes!(to_host_raw!(path), bytes)) ## Read a UTF-8 file at this path. - read_utf8! : Path => Try(Str, [PathErr(IOErr), ..]) + read_utf8! : Path => Try(Str, [PathErr(IOErr)]) read_utf8! = |path| map_file_result(Host.file_read_utf8!(to_host_raw!(path))) ## Write a UTF-8 file at this path, replacing any existing contents. - write_utf8! : Path, Str => Try({}, [PathErr(IOErr), ..]) + write_utf8! : Path, Str => Try({}, [PathErr(IOErr)]) write_utf8! = |path, content| map_file_result(Host.file_write_utf8!(to_host_raw!(path), content)) ## Delete a file at this path. - delete! : Path => Try({}, [PathErr(IOErr), ..]) + delete! : Path => Try({}, [PathErr(IOErr)]) delete! = |path| map_file_result(Host.file_delete!(to_host_raw!(path))) ## Return the size of the file at this path in bytes. - size_in_bytes! : Path => Try(U64, [PathErr(IOErr), ..]) + size_in_bytes! : Path => Try(U64, [PathErr(IOErr)]) size_in_bytes! = |path| map_file_result(Host.file_size_in_bytes!(to_host_raw!(path))) ## Check whether the file is executable under the native platform's file ## conventions. - is_executable! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_executable! : Path => Try(Bool, [PathErr(IOErr)]) is_executable! = |path| map_file_result(Host.file_is_executable!(to_host_raw!(path))) ## Check whether the current process can open the file for reading. - is_readable! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_readable! : Path => Try(Bool, [PathErr(IOErr)]) is_readable! = |path| map_file_result(Host.file_is_readable!(to_host_raw!(path))) ## Check whether the current process can open the file for writing. The file ## is not modified. - is_writable! : Path => Try(Bool, [PathErr(IOErr), ..]) + is_writable! : Path => Try(Bool, [PathErr(IOErr)]) is_writable! = |path| map_file_result(Host.file_is_writable!(to_host_raw!(path))) ## Return the last accessed time as a POSIX timestamp. - time_accessed! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr), ..]) + time_accessed! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr)]) time_accessed! = |path| map_file_result(Host.file_time_accessed!(to_host_raw!(path))).map_ok(timestamp_from_host) ## Return the last modified time as a POSIX timestamp. - time_modified! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr), ..]) + time_modified! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr)]) time_modified! = |path| map_file_result(Host.file_time_modified!(to_host_raw!(path))).map_ok(timestamp_from_host) ## Return the creation time as a POSIX timestamp. - time_created! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr), ..]) + time_created! : Path => Try(UnixTime.Timestamp, [PathErr(IOErr)]) time_created! = |path| map_file_result(Host.file_time_created!(to_host_raw!(path))).map_ok(timestamp_from_host) ## Create a hard link at `link` pointing to `original`. - hard_link! : Path, Path => Try({}, [PathErr(IOErr), ..]) + hard_link! : Path, Path => Try({}, [PathErr(IOErr)]) hard_link! = |original, link| map_file_result(Host.file_hard_link!(to_host_raw!(original), to_host_raw!(link))) ## Rename a file from `from` to `to`. - rename! : Path, Path => Try({}, [PathErr(IOErr), ..]) + rename! : Path, Path => Try({}, [PathErr(IOErr)]) rename! = |from, to| map_file_result(Host.file_rename!(to_host_raw!(from), to_host_raw!(to))) ## Create a directory at this path. - create_dir! : Path => Try({}, [PathErr(IOErr), ..]) + create_dir! : Path => Try({}, [PathErr(IOErr)]) create_dir! = |path| map_dir_result(Host.dir_create!(to_host_raw!(path))) ## Create a directory and any missing parent directories at this path. - create_all! : Path => Try({}, [PathErr(IOErr), ..]) + create_all! : Path => Try({}, [PathErr(IOErr)]) create_all! = |path| map_dir_result(Host.dir_create_all!(to_host_raw!(path))) ## Delete an empty directory at this path. - delete_empty! : Path => Try({}, [PathErr(IOErr), ..]) + delete_empty! : Path => Try({}, [PathErr(IOErr)]) delete_empty! = |path| map_dir_result(Host.dir_delete_empty!(to_host_raw!(path))) ## Delete a directory and all contents at this path. - delete_all! : Path => Try({}, [PathErr(IOErr), ..]) + delete_all! : Path => Try({}, [PathErr(IOErr)]) delete_all! = |path| map_dir_result(Host.dir_delete_all!(to_host_raw!(path))) ## List the entries in the directory at this path. - list! : Path => Try(List(Path), [PathErr(IOErr), ..]) + list! : Path => Try(List(Path), [PathErr(IOErr)]) list! = |path| match Host.dir_list!(to_host_raw!(path)) { Ok(paths) => Ok(paths.map(from_host_raw)) @@ -385,14 +385,14 @@ from_host_raw = |raw| Path.unix_bytes(raw.unix_bytes) } -map_file_result : Try(a, [FileErr(IOErr)]) -> Try(a, [PathErr(IOErr), ..]) +map_file_result : Try(a, [FileErr(IOErr)]) -> Try(a, [PathErr(IOErr)]) map_file_result = |result| match result { Ok(value) => Ok(value) Err(FileErr(err)) => Err(PathErr(err)) } -map_dir_result : Try(a, [DirErr(IOErr)]) -> Try(a, [PathErr(IOErr), ..]) +map_dir_result : Try(a, [DirErr(IOErr)]) -> Try(a, [PathErr(IOErr)]) map_dir_result = |result| match result { Ok(value) => Ok(value) diff --git a/platform/Sqlite.roc b/platform/Sqlite.roc index e7a59eed..b091a7c7 100644 --- a/platform/Sqlite.roc +++ b/platform/Sqlite.roc @@ -833,7 +833,7 @@ find_field = |fields, name| { } } -validate_unique_columns : List(Str) -> Try({}, [DuplicateColumn(Str), ..]) +validate_unique_columns : List(Str) -> Try({}, [DuplicateColumn(Str)]) validate_unique_columns = |columns| { var $index = 0 while $index < columns.len() { diff --git a/platform/Stderr.roc b/platform/Stderr.roc index 1f1c6451..6372471f 100644 --- a/platform/Stderr.roc +++ b/platform/Stderr.roc @@ -8,7 +8,7 @@ Stderr := [].{ ## followed by a newline. ## ## > To write to `stderr` without the newline, see [Stderr.write!]. - line! : Str => Try({}, [StderrErr(IOErr), ..]) + line! : Str => Try({}, [StderrErr(IOErr)]) line! = |message| match Host.stderr_line!(message) { Ok(done) => Ok(done) @@ -21,7 +21,7 @@ Stderr := [].{ ## so this may appear to do nothing until you write a newline! ## ## > To write to `stderr` with a newline at the end, see [Stderr.line!]. - write! : Str => Try({}, [StderrErr(IOErr), ..]) + write! : Str => Try({}, [StderrErr(IOErr)]) write! = |message| match Host.stderr_write!(message) { Ok(done) => Ok(done) @@ -32,7 +32,7 @@ Stderr := [].{ ## ## Most terminals will not actually display content that are written to them until they receive a newline, ## so this may appear to do nothing until you write a newline! - write_bytes! : List(U8) => Try({}, [StderrErr(IOErr), ..]) + write_bytes! : List(U8) => Try({}, [StderrErr(IOErr)]) write_bytes! = |bytes| match Host.stderr_write_bytes!(bytes) { Ok(done) => Ok(done) diff --git a/platform/Stdout.roc b/platform/Stdout.roc index ee2f6862..cce15ff7 100644 --- a/platform/Stdout.roc +++ b/platform/Stdout.roc @@ -8,7 +8,7 @@ Stdout := [].{ ## followed by a newline. ## ## > To write to `stdout` without the newline, see [Stdout.write!]. - line! : Str => Try({}, [StdoutErr(IOErr), ..]) + line! : Str => Try({}, [StdoutErr(IOErr)]) line! = |message| match Host.stdout_line!(message) { Ok(done) => Ok(done) @@ -21,7 +21,7 @@ Stdout := [].{ ## so this may appear to do nothing until you write a newline! ## ## > To write to `stdout` with a newline at the end, see [Stdout.line!]. - write! : Str => Try({}, [StdoutErr(IOErr), ..]) + write! : Str => Try({}, [StdoutErr(IOErr)]) write! = |message| match Host.stdout_write!(message) { Ok(done) => Ok(done) @@ -32,7 +32,7 @@ Stdout := [].{ ## ## Note that many terminals will not actually display content that is written to them until they receive a newline, ## so this may appear to do nothing until you write a newline! - write_bytes! : List(U8) => Try({}, [StdoutErr(IOErr), ..]) + write_bytes! : List(U8) => Try({}, [StdoutErr(IOErr)]) write_bytes! = |bytes| match Host.stdout_write_bytes!(bytes) { Ok(done) => Ok(done) diff --git a/platform/Url.roc b/platform/Url.roc index b7bc08ed..2848b111 100644 --- a/platform/Url.roc +++ b/platform/Url.roc @@ -370,7 +370,7 @@ validate_host = |raw_host| { } } -validate_dns_name : Str -> Try(Str, [InvalidHost(Str), ..]) +validate_dns_name : Str -> Try(Str, [InvalidHost(Str)]) validate_dns_name = |raw_host| { host = ascii_lower(raw_host) labels = Str.split_on(host, ".") @@ -393,7 +393,7 @@ validate_dns_name = |raw_host| { } } -validate_ipv4 : Str -> Try(Str, [InvalidIpv4(Str), ..]) +validate_ipv4 : Str -> Try(Str, [InvalidIpv4(Str)]) validate_ipv4 = |raw_host| { parts = Str.split_on(raw_host, ".") if parts.len() != 4 { @@ -429,7 +429,7 @@ parse_ipv4_parts = |parts, out| } } -parse_port : Str, [Http, Https] -> Try([None, Some(U16)], [InvalidPort(Str), PortOutOfRange(U64), ..]) +parse_port : Str, [Http, Https] -> Try([None, Some(U16)], [InvalidPort(Str), PortOutOfRange(U64)]) parse_port = |raw, scheme| { # A valid U16 port has at most five decimal digits. Reject longer input # before parsing so decimal accumulation cannot overflow U64. @@ -475,7 +475,7 @@ parse_decimal = |raw| { # Addresses are validated as eight hexadecimal groups, with at most one :: # elision. IPv4-in-IPv6 syntax is outside this module's deliberately small # subset. Serialization expands elided groups and removes leading zeroes. -validate_ipv6 : Str -> Try(Str, [InvalidIpv6(Str), ..]) +validate_ipv6 : Str -> Try(Str, [InvalidIpv6(Str)]) validate_ipv6 = |raw| { pieces = Str.split_on(raw, "::") if pieces.len() > 2 { @@ -500,7 +500,7 @@ validate_ipv6 = |raw| { } } -parse_ipv6_side : Str -> Try(List(U16), [InvalidIpv6(Str), ..]) +parse_ipv6_side : Str -> Try(List(U16), [InvalidIpv6(Str)]) parse_ipv6_side = |raw| if Str.is_empty(raw) { Ok([]) @@ -508,7 +508,7 @@ parse_ipv6_side = |raw| parse_hex_groups(Str.split_on(raw, ":"), []) } -parse_hex_groups : List(Str), List(U16) -> Try(List(U16), [InvalidIpv6(Str), ..]) +parse_hex_groups : List(Str), List(U16) -> Try(List(U16), [InvalidIpv6(Str)]) parse_hex_groups = |parts, out| match parts { [] => Ok(out) From 6488784332a9138d603b07ad45e4e2e476871196 Mon Sep 17 00:00:00 2001 From: mariano Date: Wed, 30 Sep 2026 20:03:06 -0500 Subject: [PATCH 2/3] Regenerate the Rust glue on nightly-2026-09-27; drop the perf apps' explicit open unions --- scripts/perf/app.roc | 6 +- scripts/perf/sse_app.roc | 6 +- src/roc_platform_abi.rs | 274 +++++++++++++++------------------------ 3 files changed, 112 insertions(+), 174 deletions(-) diff --git a/scripts/perf/app.roc b/scripts/perf/app.roc index 4a19de76..0e87aa1b 100644 --- a/scripts/perf/app.roc +++ b/scripts/perf/app.roc @@ -15,7 +15,7 @@ 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.with_limits( @@ -29,7 +29,7 @@ init! = || context: {}, }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, _context| { raw_path = match request.target() { @@ -50,5 +50,5 @@ respond! = |request, _context| { } } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_, _| Ok({}) diff --git a/scripts/perf/sse_app.roc b/scripts/perf/sse_app.roc index 7df7a18e..2286eafe 100644 --- a/scripts/perf/sse_app.roc +++ b/scripts/perf/sse_app.roc @@ -25,7 +25,7 @@ StreamState : { 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.with_sse_limits( @@ -45,7 +45,7 @@ init! = || context: {}, }) -respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str), ..]) +respond! : Server.Request, Context => Try(Server.Outcome, [ServerErr(Str)]) respond! = |request, _context| { raw_path = match request.target() { @@ -202,5 +202,5 @@ html_payload_with_padding = |padding, sequence| { .concat(suffix) } -shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64), ..]) +shutdown! : Server.ShutdownReason, Context => Try({}, [Exit(I64)]) shutdown! = |_, _| Ok({}) diff --git a/src/roc_platform_abi.rs b/src/roc_platform_abi.rs index 26425db2..a8e8a5c7 100644 --- a/src/roc_platform_abi.rs +++ b/src/roc_platform_abi.rs @@ -1589,35 +1589,6 @@ const _: () = assert!(core::mem::size_of::() == 24, #[cfg(target_pointer_width = "32")] const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct6020798da82f3849 alignment mismatch"); -/// Element type for __AnonStruct_1f12a65955b54fe1 -#[cfg(target_pointer_width = "32")] -#[repr(C)] -#[derive(Clone, Copy)] -pub struct AnonStruct1f12a65955b54fe1 { - pub init_bang: RocErasedCallable, - pub respond_bang: RocErasedCallable, - pub shutdown_bang: RocErasedCallable, -} - -/// Element type for __AnonStruct_1f12a65955b54fe1 -#[cfg(not(target_pointer_width = "32"))] -#[repr(C)] -#[derive(Clone, Copy)] -pub struct AnonStruct1f12a65955b54fe1 { - pub init_bang: RocErasedCallable, - pub respond_bang: RocErasedCallable, - pub shutdown_bang: RocErasedCallable, -} - -#[cfg(target_pointer_width = "64")] -const _: () = assert!(core::mem::size_of::() == 24, "AnonStruct1f12a65955b54fe1 size mismatch"); -#[cfg(target_pointer_width = "64")] -const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct1f12a65955b54fe1 alignment mismatch"); -#[cfg(target_pointer_width = "32")] -const _: () = assert!(core::mem::size_of::() == 12, "AnonStruct1f12a65955b54fe1 size mismatch"); -#[cfg(target_pointer_width = "32")] -const _: () = assert!(core::mem::align_of::() == 4, "AnonStruct1f12a65955b54fe1 alignment mismatch"); - /// Element type for __AnonStruct_60bc4208e250c775 #[cfg(target_pointer_width = "32")] #[repr(C)] @@ -1870,65 +1841,6 @@ const _: () = assert!(core::mem::size_of::() == 52, #[cfg(target_pointer_width = "32")] const _: () = assert!(core::mem::align_of::() == 4, "AnonStruct164d87d4a7b4f044 alignment mismatch"); -/// Element type for __AnonStruct_66bd3eb5a5fde7bc -#[cfg(target_pointer_width = "32")] -#[repr(C)] -#[derive(Clone, Copy)] -pub struct AnonStruct66bd3eb5a5fde7bc { - pub body_limit_bytes: u64, - pub content_length: u64, - pub authority_host: RocStr, - pub body_handle: *mut u64, - pub headers: RocList, - pub method_ext: RocStr, - pub target_authority_host: RocStr, - pub target_path: RocStr, - pub target_query: RocStr, - pub authority_port: u16, - pub target_authority_port: u16, - pub authority_port_present: bool, - pub authority_present: bool, - pub content_length_known: bool, - pub method: u8, - pub target_authority_port_present: bool, - pub target_query_present: bool, - pub target_tag: u8, -} - -/// Element type for __AnonStruct_66bd3eb5a5fde7bc -#[cfg(not(target_pointer_width = "32"))] -#[repr(C)] -#[derive(Clone, Copy)] -pub struct AnonStruct66bd3eb5a5fde7bc { - pub body_limit_bytes: u64, - pub content_length: u64, - pub authority_host: RocStr, - pub body_handle: *mut u64, - pub headers: RocList, - pub method_ext: RocStr, - pub target_authority_host: RocStr, - pub target_path: RocStr, - pub target_query: RocStr, - pub authority_port: u16, - pub target_authority_port: u16, - pub authority_port_present: bool, - pub authority_present: bool, - pub content_length_known: bool, - pub method: u8, - pub target_authority_port_present: bool, - pub target_query_present: bool, - pub target_tag: u8, -} - -#[cfg(target_pointer_width = "64")] -const _: () = assert!(core::mem::size_of::() == 184, "AnonStruct66bd3eb5a5fde7bc size mismatch"); -#[cfg(target_pointer_width = "64")] -const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct66bd3eb5a5fde7bc alignment mismatch"); -#[cfg(target_pointer_width = "32")] -const _: () = assert!(core::mem::size_of::() == 104, "AnonStruct66bd3eb5a5fde7bc size mismatch"); -#[cfg(target_pointer_width = "32")] -const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct66bd3eb5a5fde7bc alignment mismatch"); - /// Element type for __AnonStruct_49a217fc2950a160 #[cfg(target_pointer_width = "32")] #[repr(C)] @@ -2055,6 +1967,65 @@ const _: () = assert!(core::mem::size_of::() == 16, #[cfg(target_pointer_width = "32")] const _: () = assert!(core::mem::align_of::() == 8, "AnonStructFe0b1f6ac1e65458 alignment mismatch"); +/// Element type for __AnonStruct_66bd3eb5a5fde7bc +#[cfg(target_pointer_width = "32")] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct AnonStruct66bd3eb5a5fde7bc { + pub body_limit_bytes: u64, + pub content_length: u64, + pub authority_host: RocStr, + pub body_handle: *mut u64, + pub headers: RocList, + pub method_ext: RocStr, + pub target_authority_host: RocStr, + pub target_path: RocStr, + pub target_query: RocStr, + pub authority_port: u16, + pub target_authority_port: u16, + pub authority_port_present: bool, + pub authority_present: bool, + pub content_length_known: bool, + pub method: u8, + pub target_authority_port_present: bool, + pub target_query_present: bool, + pub target_tag: u8, +} + +/// Element type for __AnonStruct_66bd3eb5a5fde7bc +#[cfg(not(target_pointer_width = "32"))] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct AnonStruct66bd3eb5a5fde7bc { + pub body_limit_bytes: u64, + pub content_length: u64, + pub authority_host: RocStr, + pub body_handle: *mut u64, + pub headers: RocList, + pub method_ext: RocStr, + pub target_authority_host: RocStr, + pub target_path: RocStr, + pub target_query: RocStr, + pub authority_port: u16, + pub target_authority_port: u16, + pub authority_port_present: bool, + pub authority_present: bool, + pub content_length_known: bool, + pub method: u8, + pub target_authority_port_present: bool, + pub target_query_present: bool, + pub target_tag: u8, +} + +#[cfg(target_pointer_width = "64")] +const _: () = assert!(core::mem::size_of::() == 184, "AnonStruct66bd3eb5a5fde7bc size mismatch"); +#[cfg(target_pointer_width = "64")] +const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct66bd3eb5a5fde7bc alignment mismatch"); +#[cfg(target_pointer_width = "32")] +const _: () = assert!(core::mem::size_of::() == 104, "AnonStruct66bd3eb5a5fde7bc size mismatch"); +#[cfg(target_pointer_width = "32")] +const _: () = assert!(core::mem::align_of::() == 8, "AnonStruct66bd3eb5a5fde7bc alignment mismatch"); + /// Element type for __AnonStruct_628b43fd33b27733 #[cfg(target_pointer_width = "32")] #[repr(C)] @@ -13619,39 +13590,6 @@ unsafe impl RocRelease for HostTcpWriteResultRelease { } } -impl AnonStruct1f12a65955b54fe1 { - /// Recursively decrement Roc-owned fields. - /// - /// # Safety - /// `self` must own one live Roc reference for each refcounted field. - pub unsafe fn decref(self, roc_host: &RocHost) { - let value = self; - unsafe { decref_erased_callable(value.init_bang, roc_host); } - unsafe { decref_erased_callable(value.respond_bang, roc_host); } - unsafe { decref_erased_callable(value.shutdown_bang, roc_host); } - } - - /// Increment Roc-owned fields. - /// - /// # Safety - /// `self` must point at live Roc allocations. The retained references must - /// be balanced by later decrefs. - pub unsafe fn incref(self, amount: isize) { - let value = self; - unsafe { incref_erased_callable(value.init_bang, amount); } - unsafe { incref_erased_callable(value.respond_bang, amount); } - unsafe { incref_erased_callable(value.shutdown_bang, amount); } - } -} - -pub struct AnonStruct1f12a65955b54fe1Release; - -unsafe impl RocRelease for AnonStruct1f12a65955b54fe1Release { - unsafe fn release(value: AnonStruct1f12a65955b54fe1, roc_host: &RocHost) { - unsafe { value.decref(roc_host); } - } -} - impl InitForHostResult { /// Recursively decrement Roc-owned payloads. /// @@ -13901,47 +13839,6 @@ unsafe impl RocRelease for AnonStruct164d87d4a7b4f04 } } -impl AnonStruct66bd3eb5a5fde7bc { - /// Recursively decrement Roc-owned fields. - /// - /// # Safety - /// `self` must own one live Roc reference for each refcounted field. - pub unsafe fn decref(self, roc_host: &RocHost) { - let value = self; - unsafe { value.authority_host.decref(roc_host); } - unsafe { decref_box_with(value.body_handle as RocBox, core::mem::align_of::(), false, None, roc_host); } - unsafe { decref_list_of_anon_struct82a96c5d55d63488(value.headers, roc_host); } - unsafe { value.method_ext.decref(roc_host); } - unsafe { value.target_authority_host.decref(roc_host); } - unsafe { value.target_path.decref(roc_host); } - unsafe { value.target_query.decref(roc_host); } - } - - /// Increment Roc-owned fields. - /// - /// # Safety - /// `self` must point at live Roc allocations. The retained references must - /// be balanced by later decrefs. - pub unsafe fn incref(self, amount: isize) { - let value = self; - unsafe { value.authority_host.incref(amount); } - unsafe { incref_box(value.body_handle as RocBox, amount); } - unsafe { value.headers.incref(amount); } - unsafe { value.method_ext.incref(amount); } - unsafe { value.target_authority_host.incref(amount); } - unsafe { value.target_path.incref(amount); } - unsafe { value.target_query.incref(amount); } - } -} - -pub struct AnonStruct66bd3eb5a5fde7bcRelease; - -unsafe impl RocRelease for AnonStruct66bd3eb5a5fde7bcRelease { - unsafe fn release(value: AnonStruct66bd3eb5a5fde7bc, roc_host: &RocHost) { - unsafe { value.decref(roc_host); } - } -} - impl InternalServerOutcomeToHost { /// Recursively decrement Roc-owned payloads. /// @@ -14182,14 +14079,20 @@ unsafe impl RocRelease for AnonStructFe0b1f6ac1e6545 } } -impl AnonStruct628b43fd33b27733 { +impl AnonStruct66bd3eb5a5fde7bc { /// Recursively decrement Roc-owned fields. /// /// # Safety /// `self` must own one live Roc reference for each refcounted field. pub unsafe fn decref(self, roc_host: &RocHost) { let value = self; - unsafe { value.detail.decref(roc_host); } + unsafe { value.authority_host.decref(roc_host); } + unsafe { decref_box_with(value.body_handle as RocBox, core::mem::align_of::(), false, None, roc_host); } + unsafe { decref_list_of_anon_struct82a96c5d55d63488(value.headers, roc_host); } + unsafe { value.method_ext.decref(roc_host); } + unsafe { value.target_authority_host.decref(roc_host); } + unsafe { value.target_path.decref(roc_host); } + unsafe { value.target_query.decref(roc_host); } } /// Increment Roc-owned fields. @@ -14199,14 +14102,20 @@ impl AnonStruct628b43fd33b27733 { /// be balanced by later decrefs. pub unsafe fn incref(self, amount: isize) { let value = self; - unsafe { value.detail.incref(amount); } + unsafe { value.authority_host.incref(amount); } + unsafe { incref_box(value.body_handle as RocBox, amount); } + unsafe { value.headers.incref(amount); } + unsafe { value.method_ext.incref(amount); } + unsafe { value.target_authority_host.incref(amount); } + unsafe { value.target_path.incref(amount); } + unsafe { value.target_query.incref(amount); } } } -pub struct AnonStruct628b43fd33b27733Release; +pub struct AnonStruct66bd3eb5a5fde7bcRelease; -unsafe impl RocRelease for AnonStruct628b43fd33b27733Release { - unsafe fn release(value: AnonStruct628b43fd33b27733, roc_host: &RocHost) { +unsafe impl RocRelease for AnonStruct66bd3eb5a5fde7bcRelease { + unsafe fn release(value: AnonStruct66bd3eb5a5fde7bc, roc_host: &RocHost) { unsafe { value.decref(roc_host); } } } @@ -14248,6 +14157,35 @@ unsafe impl RocRelease for ShutdownForHostResultRelease { } } +impl AnonStruct628b43fd33b27733 { + /// Recursively decrement Roc-owned fields. + /// + /// # Safety + /// `self` must own one live Roc reference for each refcounted field. + pub unsafe fn decref(self, roc_host: &RocHost) { + let value = self; + unsafe { value.detail.decref(roc_host); } + } + + /// Increment Roc-owned fields. + /// + /// # Safety + /// `self` must point at live Roc allocations. The retained references must + /// be balanced by later decrefs. + pub unsafe fn incref(self, amount: isize) { + let value = self; + unsafe { value.detail.incref(amount); } + } +} + +pub struct AnonStruct628b43fd33b27733Release; + +unsafe impl RocRelease for AnonStruct628b43fd33b27733Release { + unsafe fn release(value: AnonStruct628b43fd33b27733, roc_host: &RocHost) { + unsafe { value.decref(roc_host); } + } +} + /// Release one owned reference to a `RocList`. /// /// The allocation's final reference is claimed atomically before any element From a21899b13aba91c34bd0242ee4604dfdf7a86fca Mon Sep 17 00:00:00 2001 From: "Luke Boswell (Linux-Desktop)" Date: Thu, 1 Oct 2026 11:14:53 +1000 Subject: [PATCH 3/3] Defer examples blocked by the Gregorian package release --- AGENTS.md | 2 +- CONTRIBUTING.md | 4 +- README.md | 9 +- examples/{command.roc => command.todoroc} | 4 + examples/{echo.roc => echo.todoroc} | 4 + ...or-handling.roc => error-handling.todoroc} | 4 + ...le-accessed-modified-created-time.todoroc} | 4 + examples/{hello-web.roc => hello-web.todoroc} | 4 + .../{init-basic.roc => init-basic.todoroc} | 4 + scripts/test_harness_test.py | 2 +- scripts/test_spec.json | 545 ------------------ 11 files changed, 34 insertions(+), 552 deletions(-) rename examples/{command.roc => command.todoroc} (94%) rename examples/{echo.roc => echo.todoroc} (93%) rename examples/{error-handling.roc => error-handling.todoroc} (89%) rename examples/{file-accessed-modified-created-time.roc => file-accessed-modified-created-time.todoroc} (86%) rename examples/{hello-web.roc => hello-web.todoroc} (85%) rename examples/{init-basic.roc => init-basic.todoroc} (84%) diff --git a/AGENTS.md b/AGENTS.md index 3227cc19..5b25575d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a60435ae..a5495df2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 by default. Build one explicit @@ -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 diff --git a/README.md b/README.md index cc49e0f2..70088fb4 100644 --- a/README.md +++ b/README.md @@ -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( @@ -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({}) ``` @@ -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 diff --git a/examples/command.roc b/examples/command.todoroc similarity index 94% rename from examples/command.roc rename to examples/command.todoroc index c161d540..e0adfe54 100644 --- a/examples/command.roc +++ b/examples/command.todoroc @@ -1,3 +1,7 @@ +# 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", diff --git a/examples/echo.roc b/examples/echo.todoroc similarity index 93% rename from examples/echo.roc rename to examples/echo.todoroc index 82763527..b1426caf 100644 --- a/examples/echo.roc +++ b/examples/echo.todoroc @@ -1,3 +1,7 @@ +# 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 ## Echo server: logs the request method and target, then replies with the request body. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", diff --git a/examples/error-handling.roc b/examples/error-handling.todoroc similarity index 89% rename from examples/error-handling.roc rename to examples/error-handling.todoroc index 3c8d7c5a..8b36353d 100644 --- a/examples/error-handling.roc +++ b/examples/error-handling.todoroc @@ -1,3 +1,7 @@ +# 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 typed error handling while fetching content from a configured URL. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", diff --git a/examples/file-accessed-modified-created-time.roc b/examples/file-accessed-modified-created-time.todoroc similarity index 86% rename from examples/file-accessed-modified-created-time.roc rename to examples/file-accessed-modified-created-time.todoroc index 68921978..c31669a1 100644 --- a/examples/file-accessed-modified-created-time.roc +++ b/examples/file-accessed-modified-created-time.todoroc @@ -1,3 +1,7 @@ +# 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 ## Reads and serves the accessed, modified, and created timestamps of `LICENSE`. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", diff --git a/examples/hello-web.roc b/examples/hello-web.todoroc similarity index 85% rename from examples/hello-web.roc rename to examples/hello-web.todoroc index cb47bc72..4fbf6e52 100644 --- a/examples/hello-web.roc +++ b/examples/hello-web.todoroc @@ -1,3 +1,7 @@ +# 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 ## Uses host-owned operational telemetry and responds with a simple HTML greeting. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", diff --git a/examples/init-basic.roc b/examples/init-basic.todoroc similarity index 84% rename from examples/init-basic.roc rename to examples/init-basic.todoroc index f364ba75..06a84a79 100644 --- a/examples/init-basic.roc +++ b/examples/init-basic.todoroc @@ -1,3 +1,7 @@ +# 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 ## Initializes shared context once and uses it while logging and responding to requests. app [Context, program] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.16.0/42jC1JT3auhHSmv2Ah8mW5F2MXiAakq1UQQ4NQceQjXw.tar.zst", diff --git a/scripts/test_harness_test.py b/scripts/test_harness_test.py index 248997ac..3a1254da 100644 --- a/scripts/test_harness_test.py +++ b/scripts/test_harness_test.py @@ -360,7 +360,7 @@ def test_build_optimization_defaults_to_speed(self) -> None: "dev", ) self.assertEqual( - test.build_optimization(by_path["examples/hello-web.roc"]), + test.build_optimization(by_path["examples/health.roc"]), "speed", ) diff --git a/scripts/test_spec.json b/scripts/test_spec.json index 3820a313..1cdd2732 100644 --- a/scripts/test_spec.json +++ b/scripts/test_spec.json @@ -7,65 +7,6 @@ "run": true }, "apps": [ - { - "path": "examples/command.roc", - "mode": "server", - "stages": { - "test": false - }, - "test_skip": { - "reason": "The pinned Roc compiler segfaults while collecting roc-gregorian's imported iterator expects; check, build, and runtime stages remain active.", - "issue": "https://github.com/roc-lang/roc/issues/10422" - }, - "cases": [ - { - "name": "commands-and-request", - "env": { - "PYTHON": "{python}", - "COMMAND_HELPER": "{root}/scripts/command_helper.py", - "COMMAND_EXAMPLES_DIR": "{root}/examples", - "COMMAND_SCRIPTS_DIR": "{root}/scripts" - }, - "requests": [ - { - "method": "GET", - "target": "/", - "expect_body": "Command succeeded." - }, - { - "method": "GET", - "target": "/timeout", - "expect_body": "Command timed out." - }, - { - "method": "GET", - "target": "/output-limit", - "expect_body": "Command output was limited." - } - ], - "concurrent_requests": [ - { - "target": "/cwd-examples", - "expect_body": "examples\n" - }, - { - "launch_after_ms": 20, - "target": "/cwd-scripts", - "expect_body": "scripts\n" - } - ], - "stdout_contains": [ - "Hello", - "stdout_utf8: \"Hi", - "Exit code: 1", - "stdout_bytes: [72, 105, 10]" - ], - "stdout_regex": [ - "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z GET Resource" - ] - } - ] - }, { "path": "examples/datastar/main.roc", "mode": "server", @@ -1176,326 +1117,6 @@ } ] }, - { - "path": "examples/echo.roc", - "mode": "server", - "cases": [ - { - "name": "utf8-and-binary", - "requests": [ - { - "method": "POST", - "target": "/echo?kind=utf8", - "body": "echo me", - "expect_body": "echo me" - }, - { - "method": "POST", - "target": "/binary", - "body_hex": "00ff410a", - "expect_body_hex": "00ff410a" - }, - { - "method": "POST", - "target": "/first-chunk", - "body": "seamless response", - "expect_body": "seamless response" - }, - { - "method": "POST", - "target": "/compressed", - "headers": { - "Accept-Encoding": "br;q=0.5, gzip;q=1" - }, - "body_repeat": "compressible echo body ", - "body_size_bytes": 4096, - "decode_response": "gzip", - "expect_body_bytes": 4096, - "body_contains": [ - "compressible echo body" - ], - "response_headers": { - "Content-Encoding": "gzip", - "Vary": "Accept-Encoding" - } - }, - { - "method": "POST", - "target": "/zstandard", - "headers": { - "Accept-Encoding": "gzip;q=0.5, br;q=0.5, zstd" - }, - "body_repeat": "compressible zstandard body ", - "body_size_bytes": 4096, - "response_headers": { - "Content-Encoding": "zstd", - "Vary": "Accept-Encoding" - } - }, - { - "raw": true, - "half_close": false, - "expect_close": true, - "fragments": [ - { - "data": "GET /incomplete HTTP/1.1\r\nHost: local", - "delay_ms": 1000 - } - ] - }, - { - "raw": true, - "half_close": false, - "expect_close": true, - "fragments": [ - { - "data": "POST /body-timeout HTTP/1.1\r\nHost: localhost\r\nContent-Length: 2\r\nConnection: close\r\n\r\na", - "delay_ms": 1000 - } - ], - "response_contains": [ - "HTTP/1.1 408 Request Timeout", - "Request body timed out" - ] - }, - { - "raw": true, - "half_close": false, - "expect_close": true, - "fragments": [ - { - "data": "POST /body-progress HTTP/1.1\r\nHost: localhost\r\nContent-Length: 2\r\nConnection: close\r\n\r\na", - "delay_ms": 50 - }, - { - "data": "b" - } - ], - "response_contains": [ - "HTTP/1.1 200 OK", - "\r\n\r\nab" - ] - }, - { - "raw": true, - "half_close": false, - "expect_close": true, - "data": "GET /keep-alive HTTP/1.1\r\nHost: localhost\r\n\r\n", - "response_contains": [ - "HTTP/1.1 200 OK" - ] - }, - { - "raw": true, - "half_close": false, - "expect_close": true, - "data": "GET /reuse-one HTTP/1.1\r\nHost: localhost\r\n\r\nGET /reuse-two HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n", - "response_regex": [ - "HTTP/1\\.1 200 OK.*HTTP/1\\.1 200 OK" - ] - }, - { - "raw": true, - "half_close": false, - "data_hex": "474554202f20485454502f312e310d0a486f73743a203132372e302e302e310d0a582d546573743a20ff0d0a436f6e6e656374696f6e3a20636c6f73650d0a0d0a", - "response_contains": [ - "HTTP/1.1 400 Bad Request", - "Request header values must be valid UTF-8" - ] - } - ], - "concurrent_requests": [ - { - "method": "POST", - "target": "/first-chunk", - "body": "first concurrent body", - "expect_body": "first concurrent body" - }, - { - "method": "POST", - "target": "/first-chunk", - "body": "second concurrent body", - "expect_body": "second concurrent body" - } - ], - "stdout_contains": [ - "POST Resource" - ] - }, - { - "name": "typed-http1-request-target", - "requests": [ - { - "raw": true, - "half_close": false, - "data": "GET /request-info HTTP/1.1\r\nHost: example.test:8080\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 200 OK", - "target=resource\npath=/request-info\nquery=absent\nauthority=present\nhost=example.test\nport=present:8080" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET /request-info? HTTP/1.1\r\nHost: empty-query.test\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "target=resource\npath=/request-info\nquery=present:\nauthority=present\nhost=empty-query.test\nport=absent" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET /request-info%2Fencoded?q=a%2Fb%26c HTTP/1.1\r\nHost: encoded.test\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "target=resource\npath=/request-info%2Fencoded\nquery=present:q=a%2Fb%26c" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET http://absolute.test?from=absolute HTTP/1.1\r\nHost: ignored.test\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "target=resource\npath=/\nquery=present:from=absolute\nauthority=present\nhost=absolute.test\nport=absent" - ] - }, - { - "raw": true, - "half_close": false, - "data": "CONNECT [2001:db8::1]:443 HTTP/1.1\r\nHost: proxy.test\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request", - "target=authority\nauthority=present\nhost=[2001:db8::1]\nport=present:443" - ] - }, - { - "raw": true, - "half_close": false, - "data": "OPTIONS * HTTP/1.1\r\nHost: options.test\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "target=asterisk\nauthority=present\nhost=options.test\nport=absent" - ] - }, - { - "raw": true, - "half_close": false, - "data": "OPTIONS * HTTP/1.1\r\nHost:\r\nX-Request-Info: describe\r\nConnection: close\r\n\r\n", - "response_contains": [ - "target=asterisk\nauthority=absent" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET /escaped%2Fpath HTTP/1.1\r\nHost: escape.test\r\nX-Request-Info: escape-path\r\nConnection: close\r\n\r\n", - "response_contains": [ - "\r\n\r\n/escaped%2Fpath" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET / HTTP/1.1\r\nHost: escape.example:8443\r\nX-Request-Info: escape-authority\r\nConnection: close\r\n\r\n", - "response_contains": [ - "\r\n\r\nescape.example" - ] - } - ] - }, - { - "name": "reject-malformed-http1-control-data", - "requests": [ - { - "raw": true, - "half_close": false, - "data": "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET / HTTP/1.1\r\nHost: one.test\r\nHost: two.test\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET / HTTP/1.1\r\nHost: user@example.test\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET * HTTP/1.1\r\nHost: example.test\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request" - ] - }, - { - "raw": true, - "half_close": false, - "data": "GET example.test:80 HTTP/1.1\r\nHost: example.test\r\nConnection: close\r\n\r\n", - "response_contains": [ - "HTTP/1.1 400 Bad Request" - ] - } - ] - }, - { - "name": "http2-authority-and-host-agree", - "http2_requests": [ - { - "name": "agree", - "target": "/request-info?via=h2", - "authority": "h2.example:8443", - "headers": { - "Host": "h2.example:8443", - "X-Request-Info": "describe" - }, - "expect_body": "target=resource\npath=/request-info\nquery=present:via=h2\nauthority=present\nhost=h2.example\nport=present:8443" - } - ], - "http2_completion_order": ["agree"] - }, - { - "name": "http2-host-fallback", - "http2_requests": [ - { - "name": "fallback", - "target": "/request-info", - "authority": null, - "headers": { - "Host": "fallback.example", - "X-Request-Info": "describe" - }, - "expect_body": "target=resource\npath=/request-info\nquery=absent\nauthority=present\nhost=fallback.example\nport=absent" - } - ], - "http2_completion_order": ["fallback"] - }, - { - "name": "http2-authority-host-disagreement", - "http2_requests": [ - { - "name": "disagree", - "target": "/request-info", - "authority": "authority.example", - "headers": { - "Host": "other.example", - "X-Request-Info": "describe" - }, - "expect_body": "Invalid request target or authority" - } - ], - "http2_completion_order": ["disagree"] - } - ] - }, { "path": "examples/env.roc", "mode": "server", @@ -1523,95 +1144,6 @@ } ] }, - { - "path": "examples/error-handling.roc", - "mode": "server", - "build_opt": "dev", - "cases": [ - { - "name": "outbound-http", - "helper": "http", - "env": { - "TARGET_URL": "http://127.0.0.1:9000/example" - }, - "requests": [ - { - "expect_body": "Example Domain" - } - ], - "stdout_regex": [ - "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z GET Resource" - ] - }, - { - "name": "semantic-error-fallback", - "env": { - "TARGET_URL": "http://127.0.0.1:1/unavailable" - }, - "requests": [ - { - "status": 500, - "expect_body": "Internal Server Error" - } - ], - "stderr_contains": [ - "ERROR respond! method=GET target=Resource({ raw_path: \"/\", raw_query: Absent }): FailedToFetch(" - ] - } - ] - }, - { - "path": "examples/file-accessed-modified-created-time.roc", - "mode": "server", - "stages": { - "test": false - }, - "test_skip": { - "reason": "The pinned Roc compiler segfaults while collecting roc-gregorian's imported iterator expects; check, build, and runtime stages remain active.", - "issue": "https://github.com/roc-lang/roc/issues/10422" - }, - "cases": [ - { - "name": "license-metadata", - "requests": [ - { - "body_contains": [ - "LICENSE file time metadata:", - "Modified:", - "Accessed:", - "Created:" - ] - } - ], - "stdout_regex": [ - "LICENSE file time metadata:\\nModified: 20[^\\n]+\\nAccessed: 20[^\\n]+\\nCreated: 20[^\\n]+" - ] - }, - { - "name": "pre-epoch-metadata", - "cwd": "temp", - "fixtures": [ - { - "dest": "{temp}/LICENSE", - "text": "pre-epoch metadata fixture", - "mtime_unix": -1 - } - ], - "requests": [ - { - "body_contains": [ - "Modified: 1969-12-31T23:59:59Z", - "Accessed: 1969-12-31T23:59:59Z" - ] - } - ], - "stdout_contains": [ - "Modified: 1969-12-31T23:59:59Z", - "Accessed: 1969-12-31T23:59:59Z" - ] - } - ] - }, { "path": "examples/file-permissions.roc", "mode": "server", @@ -1764,62 +1296,6 @@ } ] }, - { - "path": "examples/hello-web.roc", - "mode": "server", - "cases": [ - { - "name": "semantic-http", - "requests": [ - { - "method": "GET", - "target": "/", - "expect_body": "Hello from server
" - }, - { - "method": "GET", - "target": "/metrics", - "body_contains": [ - "basic_webserver_http_requests_total{method=\"GET\",destination=\"roc\",completion=\"end_of_stream\"} 1", - "basic_webserver_http_requests_active 1", - "# EOF" - ], - "response_headers": { - "Content-Type": "application/openmetrics-text; version=1.0.0; charset=utf-8", - "Cache-Control": "no-store" - } - } - ], - "stdout_regex": [ - "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z GET Resource" - ], - "stderr_regex": [ - "\\{\"timestamp_unix_ms\":[0-9]+,\"duration_us\":[0-9]+,\"request_id\":\"[0-9a-f]{32}\",\"method\":\"GET\",\"status\":200,\"destination\":\"roc\",\"completion\":\"end_of_stream\"[^\\n]+\"target_path\":\"/\"\\}" - ] - }, - { - "name": "raw-fragmented-request", - "requests": [ - { - "raw": true, - "half_close": false, - "fragments": [ - { - "data": "GET /raw HTTP/1.1\r\nHost: local" - }, - { - "data": "host\r\nConnection: close\r\n\r\n" - } - ], - "response_contains": [ - "HTTP/1.1 200 OK\r\n", - "\r\n\r\nHello from server
" - ] - } - ] - } - ] - }, { "path": "examples/health.roc", "mode": "server", @@ -2009,27 +1485,6 @@ } ] }, - { - "path": "examples/init-basic.roc", - "mode": "server", - "stages": { - "test": false - }, - "test_skip": { - "reason": "The pinned Roc compiler segfaults while collecting roc-gregorian's imported iterator expects; check, build, and runtime stages remain active.", - "issue": "https://github.com/roc-lang/roc/issues/10422" - }, - "cases": [ - { - "name": "context-from-init", - "requests": [ - { - "expect_body": "init gave me 🎁" - } - ] - } - ] - }, { "path": "examples/try.roc", "mode": "server",