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
84 changes: 5 additions & 79 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,79 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# RSR-compliant .gitignore

# OS & Editor
.DS_Store
Thumbs.db
*.swp
*.swo
*~
.idea/
.vscode/

# Build
/target/
/_build/
/build/
/dist/
/out/

# Dependencies
/node_modules/
/vendor/
/deps/
/.elixir_ls/

# Rust
# Cargo.lock # Keep for binaries

# Elixir
/cover/
/doc/
*.ez
erl_crash.dump

# Julia
*.jl.cov
*.jl.mem
/Manifest.toml

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

# Python (SaltStack only)
__pycache__/
*.py[cod]
.venv/

# Ada/SPARK
*.ali
/obj/
/bin/

# Haskell
/.stack-work/
/dist-newstyle/

# Chapel
*.chpl.tmp.*

# Secrets
.env
.env.*
*.pem
*.key
secrets/

# Test/Coverage
/coverage/
htmlcov/

# Logs
*.log
/logs/

# Temp
/tmp/
*.tmp
*.bak
lib/
node_modules/
.bsb.lock
*.res.js
.merlin
17 changes: 17 additions & 0 deletions LICENCE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007

Copyright (C) 2025 Hyperpolymath

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
184 changes: 184 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1 +1,185 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2025 Hyperpolymath

= rescript-poly-core
:toc:
:toc-placement: preamble
:icons: font

**Shared foundation library for the Hyperpolymath ReScript ecosystem.**

Part of the https://github.com/hyperpolymath/rescript-full-stack[ReScript Full Stack] ecosystem.

== Features

* **Core utilities** - Result extensions, async helpers, structured logging, config loading
* **MCP infrastructure** - Protocol types, server builder, tool registration
* **Reusable patterns** - Common abstractions used across poly-* projects
* **Zero external dependencies** - Only requires @rescript/core

== Installation

[source,bash]
----
deno add jsr:@hyperpolymath/rescript-poly-core
----

== Modules

=== Core.Result

Extended Result type utilities for error handling.

[source,rescript]
----
open PolyCore

// Chain results
let result = Ok(42)
->Result.map(x => x * 2)
->Result.flatMap(x => if x > 50 { Ok(x) } else { Error("Too small") })

// Collect all or fail
let results = Result.all([Ok(1), Ok(2), Ok(3)]) // Ok([1, 2, 3])

// Try/catch to Result
let parsed = Result.tryCatch(
() => JSON.parseExn(input),
_ => "Invalid JSON"
)
----

=== Core.Async

Promise utilities for async operations.

[source,rescript]
----
open PolyCore

// Sleep
await Async.sleep(1000)

// Timeout
let result = await Async.timeout(5000, fetchData())

// Retry with exponential backoff
let data = await Async.retry(
~config={maxAttempts: 3, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2.0},
() => fetchUnreliableApi()
)

// Parallel with concurrency limit
let results = await Async.parallelLimit(~concurrency=5, tasks)

// Debounce/throttle
let debouncedSave = Async.debounce(500, data => save(data))
----

=== Core.Logger

Structured JSON logging.

[source,rescript]
----
open PolyCore

let logger = Logger.make(~config={
minLevel: Logger.Debug,
json: true,
timestamps: true,
context: Dict.fromArray([("service", "my-app")]),
})

logger->Logger.info("Server started", ~extra=Dict.fromArray([
("port", JSON.Encode.int(8080)),
]))

// Child logger with additional context
let reqLogger = logger->Logger.child(Dict.fromArray([
("requestId", "abc123"),
]))
----

=== Core.Config

Configuration loading from environment.

[source,rescript]
----
open PolyCore

let config = Config.fromEnv(~prefix="APP_")

let port = config->Config.getIntOr("PORT", 3000)
let debug = config->Config.getBoolOr("DEBUG", false)
let dbUrl = config->Config.getString("DATABASE_URL") // throws if missing
----

=== MCP.Protocol

MCP protocol types and builders.

[source,rescript]
----
open PolyCore.MCP

// Build tool results
let result = Protocol.success("Operation completed")
let jsonResult = Protocol.successJson({"count": 42})
let errorResult = Protocol.error("Something went wrong")

// Build schemas
let schema = Protocol.objectSchema(
~properties=Dict.fromArray([
("name", Protocol.stringProp(~description="The user's name")),
("age", Protocol.numberProp(~description="Age in years")),
]),
~required=["name"],
)

// Parse arguments
let name = Protocol.requireArg(args, "name")
let limit = Protocol.getIntArg(args, "limit")->Option.getOr(10)
----

=== MCP.Server

MCP server builder.

[source,rescript]
----
open PolyCore.MCP

let server = Server.make(~name="my-mcp", ~version="1.0.0")
->Server.registerTool(
{
name: "greet",
description: "Greet a user",
inputSchema: Protocol.objectSchema(
~properties=Dict.fromArray([
("name", Protocol.stringProp(~description="Name to greet")),
]),
~required=["name"],
),
},
async args => {
switch Protocol.requireArg(args, "name") {
| Ok(name) => Protocol.success(`Hello, ${name}!`)
| Error(e) => Protocol.error(e)
}
},
)

// Handle incoming requests
let response = await server->Server.handleRequest("tools/call", Some(params))
----

== Related

* https://github.com/hyperpolymath/rescript-full-stack[rescript-full-stack] - Full ecosystem overview
* https://github.com/hyperpolymath/poly-mcps[poly-mcps] - MCP servers using this library

== Licence

AGPL-3.0-or-later
14 changes: 14 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@hyperpolymath/rescript-poly-core",
"version": "0.1.0",
"exports": "./src/PolyCore.res.js",
"tasks": {
"build": "rescript build",
"clean": "rescript clean",
"dev": "rescript build -w",
"test": "deno test --allow-all tests/"
},
"compilerOptions": {
"lib": ["deno.ns", "deno.unstable"]
}
}
22 changes: 22 additions & 0 deletions rescript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@hyperpolymath/rescript-poly-core",
"sources": [
{
"dir": "src",
"subdirs": true
}
],
"package-specs": [
{
"module": "esmodule",
"in-source": true
}
],
"suffix": ".res.js",
"bs-dependencies": [
"@rescript/core"
],
"bsc-flags": [
"-open RescriptCore"
]
}
Loading
Loading