- Notifications
You must be signed in to change notification settings - Fork0
Resumability JavaScript Framework
License
async-framework/async-framework
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A lightweight, signal-based framework for building reactive web applications with custom elements and async handlers.
- Signals: Reactive state management
- Custom Elements: Web components with async capabilities
- Event Handlers: Async event handling with dynamic imports
- JSX Support: Optional JSX/TSX support for component creation
Component | Status | Description |
---|---|---|
AsyncLoader | Stable-ish | Core async loading functionality for handlers and modules |
HandlerRegistry | Stable-ish | Event handler registration and management system |
Framework Core | Unstable | Core framework features and utilities |
JSX Runtime | Unstable | JSX/TSX support and rendering (under development) |
Signals | Experimental | Reactive state management (API may change) |
Signal-List | Experimental | A signal-list primitive to optimize rendering lists |
Signal-Table | Experimental | A signal-table primitive to optimize rendering tables |
Custom Elements | Experimental | Web Components integration and lifecycle management |
Templates | Experimental | HTML template handling and instantiation |
QwikLoader | Experimental | Replace QwikLoader with AsyncLoader |
Signals are reactive state containers that automatically track dependencies and update subscribers:
import{signal,computed}from'@async/framework';// Create a basic signalconstcount=signal(0);// Read and write to signalconsole.log(count.value);// 0count.value=1;// Create a computed signalconstdoubled=computed(()=>count.value*2);
Create reactive web components using signals:
// counter-element.jsimport{signal}from'@async/framework';exportclassCounterElementextendsHTMLElement{constructor(){super();this.count=signal(0);}connectedCallback(){this.innerHTML=/*html*/` <button on:click="./handlers/increment.js">Count:${this.count.value}</button> `;// Auto-update view when signal changesconstbuttonEl=this.querySelector('button');this.count.subscribe(newValue=>{buttonEl.textContent=`Count:${newValue}`;});}}// in maincustomElements.define('counter-element',CounterElement);
Event handlers can be loaded asynchronously and chained:
HTML:
<!-- Multiple handlers separated by commas --><buttonon:click="./handlers/validate.js, ./handlers/submit.js"> Submit</button><!-- Handler with specific export --><divon:dragover="./handlers/drag.js#onDragover"> Drag here</div>
Handler files:
// handlers/validate.jsexportfunctionhandler(context){const{ event, element}=context;if(!element.value){context.break();// Prevents next handlers from executingreturnfalse;}}// handlers/submit.jsexportasyncfunctionhandler(context){const{ event, element}=context;constresult=awaitsubmitData(element.value);returnresult;}
Create components using JSX/TSX:
// Counter.tsximport{signal}from'@async/framework';exportfunctionCounter(){constcount=signal(0);return(<div><h1>Count:{count}</h1><buttonon:click={()=>count.value++}> Increment</button></div>);}
Here's a complete example combining all features:
index.html:
<!DOCTYPE html><htmllang="en"><head><title>Async Framework Demo</title></head><body><divdata-container="root"><todo-app></todo-app></div><scripttype="module">import{render}from"@async/framework";import{TotoApp}from"./TodoApp.js";// Register the custom elementcustomElements.define("todo-app",TotoApp);// Render the component into the containerrender(document.querySelector("todo-app"){root:document.querySelector('[data-container="root"]'),// events in the appevents:["click","keyup"],},);</script></body></html>
TodoApp.js:
import{ContextWrapper,html,signal,each,wrapContext}from"@async/framework";exportclassTodoAppextendsHTMLElement{privatewrapper:ContextWrapper;privatetodos;privateinputValue;constructor(){super();this.wrapper=wrapContext(this,()=>{this.todos=signal<string[]>([]);this.inputValue=signal("");});}createTemplate(){consttemplate=html`<divclass="p-6 bg-white rounded-lg shadow-md"><divclass="mb-4 flex gap-2"><inputtype="text"class="flex-1 px-4 py-2 border rounded"value="${this.inputValue}"on:keyup="./handlers/input.js"><buttonclass="px-4 py-2 bg-indigo-600 text-white rounded"on:click="./handlers/add-todo.js, ./handlers/clear-input.js"> Add Todo</button></div><ulclass="space-y-2">${each(this.todos,(todo)=>html`<liclass="flex items-center justify-between p-2 border rounded"><span>${todo}</span><buttonclass="px-2 py-1 bg-red-500 text-white rounded"on:click="./handlers/remove-todo.js"> Remove</button></li> `)}</ul></div> `;returntemplate;}connectedCallback(){this.wrapper.render(()=>this.createTemplate());}disconnectedCallback(){this.wrapper.cleanup();}}
Handlers:
// handlers/input.jsexportfunctionhandler(context){const{ element}=context;constcomponent=element.closest("todo-app");component.inputValue.value=element.value;}// handlers/add-todo.jsexportfunctionhandler(context){const{ element}=context;constcomponent=element.closest("todo-app");constnewTodo=component.inputValue.value.trim();if(newTodo){component.todos.value=[...component.todos.value,newTodo];}}// handlers/clear-input.jsexportfunctionhandler(context){const{ element}=context;constcomponent=element.closest("todo-app");component.inputValue.value='';context.element.querySelector('input').value='';}
- 🔄 Reactive signals for state management
- ⚡ Async event handlers with dynamic imports
- 🧩 Web Components integration
- ⚛️ Optional JSX support
- 🔌 Pluggable architecture
- 📦 No build step required
- 🪶 Lightweight and performant
- Keep handlers small and focused
- Use signals for shared state
- Leverage async handlers for complex operations
- Break down components into smaller, reusable pieces
- Use computed signals for derived state
packages/ examples/ # Example applications async-loader/ # Core async loading functionality dev/ # Development server custom-element-signals/ # Custom element integration
- Clone the repository
- Install Deno if not already installed
- Run example apps:deno task start
Visithttp://localhost:8000 to see the examples in action.
Use this prompt to help AI assistants understand how to work with this framework:
I'm using a custom web framework with the following characteristics:
- It's built for Deno and uses TypeScript/JavaScript
- Components should preferably be created using JSX/TSX (though Custom Elements are supported)
- State management uses Signals (reactive state containers)
- Event handling uses async handlers loaded dynamically
BASIC SETUP:
- Create an index.html with this structure:
<!DOCTYPE html><html><head><title>App</title></head><body><divid="app"></div><scripttype="module">import{render}from'@async/framework';import{App}from'./App.tsx';// Bootstrap the applicationrender(<App/>,document.getElementById('app'));</script></body></html>
JSX COMPONENTS (Preferred Method):
- Create components in .tsx files
- Use signals for state management
Example App.tsx:
import{signal}from'@async/framework';exportfunctionApp(){constcount=signal(0);return(<div><h1>Count:{count}</h1><buttonon:click="./handlers/increment.js">Add</button></div>);}
EVENT HANDLING:
- Events are handled using file paths in on: attributes
- Multiple handlers can be chained with commas
- Handlers receive a context object with:
{event,// Original DOM eventelement,// Target elementdispatch(),// Dispatch custom eventsvalue,// Passed between chained handlers// helperseventName,// Name of the eventattrValue,// Original attribute valuehandlers,// Handler registrysignals,// Signal registrytemplates,// Tenplate registrycontainer,// Container element// TODO: component, // Component refmodule,// Module file instance of the handlercanceled,// If we canceled the chained handlersbreak(),// break out of chained handlers// mimic EventpreventDefault(),stopPropagation(),target,}
Handler Patterns:
- Default Export:
// handlers/submit.js// typeof module.default === 'function'exportdefaultfunction(context){// Used when no specific method is referenced}
- Named Event Handler:
// handlers/form.js// "submit" -> "on" + capitalize("submit")exportfunctiononSubmit(context){// Automatically matched when event name is "submit"}
- Hash-Referenced Export:
// handlers/drag.jsexportfunctionmyCustomNamedHandler(context){}exportfunctiononDragend(context){}// Use hash to target specific export<divon:drag="./handlers/drag.js#myCustomNamedHandler"/>// dragend will resolve to onDragend<divon:dragend="./handlers/drag.js"/>
- Inline Function (JSX):
<buttononClick={(context)=>{console.log('Clicked!',context);}}>
Examples:
<!-- Chain multiple handler files --><buttonon:click="./handlers/validate.js, ./handlers/submit.js"> Submit</button><!-- Target specific export with hash --><divon:dragover="./handlers/drag.js#onDragover"> Drop Zone</div><!-- Use event-named export --><formon:submit="./handlers/form.js"><!-- handler will use onSubmit export --></form>
Handler Context:
{event,// Original DOM eventelement,// Target elementdispatch(),// Dispatch custom eventsvalue,// Passed between chained handlers// helperseventName,// Name of the eventattrValue,// Original attribute valuehandlers,// Handler registrysignals,// Signal registrytemplates,// Tenplate registrycontainer,// Container element// TODO: component, // Component refmodule,// Module file instance of the handlercanceled,// If we canceled the chained handlersbreak(),// break out of chained handlers// mimic EventpreventDefault(),stopPropagation(),target,}
Control Flow:
- Invoke context.break() to stop handler chain (rarely needed)
- Return values are passed to next handler via context.value
SIGNALS:
- Used for reactive state management
- Created using signal(initialValue)
- Access value with .value
- Can be computed using computed(() => ...)
- Separating get and set using createSignal(initialValue)
- Access value with [get, set] = createSignal()Example:
constcount=signal(0);count.value++;// Updates all subscribersconstdoubled=computed(()=>count.value*2);// passing around get and setconst[getCount,setCount]=createSignal(0);setCount(getCount()+1);// Updates all subscribersconstdoubled=computed(()=>getCount*2);
FILE STRUCTURE:
project/ ├── index.html ├── App.tsx ├── components/ │ └── Counter.tsx └── handlers/ ├── increment.js └── submit.js
When working with this framework, please follow these conventions and patterns. The framework emphasizes clean separation of concerns, reactive state management, and async event handling.
END PROMPT
About
Resumability JavaScript Framework
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.