From 69cd6b82a3054879ee2f4c222d0f4464c980234d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 6 Dec 2025 14:09:42 +0000 Subject: [PATCH 1/2] feat: add RSR (Rhodium Standard Repositories) compliance This commit brings the repository into compliance with the RSR specification: Documentation: - Convert README.md to README.adoc (AsciiDoc format) - Add SECURITY.md with vulnerability reporting procedures - Add CODE_OF_CONDUCT.adoc (Contributor Covenant v2.1) - Add CONTRIBUTING.adoc with TPCF guidelines - Add GOVERNANCE.adoc with project governance model - Add CHANGELOG.md following Keep a Changelog format - Add FUNDING.yml for transparency Licensing: - Add dual MIT + Palimpsest v0.8 licensing - Create LICENSE.txt with both licenses - Create LICENSE-PALIMPSEST.txt Infrastructure: - Add flake.nix for Nix development environment - Add justfile for task automation - Add .gitattributes for consistent handling - Add .well-known/ directory with security.txt and funding.json Code Quality: - Add SPDX license headers to all source files --- .gitattributes | 49 +++++ .well-known/funding.json | 20 +++ .well-known/security.txt | 9 + CHANGELOG.md | 47 +++++ CODE_OF_CONDUCT.adoc | 100 +++++++++++ CONTRIBUTING.adoc | 201 +++++++++++++++++++++ FUNDING.yml | 25 +++ GOVERNANCE.adoc | 121 +++++++++++++ LICENSE-PALIMPSEST.txt | 56 ++++++ LICENSE => LICENSE.txt | 13 ++ README.adoc | 306 ++++++++++++++++++++++++++++++++ README.md | 244 ------------------------- SECURITY.md | 58 ++++++ examples/01_counter/Counter.res | 3 + flake.nix | 65 +++++++ justfile | 77 ++++++++ src/Tea.res | 3 + src/Tea.resi | 3 + src/Tea_App.res | 3 + src/Tea_App.resi | 3 + src/Tea_Cmd.res | 3 + src/Tea_Cmd.resi | 3 + src/Tea_Html.res | 3 + src/Tea_Html.resi | 3 + src/Tea_Json.res | 3 + src/Tea_Json.resi | 3 + src/Tea_Sub.res | 3 + src/Tea_Sub.resi | 3 + src/Tea_Test.res | 3 + src/Tea_Test.resi | 3 + 30 files changed, 1192 insertions(+), 244 deletions(-) create mode 100644 .gitattributes create mode 100644 .well-known/funding.json create mode 100644 .well-known/security.txt create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.adoc create mode 100644 CONTRIBUTING.adoc create mode 100644 FUNDING.yml create mode 100644 GOVERNANCE.adoc create mode 100644 LICENSE-PALIMPSEST.txt rename LICENSE => LICENSE.txt (63%) create mode 100644 README.adoc delete mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 flake.nix create mode 100644 justfile diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a8724e7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: MIT AND Palimpsest-0.8 +# SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + +# Git attributes for rescript-tea + +# Auto-detect text files and normalize line endings +* text=auto + +# ReScript source files +*.res text eol=lf linguist-language=ReScript +*.resi text eol=lf linguist-language=ReScript + +# JavaScript (generated) +*.js text eol=lf +*.mjs text eol=lf + +# Configuration files +*.json text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.toml text eol=lf + +# Documentation +*.md text eol=lf +*.adoc text eol=lf +*.txt text eol=lf + +# Shell scripts +*.sh text eol=lf + +# Nix files +*.nix text eol=lf + +# Lock files - don't merge, always use ours +package-lock.json merge=ours + +# Binary files +*.png binary +*.jpg binary +*.gif binary +*.ico binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary + +# Exports for GitHub linguist +*.res linguist-language=ReScript +*.resi linguist-language=ReScript diff --git a/.well-known/funding.json b/.well-known/funding.json new file mode 100644 index 0000000..f78e3ab --- /dev/null +++ b/.well-known/funding.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://fundinglink.org/schema/v1/funding.json", + "version": "1.0.0", + "entity": { + "type": "individual", + "name": "Jonathan D.A. Jewell" + }, + "projects": [ + { + "name": "rescript-tea", + "description": "The Elm Architecture for ReScript", + "repository": "https://github.com/Hyperpolymath/rescript-tea", + "license": "MIT AND Palimpsest-0.8" + } + ], + "funding": { + "channels": [], + "plans": [] + } +} diff --git a/.well-known/security.txt b/.well-known/security.txt new file mode 100644 index 0000000..52bb66e --- /dev/null +++ b/.well-known/security.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT AND Palimpsest-0.8 +# Security contact information for rescript-tea +# See: https://securitytxt.org/ + +Contact: https://github.com/Hyperpolymath/rescript-tea/security/advisories/new +Expires: 2025-12-31T23:59:59.000Z +Preferred-Languages: en +Canonical: https://github.com/Hyperpolymath/rescript-tea/.well-known/security.txt +Policy: https://github.com/Hyperpolymath/rescript-tea/blob/main/SECURITY.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..32c2316 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ + + + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- RSR (Rhodium Standard Repositories) compliance +- Nix flake for reproducible development environment +- Justfile for task automation +- SPDX license headers on all source files + +## [0.1.0] - 2024-12-06 + +### Added +- Initial release of rescript-tea +- Core modules: + - `Tea_Cmd` - Commands for side effects + - `Tea_Sub` - Subscriptions for external events + - `Tea_App` - Application runtime with React integration + - `Tea_Html` - Type-safe HTML helpers + - `Tea_Json` - Elm-style JSON decoders + - `Tea_Test` - Testing utilities + - `Tea` - Main entry point +- Subscription types: + - `Sub.Time.every` - Timer subscriptions + - `Sub.Keyboard.downs/ups` - Keyboard events + - `Sub.Mouse.clicks/moves` - Mouse events + - `Sub.Window.resizes` - Window resize events +- Counter example demonstrating basic TEA patterns +- React hooks-based runtime implementation +- Functor-based API (`Make`, `MakeSimple`, `MakeWithDispatch`) + +### Technical Details +- Built on React 18+ with hooks +- Uses Belt library for collections +- ReScript 11+ compatibility +- ESModule output format + +[Unreleased]: https://github.com/Hyperpolymath/rescript-tea/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/Hyperpolymath/rescript-tea/releases/tag/v0.1.0 diff --git a/CODE_OF_CONDUCT.adoc b/CODE_OF_CONDUCT.adoc new file mode 100644 index 0000000..c89d828 --- /dev/null +++ b/CODE_OF_CONDUCT.adoc @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell += Code of Conduct +:toc: + +== Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +== Our Standards + +=== Positive Behaviors + +Examples of behavior that contributes to a positive environment: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes +* Focusing on what is best for the overall community +* Using welcoming and inclusive language + +=== Unacceptable Behaviors + +Examples of unacceptable behavior: + +* The use of sexualized language or imagery, and sexual attention or advances +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information without explicit permission +* Other conduct which could reasonably be considered inappropriate + +== Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +== Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. + +== Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement. + +All complaints will be reviewed and investigated promptly and fairly. + +== Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +=== 1. Correction + +*Community Impact*: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +*Consequence*: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. + +=== 2. Warning + +*Community Impact*: A violation through a single incident or series of actions. + +*Consequence*: A warning with consequences for continued behavior. No +interaction with the people involved for a specified period of time. + +=== 3. Temporary Ban + +*Community Impact*: A serious violation of community standards. + +*Consequence*: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. + +=== 4. Permanent Ban + +*Community Impact*: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +*Consequence*: A permanent ban from any sort of public interaction within +the community. + +== Attribution + +This Code of Conduct is adapted from the https://www.contributor-covenant.org[Contributor Covenant], +version 2.1. diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 0000000..332665e --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell += Contributing to rescript-tea +:toc: +:toclevels: 2 + +Thank you for your interest in contributing to rescript-tea! + +== Getting Started + +=== Prerequisites + +* Node.js 18+ +* npm or pnpm +* ReScript compiler (installed via npm) +* Nix (optional, for reproducible development environment) + +=== Development Setup + +[source,bash] +---- +# Clone the repository +git clone https://github.com/Hyperpolymath/rescript-tea.git +cd rescript-tea + +# Install dependencies +npm install + +# Build the project +npm run build + +# Run in watch mode +npm run dev +---- + +=== Using Nix + +[source,bash] +---- +# Enter development shell +nix develop + +# Or use direnv +direnv allow +---- + +== Tri-Perimeter Contribution Framework (TPCF) + +This project follows the RSR Tri-Perimeter Contribution Framework: + +=== Perimeter 1 (Core) + +Maintainers only. Changes to: + +* Build system configuration +* Core runtime (`Tea_App.res`) +* Release management +* Security-critical code + +=== Perimeter 2 (Expert) + +Trusted contributors. Changes to: + +* Protocol extensions +* New subscription types +* Performance optimizations +* API design changes + +=== Perimeter 3 (Community) + +Open participation: + +* Documentation improvements +* Bug fixes with tests +* New examples +* Test coverage +* Issue triage + +== How to Contribute + +=== Reporting Bugs + +1. Check existing issues to avoid duplicates +2. Create a new issue with: + * Clear, descriptive title + * Steps to reproduce + * Expected vs actual behavior + * ReScript/Node.js versions + * Minimal reproduction code + +=== Suggesting Features + +1. Open a discussion or issue +2. Describe the use case +3. Explain why existing features don't suffice +4. Propose an API design (if applicable) + +=== Submitting Pull Requests + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/my-feature` +3. Make your changes +4. Add/update tests +5. Ensure all tests pass: `npm test` +6. Add SPDX headers to new files +7. Update documentation if needed +8. Submit a pull request + +=== Code Style + +* Follow existing code patterns +* Use meaningful variable names +* Keep functions small and focused +* Add type annotations for public APIs +* Document complex logic with comments + +=== SPDX Headers + +All source files must include SPDX headers: + +[source,rescript] +---- +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Your Name +---- + +=== Commit Messages + +Follow conventional commits: + +---- +type(scope): description + +[optional body] + +[optional footer] +---- + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` + +Examples: + +---- +feat(cmd): add Cmd.debounce for rate-limited commands +fix(sub): prevent memory leak in subscription cleanup +docs: update README with new API examples +---- + +== Testing + +[source,bash] +---- +# Run all tests +npm test + +# Run tests in watch mode +npm run test:watch +---- + +=== Writing Tests + +Use `Tea_Test` utilities: + +[source,rescript] +---- +// Test update function +let model = Tea_Test.simulate( + ~init, + ~update, + ~msgs=[Increment, Increment, Decrement], +) +assert(model.count == 1) + +// Test commands +let cmds = Tea_Test.collectCmds(~init, ~update, ~msgs=[FetchData]) +assert(Belt.Array.length(cmds) > 0) +---- + +== Documentation + +* Update README.adoc for user-facing changes +* Add JSDoc comments to public functions +* Include examples for new features +* Update CHANGELOG.md + +== Code of Conduct + +Please read and follow our link:CODE_OF_CONDUCT.adoc[Code of Conduct]. + +== License + +By contributing, you agree that your contributions will be licensed under the +project's dual MIT and Palimpsest v0.8 license. + +== Questions? + +* Open a GitHub Discussion +* Check existing documentation +* Review closed issues/PRs + +Thank you for contributing! diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..cd1f1e9 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT AND Palimpsest-0.8 +# SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + +# Funding information for rescript-tea +# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository + +github: [] +patreon: "" +open_collective: "" +ko_fi: "" +tidelift: "" +community_bridge: "" +liberapay: "" +issuehunt: "" +lfx_crowdfunding: "" +polar: "" +buy_me_a_coffee: "" +custom: [] + +# This project is currently not accepting financial sponsorship. +# If you'd like to support the project, consider: +# - Contributing code or documentation +# - Reporting bugs +# - Helping other users +# - Spreading the word diff --git a/GOVERNANCE.adoc b/GOVERNANCE.adoc new file mode 100644 index 0000000..c0ad242 --- /dev/null +++ b/GOVERNANCE.adoc @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell += Governance +:toc: + +== Overview + +rescript-tea follows a benevolent dictator for life (BDFL) governance model with +community input through the Tri-Perimeter Contribution Framework (TPCF). + +== Roles + +=== Project Lead (BDFL) + +* Final decision authority on project direction +* Responsible for releases and security +* Manages maintainer team +* Resolves disputes + +=== Maintainers + +* Merge permissions for all perimeters +* Code review responsibilities +* Issue triage and labeling +* Community support + +=== Contributors + +* Submit pull requests +* Report issues +* Participate in discussions +* Improve documentation + +== Decision Making + +=== Consensus Seeking + +Most decisions are made through discussion and consensus: + +1. Proposal raised (issue, discussion, or PR) +2. Community feedback period (typically 1 week) +3. Maintainer review +4. Decision documented + +=== Voting + +For significant changes without consensus: + +* Maintainers vote +* Simple majority required +* Project Lead has tie-breaking vote + +=== Fast-Track + +Security fixes and critical bugs may be merged without full consensus period. + +== Tri-Perimeter Framework + +=== Perimeter 1: Core + +*Who*: Maintainers only + +*What*: + +* Build system (flake.nix, justfile) +* Core runtime (Tea_App) +* Release process +* Security fixes + +*Process*: Direct merge after maintainer review + +=== Perimeter 2: Expert + +*Who*: Maintainers + trusted contributors + +*What*: + +* New modules +* API changes +* Performance work +* Complex features + +*Process*: PR + 2 maintainer approvals + +=== Perimeter 3: Community + +*Who*: Anyone + +*What*: + +* Documentation +* Examples +* Tests +* Bug fixes + +*Process*: PR + 1 maintainer approval + +== Becoming a Maintainer + +1. Sustained, quality contributions over 6+ months +2. Demonstrated understanding of project goals +3. Nomination by existing maintainer +4. Approval by Project Lead + +== Conflict Resolution + +1. Discussion in relevant issue/PR +2. Escalation to maintainer team +3. Final decision by Project Lead +4. Appeals via community discussion + +== Changes to Governance + +This document may be amended through the standard decision-making process. +Significant changes require community discussion period. + +== Communication + +* GitHub Issues: Bug reports, feature requests +* GitHub Discussions: Questions, ideas, community chat +* Pull Requests: Code contributions diff --git a/LICENSE-PALIMPSEST.txt b/LICENSE-PALIMPSEST.txt new file mode 100644 index 0000000..8b08950 --- /dev/null +++ b/LICENSE-PALIMPSEST.txt @@ -0,0 +1,56 @@ +Palimpsest License v0.8 + +Copyright (c) 2024 Jonathan D.A. Jewell + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +"Software" means the source code, object code, documentation, and any other +materials provided under this license. + +"Derivative Work" means any work that is based on (or derived from) the +Software. + +"Commercial Use" means use of the Software for the purpose of generating +revenue or monetary compensation. + +2. Grant of Rights + +Subject to the terms of this license, the licensor grants you a worldwide, +royalty-free, non-exclusive license to: + +a) Use the Software for any purpose, including Commercial Use +b) Modify the Software and create Derivative Works +c) Distribute the Software and Derivative Works +d) Sublicense the Software + +3. Conditions + +You may exercise the rights granted above provided that you: + +a) Include a copy of this license with any distribution of the Software +b) Provide attribution to the original authors +c) Clearly mark any modifications you make to the Software +d) Do not use the name of the licensor to endorse derived products + +4. Ethical Use Clause + +You agree not to use the Software in any way that: + +a) Violates applicable laws or regulations +b) Infringes on the rights of others +c) Causes harm to individuals or communities +d) Facilitates surveillance, oppression, or discrimination + +5. Disclaimer + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. + +6. Termination + +This license terminates automatically if you violate any of its terms. diff --git a/LICENSE b/LICENSE.txt similarity index 63% rename from LICENSE rename to LICENSE.txt index cd7923b..ab6ac35 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,4 +1,11 @@ +SPDX-License-Identifier: MIT AND Palimpsest-0.8 + +This project is dual-licensed under the MIT License and Palimpsest License v0.8. +You may choose either license to govern your use of this software. + +================================================================================ MIT License +================================================================================ Copyright (c) 2024 Jonathan D.A. Jewell @@ -19,3 +26,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ +Palimpsest License v0.8 +================================================================================ + +See LICENSE-PALIMPSEST.txt for the full text of the Palimpsest License. diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..e6e9607 --- /dev/null +++ b/README.adoc @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell += rescript-tea +:toc: macro +:toc-title: Contents +:toclevels: 3 +:icons: font +:source-highlighter: rouge +:experimental: + +The Elm Architecture (TEA) for ReScript, providing a principled way to build web applications with guaranteed state consistency, exhaustive event handling, and time-travel debugging. + +toc::[] + +== Overview + +rescript-tea implements The Elm Architecture pattern for ReScript with React integration. It provides: + +* *Single source of truth* - One model, one update pathway +* *Pure updates* - Easy to test, easy to reason about +* *Type-safe* - Compiler catches missing message handlers +* *React integration* - Uses React for rendering, compatible with existing components +* *Commands & Subscriptions* - Declarative side effects + +== Installation + +[source,bash] +---- +npm install rescript-tea +---- + +Add to your `rescript.json`: + +[source,json] +---- +{ + "bs-dependencies": ["rescript-tea", "@rescript/react"] +} +---- + +== Quick Start + +[source,rescript] +---- +open Tea + +// 1. Define your model +type model = {count: int} + +// 2. Define your messages +type msg = + | Increment + | Decrement + +// 3. Initialize your app +let init = () => ({count: 0}, Cmd.none) + +// 4. Handle updates +let update = (msg, model) => { + switch msg { + | Increment => ({count: model.count + 1}, Cmd.none) + | Decrement => ({count: model.count - 1}, Cmd.none) + } +} + +// 5. Render your view (with dispatch for event handling) +let view = (model, dispatch) => { +
+ + {model.count->Belt.Int.toString->React.string} + +
+} + +// 6. Declare subscriptions (none for this simple example) +let subscriptions = _model => Sub.none + +// 7. Create the app component +module App = MakeWithDispatch({ + type model = model + type msg = msg + type flags = unit + let init = _ => init() + let update = update + let view = view + let subscriptions = subscriptions +}) + +// 8. Mount it +switch ReactDOM.querySelector("#root") { +| Some(root) => { + let rootElement = ReactDOM.Client.createRoot(root) + rootElement->ReactDOM.Client.Root.render() + } +| None => () +} +---- + +== Core Concepts + +=== Model + +Your application state is a single value (typically a record): + +[source,rescript] +---- +type model = { + user: option, + posts: array, + loading: bool, +} +---- + +=== Messages + +All possible events are variants of a single type: + +[source,rescript] +---- +type msg = + | FetchPosts + | GotPosts(result, error>) + | SelectPost(int) + | Logout +---- + +=== Update + +A pure function that handles messages: + +[source,rescript] +---- +let update = (msg, model) => { + switch msg { + | FetchPosts => (model, fetchPostsCmd) + | GotPosts(Ok(posts)) => ({...model, posts, loading: false}, Cmd.none) + | GotPosts(Error(_)) => ({...model, loading: false}, Cmd.none) + | SelectPost(id) => ({...model, selectedId: Some(id)}, Cmd.none) + | Logout => ({...model, user: None}, Cmd.none) + } +} +---- + +=== Commands + +Descriptions of side effects to perform: + +[source,rescript] +---- +// Do nothing +Cmd.none + +// Batch multiple commands +Cmd.batch([cmd1, cmd2, cmd3]) + +// Perform an async operation +Cmd.perform(() => fetchUser("alice"), user => GotUser(user)) + +// Handle potential failures +Cmd.attempt(() => fetchUser("alice"), result => GotUser(result)) +---- + +=== Subscriptions + +Declarations of external event sources: + +[source,rescript] +---- +let subscriptions = model => { + if model.timerRunning { + Sub.Time.every(1000, time => Tick(time)) + } else { + Sub.none + } +} +---- + +Built-in subscriptions: + +* `Sub.Time.every(ms, toMsg)` - Timer +* `Sub.Keyboard.downs(toMsg)` - Key down events +* `Sub.Keyboard.ups(toMsg)` - Key up events +* `Sub.Mouse.clicks(toMsg)` - Mouse clicks +* `Sub.Mouse.moves(toMsg)` - Mouse movement +* `Sub.Window.resizes(toMsg)` - Window resize + +== Modules + +=== Tea.Cmd + +Commands for side effects. + +=== Tea.Sub + +Subscriptions for external events. + +=== Tea.Json + +Type-safe JSON decoding: + +[source,rescript] +---- +open Tea.Json + +let userDecoder = map3( + (id, name, email) => {id, name, email}, + field("id", int), + field("name", string), + field("email", string), +) + +// Use it +switch decodeString(userDecoder, jsonString) { +| Ok(user) => // use user +| Error(err) => Console.log(errorToString(err)) +} +---- + +=== Tea.Html + +Optional HTML helpers (you can also use JSX directly): + +[source,rescript] +---- +open Tea.Html + +let view = model => { + div([className("container")], [ + h1([], [text("Hello")]), + button([onClick(Increment)], [text("+")]), + ]) +} +---- + +=== Tea.Test + +Testing utilities: + +[source,rescript] +---- +// Simulate a sequence of messages +let finalModel = Tea.Test.simulate( + ~init, + ~update, + ~msgs=[Increment, Increment, Decrement], +) + +// Collect commands for inspection +let cmds = Tea.Test.collectCmds( + ~init, + ~update, + ~msgs=[FetchUser("alice")], +) +---- + +== Examples + +See the `examples/` directory: + +* `01_counter/` - Basic counter + +== Architecture: React Hooks Integration + +rescript-tea uses React hooks internally to implement the TEA runtime: + +* *useState* - Stores the model state +* *useRef* - Maintains cleanup functions for subscriptions +* *useEffect* - Executes commands and manages subscription lifecycle +* *useCallback* - Memoizes the dispatch function + +This provides a seamless integration with React while maintaining TEA's guarantees. + +== Why TEA? + +[cols="1,2"] +|=== +|Bug Type |How TEA Prevents It + +|Stale UI +|View is pure function of Model + +|Forgotten state updates +|View recomputes entirely + +|Unhandled events +|Variant types = compiler warnings + +|Race conditions +|Single update pathway + +|Untestable code +|Pure functions = easy testing +|=== + +== License + +This project is dual-licensed under: + +* link:LICENSE.txt[MIT License] +* link:LICENSE-PALIMPSEST.txt[Palimpsest License v0.8] + +See link:CONTRIBUTING.adoc[CONTRIBUTING] for details on how to contribute. + +== RSR Compliance + +This repository follows the https://github.com/Hyperpolymath/rhodium-standard-repositories[Rhodium Standard Repositories] specification. diff --git a/README.md b/README.md deleted file mode 100644 index 575c79c..0000000 --- a/README.md +++ /dev/null @@ -1,244 +0,0 @@ -# rescript-tea - -The Elm Architecture (TEA) for ReScript, providing a principled way to build web applications with guaranteed state consistency, exhaustive event handling, and time-travel debugging. - -## Features - -- **Single source of truth** - One model, one update pathway -- **Pure updates** - Easy to test, easy to reason about -- **Type-safe** - Compiler catches missing message handlers -- **React integration** - Uses React for rendering, compatible with existing components -- **Commands & Subscriptions** - Declarative side effects - -## Installation - -```bash -npm install rescript-tea -``` - -Add to your `rescript.json`: - -```json -{ - "bs-dependencies": ["rescript-tea", "@rescript/react"] -} -``` - -## Quick Start - -```rescript -open Tea - -// 1. Define your model -type model = {count: int} - -// 2. Define your messages -type msg = - | Increment - | Decrement - -// 3. Initialize your app -let init = () => ({count: 0}, Cmd.none) - -// 4. Handle updates -let update = (msg, model) => { - switch msg { - | Increment => ({count: model.count + 1}, Cmd.none) - | Decrement => ({count: model.count - 1}, Cmd.none) - } -} - -// 5. Render your view -let view = model => { -
- - {model.count->Int.toString->React.string} - -
-} - -// 6. Declare subscriptions (none for this simple example) -let subscriptions = _model => Sub.none - -// 7. Create the app component -module App = MakeSimple({ - type model = model - type msg = msg - let app = {init, update, view, subscriptions} -}) - -// 8. Mount it -switch ReactDOM.querySelector("#root") { -| Some(root) => ReactDOM.render(, root) -| None => () -} -``` - -## Core Concepts - -### Model - -Your application state is a single value (typically a record): - -```rescript -type model = { - user: option, - posts: array, - loading: bool, -} -``` - -### Messages - -All possible events are variants of a single type: - -```rescript -type msg = - | FetchPosts - | GotPosts(result, error>) - | SelectPost(int) - | Logout -``` - -### Update - -A pure function that handles messages: - -```rescript -let update = (msg, model) => { - switch msg { - | FetchPosts => (model, fetchPostsCmd) - | GotPosts(Ok(posts)) => ({...model, posts, loading: false}, Cmd.none) - | GotPosts(Error(_)) => ({...model, loading: false}, Cmd.none) - | SelectPost(id) => ({...model, selectedId: Some(id)}, Cmd.none) - | Logout => ({...model, user: None}, Cmd.none) - } -} -``` - -### Commands - -Descriptions of side effects to perform: - -```rescript -// Do nothing -Cmd.none - -// Batch multiple commands -Cmd.batch([cmd1, cmd2, cmd3]) - -// Perform an async operation -Cmd.perform(() => fetchUser("alice"), user => GotUser(user)) - -// Handle potential failures -Cmd.attempt(() => fetchUser("alice"), result => GotUser(result)) -``` - -### Subscriptions - -Declarations of external event sources: - -```rescript -let subscriptions = model => { - if model.timerRunning { - Sub.Time.every(1000, time => Tick(time)) - } else { - Sub.none - } -} -``` - -Built-in subscriptions: -- `Sub.Time.every(ms, toMsg)` - Timer -- `Sub.Keyboard.downs(toMsg)` - Key down events -- `Sub.Keyboard.ups(toMsg)` - Key up events -- `Sub.Mouse.clicks(toMsg)` - Mouse clicks -- `Sub.Mouse.moves(toMsg)` - Mouse movement -- `Sub.Window.resizes(toMsg)` - Window resize - -## Modules - -### Tea.Cmd - -Commands for side effects. - -### Tea.Sub - -Subscriptions for external events. - -### Tea.Json - -Type-safe JSON decoding: - -```rescript -open Tea.Json - -let userDecoder = map3( - (id, name, email) => {id, name, email}, - field("id", int), - field("name", string), - field("email", string), -) - -// Use it -switch decodeString(userDecoder, jsonString) { -| Ok(user) => // use user -| Error(err) => Console.log(errorToString(err)) -} -``` - -### Tea.Html - -Optional HTML helpers (you can also use JSX directly): - -```rescript -open Tea.Html - -let view = model => { - div([className("container")], [ - h1([], [text("Hello")]), - button([onClick(Increment)], [text("+")]), - ]) -} -``` - -### Tea.Test - -Testing utilities: - -```rescript -// Simulate a sequence of messages -let finalModel = Tea.Test.simulate( - ~init, - ~update, - ~msgs=[Increment, Increment, Decrement], -) - -// Collect commands for inspection -let cmds = Tea.Test.collectCmds( - ~init, - ~update, - ~msgs=[FetchUser("alice")], -) -``` - -## Examples - -See the `examples/` directory: - -- `01_counter/` - Basic counter -- More coming soon... - -## Why TEA? - -| Bug Type | How TEA Prevents It | -|----------|---------------------| -| Stale UI | View is pure function of Model | -| Forgotten state updates | View recomputes entirely | -| Unhandled events | Variant types = compiler warnings | -| Race conditions | Single update pathway | -| Untestable code | Pure functions = easy testing | - -## License - -MIT diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..c8f9a5d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,58 @@ + + + +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.1.x | :white_check_mark: | + +## Reporting a Vulnerability + +We take security vulnerabilities seriously. If you discover a security issue, +please report it responsibly. + +### How to Report + +1. **Do NOT** create a public GitHub issue for security vulnerabilities +2. Email security concerns to: security@example.com (or create a private security advisory) +3. Include as much detail as possible: + - Description of the vulnerability + - Steps to reproduce + - Potential impact + - Suggested fix (if any) + +### What to Expect + +- **Acknowledgment**: Within 48 hours of your report +- **Initial Assessment**: Within 7 days +- **Resolution Timeline**: Depends on severity + - Critical: 24-72 hours + - High: 1-2 weeks + - Medium: 2-4 weeks + - Low: Next release cycle + +### Security Best Practices + +When using rescript-tea: + +1. **Keep dependencies updated** - Run `npm audit` regularly +2. **Validate all external data** - Use `Tea.Json` decoders for type-safe parsing +3. **Sanitize user input** - Never trust data from external sources +4. **Use HTTPS** - Always serve your application over secure connections + +## Security Features + +rescript-tea provides several security benefits: + +- **Type Safety**: ReScript's type system prevents many common vulnerabilities +- **Immutable State**: TEA's architecture prevents accidental state mutations +- **Pure Functions**: Side effects are explicit and controlled through Commands +- **JSON Decoders**: Type-safe parsing prevents injection attacks + +## Acknowledgments + +We appreciate responsible disclosure and will acknowledge security researchers +who help improve the security of this project. diff --git a/examples/01_counter/Counter.res b/examples/01_counter/Counter.res index a27c40e..a8c5abb 100644 --- a/examples/01_counter/Counter.res +++ b/examples/01_counter/Counter.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" A simple counter example demonstrating the basics of TEA. ") diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..2f27813 --- /dev/null +++ b/flake.nix @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: MIT AND Palimpsest-0.8 +# SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell +{ + description = "rescript-tea - The Elm Architecture for ReScript"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + # Node.js environment + nodejs_20 + nodePackages.npm + + # Development tools + just + git + + # Optional: for documentation + asciidoctor + ]; + + shellHook = '' + echo "rescript-tea development environment" + echo "Node.js: $(node --version)" + echo "npm: $(npm --version)" + echo "" + echo "Commands:" + echo " npm install - Install dependencies" + echo " npm run build - Build the project" + echo " npm run dev - Watch mode" + echo " just --list - Show available tasks" + ''; + }; + + # Build the project + packages.default = pkgs.buildNpmPackage { + pname = "rescript-tea"; + version = "0.1.0"; + src = ./.; + + npmDepsHash = ""; # Will be computed on first build + + buildPhase = '' + npm run build + ''; + + installPhase = '' + mkdir -p $out + cp -r lib $out/ + cp -r src $out/ + cp package.json $out/ + ''; + }; + } + ); +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..ec81e4b --- /dev/null +++ b/justfile @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: MIT AND Palimpsest-0.8 +# SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell +# +# justfile for rescript-tea +# See: https://just.systems/ + +# Default recipe - show help +default: + @just --list + +# Install dependencies +install: + npm install + +# Build the project +build: + npm run build + +# Build in watch mode +dev: + npm run dev + +# Clean build artifacts +clean: + rm -rf lib + rm -rf node_modules/.cache + +# Run tests +test: + npm test + +# Format code (if formatter is configured) +fmt: + @echo "Formatting not yet configured" + +# Lint code +lint: + @echo "Linting not yet configured" + +# Check types +typecheck: + npm run build + +# Full CI check +ci: clean install build test + +# Generate documentation +docs: + asciidoctor README.adoc -o docs/index.html + +# Audit dependencies for security issues +audit: + npm audit + +# Update dependencies +update: + npm update + +# Create a new release (maintainers only) +release version: + @echo "Creating release {{version}}" + npm version {{version}} + git push --follow-tags + +# Run RSR compliance audit +rsr-audit: + @echo "RSR Compliance Check" + @echo "====================" + @test -f README.adoc && echo "✓ README.adoc" || echo "✗ README.adoc" + @test -f LICENSE.txt && echo "✓ LICENSE.txt" || echo "✗ LICENSE.txt" + @test -f SECURITY.md && echo "✓ SECURITY.md" || echo "✗ SECURITY.md" + @test -f CODE_OF_CONDUCT.adoc && echo "✓ CODE_OF_CONDUCT.adoc" || echo "✗ CODE_OF_CONDUCT.adoc" + @test -f CONTRIBUTING.adoc && echo "✓ CONTRIBUTING.adoc" || echo "✗ CONTRIBUTING.adoc" + @test -f GOVERNANCE.adoc && echo "✓ GOVERNANCE.adoc" || echo "✗ GOVERNANCE.adoc" + @test -f CHANGELOG.md && echo "✓ CHANGELOG.md" || echo "✗ CHANGELOG.md" + @test -f flake.nix && echo "✓ flake.nix" || echo "✗ flake.nix" + @test -d .well-known && echo "✓ .well-known/" || echo "✗ .well-known/" diff --git a/src/Tea.res b/src/Tea.res index 837764b..ca141d0 100644 --- a/src/Tea.res +++ b/src/Tea.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" The Elm Architecture for ReScript. diff --git a/src/Tea.resi b/src/Tea.resi index 865ecfd..21ece65 100644 --- a/src/Tea.resi +++ b/src/Tea.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" The Elm Architecture for ReScript. ") diff --git a/src/Tea_App.res b/src/Tea_App.res index 85224f5..a0767da 100644 --- a/src/Tea_App.res +++ b/src/Tea_App.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" The TEA application runtime. Creates React components from app specifications. Handles the update loop, command execution, and subscription management. diff --git a/src/Tea_App.resi b/src/Tea_App.resi index 82c5c83..f6773ca 100644 --- a/src/Tea_App.resi +++ b/src/Tea_App.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" The TEA application runtime. Creates React components from app specifications. ") diff --git a/src/Tea_Cmd.res b/src/Tea_Cmd.res index 6823e13..d3c4223 100644 --- a/src/Tea_Cmd.res +++ b/src/Tea_Cmd.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Commands represent side effects to be performed by the runtime. They are pure descriptions of work to do, not the work itself. diff --git a/src/Tea_Cmd.resi b/src/Tea_Cmd.resi index 57629b5..356a18d 100644 --- a/src/Tea_Cmd.resi +++ b/src/Tea_Cmd.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Commands represent side effects to be performed by the runtime. They are pure descriptions of work to do, not the work itself. diff --git a/src/Tea_Html.res b/src/Tea_Html.res index 3b150f7..1564ade 100644 --- a/src/Tea_Html.res +++ b/src/Tea_Html.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Type-safe HTML helpers for TEA applications. This is a thin wrapper around React elements - users can also use JSX directly. diff --git a/src/Tea_Html.resi b/src/Tea_Html.resi index d82cdfc..a368660 100644 --- a/src/Tea_Html.resi +++ b/src/Tea_Html.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Type-safe HTML helpers for TEA applications. This is a thin wrapper around React elements - users can also use JSX directly. diff --git a/src/Tea_Json.res b/src/Tea_Json.res index 4216ea3..2c6a09d 100644 --- a/src/Tea_Json.res +++ b/src/Tea_Json.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Type-safe JSON decoding inspired by Elm's Json.Decode. Decoders are composable and provide helpful error messages with path information. diff --git a/src/Tea_Json.resi b/src/Tea_Json.resi index c1acf73..aedca2c 100644 --- a/src/Tea_Json.resi +++ b/src/Tea_Json.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Type-safe JSON decoding inspired by Elm's Json.Decode. Decoders are composable and provide helpful error messages with path information. diff --git a/src/Tea_Sub.res b/src/Tea_Sub.res index 368041c..97c1a82 100644 --- a/src/Tea_Sub.res +++ b/src/Tea_Sub.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Subscriptions declare external event sources the application listens to. They are diffed on each update - only changed subscriptions are re-subscribed. diff --git a/src/Tea_Sub.resi b/src/Tea_Sub.resi index 77abb56..450744e 100644 --- a/src/Tea_Sub.resi +++ b/src/Tea_Sub.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Subscriptions declare external event sources the application listens to. They are diffed on each update - only changed subscriptions are re-subscribed. diff --git a/src/Tea_Test.res b/src/Tea_Test.res index f4bc70e..f6f31a3 100644 --- a/src/Tea_Test.res +++ b/src/Tea_Test.res @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Testing utilities for TEA applications. Allows testing update functions in isolation without mounting components. diff --git a/src/Tea_Test.resi b/src/Tea_Test.resi index 6407ef3..5bd87a3 100644 --- a/src/Tea_Test.resi +++ b/src/Tea_Test.resi @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell + @@ocaml.doc(" Testing utilities for TEA applications. ") From 56d42de15b777aa3d0e2c9c049dad14e3606ac3d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 7 Dec 2025 14:54:55 +0000 Subject: [PATCH 2/2] docs: add project roadmap with MVP 1.0 checklist and future phases --- ROADMAP.adoc | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 ROADMAP.adoc diff --git a/ROADMAP.adoc b/ROADMAP.adoc new file mode 100644 index 0000000..944d8b5 --- /dev/null +++ b/ROADMAP.adoc @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: MIT AND Palimpsest-0.8 +// SPDX-FileCopyrightText: 2024 Jonathan D.A. Jewell += rescript-tea Roadmap +:toc: + +== Current Status: Alpha (v0.1.0) + +Core TEA implementation complete with basic subscriptions. + +== MVP 1.0 Release Checklist + +=== Must Have (P0) + +[cols="1,3,1"] +|=== +|Status |Item |Effort + +|[ ] +|Unit tests for core modules (Tea_Cmd, Tea_Sub, Tea_Json) +|Medium + +|[ ] +|Tea_Http module for HTTP requests +|Medium + +|[ ] +|HTTP example (fetch data, loading states, error handling) +|Small + +|[ ] +|Fix ReactDOM.Style.make deprecation warnings +|Small + +|[ ] +|npm publish configuration (package.json fields, .npmignore) +|Small + +|[ ] +|GitHub Actions CI (build, test, lint) +|Small + +|[ ] +|API documentation (generated from .resi files) +|Medium +|=== + +=== Should Have (P1) + +[cols="1,3,1"] +|=== +|Status |Item |Effort + +|[ ] +|Tea_Storage module (localStorage/sessionStorage) +|Small + +|[ ] +|Form example with validation +|Medium + +|[ ] +|TodoMVC example (proves complete CRUD) +|Medium + +|[ ] +|Performance benchmarks +|Small +|=== + +=== Nice to Have (P2) + +[cols="1,3,1"] +|=== +|Status |Item |Effort + +|[ ] +|Tea_Navigation (SPA routing) +|Large + +|[ ] +|Tea_Debug (time-travel debugger) +|Large + +|[ ] +|Tea_Ports (JS interop helpers) +|Medium +|=== + +== Phase 2: Ecosystem (v1.1 - v1.x) + +After MVP, expand the ecosystem: + +=== Modules + +* **Tea_Navigation** - Client-side routing + - URL parsing and building + - History API integration + - Route-based subscriptions + - Nested routes + +* **Tea_Http** enhancements + - Request cancellation + - Retry policies + - Caching strategies + - File uploads + +* **Tea_Storage** + - localStorage commands + - sessionStorage commands + - IndexedDB (maybe) + +* **Tea_Ports** + - Type-safe JS interop + - Port-based subscriptions + - Encoder/decoder helpers + +=== Examples + +* TodoMVC (complete CRUD) +* SPA with routing +* Real-time chat (WebSocket) +* Form wizard with validation + +== Phase 3: Advanced (v2.0+) + +=== Tea_Debug + +Time-travel debugging: + +* Message history +* State snapshots +* Replay functionality +* Export/import sessions +* Browser DevTools integration + +=== Tea_Animation + +Declarative animations: + +* Spring physics +* Keyframe animations +* Subscription-based animation loop +* Interruptible animations + +=== Tea_Test Enhancements + +* Property-based testing helpers +* Snapshot testing for views +* Command/subscription mocking +* Integration test utilities + +=== Performance + +* Virtual DOM diffing optimizations +* Lazy subscriptions +* Batched updates +* Memory profiling tools + +== Release Criteria + +=== Alpha (current) +- [x] Core modules compile +- [x] Basic example works +- [ ] Manual testing passes + +=== Beta (v0.9.0) +- [ ] All P0 items complete +- [ ] Unit test coverage > 70% +- [ ] CI passing +- [ ] npm installable + +=== Stable (v1.0.0) +- [ ] All P1 items complete +- [ ] Used in at least one real project +- [ ] API stable (no breaking changes expected) +- [ ] Documentation complete +- [ ] Community feedback incorporated + +== Contributing to the Roadmap + +See link:CONTRIBUTING.adoc[CONTRIBUTING] for how to propose changes. + +Priority is determined by: +1. User demand (issues, discussions) +2. Ecosystem gaps (what's blocking real-world use) +3. Maintainer capacity