My aggregated monorepo of OCaml code, automaintained

Documentation and code quality improvements across libraries

- Add README files for jsonwt, owntracks, monopam, and srcsetter
- Fix langdetect language count (47→49) in dune-project
- Remove unused variable in crockford encode function
- Improve retention type documentation in zulip channels.mli
- Refactor conpool is_healthy to reduce nesting and improve clarity
- Document unimplemented IDNA features in punycode README

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+370 -62
+86
monopam/README.md
··· 1 + # monopam 2 + 3 + Opam overlay and monorepo manager using git subtrees. 4 + 5 + ## Overview 6 + 7 + Monopam manages synchronization between: 8 + - An opam overlay repository 9 + - Individual git checkouts of packages 10 + - A monorepo using git subtrees 11 + 12 + This enables maintaining multiple OCaml packages in a single repository while preserving their individual git histories. 13 + 14 + ## Installation 15 + 16 + ``` 17 + opam install monopam 18 + ``` 19 + 20 + ## Workflow 21 + 22 + ### Initialize 23 + 24 + ```bash 25 + monopam init 26 + ``` 27 + 28 + Creates configuration and initializes the monorepo structure. 29 + 30 + ### Check Status 31 + 32 + ```bash 33 + monopam status 34 + ``` 35 + 36 + Shows synchronization state of all packages, including: 37 + - Pending commits in checkouts 38 + - Uncommitted changes in monorepo 39 + - Remote tracking status 40 + 41 + ### Pull Updates 42 + 43 + ```bash 44 + monopam pull # Pull all packages 45 + monopam pull <package> # Pull specific package 46 + ``` 47 + 48 + For each package: 49 + 1. Clones or fetches the individual checkout 50 + 2. Adds or pulls the subtree in the monorepo 51 + 52 + ### Push Changes 53 + 54 + ```bash 55 + monopam push # Push all packages 56 + monopam push <package> # Push specific package 57 + ``` 58 + 59 + For each package with monorepo changes: 60 + 1. Splits the subtree commits 61 + 2. Pushes to the individual checkout 62 + 63 + Use `--upstream` to also push checkouts to their remotes. 64 + 65 + ### Add/Remove Packages 66 + 67 + ```bash 68 + monopam add <package> # Add package to monorepo 69 + monopam remove <package> # Remove package from monorepo 70 + ``` 71 + 72 + ## Directory Structure 73 + 74 + ``` 75 + overlay/ # Opam overlay repository 76 + src/ # Individual git checkouts 77 + package-a/ 78 + package-b/ 79 + monorepo/ # Combined monorepo 80 + package-a/ # Subtree from src/package-a 81 + package-b/ # Subtree from src/package-b 82 + ``` 83 + 84 + ## License 85 + 86 + ISC
+48 -56
ocaml-conpool/lib/conpool.ml
··· 242 242 243 243 let is_healthy (pool : ('clock, 'net) internal) ?(check_readable = false) conn = 244 244 let now = get_time pool in 245 + let endpoint = Connection.endpoint conn in 246 + let age = now -. Connection.created_at conn in 247 + let idle_time = now -. Connection.last_used conn in 248 + let max_lifetime = Config.max_connection_lifetime pool.config in 249 + let max_idle = Config.max_idle_time pool.config in 245 250 246 251 (* Check age *) 247 - let age = now -. Connection.created_at conn in 248 - let max_lifetime = Config.max_connection_lifetime pool.config in 249 - if age > max_lifetime then begin 252 + if age > max_lifetime then ( 250 253 Log.debug (fun m -> 251 254 m "Connection to %a unhealthy: exceeded max lifetime (%.2fs > %.2fs)" 252 - Endpoint.pp (Connection.endpoint conn) age max_lifetime); 253 - false 254 - end 255 + Endpoint.pp endpoint age max_lifetime); 256 + false) 255 257 (* Check idle time *) 256 - else begin 257 - let max_idle = Config.max_idle_time pool.config in 258 - if now -. Connection.last_used conn > max_idle then begin 259 - let idle_time = now -. Connection.last_used conn in 260 - Log.debug (fun m -> 261 - m "Connection to %a unhealthy: exceeded max idle time (%.2fs > %.2fs)" 262 - Endpoint.pp (Connection.endpoint conn) idle_time max_idle); 263 - false 264 - end (* Check use count *) 265 - else if 266 - match Config.max_connection_uses pool.config with 267 - | Some max -> Connection.use_count conn >= max 268 - | None -> false 269 - then begin 270 - Log.debug (fun m -> 271 - m "Connection to %a unhealthy: exceeded max use count (%d)" 272 - Endpoint.pp (Connection.endpoint conn) 273 - (Connection.use_count conn)); 274 - false 275 - end (* Optional: custom health check *) 276 - else if 277 - match Config.health_check pool.config with 278 - | Some check -> ( 279 - try 280 - let healthy = check (Connection.flow conn) in 281 - if not healthy then 282 - Log.debug (fun m -> 283 - m "Connection to %a failed custom health check" Endpoint.pp 284 - (Connection.endpoint conn)); 285 - not healthy 286 - with e -> 258 + else if idle_time > max_idle then ( 259 + Log.debug (fun m -> 260 + m "Connection to %a unhealthy: exceeded max idle time (%.2fs > %.2fs)" 261 + Endpoint.pp endpoint idle_time max_idle); 262 + false) 263 + (* Check use count *) 264 + else if 265 + match Config.max_connection_uses pool.config with 266 + | Some max -> Connection.use_count conn >= max 267 + | None -> false 268 + then ( 269 + Log.debug (fun m -> 270 + m "Connection to %a unhealthy: exceeded max use count (%d)" 271 + Endpoint.pp endpoint (Connection.use_count conn)); 272 + false) 273 + (* Optional: custom health check *) 274 + else if 275 + match Config.health_check pool.config with 276 + | Some check -> ( 277 + try 278 + let healthy = check (Connection.flow conn) in 279 + if not healthy then 287 280 Log.debug (fun m -> 288 - m "Connection to %a health check raised exception: %s" 289 - Endpoint.pp (Connection.endpoint conn) (Printexc.to_string e)); 290 - true (* Exception in health check = unhealthy *)) 291 - | None -> false 292 - then false (* Optional: check if socket still connected *) 293 - else if check_readable then 294 - try 295 - (* TODO avsm: a sockopt for this? *) 296 - true 297 - with _ -> false 298 - else begin 299 - Log.debug (fun m -> 300 - m "Connection to %a is healthy (age=%.2fs, idle=%.2fs, uses=%d)" 301 - Endpoint.pp (Connection.endpoint conn) age 302 - (now -. Connection.last_used conn) 303 - (Connection.use_count conn)); 304 - true 305 - end 306 - end 281 + m "Connection to %a failed custom health check" 282 + Endpoint.pp endpoint); 283 + not healthy 284 + with e -> 285 + Log.debug (fun m -> 286 + m "Connection to %a health check raised exception: %s" 287 + Endpoint.pp endpoint (Printexc.to_string e)); 288 + true) 289 + | None -> false 290 + then false 291 + (* Optional: check if socket still connected *) 292 + else if check_readable then (try true with _ -> false) 293 + (* All checks passed *) 294 + else ( 295 + Log.debug (fun m -> 296 + m "Connection to %a is healthy (age=%.2fs, idle=%.2fs, uses=%d)" 297 + Endpoint.pp endpoint age idle_time (Connection.use_count conn)); 298 + true) 307 299 308 300 (** {1 Internal Pool Operations} *) 309 301
+1 -3
ocaml-crockford/lib/crockford.ml
··· 63 63 process 0 0 64 64 65 65 let encode ?(split_every=0) ?(min_length=0) ?(checksum=false) number = 66 - let original_number = number in 67 - 68 66 (* Build base32 encoding *) 69 67 let rec build_encoding acc n = 70 68 if Int64.equal n 0L then acc ··· 98 96 (* Add checksum *) 99 97 let with_checksum = 100 98 if checksum then 101 - let cs = generate_checksum original_number in 99 + let cs = generate_checksum number in 102 100 padded ^ Printf.sprintf "%02Ld" cs 103 101 else 104 102 padded
+74
ocaml-jsonwt/README.md
··· 1 + # jsonwt 2 + 3 + JSON Web Token (JWT) and CBOR Web Token (CWT) implementation for OCaml. 4 + 5 + ## Overview 6 + 7 + A type-safe implementation of JWT (RFC 7519) and CWT (RFC 8392) with full support for: 8 + 9 + - **JWT parsing and creation** - Compact serialization format 10 + - **Signature verification** - HMAC, RSA, ECDSA, and EdDSA algorithms 11 + - **Claims validation** - Expiration, not-before, issuer, audience checks 12 + - **JSON Web Key (JWK)** - Key representation per RFC 7517 13 + - **Nested JWTs** - Recursive parsing with depth limits 14 + - **CBOR Web Tokens** - RFC 8392 for constrained environments 15 + 16 + ## Installation 17 + 18 + ``` 19 + opam install jsonwt 20 + ``` 21 + 22 + ## Usage 23 + 24 + ### Parsing and Verifying a JWT 25 + 26 + ```ocaml 27 + let token_string = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." in 28 + match Jsonwt.parse token_string with 29 + | Ok jwt -> 30 + let key = Jsonwt.Jwk.symmetric "secret-key" in 31 + (match Jsonwt.verify ~key jwt with 32 + | Ok () -> print_endline "Valid signature" 33 + | Error e -> print_endline (Jsonwt.error_to_string e)) 34 + | Error e -> print_endline (Jsonwt.error_to_string e) 35 + ``` 36 + 37 + ### Creating a JWT 38 + 39 + ```ocaml 40 + let header = Jsonwt.Header.make ~typ:"JWT" Jsonwt.Algorithm.HS256 in 41 + let claims = Jsonwt.Claims.( 42 + empty 43 + |> set_iss "https://example.com" 44 + |> set_sub "user123" 45 + |> set_exp (Ptime.of_float_s 1700000000.0 |> Option.get) 46 + |> build 47 + ) in 48 + let key = Jsonwt.Jwk.symmetric "secret-key" in 49 + match Jsonwt.create ~header ~claims ~key with 50 + | Ok jwt -> print_endline (Jsonwt.encode jwt) 51 + | Error e -> print_endline (Jsonwt.error_to_string e) 52 + ``` 53 + 54 + ## Supported Algorithms 55 + 56 + | Algorithm | Description | 57 + |-----------|-------------| 58 + | HS256/384/512 | HMAC with SHA-2 | 59 + | RS256/384/512 | RSASSA-PKCS1-v1_5 with SHA-2 | 60 + | ES256/384/512 | ECDSA with P-256/384/521 | 61 + | EdDSA | Ed25519 signatures | 62 + | none | Unsecured (requires explicit opt-in) | 63 + 64 + ## References 65 + 66 + - [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) - JSON Web Token (JWT) 67 + - [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) - JSON Web Signature (JWS) 68 + - [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517) - JSON Web Key (JWK) 69 + - [RFC 7518](https://datatracker.ietf.org/doc/html/rfc7518) - JSON Web Algorithms (JWA) 70 + - [RFC 8392](https://datatracker.ietf.org/doc/html/rfc8392) - CBOR Web Token (CWT) 71 + 72 + ## License 73 + 74 + ISC
+1 -1
ocaml-langdetect/dune-project
··· 16 16 (synopsis "Language detection library using n-gram frequency analysis") 17 17 (description 18 18 "An OCaml port of the Cybozu langdetect algorithm. Detects the natural \ 19 - language of text using n-gram frequency profiles. Supports 47 languages \ 19 + language of text using n-gram frequency profiles. Supports 49 languages \ 20 20 including English, Chinese, Japanese, Arabic, and many European languages.") 21 21 (depends 22 22 (ocaml (>= 5.1.0))
+1 -1
ocaml-langdetect/langdetect.opam
··· 2 2 opam-version: "2.0" 3 3 synopsis: "Language detection library using n-gram frequency analysis" 4 4 description: 5 - "An OCaml port of the Cybozu langdetect algorithm. Detects the natural language of text using n-gram frequency profiles. Supports 47 languages including English, Chinese, Japanese, Arabic, and many European languages." 5 + "An OCaml port of the Cybozu langdetect algorithm. Detects the natural language of text using n-gram frequency profiles. Supports 49 languages including English, Chinese, Japanese, Arabic, and many European languages." 6 6 maintainer: ["Anil Madhavapeddy <anil@recoil.org>"] 7 7 authors: ["Anil Madhavapeddy"] 8 8 license: "MIT"
+67
ocaml-owntracks/README.md
··· 1 + # owntracks 2 + 3 + OCaml types and JSON codecs for OwnTracks MQTT location messages. 4 + 5 + ## Overview 6 + 7 + [OwnTracks](https://owntracks.org/) is an open-source location tracking application that publishes GPS coordinates, accuracy, speed, battery, and other device state over MQTT. This library provides type-safe parsing and serialization of all OwnTracks message types. 8 + 9 + ## Packages 10 + 11 + - **owntracks** - Core types and jsont codecs 12 + - **owntracks-cli** - Command-line tools for MQTT subscription and data export 13 + 14 + ## Installation 15 + 16 + ``` 17 + opam install owntracks # Core library 18 + opam install owntracks-cli # CLI tools 19 + ``` 20 + 21 + ## Usage 22 + 23 + ### Decoding Messages 24 + 25 + ```ocaml 26 + let json = {|{"_type":"location","lat":51.5,"lon":-0.1,"tst":1234567890}|} in 27 + match Jsont_bytesrw.decode_string Owntracks.Message.jsont json with 28 + | Ok (Location loc) -> 29 + Printf.printf "Location: %.4f, %.4f\n" 30 + (Owntracks.Location.lat loc) 31 + (Owntracks.Location.lon loc) 32 + | Ok _ -> print_endline "Other message type" 33 + | Error e -> Printf.printf "Error: %s\n" e 34 + ``` 35 + 36 + ## Message Types 37 + 38 + | Type | Description | 39 + |------|-------------| 40 + | Location | GPS coordinates, accuracy, speed, battery | 41 + | Transition | Region entry/exit events | 42 + | Waypoint | Monitored region definitions | 43 + | Card | User information for display | 44 + | LWT | Last Will and Testament (disconnect notification) | 45 + 46 + ## Integration Modules 47 + 48 + - **Mqtt** - MQTT message parsing and topic helpers 49 + - **Recorder** - OwnTracks Recorder HTTP API parsing 50 + - **Geojson** - Convert locations to GeoJSON format 51 + 52 + ## CLI Tools 53 + 54 + The `owntracks-cli` package provides: 55 + 56 + - Real-time MQTT monitoring 57 + - GeoJSON export 58 + - OwnTracks Recorder API queries 59 + 60 + ## References 61 + 62 + - [OwnTracks JSON Format](https://owntracks.org/booklet/tech/json/) 63 + - [OwnTracks Recorder](https://owntracks.org/booklet/guide/recorder/) 64 + 65 + ## License 66 + 67 + ISC
+10
ocaml-punycode/README.md
··· 90 90 odig doc puny 91 91 ``` 92 92 93 + ## Limitations 94 + 95 + The following IDNA 2008 features are not yet implemented: 96 + 97 + - **Bidi rules** (RFC 5893): Bidirectional text validation for right-to-left scripts 98 + - **Contextual joiners** (RFC 5892 Appendix A.1): Zero-width joiner/non-joiner validation 99 + 100 + These checks are disabled by default in the API. Most common use cases (European languages, CJK) work correctly without them. 101 + 93 102 ## References 94 103 95 104 - [RFC 3492](https://datatracker.ietf.org/doc/html/rfc3492) - Punycode: A Bootstring encoding of Unicode for IDNA 96 105 - [RFC 5891](https://datatracker.ietf.org/doc/html/rfc5891) - Internationalized Domain Names in Applications (IDNA): Protocol 97 106 - [RFC 5892](https://datatracker.ietf.org/doc/html/rfc5892) - Unicode Code Points and IDNA 107 + - [RFC 5893](https://datatracker.ietf.org/doc/html/rfc5893) - Right-to-Left Scripts for IDNA 98 108 - [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035) - Domain Names Implementation and Specification 99 109 100 110 ## License
+3 -1
ocaml-zulip/lib/zulip/channels.mli
··· 54 54 history_public_to_subscribers : bool option; 55 55 (** Whether history is visible to new subscribers *) 56 56 message_retention_days : int option option; 57 - (** Message retention (None = default, Some None = forever) *) 57 + (** Message retention policy. [None] = use organization default, 58 + [Some None] = unlimited retention, [Some (Some n)] = n days. *) 58 59 can_remove_subscribers_group : int option; 59 60 (** User group that can remove subscribers *) 60 61 } ··· 91 92 unit -> 92 93 unit 93 94 (** Update channel properties. 95 + @param message_retention_days [None] = unlimited, [Some n] = n days 94 96 @raise Eio.Io on failure *) 95 97 96 98 (** {1 Deleting Channels} *)
+79
srcsetter/README.md
··· 1 + # srcsetter 2 + 3 + Responsive image generation for HTML srcset attributes. 4 + 5 + ## Overview 6 + 7 + Srcsetter processes a directory of images and outputs responsive variants suitable for embedding as `<img srcset>` tags in websites. It uses ImageMagick for image processing and outputs WebP format. 8 + 9 + ## Packages 10 + 11 + - **srcsetter** - Core library for image entry management 12 + - **srcsetter-cmd** - CLI tool for batch processing 13 + 14 + ## Installation 15 + 16 + ``` 17 + opam install srcsetter # Library only 18 + opam install srcsetter-cmd # CLI tool (includes library) 19 + ``` 20 + 21 + ## Usage 22 + 23 + ### Library 24 + 25 + ```ocaml 26 + (* Load entries from JSON *) 27 + match Srcsetter.list_of_json json_string with 28 + | Ok entries -> 29 + List.iter (fun entry -> 30 + let name = Srcsetter.name entry in 31 + let (w, h) = Srcsetter.dims entry in 32 + Printf.printf "%s: %dx%d\n" name w h; 33 + (* Access variants *) 34 + Srcsetter.MS.iter (fun variant_name (vw, vh) -> 35 + Printf.printf " %s: %dx%d\n" variant_name vw vh 36 + ) (Srcsetter.variants entry) 37 + ) entries 38 + | Error msg -> Printf.printf "Error: %s\n" msg 39 + ``` 40 + 41 + ### CLI 42 + 43 + ```bash 44 + srcsetter process input_dir/ output_dir/ 45 + ``` 46 + 47 + ## Image Entry Structure 48 + 49 + Each entry tracks: 50 + - **name** - Output filename (e.g., `photo.webp`) 51 + - **slug** - URL-safe identifier 52 + - **origin** - Original source file path 53 + - **dims** - Base image dimensions (width, height) 54 + - **variants** - Map of variant filenames to dimensions 55 + 56 + ## JSON Format 57 + 58 + ```json 59 + [ 60 + { 61 + "name": "photo.webp", 62 + "slug": "photo", 63 + "origin": "photos/DSC_1234.jpg", 64 + "dims": [1920, 1080], 65 + "variants": { 66 + "photo-640.webp": [640, 360], 67 + "photo-1280.webp": [1280, 720] 68 + } 69 + } 70 + ] 71 + ``` 72 + 73 + ## Requirements 74 + 75 + - ImageMagick CLI tools 76 + 77 + ## License 78 + 79 + ISC