diff --git a/gleam.toml b/gleam.toml index e0333fe..577c9af 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,4 +1,4 @@ -name = "cf_workers" +name = "cf" version = "1.0.0" target = "javascript" diff --git a/src/cf/crypto.gleam b/src/cf/crypto.gleam new file mode 100644 index 0000000..c83ed96 --- /dev/null +++ b/src/cf/crypto.gleam @@ -0,0 +1,10 @@ +//// Web Crypto helpers exposed by the Cloudflare Workers runtime. + +import gleam/javascript/promise.{type Promise} + +/// Generate a random UUID string. +/// +/// This wraps the runtime's cryptographic UUID generation. The returned value is +/// suitable for application identifiers where a random UUID is acceptable. +@external(javascript, "../cf_crypto_ffi.mjs", "random_uuid") +pub fn random_uuid() -> Promise(String) diff --git a/src/cf/d1.gleam b/src/cf/d1.gleam new file mode 100644 index 0000000..141e23b --- /dev/null +++ b/src/cf/d1.gleam @@ -0,0 +1,130 @@ +//// Cloudflare D1 binding helpers. +//// +//// This module is a small Gleam wrapper around the JavaScript D1 Worker Binding +//// API. It keeps Cloudflare database handles opaque and exposes the common D1 +//// operations as `Promise(Result(_, String))` values rather than throwing for +//// ordinary query failures. +//// +//// Values returned from D1 rows are represented as `gleam/dynamic.Dynamic`. +//// Decode them at the boundary of your application using `gleam/dynamic/decode`. +//// +//// The wrapper does not attempt to model SQL types or schemas. Applications own +//// SQL construction, parameter ordering, row decoding, and domain-specific error +//// conversion. + +import gleam/dynamic.{type Dynamic} +import gleam/javascript/array.{type Array} +import gleam/javascript/promise.{type Promise} + +/// An opaque Cloudflare D1 database binding. +/// +/// Obtain this from `cf/environment.d1` using the binding name from your +/// Worker environment. The value corresponds to Cloudflare's `D1Database` object. +pub type Database + +/// Prepare a SQL statement against a D1 database. +/// +/// The query may contain positional placeholders (`?`) for later binding with +/// `bind`. This function mirrors `D1Database.prepare` and does not execute SQL. +@external(javascript, "../cf_d1_ffi.mjs", "prepare") +pub fn prepare(db: Database, query: String) -> PreparedStatement + +/// An opaque prepared D1 statement. +/// +/// Prepared statements can be executed directly with `run`, `raw`, or `first`, +/// or grouped with `batch`. +pub type PreparedStatement + +@external(javascript, "../cf_d1_ffi.mjs", "bind") +fn do_bind( + statement: PreparedStatement, + values: Array(String), +) -> PreparedStatement + +/// Bind string parameters to a prepared statement. +/// +/// Values are spread into D1's JavaScript `statement.bind(...values)` call in +/// the order supplied. This package currently models bound values as strings; +/// callers should convert numbers and other primitive values explicitly before +/// binding. +pub fn bind( + statement: PreparedStatement, + values: List(String), +) -> PreparedStatement { + do_bind(statement, array.from_list(values)) +} + +/// Result metadata and rows from a D1 statement execution. +/// +/// `success` mirrors D1's statement success flag. `meta` is Cloudflare's D1 meta +/// object as dynamic data, including fields such as `changes`, `last_row_id`, +/// and timing information when available. `results` is an array of row objects, +/// also represented dynamically. +/// +/// For `insert ... returning`, `update ... returning`, and `select` queries, +/// `results` contains the returned rows. For statements that do not return rows, +/// it is typically empty. +pub type RunResult { + RunResult(success: Bool, meta: Dynamic, results: Array(Dynamic)) +} + +/// Execute a prepared statement using D1's `run` method. +/// +/// Use this for inserts, updates, deletes, or SQL that should return a D1 result +/// object. D1 errors are returned as `Error(String)`. +@external(javascript, "../cf_d1_ffi.mjs", "run") +pub fn run(statement: PreparedStatement) -> Promise(Result(RunResult, String)) + +/// Execute a prepared statement and return rows as positional arrays. +/// +/// This mirrors D1's `raw` method. It is useful when selecting a known column +/// order and decoding by index rather than by object field name. +@external(javascript, "../cf_d1_ffi.mjs", "raw") +pub fn raw( + statement: PreparedStatement, +) -> Promise(Result(Array(Array(Dynamic)), String)) + +/// Execute a prepared statement and return the first row. +/// +/// The returned dynamic value is the first row object from D1. If D1 returns no +/// row, Cloudflare's binding decides the exact JavaScript value; decode it at +/// the call site according to the query you issued. +@external(javascript, "../cf_d1_ffi.mjs", "first") +pub fn first(statement: PreparedStatement) -> Promise(Result(Dynamic, String)) + +@external(javascript, "../cf_d1_ffi.mjs", "batch") +fn do_batch( + db: Database, + statements: Array(PreparedStatement), +) -> Promise(Result(Array(RunResult), String)) + +/// Execute multiple prepared statements as one D1 batch. +/// +/// D1 executes batch statements sequentially and returns one `RunResult` per +/// statement in the same order. Each result contains that statement's `success`, +/// `meta`, and `results` fields. +/// +/// Cloudflare D1 rejects empty batches. Callers should avoid calling `batch` +/// with `[]`, or deliberately handle the returned error. +pub fn batch( + db: Database, + statements: List(PreparedStatement), +) -> Promise(Result(Array(RunResult), String)) { + use result <- promise.await(do_batch(db, array.from_list(statements))) + result |> promise.resolve() +} + +/// Result from D1's `exec` API. +/// +/// `count` is the number of statements executed. `duration` is D1's reported +/// execution duration. +pub type ExecResult { + ExecResult(count: Int, duration: Float) +} + +/// Execute one or more SQL statements with D1's `exec` API. +/// +/// This is intended for direct SQL strings such as migrations. Prefer prepared +/// statements and `bind` for user-provided data. +@external(javascript, "../cf_d1_ffi.mjs", "exec") +pub fn exec(db: Database, query: String) -> Promise(Result(ExecResult, String)) diff --git a/src/cf_workers/environment.gleam b/src/cf/environment.gleam similarity index 54% rename from src/cf_workers/environment.gleam rename to src/cf/environment.gleam index 155cc5a..6293ae3 100644 --- a/src/cf_workers/environment.gleam +++ b/src/cf/environment.gleam @@ -1,12 +1,28 @@ -import cf_workers/d1 -import cf_workers/images -import cf_workers/queue -import cf_workers/r2 +//// Helpers for reading Cloudflare Worker environment bindings. +//// +//// Cloudflare passes bindings, secrets, and other configuration on the Worker +//// environment object. This module keeps that JavaScript object opaque and +//// exposes typed accessors for the bindings used by this package. +//// +//// Each accessor takes the configured binding name. Missing bindings are mapped +//// to domain-specific `Error` variants so application startup code can report +//// clear configuration failures. + +import cf/d1 +import cf/images +import cf/queue +import cf/r2 import gleam/result import gleam/string +/// The opaque Worker environment object passed to a fetch, queue, or scheduled handler. pub type Environment +/// Configuration errors found while reading Worker environment bindings. +/// +/// `Missing*` variants mean the named binding is absent or not exposed on the +/// environment object. `EmptySecret` means the binding exists but trims to an +/// empty string, which is usually a deployment/configuration error. pub type Error { MissingDatabase(binding: String) MissingBucket(binding: String) @@ -16,33 +32,38 @@ pub type Error { EmptySecret(binding: String) } -@external(javascript, "../cf_workers_environment_ffi.mjs", "d1_database") +@external(javascript, "../cf_environment_ffi.mjs", "d1_database") fn d1_database( environment: Environment, binding: String, ) -> Result(d1.Database, String) -@external(javascript, "../cf_workers_environment_ffi.mjs", "r2_bucket") +@external(javascript, "../cf_environment_ffi.mjs", "r2_bucket") fn r2_bucket( environment: Environment, binding: String, ) -> Result(r2.Bucket, String) -@external(javascript, "../cf_workers_environment_ffi.mjs", "images") +@external(javascript, "../cf_environment_ffi.mjs", "images") fn image_binding( environment: Environment, binding: String, ) -> Result(images.Binding, String) -@external(javascript, "../cf_workers_environment_ffi.mjs", "queue") +@external(javascript, "../cf_environment_ffi.mjs", "queue") fn queue_binding( environment: Environment, binding: String, ) -> Result(queue.Queue, String) -@external(javascript, "../cf_workers_environment_ffi.mjs", "secret") +@external(javascript, "../cf_environment_ffi.mjs", "secret") fn secret(environment: Environment, binding: String) -> Result(String, String) +/// Convert an environment error into a short human-readable message. +/// +/// This is intended for startup diagnostics, HTTP 500 messages during local +/// development, and test failures. Production applications may prefer mapping +/// variants to structured logs instead. pub fn error_to_string(error: Error) -> String { case error { MissingDatabase(binding) -> "missing D1 database binding: " <> binding @@ -54,6 +75,10 @@ pub fn error_to_string(error: Error) -> String { } } +/// Read a required non-empty secret binding. +/// +/// Secrets are returned as strings. The value is trimmed only to validate that it +/// is not empty; the original value is returned unchanged on success. pub fn required_secret( environment: Environment, binding: String, @@ -69,6 +94,7 @@ pub fn required_secret( } } +/// Read a D1 database binding by name. pub fn d1( environment: Environment, binding: String, @@ -77,6 +103,7 @@ pub fn d1( |> result.replace_error(MissingDatabase(binding)) } +/// Read an R2 bucket binding by name. pub fn bucket( environment: Environment, binding: String, @@ -85,6 +112,7 @@ pub fn bucket( |> result.replace_error(MissingBucket(binding)) } +/// Read a Cloudflare Images binding by name. pub fn images_binding( environment: Environment, binding: String, @@ -93,6 +121,7 @@ pub fn images_binding( |> result.replace_error(MissingImages(binding)) } +/// Read a Cloudflare Queue producer binding by name. pub fn queue( environment: Environment, binding: String, diff --git a/src/cf/execution_context.gleam b/src/cf/execution_context.gleam new file mode 100644 index 0000000..846835b --- /dev/null +++ b/src/cf/execution_context.gleam @@ -0,0 +1,27 @@ +//// Cloudflare Worker execution context helpers. +//// +//// Workers expose `ctx.waitUntil(promise)` so background work can continue +//// after a response is returned. This module wraps that API in an opaque +//// `WaitUntil` value and a Gleam-friendly `run` function. + +import gleam/dynamic.{type Dynamic} +import gleam/javascript/promise.{type Promise} + +/// Opaque wrapper around Cloudflare's `ExecutionContext.waitUntil` function. +pub type WaitUntil + +/// Create a `WaitUntil` wrapper from the raw Worker execution context. +/// +/// Pass the JavaScript `ctx` value received by the Worker handler. The returned +/// value can be stored in application context and passed to code that needs to +/// schedule background work without exposing the raw JavaScript object. +@external(javascript, "../cf_execution_context_ffi.mjs", "new_wait_until") +pub fn new_wait_until(ctx: Dynamic) -> WaitUntil + +/// Run an asynchronous task through `ctx.waitUntil`. +/// +/// The `task` function is called immediately and its returned promise is passed +/// to Cloudflare. The function itself returns `Nil` because `waitUntil` schedules +/// work rather than producing an application result. +@external(javascript, "../cf_execution_context_ffi.mjs", "wait_until_run") +pub fn run(wait_until: WaitUntil, task: fn() -> Promise(Nil)) -> Nil diff --git a/src/cf/images.gleam b/src/cf/images.gleam new file mode 100644 index 0000000..ca87359 --- /dev/null +++ b/src/cf/images.gleam @@ -0,0 +1,48 @@ +//// Cloudflare Images binding helpers. +//// +//// This module exposes a small API for converting image bytes through a +//// Cloudflare Images binding. The input and output are represented as +//// `BitArray` values, so this is best suited to small or already-buffered +//// images. + +import gleam/javascript/promise.{type Promise} + +/// Opaque Cloudflare Images binding. +pub type Binding + +/// Output image formats supported by this wrapper. +pub type Format { + Avif + Webp + Png + Jpeg +} + +/// Return the HTTP content type for an image format. +pub fn content_type(format: Format) -> String { + case format { + Avif -> "image/avif" + Webp -> "image/webp" + Png -> "image/png" + Jpeg -> "image/jpeg" + } +} + +@external(javascript, "../cf_images_ffi.mjs", "convert") +fn do_convert( + binding: Binding, + image: BitArray, + format: String, +) -> Promise(Result(BitArray, Nil)) + +/// Convert image bytes to another format. +/// +/// The `format` is converted to the corresponding content type before crossing +/// the JavaScript FFI boundary. The returned bytes are the converted image. +pub fn convert( + binding: Binding, + image: BitArray, + format: Format, +) -> Promise(Result(BitArray, Nil)) { + do_convert(binding, image, content_type(format)) +} diff --git a/src/cf/queue.gleam b/src/cf/queue.gleam new file mode 100644 index 0000000..28b5584 --- /dev/null +++ b/src/cf/queue.gleam @@ -0,0 +1,72 @@ +//// Cloudflare Queues binding helpers. +//// +//// This module wraps the Queue producer and consumer objects supplied by +//// Cloudflare Workers. Queue handles and messages are opaque; applications use +//// these helpers to send simple payloads, inspect message bodies, and control +//// acknowledgement/retry behaviour in queue consumers. + +import gleam/dynamic.{type Dynamic} +import gleam/javascript/array +import gleam/javascript/promise.{type Promise} + +/// An opaque Cloudflare Queue producer binding. +/// +/// Obtain this from `cf/environment.queue` using the binding name from +/// your Worker environment. +pub type Queue + +/// An opaque queue message delivered to a Worker queue consumer. +pub type Message + +/// An opaque batch of queue messages delivered to a Worker queue consumer. +pub type MessageBatch + +/// Send an integer payload to a Cloudflare Queue. +/// +/// The payload is passed directly to `queue.send`. D1 outbox workflows commonly +/// send database-generated integer IDs so the consumer can load the pending +/// outbox row by ID. +@external(javascript, "../cf_queue_ffi.mjs", "send_int") +pub fn send_int(queue: Queue, body: Int) -> Promise(Result(Nil, String)) + +/// Send a string payload to a Cloudflare Queue. +/// +/// Use this for already-encoded application payloads or identifiers. Errors from +/// the Cloudflare binding are returned as `Error(String)`. +@external(javascript, "../cf_queue_ffi.mjs", "send_string") +pub fn send_string(queue: Queue, body: String) -> Promise(Result(Nil, String)) + +@external(javascript, "../cf_queue_ffi.mjs", "messages") +fn do_messages(batch: MessageBatch) -> array.Array(Message) + +/// Convert a Cloudflare message batch into a Gleam list of messages. +/// +/// Queue handlers receive a JavaScript batch object. This helper extracts its +/// `messages` array and converts it to a list for normal Gleam processing. +pub fn messages(batch: MessageBatch) -> List(Message) { + batch + |> do_messages + |> array.to_list +} + +/// Read the dynamic body of a queue message. +/// +/// Decode the returned `Dynamic` according to the payload type your producer +/// sends. For example, messages sent with `send_int` should be decoded as an +/// integer. +@external(javascript, "../cf_queue_ffi.mjs", "body") +pub fn body(message: Message) -> Dynamic + +/// Ask Cloudflare Queues to retry a message. +/// +/// Call this when processing failed and the message should be delivered again +/// according to the queue's retry policy. +@external(javascript, "../cf_queue_ffi.mjs", "retry") +pub fn retry(message: Message) -> Nil + +/// Acknowledge a queue message. +/// +/// Call this after successful processing when you do not want the message to be +/// retried. +@external(javascript, "../cf_queue_ffi.mjs", "ack") +pub fn ack(message: Message) -> Nil diff --git a/src/cf/r2.gleam b/src/cf/r2.gleam new file mode 100644 index 0000000..fb0eb72 --- /dev/null +++ b/src/cf/r2.gleam @@ -0,0 +1,101 @@ +//// Cloudflare R2 binding helpers. +//// +//// This module wraps common R2 bucket operations. Bucket, object, and object +//// body handles are opaque because they are JavaScript values supplied by the +//// Cloudflare runtime. + +import gleam/javascript/promise.{type Promise} +import gleam/option.{type Option} + +/// An opaque Cloudflare R2 bucket binding. +pub type Bucket + +/// Metadata for an R2 object returned by operations such as `head`, `put`, and `list`. +pub type Object + +/// An R2 object body returned by `get`. +/// +/// Use `read_bytes` to consume the body as a `BitArray`. +pub type ObjectBody + +/// Delete an object by key. +@external(javascript, "../cf_r2_ffi.mjs", "delete") +pub fn delete(bucket: Bucket, key: String) -> Promise(Result(Nil, String)) + +/// Fetch an object body by key. +/// +/// Returns `Error(Nil)` when the object cannot be read or is missing. Use `head` +/// when you only need metadata. +@external(javascript, "../cf_r2_ffi.mjs", "get") +pub fn get(bucket: Bucket, key: String) -> Promise(Result(ObjectBody, Nil)) + +/// Read an object body fully into memory as bytes. +/// +/// This is convenient for small objects. For large objects, prefer streaming at +/// the JavaScript boundary rather than buffering everything into a `BitArray`. +@external(javascript, "../cf_r2_ffi.mjs", "read_bytes") +pub fn read_bytes(object: ObjectBody) -> Promise(Result(BitArray, Nil)) + +/// Fetch object metadata without reading the body. +@external(javascript, "../cf_r2_ffi.mjs", "head") +pub fn head(bucket: Bucket, key: String) -> Promise(Result(Object, Nil)) + +@external(javascript, "../cf_r2_ffi.mjs", "put") +fn do_put( + bucket: Bucket, + key: String, + value: BitArray, +) -> Promise(Result(Object, Nil)) + +@external(javascript, "../cf_r2_ffi.mjs", "put_with_content_type") +fn do_put_with_content_type( + bucket: Bucket, + key: String, + value: BitArray, + content_type: String, +) -> Promise(Result(Object, Nil)) + +/// Store bytes in R2 under `key`. +/// +/// No content type is supplied. Use `put_with_content_type` when serving the +/// object through HTTP or when downstream tooling needs media metadata. +pub fn put( + bucket: Bucket, + key: String, + value: BitArray, +) -> Promise(Result(Object, Nil)) { + do_put(bucket, key, value) +} + +/// Store bytes in R2 with a content type. +/// +/// The `content_type` should be a valid HTTP media type such as `image/png` or +/// `application/json`. +pub fn put_with_content_type( + bucket: Bucket, + key: String, + value: BitArray, + content_type: String, +) -> Promise(Result(Object, Nil)) { + do_put_with_content_type(bucket, key, value, content_type) +} + +/// One page of R2 object listing results. +/// +/// `keys` contains object keys in the page. `cursor` is the next cursor when +/// more pages are available. `complete` is true when the listing is exhausted. +pub type ObjectPage { + ObjectPage(keys: List(String), cursor: Option(String), complete: Bool) +} + +/// List object keys in an R2 bucket. +/// +/// `prefix` restricts results to matching keys. `cursor` continues a previous +/// listing. `limit` asks R2 to cap the page size. +@external(javascript, "../cf_r2_ffi.mjs", "list") +pub fn list( + bucket: Bucket, + prefix: Option(String), + cursor: Option(String), + limit: Option(Int), +) -> Promise(Result(ObjectPage, String)) diff --git a/src/cf_workers_crypto_ffi.mjs b/src/cf_crypto_ffi.mjs similarity index 100% rename from src/cf_workers_crypto_ffi.mjs rename to src/cf_crypto_ffi.mjs diff --git a/src/cf_workers_d1_ffi.mjs b/src/cf_d1_ffi.mjs similarity index 72% rename from src/cf_workers_d1_ffi.mjs rename to src/cf_d1_ffi.mjs index 2e0714a..c7a1bf4 100644 --- a/src/cf_workers_d1_ffi.mjs +++ b/src/cf_d1_ffi.mjs @@ -1,4 +1,5 @@ import { Result$Ok, Result$Error } from "./gleam.mjs"; +import { RunResult$RunResult } from "./cf/d1.mjs" export function prepare(db, query) { return db.prepare(query); @@ -34,7 +35,13 @@ export async function first(statement) { export async function batch(db, statements) { try { - return Result$Ok(await db.batch(statements)); + const awaited = await db.batch(statements) + let results = [] + for (const statement_result of awaited) { + const run_result = RunResult$RunResult(statement_result.success, statement_result.meta, statement_result.results) + results.push(run_result) + } + return Result$Ok(results); } catch (error) { return Result$Error(`${error}`); } diff --git a/src/cf_workers_environment_ffi.mjs b/src/cf_environment_ffi.mjs similarity index 100% rename from src/cf_workers_environment_ffi.mjs rename to src/cf_environment_ffi.mjs diff --git a/src/cf_workers_execution_context_ffi.mjs b/src/cf_execution_context_ffi.mjs similarity index 100% rename from src/cf_workers_execution_context_ffi.mjs rename to src/cf_execution_context_ffi.mjs diff --git a/src/cf_workers_images_ffi.mjs b/src/cf_images_ffi.mjs similarity index 68% rename from src/cf_workers_images_ffi.mjs rename to src/cf_images_ffi.mjs index 37d0717..bc9b466 100644 --- a/src/cf_workers_images_ffi.mjs +++ b/src/cf_images_ffi.mjs @@ -9,14 +9,12 @@ export async function convert(binding, image, format) { ).response(); if (!response.ok) { - return Result$Error(`Images conversion failed with status ${response.status}`); + return Result$Error(); } const converted = await response.arrayBuffer(); return Result$Ok(toBitArray(new Uint8Array(converted))); } catch (error) { - return Result$Error( - error instanceof globalThis.Error ? error.message : String(error), - ); + return Result$Error(); } } diff --git a/src/cf_workers_queue_ffi.mjs b/src/cf_queue_ffi.mjs similarity index 100% rename from src/cf_workers_queue_ffi.mjs rename to src/cf_queue_ffi.mjs diff --git a/src/cf_workers_r2_ffi.mjs b/src/cf_r2_ffi.mjs similarity index 97% rename from src/cf_workers_r2_ffi.mjs rename to src/cf_r2_ffi.mjs index b9e97bc..edfe9be 100644 --- a/src/cf_workers_r2_ffi.mjs +++ b/src/cf_r2_ffi.mjs @@ -1,7 +1,7 @@ import { Result$Ok, Result$Error, toBitArray } from "./gleam.mjs"; import { Option$Some, Option$None } from "../gleam_stdlib/gleam/option.mjs"; import { List$Empty, List$NonEmpty } from "./gleam.mjs"; -import { ObjectPage$ObjectPage } from "./cf_workers/r2.mjs"; +import { ObjectPage$ObjectPage } from "./cf/r2.mjs"; function optionToValue(option) { if (option instanceof Option$Some) { diff --git a/src/cf_workers/crypto.gleam b/src/cf_workers/crypto.gleam deleted file mode 100644 index 340f533..0000000 --- a/src/cf_workers/crypto.gleam +++ /dev/null @@ -1,4 +0,0 @@ -import gleam/javascript/promise.{type Promise} - -@external(javascript, "../cf_workers_crypto_ffi.mjs", "random_uuid") -pub fn random_uuid() -> Promise(String) diff --git a/src/cf_workers/d1.gleam b/src/cf_workers/d1.gleam deleted file mode 100644 index e8e4ed5..0000000 --- a/src/cf_workers/d1.gleam +++ /dev/null @@ -1,52 +0,0 @@ -import gleam/dynamic.{type Dynamic} -import gleam/javascript/array.{type Array} -import gleam/javascript/promise.{type Promise} - -pub type Database - -@external(javascript, "../cf_workers_d1_ffi.mjs", "prepare") -pub fn prepare(db: Database, query: String) -> PreparedStatement - -pub type PreparedStatement - -@external(javascript, "../cf_workers_d1_ffi.mjs", "bind") -fn do_bind( - statement: PreparedStatement, - values: Array(String), -) -> PreparedStatement - -pub fn bind(statement, values) { - do_bind(statement, array.from_list(values)) -} - -pub type RunResult { - RunResult(success: Bool, meta: Dynamic, results: Array(Dynamic)) -} - -@external(javascript, "../cf_workers_d1_ffi.mjs", "run") -pub fn run(statement: PreparedStatement) -> Promise(Result(RunResult, String)) - -@external(javascript, "../cf_workers_d1_ffi.mjs", "raw") -pub fn raw( - statement: PreparedStatement, -) -> Promise(Result(Array(Array(Dynamic)), String)) - -@external(javascript, "../cf_workers_d1_ffi.mjs", "first") -pub fn first(statement: PreparedStatement) -> Promise(Result(Dynamic, String)) - -@external(javascript, "../cf_workers_d1_ffi.mjs", "batch") -fn do_batch( - db: Database, - statements: Array(PreparedStatement), -) -> Promise(Result(Array(RunResult), String)) - -pub fn batch(db, statements) { - do_batch(db, array.from_list(statements)) -} - -pub type ExecResult { - ExecResult(count: Int, duration: Float) -} - -@external(javascript, "../cf_workers_d1_ffi.mjs", "exec") -pub fn exec(db: Database, query: String) -> Promise(Result(ExecResult, String)) diff --git a/src/cf_workers/execution_context.gleam b/src/cf_workers/execution_context.gleam deleted file mode 100644 index e137c30..0000000 --- a/src/cf_workers/execution_context.gleam +++ /dev/null @@ -1,10 +0,0 @@ -import gleam/dynamic.{type Dynamic} -import gleam/javascript/promise.{type Promise} - -pub type WaitUntil - -@external(javascript, "../cf_workers_execution_context_ffi.mjs", "new_wait_until") -pub fn new_wait_until(ctx: Dynamic) -> WaitUntil - -@external(javascript, "../cf_workers_execution_context_ffi.mjs", "wait_until_run") -pub fn run(wait_until: WaitUntil, task: fn() -> Promise(Nil)) -> Nil diff --git a/src/cf_workers/images.gleam b/src/cf_workers/images.gleam deleted file mode 100644 index 527bd4a..0000000 --- a/src/cf_workers/images.gleam +++ /dev/null @@ -1,34 +0,0 @@ -import gleam/javascript/promise.{type Promise} - -pub type Binding - -pub type Format { - Avif - Webp - Png - Jpeg -} - -pub fn content_type(format: Format) -> String { - case format { - Avif -> "image/avif" - Webp -> "image/webp" - Png -> "image/png" - Jpeg -> "image/jpeg" - } -} - -@external(javascript, "../cf_workers_images_ffi.mjs", "convert") -fn do_convert( - binding: Binding, - image: BitArray, - format: String, -) -> Promise(Result(BitArray, String)) - -pub fn convert( - binding: Binding, - image: BitArray, - format: Format, -) -> Promise(Result(BitArray, String)) { - do_convert(binding, image, content_type(format)) -} diff --git a/src/cf_workers/queue.gleam b/src/cf_workers/queue.gleam deleted file mode 100644 index 1ac0a7f..0000000 --- a/src/cf_workers/queue.gleam +++ /dev/null @@ -1,33 +0,0 @@ -import gleam/dynamic.{type Dynamic} -import gleam/javascript/array -import gleam/javascript/promise.{type Promise} - -pub type Queue - -pub type Message - -pub type MessageBatch - -@external(javascript, "../cf_workers_queue_ffi.mjs", "send_int") -pub fn send_int(queue: Queue, body: Int) -> Promise(Result(Nil, String)) - -@external(javascript, "../cf_workers_queue_ffi.mjs", "send_string") -pub fn send_string(queue: Queue, body: String) -> Promise(Result(Nil, String)) - -@external(javascript, "../cf_workers_queue_ffi.mjs", "messages") -fn do_messages(batch: MessageBatch) -> array.Array(Message) - -pub fn messages(batch: MessageBatch) -> List(Message) { - batch - |> do_messages - |> array.to_list -} - -@external(javascript, "../cf_workers_queue_ffi.mjs", "body") -pub fn body(message: Message) -> Dynamic - -@external(javascript, "../cf_workers_queue_ffi.mjs", "retry") -pub fn retry(message: Message) -> Nil - -@external(javascript, "../cf_workers_queue_ffi.mjs", "ack") -pub fn ack(message: Message) -> Nil diff --git a/src/cf_workers/r2.gleam b/src/cf_workers/r2.gleam deleted file mode 100644 index 54c5c70..0000000 --- a/src/cf_workers/r2.gleam +++ /dev/null @@ -1,64 +0,0 @@ -import gleam/javascript/promise.{type Promise} -import gleam/option.{type Option} - -pub type Bucket - -pub type Object - -pub type ObjectBody - -@external(javascript, "../cf_workers_r2_ffi.mjs", "delete") -pub fn delete(bucket: Bucket, key: String) -> Promise(Result(Nil, String)) - -@external(javascript, "../cf_workers_r2_ffi.mjs", "get") -pub fn get(bucket: Bucket, key: String) -> Promise(Result(ObjectBody, Nil)) - -@external(javascript, "../cf_workers_r2_ffi.mjs", "read_bytes") -pub fn read_bytes(object: ObjectBody) -> Promise(Result(BitArray, Nil)) - -@external(javascript, "../cf_workers_r2_ffi.mjs", "head") -pub fn head(bucket: Bucket, key: String) -> Promise(Result(Object, Nil)) - -@external(javascript, "../cf_workers_r2_ffi.mjs", "put") -fn do_put( - bucket: Bucket, - key: String, - value: BitArray, -) -> Promise(Result(Object, Nil)) - -@external(javascript, "../cf_workers_r2_ffi.mjs", "put_with_content_type") -fn do_put_with_content_type( - bucket: Bucket, - key: String, - value: BitArray, - content_type: String, -) -> Promise(Result(Object, Nil)) - -pub fn put( - bucket: Bucket, - key: String, - value: BitArray, -) -> Promise(Result(Object, Nil)) { - do_put(bucket, key, value) -} - -pub fn put_with_content_type( - bucket: Bucket, - key: String, - value: BitArray, - content_type: String, -) -> Promise(Result(Object, Nil)) { - do_put_with_content_type(bucket, key, value, content_type) -} - -pub type ObjectPage { - ObjectPage(keys: List(String), cursor: Option(String), complete: Bool) -} - -@external(javascript, "../cf_workers_r2_ffi.mjs", "list") -pub fn list( - bucket: Bucket, - prefix: Option(String), - cursor: Option(String), - limit: Option(Int), -) -> Promise(Result(ObjectPage, String))