Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 22 additions & 42 deletions src/new/templates/rust/ui/hyperapp-skeleton/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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"
]
```

Expand Down Expand Up @@ -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<T>)
- 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.
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand All @@ -36,7 +34,7 @@ struct FileExplorerState {
cwd: String,
}

#[hyperprocess(
#[hyperapp_macro::hyperapp(
name = "file-explorer",
ui = Some(HttpBindingConfig::default().secure_subdomain(true)),
endpoints = vec![
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ panic = "abort"
[workspace]
members = [
"id",
"target/caller-utils",
"target/id-caller-util?",
]
resolver = "2"
Original file line number Diff line number Diff line change
@@ -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).
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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![
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("/", "");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ panic = "abort"
[workspace]
members = [
"sign",
"target/caller-utils",
"target/sign-caller-util?",
]
resolver = "2"
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>) -> anyhow::Result<Vec<u8>> {
let message = make_message(&message);
Expand Down Expand Up @@ -69,14 +68,14 @@ fn make_message(bytes: &Vec<u8>) -> Vec<u8> {
[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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"]
Expand Down
Loading