Version 1.1.0 (see README for changelog)
This commit is contained in:
parent
ca1a993b31
commit
9fdc469325
3 changed files with 55 additions and 57 deletions
62
README.md
62
README.md
|
|
@ -1,48 +1,14 @@
|
||||||
# framerock
|
# framerock
|
||||||
|
|
||||||
*minimalist framework for realtime, AI-Ready web apps*
|
|
||||||
|
|
||||||
## about
|
|
||||||
|
|
||||||
framerock is a lightweight framework that enables developers to build client-server JavaScript applications with minimal boilerplate. It abstracts away web server infrastructure and WebSocket management, focusing purely on the core logic of your app.
|
|
||||||
|
|
||||||
## features
|
|
||||||
|
|
||||||
framerock provides a simple, secure foundation for real-time web applications. It handles the full lifecycle of a web server, including serving static resources and managing bidirectional communication via WebSocket. The framework is designed for speed, clarity, and ease of integration.
|
|
||||||
|
|
||||||
| Feature | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| Zero-dependency | Runs entirely on the Bun runtime with no external packages required |
|
|
||||||
| Real-time bidirectional communication | Uses raw bytes for transport, enabling full control over message format and encoding |
|
|
||||||
| Automatic PING/PONG | Maintains active connections with configurable timeouts and intervals |
|
|
||||||
| Error resilience | Wraps provided functions in try/catch blocks to prevent crashes during runtime |
|
|
||||||
|
|
||||||
## AI-Ready
|
|
||||||
|
|
||||||
framerock is specifically engineered to work seamlessly with AI tools and agent-based workflows. By minimizing surface area and eliminating boilerplate, it reduces context overhead and allows models to focus on application logic. This makes it ideal for iterative development, prompt engineering, and generating or reviewing agent behaviors.
|
|
||||||
|
|
||||||
| Benefit | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| Reduced context noise | LLMs receive only the relevant code and logic, not server setup or routing details |
|
|
||||||
| Faster iteration | Small, focused codebases enable rapid prototyping and feedback loops |
|
|
||||||
| Direct compatibility | Can be used as both input and output for LLMs without intermediate parsing or transformation |
|
|
||||||
| Safe for evaluation | Raw bytes are passed directly to backend functions, avoiding unsafe string parsing or injection risks |
|
|
||||||
|
|
||||||
## when to use
|
|
||||||
|
|
||||||
- when building a real-time single-page application with low-latency client-server interaction
|
|
||||||
- when you want to leverage AI tools to generate or review frontend or backend code without entangling it in web server logic
|
|
||||||
- when you need a lightweight, portable, and zero-dependency foundation for experimentation or deployment
|
|
||||||
|
|
||||||
## getting started
|
## getting started
|
||||||
|
|
||||||
Install framerock via Bun:
|
Install:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun add 'git+https://git.daemons.my/dab/framerock.git'
|
bun add 'git+https://git.daemons.my/dab/framerock.git'
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a file named `your-app-entrypoint.js` with the following content:
|
Create `your-app-entrypoint.js`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { async_run } from 'framerock'
|
import { async_run } from 'framerock'
|
||||||
|
|
@ -61,30 +27,18 @@ await async_run({
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the app:
|
Run server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run ./your-app-entrypoint.js
|
bun run your-app-entrypoint.js
|
||||||
```
|
```
|
||||||
|
|
||||||
The app will start at `http://127.0.0.1:8800`. Open the page in a browser to see the real-time interaction between client and server.
|
|
||||||
|
|
||||||
## codebase overview
|
|
||||||
|
|
||||||
| file | purpose |
|
|
||||||
|------|---------|
|
|
||||||
| `backend/index.js` | Implements the server-side logic including web server setup, route handling, WebSocket lifecycle management, and dynamic script generation |
|
|
||||||
| `frontend/index.js` | Provides client-side utility functions for managing WebSocket connections and safely executing frontend code via a custom eval wrapper |
|
|
||||||
|
|
||||||
## why framerock
|
|
||||||
|
|
||||||
- eliminates the need to write or maintain repetitive web server or routing code
|
|
||||||
- enables developers to focus on application logic rather than infrastructure
|
|
||||||
- supports rapid iteration and experimentation, especially when paired with AI tools
|
|
||||||
- provides a clean, transparent, and portable foundation that works across environments
|
|
||||||
|
|
||||||
## changelog
|
## changelog
|
||||||
|
|
||||||
|
- **Version 1.1.0**
|
||||||
|
- added `func_register_teardown`, made `jsbuild_app_frontend` optional
|
||||||
|
- **Version 1.0.1**
|
||||||
|
- added `handle_fetch_fallback`
|
||||||
- **Version 1.0.0**
|
- **Version 1.0.0**
|
||||||
- `page_title` now defaults to "framerock app"
|
- `page_title` now defaults to "framerock app"
|
||||||
- `client_id` now available inside `handle_transport_bytes` (added property to `utils` object)
|
- `client_id` now available inside `handle_transport_bytes` (added property to `utils` object)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,30 @@ const wrap_transport_sendbytes = function (ws, msg_bytes) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAP_TEARDOWNS = new Map()
|
||||||
|
|
||||||
|
const wrap_register_teardown = function (client_id, func_teardown) {
|
||||||
|
|
||||||
|
const uid_teardown = crypto.randomUUID()
|
||||||
|
|
||||||
|
let obj_teardowns = MAP_TEARDOWNS.get(client_id)
|
||||||
|
if (obj_teardowns === undefined) {
|
||||||
|
obj_teardowns = {}
|
||||||
|
MAP_TEARDOWNS.set(client_id, obj_teardowns)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_teardowns[uid_teardown] = func_teardown
|
||||||
|
|
||||||
|
const func_unregister_teardown = () => {
|
||||||
|
//console.info(['UNREGISTER TEARDOWN', { client_id, uid_teardown }])
|
||||||
|
delete obj_teardowns[uid_teardown]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return func_unregister_teardown
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const start_server = function ({ context_meta, config, jsbuild_app_frontend, handle_transport_bytes, handle_fetch_fallback }) {
|
const start_server = function ({ context_meta, config, jsbuild_app_frontend, handle_transport_bytes, handle_fetch_fallback }) {
|
||||||
const make_session_object = (ws) => {
|
const make_session_object = (ws) => {
|
||||||
const map_obj = {
|
const map_obj = {
|
||||||
|
|
@ -94,8 +118,13 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend, han
|
||||||
let str_js
|
let str_js
|
||||||
// wrap provided function in case it throws error
|
// wrap provided function in case it throws error
|
||||||
try {
|
try {
|
||||||
|
if (jsbuild_app_frontend !== undefined) {
|
||||||
str_js = await jsbuild_app_frontend()
|
str_js = await jsbuild_app_frontend()
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
str_js = 'document.body.innerHTML = "Welcome to your framerock app!"'
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(['error during JS build', err])
|
console.error(['error during JS build', err])
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +198,20 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend, han
|
||||||
// cleanup from this context
|
// cleanup from this context
|
||||||
context_meta.ws_map.delete(ws.data.uid)
|
context_meta.ws_map.delete(ws.data.uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=========================================//
|
||||||
|
// NOTE: in ALL close() cases, run the teardown logic
|
||||||
|
|
||||||
|
const obj_teardowns = MAP_TEARDOWNS.get(ws.data.uid)
|
||||||
|
if (obj_teardowns !== undefined) {
|
||||||
|
for (let [ uid_teardown, func_teardown ] of Object.entries(obj_teardowns)) {
|
||||||
|
func_teardown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MAP_TEARDOWNS.delete(ws.data.uid)
|
||||||
|
//=========================================//
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
|
|
@ -177,8 +220,9 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend, han
|
||||||
console.info(['ws message', ws.data.uid, { size: m_bytes }])
|
console.info(['ws message', ws.data.uid, { size: m_bytes }])
|
||||||
if (handle_transport_bytes !== undefined) {
|
if (handle_transport_bytes !== undefined) {
|
||||||
const transport_send_bytes = wrap_transport_sendbytes.bind(null, ws)
|
const transport_send_bytes = wrap_transport_sendbytes.bind(null, ws)
|
||||||
|
const func_register_teardown = wrap_register_teardown.bind(null, ws.data.uid)
|
||||||
try {
|
try {
|
||||||
const utils = { client_id: ws.data.uid, transport_send_bytes }
|
const utils = { client_id: ws.data.uid, transport_send_bytes, func_register_teardown }
|
||||||
handle_transport_bytes(message, utils)
|
handle_transport_bytes(message, utils)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "framerock",
|
"name": "framerock",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "backend/index.js"
|
"main": "backend/index.js"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue