Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ erl_crash.dump
/Manifest.toml

# ReScript
/lib/bs/
/lib/
/.bsb.lock
.merlin

# npm (fallback only - prefer Deno)
package-lock.json

# Python (SaltStack only)
__pycache__/
Expand Down
12 changes: 12 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json",
"tasks": {
"build": "deno run -A npm:rescript@11.1.4 build",
"clean": "deno run -A npm:rescript@11.1.4 clean",
"test": "deno test --allow-read tests/",
"check": "deno task build && deno task test"
},
"imports": {
"rescript": "npm:rescript@11.1.4"
}
}
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "bridge-web-rescript",
"version": "0.1.0",
"type": "module",
"scripts": {
"build": "rescript build",
"clean": "rescript clean",
"test": "node --test tests/bridge_test.mjs",
"check": "npm run build && npm run test"
},
"devDependencies": {
"rescript": "^11.1.4"
}
}
20 changes: 20 additions & 0 deletions rescript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "bridge-web-rescript",
"sources": [
{
"dir": "src",
"subdirs": true
}
],
"package-specs": [
{
"module": "esmodule",
"in-source": false
}
],
"suffix": ".mjs",
"bs-dependencies": [],
"warnings": {
"number": "+A-48-42"
}
}
63 changes: 63 additions & 0 deletions src/Bridge.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 Hyperpolymath

/**
* Bridge - A minimal input → output transformation module.
* Demonstrates the core pattern: receive input, transform, emit output.
*/

/** Result type for bridge operations */
type bridgeResult<'a> = Ok('a) | Error(string)

/** Bridge configuration */
type config = {
name: string,
version: string,
}

/** Default configuration */
let defaultConfig: config = {
name: "bridge",
version: "0.1.0",
}

/** Transform input string to output with bridge metadata */
let transform = (input: string): string => {
`[bridge] ${input}`
}

/** Transform with result wrapper for error handling */
let transformSafe = (input: string): bridgeResult<string> => {
if input == "" {
Error("Input cannot be empty")
} else {
Ok(transform(input))
}
}

/** Compose two transformations */
let compose = (f: string => string, g: string => string): (string => string) => {
(input: string) => g(f(input))
}

/** Identity transform - returns input unchanged */
let identity = (input: string): string => input

/** Uppercase transform - binds to JavaScript String.toUpperCase */
@send external toUpperCase: string => string = "toUpperCase"
let uppercase = (input: string): string => input->toUpperCase

/** Prefix transform factory */
let prefix = (pre: string): (string => string) => {
(input: string) => `${pre}${input}`
}

/** Suffix transform factory */
let suffix = (suf: string): (string => string) => {
(input: string) => `${input}${suf}`
}

/** Get bridge info */
let info = (config: config): string => {
`${config.name} v${config.version}`
}
96 changes: 96 additions & 0 deletions tests/bridge_test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 Hyperpolymath

/**
* Bridge module tests - verifies input → output transformations
*/

import { test, describe } from "node:test";
import { strictEqual, deepStrictEqual } from "node:assert";
import {
defaultConfig,
transform,
transformSafe,
compose,
identity,
uppercase,
prefix,
suffix,
info,
} from "../lib/es6/src/Bridge.mjs";

describe("Bridge", () => {
describe("transform", () => {
test("adds bridge prefix to input", () => {
strictEqual(transform("hello"), "[bridge] hello");
});

test("handles empty string", () => {
strictEqual(transform(""), "[bridge] ");
});
});

describe("transformSafe", () => {
test("returns Ok for valid input", () => {
const result = transformSafe("hello");
deepStrictEqual(result, { TAG: "Ok", _0: "[bridge] hello" });
});

test("returns Error for empty input", () => {
const result = transformSafe("");
deepStrictEqual(result, { TAG: "Error", _0: "Input cannot be empty" });
});
});

describe("compose", () => {
test("composes two functions left-to-right", () => {
const prefixHello = prefix("hello-");
const suffixWorld = suffix("-world");
const composed = compose(prefixHello, suffixWorld);
strictEqual(composed("test"), "hello-test-world");
});
});

describe("identity", () => {
test("returns input unchanged", () => {
strictEqual(identity("test"), "test");
});
});

describe("uppercase", () => {
test("converts string to uppercase", () => {
strictEqual(uppercase("hello"), "HELLO");
});
});

describe("prefix", () => {
test("creates a function that adds prefix", () => {
const addPrefix = prefix("pre-");
strictEqual(addPrefix("test"), "pre-test");
});
});

describe("suffix", () => {
test("creates a function that adds suffix", () => {
const addSuffix = suffix("-suf");
strictEqual(addSuffix("test"), "test-suf");
});
});

describe("info", () => {
test("returns formatted config info", () => {
strictEqual(info(defaultConfig), "bridge v0.1.0");
});

test("works with custom config", () => {
const customConfig = { name: "custom", version: "1.0.0" };
strictEqual(info(customConfig), "custom v1.0.0");
});
});

describe("defaultConfig", () => {
test("has correct default values", () => {
deepStrictEqual(defaultConfig, { name: "bridge", version: "0.1.0" });
});
});
});
Loading