diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/README.md index 80523403..64a916a7 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/README.md +++ b/src/new/templates/rust/ui/hyperapp-skeleton/README.md @@ -12,20 +12,15 @@ Insert your app spec here, e.g.: Todo List with P2P Sync. A collaborative TODO list where items sync between nodes. -Write a spec, and then implement it step by step. Use the README.md given in hyperapp-skeleton to find instructions on specific details. -At the end, I should be able to run `kit bs —hyperapp` and manually test that the app works. +Write a spec, and then implement it step by step. +Use the README.md given in hyperapp-skeleton to find instructions on specific details. +At the end, I should be able to run `kit build --hyperapp && kit s` and manually test that the app works. ``` The rest of this document is aimed at *LLMs* not *humans*. ## Quick Start -### Prerequisites - -- Hyperware development environment (`kit` command) -- Rust toolchain -- Node.js and npm - ### Building Always build with @@ -61,7 +56,8 @@ hyperapp-skeleton/ ### 1. The Hyperprocess Macro -The `#[hyperprocess]` macro is the core of the Hyperapp framework. It provides: +The `#[hyperapp_macro::hyperapp]` macro is the core of the Hyperapp framework. +It provides: - Async/await support without tokio - Automatic WIT generation - State persistence @@ -79,7 +75,7 @@ async fn my_endpoint(&self) -> String { ``` #### Remote Requests -All remote requests must use `.expects_response(30)`, where the value 30 sets a 30‑second response timeout. +All remote requests must set `.expects_response(time)`, where `time` is the response timeout, in seconds. ```rust let req = Request::to(("friend.os", "some-hyperapp", "some-hyperapp", "publisher.os")) .expects_response(30) @@ -101,7 +97,8 @@ Parameters must be sent as tuples for multi-parameter methods: ``` #### Frontend keys in snake_case -All keys in TypeScript need to stay in snake_case (`node_id`), camelCase (`nodeId`) will break the app! +All keys in TypeScript need to stay in snake_case (e.g. `node_id`). +camelCase (e.g. `nodeId`) will break the app! ```typescript export interface StatusSnapshot { node_id: string; @@ -154,7 +151,7 @@ Add system permissions in `pkg/manifest.json`: "request_capabilities": [ "homepage:homepage:sys", "http-server:distro:sys", - "vfs:distro:sys" // Add as needed + "vfs:distro:sys" ] ``` @@ -182,47 +179,29 @@ If sending messages between nodes, set: - Check that the app is running in Hyperware environment ### WIT Generation Errors -- Use simple types or return JSON strings -- No HashMap (use Vec<(K,V)>) - No fixed arrays (use Vec) - Add #[derive(PartialEq)] to structs -### Import Errors -- Import the most important structs and functions from `hyperware_process_lib`, e.g. `Request`, `LazyLoadBlob`, `ProcessId` - -### manifest.json missing -- Run `kit b --hyperapp` to generate it - ### Naming Restrictions - No struct/enum/interface name is allowed to contain digits or the substring "stream", because WIT doesn't allow it - No record/variant/enum name is allowed to end with `Request`, `Response`, `RequestWrapper`, `ResponseWrapper`, because TS caller utils are autogenerated with those suffixes ## Instructions -### Create an implementation plan - Carefully read the prompt; look carefully at `instructions.md` (if it exists) and in the example-apps directory. -In particular, note the example applications `example-apps/sign/`, `example-apps/id/`, and `example-apps/file-explorer`. -Note that `file-explorer` example contains an `api` folder, which is generated by the compiler, and not human or LLM written. -`sign` and `id` demonstrate local messaging. -`file-explorer` demonstrates VFS interactions. - -Note in particular that bindings for the UI will be generated when the app is built with `kit build --hyperapp`. -As such, first design and implement the backend; the interface will be generated from the backend; finally design and implement the frontend to consume the interface. -Subsequent changes to the interface must follow this pattern as well: start in backend, generate interface, finish in frontend - -Do NOT create the API. -The API is machine generated. -You create types that end up in the API by defining and using them in functions in the Rust backend "hyperapp" -### Implement the plan +In `example-apps/`: +- `sign` and `id` demonstrate local messaging. +- `file-explorer` demonstrates VFS interactions. + The `file-explorer` example contains an `api` folder, which is generated by the compiler, and not human or LLM written. -Look carefully at `IMPLEMENTATION_PLAN.md` and in the `example-apps/` directory, if relevant. +Look at the `hyperware_process_lib` and in particular at `hyperapp`. +If not provided with local repo look at https://github.com/hyperware-ai/process_lib/tree/main/src and https://raw.githubusercontent.com/hyperware-ai/process_lib/refs/heads/main/src/hyperapp.rs -Work from the existing template that exists at `hyperapp-skeleton/` and `ui/`. +Work from the existing template, whose backend lives at `my-app-name/` and frontend at `ui/`. -Note in particular that bindings for the UI will be generated when the app is built with `kit build --hyperapp`. -As such, first design and implement the backend; the interface will be generated from the backend; finally design and implement the frontend to consume the interface. +Bindings for the UI will be generated when the app is built with `kit build --hyperapp`. +Thus, first design and implement the backend; the interface will be generated from the backend; finally design and implement the frontend to consume the interface. Subsequent changes to the interface must follow this pattern as well: start in backend, generate interface, finish in frontend Do NOT create the API. @@ -231,10 +210,11 @@ You create types that end up in the API by defining and using them in functions When sending p2p always send using `hyperware_process_lib::hyperapp::send` or `hyperware_process_lib::hyperapp::send_rmp` when expecting a Response, never `.send_and_await_response`. Example usage of `send` and `send_rmp` are shown in example_apps. -If not expecting a response, use `.send`. +If not expecting a response, use `hyperware_process_lib::Request::send`. -Do not worry about serialization/deserialization when using `send` and `send_rmp` functions for p2p communication. -Notice that this all happens within those functions: just take the rust types as args and return rust types as return values. +`send` and `send_rmp` handle (de)serialization. +Just put Rust types as args and return Rust types as return values. +Do not return String or JSON if a more specific Rust type will work. If you create a GUI for the app you MUST use target/ui/caller-utils.ts for HTTP requests to the backend. Do NOT edit this file: it is machine generated. diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml index 884c9dee..33fefce8 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml @@ -1,20 +1,17 @@ [dependencies] anyhow = "1.0" md5 = "0.7" +hyperapp_macro = "0.1.1" process_macros = "0.1" serde_json = "1.0" serde_urlencoded = "0.7" tracing = "0.1.37" wit-bindgen = "0.42.1" -[dependencies.hyperprocess_macro] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "66884c0" - [dependencies.hyperware_process_lib] features = ["hyperapp"] git = "https://github.com/hyperware-ai/process_lib" -rev = "4beff93" +rev = "1a6ad9d" [dependencies.serde] features = ["derive"] diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs index ec0b85a6..cbcfb541 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs @@ -1,4 +1,3 @@ -use hyperprocess_macro::hyperprocess; use hyperware_process_lib::hyperapp::{add_response_header, get_path, send, SaveOptions}; use hyperware_process_lib::logging::{debug, error, info, init_logging, Level}; use hyperware_process_lib::our; @@ -11,7 +10,6 @@ const ICON: &str = include_str!("./icon"); const PROCESS_ID_LINK: &str = "explorer:file-explorer:sys"; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] pub struct FileInfo { pub name: String, pub path: String, @@ -36,7 +34,7 @@ struct FileExplorerState { cwd: String, } -#[hyperprocess( +#[hyperapp_macro::hyperapp( name = "file-explorer", ui = Some(HttpBindingConfig::default().secure_subdomain(true)), endpoints = vec![ diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml index bf793365..a9ce9aa5 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml @@ -6,6 +6,6 @@ panic = "abort" [workspace] members = [ "id", - "target/caller-utils", + "target/id-caller-util?", ] resolver = "2" diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md index b0027a8a..2e2534d2 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md @@ -1,3 +1,11 @@ # id For use with https://github.com/nick1udwig/sign + +## Building + +``` +kit b --hyperapp -l ~/git/sign --features "caller-utils" +``` + +Or some other way of providing the sign dep (e.g. by loading `sign` onto a node running at 8080). diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml index 81a3cd48..54be75ac 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml @@ -1,26 +1,25 @@ [dependencies] anyhow = "1.0.97" +hyperapp_macro = "0.2.0" process_macros = "0.1" rmp-serde = "1.3.0" serde_json = "1.0" -wit-bindgen = "0.36.0" +wit-bindgen = "0.42.1" -[dependencies.caller-utils] -path = "../target/caller-utils" +[dependencies.hyperware_process_lib] +features = ["hyperapp"] +version = "3.0.0" -[dependencies.hyperprocess_macro] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "4c944b2" - -[dependencies.hyperware_app_common] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "4c944b2" +[dependencies.id_caller_utils] +optional = true +path = "../target/id-caller-utils" [dependencies.serde] features = ["derive"] version = "1.0" [features] +caller-utils = ["id_caller_utils"] simulation-mode = [] [lib] diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs index 8c40a479..3c9ed3eb 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs @@ -1,5 +1,4 @@ -use caller_utils::sign::{sign_local_rpc, verify_local_rpc}; -use hyperprocess_macro::hyperprocess; +use id_caller_utils::sign_app::{sign_local_rpc, verify_local_rpc}; use hyperware_process_lib::logging::{init_logging, Level}; use hyperware_process_lib::Address; @@ -10,7 +9,7 @@ fn make_sign_sys() -> Address { Address::new("our", ("sign", "sign", "sys")) } -#[hyperprocess( +#[hyperapp_macro::hyperapp( name = "id", ui = Some(HttpBindingConfig::default()), endpoints = vec![ @@ -23,7 +22,7 @@ fn make_sign_sys() -> Address { config: WsBindingConfig::default(), } ], - save_config = SaveOptions::Never, + save_config = hyperware_process_lib::hyperapp::SaveOptions::Never, wit_world = "id-sys-v0", )] impl IdState { diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx index d994bb5c..04cb7e46 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react"; import HyperwareClientApi from "@hyperware-ai/client-api"; import "./App.css"; import useIdStore from "./store/id"; -import { sign, verify, ApiError } from "../../target/ui/caller-utils"; +import { Id, ApiError } from "../../target/ui/caller-utils"; const BASE_URL = import.meta.env.BASE_URL; if (window.our) window.our.process = BASE_URL?.replace("/", ""); @@ -61,7 +61,7 @@ function App() { // Send a message to the node via the sign function try { - const signature = await sign(messageArray); + const signature = await Id.sign(messageArray); // Add the message and its signature to the store addSignedMessage(message, signature); @@ -89,7 +89,7 @@ function App() { // Send a verification request via the verify function try { - const isValid = await verify(messageArray, signedMessage.signature); + const isValid = await Id.verify(messageArray, signedMessage.signature); // Update the verification status in the store updateVerificationStatus(index, isValid); diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml index 859185a7..6a90e8f9 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml @@ -6,6 +6,6 @@ panic = "abort" [workspace] members = [ "sign", - "target/caller-utils", + "target/sign-caller-util?", ] resolver = "2" diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml index d0c792a8..63b993d5 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml @@ -1,26 +1,25 @@ [dependencies] anyhow = "1.0.97" +hyperapp_macro = "0.2.0" process_macros = "0.1" rmp-serde = "1.1.2" serde_json = "1.0" -wit-bindgen = "0.36.0" +wit-bindgen = "0.42.1" -[dependencies.caller-utils] -path = "../target/caller-utils" - -[dependencies.hyperprocess_macro] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "4c944b2" - -[dependencies.hyperware_app_common] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "4c944b2" +[dependencies.hyperware_process_lib] +features = ["hyperapp"] +version = "3.0.0" [dependencies.serde] features = ["derive"] version = "1.0" +[dependencies.sign_caller_utils] +optional = true +path = "../target/sign-caller-utils" + [features] +caller-utils = ["sign_caller_utils"] simulation-mode = [] [lib] diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs index 3f8fae06..08d83a5f 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs +++ b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs @@ -4,11 +4,10 @@ use hyperware_process_lib::logging::{init_logging, Level}; use hyperware_process_lib::net::{NetAction, NetResponse}; use hyperware_process_lib::{last_blob, our, LazyLoadBlob, Request}; -use hyperware_app_common::{send_rmp, source}; -use hyperprocess_macro::hyperprocess; +use hyperware_process_lib::hyperapp::{send_rmp, source}; #[derive(Default, Debug, serde::Serialize, serde::Deserialize)] -struct SignState {} +struct SignAppState {} async fn sign(message: Vec) -> anyhow::Result> { let message = make_message(&message); @@ -69,14 +68,14 @@ fn make_message(bytes: &Vec) -> Vec { [source().to_string().as_bytes(), &bytes].concat() } -#[hyperprocess( +#[hyperapp_macro::hyperapp( name = "sign", ui = None, endpoints = vec![], - save_config = SaveOptions::Never, - wit_world = "sign-sys-v0", + save_config = hyperware_process_lib::hyperapp::SaveOptions::Never, + wit_world = "sign-app-sys-v0", )] -impl SignState { +impl SignAppState { #[init] async fn init(&mut self) { init_logging(Level::DEBUG, Level::INFO, None, None, None).unwrap(); diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/Cargo.toml index 78b1033e..c69056d4 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/Cargo.toml +++ b/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/Cargo.toml @@ -1,5 +1,6 @@ [dependencies] anyhow = "1.0" +hyperapp_macro = "0.2.0" process_macros = "0.1" serde_json = "1.0" wit-bindgen = "0.42.1" @@ -8,14 +9,9 @@ wit-bindgen = "0.42.1" optional = true path = "../target/hyperapp-skeleton-caller-utils" -[dependencies.hyperprocess_macro] -git = "https://github.com/hyperware-ai/hyperprocess-macro" -rev = "aaf7e02" - [dependencies.hyperware_process_lib] +version = "3.0.0" features = ["hyperapp"] -git = "https://github.com/hyperware-ai/process_lib" -rev = "e8b065179ce5d15893a23142416e59c87e0f31f6" [dependencies.serde] features = ["derive"] diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/src/lib.rs index 8bf80f0a..55623e66 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/src/lib.rs +++ b/src/new/templates/rust/ui/hyperapp-skeleton/hyperapp-skeleton/src/lib.rs @@ -2,14 +2,6 @@ // This is a minimal, well-commented skeleton app for the Hyperware platform // using the Hyperapp framework (macro-driven approach). -// CRITICAL IMPORTS - DO NOT MODIFY THESE -// The hyperprocess_macro provides everything you need including: -// - Async/await support (custom runtime) -// - Automatic WIT (WebAssembly Interface Types) generation -// - State persistence -// - HTTP/WebSocket bindings -use hyperprocess_macro::hyperprocess; - use hyperware_process_lib::{homepage::add_to_homepage, our, println}; use serde::{Deserialize, Serialize}; @@ -33,8 +25,13 @@ pub struct Status { } // STEP 2: IMPLEMENT YOUR APP LOGIC -// The #[hyperprocess] attribute goes HERE, before the impl block -#[hyperprocess( + +// The hyperapp_macro provides: +// - Async/await support (custom runtime) +// - Automatic WIT (WebAssembly Interface Types) generation +// - State persistence +// - HTTP/WebSocket bindings +#[hyperapp_macro::hyperapp( name = "HyperappSkeleton App", ui = Some(hyperware_process_lib::http::server::HttpBindingConfig::default()), endpoints = vec![ @@ -43,7 +40,7 @@ pub struct Status { config: hyperware_process_lib::http::server::HttpBindingConfig::new(false, false, false, None), }, ], - save_config = hyperware_process_lib::hyperapp::SaveOptions::EveryMessage, + save_config = hyperware_process_lib::hyperapp::SaveOptions::OnDiff, wit_world = "hyperapp-skeleton-template-dot-os-v0" )] impl AppState { @@ -58,16 +55,16 @@ impl AppState { // Add your app to the Hyperware homepage // Parameters: name, icon, path, widget add_to_homepage("HyperappSkeleton App", Some(ICON), Some("/"), None); - + // Initialize your app state self.counter = 0; self.messages.push("App initialized!".to_string()); - + // Get our node identity (useful for P2P apps) let our_node = our().node.clone(); println!("HyperappSkeleton app initialized on node: {}", our_node); } - + // HTTP ENDPOINT EXAMPLE #[local] #[http] @@ -78,7 +75,7 @@ impl AppState { node: our().node.clone(), }) } - + // HTTP ENDPOINT WITH PARAMETERS // Frontend sends parameters as either: // - Single value: { "MethodName": value } @@ -92,23 +89,23 @@ impl AppState { Ok(self.counter) } - + // HTTP ENDPOINT RETURNING COMPLEX DATA // For complex types, return as JSON string to avoid WIT limitations #[local] #[http] async fn get_messages(&self) -> Result, String> { Ok(self.messages.clone()) - } + } } // WIT TYPE COMPATIBILITY NOTES: -// The hyperprocess macro generates WebAssembly Interface Types from your code. +// The hyperapp macro generates WebAssembly Interface Types from your code. // Supported types: // ✅ Primitives: bool, u8-u64, i8-i64, f32, f64, String // ✅ Vec where T is supported -// ✅ Option where T is supported +// ✅ Option where T is supported // ✅ Simple structs with public fields // ❌ HashMap - use Vec<(K,V)> instead // ❌ Fixed arrays [T; N] - use Vec diff --git a/src/new/templates/ui/hyperapp-skeleton/ui/index.html b/src/new/templates/ui/hyperapp-skeleton/ui/index.html index a8805baf..4ac4c16f 100644 --- a/src/new/templates/ui/hyperapp-skeleton/ui/index.html +++ b/src/new/templates/ui/hyperapp-skeleton/ui/index.html @@ -4,11 +4,21 @@ - + - HyperappSkeleton App - Hyperware + Hyperapp Skeleton - Hyperware + + + + + + + + + +