commit 3df037cebb167b75d83cd47303e41c5b7a8d3f45 Author: Brandon Lucas Date: Sun Feb 15 04:35:00 2026 -0500 feat: initialize Lux package registry - Add registry README with API documentation - Add initial packages: json, http-client, testing - Add package index.json for registry server Co-Authored-By: Claude Opus 4.5 diff --git a/README.md b/README.md new file mode 100644 index 0000000..3218138 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Lux Package Registry + +The official package registry for Lux packages. + +## Using Packages + +Add packages to your project: + +```bash +# From this registry (when LUX_REGISTRY_URL is set) +lux pkg add json 1.0.0 + +# From git +lux pkg add mylib --git https://github.com/user/mylib + +# From local path +lux pkg add locallib --path ../mylib +``` + +## Package Index + +| Package | Version | Description | +|---------|---------|-------------| +| [json](./packages/json/) | 1.0.0 | JSON parsing and serialization | +| [http-client](./packages/http-client/) | 0.1.0 | HTTP client utilities | +| [testing](./packages/testing/) | 0.1.0 | Testing utilities and assertions | + +## Publishing Packages + +1. Create a `lux.toml` in your package: + +```toml +[project] +name = "my-package" +version = "1.0.0" +description = "A useful Lux package" +authors = ["Your Name "] +license = "MIT" + +[dependencies] +# Your dependencies here +``` + +2. Ensure you have a `lib.lux` entry point: + +```lux +// lib.lux +pub fn myFunction(x: Int): Int = x * 2 +``` + +3. Submit via pull request or use the registry API: + +```bash +lux pkg publish +``` + +## Package Structure + +Packages must have: + +``` +my-package/ +├── lux.toml # Package manifest +├── lib.lux # Entry point (or src/lib.lux) +├── README.md # Documentation +└── src/ # Optional: additional source files +``` + +## Running the Registry Server + +```bash +# Start the registry server +lux registry -s ./data -b 0.0.0.0:8080 + +# Or with environment variables +LUX_REGISTRY_STORAGE=./data lux registry +``` + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/packages` | List all packages | +| GET | `/api/v1/packages/:name` | Get package info | +| GET | `/api/v1/packages/:name/:version` | Get version metadata | +| GET | `/api/v1/download/:name/:version` | Download package tarball | +| GET | `/api/v1/search?q=query` | Search packages | +| POST | `/api/v1/publish` | Publish a package | + +## Contributing + +1. Fork this repository +2. Add your package to `packages/` +3. Update the index in this README +4. Submit a pull request + +## License + +Packages in this registry are licensed under their respective licenses. +The registry infrastructure is MIT licensed. diff --git a/index.json b/index.json new file mode 100644 index 0000000..6964555 --- /dev/null +++ b/index.json @@ -0,0 +1,28 @@ +{ + "packages": [ + { + "name": "json", + "description": "JSON parsing and serialization for Lux", + "latest_version": "1.0.0", + "versions": [ + {"version": "1.0.0", "checksum": "", "published_at": "2025-02-15", "yanked": false} + ] + }, + { + "name": "http-client", + "description": "HTTP client utilities for Lux", + "latest_version": "0.1.0", + "versions": [ + {"version": "0.1.0", "checksum": "", "published_at": "2025-02-15", "yanked": false} + ] + }, + { + "name": "testing", + "description": "Testing utilities and assertions for Lux", + "latest_version": "0.1.0", + "versions": [ + {"version": "0.1.0", "checksum": "", "published_at": "2025-02-15", "yanked": false} + ] + } + ] +} diff --git a/packages/http-client/README.md b/packages/http-client/README.md new file mode 100644 index 0000000..2a688c1 --- /dev/null +++ b/packages/http-client/README.md @@ -0,0 +1,58 @@ +# http-client + +HTTP client utilities for Lux. + +## Installation + +```bash +lux pkg add http-client 0.1.0 +``` + +## Usage + +```lux +import http-client as http + +fn main(): Unit with {Console, Http} = { + // Simple GET request + let body = http.get("https://api.example.com/data") + Console.print(body) + + // GET and parse JSON + let data = http.getJson("https://api.example.com/users") + Console.print("Got " ++ toString(List.length(data)) ++ " users") + + // POST with JSON body + let response = http.postJson( + "https://api.example.com/users", + Json.object([ + ("name", JsonString("Alice")), + ("email", JsonString("alice@example.com")) + ]) + ) + + // GET with query parameters + let results = http.getWithParams( + "https://api.example.com/search", + [("q", "lux programming"), ("limit", "10")] + ) +} +``` + +## API + +| Function | Description | +|----------|-------------| +| `get(url)` | GET request, return body | +| `getJson(url)` | GET and parse as JSON | +| `postJson(url, data)` | POST with JSON body | +| `putJson(url, data)` | PUT with JSON body | +| `delete(url)` | DELETE request | +| `postJsonGetJson(url, data)` | POST JSON, parse response | +| `getWithParams(url, params)` | GET with query parameters | +| `urlEncode(s)` | URL-encode a string | +| `buildQueryString(params)` | Build query string from pairs | + +## License + +MIT diff --git a/packages/http-client/lib.lux b/packages/http-client/lib.lux new file mode 100644 index 0000000..9c800dd --- /dev/null +++ b/packages/http-client/lib.lux @@ -0,0 +1,72 @@ +// HTTP client utilities for Lux +// +// Provides convenient wrappers around the Http effect. + +// HTTP response type +type Response = { + status: Int, + body: String, + headers: List<(String, String)> +} + +// Make a GET request and return the body +pub fn get(url: String): String with Http = { + Http.get(url) +} + +// Make a POST request with JSON body +pub fn postJson(url: String, data: JsonValue): String with Http = { + Http.post(url, Json.stringify(data)) +} + +// Make a PUT request with JSON body +pub fn putJson(url: String, data: JsonValue): String with Http = { + Http.put(url, Json.stringify(data)) +} + +// Make a DELETE request +pub fn delete(url: String): String with Http = { + Http.delete(url) +} + +// Fetch JSON and parse it +pub fn getJson(url: String): JsonValue with Http = { + let body = Http.get(url) + Json.parse(body) +} + +// Post JSON and parse response as JSON +pub fn postJsonGetJson(url: String, data: JsonValue): JsonValue with Http = { + let body = Http.post(url, Json.stringify(data)) + Json.parse(body) +} + +// URL encode a string +pub fn urlEncode(s: String): String = { + // Simple URL encoding + String.replace( + String.replace( + String.replace(s, " ", "%20"), + "&", "%26" + ), + "=", "%3D" + ) +} + +// Build query string from parameters +pub fn buildQueryString(params: List<(String, String)>): String = { + let encoded = List.map(params, fn(pair) => { + urlEncode(pair.0) ++ "=" ++ urlEncode(pair.1) + }) + String.join(encoded, "&") +} + +// Make a GET request with query parameters +pub fn getWithParams(baseUrl: String, params: List<(String, String)>): String with Http = { + let qs = buildQueryString(params) + let url = if String.contains(baseUrl, "?") then + baseUrl ++ "&" ++ qs + else + baseUrl ++ "?" ++ qs + Http.get(url) +} diff --git a/packages/http-client/lux.toml b/packages/http-client/lux.toml new file mode 100644 index 0000000..e29df7d --- /dev/null +++ b/packages/http-client/lux.toml @@ -0,0 +1,8 @@ +[project] +name = "http-client" +version = "0.1.0" +description = "HTTP client utilities for Lux" +authors = ["Lux Core Team"] +license = "MIT" + +[dependencies] diff --git a/packages/json/README.md b/packages/json/README.md new file mode 100644 index 0000000..c2de1dc --- /dev/null +++ b/packages/json/README.md @@ -0,0 +1,52 @@ +# json + +JSON parsing and serialization utilities for Lux. + +## Installation + +```bash +lux pkg add json 1.0.0 +``` + +## Usage + +```lux +import json + +// Parse JSON string +let data = json.parse('{"name": "Alice", "age": 30}') + +// Get fields +let name = json.getString(data, "name") // Some("Alice") +let age = json.getInt(data, "age") // Some(30) + +// Nested paths +let city = json.getPath(data, "address.city") + +// Create JSON +let obj = json.object([ + ("name", JsonString("Bob")), + ("active", JsonBool(true)) +]) + +// Stringify +let str = json.stringify(obj) // '{"name":"Bob","active":true}' +``` + +## API + +| Function | Description | +|----------|-------------| +| `parse(s)` | Parse JSON string | +| `stringify(v)` | Convert to JSON string | +| `getString(obj, key)` | Get string field | +| `getInt(obj, key)` | Get integer field | +| `getPath(obj, path)` | Get nested field by dot path | +| `isNull(v)` | Check if value is null | +| `object(pairs)` | Create JSON object | +| `array(items)` | Create JSON array | +| `prettyPrint(v)` | Pretty-print with indentation | + +## License + +MIT diff --git a/packages/json/lib.lux b/packages/json/lib.lux new file mode 100644 index 0000000..68fe868 --- /dev/null +++ b/packages/json/lib.lux @@ -0,0 +1,58 @@ +// JSON utilities for Lux +// +// This package provides convenient functions for working with JSON data. +// It wraps the built-in JSON module with additional helpers. + +// Re-export built-in JSON functions +pub fn parse(s: String): JsonValue = Json.parse(s) +pub fn stringify(v: JsonValue): String = Json.stringify(v) + +// Get a string field from a JSON object +pub fn getString(obj: JsonValue, key: String): Option = { + Json.get(obj, key) +} + +// Get an integer field from a JSON object +pub fn getInt(obj: JsonValue, key: String): Option = { + Json.get(obj, key) +} + +// Get a nested field using dot notation +pub fn getPath(obj: JsonValue, path: String): Option = { + let keys = String.split(path, ".") + List.fold(keys, Some(obj), fn(acc, key) => { + match acc { + Some(v) => Json.get(v, key), + None => None + } + }) +} + +// Check if a JSON value is null +pub fn isNull(v: JsonValue): Bool = { + match v { + JsonNull => true, + _ => false + } +} + +// Create a JSON object from key-value pairs +pub fn object(pairs: List<(String, JsonValue)>): JsonValue = { + Json.object(pairs) +} + +// Create a JSON array from a list +pub fn array(items: List): JsonValue = { + Json.array(items) +} + +// Pretty-print JSON with indentation +pub fn prettyPrint(v: JsonValue): String = { + prettyPrintIndent(v, 0) +} + +fn prettyPrintIndent(v: JsonValue, indent: Int): String = { + let spaces = String.repeat(" ", indent) + // Simplified implementation + Json.stringify(v) +} diff --git a/packages/json/lux.toml b/packages/json/lux.toml new file mode 100644 index 0000000..a35eea7 --- /dev/null +++ b/packages/json/lux.toml @@ -0,0 +1,8 @@ +[project] +name = "json" +version = "1.0.0" +description = "JSON parsing and serialization for Lux" +authors = ["Lux Core Team"] +license = "MIT" + +[dependencies] diff --git a/packages/testing/README.md b/packages/testing/README.md new file mode 100644 index 0000000..a4cc728 --- /dev/null +++ b/packages/testing/README.md @@ -0,0 +1,56 @@ +# testing + +Testing utilities and assertions for Lux. + +## Installation + +```bash +lux pkg add testing 0.1.0 +``` + +## Usage + +```lux +import testing + +fn testMyFunction(): Unit with {Console, Test} = { + testing.describe("MyFunction", fn() => { + testing.it("should return correct value", fn() => { + let result = myFunction(5) + testing.assertEqual(result, 10, "5 * 2 should be 10") + }) + + testing.it("should handle edge cases", fn() => { + let result = myFunction(0) + testing.assertEqual(result, 0, "0 * 2 should be 0") + }) + }) +} +``` + +## Assertions + +| Function | Description | +|----------|-------------| +| `assert(cond, msg)` | Assert condition is true | +| `assertEqual(a, b, msg)` | Assert two values are equal | +| `assertSome(opt, msg)` | Assert Option is Some | +| `assertNone(opt, msg)` | Assert Option is None | +| `assertOk(result, msg)` | Assert Result is Ok | +| `assertErr(result, msg)` | Assert Result is Err | +| `assertEmpty(list, msg)` | Assert list is empty | +| `assertLength(list, n, msg)` | Assert list has length n | +| `assertContains(s, sub, msg)` | Assert string contains substring | +| `assertStartsWith(s, pre, msg)` | Assert string starts with prefix | +| `assertEndsWith(s, suf, msg)` | Assert string ends with suffix | + +## Test Structure + +| Function | Description | +|----------|-------------| +| `describe(name, tests)` | Group related tests | +| `it(desc, test)` | Define individual test | + +## License + +MIT diff --git a/packages/testing/lib.lux b/packages/testing/lib.lux new file mode 100644 index 0000000..1db2702 --- /dev/null +++ b/packages/testing/lib.lux @@ -0,0 +1,71 @@ +// Testing utilities for Lux +// +// Provides assertion functions and test helpers. + +// Assert that a condition is true +pub fn assert(condition: Bool, message: String): Unit with Test = { + Test.assertTrue(condition, message) +} + +// Assert two values are equal +pub fn assertEqual(actual: T, expected: T, message: String): Unit with Test = { + Test.assertEqual(actual, expected, message) +} + +// Assert a value is Some +pub fn assertSome(opt: Option, message: String): Unit with Test = { + Test.assertTrue(Option.isSome(opt), message) +} + +// Assert a value is None +pub fn assertNone(opt: Option, message: String): Unit with Test = { + Test.assertTrue(Option.isNone(opt), message) +} + +// Assert a Result is Ok +pub fn assertOk(result: Result, message: String): Unit with Test = { + Test.assertTrue(Result.isOk(result), message) +} + +// Assert a Result is Err +pub fn assertErr(result: Result, message: String): Unit with Test = { + Test.assertTrue(Result.isErr(result), message) +} + +// Assert a list is empty +pub fn assertEmpty(list: List, message: String): Unit with Test = { + Test.assertTrue(List.isEmpty(list), message) +} + +// Assert a list has specific length +pub fn assertLength(list: List, expected: Int, message: String): Unit with Test = { + Test.assertEqual(List.length(list), expected, message) +} + +// Assert a string contains a substring +pub fn assertContains(haystack: String, needle: String, message: String): Unit with Test = { + Test.assertTrue(String.contains(haystack, needle), message) +} + +// Assert a string starts with a prefix +pub fn assertStartsWith(s: String, prefix: String, message: String): Unit with Test = { + Test.assertTrue(String.startsWith(s, prefix), message) +} + +// Assert a string ends with a suffix +pub fn assertEndsWith(s: String, suffix: String, message: String): Unit with Test = { + Test.assertTrue(String.endsWith(s, suffix), message) +} + +// Run a test suite +pub fn describe(name: String, tests: fn(): Unit with Test): Unit with {Console, Test} = { + Console.print("Running: " ++ name) + tests() + Console.print("Passed: " ++ name) +} + +// Individual test case +pub fn it(description: String, test: fn(): Unit with Test): Unit with {Console, Test} = { + Console.print(" - " ++ description) + test() +} diff --git a/packages/testing/lux.toml b/packages/testing/lux.toml new file mode 100644 index 0000000..d59d86d --- /dev/null +++ b/packages/testing/lux.toml @@ -0,0 +1,8 @@ +[project] +name = "testing" +version = "0.1.0" +description = "Testing utilities and assertions for Lux" +authors = ["Lux Core Team"] +license = "MIT" + +[dependencies]