import filepath import gleam/fetch import gleam/http import gleam/http/request import gleam/http/response import gleam/io import gleam/javascript/promise import gleam/option import gleam/result import gleam/string import gleam/uri import glen import glen/status import glen_node import midas/js/run as r import midas/node/browser import midas/node/file_system as fs import midas/node/gleam import midas/node/rollup import midas/node/zip import midas/task as t import plinth/browser/crypto/subtle import snag pub fn run(task, root) { case task { t.Done(value) -> promise.resolve(Ok(value)) t.Abort(reason) -> promise.resolve(Error(reason)) t.Bundle(module, function, resume) -> { use project <- r.try(fs.current_directory()) use js_dir <- r.try(gleam.build_js(project)) let assert Ok(#(package, _)) = string.split_once(module, "/") // Assumes that the package and module share name at top level // let package = "eyg" let module_path = string.concat([package, "/", module]) use bundle <- promise.await(rollup.bundle_fn( js_dir, module_path, function, )) let bundle = result.map_error(bundle, snag.pretty_print) run(resume(bundle), root) } t.Fetch(request, resume) -> { use return <- promise.await(do_fetch(request)) run(resume(return), root) } t.Follow(uri, resume) -> { use return <- promise.await(do_follow(uri)) let assert Ok(raw) = return run(resume(uri.parse(raw)), root) } t.Hash(algorithm, bytes, resume) -> { let algorithm = case algorithm { t.SHA1 -> subtle.SHA1 t.SHA256 -> subtle.SHA256 t.SHA384 -> subtle.SHA384 t.SHA512 -> subtle.SHA512 } use result <- promise.await(subtle.digest(algorithm, bytes)) run(resume(result), root) } t.List(directory, resume) -> { let path = filepath.join(root, directory) run( resume(fs.read_directory(path) |> result.map_error(snag.pretty_print)), root, ) } t.Log(message, resume) -> { io.println(message) run(resume(Ok(Nil)), root) } t.Read(file, resume) -> { let path = filepath.join(root, file) let result = fs.read(path) |> result.map_error(snag.line_print) run(resume(result), root) } t.Serve(port, handle, resume) -> { let port = option.unwrap(port, 8080) let result = glen_node.serve(port, fn(request) { use body <- promise.map(glen.read_body_bits(request)) case body { Ok(body) -> { let request = request.set_body(request, body) let response = handle(request) response |> response.set_body(glen.Bits(response.body)) } Error(_reason) -> response.new(500) |> response.set_body(glen.Bits(<<"failed to read request body">>)) } }) |> result.replace(Nil) run(resume(result), root) } t.Write(file, bytes, resume) -> { let path = filepath.join(root, file) let result = fs.write(path, bytes) |> result.map_error(snag.line_print) run(resume(result), root) } t.Zip(files, resume) -> { use return <- promise.await(zip.zip(files)) run(resume(Ok(return)), root) } } } pub fn do_fetch(request) { use response <- promise.await(fetch.send_bits(request)) let assert Ok(response) = response use response <- promise.await(fetch.read_bytes_body(response)) let response = case response { Ok(response) -> Ok(response) Error(fetch.NetworkError(s)) -> Error(t.NetworkError(s)) Error(fetch.UnableToReadBody) -> Error(t.UnableToReadBody) Error(fetch.InvalidJsonBody) -> panic } promise.resolve(response) } fn do_follow(url) { browser.open(url) receive_redirect() } fn handle_redirect(request: glen.Request, resolve) { case request.method { http.Get -> "

finalising authorization

" |> glen.html(status.ok) |> promise.resolve http.Post -> { use body <- promise.await(glen.read_text_body(request)) case body { Ok(body) -> { resolve(body) "" // Post should return no content 201 but this crashes glen |> glen.html(status.ok) |> promise.resolve } Error(_) -> "Not text content" |> glen.html(status.bad_request) |> promise.resolve } } _ -> panic as "unexpected method" } } fn receive_redirect() { let #(promise, resolve) = promise.start() case glen_node.serve(8080, handle_redirect(_, resolve)) { Ok(server) -> { use url <- promise.await(promise) use Nil <- promise.await(glen_node.close(server)) promise.resolve(Ok(url)) } Error(reason) -> promise.resolve(Error(reason)) } }