### Set up a simple server with rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Example of setting up a basic HTTP server using Bun.serve. It demonstrates how to access request headers and send a response. ```rescript let server = Bun.serve({ fetch: async (request, _server) => { let userName = request ->Request.headers ->Headers.get("x-user-name") ->Option.getOr("Unknown user") Response.make(`Hello ${userName}!`, ~options={status: 200}) }, }) let port = server ->Bun.Server.port ->Int.toString let hostName = server->Bun.Server.hostname Console.log(`Server listening on http://${hostName}:${port} લાગ!`) ``` -------------------------------- ### Install rescript-bun Source: https://context7.com/zth/rescript-bun/llms.txt Install the rescript-bun library using npm. Configure rescript.json to open RescriptBun and RescriptBun.Globals. ```bash npm i rescript-bun@2 ``` ```json { "dependencies": ["rescript-bun"], "bsc-flags": ["-open RescriptBun", "-open RescriptBun.Globals"] } ``` -------------------------------- ### Use file system router with rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Example of creating a file system router using Bun.FileSystemRouter.make. This setup is inspired by Next.js routing and specifies a directory for pages and an origin. ```rescript let router = Bun.FileSystemRouter.make({ style: NextJs, dir: "./pages", origin: "https://mydomain.com", assetPrefix: "_next/static/", }) let matches = router->Bun.FileSystemRouter.match("/") ``` -------------------------------- ### Install rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Install the rescript-bun package using npm. Ensure you are using ReScript v12 or later. ```bash npm i rescript-bun@2 ``` -------------------------------- ### Bun.S3 Client for Object Storage Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates creating a scoped S3 client, writing, reading, checking existence, getting stats, generating presigned URLs, listing objects, and deleting files. Ensure correct credentials and bucket name are configured. ```rescript open Bun.S3 // Create a scoped client let client = S3Client.make({ accessKeyId: "AKIAIOSFODNN7EXAMPLE", secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", region: "us-east-1", bucket: "my-bucket", }) // Get a file handle and write to it let s3File = client->S3Client.file("uploads/photo.jpg") let _ = await s3File->S3File.writeString("file content here") // Read back let exists = await s3File->S3File.exists Console.log(`Exists: ${exists->Bool.toString}`) let stats = await s3File->S3File.stat Console.log(`Size: ${stats.size->Float.toString}, ETag: ${stats.etag}`) // Generate a presigned URL (expires in 5 minutes) let url = s3File->S3File.presign(~options={expiresIn: 300.}) Console.log(`Download URL: ${url}`) // List objects let listing = await client->S3Client.list(~input={prefix: "uploads/", maxKeys: 10.}) switch listing.contents { | Some(items) => items->Array.forEach(item => Console.log(item.key->Option.getOr("?"))) | None => Console.log("No items found") } // Delete await s3File->S3File.delete ``` -------------------------------- ### Write tests with rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Use the 'Test' module to write tests with Bun's built-in test runner. This example demonstrates a simple addition test. ```rescript open Test describe("Playing around with tests", () => { test("addition works", () => { expect(1 + 1)->Expect.toBe(2) }) }) ``` -------------------------------- ### CSRF Token Generation and Verification with Bun.CSRF Source: https://context7.com/zth/rescript-bun/llms.txt Provides examples for generating CSRF tokens using explicit secrets or default in-memory secrets, with options for algorithm, encoding, and expiry. Verification with and without explicit options is also shown. ```rescript let secret = "my-very-secret-key" // Generate with explicit secret let token = Bun.CSRF.generateWithSecret(secret) Console.log(token) // base64url encoded token // Generate with options let customToken = Bun.CSRF.generateWithSecretOptions(secret, { algorithm: Sha256, encoding: Base64Url, expiresIn: 3600000., // 1 hour in ms }) // Verify let isValid = Bun.CSRF.verifyWithOptions(token, {secret, algorithm: Sha256}) Console.log(`CSRF valid: ${isValid->Bool.toString}`) // Default in-memory secret (suitable for single-process apps) let defaultToken = Bun.CSRF.generate() let defaultValid = Bun.CSRF.verify(defaultToken) Console.log(`Default CSRF valid: ${defaultValid->Bool.toString}) ``` -------------------------------- ### Transform HTML with HTMLRewriter in Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Shows how to use HTMLRewriter to modify HTML content. Examples include renaming tags, collecting specific elements, and injecting new content. This is useful for dynamic content manipulation on the server. ```rescript // Rewrite all
tags to
let rewriter = HTMLRewriter.make() ->HTMLRewriter.on( "*", {element: element => { if element.tagName == "div" { element.tagName = "section" } }}, ) let response = await fetch("https://bun.sh") let transformed = rewriter->HTMLRewriter.transform(response) let html = await transformed->Response.text Console.log(html) // Collect all internal links let links = [] let _collector = HTMLRewriter.make() ->HTMLRewriter.on( "a[href]", {element: el => { switch el.getAttribute("href") { | Value(href) if href->String.startsWith("/") => links->Array.push(href) | _ => () } }}, ) ->HTMLRewriter.transform(Response.make(html)) Console.log(links) // Inject a banner before let _injector = HTMLRewriter.make() ->HTMLRewriter.on( "body", {element: el => { el.append("

Injected by rescript-bun

", ~options={html: true}) }}, ) ``` -------------------------------- ### Read and Write Files with Bun.file Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates reading JSON config files, checking existence, writing strings to disk, copying files, and using incremental file writers. Files are read lazily. ```rescript let configFile = Bun.file("./config.json") Console.log(`Type: ${configFile->Bun.BunFile.type_}`) // "application/json" Console.log(`Size: ${configFile->Bun.BunFile.size->Float.toString} bytes`) let exists = await configFile->Bun.BunFile.exists if exists { let json = await configFile->Bun.BunFile.json Console.log(json) } else { Console.log("Config not found") } ``` ```rescript // Write a string to disk let bytesWritten = await Bun.Write.write( ~destination=Bun.Write.Destination.fromPath("./output.txt"), ~input=Bun.Write.Input.fromString("Hello, Bun!"), ) Console.log(`Wrote ${bytesWritten->Float.toString} bytes`) ``` ```rescript // Copy file to file (uses OS-level copy_file_range / clonefile) let _ = await Bun.Write.writeFileToFile( ~destination=Bun.file("./backup.json"), ~input=Bun.file("./config.json"), ) ``` ```rescript // Incremental file writer (for large files or streams) let writer = Bun.file("./log.txt")->Bun.BunFile.writer() let _ = writer->Bun.FileSink.writeString("Line 1\n") let _ = writer->Bun.FileSink.writeString("Line 2\n") let _ = writer->Bun.FileSink.end() ``` -------------------------------- ### Implement File-System Routing with Bun.FileSystemRouter Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates setting up and using Bun.FileSystemRouter for Next.js-style file-system-based routing. It matches URL paths to files, extracts dynamic parameters, and can be integrated into a Bun server. Configure `dir`, `origin`, and `fileExtensions` as needed. ```rescript let router = Bun.FileSystemRouter.make({ style: NextJs, dir: "./pages", origin: "https://mydomain.com", assetPrefix: "_next/static/", fileExtensions: [".res", ".js", ".ts"], }) // Match a URL path switch router->Bun.FileSystemRouter.match("/blog/2024/hello-world") { | Some(route) => { Console.log(`Matched: ${route.name}`) Console.log(`File: ${route.filePath}`) let year = route.params->Dict.get("year")->Option.getOr("unknown") let slug = route.params->Dict.get("slug")->Option.getOr("unknown") Console.log(`Year=${year} Slug=${slug}`) } | None => Console.log("No match") } // Use inside a server let _server = Bun.serve({ port: 3000, fetch: async (request, _server) => { switch router->Bun.FileSystemRouter.matchRequest(request) { | Some(route) => Response.make(`Serving: ${route.filePath}`) | None => Response.make("Not Found", ~options={status: 404}) } }, }) // Routes map (filename → path pattern) Console.log(router->Bun.FileSystemRouter.routes) ``` -------------------------------- ### Bun SQL Client for PostgreSQL, MySQL, and SQLite Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates connecting to databases using URLs, executing parameterized queries with tagged template literals, managing transactions, running raw SQL, executing SQL files, and using the SQLite dialect. Ensure database URLs are correctly formatted. ```rescript // Connect with a URL let pg = SQL.make("postgres://user:pass@localhost:5432/mydb") // Template literal query (safe parameterized) let results = await pg->SQL.query( ["SELECT * FROM products WHERE price < ", " AND active = "], [Number(100.0), Boolean(true)], ) // Transactions let _ = await pg->SQL.begin(async tx => { let _ = await tx->SQL.query( ["INSERT INTO accounts (name) VALUES ("], [String("Alice")], ) let _ = await tx->SQL.query( ["INSERT INTO accounts (name) VALUES ("], [String("Bob")], ) Promise.resolve() }) // Raw (unsafe) SQL without parameters let raw = await pg->SQL.unsafe("SELECT version()") // Run a SQL file let _ = await pg->SQL.file("./migrations/001_init.sql") // SQLite let sqlite = SQL.make("sqlite://local.db") let rows = await sqlite->SQL.query(["SELECT * FROM users WHERE id = "], [Number(1.0)]) await pg->SQL.end() ``` -------------------------------- ### Connect and Use RedisClient in Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates connecting to Redis/Valkey using default or custom URLs and options. Includes basic get/set, expiry, atomic counters, hash operations, set membership, and ping commands. Ensure Redis/Valkey is running and accessible. ```rescript open Redis // Connect to default (reads VALKEY_URL / REDIS_URL, or localhost:6379) let client = RedisClient.make() // Or with explicit URL and options let custom = RedisClient.make( ~url="redis://localhost:6379", ~options={ autoReconnect: true, maxRetries: 3, enableAutoPipelining: true, tls: false, }, ) // Basic get/set await client->RedisClient.set("greeting", RedisClient.String("Hello Bun")) let value = await client->RedisClient.get("greeting") Console.log(value->Null.getOr("(nil)")) // Hello Bun // Set with expiry (EX = seconds) await client->RedisClient.setWithExpiry("session:abc", RedisClient.String("data"), #EX, 3600) // Atomic counter let count = await client->RedisClient.incr("page_views") Console.log(`Views: ${count->Int.toString}`) // TTL and expiry let ttl = await client->RedisClient.ttl("session:abc") Console.log(`TTL: ${ttl->Int.toString}s`) // Hash operations await client->RedisClient.hmset("user:1", ["name", "Alice", "role", "admin"]) let allFields = await client->RedisClient.hgetall("user:1") Console.log(allFields) // Set membership let _ = await client->RedisClient.sadd("tags", "rescript") let members = await client->RedisClient.smembers("tags") Console.log(members) // Ping let pong = await client->RedisClient.ping Console.log(pong == #PONG ? "alive" : "dead") client->RedisClient.close ``` -------------------------------- ### Bun.serve - HTTP Server Source: https://context7.com/zth/rescript-bun/llms.txt Creates a high-performance HTTP server. The fetch handler receives a Request.t and the live Server.t and must return a promise. TLS, route maps, Unix sockets, hot reload, and WebSocket upgrades are all supported via optional fields. ```APIDOC ## Bun.serve - HTTP Server ### Description Creates a high-performance HTTP server. The `fetch` handler receives a `Request.t` and the live `Server.t` and must return a `promise`. TLS, route maps, Unix sockets, hot reload, and WebSocket upgrades are all supported via optional fields. ### Method POST ### Endpoint /serve ### Parameters #### Request Body - **port** (number) - Required - The port to listen on. - **fetch** (function) - Required - The fetch handler. - **error** (function) - Optional - The error handler. - **tls** (object) - Optional - TLS configuration. - **cert** (array) - Required - Certificate files. - **key** (array) - Required - Key files. - **ca** (array) - Required - CA files. ### Request Example ```rescript let server = Bun.serve({ port: 3000, fetch: async (request, _server) => { let url = request->Request.url->URL.make switch url->URL.pathname { | "/" => Response.makeWithJsonUnsafe({"message": "hello", "ok": true}) | "/health" => Response.make("OK", ~options={status: 200}) | _ => Response.make("Not Found", ~options={status: 404}) } }, error: (_server, _err) => Some(Response.make("Internal Server Error", ~options={status: 500})), }) Console.log(`Listening on http://${server->Bun.Server.hostname}:${server->Bun.Server.port->Int.toString}`) // TLS variant let _tlsServer = Bun.serve({ port: 443, fetch: async (_req, _srv) => Response.make("Secure!"), tls: { cert: [Bun.file("cert.pem")], key: [Bun.file("key.pem")], ca: [Bun.file("ca.pem")], }, }) ``` ### Response #### Success Response (200) - **server** (Bun.Server.t) - The created server instance. #### Response Example ```json { "message": "Server started", "port": 3000 } ``` ``` -------------------------------- ### Create HTTP Server with Bun.serve Source: https://context7.com/zth/rescript-bun/llms.txt Implement a high-performance HTTP server using Bun.serve. The fetch handler processes requests and returns responses. Supports routing, TLS, and error handling. ```rescript // Simple server with routing and TLS let server = Bun.serve({ port: 3000, fetch: async (request, _server) => { let url = request->Request.url->URL.make switch url->URL.pathname { | "/" => Response.makeWithJsonUnsafe({"message": "hello", "ok": true}) | "/health" => Response.make("OK", ~options={status: 200}) | _ => Response.make("Not Found", ~options={status: 404}) } }, error: (_server, _err) => Some(Response.make("Internal Server Error", ~options={status: 500})), }) Console.log(`Listening on http://${server->Bun.Server.hostname}:${server->Bun.Server.port->Int.toString}`) // TLS variant let _tlsServer = Bun.serve({ port: 443, fetch: async (_req, _srv) => Response.make("Secure!"), tls: { cert: [Bun.file("cert.pem")], key: [Bun.file("key.pem")], ca: [Bun.file("ca.pem")], }, }) ``` -------------------------------- ### Create WebSocket Server with Bun.serveWithWebSocket Source: https://context7.com/zth/rescript-bun/llms.txt Upgrade HTTP connections to WebSocket using Bun.serveWithWebSocket. Define handlers for connection open, message, close, and drain events. ```rescript let _wsServer = Bun.serveWithWebSocket({ port: 8080, fetch: async (request, server) => { let url = request->Request.url->URL.make if url->URL.pathname == "/chat" { let upgraded = server->Bun.Server.upgrade(request) if upgraded { // Response is ignored after upgrade Response.make("", ~options={status: 101}) } else { Response.make("WebSocket upgrade failed", ~options={status: 400}) } } else { Response.make("Use /chat for WebSocket", ~options={status: 200}) } }, websocket: { open_: ws => Console.log(`Client connected: ${ws->WebSocket.ServerWebSocket.data}`), message: (ws, msg) => { Console.log(`Received: ${msg}`) ws->WebSocket.ServerWebSocket.send(~message=`Echo: ${msg}`) }, close: (_ws, _code, _reason) => Console.log("Client disconnected"), }, }) ``` -------------------------------- ### BunSqlite for Embedded SQLite Databases Source: https://context7.com/zth/rescript-bun/llms.txt Illustrates opening a database file, creating tables, inserting and querying data using prepared statements, iterating over results, and using in-memory databases. Supports serialization for backups. ```rescript open BunSqlite // Open (or create) a database let db = Database.make("./app.db") // Create a table db->Database.query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")->Statement.run({}) // Insert rows let insert = db->Database.query("INSERT INTO users (name, age) VALUES ($name, $age)") insert->Statement.run({"$name": "Alice", "$age": 30}) insert->Statement.run({"$name": "Bob", "$age": 25}) // Query all rows let stmt = db->Database.query("SELECT * FROM users WHERE age > $minAge") let rows = stmt->Statement.all({"$minAge": 20}) rows->Array.forEach(row => Console.log(row)) // Query single row let user = stmt->Statement.get({"$minAge": 28}) switch user->Nullable.toOption { | Some(u) => Console.log(u) | None => Console.log("Not found") } // Iterate (memory-efficient for large results) let iter = stmt->Statement.iterate({"$minAge": 0}) iter->Iterator.forEach(row => Console.log(row)) // In-memory database (useful for tests) let memDb = Database.makeInMemory() memDb->Database.query("CREATE TABLE t (x INT)")->Statement.run({}) // Serialize to ArrayBuffer (e.g. for backup) let _buf = db->Database.serialize() db->Database.close() ``` -------------------------------- ### Bun.FileSystemRouter Source: https://context7.com/zth/rescript-bun/llms.txt Next.js-style file-system router that matches URL paths to files in a directory, extracting dynamic route parameters. ```APIDOC ## Bun.FileSystemRouter — File-System-Based Routing Next.js-style file-system router that matches URL paths to files in a directory, extracting dynamic route parameters. ```rescript let router = Bun.FileSystemRouter.make({ style: NextJs, dir: "./pages", origin: "https://mydomain.com", assetPrefix: "_next/static/", fileExtensions: [".res", ".js", ".ts"], }) // Match a URL path switch router->Bun.FileSystemRouter.match("/blog/2024/hello-world") { | Some(route) => { Console.log(`Matched: ${route.name}`) // "/blog/[year]/[slug]" Console.log(`File: ${route.filePath}`) let year = route.params->Dict.get("year")->Option.getOr("unknown") let slug = route.params->Dict.get("slug")->Option.getOr("unknown") Console.log(`Year=${year} Slug=${slug}`) } | None => Console.log("No match") } // Use inside a server let _server = Bun.serve({ port: 3000, fetch: async (request, _server) => { switch router->Bun.FileSystemRouter.matchRequest(request) { | Some(route) => Response.make(`Serving: ${route.filePath}`) | None => Response.make("Not Found", ~options={status: 404}) } }, }) // Routes map (filename → path pattern) Console.log(router->Bun.FileSystemRouter.routes) ``` ``` -------------------------------- ### Create and Serialize Cookie with Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates creating a `Cookie` object with various attributes and serializing it into a string suitable for HTTP headers. Also shows parsing a cookie string and checking expiry. ```rescript // Create and serialize a cookie let sessionCookie = Cookie.make({ name: "session", value: "abc123", httpOnly: true, secure: true, sameSite: Strict, maxAge: 86400, path: "/", }) Console.log(sessionCookie->Cookie.serialize) // session=abc123; Max-Age=86400; Path=/; HttpOnly; Secure; SameSite=Strict // Parse a cookie header string let parsed = Cookie.fromString("theme=dark; Path=/; Max-Age=604800") Console.log(parsed->Cookie.toJSON) // Check expiry Console.log(sessionCookie->Cookie.isExpired->Bool.toString) // false ``` -------------------------------- ### Rescript Unit Testing with bun:test Source: https://context7.com/zth/rescript-bun/llms.txt Shows how to use Bun's built-in `bun:test` module for writing unit tests in Rescript, including describe blocks, basic tests, async tests, skipped tests, and various assertion matchers. ```rescript open Test // Lifecycle hooks beforeAll(() => { Console.log("Setting up test suite") }) afterEach(() => { Console.log("Cleaning up") }) describe("Math utils", () => { test("addition", () => { expect(1 + 1)->Expect.toBe(2) }) test("floating point", () => { expect(0.1 +. 0.2)->Expect.toBeCloseTo(0.3, ~numDigits=5.) }) testAsync("async test with timeout", async () => { await Bun.sleep(~ms=Number(10.)) expect(true)->Expect.toBeTruthy }, ~options=Options({timeout: 5000})) Test.skip("not implemented yet", () => { expect(false)->Expect.toBeTrue }) }) describe("Strings", () => { test("contains", () => { expect("rescript-bun")->Expect.toContain("bun") }) test("starts with", () => { expect("hello world")->Expect.toStartWith("hello") }) test("matches regex", () => { expect("bun v2.1.0")->Expect.toMatch(%re("/v\d+\.\d+/")) }) }) describe("Arrays", () => { test("length", () => { expect([1, 2, 3])->Expect.toHaveLength(3.) }) test("deep equal", () => { expect({"a": 1, "b": 2})->Expect.toEqual({"a": 1, "b": 2}) }) }) ``` -------------------------------- ### Bun.Server - Server Instance Source: https://context7.com/zth/rescript-bun/llms.txt Provides runtime control over a running server: stop, reload handlers without restart, inspect pending connections, and publish to WebSocket topics. ```APIDOC ## Bun.Server - Server Instance ### Description Provides runtime control over a running server: stop, reload handlers without restart, inspect pending connections, and publish to WebSocket topics. ### Method Various (GET, POST, PUT, DELETE, etc. depending on the operation) ### Endpoint Operates on an existing `Bun.Server.t` instance. ### Parameters #### Instance Methods - **reload(options)**: Reloads the fetch handler without dropping connections. - **fetch** (function) - Required - The new fetch handler. - **stop(options)**: Stops the server. - **closeActiveConnections** (boolean) - Optional - If true, closes active connections immediately. #### Instance Properties - **pendingRequests** (number) - Number of pending requests. - **pendingWebSockets** (number) - Number of pending WebSockets. - **development** (boolean) - Whether the server is in development mode. ### Request Example ```rescript let server = Bun.serve({ port: 3000, fetch: async (_req, _srv) => Response.make("v1"), }) // Hot-reload the fetch handler without dropping connections server->Bun.Server.reload({ fetch: async (_req, _srv) => Response.make("v2 – hot reloaded!"), }) // Inspect state Console.log(`Pending requests: ${server->Bun.Server.pendingRequests->Int.toString}`) Console.log(`Pending WebSockets: ${server->Bun.Server.pendingWebSockets->Int.toString}`) Console.log(`Dev mode: ${server->Bun.Server.development->Bool.toString}`) // Broadcast to all subscribers on a topic server->Bun.Server.publish(~topic="updates", ~data={\"event\":\"deploy\",\"version\":\"2.1.0\"}) // Graceful shutdown (waits for in-flight requests) server->Bun.Server.stop() // Immediate shutdown server->Bun.Server.stop(~closeActiveConnections=true) ``` ### Response #### Success Response (200) - **status** (string) - Indicates the success of the operation (e.g., "Server reloaded", "Server stopped"). #### Response Example ```json { "status": "Server stopped" } ``` ``` -------------------------------- ### Manage Server Instance with Bun.Server Source: https://context7.com/zth/rescript-bun/llms.txt Control a running server instance using Bun.Server methods. Supports hot-reloading, inspecting state, broadcasting messages, and graceful shutdown. ```rescript let server = Bun.serve({ port: 3000, fetch: async (_req, _srv) => Response.make("v1"), }) // Hot-reload the fetch handler without dropping connections server->Bun.Server.reload({ fetch: async (_req, _srv) => Response.make("v2 – hot reloaded!"), }) // Inspect state Console.log(`Pending requests: ${server->Bun.Server.pendingRequests->Int.toString}`) Console.log(`Pending WebSockets: ${server->Bun.Server.pendingWebSockets->Int.toString}`) Console.log(`Dev mode: ${server->Bun.Server.development->Bool.toString}`) // Broadcast to all subscribers on a topic server->Bun.Server.publish(~topic="updates", ~data={\"event\":\"deploy\",\"version\":\"2.1.0\"}) // Graceful shutdown (waits for in-flight requests) server->Bun.Server.stop() // Immediate shutdown server->Bun.Server.stop(~closeActiveConnections=true) ``` -------------------------------- ### Bun.SemVer for Version Comparison in Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Shows how to use `Bun.SemVer` to check if a version satisfies a given range and to compare two versions to determine their order. ```rescript let {satisfies, order} = Bun.SemVer.semver // Check range satisfaction Console.log(satisfies(~version="2.1.0", ~range=">=2.0.0 <3.0.0")->Bool.toString) // true Console.log(satisfies(~version="1.9.9", ~range="^2.0.0")->Bool.toString) // false // Compare two versions switch order(~v1="2.1.0", ~v2="2.0.3") { | V1IsGreater => Console.log("v1 is newer") | V2IsGreater => Console.log("v2 is newer") | Equal => Console.log("same version") } ``` -------------------------------- ### Bun.Build Source: https://context7.com/zth/rescript-bun/llms.txt Bun's built-in JavaScript bundler with features like tree-shaking, code splitting, source maps, minification, and multi-target output. ```APIDOC ## Bun.Build — Bundler Bun's built-in JavaScript bundler with tree-shaking, code splitting, source maps, minification, and multi-target output. ```rescript let output = await Bun.build({ entrypoints: ["./src/index.ts"], outdir: "./dist", target: Browser, format: Esm, splitting: true, sourcemap: External, minify: Bool(true), define: Dict.fromArray([("__VERSION__", "\"2.1.0\"")]), external_: ["react", "react-dom"], naming: String("[name]-[hash].[ext]"), }) if output.success { output.outputs->Array.forEach(artifact => { Console.log(`Built: ${artifact.path} (${artifact.kind->Obj.magic})`) switch artifact.sourcemap->Null.toOption { | Some(sm) => Console.log(`Sourcemap: ${sm.path}`) | None => () } }) } else { output.logs->Array.forEach(msg => Console.log(msg->Obj.magic)) } ``` ``` -------------------------------- ### Bun.Build — Bundler Source: https://context7.com/zth/rescript-bun/llms.txt Utilize Bun.build for efficient JavaScript bundling with features like tree-shaking, code splitting, source maps, and minification. Configure multiple output targets. ```rescript let output = await Bun.build({ entrypoints: ["./src/index.ts"], outdir: "./dist", target: Browser, format: Esm, splitting: true, sourcemap: External, minify: Bool(true), define: Dict.fromArray([("__VERSION__", "\"2.1.0\"")]), external_: ["react", "react-dom"], naming: String("[name]-[hash].[ext]"), }) if output.success { output.outputs->Array.forEach(artifact => { Console.log(`Built: ${artifact.path} (${artifact.kind->Obj.magic})`) switch artifact.sourcemap->Null.toOption { | Some(sm) => Console.log(`Sourcemap: ${sm.path}`) | None => () } }) } else { output.logs->Array.forEach(msg => Console.log(msg->Obj.magic)) } ``` -------------------------------- ### Node-Compatible Process Module in Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Demonstrates accessing Node.js process information like environment variables, arguments, current working directory, and memory usage. Also shows how to handle process events like exit and uncaught exceptions. ```rescript // Read environment and args let nodeEnv = Process.process.env->Dict.get("NODE_ENV")->Option.getOr("development") let args = Process.process.argv Console.log(`NODE_ENV=${nodeEnv}, args=${args->Array.length->Int.toString}`) // Current working directory let cwd = Process.process->Process.cwd Console.log(`CWD: ${cwd}`) // Defer until after current call stack Process.process->Process.nextTick(() => { Console.log("Runs after current event loop tick") }) // Memory usage let mem = Process.process->Process.memoryUsage Console.log(`Heap used: ${mem.heapUsed->Int.toString} bytes`) // Process events Process.process ->Process.Events.onExit(code => Console.log(`Exiting with code: ${code->Int.toString}`)) ->Process.Events.onUncaughtException((err, _origin) => { Console.log(`Uncaught: ${err->Obj.magic}`) Process.process->Process.exitWithCode(1) }) ->ignore // System info Console.log(Process.process.platform) // "linux", "darwin", etc. Console.log(`PID: ${Process.process->Process.pid->Int.toString}) ``` -------------------------------- ### Bun.Secrets for OS-Backed Secure Storage Source: https://context7.com/zth/rescript-bun/llms.txt Shows how to store, retrieve, and delete credentials using the OS keychain. Requires appropriate permissions for accessing the system's secure storage. ```rescript open Bun.Secrets // Store a credential await set({service: "my-app", name: "api-key", value: "sk-abc123"}) // Retrieve it let result = await get({service: "my-app", name: "api-key"}) switch result->Null.toOption { | Some(value) => Console.log(`API Key: ${value}`) | None => Console.log("Credential not found") } // Delete it let deleted = await delete({service: "my-app", name: "api-key"}) Console.log(`Deleted: ${deleted->Bool.toString}`) ``` -------------------------------- ### Finding Executables with Bun.which Source: https://context7.com/zth/rescript-bun/llms.txt Locate an executable file in the system's PATH. Returns Some(path) if found, None otherwise. ```rescript switch Bun.which("node") { | Some(path) => Console.log(`node is at: ${path}`) | None => Console.log("node not found") } ``` -------------------------------- ### Hash and verify password with rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Demonstrates using Bun.Password.hash to securely hash a password and Bun.Password.verify to check if a password matches a hash. Supports BCrypt algorithm with configurable cost. ```rescript let password = "super-secure-pa$$word" let bcryptHash = await Bun.Password.hash( password, ~algorithm=BCryptAlgorithm({ cost: 4, // number between 4-31 }), ) let isMatch = await Bun.Password.verify(password, ~hash) ``` -------------------------------- ### Open RescriptBun Globals in rescript.json Source: https://github.com/zth/rescript-bun/blob/main/README.md Add '-open RescriptBun' and '-open RescriptBun.Globals' to your rescript.json's bsc-flags to make Bun globals available without explicit module access. ```json { "bsc-flags": ["-open RescriptBun", "-open RescriptBun.Globals"] } ``` -------------------------------- ### Configure rescript.json for rescript-bun Source: https://github.com/zth/rescript-bun/blob/main/README.md Include 'rescript-bun' in your project's rescript.json dependencies. This makes the library available for use. ```json { "dependencies": ["rescript-bun"] } ``` -------------------------------- ### Password Hashing with Bun.Password Source: https://context7.com/zth/rescript-bun/llms.txt Shows asynchronous password hashing using Argon2 or bcrypt, with options for algorithm and cost. Verification is also demonstrated, inferring the algorithm from the hash. ```rescript let password = "super-secure-pa$$word" // Hash with argon2id (default) let hash = await Bun.Password.hash(password) Console.log(hash) // $argon2id$v=19$... // Hash with argon2d and custom cost let argonHash = await Bun.Password.hash( password, ~algorithm=Argon2d({memoryCost: 65536., timeCost: 3.}), ) // Hash with bcrypt let bcryptHash = await Bun.Password.hash( password, ~algorithm=BCryptAlgorithm({cost: 12}), ) // Verify (algorithm inferred from hash prefix) let isValid = await Bun.Password.verify(password, ~hash) Console.log(`Valid: ${isValid->Bool.toString}`) // Valid: true let isBcryptValid = await Bun.Password.verify(password, ~hash=bcryptHash, ~algorithm=Bcrypt) Console.log(`BCrypt valid: ${isBcryptValid->Bool.toString}) ``` -------------------------------- ### Cryptographic Hashing with Bun.CryptoHasher Source: https://context7.com/zth/rescript-bun/llms.txt Illustrates one-shot and streaming cryptographic hashing using various algorithms like SHA-256, SHA-512, and MD5. Also shows a convenience shortcut for SHA and a non-cryptographic hash. ```rescript // One-shot hash to hex string let digest = Bun.CryptoHasher.hashToString( ~algorithm=Sha256, ~input=Bun.CryptoHasher.StringOrBuffer.fromString("hello world"), ~encoding=Hex, ) Console.log(digest) // b94d27b9934d3e08... // Streaming incremental hash let hasher = Bun.CryptoHasher.make(Sha256) let _ = hasher->Bun.CryptoHasher.update( ~input=Bun.CryptoHasher.StringOrBuffer.fromString("chunk 1"), ) let _ = hasher->Bun.CryptoHasher.update( ~input=Bun.CryptoHasher.StringOrBuffer.fromString(" chunk 2"), ) let result = hasher->Bun.CryptoHasher.digest(Hex) Console.log(result) // SHA-512/256 convenience shortcut let shaDigest = Bun.shaToString( ~input=Bun.StringOrBuffer.fromString("data"), ~encoding=Base64, ) Console.log(shaDigest) // Non-cryptographic fast hash (Wyhash) let h = Bun.Hash.hashString("some key", ~seed=42.) Console.log(h->Float.toString) ``` -------------------------------- ### Path and File URL Conversion Source: https://context7.com/zth/rescript-bun/llms.txt Convert between file system paths and file URLs using Bun.pathToFileURL and Bun.fileURLToPath. ```rescript let url = Bun.pathToFileURL("/home/user/app.js") let path = Bun.fileURLToPath("file:///home/user/app.js") Console.log(path) ``` -------------------------------- ### RedisClient Source: https://context7.com/zth/rescript-bun/llms.txt Bun's native Redis (and Valkey-compatible) client with auto-pipelining, TLS, auto-reconnect, and a full command set. ```APIDOC ## Redis.RedisClient — Redis / Valkey Client Bun's native Redis (and Valkey-compatible) client with auto-pipelining, TLS, auto-reconnect, and a full command set. ```rescript open Redis // Connect to default (reads VALKEY_URL / REDIS_URL, or localhost:6379) let client = RedisClient.make() // Or with explicit URL and options let custom = RedisClient.make( ~url="redis://localhost:6379", ~options={ autoReconnect: true, maxRetries: 3, enableAutoPipelining: true, tls: false, }, ) // Basic get/set await client->RedisClient.set("greeting", RedisClient.String("Hello Bun")) let value = await client->RedisClient.get("greeting") Console.log(value->Null.getOr("(nil)")) // Hello Bun // Set with expiry (EX = seconds) await client->RedisClient.setWithExpiry("session:abc", RedisClient.String("data"), #EX, 3600) // Atomic counter let count = await client->RedisClient.incr("page_views") Console.log(`Views: ${count->Int.toString}`) // TTL and expiry let ttl = await client->RedisClient.ttl("session:abc") Console.log(`TTL: ${ttl->Int.toString}s`) // Hash operations await client->RedisClient.hmset("user:1", ["name", "Alice", "role", "admin"]) let allFields = await client->RedisClient.hgetall("user:1") Console.log(allFields) // Set membership let _ = await client->RedisClient.sadd("tags", "rescript") let members = await client->RedisClient.smembers("tags") Console.log(members) // Ping let pong = await client->RedisClient.ping Console.log(pong == #PONG ? "alive" : "dead") client->RedisClient.close ``` ``` -------------------------------- ### Bun.Glob Source: https://context7.com/zth/rescript-bun/llms.txt A file-system glob matching utility that supports pattern-based file scanning with both asynchronous and synchronous iterators. ```APIDOC ## Bun.Glob — File-System Glob Matching Pattern-based file scanner with async and sync iterators. Supports `*`, `**`, `?`, and `{a,b}` globbing. ```rescript let glob = Bun.Glob.make("src/**/*.{res,js}") // Async scan let asyncIter = glob->Bun.Glob.scan(~options={ cwd: ".", dot: false, absolute: true, onlyFiles: true, }) // Consume with for-await pattern via AsyncIterator bindings // Sync scan let iter = glob->Bun.Glob.scanSync(~options={cwd: "./src", dot: false}) let files = iter->Iterator.toArray Console.log(`Found ${files->Array.length->Int.toString} files`) files->Array.forEach(f => Console.log(f)) // Simple string match (no FS access) let pattern = Bun.Glob.make("*.{ts,tsx}") Console.log(pattern->Bun.Glob.match("app.tsx")) // true Console.log(pattern->Bun.Glob.match("app.css")) // false ``` ``` -------------------------------- ### Bun.serveWithWebSocket - WebSocket Server Source: https://context7.com/zth/rescript-bun/llms.txt Upgrades HTTP connections to WebSocket using `server.upgrade(req)`. The `websocket` field accepts `open`, `message`, `close`, and `drain` handlers typed over a generic `'websocketDataType`. ```APIDOC ## Bun.serveWithWebSocket - WebSocket Server ### Description Upgrades HTTP connections to WebSocket using `server.upgrade(req)`. The `websocket` field accepts `open`, `message`, `close`, and `drain` handlers typed over a generic `'websocketDataType`. ### Method POST ### Endpoint /serveWithWebSocket ### Parameters #### Request Body - **port** (number) - Required - The port to listen on. - **fetch** (function) - Required - The fetch handler. - **websocket** (object) - Required - WebSocket event handlers. - **open_** (function) - Optional - Called when a client connects. - **message** (function) - Optional - Called when a message is received. - **close** (function) - Optional - Called when a client disconnects. - **drain** (function) - Optional - Called when the WebSocket is ready to send more data. ### Request Example ```rescript let _wsServer = Bun.serveWithWebSocket({ port: 8080, fetch: async (request, server) => { let url = request->Request.url->URL.make if url->URL.pathname == "/chat" { let upgraded = server->Bun.Server.upgrade(request) if upgraded { // Response is ignored after upgrade Response.make("", ~options={status: 101}) } else { Response.make("WebSocket upgrade failed", ~options={status: 400}) } } else { Response.make("Use /chat for WebSocket", ~options={status: 200}) } }, websocket: { open_: ws => Console.log(`Client connected: ${ws->WebSocket.ServerWebSocket.data}`), message: (ws, msg) => { Console.log(`Received: ${msg}`) ws->WebSocket.ServerWebSocket.send(~message=`Echo: ${msg}`) }, close: (_ws, _code, _reason) => Console.log("Client disconnected"), }, }) ``` ### Response #### Success Response (200) - **server** (Bun.Server.t) - The created WebSocket server instance. #### Response Example ```json { "message": "WebSocket server started", "port": 8080 } ``` ``` -------------------------------- ### Compression/Decompression with Bun.gzipSync and Bun.gunzipSync Source: https://context7.com/zth/rescript-bun/llms.txt Perform synchronous gzip compression and gunzip decompression on Uint8Array data. ```rescript let data = Uint8Array.make([72, 101, 108, 108, 111]) let compressed = Bun.gzipSync(~data) let decompressed = Bun.gunzipSync(compressed) Console.log(decompressed) ``` -------------------------------- ### Bun.Transpiler Source: https://context7.com/zth/rescript-bun/llms.txt Provides a fast, in-process JavaScript/TypeScript/JSX transpiler that does not resolve imports. It's useful for build tools, REPL-like features, and static analysis. ```APIDOC ## Bun.Transpiler — JS/TS/JSX Transpiler Fast, in-process TypeScript/JSX transpiler that does not resolve imports. Useful for build tools, REPL-like features, and static analysis. ```rescript let transpiler = Bun.Transpiler.make(~options={ loader: Tsx, target: Browser, define: Dict.fromArray([("process.env.NODE_ENV", "\"production\"")]), deadCodeElimination: true, minifyWhitespace: true, }) // Synchronous transform let jsCode = transpiler->Bun.Transpiler.transformSyncWithLoader( `const App = () =>
Hello
; export default App;`, ~loader=Tsx, ) Console.log(jsCode) // Async transform let asyncOutput = await transpiler->Bun.Transpiler.transform( `interface Foo { x: number } const f: Foo = { x: 1 }`, ~loader=Ts, ) Console.log(asyncOutput) // Scan imports and exports without full transpilation let {imports, exports} = transpiler->Bun.Transpiler.scan( Bun.StringOrBuffer.fromString(` import {foo} from "react"; import type {Bar} from "./types"; export const hello = "world"; `), ) Console.log(`Exports: ${exports->Array.joinWith(", ")}`) imports->Array.forEach(i => Console.log(`Import: ${i.path}`)) ``` ``` -------------------------------- ### Manage Multiple Cookies with CookieMap in Rescript Source: https://context7.com/zth/rescript-bun/llms.txt Illustrates using `CookieMap` to manage multiple cookies, including parsing from a string, retrieving values, updating, deleting, and serializing into `Set-Cookie` headers. Also shows integration with `Bun.serve` for request cookie handling. ```rescript // CookieMap: manage multiple cookies let map = CookieMap.fromString("theme=dark; session=abc123; lang=en") let theme = map->CookieMap.get("theme")->Null.getOr("light") Console.log(theme) // dark map->CookieMap.set("session", "newtoken123") map->CookieMap.delete({name: "lang"}) // Serialize to Set-Cookie headers array let headers = map->CookieMap.toSetCookieHeaders headers->Array.forEach(h => Console.log(h)) // Use in a server let _server = Bun.serve({ port: 3000, fetch: async (request, _server) => { let cookies = request->Bun.BunRequest.cookies let user = cookies->CookieMap.get("user")->Null.getOr("guest") Response.make(`Hello, ${user}!`) }, }) ```