A yet another cloudflare bindings library for Gleam
2

Configure Feed

Select the types of activity you want to include in your feed.

Init

Renatillas (Jun 27, 2026, 4:38 PM +0200) fc2e0e1e

+584
+1
.gitignore
··· 1 + build/
+7
gleam.toml
··· 1 + name = "cf_workers" 2 + version = "1.0.0" 3 + target = "javascript" 4 + 5 + [dependencies] 6 + gleam_stdlib = ">= 1.0.0 and < 2.0.0" 7 + gleam_javascript = ">= 1.0.0 and < 2.0.0"
+16
manifest.toml
··· 1 + # Do not manually edit this file, it is managed by Gleam. 2 + # 3 + # This file locks the dependency versions used, to make your build 4 + # deterministic and to prevent unexpected versions from being included 5 + # in your application. 6 + # 7 + # You should check this file into your source control repository. 8 + 9 + packages = [ 10 + { name = "gleam_javascript", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "EF6C77A506F026C6FB37941889477CD5E4234FCD4337FF0E9384E297CB8F97EB" }, 11 + { name = "gleam_stdlib", version = "1.0.3", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1F543AFBA5D33DA493E6087F4E4C4F20D899411343512686C98A8ABB2963CF22" }, 12 + ] 13 + 14 + [requirements] 15 + gleam_javascript = { version = ">= 1.0.0 and < 2.0.0" } 16 + gleam_stdlib = { version = ">= 1.0.0 and < 2.0.0" }
+3
src/cf_workers_crypto_ffi.mjs
··· 1 + export async function random_uuid() { 2 + return crypto.randomUUID(); 3 + }
+49
src/cf_workers_d1_ffi.mjs
··· 1 + import { Result$Ok, Result$Error } from "./gleam.mjs"; 2 + 3 + export function prepare(db, query) { 4 + return db.prepare(query); 5 + } 6 + 7 + export function bind(statement, values) { 8 + return statement.bind(...values); 9 + } 10 + 11 + export async function run(statement) { 12 + try { 13 + return Result$Ok(await statement.run()); 14 + } catch (error) { 15 + return Result$Error(`${error}`); 16 + } 17 + } 18 + 19 + export async function raw(statement) { 20 + try { 21 + return Result$Ok(await statement.raw()); 22 + } catch (error) { 23 + return Result$Error(`${error}`); 24 + } 25 + } 26 + 27 + export async function first(statement) { 28 + try { 29 + return Result$Ok(await statement.first()); 30 + } catch (error) { 31 + return Result$Error(`${error}`); 32 + } 33 + } 34 + 35 + export async function batch(db, statements) { 36 + try { 37 + return Result$Ok(await db.batch(statements)); 38 + } catch (error) { 39 + return Result$Error(`${error}`); 40 + } 41 + } 42 + 43 + export async function exec(db, query) { 44 + try { 45 + return Result$Ok(await db.exec(query)); 46 + } catch (error) { 47 + return Result$Error(`${error}`); 48 + } 49 + }
+39
src/cf_workers_environment_ffi.mjs
··· 1 + import { Result$Ok, Result$Error } from "./gleam.mjs"; 2 + 3 + export function d1_database(environment, binding) { 4 + const database = environment?.[binding]; 5 + return database && typeof database.prepare === "function" 6 + ? Result$Ok(database) 7 + : Result$Error(binding); 8 + } 9 + 10 + export function r2_bucket(environment, binding) { 11 + const bucket = environment?.[binding]; 12 + return bucket 13 + && typeof bucket.put === "function" 14 + && typeof bucket.get === "function" 15 + && typeof bucket.head === "function" 16 + && typeof bucket.list === "function" 17 + && typeof bucket.delete === "function" 18 + ? Result$Ok(bucket) 19 + : Result$Error(binding); 20 + } 21 + 22 + export function images(environment, binding) { 23 + const imageBinding = environment?.[binding]; 24 + return imageBinding && typeof imageBinding.input === "function" 25 + ? Result$Ok(imageBinding) 26 + : Result$Error(binding); 27 + } 28 + 29 + export function queue(environment, binding) { 30 + const queueBinding = environment?.[binding]; 31 + return queueBinding && typeof queueBinding.send === "function" 32 + ? Result$Ok(queueBinding) 33 + : Result$Error(binding); 34 + } 35 + 36 + export function secret(environment, binding) { 37 + const value = environment?.[binding]; 38 + return typeof value === "string" ? Result$Ok(value) : Result$Error(binding); 39 + }
+7
src/cf_workers_execution_context_ffi.mjs
··· 1 + export function new_wait_until(ctx) { 2 + return ctx.waitUntil.bind(ctx); 3 + } 4 + 5 + export function wait_until_run(waitUntil, task) { 6 + waitUntil(task()); 7 + }
+22
src/cf_workers_images_ffi.mjs
··· 1 + import { Result$Ok, Result$Error, toBitArray } from "./gleam.mjs"; 2 + 3 + export async function convert(binding, image, format) { 4 + try { 5 + const response = ( 6 + await binding 7 + .input(image.rawBuffer) 8 + .output({ format }) 9 + ).response(); 10 + 11 + if (!response.ok) { 12 + return Result$Error(`Images conversion failed with status ${response.status}`); 13 + } 14 + 15 + const converted = await response.arrayBuffer(); 16 + return Result$Ok(toBitArray(new Uint8Array(converted))); 17 + } catch (error) { 18 + return Result$Error( 19 + error instanceof globalThis.Error ? error.message : String(error), 20 + ); 21 + } 22 + }
+35
src/cf_workers_queue_ffi.mjs
··· 1 + import { Result$Ok, Result$Error } from "./gleam.mjs"; 2 + 3 + export async function send_int(queue, body) { 4 + try { 5 + await queue.send(body); 6 + return Result$Ok(undefined); 7 + } catch (error) { 8 + return Result$Error(`${error}`); 9 + } 10 + } 11 + 12 + export async function send_string(queue, body) { 13 + try { 14 + await queue.send(body); 15 + return Result$Ok(undefined); 16 + } catch (error) { 17 + return Result$Error(`${error}`); 18 + } 19 + } 20 + 21 + export function messages(batch) { 22 + return batch.messages ?? []; 23 + } 24 + 25 + export function body(message) { 26 + return message.body; 27 + } 28 + 29 + export function retry(message) { 30 + message.retry(); 31 + } 32 + 33 + export function ack(message) { 34 + message.ack(); 35 + }
+106
src/cf_workers_r2_ffi.mjs
··· 1 + import { Result$Ok, Result$Error, toBitArray } from "./gleam.mjs"; 2 + import { Option$Some, Option$None } from "../gleam_stdlib/gleam/option.mjs"; 3 + import { List$Empty, List$NonEmpty } from "./gleam.mjs"; 4 + import { ObjectPage$ObjectPage } from "./cf_workers/r2.mjs"; 5 + 6 + function optionToValue(option) { 7 + if (option instanceof Option$Some) { 8 + const tag = Object.keys(option)[1]; 9 + return option[tag]; 10 + } 11 + return undefined; 12 + } 13 + 14 + function valueToOption(value) { 15 + return typeof value === "string" && value.length > 0 16 + ? Option$Some(value) 17 + : Option$None(); 18 + } 19 + 20 + function listFromArray(items) { 21 + let list = List$Empty(); 22 + for (let index = items.length - 1; index >= 0; index -= 1) { 23 + list = List$NonEmpty(items[index], list); 24 + } 25 + return list; 26 + } 27 + 28 + export async function delete_(bucket, key) { 29 + try { 30 + await bucket.delete(key); 31 + return Result$Ok(undefined); 32 + } catch (error) { 33 + return Result$Error( 34 + error instanceof globalThis.Error ? error.message : String(error), 35 + ); 36 + } 37 + } 38 + 39 + export { delete_ as delete }; 40 + 41 + export async function get(bucket, key) { 42 + try { 43 + const object = await bucket.get(key); 44 + return object === null ? Result$Error(undefined) : Result$Ok(object); 45 + } catch (_error) { 46 + return Result$Error(undefined); 47 + } 48 + } 49 + 50 + export async function read_bytes(object) { 51 + try { 52 + const bytes = await object.arrayBuffer(); 53 + return Result$Ok(toBitArray(new Uint8Array(bytes))); 54 + } catch (_error) { 55 + return Result$Error(undefined); 56 + } 57 + } 58 + 59 + export async function head(bucket, key) { 60 + try { 61 + const object = await bucket.head(key); 62 + return object === null ? Result$Error(undefined) : Result$Ok(object); 63 + } catch (_error) { 64 + return Result$Error(undefined); 65 + } 66 + } 67 + 68 + export async function put(bucket, key, data, options) { 69 + try { 70 + const object = await bucket.put(key, data.rawBuffer, options); 71 + return object === null ? Result$Error(undefined) : Result$Ok(object); 72 + } catch (_error) { 73 + return Result$Error(undefined); 74 + } 75 + } 76 + 77 + export async function put_with_content_type(bucket, key, data, contentType) { 78 + try { 79 + const object = await bucket.put(key, data.rawBuffer, { 80 + httpMetadata: { contentType }, 81 + }); 82 + return object === null ? Result$Error(undefined) : Result$Ok(object); 83 + } catch (_error) { 84 + return Result$Error(undefined); 85 + } 86 + } 87 + 88 + export async function list(bucket, prefix, cursor, limit) { 89 + try { 90 + const page = await bucket.list({ 91 + prefix: optionToValue(prefix), 92 + cursor: optionToValue(cursor), 93 + limit: optionToValue(limit), 94 + }); 95 + 96 + return Result$Ok(ObjectPage$ObjectPage( 97 + listFromArray(page.objects.map((o) => o.key)), 98 + valueToOption(page.cursor), 99 + !page.truncated, 100 + )); 101 + } catch (error) { 102 + return Result$Error( 103 + error instanceof globalThis.Error ? error.message : String(error), 104 + ); 105 + } 106 + }
+4
src/cf_workers/crypto.gleam
··· 1 + import gleam/javascript/promise.{type Promise} 2 + 3 + @external(javascript, "../cf_workers_crypto_ffi.mjs", "random_uuid") 4 + pub fn random_uuid() -> Promise(String)
+52
src/cf_workers/d1.gleam
··· 1 + import gleam/dynamic.{type Dynamic} 2 + import gleam/javascript/array.{type Array} 3 + import gleam/javascript/promise.{type Promise} 4 + 5 + pub type Database 6 + 7 + @external(javascript, "../cf_workers_d1_ffi.mjs", "prepare") 8 + pub fn prepare(db: Database, query: String) -> PreparedStatement 9 + 10 + pub type PreparedStatement 11 + 12 + @external(javascript, "../cf_workers_d1_ffi.mjs", "bind") 13 + fn do_bind( 14 + statement: PreparedStatement, 15 + values: Array(String), 16 + ) -> PreparedStatement 17 + 18 + pub fn bind(statement, values) { 19 + do_bind(statement, array.from_list(values)) 20 + } 21 + 22 + pub type RunResult { 23 + RunResult(success: Bool, meta: Dynamic, results: Array(Dynamic)) 24 + } 25 + 26 + @external(javascript, "../cf_workers_d1_ffi.mjs", "run") 27 + pub fn run(statement: PreparedStatement) -> Promise(Result(RunResult, String)) 28 + 29 + @external(javascript, "../cf_workers_d1_ffi.mjs", "raw") 30 + pub fn raw( 31 + statement: PreparedStatement, 32 + ) -> Promise(Result(Array(Array(Dynamic)), String)) 33 + 34 + @external(javascript, "../cf_workers_d1_ffi.mjs", "first") 35 + pub fn first(statement: PreparedStatement) -> Promise(Result(Dynamic, String)) 36 + 37 + @external(javascript, "../cf_workers_d1_ffi.mjs", "batch") 38 + fn do_batch( 39 + db: Database, 40 + statements: Array(PreparedStatement), 41 + ) -> Promise(Result(Array(RunResult), String)) 42 + 43 + pub fn batch(db, statements) { 44 + do_batch(db, array.from_list(statements)) 45 + } 46 + 47 + pub type ExecResult { 48 + ExecResult(count: Int, duration: Float) 49 + } 50 + 51 + @external(javascript, "../cf_workers_d1_ffi.mjs", "exec") 52 + pub fn exec(db: Database, query: String) -> Promise(Result(ExecResult, String))
+102
src/cf_workers/environment.gleam
··· 1 + import cf_workers/d1 2 + import cf_workers/images 3 + import cf_workers/queue 4 + import cf_workers/r2 5 + import gleam/result 6 + import gleam/string 7 + 8 + pub type Environment 9 + 10 + pub type Error { 11 + MissingDatabase(binding: String) 12 + MissingBucket(binding: String) 13 + MissingImages(binding: String) 14 + MissingQueue(binding: String) 15 + MissingSecret(binding: String) 16 + EmptySecret(binding: String) 17 + } 18 + 19 + @external(javascript, "../cf_workers_environment_ffi.mjs", "d1_database") 20 + fn d1_database( 21 + environment: Environment, 22 + binding: String, 23 + ) -> Result(d1.Database, String) 24 + 25 + @external(javascript, "../cf_workers_environment_ffi.mjs", "r2_bucket") 26 + fn r2_bucket( 27 + environment: Environment, 28 + binding: String, 29 + ) -> Result(r2.Bucket, String) 30 + 31 + @external(javascript, "../cf_workers_environment_ffi.mjs", "images") 32 + fn image_binding( 33 + environment: Environment, 34 + binding: String, 35 + ) -> Result(images.Binding, String) 36 + 37 + @external(javascript, "../cf_workers_environment_ffi.mjs", "queue") 38 + fn queue_binding( 39 + environment: Environment, 40 + binding: String, 41 + ) -> Result(queue.Queue, String) 42 + 43 + @external(javascript, "../cf_workers_environment_ffi.mjs", "secret") 44 + fn secret(environment: Environment, binding: String) -> Result(String, String) 45 + 46 + pub fn error_to_string(error: Error) -> String { 47 + case error { 48 + MissingDatabase(binding) -> "missing D1 database binding: " <> binding 49 + MissingBucket(binding) -> "missing R2 bucket binding: " <> binding 50 + MissingImages(binding) -> "missing Images binding: " <> binding 51 + MissingQueue(binding) -> "missing Queue binding: " <> binding 52 + MissingSecret(binding) -> "missing secret binding: " <> binding 53 + EmptySecret(binding) -> "empty secret binding: " <> binding 54 + } 55 + } 56 + 57 + pub fn required_secret( 58 + environment: Environment, 59 + binding: String, 60 + ) -> Result(String, Error) { 61 + use value <- result.try( 62 + secret(environment, binding) 63 + |> result.replace_error(MissingSecret(binding)), 64 + ) 65 + 66 + case string.trim(value) { 67 + "" -> Error(EmptySecret(binding)) 68 + _ -> Ok(value) 69 + } 70 + } 71 + 72 + pub fn d1( 73 + environment: Environment, 74 + binding: String, 75 + ) -> Result(d1.Database, Error) { 76 + d1_database(environment, binding) 77 + |> result.replace_error(MissingDatabase(binding)) 78 + } 79 + 80 + pub fn bucket( 81 + environment: Environment, 82 + binding: String, 83 + ) -> Result(r2.Bucket, Error) { 84 + r2_bucket(environment, binding) 85 + |> result.replace_error(MissingBucket(binding)) 86 + } 87 + 88 + pub fn images_binding( 89 + environment: Environment, 90 + binding: String, 91 + ) -> Result(images.Binding, Error) { 92 + image_binding(environment, binding) 93 + |> result.replace_error(MissingImages(binding)) 94 + } 95 + 96 + pub fn queue( 97 + environment: Environment, 98 + binding: String, 99 + ) -> Result(queue.Queue, Error) { 100 + queue_binding(environment, binding) 101 + |> result.replace_error(MissingQueue(binding)) 102 + }
+10
src/cf_workers/execution_context.gleam
··· 1 + import gleam/dynamic.{type Dynamic} 2 + import gleam/javascript/promise.{type Promise} 3 + 4 + pub type WaitUntil 5 + 6 + @external(javascript, "../cf_workers_execution_context_ffi.mjs", "new_wait_until") 7 + pub fn new_wait_until(ctx: Dynamic) -> WaitUntil 8 + 9 + @external(javascript, "../cf_workers_execution_context_ffi.mjs", "wait_until_run") 10 + pub fn run(wait_until: WaitUntil, task: fn() -> Promise(Nil)) -> Nil
+34
src/cf_workers/images.gleam
··· 1 + import gleam/javascript/promise.{type Promise} 2 + 3 + pub type Binding 4 + 5 + pub type Format { 6 + Avif 7 + Webp 8 + Png 9 + Jpeg 10 + } 11 + 12 + pub fn content_type(format: Format) -> String { 13 + case format { 14 + Avif -> "image/avif" 15 + Webp -> "image/webp" 16 + Png -> "image/png" 17 + Jpeg -> "image/jpeg" 18 + } 19 + } 20 + 21 + @external(javascript, "../cf_workers_images_ffi.mjs", "convert") 22 + fn do_convert( 23 + binding: Binding, 24 + image: BitArray, 25 + format: String, 26 + ) -> Promise(Result(BitArray, String)) 27 + 28 + pub fn convert( 29 + binding: Binding, 30 + image: BitArray, 31 + format: Format, 32 + ) -> Promise(Result(BitArray, String)) { 33 + do_convert(binding, image, content_type(format)) 34 + }
+33
src/cf_workers/queue.gleam
··· 1 + import gleam/dynamic.{type Dynamic} 2 + import gleam/javascript/array 3 + import gleam/javascript/promise.{type Promise} 4 + 5 + pub type Queue 6 + 7 + pub type Message 8 + 9 + pub type MessageBatch 10 + 11 + @external(javascript, "../cf_workers_queue_ffi.mjs", "send_int") 12 + pub fn send_int(queue: Queue, body: Int) -> Promise(Result(Nil, String)) 13 + 14 + @external(javascript, "../cf_workers_queue_ffi.mjs", "send_string") 15 + pub fn send_string(queue: Queue, body: String) -> Promise(Result(Nil, String)) 16 + 17 + @external(javascript, "../cf_workers_queue_ffi.mjs", "messages") 18 + fn do_messages(batch: MessageBatch) -> array.Array(Message) 19 + 20 + pub fn messages(batch: MessageBatch) -> List(Message) { 21 + batch 22 + |> do_messages 23 + |> array.to_list 24 + } 25 + 26 + @external(javascript, "../cf_workers_queue_ffi.mjs", "body") 27 + pub fn body(message: Message) -> Dynamic 28 + 29 + @external(javascript, "../cf_workers_queue_ffi.mjs", "retry") 30 + pub fn retry(message: Message) -> Nil 31 + 32 + @external(javascript, "../cf_workers_queue_ffi.mjs", "ack") 33 + pub fn ack(message: Message) -> Nil
+64
src/cf_workers/r2.gleam
··· 1 + import gleam/javascript/promise.{type Promise} 2 + import gleam/option.{type Option} 3 + 4 + pub type Bucket 5 + 6 + pub type Object 7 + 8 + pub type ObjectBody 9 + 10 + @external(javascript, "../cf_workers_r2_ffi.mjs", "delete") 11 + pub fn delete(bucket: Bucket, key: String) -> Promise(Result(Nil, String)) 12 + 13 + @external(javascript, "../cf_workers_r2_ffi.mjs", "get") 14 + pub fn get(bucket: Bucket, key: String) -> Promise(Result(ObjectBody, Nil)) 15 + 16 + @external(javascript, "../cf_workers_r2_ffi.mjs", "read_bytes") 17 + pub fn read_bytes(object: ObjectBody) -> Promise(Result(BitArray, Nil)) 18 + 19 + @external(javascript, "../cf_workers_r2_ffi.mjs", "head") 20 + pub fn head(bucket: Bucket, key: String) -> Promise(Result(Object, Nil)) 21 + 22 + @external(javascript, "../cf_workers_r2_ffi.mjs", "put") 23 + fn do_put( 24 + bucket: Bucket, 25 + key: String, 26 + value: BitArray, 27 + ) -> Promise(Result(Object, Nil)) 28 + 29 + @external(javascript, "../cf_workers_r2_ffi.mjs", "put_with_content_type") 30 + fn do_put_with_content_type( 31 + bucket: Bucket, 32 + key: String, 33 + value: BitArray, 34 + content_type: String, 35 + ) -> Promise(Result(Object, Nil)) 36 + 37 + pub fn put( 38 + bucket: Bucket, 39 + key: String, 40 + value: BitArray, 41 + ) -> Promise(Result(Object, Nil)) { 42 + do_put(bucket, key, value) 43 + } 44 + 45 + pub fn put_with_content_type( 46 + bucket: Bucket, 47 + key: String, 48 + value: BitArray, 49 + content_type: String, 50 + ) -> Promise(Result(Object, Nil)) { 51 + do_put_with_content_type(bucket, key, value, content_type) 52 + } 53 + 54 + pub type ObjectPage { 55 + ObjectPage(keys: List(String), cursor: Option(String), complete: Bool) 56 + } 57 + 58 + @external(javascript, "../cf_workers_r2_ffi.mjs", "list") 59 + pub fn list( 60 + bucket: Bucket, 61 + prefix: Option(String), 62 + cursor: Option(String), 63 + limit: Option(Int), 64 + ) -> Promise(Result(ObjectPage, String))