@papack/ssr 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- let e=require(`node:http`);function t(e){let{each:t,children:n}=e;if(!t||t.length===0)return null;let r=n[0];if(typeof r!=`function`)throw Error(`<For> expects a single function child`);return t.map(e=>r(e))}async function n(e){let{children:t}=e;return t?(await Promise.all(t)).join(``):``}async function r(e,t,...n){if(typeof e==`function`)return await e({...t??{},children:n});let r=`<${e}`;if(t)for(let[e,n]of Object.entries(t))e!==`children`&&(n==null||n===!1||(n===!0?r+=` ${e}`:r+=` ${e}="${String(n)}"`));r+=`>`;for(let e of n)r+=await i(e);return r+=`</${e}>`,r}async function i(e){return e==null||e===!1||e===!0?``:e instanceof Promise?i(await e):Array.isArray(e)?(await Promise.all(e.map(i))).join(``):String(e)}var a=class{server;routes=[];ctx;notFoundHandler;errorHandler;constructor(t){this.ctx=t,this.server=(0,e.createServer)(async(e,t)=>{try{if(e.method!==`GET`){t.statusCode=405,t.end(`METHOD_NOT_ALLOWED`);return}let n=e.url??`/`,r=new URL(n,`http://localhost`),i=this.splitPath(r.pathname);for(let n of this.routes){if(n.parts.length!==i.length)continue;let a={},o=!0;for(let e=0;e<n.parts.length;e++){let t=n.parts[e],r=i[e];if(!t||!r){o=!1;break}if(t.startsWith(`:`))a[t.slice(1)]=r;else if(t!==r){o=!1;break}}if(!o)continue;let s=r.pathname+`?`+Object.entries(a).map(([e,t])=>`${e}=${t}`).join(`&`);if(n.ttl&&n.cache){let e=n.cache.get(s);if(e&&e.expires>Date.now()){t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(e.value);return}}let c={req:e,res:t,params:a,...this.ctx},l=await n.handler(c);n.contentType.startsWith(`text/html`)&&(l=`<!DOCTYPE html>`+l),n.ttl&&n.cache&&n.cache.set(s,{value:l,expires:Date.now()+n.ttl}),t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(l);return}if(this.notFoundHandler){let n={req:e,res:t,params:{},...this.ctx},r=await this.notFoundHandler(n);t.statusCode=404,t.setHeader(`content-type`,`text/html; charset=utf-8`),t.end(r);return}t.statusCode=404,t.end(`NOT_FOUND`)}catch(n){await this.handleError(n,e,t)}})}async handleError(e,t,n){if(this.errorHandler){let r={req:t,res:n,params:{},...this.ctx},i=await this.errorHandler(r,e);n.statusCode=500,n.setHeader(`content-type`,`text/html; charset=utf-8`),n.end(i);return}n.statusCode=500,n.setHeader(`content-type`,`text/plain`),n.end(`INTERNAL_ERROR`)}html(e,t){this.addRoute(e.path,`text/html; charset=utf-8`,t,e.ttl)}css(e,t){this.addRoute(e.path,`text/css; charset=utf-8`,t,e.ttl)}js(e,t){this.addRoute(e.path,`application/javascript; charset=utf-8`,t,e.ttl)}notFound(e){this.notFoundHandler=e}error(e){this.errorHandler=e}listen(e,t){this.server.listen(e,t)}addRoute(e,t,n,r){let i=this.splitPath(e);this.routes.push({parts:i,contentType:t,handler:n,ttl:r,cache:r?new Map:void 0})}splitPath(e){return e.split(`/`).filter(Boolean)}};async function o(e){return e.when!==!0||!e.children||e.children.length===0?``:(await Promise.all(e.children)).join(``)}exports.For=t,exports.Router=a,exports.Show=o,exports.fragment=n,exports.jsx=r;
1
+ let e=require(`node:http`);function t(e){let{each:t,children:n}=e;if(!t||t.length===0)return null;let r=n[0];if(typeof r!=`function`)throw Error(`<For> expects a single function child`);return t.map(e=>r(e))}async function n(e){let{children:t}=e;return t?(await Promise.all(t)).join(``):``}async function r(e,t,...n){if(typeof e==`function`)return await e({...t??{},children:n});let r=`<${e}`;if(t)for(let[e,n]of Object.entries(t))e!==`children`&&(n==null||n===!1||(n===!0?r+=` ${e}`:r+=` ${e}="${String(n)}"`));r+=`>`;for(let e of n)r+=await i(e);return r+=`</${e}>`,r}async function i(e){return e==null||e===!1||e===!0?``:e instanceof Promise?i(await e):Array.isArray(e)?(await Promise.all(e.map(i))).join(``):String(e)}var a=class{server;routes=[];ctx;notFoundHandler;errorHandler;constructor(t){this.ctx=t,this.server=(0,e.createServer)(async(e,t)=>{try{if(e.method!==`GET`){t.statusCode=405,t.end(`METHOD_NOT_ALLOWED`);return}let n=e.url??`/`,r=new URL(n,`http://localhost`),i=this.splitPath(r.pathname);for(let n of this.routes){if(n.parts.length!==i.length)continue;let r={},a=!0;for(let e=0;e<n.parts.length;e++){let t=n.parts[e],o=i[e];if(!t||!o){a=!1;break}if(t.startsWith(`:`))r[t.slice(1)]=o;else if(t!==o){a=!1;break}}if(!a)continue;let o={req:e,res:t,params:r,...this.ctx},s=await n.handler(o);n.contentType.startsWith(`text/html`)&&(s=`<!DOCTYPE html>`+s),t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(s);return}if(this.notFoundHandler){let n={req:e,res:t,params:{},...this.ctx},r=await this.notFoundHandler(n);t.statusCode=404,t.setHeader(`content-type`,`text/html; charset=utf-8`),t.end(r);return}t.statusCode=404,t.end(`NOT_FOUND`)}catch(n){await this.handleError(n,e,t)}})}async handleError(e,t,n){if(this.errorHandler){let r={req:t,res:n,params:{},...this.ctx},i=await this.errorHandler(r,e);n.statusCode=500,n.setHeader(`content-type`,`text/html; charset=utf-8`),n.end(i);return}n.statusCode=500,n.setHeader(`content-type`,`text/plain`),n.end(`INTERNAL_ERROR`)}html(e,t){this.addRoute(e.path,`text/html; charset=utf-8`,t)}css(e,t){this.addRoute(e.path,`text/css; charset=utf-8`,t)}js(e,t){this.addRoute(e.path,`application/javascript; charset=utf-8`,t)}notFound(e){this.notFoundHandler=e}error(e){this.errorHandler=e}listen(e,t){this.server.listen(e,t)}addRoute(e,t,n){let r=this.splitPath(e);this.routes.push({parts:r,contentType:t,handler:n})}splitPath(e){return e.split(`/`).filter(Boolean)}};async function o(e){return e.when!==!0||!e.children||e.children.length===0?``:(await Promise.all(e.children)).join(``)}exports.For=t,exports.Router=a,exports.Show=o,exports.fragment=n,exports.jsx=r;
package/dist/index.d.cts CHANGED
@@ -35,7 +35,6 @@ type Context<Ctx> = {
35
35
  } & Ctx;
36
36
  type RouteOptions = {
37
37
  path: string;
38
- ttl?: number;
39
38
  };
40
39
  declare class Router<Ctx extends object> {
41
40
  private server;
package/dist/index.d.mts CHANGED
@@ -35,7 +35,6 @@ type Context<Ctx> = {
35
35
  } & Ctx;
36
36
  type RouteOptions = {
37
37
  path: string;
38
- ttl?: number;
39
38
  };
40
39
  declare class Router<Ctx extends object> {
41
40
  private server;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../core/for.ts","../core/jsx.ts","../core/fragment.ts","../core/router.ts","../core/show.ts"],"sourcesContent":[],"mappings":";;;KAAK;iBACY;oBACG;;AAGJ,iBAAA,GAAc,CAAA,CAAA,CAAA,CAAA,KAAQ,EAAR,QAAQ,CAAC,CAAD,CAAA,CAAA,EAAA,GAAA,EAAA,GAAA,IAAA;;;;;4BCHV;IDFvB,UAAQ,iBAEQ,CAAA;MAGF,CAAA,GAAA,EAAA,MAAoB,CAAT,EAAA,GAAA;;;;KCGzB,SAAA;KAEA,KAAA,GAAQ,YAAY,UAAU,QAAQ,YAAY;KAE3C,KAAA,GAVuB;EAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,GAAA;EAAA,QAAA,CAAA,EAYtB,KAZsB,EAAA;AAAA,CAAA;AAQ9B,iBAOiB,GAAA,CAPZ,GAAA,EAAA,MAAA,GAAA,CAAA,CAAA,KAAA,EAAA,GAAA,EAAA,GAAA,MAAA,GAQgC,OARhC,CAAA,MAAA,CAAA,CAAA,EAAA,KAAA,EASD,KATC,GAAA,IAAA,EAAA,GAAA,QAAA,EAUK,KAVL,EAAA,CAAA,EAWP,OAXO,CAAA,MAAA,CAAA;;;iBCRY,QAAA,QAAgB,QAAQ;;;KCClC;OACL;EHJF,GAAA,EGKE,cHLM;EAKG,MAAG,EGCT,MHDS,CAAA,MAAW,EAAA,MAAA,CAAQ;IGElC;KAeC,YAAA;;;;cAKQ,MFzBsB,CAAA,YAAA,MAAA,CAAA,CAAA;EAAA,QAAA,MAAA;EAAA,QAAA,MAAA;EAM9B,QAAA,GAAA;EAEA,QAAK,eAAA;EAAG,QAAA,YAAA;EAAY,WAAA,CAAA,GAAA,EE4BN,GF5BM;EAAkB,QAAA,WAAA;EAAY,IAAA,CAAA,IAAA,EEgK7C,YFhK6C,EAAA,OAAA,EAAA,CAAA,GAAA,EEiKpC,OFjKoC,CEiK5B,GFjK4B,CAAA,EAAA,GAAA,MAAA,GEiKV,OFjKU,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAApB,GAAA,CAAA,IAAA,EEuKzB,YFvKyB,EAAA,OAAA,EAAA,CAAA,GAAA,EEwKhB,OFxKgB,CEwKR,GFxKQ,CAAA,EAAA,GAAA,MAAA,GEwKU,OFxKV,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAAO,EAAA,CAAA,IAAA,EE8KhC,YF9KgC,EAAA,OAAA,EAAA,CAAA,GAAA,EE+KvB,OF/KuB,CE+Kf,GF/Ke,CAAA,EAAA,GAAA,MAAA,GE+KG,OF/KH,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAE9B,QAAK,CAAA,OAAA,EAAA,CAEJ,GAAA,EEqLa,OFrLR,CEqLgB,GFrLhB,CAAA,EAAA,GAAA,MAAA,GEqLkC,OFrLlC,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAGI,KAAA,CAAG,OAAA,EAAA,CAAA,GAAA,EEuLN,OFvLM,CEuLE,GFvLF,CAAA,EAAA,GAAA,EAAA,OAAA,EAAA,GAAA,MAAA,GEuLkC,OFvLlC,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EACiB,MAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,EAAA,GAAA,GAAA,IAAA,CAAA,EAAA,IAAA;EACjC,QAAA,QAAA;EACM,QAAA,SAAA;;;;KGpBV,SAAA;;;;AJKW,iBIAM,IAAA,CJAQ,CAAA,EIAA,SJAQ,CAAA,EIAI,OJAJ,CAAA,MAAA,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../core/for.ts","../core/jsx.ts","../core/fragment.ts","../core/router.ts","../core/show.ts"],"sourcesContent":[],"mappings":";;;KAAK;iBACY;oBACG;;AAGJ,iBAAA,GAAc,CAAA,CAAA,CAAA,CAAA,KAAQ,EAAR,QAAQ,CAAC,CAAD,CAAA,CAAA,EAAA,GAAA,EAAA,GAAA,IAAA;;;;;4BCHV;IDFvB,UAAQ,iBAEQ,CAAA;MAGF,CAAA,GAAA,EAAA,MAAoB,CAAT,EAAA,GAAA;;;;KCGzB,SAAA;KAEA,KAAA,GAAQ,YAAY,UAAU,QAAQ,YAAY;KAE3C,KAAA,GAVuB;EAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,GAAA;EAAA,QAAA,CAAA,EAYtB,KAZsB,EAAA;AAAA,CAAA;AAQ9B,iBAOiB,GAAA,CAPZ,GAAA,EAAA,MAAA,GAAA,CAAA,CAAA,KAAA,EAAA,GAAA,EAAA,GAAA,MAAA,GAQgC,OARhC,CAAA,MAAA,CAAA,CAAA,EAAA,KAAA,EASD,KATC,GAAA,IAAA,EAAA,GAAA,QAAA,EAUK,KAVL,EAAA,CAAA,EAWP,OAXO,CAAA,MAAA,CAAA;;;iBCRY,QAAA,QAAgB,QAAQ;;;KCClC;OACL;EHJF,GAAA,EGKE,cHLM;EAKG,MAAG,EGCT,MHDS,CAAA,MAAW,EAAA,MAAA,CAAQ;IGElC;KAQC,YAAA;;;cAIQ;UFjBe,MAAO;EAAA,QAAA,MAAA;EAAA,QAAA,GAAA;EAM9B,QAAA,eAAS;EAET,QAAK,YAAA;EAAG,WAAA,CAAA,GAAA,EEoBM,GFpBN;EAAY,QAAA,WAAA;EAAkB,IAAA,CAAA,IAAA,EEgIjC,YFhIiC,EAAA,OAAA,EAAA,CAAA,GAAA,EEiIxB,OFjIwB,CEiIhB,GFjIgB,CAAA,EAAA,GAAA,MAAA,GEiIE,OFjIF,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAAY,GAAA,CAAA,IAAA,EEuI7C,YFvI6C,EAAA,OAAA,EAAA,CAAA,GAAA,EEwIpC,OFxIoC,CEwI5B,GFxI4B,CAAA,EAAA,GAAA,MAAA,GEwIV,OFxIU,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAApB,EAAA,CAAA,IAAA,EE8IzB,YF9IyB,EAAA,OAAA,EAAA,CAAA,GAAA,EE+IhB,OF/IgB,CE+IR,GF/IQ,CAAA,EAAA,GAAA,MAAA,GE+IU,OF/IV,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAAO,QAAA,CAAA,OAAA,EAAA,CAAA,GAAA,EEoJhB,OFpJgB,CEoJR,GFpJQ,CAAA,EAAA,GAAA,MAAA,GEoJU,OFpJV,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAE9B,KAAA,CAAA,OAAK,EAAA,CAAA,GAAA,EEuJE,OFrJD,CEqJS,GFrJT,CAAA,EAAA,GAAA,EAAA,OAAA,EAAA,GAAA,MAAA,GEqJyC,OFrJzC,CAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAGI,MAAG,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,EAAA,GAAA,GAAA,IAAA,CAAA,EAAA,IAAA;EACiB,QAAA,QAAA;EACjC,QAAA,SAAA;;;;KGnBJ,SAAA;;;;AJKW,iBIAM,IAAA,CJAQ,CAAA,EIAA,SJAQ,CAAA,EIAI,OJAJ,CAAA,MAAA,CAAA"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{createServer as e}from"node:http";function t(e){let{each:t,children:n}=e;if(!t||t.length===0)return null;let r=n[0];if(typeof r!=`function`)throw Error(`<For> expects a single function child`);return t.map(e=>r(e))}async function n(e){let{children:t}=e;return t?(await Promise.all(t)).join(``):``}async function r(e,t,...n){if(typeof e==`function`)return await e({...t??{},children:n});let r=`<${e}`;if(t)for(let[e,n]of Object.entries(t))e!==`children`&&(n==null||n===!1||(n===!0?r+=` ${e}`:r+=` ${e}="${String(n)}"`));r+=`>`;for(let e of n)r+=await i(e);return r+=`</${e}>`,r}async function i(e){return e==null||e===!1||e===!0?``:e instanceof Promise?i(await e):Array.isArray(e)?(await Promise.all(e.map(i))).join(``):String(e)}var a=class{server;routes=[];ctx;notFoundHandler;errorHandler;constructor(t){this.ctx=t,this.server=e(async(e,t)=>{try{if(e.method!==`GET`){t.statusCode=405,t.end(`METHOD_NOT_ALLOWED`);return}let n=e.url??`/`,r=new URL(n,`http://localhost`),i=this.splitPath(r.pathname);for(let n of this.routes){if(n.parts.length!==i.length)continue;let a={},o=!0;for(let e=0;e<n.parts.length;e++){let t=n.parts[e],r=i[e];if(!t||!r){o=!1;break}if(t.startsWith(`:`))a[t.slice(1)]=r;else if(t!==r){o=!1;break}}if(!o)continue;let s=r.pathname+`?`+Object.entries(a).map(([e,t])=>`${e}=${t}`).join(`&`);if(n.ttl&&n.cache){let e=n.cache.get(s);if(e&&e.expires>Date.now()){t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(e.value);return}}let c={req:e,res:t,params:a,...this.ctx},l=await n.handler(c);n.contentType.startsWith(`text/html`)&&(l=`<!DOCTYPE html>`+l),n.ttl&&n.cache&&n.cache.set(s,{value:l,expires:Date.now()+n.ttl}),t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(l);return}if(this.notFoundHandler){let n={req:e,res:t,params:{},...this.ctx},r=await this.notFoundHandler(n);t.statusCode=404,t.setHeader(`content-type`,`text/html; charset=utf-8`),t.end(r);return}t.statusCode=404,t.end(`NOT_FOUND`)}catch(n){await this.handleError(n,e,t)}})}async handleError(e,t,n){if(this.errorHandler){let r={req:t,res:n,params:{},...this.ctx},i=await this.errorHandler(r,e);n.statusCode=500,n.setHeader(`content-type`,`text/html; charset=utf-8`),n.end(i);return}n.statusCode=500,n.setHeader(`content-type`,`text/plain`),n.end(`INTERNAL_ERROR`)}html(e,t){this.addRoute(e.path,`text/html; charset=utf-8`,t,e.ttl)}css(e,t){this.addRoute(e.path,`text/css; charset=utf-8`,t,e.ttl)}js(e,t){this.addRoute(e.path,`application/javascript; charset=utf-8`,t,e.ttl)}notFound(e){this.notFoundHandler=e}error(e){this.errorHandler=e}listen(e,t){this.server.listen(e,t)}addRoute(e,t,n,r){let i=this.splitPath(e);this.routes.push({parts:i,contentType:t,handler:n,ttl:r,cache:r?new Map:void 0})}splitPath(e){return e.split(`/`).filter(Boolean)}};async function o(e){return e.when!==!0||!e.children||e.children.length===0?``:(await Promise.all(e.children)).join(``)}export{t as For,a as Router,o as Show,n as fragment,r as jsx};
1
+ import{createServer as e}from"node:http";function t(e){let{each:t,children:n}=e;if(!t||t.length===0)return null;let r=n[0];if(typeof r!=`function`)throw Error(`<For> expects a single function child`);return t.map(e=>r(e))}async function n(e){let{children:t}=e;return t?(await Promise.all(t)).join(``):``}async function r(e,t,...n){if(typeof e==`function`)return await e({...t??{},children:n});let r=`<${e}`;if(t)for(let[e,n]of Object.entries(t))e!==`children`&&(n==null||n===!1||(n===!0?r+=` ${e}`:r+=` ${e}="${String(n)}"`));r+=`>`;for(let e of n)r+=await i(e);return r+=`</${e}>`,r}async function i(e){return e==null||e===!1||e===!0?``:e instanceof Promise?i(await e):Array.isArray(e)?(await Promise.all(e.map(i))).join(``):String(e)}var a=class{server;routes=[];ctx;notFoundHandler;errorHandler;constructor(t){this.ctx=t,this.server=e(async(e,t)=>{try{if(e.method!==`GET`){t.statusCode=405,t.end(`METHOD_NOT_ALLOWED`);return}let n=e.url??`/`,r=new URL(n,`http://localhost`),i=this.splitPath(r.pathname);for(let n of this.routes){if(n.parts.length!==i.length)continue;let r={},a=!0;for(let e=0;e<n.parts.length;e++){let t=n.parts[e],o=i[e];if(!t||!o){a=!1;break}if(t.startsWith(`:`))r[t.slice(1)]=o;else if(t!==o){a=!1;break}}if(!a)continue;let o={req:e,res:t,params:r,...this.ctx},s=await n.handler(o);n.contentType.startsWith(`text/html`)&&(s=`<!DOCTYPE html>`+s),t.statusCode=200,t.setHeader(`content-type`,n.contentType),t.end(s);return}if(this.notFoundHandler){let n={req:e,res:t,params:{},...this.ctx},r=await this.notFoundHandler(n);t.statusCode=404,t.setHeader(`content-type`,`text/html; charset=utf-8`),t.end(r);return}t.statusCode=404,t.end(`NOT_FOUND`)}catch(n){await this.handleError(n,e,t)}})}async handleError(e,t,n){if(this.errorHandler){let r={req:t,res:n,params:{},...this.ctx},i=await this.errorHandler(r,e);n.statusCode=500,n.setHeader(`content-type`,`text/html; charset=utf-8`),n.end(i);return}n.statusCode=500,n.setHeader(`content-type`,`text/plain`),n.end(`INTERNAL_ERROR`)}html(e,t){this.addRoute(e.path,`text/html; charset=utf-8`,t)}css(e,t){this.addRoute(e.path,`text/css; charset=utf-8`,t)}js(e,t){this.addRoute(e.path,`application/javascript; charset=utf-8`,t)}notFound(e){this.notFoundHandler=e}error(e){this.errorHandler=e}listen(e,t){this.server.listen(e,t)}addRoute(e,t,n){let r=this.splitPath(e);this.routes.push({parts:r,contentType:t,handler:n})}splitPath(e){return e.split(`/`).filter(Boolean)}};async function o(e){return e.when!==!0||!e.children||e.children.length===0?``:(await Promise.all(e.children)).join(``)}export{t as For,a as Router,o as Show,n as fragment,r as jsx};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["params: Record<string, string>","ctx: Context<Ctx>","ctx"],"sources":["../core/for.ts","../core/fragment.ts","../core/jsx.ts","../core/router.ts","../core/show.ts"],"sourcesContent":["type ForProps<T> = {\n each: readonly T[];\n children: [(item: T) => any];\n};\n\nexport function For<T>(props: ForProps<T>) {\n const { each, children } = props;\n if (!each || each.length === 0) return null;\n\n const render = children[0];\n if (typeof render !== \"function\") {\n throw new Error(\"<For> expects a single function child\");\n }\n\n return each.map((item) => render(item));\n}\n","import type { Props } from \"./jsx\";\n\nexport async function fragment(props: Props): Promise<string> {\n const { children } = props;\n\n if (!children) return \"\";\n\n const parts = await Promise.all(children);\n return parts.join(\"\");\n}\n","declare global {\n namespace JSX {\n type Element = string | Promise<string>;\n interface IntrinsicElements {\n [tag: string]: any;\n }\n }\n}\ntype Primitive = string | number | boolean | null | undefined;\n\ntype Child = Primitive | Child[] | Promise<Primitive | Child[]>;\n\nexport type Props = {\n [key: string]: any;\n children?: Child[];\n};\n\nexport async function jsx(\n tag: string | ((props: any) => string | Promise<string>),\n props: Props | null,\n ...children: Child[]\n): Promise<string> {\n if (typeof tag === \"function\") {\n return await tag({ ...(props ?? {}), children });\n }\n\n let html = `<${tag}`;\n\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\") continue;\n if (value == null || value === false) continue;\n\n if (value === true) {\n html += ` ${key}`;\n } else {\n html += ` ${key}=\"${String(value)}\"`;\n }\n }\n }\n\n html += \">\";\n\n for (const c of children) {\n html += await renderChild(c);\n }\n\n html += `</${tag}>`;\n\n return html;\n}\n\nasync function renderChild(child: Child): Promise<string> {\n if (child == null || child === false || child === true) return \"\";\n\n if (child instanceof Promise) {\n return renderChild(await child);\n }\n\n if (Array.isArray(child)) {\n const parts = await Promise.all(child.map(renderChild));\n return parts.join(\"\");\n }\n\n return String(child);\n}\n","import { createServer, type Server } from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nexport type Context<Ctx> = {\n req: IncomingMessage;\n res: ServerResponse;\n params: Record<string, string>;\n} & Ctx;\n\ntype CacheEntry = {\n value: string;\n expires: number;\n};\n\ntype Route<Ctx> = {\n parts: string[];\n contentType: string;\n ttl?: number;\n cache?: Map<string, CacheEntry>;\n handler: (ctx: Context<Ctx>) => string | Promise<string>;\n};\n\ntype RouteOptions = {\n path: string;\n ttl?: number;\n};\n\nexport class Router<Ctx extends object> {\n private server: Server;\n private routes: Route<Ctx>[] = [];\n private ctx: Ctx;\n\n private notFoundHandler?: (ctx: Context<Ctx>) => string | Promise<string>;\n private errorHandler?: (\n ctx: Context<Ctx>,\n err: unknown\n ) => string | Promise<string>;\n\n constructor(ctx: Ctx) {\n this.ctx = ctx;\n\n this.server = createServer(async (req, res) => {\n try {\n if (req.method !== \"GET\") {\n res.statusCode = 405;\n res.end(\"METHOD_NOT_ALLOWED\");\n return;\n }\n\n const rawUrl = req.url ?? \"/\";\n const url = new URL(rawUrl, \"http://localhost\");\n const pathParts = this.splitPath(url.pathname);\n\n for (const route of this.routes) {\n if (route.parts.length !== pathParts.length) continue;\n\n const params: Record<string, string> = {};\n let match = true;\n\n for (let i = 0; i < route.parts.length; i++) {\n const routePart = route.parts[i];\n const pathPart = pathParts[i];\n\n if (!routePart || !pathPart) {\n match = false;\n break;\n }\n\n if (routePart.startsWith(\":\")) {\n params[routePart.slice(1)] = pathPart;\n } else if (routePart !== pathPart) {\n match = false;\n break;\n }\n }\n\n if (!match) continue;\n\n const cacheKey =\n url.pathname +\n \"?\" +\n Object.entries(params)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"&\");\n\n if (route.ttl && route.cache) {\n const hit = route.cache.get(cacheKey);\n if (hit && hit.expires > Date.now()) {\n res.statusCode = 200;\n res.setHeader(\"content-type\", route.contentType);\n res.end(hit.value);\n return;\n }\n }\n\n const ctx: Context<Ctx> = {\n req,\n res,\n params,\n ...this.ctx,\n };\n\n let body = await route.handler(ctx);\n\n if (route.contentType.startsWith(\"text/html\")) {\n body = \"<!DOCTYPE html>\" + body;\n }\n\n if (route.ttl && route.cache) {\n route.cache.set(cacheKey, {\n value: body,\n expires: Date.now() + route.ttl,\n });\n }\n\n res.statusCode = 200;\n res.setHeader(\"content-type\", route.contentType);\n res.end(body);\n return;\n }\n\n if (this.notFoundHandler) {\n const ctx: Context<Ctx> = {\n req,\n res,\n params: {},\n ...this.ctx,\n };\n\n const html = await this.notFoundHandler(ctx);\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.end(html);\n return;\n }\n\n res.statusCode = 404;\n res.end(\"NOT_FOUND\");\n } catch (err) {\n await this.handleError(err, req, res);\n }\n });\n }\n\n private async handleError(\n err: unknown,\n req: IncomingMessage,\n res: ServerResponse\n ) {\n if (this.errorHandler) {\n const ctx: Context<Ctx> = {\n req,\n res,\n params: {},\n ...this.ctx,\n };\n\n const html = await this.errorHandler(ctx, err);\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.end(html);\n return;\n }\n\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/plain\");\n res.end(\"INTERNAL_ERROR\");\n }\n\n html(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>\n ) {\n this.addRoute(opts.path, \"text/html; charset=utf-8\", handler, opts.ttl);\n }\n\n css(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>\n ) {\n this.addRoute(opts.path, \"text/css; charset=utf-8\", handler, opts.ttl);\n }\n\n js(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>\n ) {\n this.addRoute(\n opts.path,\n \"application/javascript; charset=utf-8\",\n handler,\n opts.ttl\n );\n }\n\n notFound(handler: (ctx: Context<Ctx>) => string | Promise<string>) {\n this.notFoundHandler = handler;\n }\n\n error(\n handler: (ctx: Context<Ctx>, err: unknown) => string | Promise<string>\n ) {\n this.errorHandler = handler;\n }\n\n listen(port: number, cb?: () => void) {\n this.server.listen(port, cb);\n }\n\n private addRoute(\n path: string,\n contentType: string,\n handler: (ctx: Context<Ctx>) => string | Promise<string>,\n ttl?: number\n ) {\n const parts = this.splitPath(path);\n\n this.routes.push({\n parts,\n contentType,\n handler,\n ttl,\n cache: ttl ? new Map() : undefined,\n });\n }\n\n private splitPath(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n }\n}\n","type ShowProps = {\n when: boolean;\n children: any;\n};\n\nexport async function Show(p: ShowProps): Promise<string> {\n if (p.when !== true) return \"\";\n if (!p.children || p.children.length === 0) return \"\";\n\n const parts = await Promise.all(p.children);\n return parts.join(\"\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["params: Record<string, string>","ctx: Context<Ctx>","ctx"],"sources":["../core/for.ts","../core/fragment.ts","../core/jsx.ts","../core/router.ts","../core/show.ts"],"sourcesContent":["type ForProps<T> = {\n each: readonly T[];\n children: [(item: T) => any];\n};\n\nexport function For<T>(props: ForProps<T>) {\n const { each, children } = props;\n if (!each || each.length === 0) return null;\n\n const render = children[0];\n if (typeof render !== \"function\") {\n throw new Error(\"<For> expects a single function child\");\n }\n\n return each.map((item) => render(item));\n}\n","import type { Props } from \"./jsx\";\n\nexport async function fragment(props: Props): Promise<string> {\n const { children } = props;\n\n if (!children) return \"\";\n\n const parts = await Promise.all(children);\n return parts.join(\"\");\n}\n","declare global {\n namespace JSX {\n type Element = string | Promise<string>;\n interface IntrinsicElements {\n [tag: string]: any;\n }\n }\n}\ntype Primitive = string | number | boolean | null | undefined;\n\ntype Child = Primitive | Child[] | Promise<Primitive | Child[]>;\n\nexport type Props = {\n [key: string]: any;\n children?: Child[];\n};\n\nexport async function jsx(\n tag: string | ((props: any) => string | Promise<string>),\n props: Props | null,\n ...children: Child[]\n): Promise<string> {\n if (typeof tag === \"function\") {\n return await tag({ ...(props ?? {}), children });\n }\n\n let html = `<${tag}`;\n\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === \"children\") continue;\n if (value == null || value === false) continue;\n\n if (value === true) {\n html += ` ${key}`;\n } else {\n html += ` ${key}=\"${String(value)}\"`;\n }\n }\n }\n\n html += \">\";\n\n for (const c of children) {\n html += await renderChild(c);\n }\n\n html += `</${tag}>`;\n\n return html;\n}\n\nasync function renderChild(child: Child): Promise<string> {\n if (child == null || child === false || child === true) return \"\";\n\n if (child instanceof Promise) {\n return renderChild(await child);\n }\n\n if (Array.isArray(child)) {\n const parts = await Promise.all(child.map(renderChild));\n return parts.join(\"\");\n }\n\n return String(child);\n}\n","import { createServer, type Server } from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nexport type Context<Ctx> = {\n req: IncomingMessage;\n res: ServerResponse;\n params: Record<string, string>;\n} & Ctx;\n\ntype Route<Ctx> = {\n parts: string[];\n contentType: string;\n handler: (ctx: Context<Ctx>) => string | Promise<string>;\n};\n\ntype RouteOptions = {\n path: string;\n};\n\nexport class Router<Ctx extends object> {\n private server: Server;\n private routes: Route<Ctx>[] = [];\n private ctx: Ctx;\n\n private notFoundHandler?: (ctx: Context<Ctx>) => string | Promise<string>;\n private errorHandler?: (\n ctx: Context<Ctx>,\n err: unknown,\n ) => string | Promise<string>;\n\n constructor(ctx: Ctx) {\n this.ctx = ctx;\n\n this.server = createServer(async (req, res) => {\n try {\n if (req.method !== \"GET\") {\n res.statusCode = 405;\n res.end(\"METHOD_NOT_ALLOWED\");\n return;\n }\n\n const rawUrl = req.url ?? \"/\";\n const url = new URL(rawUrl, \"http://localhost\");\n const pathParts = this.splitPath(url.pathname);\n\n for (const route of this.routes) {\n if (route.parts.length !== pathParts.length) continue;\n\n const params: Record<string, string> = {};\n let match = true;\n\n for (let i = 0; i < route.parts.length; i++) {\n const routePart = route.parts[i];\n const pathPart = pathParts[i];\n\n if (!routePart || !pathPart) {\n match = false;\n break;\n }\n\n if (routePart.startsWith(\":\")) {\n params[routePart.slice(1)] = pathPart;\n } else if (routePart !== pathPart) {\n match = false;\n break;\n }\n }\n\n if (!match) continue;\n\n const ctx: Context<Ctx> = {\n req,\n res,\n params,\n ...this.ctx,\n };\n\n let body = await route.handler(ctx);\n\n if (route.contentType.startsWith(\"text/html\")) {\n body = \"<!DOCTYPE html>\" + body;\n }\n\n res.statusCode = 200;\n res.setHeader(\"content-type\", route.contentType);\n res.end(body);\n return;\n }\n\n if (this.notFoundHandler) {\n const ctx: Context<Ctx> = {\n req,\n res,\n params: {},\n ...this.ctx,\n };\n\n const html = await this.notFoundHandler(ctx);\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.end(html);\n return;\n }\n\n res.statusCode = 404;\n res.end(\"NOT_FOUND\");\n } catch (err) {\n await this.handleError(err, req, res);\n }\n });\n }\n\n private async handleError(\n err: unknown,\n req: IncomingMessage,\n res: ServerResponse,\n ) {\n if (this.errorHandler) {\n const ctx: Context<Ctx> = {\n req,\n res,\n params: {},\n ...this.ctx,\n };\n\n const html = await this.errorHandler(ctx, err);\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.end(html);\n return;\n }\n\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/plain\");\n res.end(\"INTERNAL_ERROR\");\n }\n\n html(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>,\n ) {\n this.addRoute(opts.path, \"text/html; charset=utf-8\", handler);\n }\n\n css(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>,\n ) {\n this.addRoute(opts.path, \"text/css; charset=utf-8\", handler);\n }\n\n js(\n opts: RouteOptions,\n handler: (ctx: Context<Ctx>) => string | Promise<string>,\n ) {\n this.addRoute(opts.path, \"application/javascript; charset=utf-8\", handler);\n }\n\n notFound(handler: (ctx: Context<Ctx>) => string | Promise<string>) {\n this.notFoundHandler = handler;\n }\n\n error(\n handler: (ctx: Context<Ctx>, err: unknown) => string | Promise<string>,\n ) {\n this.errorHandler = handler;\n }\n\n listen(port: number, cb?: () => void) {\n this.server.listen(port, cb);\n }\n\n private addRoute(\n path: string,\n contentType: string,\n handler: (ctx: Context<Ctx>) => string | Promise<string>,\n ) {\n const parts = this.splitPath(path);\n\n this.routes.push({\n parts,\n contentType,\n handler,\n });\n }\n\n private splitPath(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n }\n}\n","type ShowProps = {\n when: boolean;\n children: any;\n};\n\nexport async function Show(p: ShowProps): Promise<string> {\n if (p.when !== true) return \"\";\n if (!p.children || p.children.length === 0) return \"\";\n\n const parts = await Promise.all(p.children);\n return parts.join(\"\");\n}\n"],"mappings":"yCAKA,SAAgB,EAAO,EAAoB,CACzC,GAAM,CAAE,OAAM,YAAa,EAC3B,GAAI,CAAC,GAAQ,EAAK,SAAW,EAAG,OAAO,KAEvC,IAAM,EAAS,EAAS,GACxB,GAAI,OAAO,GAAW,WACpB,MAAU,MAAM,wCAAwC,CAG1D,OAAO,EAAK,IAAK,GAAS,EAAO,EAAK,CAAC,CCZzC,eAAsB,EAAS,EAA+B,CAC5D,GAAM,CAAE,YAAa,EAKrB,OAHK,GAES,MAAM,QAAQ,IAAI,EAAS,EAC5B,KAAK,GAAG,CAHC,GCYxB,eAAsB,EACpB,EACA,EACA,GAAG,EACc,CACjB,GAAI,OAAO,GAAQ,WACjB,OAAO,MAAM,EAAI,CAAE,GAAI,GAAS,EAAE,CAAG,WAAU,CAAC,CAGlD,IAAI,EAAO,IAAI,IAEf,GAAI,EACF,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAM,CAC1C,IAAQ,aACR,GAAS,MAAQ,IAAU,KAE3B,IAAU,GACZ,GAAQ,IAAI,IAEZ,GAAQ,IAAI,EAAI,IAAI,OAAO,EAAM,CAAC,KAKxC,GAAQ,IAER,IAAK,IAAM,KAAK,EACd,GAAQ,MAAM,EAAY,EAAE,CAK9B,MAFA,IAAQ,KAAK,EAAI,GAEV,EAGT,eAAe,EAAY,EAA+B,CAYxD,OAXI,GAAS,MAAQ,IAAU,IAAS,IAAU,GAAa,GAE3D,aAAiB,QACZ,EAAY,MAAM,EAAM,CAG7B,MAAM,QAAQ,EAAM,EACR,MAAM,QAAQ,IAAI,EAAM,IAAI,EAAY,CAAC,EAC1C,KAAK,GAAG,CAGhB,OAAO,EAAM,CC7CtB,IAAa,EAAb,KAAwC,CACtC,OACA,OAA+B,EAAE,CACjC,IAEA,gBACA,aAKA,YAAY,EAAU,CACpB,KAAK,IAAM,EAEX,KAAK,OAAS,EAAa,MAAO,EAAK,IAAQ,CAC7C,GAAI,CACF,GAAI,EAAI,SAAW,MAAO,CACxB,EAAI,WAAa,IACjB,EAAI,IAAI,qBAAqB,CAC7B,OAGF,IAAM,EAAS,EAAI,KAAO,IACpB,EAAM,IAAI,IAAI,EAAQ,mBAAmB,CACzC,EAAY,KAAK,UAAU,EAAI,SAAS,CAE9C,IAAK,IAAM,KAAS,KAAK,OAAQ,CAC/B,GAAI,EAAM,MAAM,SAAW,EAAU,OAAQ,SAE7C,IAAMA,EAAiC,EAAE,CACrC,EAAQ,GAEZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,MAAM,OAAQ,IAAK,CAC3C,IAAM,EAAY,EAAM,MAAM,GACxB,EAAW,EAAU,GAE3B,GAAI,CAAC,GAAa,CAAC,EAAU,CAC3B,EAAQ,GACR,MAGF,GAAI,EAAU,WAAW,IAAI,CAC3B,EAAO,EAAU,MAAM,EAAE,EAAI,UACpB,IAAc,EAAU,CACjC,EAAQ,GACR,OAIJ,GAAI,CAAC,EAAO,SAEZ,IAAMC,EAAoB,CACxB,MACA,MACA,SACA,GAAG,KAAK,IACT,CAEG,EAAO,MAAM,EAAM,QAAQC,EAAI,CAE/B,EAAM,YAAY,WAAW,YAAY,GAC3C,EAAO,kBAAoB,GAG7B,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,EAAM,YAAY,CAChD,EAAI,IAAI,EAAK,CACb,OAGF,GAAI,KAAK,gBAAiB,CACxB,IAAMD,EAAoB,CACxB,MACA,MACA,OAAQ,EAAE,CACV,GAAG,KAAK,IACT,CAEK,EAAO,MAAM,KAAK,gBAAgBC,EAAI,CAC5C,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,2BAA2B,CACzD,EAAI,IAAI,EAAK,CACb,OAGF,EAAI,WAAa,IACjB,EAAI,IAAI,YAAY,OACb,EAAK,CACZ,MAAM,KAAK,YAAY,EAAK,EAAK,EAAI,GAEvC,CAGJ,MAAc,YACZ,EACA,EACA,EACA,CACA,GAAI,KAAK,aAAc,CACrB,IAAMD,EAAoB,CACxB,MACA,MACA,OAAQ,EAAE,CACV,GAAG,KAAK,IACT,CAEK,EAAO,MAAM,KAAK,aAAa,EAAK,EAAI,CAC9C,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,2BAA2B,CACzD,EAAI,IAAI,EAAK,CACb,OAGF,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,aAAa,CAC3C,EAAI,IAAI,iBAAiB,CAG3B,KACE,EACA,EACA,CACA,KAAK,SAAS,EAAK,KAAM,2BAA4B,EAAQ,CAG/D,IACE,EACA,EACA,CACA,KAAK,SAAS,EAAK,KAAM,0BAA2B,EAAQ,CAG9D,GACE,EACA,EACA,CACA,KAAK,SAAS,EAAK,KAAM,wCAAyC,EAAQ,CAG5E,SAAS,EAA0D,CACjE,KAAK,gBAAkB,EAGzB,MACE,EACA,CACA,KAAK,aAAe,EAGtB,OAAO,EAAc,EAAiB,CACpC,KAAK,OAAO,OAAO,EAAM,EAAG,CAG9B,SACE,EACA,EACA,EACA,CACA,IAAM,EAAQ,KAAK,UAAU,EAAK,CAElC,KAAK,OAAO,KAAK,CACf,QACA,cACA,UACD,CAAC,CAGJ,UAAkB,EAAwB,CACxC,OAAO,EAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,GCtL1C,eAAsB,EAAK,EAA+B,CAKxD,OAJI,EAAE,OAAS,IACX,CAAC,EAAE,UAAY,EAAE,SAAS,SAAW,EAAU,IAErC,MAAM,QAAQ,IAAI,EAAE,SAAS,EAC9B,KAAK,GAAG"}
package/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Matthias Steiner
3
+ Copyright (c) 2025 - 2026 Matthias Steiner
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@papack/ssr",
3
3
  "description": "Minimal server-side rendering framework",
4
- "version": "0.0.0",
4
+ "version": "0.1.0",
5
5
  "author": "Matthias Steiner",
6
6
  "repository": "github:papack/ssr",
7
7
  "homepage": "https://github.com/papack/ssr",
@@ -20,6 +20,7 @@
20
20
  "build": "tsdown"
21
21
  },
22
22
  "devDependencies": {
23
+ "typescript": "^5.9.3",
23
24
  "@types/bun": "latest",
24
25
  "tsdown": "^0.16.8"
25
26
  }
package/readme.md CHANGED
@@ -10,9 +10,8 @@ Minimal server-side rendering framework with JSX-to-string rendering.
10
10
  - No state, no reactivity, no hydration
11
11
  - Every route is a plain function
12
12
  - Built on Node.js `http`
13
- - Optional TTL-based response caching
14
13
 
15
- This is **SSR only**. request -> render -> response.
14
+ > **SSR only**. request -> render -> response.
16
15
 
17
16
  ## Install
18
17
 
@@ -148,18 +147,6 @@ router.error((ctx, err) => {
148
147
  });
149
148
  ```
150
149
 
151
- ## TTL Cache (Optional)
152
-
153
- ```ts
154
- router.html({ path: "/", ttl: 5000 }, () => {
155
- return <h1>Cached</h1>;
156
- });
157
- ```
158
-
159
- - Per-route in-memory cache
160
- - Cache key includes route params
161
- - TTL in milliseconds
162
-
163
150
  ## Cookies / Headers
164
151
 
165
152
  ```ts
@@ -169,3 +156,11 @@ router.html({ path: "/login" }, (ctx) => {
169
156
  return "ok";
170
157
  });
171
158
  ```
159
+
160
+ ## Sessions
161
+
162
+ **@papack/ssr** works well with **@papack/session**. Rendering is strictly request-based, while sessions are handled explicitly via cookies at the `node:http` level. Session data is available through `ctx.req` and `ctx.res`, fitting naturally into the request -> render -> response flow without implicit state.
163
+
164
+ ## Cache
165
+
166
+ **@papack/ssr** intentionally has no built-in caching. If caching is required, **@papack/cache** can be added explicitly on top to store rendered output or data, keeping cache behavior opt-in and the core SSR model predictable.