From 9fdc469325ce8668509f2f4526e1931f443734e8 Mon Sep 17 00:00:00 2001 From: dab Date: Sat, 22 Nov 2025 23:54:22 +0000 Subject: [PATCH] Version 1.1.0 (see README for changelog) --- README.md | 62 +++++++----------------------------------------- backend/index.js | 48 +++++++++++++++++++++++++++++++++++-- package.json | 2 +- 3 files changed, 55 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 37b042c..d349490 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,14 @@ # 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 -Install framerock via Bun: +Install: ```bash 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 import { async_run } from 'framerock' @@ -61,30 +27,18 @@ await async_run({ }) ``` -Run the app: +Run server: ```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 +- **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** - `page_title` now defaults to "framerock app" - `client_id` now available inside `handle_transport_bytes` (added property to `utils` object) diff --git a/backend/index.js b/backend/index.js index 71ff524..28c5ce3 100644 --- a/backend/index.js +++ b/backend/index.js @@ -37,6 +37,30 @@ const wrap_transport_sendbytes = function (ws, msg_bytes) { 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 make_session_object = (ws) => { const map_obj = { @@ -94,7 +118,12 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend, han let str_js // wrap provided function in case it throws error try { - str_js = await jsbuild_app_frontend() + if (jsbuild_app_frontend !== undefined) { + str_js = await jsbuild_app_frontend() + } + else { + str_js = 'document.body.innerHTML = "Welcome to your framerock app!"' + } } catch (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 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 }, @@ -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 }]) if (handle_transport_bytes !== undefined) { const transport_send_bytes = wrap_transport_sendbytes.bind(null, ws) + const func_register_teardown = wrap_register_teardown.bind(null, ws.data.uid) 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) } catch (err) { diff --git a/package.json b/package.json index ad29767..174ddbe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framerock", - "version": "1.0.1", + "version": "1.1.0", "type": "module", "main": "backend/index.js" }