From cd3ccdfabc01d5bd3c3b6409e63df8c90a75ef57 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sat, 14 Feb 2026 03:28:14 +0000 Subject: [PATCH 1/7] feat: embed Datastar JS bundle, serve via --datastar flag Embeds the Datastar 1.0.0-RC.7 JS bundle in the binary. When --datastar is passed, requests to /datastar@1.0.0-RC.7.js are intercepted before the user closure and served with immutable cache headers and brotli compression. --- README.md | 15 +++ examples/datastar-sdk/serve.nu | 2 +- examples/quotes/serve.nu | 2 +- src/handler.rs | 53 +++++++++- src/main.rs | 12 ++- src/stdlib/datastar/datastar@1.0.0-RC.7.js | 9 ++ src/stdlib/datastar/mod.nu | 1 + src/test_handler.rs | 114 +++++++++++++++------ www/serve.nu | 2 +- 9 files changed, 170 insertions(+), 40 deletions(-) create mode 100644 src/stdlib/datastar/datastar@1.0.0-RC.7.js diff --git a/README.md b/README.md index 23e93b6..205a700 100644 --- a/README.md +++ b/README.md @@ -879,6 +879,21 @@ Generate [Datastar](https://data-star.dev) SSE events for hypermedia interactions. Follows the [SDK ADR](https://github.com/starfederation/datastar/blob/develop/sdk/ADR.md). +Use `--datastar` to serve the embedded JS bundle at `$DATASTAR_JS_PATH` +(`/datastar@1.0.0-RC.7.js`) with immutable cache headers: + +```bash +$ http-nu --datastar :3001 -c '{|req| + use http-nu/datastar *; use http-nu/html * + HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY + (DIV {"data-signals": "{count: 0}"} + (SPAN {"data-text": "$count"} "0") + (BUTTON {"data-on:click": "@post('/inc')"} "+1") + ) + ) +}' +``` + Commands return records that pipe to `to sse` for streaming output. ```nushell diff --git a/examples/datastar-sdk/serve.nu b/examples/datastar-sdk/serve.nu index 1bf168d..96a1ed2 100644 --- a/examples/datastar-sdk/serve.nu +++ b/examples/datastar-sdk/serve.nu @@ -13,7 +13,7 @@ use http-nu/html * HEAD (META {charset: "UTF-8"}) (TITLE "Datastar SDK Demo") - (SCRIPT {type: "module" src: $DATASTAR_CDN_URL}) + (SCRIPT {type: "module" src: $DATASTAR_JS_PATH}) ) ( BODY {"data-signals": "{count: 0}"} diff --git a/examples/quotes/serve.nu b/examples/quotes/serve.nu index 03ec521..13a5a2b 100644 --- a/examples/quotes/serve.nu +++ b/examples/quotes/serve.nu @@ -69,7 +69,7 @@ def quote-html []: record -> record { (META {charset: "utf-8"}) (TITLE "Live Quotes") (STYLE "* { box-sizing: border-box; margin: 0; }") - (SCRIPT {type: "module" src: $DATASTAR_CDN_URL}) + (SCRIPT {type: "module" src: $DATASTAR_JS_PATH}) ) ( BODY {data-init: "@get('/')"} diff --git a/src/handler.rs b/src/handler.rs index bc6370d..f605194 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,5 +1,5 @@ use std::net::SocketAddr; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::time::Instant; use arc_swap::ArcSwap; @@ -20,10 +20,20 @@ use crate::worker::{spawn_eval_thread, PipelineResult}; type BoxError = Box; type HTTPResult = Result>, BoxError>; +const DATASTAR_JS_PATH: &str = "/datastar@1.0.0-RC.7.js"; +const DATASTAR_JS: &[u8] = include_bytes!("stdlib/datastar/datastar@1.0.0-RC.7.js"); + +static DATASTAR_JS_BROTLI: OnceLock> = OnceLock::new(); + +fn get_datastar_js_brotli() -> &'static [u8] { + DATASTAR_JS_BROTLI.get_or_init(|| compression::compress_full(DATASTAR_JS).unwrap()) +} + pub async fn handle( engine: Arc>, addr: Option, trusted_proxies: Arc>, + datastar: Arc, req: hyper::Request, ) -> Result>, BoxError> where @@ -33,7 +43,7 @@ where { // Load current engine snapshot - lock-free atomic operation let engine = engine.load_full(); - match handle_inner(engine, addr, trusted_proxies, req).await { + match handle_inner(engine, addr, trusted_proxies, datastar, req).await { Ok(response) => Ok(response), Err(err) => { eprintln!("Error handling request: {err}"); @@ -51,6 +61,7 @@ async fn handle_inner( engine: Arc, addr: Option, trusted_proxies: Arc>, + datastar: Arc, req: hyper::Request, ) -> HTTPResult where @@ -136,6 +147,44 @@ where // Phase 1: Log request log_request(request_id, &request); + // Built-in route: serve embedded Datastar JS bundle (requires --datastar flag) + if *datastar && request.path == DATASTAR_JS_PATH { + let use_brotli = compression::accepts_brotli(&parts.headers); + let mut header_map = hyper::header::HeaderMap::new(); + header_map.insert( + hyper::header::CONTENT_TYPE, + hyper::header::HeaderValue::from_static("application/javascript"), + ); + header_map.insert( + hyper::header::CACHE_CONTROL, + hyper::header::HeaderValue::from_static("public, max-age=31536000, immutable"), + ); + let body = if use_brotli { + header_map.insert( + hyper::header::CONTENT_ENCODING, + hyper::header::HeaderValue::from_static("br"), + ); + header_map.insert( + hyper::header::VARY, + hyper::header::HeaderValue::from_static("accept-encoding"), + ); + Full::new(Bytes::from(get_datastar_js_brotli().to_vec())) + .map_err(|never| match never {}) + .boxed() + } else { + Full::new(Bytes::from_static(DATASTAR_JS)) + .map_err(|never| match never {}) + .boxed() + }; + log_response(request_id, 200, &header_map, start_time); + let logging_body = LoggingBody::new(body, guard); + let mut response = hyper::Response::builder() + .status(200) + .body(logging_body.boxed())?; + *response.headers_mut() = header_map; + return Ok(response); + } + let reload_token = engine.reload_token.clone(); let (meta_rx, bridged_body) = spawn_eval_thread(engine, request, stream); diff --git a/src/main.rs b/src/main.rs index 2549cd3..3d10909 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,6 +94,10 @@ struct Args { )] expose: Option, + /// Serve the embedded Datastar JS bundle at /datastar@.js + #[clap(long)] + datastar: bool, + /// Trust proxies from these CIDR ranges for X-Forwarded-For parsing #[clap(long = "trust-proxy", value_name = "CIDR")] trust_proxies: Vec, @@ -284,6 +288,7 @@ async fn serve( mut rx: mpsc::Receiver, interrupt: Arc, trusted_proxies: Vec, + datastar: bool, start_time: std::time::Instant, startup_options: StartupOptions, ) -> Result<(), Box> { @@ -339,8 +344,9 @@ async fn serve( // Graceful shutdown tracker for all connections let graceful = GracefulShutdown::new(); - // Wrap trusted_proxies in Arc for sharing across connections + // Wrap shared state in Arc for sharing across connections let trusted_proxies = Arc::new(trusted_proxies); + let datastar = Arc::new(datastar); let shutdown = shutdown_signal(interrupt.clone()); tokio::pin!(shutdown); @@ -353,9 +359,10 @@ async fn serve( let io = TokioIo::new(stream); let engine = engine.clone(); let trusted_proxies = trusted_proxies.clone(); + let datastar = datastar.clone(); let service = service_fn(move |req| { - handle(engine.clone(), remote_addr, trusted_proxies.clone(), req) + handle(engine.clone(), remote_addr, trusted_proxies.clone(), datastar.clone(), req) }); // serve_connection_with_upgrades supports HTTP/1 and HTTP/2 @@ -645,6 +652,7 @@ async fn main() -> Result<(), Box> { rx, interrupt, args.trust_proxies, + args.datastar, std::time::Instant::now(), startup_options, ) diff --git a/src/stdlib/datastar/datastar@1.0.0-RC.7.js b/src/stdlib/datastar/datastar@1.0.0-RC.7.js new file mode 100644 index 0000000..bfb85cc --- /dev/null +++ b/src/stdlib/datastar/datastar@1.0.0-RC.7.js @@ -0,0 +1,9 @@ +// Datastar v1.0.0-RC.7 +var at=/🖕JS_DS🚀/.source,je=at.slice(0,5),Ge=at.slice(4),q="datastar-fetch",Z="datastar-signal-patch";var C=Object.hasOwn??Object.prototype.hasOwnProperty.call;var U=e=>e!==null&&typeof e=="object"&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null),ct=e=>{for(let t in e)if(C(e,t))return!1;return!0},Y=(e,t)=>{for(let n in e){let r=e[n];U(r)||Array.isArray(r)?Y(r,t):e[n]=t(r)}},Me=e=>{let t={};for(let[n,r]of e){let s=n.split("."),o=s.pop(),i=s.reduce((a,c)=>a[c]??={},t);i[o]=r}return t};var xe=[],Be=[],Oe=0,Le=0,We=0,Ue,j,Ne=0,M=()=>{Oe++},x=()=>{--Oe||(ft(),J())},F=e=>{Ue=j,j=e},P=()=>{j=Ue,Ue=void 0},pe=e=>Ut.bind(0,{previousValue:e,t:e,e:1}),Je=Symbol("computed"),ke=e=>{let t=Jt.bind(0,{e:17,getter:e});return t[Je]=1,t},S=e=>{let t={d:e,e:2};j&&ze(t,j),F(t),M();try{t.d()}finally{x(),P()}return gt.bind(0,t)},ft=()=>{for(;Le"getter"in e?dt(e):mt(e,e.t),dt=e=>{F(e),ht(e);try{let t=e.t;return t!==(e.t=e.getter(t))}finally{P(),yt(e)}},mt=(e,t)=>(e.e=1,e.previousValue!==(e.previousValue=t)),Ke=e=>{let t=e.e;if(!(t&64)){e.e=t|64;let n=e.r;n?Ke(n.o):Be[We++]=e}},pt=(e,t)=>{if(t&16||t&32&&bt(e.s,e)){F(e),ht(e),M();try{e.d()}finally{x(),P(),yt(e)}return}t&32&&(e.e=t&-33);let n=e.s;for(;n;){let r=n.c,s=r.e;s&64&&pt(r,r.e=s&-65),n=n.i}},Ut=(e,...t)=>{if(t.length){if(e.t!==(e.t=t[0])){e.e=17;let r=e.r;return r&&(Kt(r),Oe||ft()),!0}return!1}let n=e.t;if(e.e&16&&mt(e,n)){let r=e.r;r&&Pe(r)}return j&&ze(e,j),n},Jt=e=>{let t=e.e;if(t&16||t&32&&bt(e.s,e)){if(dt(e)){let n=e.r;n&&Pe(n)}}else t&32&&(e.e=t&-33);return j&&ze(e,j),e.t},gt=e=>{let t=e.s;for(;t;)t=Fe(t,e);let n=e.r;n&&Fe(n),e.e=0},ze=(e,t)=>{let n=t.a;if(n&&n.c===e)return;let r=n?n.i:t.s;if(r&&r.c===e){r.m=Ne,t.a=r;return}let s=e.p;if(s&&s.m===Ne&&s.o===t)return;let o=t.a=e.p={m:Ne,c:e,o:t,l:n,i:r,u:s};r&&(r.l=o),n?n.i=o:t.s=o,s?s.n=o:e.r=o},Fe=(e,t=e.o)=>{let n=e.c,r=e.l,s=e.i,o=e.n,i=e.u;if(s?s.l=r:t.a=r,r?r.i=s:t.s=s,o?o.u=i:n.p=i,i)i.n=o;else if(!(n.r=o))if("getter"in n){let a=n.s;if(a){n.e=17;do a=Fe(a,n);while(a)}}else"previousValue"in n||gt(n);return s},Kt=e=>{let t=e.n,n;e:for(;;){let r=e.o,s=r.e;if(s&60?s&12?s&4?!(s&48)&&zt(e,r)?(r.e=s|40,s&=1):s=0:r.e=s&-9|32:s=0:r.e=s|32,s&2&&Ke(r),s&1){let o=r.r;if(o){let i=(e=o).n;i&&(n={t,f:n},t=i);continue}}if(e=t){t=e.n;continue}for(;n;)if(e=n.t,n=n.f,e){t=e.n;continue e}break}},ht=e=>{Ne++,e.a=void 0,e.e=e.e&-57|4},yt=e=>{let t=e.a,n=t?t.i:e.s;for(;n;)n=Fe(n,e);e.e&=-5},bt=(e,t)=>{let n,r=0,s=!1;e:for(;;){let o=e.c,i=o.e;if(t.e&16)s=!0;else if((i&17)===17){if(lt(o)){let a=o.r;a.n&&Pe(a),s=!0}}else if((i&33)===33){(e.n||e.u)&&(n={t:e,f:n}),e=o.s,t=o,++r;continue}if(!s){let a=e.i;if(a){e=a;continue}}for(;r--;){let a=t.r,c=a.n;if(c?(e=n.t,n=n.f):e=a,s){if(lt(t)){c&&Pe(a),t=e.o;continue}s=!1}else t.e&=-33;if(t=e.o,e.i){e=e.i;continue e}}return s}},Pe=e=>{do{let t=e.o,n=t.e;(n&48)===32&&(t.e=n|16,n&2&&Ke(t))}while(e=e.n)},zt=(e,t)=>{let n=t.a;for(;n;){if(n===e)return!0;n=n.l}return!1},oe=e=>{let t=X,n=e.split(".");for(let r of n){if(t==null||!C(t,r))return;t=t[r]}return t},Ce=(e,t="")=>{let n=Array.isArray(e);if(n||U(e)){let r=n?[]:{};for(let o in e)r[o]=pe(Ce(e[o],`${t+o}.`));let s=pe(0);return new Proxy(r,{get(o,i){if(!(i==="toJSON"&&!C(r,i)))return n&&i in Array.prototype?(s(),r[i]):typeof i=="symbol"?r[i]:((!C(r,i)||r[i]()==null)&&(r[i]=pe(""),J(t+i,""),s(s()+1)),r[i]())},set(o,i,a){let c=t+i;if(n&&i==="length"){let l=r[i]-a;if(r[i]=a,l>0){let u={};for(let d=a;d{if(e!==void 0&&t!==void 0&&xe.push([e,t]),!Oe&&xe.length){let n=Me(xe);xe.length=0,document.dispatchEvent(new CustomEvent(Z,{detail:n}))}},O=(e,{ifMissing:t}={})=>{M();for(let n in e)e[n]==null?t||delete X[n]:vt(e[n],n,X,"",t);x()},T=(e,t)=>O(Me(e),t),vt=(e,t,n,r,s)=>{if(U(e)){C(n,t)&&(U(n[t])||Array.isArray(n[t]))||(n[t]={});for(let o in e)e[o]==null?s||delete n[t][o]:vt(e[o],o,n[t],`${r+t}.`,s)}else s&&C(n,t)||(n[t]=e)},ut=e=>typeof e=="string"?RegExp(e.replace(/^\/|\/$/g,"")):e,_=({include:e=/.*/,exclude:t=/(?!)/}={},n=X)=>{let r=ut(e),s=ut(t),o=[],i=[[n,""]];for(;i.length;){let[a,c]=i.pop();for(let l in a){let u=c+l;U(a[l])?i.push([a[l],`${u}.`]):r.test(u)&&!s.test(u)&&o.push([u,oe(u)])}}return Me(o)},X=Ce({});var K=e=>e instanceof HTMLElement||e instanceof SVGElement||e instanceof MathMLElement;var ge=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([a-z])([0-9]+)/gi,"$1-$2").replace(/([0-9]+)([a-z])/gi,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase();var Et=e=>ge(e).replace(/-/g,"_");var ae=e=>{try{return JSON.parse(e)}catch{return Function(`return (${e})`)()}},St={camel:e=>e.replace(/-[a-z]/g,t=>t[1].toUpperCase()),snake:e=>e.replace(/-/g,"_"),pascal:e=>e[0].toUpperCase()+St.camel(e.slice(1))},L=(e,t,n="camel")=>{for(let r of t.get("case")||[n])e=St[r]?.(e)||e;return e},G=e=>`data-${e}`;var Qt="https://data-star.dev/errors",he=(e,t,n={})=>{Object.assign(n,e);let r=new Error,s=Et(t),o=new URLSearchParams({metadata:JSON.stringify(n)}).toString(),i=JSON.stringify(n,null,2);return r.message=`${t} +More info: ${Qt}/${s}?${o} +Context: ${i}`,r},ye=new Map,Qe=new Map,At=new Map,Rt=new Proxy({},{get:(e,t)=>ye.get(t)?.apply,has:(e,t)=>ye.has(t),ownKeys:()=>Reflect.ownKeys(ye),set:()=>!1,deleteProperty:()=>!1}),be=new Map,He=[],Ze=new Set,Zt=new WeakSet,p=e=>{He.push(e),He.length===1&&setTimeout(()=>{for(let t of He)Ze.add(t.name),Qe.set(t.name,t);He.length=0,nn(),Ze.clear()})},k=e=>{ye.set(e.name,e)};document.addEventListener(q,e=>{let t=At.get(e.detail.type);t&&t.apply({error:he.bind(0,{plugin:{type:"watcher",name:t.name},element:{id:e.target.id,tag:e.target.tagName}})},e.detail.argsRaw)});var ve=e=>{At.set(e.name,e)},Tt=e=>{for(let t of e){let n=be.get(t);if(n&&be.delete(t))for(let r of n.values())for(let s of r.values())s()}},wt=G("ignore"),Yt=`[${wt}]`,Mt=e=>e.hasAttribute(`${wt}__self`)||!!e.closest(Yt),_e=(e,t)=>{for(let n of e)if(!Mt(n))for(let r in n.dataset)xt(n,r.replace(/[A-Z]/g,"-$&").toLowerCase(),n.dataset[r],t)},Xt=e=>{for(let{target:t,type:n,attributeName:r,addedNodes:s,removedNodes:o}of e)if(n==="childList"){for(let i of o)K(i)&&(Tt([i]),Tt(i.querySelectorAll("*")));for(let i of s)K(i)&&(_e([i]),_e(i.querySelectorAll("*")))}else if(n==="attributes"&&r.startsWith("data-")&&K(t)&&!Mt(t)){let i=r.slice(5),a=t.getAttribute(r);if(a===null){let c=be.get(t);if(c){let l=c.get(i);if(l){for(let u of l.values())u();c.delete(i)}}}else xt(t,i,a)}},en=new MutationObserver(Xt),tn=e=>{let[t,...n]=e.split("__"),[r,s]=t.split(/:(.+)/),o=new Map;for(let i of n){let[a,...c]=i.split(".");o.set(a,new Set(c))}return{pluginName:r,key:s,mods:o}};var nn=(e=document.documentElement,t=!0)=>{K(e)&&_e([e],!0),_e(e.querySelectorAll("*"),!0),t&&(en.observe(e,{subtree:!0,childList:!0,attributes:!0}),Zt.add(e))},xt=(e,t,n,r)=>{{let s=t,{pluginName:o,key:i,mods:a}=tn(s),c=Qe.get(o);if((!r||Ze.has(o))&&c){let l={el:e,rawKey:s,mods:a,error:he.bind(0,{plugin:{type:"attribute",name:c.name},element:{id:e.id,tag:e.tagName},expression:{rawKey:s,key:i,value:n}}),key:i,value:n,loadedPluginNames:{actions:new Set(ye.keys()),attributes:new Set(Qe.keys())},rx:void 0},u=c.requirement&&(typeof c.requirement=="string"?c.requirement:c.requirement.key)||"allowed",d=c.requirement&&(typeof c.requirement=="string"?c.requirement:c.requirement.value)||"allowed",h=i!=null&&i!=="",f=n!=null&&n!=="";if(h){if(u==="denied")throw l.error("KeyNotAllowed")}else if(u==="must")throw l.error("KeyRequired");if(f){if(d==="denied")throw l.error("ValueNotAllowed")}else if(d==="must")throw l.error("ValueRequired");if(u==="exclusive"||d==="exclusive"){if(h&&f)throw l.error("KeyAndValueProvided");if(!h&&!f)throw l.error("KeyOrValueRequired")}let m=new Map;if(f){let v;l.rx=(...A)=>(v||(v=rn(n,{returnsValue:c.returnsValue,argNames:c.argNames,cleanups:m})),v(e,...A))}let y=c.apply(l);y&&m.set("attribute",y);let b=be.get(e);if(b){let v=b.get(s);if(v)for(let A of v.values())A()}else b=new Map,be.set(e,b);b.set(s,m)}}},rn=(e,{returnsValue:t=!1,argNames:n=[],cleanups:r=new Map}={})=>{let s="";if(t){let c=/(\/(\\\/|[^/])*\/|"(\\"|[^"])*"|'(\\'|[^'])*'|`(\\`|[^`])*`|\(\s*((function)\s*\(\s*\)|(\(\s*\))\s*=>)\s*(?:\{[\s\S]*?\}|[^;){]*)\s*\)\s*\(\s*\)|[^;])+/gm,l=e.trim().match(c);if(l){let u=l.length-1,d=l[u].trim();d.startsWith("return")||(l[u]=`return (${d});`),s=l.join(`; +`)}}else s=e.trim();let o=new Map,i=RegExp(`(?:${je})(.*?)(?:${Ge})`,"gm"),a=0;for(let c of s.matchAll(i)){let l=c[1],u=`__escaped${a++}`;o.set(u,l),s=s.replace(je+l+Ge,u)}s=s.replace(/\$\['([a-zA-Z_$\d][\w$]*)'\]/g,"$$$1").replace(/\$([a-zA-Z_\d]\w*(?:[.-]\w+)*)/g,(c,l)=>l.split(".").reduce((u,d)=>`${u}['${d}']`,"$")),s=s.replaceAll(/@([A-Za-z_$][\w$]*)\(/g,'__action("$1",evt,');for(let[c,l]of o)s=s.replace(c,l);try{let c=Function("el","$","__action","evt",...n,s);return(l,...u)=>{let d=(h,f,...m)=>{let y=he.bind(0,{plugin:{type:"action",name:h},element:{id:l.id,tag:l.tagName},expression:{fnContent:s,value:e}}),b=Rt[h];if(b)return b({el:l,evt:f,error:y,cleanups:r},...m);throw y("UndefinedAction")};try{return c(l,X,d,void 0,...u)}catch(h){throw console.error(h),he({element:{id:l.id,tag:l.tagName},expression:{fnContent:s,value:e},error:h.message},"ExecuteExpression")}}}catch(c){throw console.error(c),he({expression:{fnContent:s,value:e},error:c.message},"GenerateExpression")}};k({name:"peek",apply(e,t){F();try{return t()}finally{P()}}});k({name:"setAll",apply(e,t,n){F();let r=_(n);Y(r,()=>t),O(r),P()}});k({name:"toggleAll",apply(e,t){F();let n=_(t);Y(n,r=>!r),O(n),P()}});var Ee=(e,t,n=!0)=>k({name:e,apply:async({el:r,evt:s,error:o,cleanups:i},a,{selector:c,headers:l,contentType:u="json",filterSignals:{include:d=/.*/,exclude:h=/(^|\.)_/}={},openWhenHidden:f=n,payload:m,requestCancellation:y="auto",retry:b="auto",retryInterval:v=1e3,retryScaler:A=2,retryMaxWaitMs:I=3e4,retryMaxCount:ne=10}={})=>{let de=y instanceof AbortController?y:new AbortController;y==="auto"&&(i.get(`@${e}`)?.(),i.set(`@${e}`,async()=>{de.abort(),await Promise.resolve()}));let D=null;try{if(!a?.length)throw o("FetchNoUrlProvided",{action:k});let V={Accept:"text/event-stream, text/html, application/json","Datastar-Request":!0};u==="json"&&(V["Content-Type"]="application/json");let Ae=Object.assign({},V,l),re={method:t,headers:Ae,openWhenHidden:f,retry:b,retryInterval:v,retryScaler:A,retryMaxWaitMs:I,retryMaxCount:ne,signal:de.signal,onopen:async g=>{g.status>=400&&ee(sn,r,{status:g.status.toString()})},onmessage:g=>{if(!g.event.startsWith("datastar"))return;let B=g.event,E={};for(let R of g.data.split(` +`)){let w=R.indexOf(" "),W=R.slice(0,w),De=R.slice(w+1);(E[W]||=[]).push(De)}let N=Object.fromEntries(Object.entries(E).map(([R,w])=>[R,w.join(` +`)]));ee(B,r,N)},onerror:g=>{if(Lt(g))throw g("FetchExpectedTextEventStream",{url:a});g&&(console.error(g.message),ee(on,r,{message:g.message}))}},se=new URL(a,document.baseURI),ie=new URLSearchParams(se.search);if(u==="json"){F(),m=m!==void 0?m:_({include:d,exclude:h}),P();let g=JSON.stringify(m);t==="GET"?ie.set("datastar",g):re.body=g}else if(u==="form"){let g=c?document.querySelector(c):r.closest("form");if(!g)throw o("FetchFormNotFound",{action:k,selector:c});if(!g.noValidate&&!g.checkValidity()){g.reportValidity();return}let B=new FormData(g),E=r;if(r===g&&s instanceof SubmitEvent)E=s.submitter;else{let w=W=>W.preventDefault();g.addEventListener("submit",w),D=()=>{g.removeEventListener("submit",w)}}if(E instanceof HTMLButtonElement){let w=E.getAttribute("name");w&&B.append(w,E.value)}let N=g.getAttribute("enctype")==="multipart/form-data";N||(Ae["Content-Type"]="application/x-www-form-urlencoded");let R=new URLSearchParams(B);if(t==="GET")for(let[w,W]of R)ie.append(w,W);else N?re.body=B:re.body=R}else throw o("FetchInvalidContentType",{action:k,contentType:u});ee(Ye,r,{}),se.search=ie.toString();try{await dn(se.toString(),r,re)}catch(g){if(!Lt(g))throw o("FetchFailed",{method:t,url:a,error:g.message})}}finally{ee(Xe,r,{}),D?.(),i.delete(`@${e}`)}}});Ee("get","GET",!1);Ee("patch","PATCH");Ee("post","POST");Ee("put","PUT");Ee("delete","DELETE");var Ye="started",Xe="finished",sn="error",on="retrying",an="retries-failed",ee=(e,t,n)=>document.dispatchEvent(new CustomEvent(q,{detail:{type:e,el:t,argsRaw:n}})),Lt=e=>`${e}`.includes("text/event-stream"),cn=async(e,t)=>{let n=e.getReader(),r=await n.read();for(;!r.done;)t(r.value),r=await n.read()},ln=e=>{let t,n,r,s=!1;return o=>{t?t=fn(t,o):(t=o,n=0,r=-1);let i=t.length,a=0;for(;n{let r=Nt(),s=new TextDecoder;return(o,i)=>{if(!o.length)n?.(r),r=Nt();else if(i>0){let a=s.decode(o.subarray(0,i)),c=i+(o[i+1]===32?2:1),l=s.decode(o.subarray(c));switch(a){case"data":r.data=r.data?`${r.data} +${l}`:l;break;case"event":r.event=l;break;case"id":e(r.id=l);break;case"retry":{let u=+l;Number.isNaN(u)||t(r.retry=u);break}}}}},fn=(e,t)=>{let n=new Uint8Array(e.length+t.length);return n.set(e),n.set(t,e.length),n},Nt=()=>({data:"",event:"",id:"",retry:void 0}),dn=(e,t,{signal:n,headers:r,onopen:s,onmessage:o,onclose:i,onerror:a,openWhenHidden:c,fetch:l,retry:u="auto",retryInterval:d=1e3,retryScaler:h=2,retryMaxWaitMs:f=3e4,retryMaxCount:m=10,responseOverrides:y,...b})=>new Promise((v,A)=>{let I={...r},ne,de=()=>{ne.abort(),document.hidden||g()};c||document.addEventListener("visibilitychange",de);let D,V=()=>{document.removeEventListener("visibilitychange",de),clearTimeout(D),ne.abort()};n?.addEventListener("abort",()=>{V(),v()});let Ae=l||window.fetch,re=s||(()=>{}),se=0,ie=d,g=async()=>{ne=new AbortController;let B=ne.signal;try{let E=await Ae(e,{...b,headers:I,signal:B});await re(E);let N=async($,me,$e,Re,...Wt)=>{let ot={[$e]:await me.text()};for(let Ie of Wt){let qe=me.headers.get(`datastar-${ge(Ie)}`);if(Re){let we=Re[Ie];we&&(qe=typeof we=="string"?we:JSON.stringify(we))}qe&&(ot[Ie]=qe)}ee($,t,ot),V(),v()},R=E.status,w=R===204,W=R>=300&&R<400,De=R>=400&&R<600;if(R!==200){if(i?.(),u!=="never"&&!w&&!W&&(u==="always"||u==="error"&&De)){clearTimeout(D),D=setTimeout(g,d);return}V(),v();return}se=0,d=ie;let Ve=E.headers.get("Content-Type");if(Ve?.includes("text/html"))return await N("datastar-patch-elements",E,"elements",y,"selector","mode","namespace","useViewTransition");if(Ve?.includes("application/json"))return await N("datastar-patch-signals",E,"signals",y,"onlyIfMissing");if(Ve?.includes("text/javascript")){let $=document.createElement("script"),me=E.headers.get("datastar-script-attributes");if(me)for(let[$e,Re]of Object.entries(JSON.parse(me)))$.setAttribute($e,Re);$.textContent=await E.text(),document.head.appendChild($),V();return}if(await cn(E.body,ln(un($=>{$?I["last-event-id"]=$:delete I["last-event-id"]},$=>{ie=d=$},o))),i?.(),u==="always"&&!W){clearTimeout(D),D=setTimeout(g,d);return}V(),v()}catch(E){if(!B.aborted)try{let N=a?.(E)||d;clearTimeout(D),D=setTimeout(g,N),d=Math.min(d*h,f),++se>=m?(ee(an,t,{}),V(),A("Max retries reached.")):console.error(`Datastar failed to reach ${e.toString()} retrying in ${N}ms.`)}catch(N){V(),A(N)}}};g()});p({name:"attr",requirement:{value:"must"},returnsValue:!0,apply({el:e,key:t,rx:n}){let r=(a,c)=>{c===""||c===!0?e.setAttribute(a,""):c===!1||c==null?e.removeAttribute(a):typeof c=="string"?e.setAttribute(a,c):e.setAttribute(a,JSON.stringify(c))},s=t?()=>{o.disconnect();let a=n();r(t,a),o.observe(e,{attributeFilter:[t]})}:()=>{o.disconnect();let a=n(),c=Object.keys(a);for(let l of c)r(l,a[l]);o.observe(e,{attributeFilter:c})},o=new MutationObserver(s),i=S(s);return()=>{o.disconnect(),i()}}});var mn=/^data:(?[^;]+);base64,(?.*)$/,Ct=Symbol("empty"),Ft=G("bind");p({name:"bind",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r,error:s}){let o=t!=null?L(t,n):r,i=(f,m)=>m==="number"?+f.value:f.value,a=f=>{e.value=`${f}`};if(e instanceof HTMLInputElement)switch(e.type){case"range":case"number":i=(f,m)=>m==="string"?f.value:+f.value;break;case"checkbox":i=(f,m)=>f.value!=="on"?m==="boolean"?f.checked:f.checked?f.value:"":m==="string"?f.checked?f.value:"":f.checked,a=f=>{e.checked=typeof f=="string"?f===e.value:f};break;case"radio":e.getAttribute("name")?.length||e.setAttribute("name",o),i=(f,m)=>f.checked?m==="number"?+f.value:f.value:Ct,a=f=>{e.checked=f===(typeof f=="number"?+e.value:e.value)};break;case"file":{let f=()=>{let m=[...e.files||[]],y=[];Promise.all(m.map(b=>new Promise(v=>{let A=new FileReader;A.onload=()=>{if(typeof A.result!="string")throw s("InvalidFileResultType",{resultType:typeof A.result});let I=A.result.match(mn);if(!I?.groups)throw s("InvalidDataUri",{result:A.result});y.push({name:b.name,contents:I.groups.contents,mime:I.groups.mime})},A.onloadend=()=>v(),A.readAsDataURL(b)}))).then(()=>{T([[o,y]])})};return e.addEventListener("change",f),e.addEventListener("input",f),()=>{e.removeEventListener("change",f),e.removeEventListener("input",f)}}}else if(e instanceof HTMLSelectElement){if(e.multiple){let f=new Map;i=m=>[...m.selectedOptions].map(y=>{let b=f.get(y.value);return b==="string"||b==null?y.value:+y.value}),a=m=>{for(let y of e.options)m.includes(y.value)?(f.set(y.value,"string"),y.selected=!0):m.includes(+y.value)?(f.set(y.value,"number"),y.selected=!0):y.selected=!1}}}else e instanceof HTMLTextAreaElement||(i=f=>"value"in f?f.value:f.getAttribute("value"),a=f=>{"value"in e?e.value=f:e.setAttribute("value",f)});let c=oe(o),l=typeof c,u=o;if(Array.isArray(c)&&!(e instanceof HTMLSelectElement&&e.multiple)){let f=t||r,m=document.querySelectorAll(`[${Ft}\\:${CSS.escape(f)}],[${Ft}="${CSS.escape(f)}"]`),y=[],b=0;for(let v of m){if(y.push([`${u}.${b}`,i(v,"none")]),e===v)break;b++}T(y,{ifMissing:!0}),u=`${u}.${b}`}else T([[u,i(e,l)]],{ifMissing:!0});let d=()=>{let f=oe(u);if(f!=null){let m=i(e,typeof f);m!==Ct&&T([[u,m]])}};e.addEventListener("input",d),e.addEventListener("change",d);let h=S(()=>{a(oe(u))});return()=>{h(),e.removeEventListener("input",d),e.removeEventListener("change",d)}}});p({name:"class",requirement:{value:"must"},returnsValue:!0,apply({key:e,el:t,mods:n,rx:r}){e&&=L(e,n,"kebab");let s,o=()=>{i.disconnect(),s=e?{[e]:r()}:r();for(let c in s){let l=c.split(/\s+/).filter(u=>u.length>0);if(s[c])for(let u of l)t.classList.contains(u)||t.classList.add(u);else for(let u of l)t.classList.contains(u)&&t.classList.remove(u)}i.observe(t,{attributeFilter:["class"]})},i=new MutationObserver(o),a=S(o);return()=>{i.disconnect(),a();for(let c in s){let l=c.split(/\s+/).filter(u=>u.length>0);for(let u of l)t.classList.remove(u)}}}});p({name:"computed",requirement:{value:"must"},returnsValue:!0,apply({key:e,mods:t,rx:n,error:r}){if(e)T([[L(e,t),ke(n)]]);else{let s=Object.assign({},n());Y(s,o=>{if(typeof o=="function")return ke(o);throw r("ComputedExpectedFunction")}),O(s)}}});p({name:"effect",requirement:{key:"denied",value:"must"},apply:({rx:e})=>S(e)});p({name:"indicator",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r}){let s=t!=null?L(t,n):r;T([[s,!1]]);let o=i=>{let{type:a,el:c}=i.detail;if(c===e)switch(a){case Ye:T([[s,!0]]);break;case Xe:T([[s,!1]]);break}};return document.addEventListener(q,o),()=>{T([[s,!1]]),document.removeEventListener(q,o)}}});var z=e=>{if(!e||e.size<=0)return 0;for(let t of e){if(t.endsWith("ms"))return+t.replace("ms","");if(t.endsWith("s"))return+t.replace("s","")*1e3;try{return Number.parseFloat(t)}catch{}}return 0},te=(e,t,n=!1)=>e?e.has(t.toLowerCase()):n;var et=(e,t)=>(...n)=>{setTimeout(()=>{e(...n)},t)},Pt=(e,t,n=!0,r=!1,s=!1)=>{let o=null,i=0;return(...a)=>{n&&!i?(e(...a),o=null):o=a,(!i||s)&&(i&&clearTimeout(i),i=setTimeout(()=>{r&&o!==null&&e(...o),o=null,i=0},t))}},ce=(e,t)=>{let n=t.get("delay");if(n){let o=z(n);e=et(e,o)}let r=t.get("debounce");if(r){let o=z(r),i=te(r,"leading",!1),a=!te(r,"notrailing",!1);e=Pt(e,o,i,a,!0)}let s=t.get("throttle");if(s){let o=z(s),i=!te(s,"noleading",!1),a=te(s,"trailing",!1);e=Pt(e,o,i,a)}return e};var tt=!!document.startViewTransition,Q=(e,t)=>{if(t.has("viewtransition")&&tt){let n=e;e=(...r)=>document.startViewTransition(()=>n(...r))}return e};p({name:"init",requirement:{key:"denied",value:"must"},apply({rx:e,mods:t}){let n=()=>{M(),e(),x()};n=Q(n,t);let r=0,s=t.get("delay");s&&(r=z(s),r>0&&(n=et(n,r))),n()}});p({name:"json-signals",requirement:{key:"denied"},apply({el:e,value:t,mods:n}){let r=n.has("terse")?0:2,s={};t&&(s=ae(t));let o=()=>{i.disconnect(),e.textContent=JSON.stringify(_(s),null,r),i.observe(e,{childList:!0,characterData:!0,subtree:!0})},i=new MutationObserver(o),a=S(o);return()=>{i.disconnect(),a()}}});p({name:"on",requirement:"must",argNames:["evt"],apply({el:e,key:t,mods:n,rx:r}){let s=e;n.has("window")&&(s=window);let o=c=>{c&&(n.has("prevent")&&c.preventDefault(),n.has("stop")&&c.stopPropagation()),M(),r(c),x()};o=Q(o,n),o=ce(o,n);let i={capture:n.has("capture"),passive:n.has("passive"),once:n.has("once")};if(n.has("outside")){s=document;let c=o;o=l=>{e.contains(l?.target)||c(l)}}let a=L(t,n,"kebab");if((a===q||a===Z)&&(s=document),e instanceof HTMLFormElement&&a==="submit"){let c=o;o=l=>{l?.preventDefault(),c(l)}}return s.addEventListener(a,o,i),()=>{s.removeEventListener(a,o)}}});var Ot=(e,t,n)=>Math.max(t,Math.min(n,e));var nt=new WeakSet;p({name:"on-intersect",requirement:{key:"denied",value:"must"},apply({el:e,mods:t,rx:n}){let r=()=>{M(),n(),x()};r=Q(r,t),r=ce(r,t);let s={threshold:0};t.has("full")?s.threshold=1:t.has("half")?s.threshold=.5:t.get("threshold")&&(s.threshold=Ot(Number(t.get("threshold")),0,100)/100);let o=t.has("exit"),i=new IntersectionObserver(a=>{for(let c of a)c.isIntersecting!==o&&(r(),i&&nt.has(e)&&i.disconnect())},s);return i.observe(e),t.has("once")&&nt.add(e),()=>{t.has("once")||nt.delete(e),i&&(i.disconnect(),i=null)}}});p({name:"on-interval",requirement:{key:"denied",value:"must"},apply({mods:e,rx:t}){let n=()=>{M(),t(),x()};n=Q(n,e);let r=1e3,s=e.get("duration");s&&(r=z(s),te(s,"leading",!1)&&n());let o=setInterval(n,r);return()=>{clearInterval(o)}}});p({name:"on-signal-patch",requirement:{value:"must"},argNames:["patch"],returnsValue:!0,apply({el:e,key:t,mods:n,rx:r,error:s}){if(t&&t!=="filter")throw s("KeyNotAllowed");let o=G(`${this.name}-filter`),i=e.getAttribute(o),a={};i&&(a=ae(i));let c=!1,l=ce(u=>{if(c)return;let d=_(a,u.detail);if(!ct(d)){c=!0,M();try{r(d)}finally{x(),c=!1}}},n);return document.addEventListener(Z,l),()=>{document.removeEventListener(Z,l)}}});p({name:"ref",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r}){let s=t!=null?L(t,n):r;T([[s,e]])}});var kt="none",Ht="display";p({name:"show",requirement:{key:"denied",value:"must"},returnsValue:!0,apply({el:e,rx:t}){let n=()=>{r.disconnect(),t()?e.style.display===kt&&e.style.removeProperty(Ht):e.style.setProperty(Ht,kt),r.observe(e,{attributeFilter:["style"]})},r=new MutationObserver(n),s=S(n);return()=>{r.disconnect(),s()}}});p({name:"signals",returnsValue:!0,apply({key:e,mods:t,rx:n}){let r=t.has("ifmissing");if(e)e=L(e,t),T([[e,n?.()]],{ifMissing:r});else{let s=Object.assign({},n?.());O(s,{ifMissing:r})}}});p({name:"style",requirement:{value:"must"},returnsValue:!0,apply({key:e,el:t,rx:n}){let{style:r}=t,s=new Map,o=(l,u)=>{let d=s.get(l);!u&&u!==0?d!==void 0&&(d?r.setProperty(l,d):r.removeProperty(l)):(d===void 0&&s.set(l,r.getPropertyValue(l)),r.setProperty(l,String(u)))},i=()=>{if(a.disconnect(),e)o(e,n());else{let l=n();for(let[u,d]of s)u in l||(d?r.setProperty(u,d):r.removeProperty(u));for(let u in l)o(ge(u),l[u])}a.observe(t,{attributeFilter:["style"]})},a=new MutationObserver(i),c=S(i);return()=>{a.disconnect(),c();for(let[l,u]of s)u?r.setProperty(l,u):r.removeProperty(l)}}});p({name:"text",requirement:{key:"denied",value:"must"},returnsValue:!0,apply({el:e,rx:t}){let n=()=>{r.disconnect(),e.textContent=`${t()}`,r.observe(e,{childList:!0,characterData:!0,subtree:!0})},r=new MutationObserver(n),s=S(n);return()=>{r.disconnect(),s()}}});var _t=(e,t)=>e.includes(t),pn=["remove","outer","inner","replace","prepend","append","before","after"],gn=["html","svg","mathml"];ve({name:"datastar-patch-elements",apply(e,{selector:t="",mode:n="outer",namespace:r="html",useViewTransition:s="",elements:o=""}){if(!_t(pn,n))throw e.error("PatchElementsInvalidMode",{mode:n});if(!t&&n!=="outer"&&n!=="replace")throw e.error("PatchElementsExpectedSelector");if(!_t(gn,r))throw e.error("PatchElementsInvalidNamespace",{namespace:r});let i={selector:t,mode:n,namespace:r,useViewTransition:s.trim()==="true",elements:o};tt&&s?document.startViewTransition(()=>Dt(e,i)):Dt(e,i)}});var Dt=({error:e},{selector:t,mode:n,namespace:r,elements:s})=>{let o=s.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,""),i=/<\/html>/.test(o),a=/<\/head>/.test(o),c=/<\/body>/.test(o),l=r==="svg"?"svg":r==="mathml"?"math":"",u=l?`<${l}>${s}`:s,d=new DOMParser().parseFromString(i||a||c?s:``,"text/html"),h=document.createDocumentFragment();if(i)h.appendChild(d.documentElement);else if(a&&c)h.appendChild(d.head),h.appendChild(d.body);else if(a)h.appendChild(d.head);else if(c)h.appendChild(d.body);else if(l){let f=d.querySelector("template").content.querySelector(l);for(let m of f.childNodes)h.appendChild(m)}else h=d.querySelector("template").content;if(!t&&(n==="outer"||n==="replace"))for(let f of h.children){let m;if(f instanceof HTMLHtmlElement)m=document.documentElement;else if(f instanceof HTMLBodyElement)m=document.body;else if(f instanceof HTMLHeadElement)m=document.head;else if(m=document.getElementById(f.id),!m){console.warn(e("PatchElementsNoTargetsFound"),{element:{id:f.id}});continue}$t(n,f,[m])}else{let f=document.querySelectorAll(t);if(!f.length){console.warn(e("PatchElementsNoTargetsFound"),{selector:t});return}$t(n,h,f)}},st=new WeakSet;for(let e of document.querySelectorAll("script"))st.add(e);var jt=e=>{let t=e instanceof HTMLScriptElement?[e]:e.querySelectorAll("script");for(let n of t)if(!st.has(n)){let r=document.createElement("script");for(let{name:s,value:o}of n.attributes)r.setAttribute(s,o);r.text=n.text,n.replaceWith(r),st.add(r)}},Vt=(e,t,n)=>{for(let r of e){let s=t.cloneNode(!0);jt(s),r[n](s)}},$t=(e,t,n)=>{switch(e){case"remove":for(let r of n)r.remove();break;case"outer":case"inner":for(let r of n)yn(r,t.cloneNode(!0),e),jt(r);break;case"replace":Vt(n,t,"replaceWith");break;case"prepend":case"append":case"before":case"after":Vt(n,t,e)}},H=new Map,ue=new Set,le=new Map,Se=new Set,fe=document.createElement("div");fe.hidden=!0;var Te=G("ignore-morph"),hn=`[${Te}]`,yn=(e,t,n="outer")=>{if(K(e)&&K(t)&&e.hasAttribute(Te)&&t.hasAttribute(Te)||e.parentElement?.closest(hn))return;let r=document.createElement("div");r.append(t),document.body.insertAdjacentElement("afterend",fe);let s=e.querySelectorAll("[id]");for(let{id:a,tagName:c}of s)le.has(a)?Se.add(a):le.set(a,c);e instanceof Element&&e.id&&(le.has(e.id)?Se.add(e.id):le.set(e.id,e.tagName)),ue.clear();let o=r.querySelectorAll("[id]");for(let{id:a,tagName:c}of o)ue.has(a)?Se.add(a):le.get(a)===c&&ue.add(a);for(let a of Se)ue.delete(a);le.clear(),Se.clear(),H.clear();let i=n==="outer"?e.parentElement:e;qt(i,s),qt(r,o),Gt(i,r,n==="outer"?e:null,e.nextSibling),fe.remove()},Gt=(e,t,n=null,r=null)=>{e instanceof HTMLTemplateElement&&t instanceof HTMLTemplateElement&&(e=e.content,t=t.content),n??=e.firstChild;for(let s of t.childNodes){if(n&&n!==r){let o=bn(s,n,r);if(o){if(o!==n){let i=n;for(;i&&i!==o;){let a=i;i=i.nextSibling,it(a)}}rt(o,s),n=o.nextSibling;continue}}if(s instanceof Element&&ue.has(s.id)){let o=document.getElementById(s.id),i=o;for(;i=i.parentNode;){let a=H.get(i);a&&(a.delete(s.id),a.size||H.delete(i))}Bt(e,o,n),rt(o,s),n=o.nextSibling;continue}if(H.has(s)){let o=s.namespaceURI,i=s.tagName,a=o&&o!=="http://www.w3.org/1999/xhtml"?document.createElementNS(o,i):document.createElement(i);e.insertBefore(a,n),rt(a,s),n=a.nextSibling}else{let o=document.importNode(s,!0);e.insertBefore(o,n),n=o.nextSibling}}for(;n&&n!==r;){let s=n;n=n.nextSibling,it(s)}},bn=(e,t,n)=>{let r=null,s=e.nextSibling,o=0,i=0,a=H.get(e)?.size||0,c=t;for(;c&&c!==n;){if(It(c,e)){let l=!1,u=H.get(c),d=H.get(e);if(d&&u){for(let h of u)if(d.has(h)){l=!0;break}}if(l)return c;if(!r&&!H.has(c)){if(!a)return c;r=c}}if(i+=H.get(c)?.size||0,i>a)break;r===null&&s&&It(c,s)&&(o++,s=s.nextSibling,o>=2&&(r=void 0)),c=c.nextSibling}return r||null},It=(e,t)=>e.nodeType===t.nodeType&&e.tagName===t.tagName&&(!e.id||e.id===t.id),it=e=>{H.has(e)?Bt(fe,e,null):e.parentNode?.removeChild(e)},Bt=it.call.bind(fe.moveBefore??fe.insertBefore),vn=G("preserve-attr"),rt=(e,t)=>{let n=t.nodeType;if(n===1){let r=e,s=t,o=r.hasAttribute("data-scope-children");if(r.hasAttribute(Te)&&s.hasAttribute(Te))return e;r instanceof HTMLInputElement&&s instanceof HTMLInputElement&&s.type!=="file"?s.getAttribute("value")!==r.getAttribute("value")&&(r.value=s.getAttribute("value")??""):r instanceof HTMLTextAreaElement&&s instanceof HTMLTextAreaElement&&(s.value!==r.value&&(r.value=s.value),r.firstChild&&r.firstChild.nodeValue!==s.value&&(r.firstChild.nodeValue=s.value));let i=(t.getAttribute(vn)??"").split(" ");for(let{name:a,value:c}of s.attributes)r.getAttribute(a)!==c&&!i.includes(a)&&r.setAttribute(a,c);for(let a=r.attributes.length-1;a>=0;a--){let{name:c}=r.attributes[a];!s.hasAttribute(c)&&!i.includes(c)&&r.removeAttribute(c)}o&&!r.hasAttribute("data-scope-children")&&r.setAttribute("data-scope-children",""),r.isEqualNode(s)||Gt(r,s),o&&r.dispatchEvent(new CustomEvent("datastar:scope-children",{bubbles:!1}))}return(n===8||n===3)&&e.nodeValue!==t.nodeValue&&(e.nodeValue=t.nodeValue),e},qt=(e,t)=>{for(let n of t)if(ue.has(n.id)){let r=n;for(;r&&r!==e;){let s=H.get(r);s||(s=new Set,H.set(r,s)),s.add(n.id),r=r.parentElement}}};ve({name:"datastar-patch-signals",apply({error:e},{signals:t,onlyIfMissing:n}){if(t){let r=n?.trim()==="true";O(ae(t),{ifMissing:r})}else throw e("PatchSignalsExpectedSignals")}});export{k as action,Rt as actions,p as attribute,M as beginBatch,ke as computed,S as effect,x as endBatch,_ as filtered,oe as getPath,O as mergePatch,T as mergePaths,X as root,pe as signal,F as startPeeking,P as stopPeeking,ve as watcher}; +//# sourceMappingURL=datastar.js.map diff --git a/src/stdlib/datastar/mod.nu b/src/stdlib/datastar/mod.nu index 0f7272c..fd862a0 100644 --- a/src/stdlib/datastar/mod.nu +++ b/src/stdlib/datastar/mod.nu @@ -5,6 +5,7 @@ # Follows https://github.com/starfederation/datastar/blob/develop/sdk/ADR.md export const DATASTAR_CDN_URL = "https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.7/bundles/datastar.js" +export const DATASTAR_JS_PATH = "/datastar@1.0.0-RC.7.js" # Patch HTML elements via SSE # diff --git a/src/test_handler.rs b/src/test_handler.rs index 7a86012..43546c8 100644 --- a/src/test_handler.rs +++ b/src/test_handler.rs @@ -13,6 +13,10 @@ fn no_trusted_proxies() -> Arc> { Arc::new(vec![]) } +fn no_datastar() -> Arc { + Arc::new(false) +} + #[tokio::test] async fn test_handle() { let engine = test_engine(r#"{|req| "hello world" }"#); @@ -27,6 +31,7 @@ async fn test_handle() { Arc::new(ArcSwap::from_pointee(engine)), None, no_trusted_proxies(), + no_datastar(), req, ) .await @@ -63,9 +68,15 @@ async fn test_handle_with_response_start() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine.clone(), None, no_trusted_proxies(), req) - .await - .unwrap(); + let resp = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req, + ) + .await + .unwrap(); // Verify response metadata assert_eq!(resp.status(), 201); @@ -92,7 +103,7 @@ async fn test_handle_post() { .body(Full::new(Bytes::from(body))) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); @@ -118,7 +129,7 @@ async fn test_handle_streaming() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 200); @@ -181,9 +192,15 @@ async fn test_content_type_precedence() { .uri("/") .body(Empty::::new()) .unwrap(); - let resp1 = handle(engine.clone(), None, no_trusted_proxies(), req1) - .await - .unwrap(); + let resp1 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req1, + ) + .await + .unwrap(); assert_eq!(resp1.headers()["content-type"], "text/plain"); // 2. Pipeline metadata @@ -194,9 +211,15 @@ async fn test_content_type_precedence() { let engine = Arc::new(ArcSwap::from_pointee(test_engine( r#"{|req| ls | to yaml }"#, ))); - let resp2 = handle(engine.clone(), None, no_trusted_proxies(), req2) - .await - .unwrap(); + let resp2 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req2, + ) + .await + .unwrap(); assert_eq!(resp2.headers()["content-type"], "application/yaml"); // 3. Record defaults to JSON @@ -207,9 +230,15 @@ async fn test_content_type_precedence() { let engine = Arc::new(ArcSwap::from_pointee(test_engine( r#"{|req| {foo: "bar"} }"#, ))); - let resp3 = handle(engine.clone(), None, no_trusted_proxies(), req3) - .await - .unwrap(); + let resp3 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req3, + ) + .await + .unwrap(); assert_eq!(resp3.headers()["content-type"], "application/json"); // 4. Plain text defaults to text/html @@ -220,9 +249,15 @@ async fn test_content_type_precedence() { let engine = Arc::new(ArcSwap::from_pointee(test_engine( r#"{|req| "Hello World"}"#, ))); - let resp4 = handle(engine.clone(), None, no_trusted_proxies(), req4) - .await - .unwrap(); + let resp4 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req4, + ) + .await + .unwrap(); assert_eq!(resp4.headers()["content-type"], "text/html; charset=utf-8"); // 5. Empty body has no Content-Type header @@ -231,9 +266,15 @@ async fn test_content_type_precedence() { .body(Empty::::new()) .unwrap(); let engine = Arc::new(ArcSwap::from_pointee(test_engine(r#"{|req| null}"#))); - let resp5 = handle(engine.clone(), None, no_trusted_proxies(), req5) - .await - .unwrap(); + let resp5 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req5, + ) + .await + .unwrap(); assert!( resp5.headers().get("content-type").is_none(), "Empty body should not have Content-Type header" @@ -245,9 +286,15 @@ async fn test_content_type_precedence() { .body(Empty::::new()) .unwrap(); let engine = Arc::new(ArcSwap::from_pointee(test_engine(r#"{|req| null}"#))); - let resp6 = handle(engine.clone(), None, no_trusted_proxies(), req6) - .await - .unwrap(); + let resp6 = handle( + engine.clone(), + None, + no_trusted_proxies(), + no_datastar(), + req6, + ) + .await + .unwrap(); assert_eq!(resp6.status(), 204, "Empty body should default to 204"); } @@ -263,7 +310,7 @@ async fn test_handle_bytestream() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); @@ -299,7 +346,7 @@ async fn test_handle_preserve_preamble() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); @@ -328,7 +375,7 @@ async fn test_handle_static() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 200); @@ -375,7 +422,7 @@ async fn test_handle_binary_value() { .unwrap(); // Currently this will panic, but after fixing it should return a response - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); @@ -405,7 +452,7 @@ async fn test_handle_missing_header_error() { .unwrap(); // This should fail currently - the Nu script tries to access missing column 'host' - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); @@ -431,7 +478,7 @@ async fn test_handle_script_panic() { .unwrap(); // This should return 500 instead of hanging/crashing the thread - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 500); @@ -456,7 +503,7 @@ async fn test_handle_nu_shell_column_error() { .unwrap(); // Should return 500 error instead of thread panic - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 500); @@ -478,7 +525,7 @@ async fn test_handle_script_runtime_error() { .unwrap(); // Should gracefully handle runtime errors - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 500); @@ -505,7 +552,7 @@ async fn test_multi_value_set_cookie_headers() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 200); @@ -543,7 +590,7 @@ async fn test_handle_mj_template() { .body(Empty::::new()) .unwrap(); - let resp = handle(engine, None, no_trusted_proxies(), req) + let resp = handle(engine, None, no_trusted_proxies(), no_datastar(), req) .await .unwrap(); assert_eq!(resp.status(), 200); @@ -570,6 +617,7 @@ async fn test_handle_html_record() { Arc::new(ArcSwap::from_pointee(engine)), None, no_trusted_proxies(), + no_datastar(), req, ) .await diff --git a/www/serve.nu b/www/serve.nu index 2c7b0ed..d4fc2b4 100644 --- a/www/serve.nu +++ b/www/serve.nu @@ -376,7 +376,7 @@ def install-section [] { (LINK {rel: "stylesheet" href: "https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&family=Source+Sans+3:wght@400;700&display=swap"}) (LINK {rel: "stylesheet" href: "/core.css"}) (LINK {rel: "stylesheet" href: "/syntax.css"}) - (SCRIPT {type: "module" src: $DATASTAR_CDN_URL}) + (SCRIPT {type: "module" src: $DATASTAR_JS_PATH}) (SCRIPT {src: "https://code.iconify.design/iconify-icon/2.1.0/iconify-icon.min.js"}) ] ) From 72e142884ae743e78ccb779093007938ea0b849d Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sat, 14 Feb 2026 03:45:01 +0000 Subject: [PATCH 2/7] docs: add client-side datastar counter example --- README.md | 2 +- examples/datastar-counter/serve.nu | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 examples/datastar-counter/serve.nu diff --git a/README.md b/README.md index 205a700..fc740dc 100644 --- a/README.md +++ b/README.md @@ -888,7 +888,7 @@ $ http-nu --datastar :3001 -c '{|req| HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY (DIV {"data-signals": "{count: 0}"} (SPAN {"data-text": "$count"} "0") - (BUTTON {"data-on:click": "@post('/inc')"} "+1") + (BUTTON {"data-on:click": "$count++"} "+1") ) ) }' diff --git a/examples/datastar-counter/serve.nu b/examples/datastar-counter/serve.nu new file mode 100644 index 0000000..25ac1d5 --- /dev/null +++ b/examples/datastar-counter/serve.nu @@ -0,0 +1,13 @@ +use http-nu/datastar * +use http-nu/html * + +# Run: http-nu --datastar :3001 examples/datastar-counter/serve.nu + +{|req| + HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY + (DIV {"data-signals": "{count: 0}"} + (SPAN {"data-text": "$count"} "0") + (BUTTON {"data-on:click": "$count++"} "+1") + ) + ) +} From e387eaa0c4b2188130b78031a8c4eff404ba8975 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sat, 14 Feb 2026 04:02:04 +0000 Subject: [PATCH 3/7] docs: show datastar example as standalone serve.nu --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fc740dc..9f4023e 100644 --- a/README.md +++ b/README.md @@ -883,15 +883,21 @@ Use `--datastar` to serve the embedded JS bundle at `$DATASTAR_JS_PATH` (`/datastar@1.0.0-RC.7.js`) with immutable cache headers: ```bash -$ http-nu --datastar :3001 -c '{|req| - use http-nu/datastar *; use http-nu/html * +$ http-nu --datastar :3001 ./serve.nu +``` + +```nushell +use http-nu/datastar * +use http-nu/html * + +{|req| HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY (DIV {"data-signals": "{count: 0}"} (SPAN {"data-text": "$count"} "0") (BUTTON {"data-on:click": "$count++"} "+1") ) ) -}' +} ``` Commands return records that pipe to `to sse` for streaming output. From 6af323919acfc5540b8f4111185db2fb5eaac4c5 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sat, 14 Feb 2026 04:08:48 +0000 Subject: [PATCH 4/7] style: consistent HTML DSL formatting in datastar example --- README.md | 15 +++++++++++---- examples/datastar-counter/serve.nu | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9f4023e..e436ee8 100644 --- a/README.md +++ b/README.md @@ -891,10 +891,17 @@ use http-nu/datastar * use http-nu/html * {|req| - HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY - (DIV {"data-signals": "{count: 0}"} - (SPAN {"data-text": "$count"} "0") - (BUTTON {"data-on:click": "$count++"} "+1") + HTML ( + HEAD ( + SCRIPT {type: "module" src: $DATASTAR_JS_PATH} + ) + ) ( + BODY ( + DIV {"data-signals": "{count: 0}"} ( + SPAN {"data-text": "$count"} "0" + ) ( + BUTTON {"data-on:click": "$count++"} "+1" + ) ) ) } diff --git a/examples/datastar-counter/serve.nu b/examples/datastar-counter/serve.nu index 25ac1d5..654c00e 100644 --- a/examples/datastar-counter/serve.nu +++ b/examples/datastar-counter/serve.nu @@ -4,10 +4,17 @@ use http-nu/html * # Run: http-nu --datastar :3001 examples/datastar-counter/serve.nu {|req| - HTML (HEAD (SCRIPT {type: "module" src: $DATASTAR_JS_PATH})) (BODY - (DIV {"data-signals": "{count: 0}"} - (SPAN {"data-text": "$count"} "0") - (BUTTON {"data-on:click": "$count++"} "+1") + HTML ( + HEAD ( + SCRIPT {type: "module" src: $DATASTAR_JS_PATH} + ) + ) ( + BODY ( + DIV {"data-signals": "{count: 0}"} ( + SPAN {"data-text": "$count"} "0" + ) ( + BUTTON {"data-on:click": "$count++"} "+1" + ) ) ) } From 7a7eeb14c0f5c7e6b90056b7489fcc8bd75c36b1 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sat, 14 Feb 2026 04:22:26 +0000 Subject: [PATCH 5/7] fix: restore original comment for trusted_proxies Arc wrapping --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 3d10909..f854d40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -344,7 +344,7 @@ async fn serve( // Graceful shutdown tracker for all connections let graceful = GracefulShutdown::new(); - // Wrap shared state in Arc for sharing across connections + // Wrap trusted_proxies in Arc for sharing across connections let trusted_proxies = Arc::new(trusted_proxies); let datastar = Arc::new(datastar); From 4a9b9c888d1a9b698e2af4441034284da368e111 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sun, 15 Feb 2026 15:30:04 +0000 Subject: [PATCH 6/7] chore: add .cargo/ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a9dc153..0df8bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +.cargo/ store www/assets/_/ __pycache__/ From 4fc48b01c6d6629566d969a54ed38887fc12733e Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Sun, 15 Feb 2026 15:34:32 +0000 Subject: [PATCH 7/7] refactor: pre-compress datastar JS at build time instead of runtime --- src/handler.rs | 11 +++-------- src/stdlib/datastar/datastar@1.0.0-RC.7.js.br | Bin 0 -> 11021 bytes 2 files changed, 3 insertions(+), 8 deletions(-) create mode 100644 src/stdlib/datastar/datastar@1.0.0-RC.7.js.br diff --git a/src/handler.rs b/src/handler.rs index f605194..46f549b 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,5 +1,5 @@ use std::net::SocketAddr; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use std::time::Instant; use arc_swap::ArcSwap; @@ -22,12 +22,7 @@ type HTTPResult = Result>, BoxError>; const DATASTAR_JS_PATH: &str = "/datastar@1.0.0-RC.7.js"; const DATASTAR_JS: &[u8] = include_bytes!("stdlib/datastar/datastar@1.0.0-RC.7.js"); - -static DATASTAR_JS_BROTLI: OnceLock> = OnceLock::new(); - -fn get_datastar_js_brotli() -> &'static [u8] { - DATASTAR_JS_BROTLI.get_or_init(|| compression::compress_full(DATASTAR_JS).unwrap()) -} +const DATASTAR_JS_BROTLI: &[u8] = include_bytes!("stdlib/datastar/datastar@1.0.0-RC.7.js.br"); pub async fn handle( engine: Arc>, @@ -168,7 +163,7 @@ where hyper::header::VARY, hyper::header::HeaderValue::from_static("accept-encoding"), ); - Full::new(Bytes::from(get_datastar_js_brotli().to_vec())) + Full::new(Bytes::from_static(DATASTAR_JS_BROTLI)) .map_err(|never| match never {}) .boxed() } else { diff --git a/src/stdlib/datastar/datastar@1.0.0-RC.7.js.br b/src/stdlib/datastar/datastar@1.0.0-RC.7.js.br new file mode 100644 index 0000000000000000000000000000000000000000..92ace73ba377bcd0f10207ce89823080bb059491 GIT binary patch literal 11021 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