ajo 0.1.27 → 0.1.29

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/context.js CHANGED
@@ -1,4 +1,4 @@
1
- const e = Symbol.for("ajo.context"), r = (t, o = Symbol()) => function(...l) {
1
+ const e = /* @__PURE__ */ Symbol.for("ajo.context"), r = (t, o = /* @__PURE__ */ Symbol()) => function(...l) {
2
2
  const n = this ?? c;
3
3
  return n ? l.length ? n[e][o] = l[0] : o in n[e] ? n[e][o] : t : t;
4
4
  };
package/dist/html.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./context.cjs"),g=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),m=Symbol.for("ajo.args"),y=e=>e.replace(/[&<>"']/g,r=>`&#${r.charCodeAt(0)};`),o=()=>{},b=e=>[...f(e)].join(""),f=function*(e,{alloc:r=o,push:t=o,placeholder:n=o}={}){for(e of d(e,{alloc:r,push:t,placeholder:n}))typeof e=="string"?yield y(e):yield*$(e,{alloc:r,push:t,placeholder:n})},$=function*({nodeName:e,children:r,...t},n){let l="";for(const i in t)i.startsWith("set:")||t[i]==null||t[i]===!1||(t[i]===!0?l+=` ${i}`:l+=` ${i}="${y(String(t[i]))}"`);g.has(e)?yield`<${e}${l}>`:(yield`<${e}${l}>`,r!=null&&(yield*f(r,n)),yield`</${e}>`)},d=function*(e,r){if(e==null)return;const t=typeof e;if(t!="boolean")if(t=="string")yield e;else if(t=="number"||t=="bigint")yield String(e);else if(Symbol.iterator in e)for(e of e)yield*d(e,r);else"nodeName"in e?typeof e.nodeName=="function"?yield*v(e,r):yield u(e,r):yield String(e)},v=function*({nodeName:e,fallback:r=e.fallback,...t},n){const l=e.constructor.name;e.src?yield w(e.src,t,n):l=="GeneratorFunction"?yield S(e,t,n):l=="AsyncGeneratorFunction"?yield x(e,r,t,n):(t=e(t),typeof t?.then=="function"?yield A(r,t,n):yield*d(t,n))},w=(e,r,t)=>{const n=t.alloc();return t.push({id:n,src:e,h:u(r,t),done:!0}),t.placeholder(n)},S=(e,r,t)=>{const n={...e.attrs},l={...e.args};for(const c in r)c.startsWith("attr:")?n[c.slice(5)]=r[c]:c=="key"||c=="skip"||c=="memo"||c=="ref"||c.startsWith("set:")?n[c]=r[c]:l[c]=r[c];const i={[a.Context]:Object.create(a.current()?.[a.Context]??null),[m]:l,next:o,return:o,throw:c=>{throw c}},s=e.call(i,l),p=a.current();a.current(i);try{const c=[...d(s.next().value,t)];return{...n,nodeName:e.is??"div",children:c.length==1?c[0]:c}}finally{s.return?.(),a.current(p)}},x=(e,r,t,n)=>{const l=n.alloc();return Promise.resolve().then(async()=>{const i=e(t);n={...n,alloc:(s=l)=>n.alloc(s)};try{for(t=await i.next();!t.done;)n.push({id:l,h:u(t.value,n),done:!1}),t=await i.next();n.push({id:l,h:u(t.value,n),done:!0})}catch(s){n.push({id:l,h:u(s,n),done:!0})}finally{i.return?.()}}),n.placeholder(l,r)},A=(e,r,t)=>{const n=t.alloc();return r.then(l=>t.push({id:n,h:u(l,{...t,alloc:(i=n)=>t.alloc(i)}),done:!0})),t.placeholder(n,e)},u=({key:e,skip:r,memo:t,ref:n,...l},i)=>{if("children"in l){const s=[...d(l.children,i)];s.length?l.children=s.length==1?s[0]:s:delete l.children}return l};exports.html=f;exports.render=b;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./context.cjs"),m=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),b=Symbol.for("ajo.args"),p=e=>e.replace(/[&<>"']/g,r=>`&#${r.charCodeAt(0)};`),o=()=>{},v=e=>[...y(e)].join(""),y=function*(e,{alloc:r=o,push:t=o,placeholder:n=o}={}){for(e of f(e,{alloc:r,push:t,placeholder:n}))typeof e=="string"?yield p(e):yield*w(e,{alloc:r,push:t,placeholder:n})},w=function*({nodeName:e,children:r,...t},n){let l="";for(const i in t)i.startsWith("set:")||t[i]==null||t[i]===!1||(t[i]===!0?l+=` ${i}`:l+=` ${i}="${p(String(t[i]))}"`);m.has(e)?yield`<${e}${l}>`:(yield`<${e}${l}>`,r!=null&&(yield*y(r,n)),yield`</${e}>`)},f=function*(e,r){if(e==null)return;const t=typeof e;if(t!="boolean")if(t=="string")yield e;else if(t=="number"||t=="bigint")yield String(e);else if(Symbol.iterator in e)for(e of e)yield*f(e,r);else"nodeName"in e?typeof e.nodeName=="function"?yield*$(e,r):yield a(e,r):yield String(e)},$=function*({nodeName:e,fallback:r=e.fallback,...t},n){const l=e.constructor.name;e.src?yield S(e.src,t,n):l=="GeneratorFunction"?yield x(e,t,n):l=="AsyncGeneratorFunction"?yield A(e,r,t,n):(t=e(t),typeof t?.then=="function"?yield j(r,t,n):yield*f(t,n))},S=(e,r,t)=>{const n=t.alloc();return t.push({id:n,src:e,h:a(r,t),done:!0}),t.placeholder(n)},x=(e,r,t)=>{const n={...e.attrs},l={...e.args};for(const c in r)c.startsWith("attr:")?n[c.slice(5)]=r[c]:c=="key"||c=="skip"||c=="memo"||c=="ref"||c.startsWith("set:")?n[c]=r[c]:l[c]=r[c];const i={[u.Context]:Object.create(u.current()?.[u.Context]??null),[b]:l,next:o,return:o,throw:c=>{throw c}},s=e.call(i,l),g=u.current();u.current(i);const d=c=>({...n,nodeName:e.is??"div",...a({children:c},t)});try{return d(s.next().value)}catch(c){return d(s.throw(c).value)}finally{s.return?.(),u.current(g)}},A=(e,r,t,n)=>{const l=n.alloc();return Promise.resolve().then(async()=>{const i=e(t);n={...n,alloc:(s=l)=>n.alloc(s)};try{for(t=await i.next();!t.done;)n.push({id:l,h:a(t.value,n),done:!1}),t=await i.next();n.push({id:l,h:a(t.value,n),done:!0})}catch(s){n.push({id:l,h:a(s,n),done:!0})}finally{i.return?.()}}),n.placeholder(l,r)},j=(e,r,t)=>{const n=t.alloc();return r.then(l=>t.push({id:n,h:a(l,{...t,alloc:(i=n)=>t.alloc(i)}),done:!0})),t.placeholder(n,e)},a=({key:e,skip:r,memo:t,ref:n,...l},i)=>{if("children"in l){const s=[...f(l.children,i)];s.length?l.children=s.length==1?s[0]:s:delete l.children}return l};exports.html=y;exports.render=v;
package/dist/html.js CHANGED
@@ -1,48 +1,50 @@
1
- import { Context as y, current as f } from "./context.js";
2
- const m = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]), b = Symbol.for("ajo.args"), d = (e) => e.replace(/[&<>"']/g, (r) => `&#${r.charCodeAt(0)};`), u = () => {
3
- }, j = (e) => [...p(e)].join(""), p = function* (e, { alloc: r = u, push: t = u, placeholder: n = u } = {}) {
4
- for (e of o(e, { alloc: r, push: t, placeholder: n }))
5
- typeof e == "string" ? yield d(e) : yield* $(e, { alloc: r, push: t, placeholder: n });
1
+ import { Context as d, current as o } from "./context.js";
2
+ const b = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]), w = /* @__PURE__ */ Symbol.for("ajo.args"), p = (e) => e.replace(/[&<>"']/g, (r) => `&#${r.charCodeAt(0)};`), u = () => {
3
+ }, W = (e) => [...g(e)].join(""), g = function* (e, { alloc: r = u, push: t = u, placeholder: n = u } = {}) {
4
+ for (e of f(e, { alloc: r, push: t, placeholder: n }))
5
+ typeof e == "string" ? yield p(e) : yield* $(e, { alloc: r, push: t, placeholder: n });
6
6
  }, $ = function* ({ nodeName: e, children: r, ...t }, n) {
7
7
  let l = "";
8
8
  for (const i in t)
9
- i.startsWith("set:") || t[i] == null || t[i] === !1 || (t[i] === !0 ? l += ` ${i}` : l += ` ${i}="${d(String(t[i]))}"`);
10
- m.has(e) ? yield `<${e}${l}>` : (yield `<${e}${l}>`, r != null && (yield* p(r, n)), yield `</${e}>`);
11
- }, o = function* (e, r) {
9
+ i.startsWith("set:") || t[i] == null || t[i] === !1 || (t[i] === !0 ? l += ` ${i}` : l += ` ${i}="${p(String(t[i]))}"`);
10
+ b.has(e) ? yield `<${e}${l}>` : (yield `<${e}${l}>`, r != null && (yield* g(r, n)), yield `</${e}>`);
11
+ }, f = function* (e, r) {
12
12
  if (e == null) return;
13
13
  const t = typeof e;
14
14
  if (t != "boolean")
15
15
  if (t == "string") yield e;
16
16
  else if (t == "number" || t == "bigint") yield String(e);
17
- else if (Symbol.iterator in e) for (e of e) yield* o(e, r);
18
- else "nodeName" in e ? typeof e.nodeName == "function" ? yield* w(e, r) : yield a(e, r) : yield String(e);
19
- }, w = function* ({ nodeName: e, fallback: r = e.fallback, ...t }, n) {
17
+ else if (Symbol.iterator in e) for (e of e) yield* f(e, r);
18
+ else "nodeName" in e ? typeof e.nodeName == "function" ? yield* v(e, r) : yield a(e, r) : yield String(e);
19
+ }, v = function* ({ nodeName: e, fallback: r = e.fallback, ...t }, n) {
20
20
  const l = e.constructor.name;
21
- e.src ? yield v(e.src, t, n) : l == "GeneratorFunction" ? yield x(e, t, n) : l == "AsyncGeneratorFunction" ? yield S(e, r, t, n) : (t = e(t), typeof t?.then == "function" ? yield A(r, t, n) : yield* o(t, n));
22
- }, v = (e, r, t) => {
21
+ e.src ? yield x(e.src, t, n) : l == "GeneratorFunction" ? yield S(e, t, n) : l == "AsyncGeneratorFunction" ? yield A(e, r, t, n) : (t = e(t), typeof t?.then == "function" ? yield G(r, t, n) : yield* f(t, n));
22
+ }, x = (e, r, t) => {
23
23
  const n = t.alloc();
24
24
  return t.push({ id: n, src: e, h: a(r, t), done: !0 }), t.placeholder(n);
25
- }, x = (e, r, t) => {
25
+ }, S = (e, r, t) => {
26
26
  const n = { ...e.attrs }, l = { ...e.args };
27
27
  for (const c in r)
28
28
  c.startsWith("attr:") ? n[c.slice(5)] = r[c] : c == "key" || c == "skip" || c == "memo" || c == "ref" || c.startsWith("set:") ? n[c] = r[c] : l[c] = r[c];
29
29
  const i = {
30
- [y]: Object.create(f()?.[y] ?? null),
31
- [b]: l,
30
+ [d]: Object.create(o()?.[d] ?? null),
31
+ [w]: l,
32
32
  next: u,
33
33
  return: u,
34
34
  throw: (c) => {
35
35
  throw c;
36
36
  }
37
- }, s = e.call(i, l), g = f();
38
- f(i);
37
+ }, s = e.call(i, l), m = o();
38
+ o(i);
39
+ const y = (c) => ({ ...n, nodeName: e.is ?? "div", ...a({ children: c }, t) });
39
40
  try {
40
- const c = [...o(s.next().value, t)];
41
- return { ...n, nodeName: e.is ?? "div", children: c.length == 1 ? c[0] : c };
41
+ return y(s.next().value);
42
+ } catch (c) {
43
+ return y(s.throw(c).value);
42
44
  } finally {
43
- s.return?.(), f(g);
45
+ s.return?.(), o(m);
44
46
  }
45
- }, S = (e, r, t, n) => {
47
+ }, A = (e, r, t, n) => {
46
48
  const l = n.alloc();
47
49
  return Promise.resolve().then(async () => {
48
50
  const i = e(t);
@@ -57,17 +59,17 @@ const m = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embe
57
59
  i.return?.();
58
60
  }
59
61
  }), n.placeholder(l, r);
60
- }, A = (e, r, t) => {
62
+ }, G = (e, r, t) => {
61
63
  const n = t.alloc();
62
64
  return r.then((l) => t.push({ id: n, h: a(l, { ...t, alloc: (i = n) => t.alloc(i) }), done: !0 })), t.placeholder(n, e);
63
65
  }, a = ({ key: e, skip: r, memo: t, ref: n, ...l }, i) => {
64
66
  if ("children" in l) {
65
- const s = [...o(l.children, i)];
67
+ const s = [...f(l.children, i)];
66
68
  s.length ? l.children = s.length == 1 ? s[0] : s : delete l.children;
67
69
  }
68
70
  return l;
69
71
  };
70
72
  export {
71
- p as html,
72
- j as render
73
+ g as html,
74
+ W as render
73
75
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./context.cjs"),y=Symbol.for("ajo.key"),h=Symbol.for("ajo.memo"),b=Symbol.for("ajo.ref"),p=Symbol.for("ajo.cache"),c=Symbol.for("ajo.generator"),l=Symbol.for("ajo.iterator"),m=Symbol.for("ajo.render"),a=Symbol.for("ajo.args"),v=t=>t.children,C=(t,e,...r)=>((e??={}).nodeName=t,!("children"in e)&&r.length&&(e.children=r.length==1?r[0]:r),e),f=(t,e)=>{let r=e.firstChild;for(t of g(t)){const n=E(t,e,r);r==null?e.appendChild(n):n==r?r=n.nextSibling:n==r.nextSibling?(e.appendChild(r),r=n.nextSibling):W(e,n,r)}for(;r;){const n=r.nextSibling;r.nodeType==1&&w(r),e.removeChild(r),r=n}},g=function*(t){if(t==null)return;const e=typeof t;if(e!="boolean")if(e=="string")yield t;else if(e=="number"||e=="bigint")yield String(t);else if(Symbol.iterator in t)for(t of t)yield*g(t);else"nodeName"in t?typeof t.nodeName=="function"?yield*N(t):yield t:yield String(t)},N=function*({nodeName:t,...e}){t.constructor.name=="GeneratorFunction"?yield k(t,e):yield*g(t(e))},k=function(t,e){const r={...t.attrs},n={...t.args};for(const s in e)s.startsWith("attr:")?r[s.slice(5)]=e[s]:s=="key"||s=="skip"||s=="memo"||s=="ref"||s.startsWith("set:")?r[s]=e[s]:n[s]=e[s];return{...r,nodeName:t.is??"div",[c]:t,[a]:n}},E=(t,e,r)=>typeof t=="string"?O(t,r):T(t,e,r),O=(t,e)=>{for(;e&&e.nodeType!=3;)e=e.nextSibling;return e?e.data!=t&&(e.data=t):e=document.createTextNode(t),e},T=({nodeName:t,children:e,key:r,skip:n,memo:s,ref:S,[c]:u,[a]:j,...x},A,i)=>{for(;i&&(i.localName!=t||i[y]!=null&&i[y]!=r||i[c]&&i[c]!=u);)i=i.nextSibling;return i??=document.createElementNS(x.xmlns??A.namespaceURI,t),r!=null&&(i[y]=r),(s==null||G(i[h],i[h]=s))&&(F(i[p]??R(i),i[p]=x,i),n||(u?B(u,j,i):f(e,i)),typeof S=="function"&&(i[b]=S)(i)),i},F=(t,e,r)=>{for(const n in{...t,...e})t[n]!==e[n]&&(n.startsWith("set:")?r[n.slice(4)]=e[n]:e[n]==null||e[n]===!1?r.removeAttribute(n):r.setAttribute(n,e[n]===!0?"":e[n]))},G=(t,e)=>Array.isArray(t)&&Array.isArray(e)?t.some((r,n)=>r!==e[n]):t!==e,R=t=>Array.from(t.attributes).reduce((e,r)=>(e[r.name]=r.value,e),{}),W=(t,e,r)=>{if(e.contains(document.activeElement)){const n=e.nextSibling;for(;r&&r!=e;){const s=r.nextSibling;t.insertBefore(r,n),r=s}}else t.insertBefore(e,r)},w=t=>{for(const e of t.children)w(e);typeof t.return=="function"&&t.return(),t[b]?.(null)},B=(t,e,r)=>{r[c]??=(I(r),t),Object.assign(r[a]??={},e),r[m]()},I=t=>{Object.assign(t,M),t[o.Context]=Object.create(o.current()?.[o.Context]??null)},M={[m](){const t=o.current();o.current(this);try{const{value:e,done:r}=(this[l]??=this[c].call(this,this[a])).next();f(e,this),this[b]?.(this),r&&this.return()}catch(e){this.throw(e)}finally{o.current(t)}},next(t){if(typeof t=="function")try{t.call(this,this[a])}catch(e){this.throw(e)}o.current()?.contains(this)||this[m]()},throw(t){for(let e=this;e;e=e.parentNode)if(typeof e[l]?.throw=="function")try{return f(e[l].throw(t).value,e)}catch(r){t=new Error(r instanceof Error?r.message:r,{cause:t})}throw t},return(){try{this[l]?.return()}catch(t){this.throw(t)}finally{this[l]=null}}};exports.Fragment=v;exports.h=C;exports.render=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./context.cjs"),y=Symbol.for("ajo.key"),j=Symbol.for("ajo.memo"),g=Symbol.for("ajo.ref"),A=Symbol.for("ajo.cache"),c=Symbol.for("ajo.generator"),a=Symbol.for("ajo.iterator"),b=Symbol.for("ajo.render"),l=Symbol.for("ajo.args"),N=e=>e.children,k=(e,t,...r)=>((t??={}).nodeName=e,!("children"in t)&&r.length&&(t.children=r.length==1?r[0]:r),t),f=(e,t,r=t.firstChild,s=null)=>{for(e of S(e)){const n=E(e,t,r);r==null?m(t,n,s):n==r?r=n.nextSibling:n==r.nextSibling?(m(t,r,s),r=n.nextSibling):m(t,n,r)}for(;r!=s;){const n=r.nextSibling;r.nodeType==1&&p(r),t.removeChild(r),r=n}},S=function*(e){if(e==null)return;const t=typeof e;if(t!="boolean")if(t=="string")yield e;else if(t=="number"||t=="bigint")yield String(e);else if(Symbol.iterator in e)for(e of e)yield*S(e);else"nodeName"in e?typeof e.nodeName=="function"?yield*O(e):yield e:yield String(e)},O=function*({nodeName:e,...t}){e.constructor.name=="GeneratorFunction"?yield T(e,t):yield*S(e(t))},T=(e,t)=>{const r={...e.attrs},s={...e.args};for(const n in t)n.startsWith("attr:")?r[n.slice(5)]=t[n]:n=="key"||n=="skip"||n=="memo"||n=="ref"||n.startsWith("set:")?r[n]=t[n]:s[n]=t[n];return{...r,nodeName:e.is??"div",[c]:e,[l]:s}},E=(e,t,r)=>typeof e=="string"?F(e,r):G(e,t,r),F=(e,t)=>{for(;t&&t.nodeType!=3;)t=t.nextSibling;return t?t.data!=e&&(t.data=e):t=document.createTextNode(e),t},G=({nodeName:e,children:t,key:r,skip:s,memo:n,ref:x,[c]:u,[l]:v,...w},C,i)=>{for(;i&&(i.localName!=e||i[y]!=null&&i[y]!=r||i[c]&&i[c]!=u);)i=i.nextSibling;return i??=document.createElementNS(w.xmlns??C.namespaceURI,e),r!=null&&(i[y]=r),(n==null||W(i[j],i[j]=n))&&(R(i[A]??B(i),i[A]=w,i),s||(u?I(u,v,i):f(t,i)),typeof x=="function"&&(i[g]=x)(i)),i},R=(e,t,r)=>{for(const s in{...e,...t})e[s]!==t[s]&&(s.startsWith("set:")?r[s.slice(4)]=t[s]:t[s]==null||t[s]===!1?r.removeAttribute(s):r.setAttribute(s,t[s]===!0?"":t[s]))},W=(e,t)=>Array.isArray(e)&&Array.isArray(t)?e.some((r,s)=>r!==t[s]):e!==t,B=e=>Array.from(e.attributes).reduce((t,r)=>(t[r.name]=r.value,t),{}),m=(e,t,r)=>{if(t.contains(document.activeElement)){const s=t.nextSibling;for(;r&&r!=t;){const n=r.nextSibling;e.insertBefore(r,s),r=n}}else e.insertBefore(t,r)},p=e=>{for(const t of e.children)p(t);typeof e.return=="function"&&e.return(),e[g]?.(null)},I=(e,t,r)=>{r[c]??=(M(r),e),Object.assign(r[l]??={},t),r[b]()},M=e=>{Object.assign(e,h),e[o.Context]=Object.create(o.current()?.[o.Context]??null)},h={[b](){const e=o.current();o.current(this);try{const{value:t,done:r}=(this[a]??=this[c].call(this,this[l])).next();f(t,this),this[g]?.(this),r&&this.return()}catch(t){this.throw(t)}finally{o.current(e)}},next(e){try{e?.call(this,this[l])}catch(t){return this.throw(t)}o.current()?.contains(this)||this[b]()},throw(e){for(let t=this;t;t=t.parentNode)if(t[a]?.throw)try{return f(t[a].throw(e).value,t)}catch(r){e=new Error(r?.message??r,{cause:e})}throw e},return(){try{this[a]?.return()}catch(e){this.throw(e)}finally{this[a]=null}}};exports.Fragment=N;exports.h=k;exports.render=f;
package/dist/index.js CHANGED
@@ -1,94 +1,93 @@
1
- import { Context as h, current as l } from "./context.js";
2
- const u = Symbol.for("ajo.key"), p = Symbol.for("ajo.memo"), b = Symbol.for("ajo.ref"), w = Symbol.for("ajo.cache"), o = Symbol.for("ajo.generator"), c = Symbol.for("ajo.iterator"), y = Symbol.for("ajo.render"), a = Symbol.for("ajo.args"), M = (t) => t.children, U = (t, e, ...r) => ((e ??= {}).nodeName = t, !("children" in e) && r.length && (e.children = r.length == 1 ? r[0] : r), e), m = (t, e) => {
3
- let r = e.firstChild;
4
- for (t of g(t)) {
5
- const i = k(t, e, r);
6
- r == null ? e.appendChild(i) : i == r ? r = i.nextSibling : i == r.nextSibling ? (e.appendChild(r), r = i.nextSibling) : W(e, i, r);
1
+ import { Context as j, current as c } from "./context.js";
2
+ const u = /* @__PURE__ */ Symbol.for("ajo.key"), p = /* @__PURE__ */ Symbol.for("ajo.memo"), g = /* @__PURE__ */ Symbol.for("ajo.ref"), A = /* @__PURE__ */ Symbol.for("ajo.cache"), o = /* @__PURE__ */ Symbol.for("ajo.generator"), a = /* @__PURE__ */ Symbol.for("ajo.iterator"), m = /* @__PURE__ */ Symbol.for("ajo.render"), l = /* @__PURE__ */ Symbol.for("ajo.args"), U = (e) => e.children, h = (e, t, ...r) => ((t ??= {}).nodeName = e, !("children" in t) && r.length && (t.children = r.length == 1 ? r[0] : r), t), b = (e, t, r = t.firstChild, s = null) => {
3
+ for (e of S(e)) {
4
+ const n = G(e, t, r);
5
+ r == null ? y(t, n, s) : n == r ? r = n.nextSibling : n == r.nextSibling ? (y(t, r, s), r = n.nextSibling) : y(t, n, r);
7
6
  }
8
- for (; r; ) {
9
- const i = r.nextSibling;
10
- r.nodeType == 1 && j(r), e.removeChild(r), r = i;
7
+ for (; r != s; ) {
8
+ const n = r.nextSibling;
9
+ r.nodeType == 1 && N(r), t.removeChild(r), r = n;
11
10
  }
12
- }, g = function* (t) {
13
- if (t == null) return;
14
- const e = typeof t;
15
- if (e != "boolean")
16
- if (e == "string") yield t;
17
- else if (e == "number" || e == "bigint") yield String(t);
18
- else if (Symbol.iterator in t) for (t of t) yield* g(t);
19
- else "nodeName" in t ? typeof t.nodeName == "function" ? yield* C(t) : yield t : yield String(t);
20
- }, C = function* ({ nodeName: t, ...e }) {
21
- t.constructor.name == "GeneratorFunction" ? yield N(t, e) : yield* g(t(e));
22
- }, N = function(t, e) {
23
- const r = { ...t.attrs }, i = { ...t.args };
24
- for (const s in e)
25
- s.startsWith("attr:") ? r[s.slice(5)] = e[s] : s == "key" || s == "skip" || s == "memo" || s == "ref" || s.startsWith("set:") ? r[s] = e[s] : i[s] = e[s];
26
- return { ...r, nodeName: t.is ?? "div", [o]: t, [a]: i };
27
- }, k = (t, e, r) => typeof t == "string" ? E(t, r) : G(t, e, r), E = (t, e) => {
28
- for (; e && e.nodeType != 3; ) e = e.nextSibling;
29
- return e ? e.data != t && (e.data = t) : e = document.createTextNode(t), e;
30
- }, G = ({ nodeName: t, children: e, key: r, skip: i, memo: s, ref: S, [o]: f, [a]: A, ...x }, v, n) => {
31
- for (; n && (n.localName != t || n[u] != null && n[u] != r || n[o] && n[o] != f); ) n = n.nextSibling;
32
- return n ??= document.createElementNS(x.xmlns ?? v.namespaceURI, t), r != null && (n[u] = r), (s == null || R(n[p], n[p] = s)) && (O(n[w] ?? T(n), n[w] = x, n), i || (f ? B(f, A, n) : m(e, n)), typeof S == "function" && (n[b] = S)(n)), n;
33
- }, O = (t, e, r) => {
34
- for (const i in { ...t, ...e })
35
- t[i] !== e[i] && (i.startsWith("set:") ? r[i.slice(4)] = e[i] : e[i] == null || e[i] === !1 ? r.removeAttribute(i) : r.setAttribute(i, e[i] === !0 ? "" : e[i]));
36
- }, R = (t, e) => Array.isArray(t) && Array.isArray(e) ? t.some((r, i) => r !== e[i]) : t !== e, T = (t) => Array.from(t.attributes).reduce((e, r) => (e[r.name] = r.value, e), {}), W = (t, e, r) => {
37
- if (e.contains(document.activeElement)) {
38
- const i = e.nextSibling;
39
- for (; r && r != e; ) {
40
- const s = r.nextSibling;
41
- t.insertBefore(r, i), r = s;
11
+ }, S = function* (e) {
12
+ if (e == null) return;
13
+ const t = typeof e;
14
+ if (t != "boolean")
15
+ if (t == "string") yield e;
16
+ else if (t == "number" || t == "bigint") yield String(e);
17
+ else if (Symbol.iterator in e) for (e of e) yield* S(e);
18
+ else "nodeName" in e ? typeof e.nodeName == "function" ? yield* C(e) : yield e : yield String(e);
19
+ }, C = function* ({ nodeName: e, ...t }) {
20
+ e.constructor.name == "GeneratorFunction" ? yield E(e, t) : yield* S(e(t));
21
+ }, E = (e, t) => {
22
+ const r = { ...e.attrs }, s = { ...e.args };
23
+ for (const n in t)
24
+ n.startsWith("attr:") ? r[n.slice(5)] = t[n] : n == "key" || n == "skip" || n == "memo" || n == "ref" || n.startsWith("set:") ? r[n] = t[n] : s[n] = t[n];
25
+ return { ...r, nodeName: e.is ?? "div", [o]: e, [l]: s };
26
+ }, G = (e, t, r) => typeof e == "string" ? O(e, r) : R(e, t, r), O = (e, t) => {
27
+ for (; t && t.nodeType != 3; ) t = t.nextSibling;
28
+ return t ? t.data != e && (t.data = e) : t = document.createTextNode(e), t;
29
+ }, R = ({ nodeName: e, children: t, key: r, skip: s, memo: n, ref: x, [o]: f, [l]: k, ...w }, v, i) => {
30
+ for (; i && (i.localName != e || i[u] != null && i[u] != r || i[o] && i[o] != f); ) i = i.nextSibling;
31
+ return i ??= document.createElementNS(w.xmlns ?? v.namespaceURI, e), r != null && (i[u] = r), (n == null || W(i[p], i[p] = n)) && (T(i[A] ?? B(i), i[A] = w, i), s || (f ? F(f, k, i) : b(t, i)), typeof x == "function" && (i[g] = x)(i)), i;
32
+ }, T = (e, t, r) => {
33
+ for (const s in { ...e, ...t })
34
+ e[s] !== t[s] && (s.startsWith("set:") ? r[s.slice(4)] = t[s] : t[s] == null || t[s] === !1 ? r.removeAttribute(s) : r.setAttribute(s, t[s] === !0 ? "" : t[s]));
35
+ }, W = (e, t) => Array.isArray(e) && Array.isArray(t) ? e.some((r, s) => r !== t[s]) : e !== t, B = (e) => Array.from(e.attributes).reduce((t, r) => (t[r.name] = r.value, t), {}), y = (e, t, r) => {
36
+ if (t.contains(document.activeElement)) {
37
+ const s = t.nextSibling;
38
+ for (; r && r != t; ) {
39
+ const n = r.nextSibling;
40
+ e.insertBefore(r, s), r = n;
42
41
  }
43
- } else t.insertBefore(e, r);
44
- }, j = (t) => {
45
- for (const e of t.children) j(e);
46
- typeof t.return == "function" && t.return(), t[b]?.(null);
47
- }, B = (t, e, r) => {
48
- r[o] ??= (F(r), t), Object.assign(r[a] ??= {}, e), r[y]();
49
- }, F = (t) => {
50
- Object.assign(t, I), t[h] = Object.create(l()?.[h] ?? null);
51
- }, I = {
52
- [y]() {
53
- const t = l();
54
- l(this);
42
+ } else e.insertBefore(t, r);
43
+ }, N = (e) => {
44
+ for (const t of e.children) N(t);
45
+ typeof e.return == "function" && e.return(), e[g]?.(null);
46
+ }, F = (e, t, r) => {
47
+ r[o] ??= (I(r), e), Object.assign(r[l] ??= {}, t), r[m]();
48
+ }, I = (e) => {
49
+ Object.assign(e, K), e[j] = Object.create(c()?.[j] ?? null);
50
+ }, K = {
51
+ [m]() {
52
+ const e = c();
53
+ c(this);
55
54
  try {
56
- const { value: e, done: r } = (this[c] ??= this[o].call(this, this[a])).next();
57
- m(e, this), this[b]?.(this), r && this.return();
58
- } catch (e) {
59
- this.throw(e);
55
+ const { value: t, done: r } = (this[a] ??= this[o].call(this, this[l])).next();
56
+ b(t, this), this[g]?.(this), r && this.return();
57
+ } catch (t) {
58
+ this.throw(t);
60
59
  } finally {
61
- l(t);
60
+ c(e);
62
61
  }
63
62
  },
64
- next(t) {
65
- if (typeof t == "function") try {
66
- t.call(this, this[a]);
67
- } catch (e) {
68
- this.throw(e);
63
+ next(e) {
64
+ try {
65
+ e?.call(this, this[l]);
66
+ } catch (t) {
67
+ return this.throw(t);
69
68
  }
70
- l()?.contains(this) || this[y]();
69
+ c()?.contains(this) || this[m]();
71
70
  },
72
- throw(t) {
73
- for (let e = this; e; e = e.parentNode) if (typeof e[c]?.throw == "function") try {
74
- return m(e[c].throw(t).value, e);
71
+ throw(e) {
72
+ for (let t = this; t; t = t.parentNode) if (t[a]?.throw) try {
73
+ return b(t[a].throw(e).value, t);
75
74
  } catch (r) {
76
- t = new Error(r instanceof Error ? r.message : r, { cause: t });
75
+ e = new Error(r?.message ?? r, { cause: e });
77
76
  }
78
- throw t;
77
+ throw e;
79
78
  },
80
79
  return() {
81
80
  try {
82
- this[c]?.return();
83
- } catch (t) {
84
- this.throw(t);
81
+ this[a]?.return();
82
+ } catch (e) {
83
+ this.throw(e);
85
84
  } finally {
86
- this[c] = null;
85
+ this[a] = null;
87
86
  }
88
87
  }
89
88
  };
90
89
  export {
91
- M as Fragment,
92
- U as h,
93
- m as render
90
+ U as Fragment,
91
+ h,
92
+ b as render
94
93
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "ajo is a JavaScript view library for building user interfaces",
5
5
  "type": "module",
6
6
  "types": "./types.ts",
@@ -33,11 +33,11 @@
33
33
  "types.ts"
34
34
  ],
35
35
  "devDependencies": {
36
- "@types/node": "24.3.0",
37
- "happy-dom": "18.0.1",
38
- "vite": "7.1.4",
39
- "vite-tsconfig-paths": "5.1.4",
40
- "vitest": "3.2.4"
36
+ "@types/node": "25.0.9",
37
+ "happy-dom": "20.3.1",
38
+ "vite": "7.3.1",
39
+ "vite-tsconfig-paths": "6.0.4",
40
+ "vitest": "4.0.17"
41
41
  },
42
42
  "keywords": [
43
43
  "ui",
package/readme.md CHANGED
@@ -15,320 +15,433 @@
15
15
 
16
16
  A modern JavaScript library for building user interfaces with generator-based state management, efficient DOM updates, and streaming server-side rendering.
17
17
 
18
- ## Features
19
-
20
- - **Generator-Based Components**: Use JavaScript generator functions (`function*`) for stateful components with built-in lifecycle management
21
- - **Efficient DOM Updates**: In-place DOM reconciliation minimizes DOM manipulation and maximizes performance
22
- - **Declarative JSX**: Write components using familiar JSX syntax with full TypeScript support
23
- - **Performance Optimization**: Built-in `memo` attribute for fine-grained performance optimization
24
- - **Context API**: Share state across component trees without prop drilling
25
- - **Server-Side Rendering**: Complete SSR solution with streaming and hydration support
26
- - **Islands Architecture**: Selective hydration for maximum performance with minimal client-side JavaScript
18
+ - **Generator-Based Components**: Use `function*` for stateful components with built-in lifecycle
19
+ - **Efficient DOM Updates**: In-place reconciliation minimizes DOM manipulation
20
+ - **Streaming SSR**: Progressive rendering with selective hydration (islands)
27
21
 
28
22
  ## Quick Start
29
23
 
30
- Install Ajo using your preferred package manager:
31
-
32
24
  ```bash
33
25
  npm install ajo
34
26
  ```
35
27
 
36
- Create your first component:
37
-
38
28
  ```javascript
39
29
  import { render } from 'ajo'
40
30
 
41
- // Stateless component
42
- const Greeting = ({ name }) => <p>Hello, {name}!</p>
43
-
44
- // Stateful component
45
31
  function* Counter() {
46
-
47
32
  let count = 0
48
33
 
49
- const increment = () => this.next(() => count++)
50
-
51
34
  while (true) yield (
52
- <button set:onclick={increment}>
35
+ <button set:onclick={() => this.next(() => count++)}>
53
36
  Count: {count}
54
37
  </button>
55
38
  )
56
39
  }
57
40
 
58
- // Render to DOM
59
41
  render(<Counter />, document.body)
60
42
  ```
61
43
 
62
- ## Core Concepts
44
+ ### Build Configuration
63
45
 
64
- ### Component Types
46
+ Configure your build tool to use Ajo's JSX factory:
65
47
 
66
- **Stateless Components** are pure functions:
67
- ```javascript
68
- const UserCard = ({ user }) => (
69
- <div class="user-card">
70
- <h3>{user.name}</h3>
71
- <p>{user.email}</p>
72
- </div>
73
- );
48
+ **Vite:**
49
+ ```typescript
50
+ // vite.config.ts
51
+ import { defineConfig } from 'vite'
52
+
53
+ export default defineConfig({
54
+ esbuild: {
55
+ jsxFactory: 'h',
56
+ jsxFragment: 'Fragment',
57
+ jsxInject: `import { h, Fragment } from 'ajo'`,
58
+ },
59
+ })
74
60
  ```
75
61
 
76
- **Stateful Components** use generator functions with automatic wrapper elements:
77
- ```javascript
78
- function* TodoList() {
79
-
80
- let todos = []
81
-
82
- const addTodo = text => this.next(() => todos.push({ id: Date.now(), text }))
83
-
84
- while (true) yield (
85
- <>
86
- <input set:onkeydown={e => e.key === 'Enter' && addTodo(e.target.value)} />
87
- <ul>
88
- {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
89
- </ul>
90
- </>
91
- )
62
+ **TypeScript:**
63
+ ```json
64
+ {
65
+ "compilerOptions": {
66
+ "jsx": "react",
67
+ "jsxFactory": "h",
68
+ "jsxFragmentFactory": "Fragment"
69
+ }
92
70
  }
93
71
  ```
94
72
 
95
- ### State Management Pattern
73
+ **Other build tools:** Set `jsxFactory: 'h'`, `jsxFragment: 'Fragment'`, and auto-import `{ h, Fragment }` from `'ajo'`.
96
74
 
97
- The generator structure provides a natural mental model:
98
- - **Before the loop**: Persistent state, handlers, and utilities
99
- - **Inside the loop**: Re-executed on each render for derived values
75
+ ## Core Concepts
76
+
77
+ ### Stateless Components
78
+
79
+ Pure functions that receive props and return JSX:
100
80
 
101
81
  ```javascript
102
- function* ShoppingCart(args) {
82
+ const Greeting = ({ name }) => <p>Hello, {name}!</p>
83
+ ```
103
84
 
104
- // Persistent state (like useState)
105
- let items = []
85
+ ### Stateful Components
106
86
 
107
- // Persistent handlers (like useCallback)
108
- const addItem = product => this.next(() => items.push(product))
87
+ Generator functions with automatic wrapper elements. The structure provides a natural mental model:
109
88
 
110
- // Main render loop
111
- while (true) {
89
+ - **Before the loop**: Persistent state and handlers (survives re-renders)
90
+ - **Inside the loop**: Derived values (computed fresh each render)
91
+
92
+ ```javascript
93
+ function* TodoList() {
94
+ let todos = []
95
+ let text = ''
112
96
 
113
- // Derived values computed fresh each render
114
- const total = items.reduce((sum, item) => sum + item.price, 0)
115
- const itemCount = items.length
97
+ const add = () => this.next(() => {
98
+ if (text.trim()) {
99
+ todos.push({ id: Date.now(), text })
100
+ text = ''
101
+ }
102
+ })
103
+
104
+ while (true) {
105
+ const count = todos.length
116
106
 
117
107
  yield (
118
108
  <>
119
- <h2>Cart ({itemCount} items)</h2>
120
- <p>Total: ${total}</p>
121
- {/* ... */}
109
+ <input
110
+ set:value={text}
111
+ set:oninput={e => text = e.target.value}
112
+ set:onkeydown={e => e.key === 'Enter' && add()}
113
+ />
114
+ <button set:onclick={add}>Add ({count})</button>
115
+ <ul>
116
+ {todos.map(t => <li key={t.id}>{t.text}</li>)}
117
+ </ul>
122
118
  </>
123
119
  )
124
120
  }
125
121
  }
126
122
  ```
127
123
 
128
- ### Special Attributes
124
+ ### Re-rendering with `this.next()`
129
125
 
130
- - **`key`**: List reconciliation
131
- - **`ref`**: DOM element access
132
- - **`memo`**: Performance optimization
133
- - **`skip`**: Third-party DOM management
134
- - **`set:`**: Direct property setting
135
- - **`attr:`**: Force HTML attributes
126
+ Call `this.next()` to trigger a re-render. The optional callback receives current props, useful when props may have changed:
136
127
 
137
128
  ```javascript
138
- function* MapComponent(args) {
129
+ function* Stepper(args) {
130
+ let count = 0
139
131
 
140
- let mapRef = null
132
+ // Access current props in callback
133
+ const inc = () => this.next(({ step = 1 }) => count += step)
141
134
 
142
135
  while (true) yield (
143
- <div
144
- ref={el => {
145
- if (el && !mapRef) {
146
- mapRef = el
147
- // Third-party map library controls this DOM
148
- new GoogleMap(el, args.config)
149
- }
150
- }}
151
- skip={true}
152
- >
153
- {/* Google Maps API manages children elements */}
154
- </div>
136
+ <button set:onclick={inc}>Count: {count} (+{args.step})</button>
155
137
  )
156
138
  }
157
139
  ```
158
140
 
159
- ## Server-Side Rendering
141
+ **Never destructure args in the generator signature** - it locks values to initial props:
160
142
 
161
- ### Static SSR
162
143
  ```javascript
163
- import { render } from 'ajo/html'
144
+ // DON'T - values frozen at mount
145
+ function* Bad({ step }) { let count = step }
164
146
 
165
- const html = render(<App />)
147
+ // DO - use args directly, or destructure inside the loop
148
+ function* Good(args) {
149
+ while (true) {
150
+ const { step } = args // fresh each render
151
+ yield ...
152
+ }
153
+ }
166
154
  ```
167
155
 
168
- ### Streaming SSR
169
- ```javascript
170
- import { stream } from 'ajo/stream'
156
+ ### Lifecycle and Cleanup
157
+
158
+ Use `try...finally` for cleanup when the component unmounts:
171
159
 
172
- for await (const chunk of stream(<App />)) response.write(chunk)
160
+ ```javascript
161
+ function* Clock() {
162
+ let time = new Date()
163
+ const interval = setInterval(() => this.next(() => time = new Date()), 1000)
164
+
165
+ try {
166
+ while (true) yield <p>{time.toLocaleTimeString()}</p>
167
+ } finally {
168
+ clearInterval(interval)
169
+ }
170
+ }
173
171
  ```
174
172
 
175
- ## Best Practices
173
+ ### Error Boundaries
176
174
 
177
- 1. **Use fragments in stateful components** to avoid unnecessary DOM nesting
178
- 2. **Don't destructure props in function signatures** - use `args` parameter or destructure inside the render loop
179
- 3. **Leverage the generator structure** for natural state and lifecycle management
180
- 4. **Use TypeScript** for enhanced developer experience
175
+ Use `try...catch` **inside** the loop to catch errors and recover:
181
176
 
182
- ## API Reference
177
+ ```javascript
178
+ function* ErrorBoundary(args) {
179
+ while (true) {
180
+ try {
181
+ yield args.children
182
+ } catch (error) {
183
+ yield (
184
+ <>
185
+ <p>Error: {error.message}</p>
186
+ <button set:onclick={() => this.next()}>Retry</button>
187
+ </>
188
+ )
189
+ }
190
+ }
191
+ }
192
+ ```
193
+
194
+ ## Special Attributes
183
195
 
184
- ### Core Module (`ajo`)
196
+ | Attribute | Description |
197
+ |-----------|-------------|
198
+ | `key` | Unique identifier for list reconciliation |
199
+ | `ref` | Callback receiving DOM element (or `null` on unmount) |
200
+ | `memo` | Skip reconciliation: `memo={[deps]}`, `memo={value}`, or `memo` (render once) |
201
+ | `skip` | Exclude children from reconciliation (required with `set:innerHTML`) |
202
+ | `set:*` | Set DOM properties instead of HTML attributes |
203
+ | `attr:*` | Force HTML attributes on stateful component wrappers |
185
204
 
186
- #### `render(children: Children, container: Element): void`
187
- Renders JSX into a DOM container element.
205
+ ### `set:` - DOM Properties vs HTML Attributes
188
206
 
189
207
  ```javascript
190
- import { render } from 'ajo'
208
+ // Events (always use set:)
209
+ <button set:onclick={handleClick}>Click</button>
191
210
 
192
- render(<App />, document.getElementById('root'))
193
- ```
211
+ // Dynamic values that need to sync with state
212
+ <input set:value={text} /> // DOM property (syncs)
213
+ <input value="initial" /> // HTML attribute (initial only)
194
214
 
195
- #### `h(type: Type, props?: Props, ...children: Children[]): VNode`
196
- JSX factory function (rarely used directly).
215
+ <input type="checkbox" set:checked={bool} />
216
+ <video set:currentTime={0} set:muted />
217
+
218
+ // innerHTML requires skip
219
+ <div set:innerHTML={html} skip />
220
+ ```
197
221
 
198
- #### `Fragment({ children }: { children: Children }): Children`
199
- JSX fragment component for rendering multiple elements without a wrapper.
222
+ ### `ref` - DOM Access
200
223
 
201
224
  ```javascript
202
- const List = () => (
203
- <>
204
- <li>Item 1</li>
205
- <li>Item 2</li>
206
- </>
207
- )
208
- ```
225
+ function* AutoFocus() {
226
+ let input = null
209
227
 
210
- ### Stateful Component Instance Methods
228
+ while (true) yield (
229
+ <>
230
+ <input ref={el => el?.focus()} />
231
+ <button set:onclick={() => input?.select()}>Select</button>
232
+ </>
233
+ )
234
+ }
211
235
 
212
- Stateful components have access to several instance methods through `this`:
236
+ // Ref to stateful component includes control methods
237
+ let timer = null
238
+ <Clock ref={el => timer = el} />
239
+ timer?.next() // trigger re-render from outside
240
+ ```
213
241
 
214
- #### `this.next(callback?: (args: ComponentArgs) => void): void`
215
- Triggers a re-render of the component by advancing to the next yield point. Optionally accepts a callback function that receives the component's current props/args as the first parameter.
242
+ ### `memo` - Performance Optimization
216
243
 
217
244
  ```javascript
218
- function* Counter(args) {
219
-
220
- let count = 0
245
+ <div memo={[user.id]}>...</div> // re-render when user.id changes
246
+ <div memo={count}>...</div> // re-render when count changes
247
+ <footer memo>Static content</footer> // render once, never update
248
+ ```
221
249
 
222
- const increment = () => {
223
- // Simple re-render
224
- this.next(() => count++)
225
- }
250
+ ### `skip` - Third-Party DOM
226
251
 
227
- const incrementByStep = () => {
228
- // Access current props in callback
229
- this.next(({ step }) => count += step)
230
- }
252
+ ```javascript
253
+ function* Chart(args) {
254
+ let chart = null
231
255
 
232
- // ... rest of component
256
+ while (true) yield (
257
+ <div skip ref={el => el && (chart ??= new ChartLib(el, args.data))} />
258
+ )
233
259
  }
234
260
  ```
235
261
 
236
- #### `this.throw(error: unknown): void`
237
- Throws an error that can be caught by parent error boundaries.
262
+ ### `attr:` - Wrapper Attributes
238
263
 
239
- #### `this.return(): void`
240
- Terminates the generator and triggers cleanup (rarely used directly).
264
+ ```javascript
265
+ <Counter
266
+ initial={0} // → args
267
+ attr:id="main" // → wrapper HTML attribute
268
+ attr:class="widget" // → wrapper HTML attribute
269
+ set:onclick={fn} // → wrapper DOM property
270
+ />
271
+ ```
241
272
 
242
- ### Context Module (`ajo/context`)
273
+ ## Context API
243
274
 
244
- #### `context<T>(fallback?: T): ContextFunction<T>`
245
- Creates a context for sharing data across component trees.
275
+ Share data across component trees without prop drilling:
246
276
 
247
277
  ```javascript
248
278
  import { context } from 'ajo/context'
249
279
 
250
280
  const ThemeContext = context('light')
251
-
252
- // Set value
253
- ThemeContext('dark')
254
-
255
- // Get value
256
- const theme = ThemeContext() // 'dark'
257
281
  ```
258
282
 
259
- ### HTML Module (`ajo/html`)
260
-
261
- #### `render(children: Children): string`
262
- Renders JSX to an HTML string for static site generation.
283
+ **Stateless**: read only.
284
+ **Stateful**: read/write. Write inside the loop to update each render, or outside for a one-time set.
263
285
 
264
286
  ```javascript
265
- import { render } from 'ajo/html'
287
+ // Stateless - read only
288
+ const Card = ({ title }) => {
289
+ const theme = ThemeContext()
290
+ return <div class={`card theme-${theme}`}>{title}</div>
291
+ }
292
+
293
+ // Stateful - write inside loop (updates each render)
294
+ function* ThemeProvider(args) {
295
+ let theme = 'light'
266
296
 
267
- const html = render(<HomePage title="Welcome" />)
297
+ while (true) {
298
+ ThemeContext(theme)
299
+ yield (
300
+ <>
301
+ <button set:onclick={() => this.next(() => theme = theme === 'light' ? 'dark' : 'light')}>
302
+ {theme}
303
+ </button>
304
+ {args.children}
305
+ </>
306
+ )
307
+ }
308
+ }
309
+
310
+ // Stateful - write outside loop (one-time set)
311
+ function* FixedTheme(args) {
312
+ ThemeContext('dark') // set once at mount
313
+ while (true) yield args.children
314
+ }
268
315
  ```
269
316
 
270
- #### `html(children: Children, hooks?: Hooks): IterableIterator<string>`
271
- Low-level HTML streaming function with custom hooks.
317
+ ## Async Operations
318
+
319
+ ```javascript
320
+ function* UserProfile(args) {
321
+ let data = null, error = null, loading = true
272
322
 
273
- ### Stream Module (`ajo/stream`)
323
+ fetch(`/api/users/${args.id}`)
324
+ .then(r => r.json())
325
+ .then(d => this.next(() => { data = d; loading = false }))
326
+ .catch(e => this.next(() => { error = e; loading = false }))
274
327
 
275
- #### `stream(children: Children): AsyncIterableIterator<string>`
276
- Renders components to an async stream for progressive SSR.
328
+ while (true) {
329
+ if (loading) yield <p>Loading...</p>
330
+ else if (error) yield <p>Error: {error.message}</p>
331
+ else yield <h1>{data.name}</h1>
332
+ }
333
+ }
334
+ ```
335
+
336
+ ## Server-Side Rendering
277
337
 
278
338
  ```javascript
339
+ // Static
340
+ import { render } from 'ajo/html'
341
+ const html = render(<App />)
342
+
343
+ // Streaming
279
344
  import { stream } from 'ajo/stream'
345
+ for await (const chunk of stream(<App />)) res.write(chunk)
280
346
 
281
- for await (const chunk of stream(<App />)) response.write(chunk)
347
+ // Hydration (client-side)
348
+ import { hydrate } from 'ajo/stream'
349
+ window.$stream = { push: hydrate }
282
350
  ```
283
351
 
284
- #### `hydrate(patch: Patch): Promise<void>`
285
- Client-side function for applying streamed patches during hydration.
352
+ ### Islands Architecture
286
353
 
287
354
  ```javascript
288
- import { hydrate } from 'ajo/stream'
355
+ function* Interactive() {
356
+ let count = 0
357
+ while (true) yield (
358
+ <button set:onclick={() => this.next(() => count++)}>
359
+ {count}
360
+ </button>
361
+ )
362
+ }
289
363
 
290
- window.$stream = { push: hydrate }
364
+ Interactive.src = '/islands/interactive.js' // hydrate on client
365
+
366
+ const Page = () => (
367
+ <html>
368
+ <body>
369
+ <p>Static content</p>
370
+ <Interactive fallback={<button>0</button>} />
371
+ </body>
372
+ </html>
373
+ )
291
374
  ```
292
375
 
293
- ### TypeScript Support
376
+ ## TypeScript
294
377
 
295
378
  ```typescript
296
- // Component types
297
- type Stateless<Props = {}> = (props: Props) => Children
298
- type Stateful<Props = {}, Tag = 'div'> = {
299
- (this: StatefulElement<Tag>, props: Props): Iterator<Children>
300
- is?: Tag
301
- attrs?: Record<string, unknown>
302
- args?: Partial<Props>
379
+ import type { Stateless, Stateful, WithChildren } from 'ajo'
380
+
381
+ // Stateless
382
+ type CardProps = WithChildren<{ title: string }>
383
+ const Card: Stateless<CardProps> = ({ title, children }) => (
384
+ <div class="card"><h3>{title}</h3>{children}</div>
385
+ )
386
+
387
+ // Stateful with custom wrapper element
388
+ type CounterProps = { initial: number; step?: number }
389
+
390
+ const Counter: Stateful<CounterProps, 'section'> = function* (args) {
391
+ let count = args.initial
392
+
393
+ while (true) {
394
+ const { step = 1 } = args
395
+ yield <button set:onclick={() => this.next(() => count += step)}>+{step}</button>
396
+ }
303
397
  }
304
398
 
305
- // Stateful component instance
306
- type StatefulElement<Tag> = HTMLElement & {
307
- next: (callback?: (args: ComponentArgs) => void) => void
308
- throw: (error: unknown) => void
309
- return: () => void
310
- };
311
-
312
- // Element types
313
- type Children = unknown
314
- type VNode<Type, Props> = Props & {
315
- nodeName: Type
316
- children?: Children
317
- };
318
-
319
- // Special attributes
320
- type SpecialAttributes = {
321
- key?: unknown
322
- ref?: (element: Element | null) => void
323
- memo?: unknown[]
324
- skip?: boolean
325
- };
399
+ Counter.is = 'section' // wrapper element (default: 'div')
400
+ Counter.attrs = { class: 'counter' } // default wrapper attributes
401
+ Counter.args = { step: 1 } // default args
402
+
403
+ // Ref typing
404
+ let ref: ThisParameterType<typeof Counter> | null = null
405
+ <Counter ref={el => ref = el} initial={0} />
326
406
  ```
327
407
 
328
- ## Documentation
408
+ ## API Reference
409
+
410
+ ### `ajo`
411
+ | Export | Description |
412
+ |--------|-------------|
413
+ | `render(children, container, start?, end?)` | Render to DOM. Optional `start`/`end` for targeted updates. |
414
+ | `h`, `Fragment` | JSX factory and fragment |
415
+
416
+ ### `ajo/context`
417
+ | Export | Description |
418
+ |--------|-------------|
419
+ | `context<T>(fallback?)` | Create context. Call with value to write, without to read. |
420
+
421
+ ### `ajo/html`
422
+ | Export | Description |
423
+ |--------|-------------|
424
+ | `render(children)` | Render to HTML string |
425
+
426
+ ### `ajo/stream`
427
+ | Export | Description |
428
+ |--------|-------------|
429
+ | `stream(children)` | Async iterator for streaming SSR |
430
+ | `hydrate(patch)` | Apply streamed patch on client |
431
+
432
+ ### Stateful `this`
433
+ | Method | Description |
434
+ |--------|-------------|
435
+ | `this.next(fn?)` | Re-render. Callback receives current args. |
436
+ | `this.throw(error)` | Throw to parent boundary |
437
+ | `this.return()` | Terminate generator |
438
+
439
+ `this` is also the wrapper element (`this.addEventListener()`, etc).
440
+
441
+ ## For AI Assistants
329
442
 
330
- For comprehensive guides, advanced patterns, and detailed examples, see [documentation.md](./documentation.md).
443
+ See [LLMs.md](./LLMs.md) for a condensed reference.
331
444
 
332
445
  ## License
333
446
 
334
- ISC © [Cristian Falcone](cristianfalcone.com)
447
+ ISC © [Cristian Falcone](https://cristianfalcone.com)
package/types.ts CHANGED
@@ -2,13 +2,13 @@ declare module 'ajo' {
2
2
 
3
3
  type Tag = keyof (HTMLElementTagNameMap & SVGElementTagNameMap)
4
4
 
5
- type Type = Tag | Stateless | Stateful
5
+ type Type<TArguments extends Args = {}, TTag extends Tag = 'div'> = Tag | Stateless<TArguments> | Stateful<TArguments, TTag>
6
6
 
7
- type Component<TProps extends Props = {}> = Stateless<TProps> | Stateful<TProps>
7
+ type Component<TArguments extends Args = {}> = Stateless<TArguments> | Stateful<TArguments>
8
8
 
9
- type Props = Record<string, unknown>
9
+ type Args = Record<string, unknown>
10
10
 
11
- type VNode<TTag extends Type, TProps extends Props> = TProps & {
11
+ type VNode<TTag extends Type, TArgs extends Args> = TArgs & {
12
12
  nodeName: TTag,
13
13
  children?: Children,
14
14
  }
@@ -21,7 +21,7 @@ declare module 'ajo' {
21
21
  ? SVGElementTagNameMap[TTag]
22
22
  : never
23
23
 
24
- type SpecialProps<TElement> = {
24
+ type SpecialAttrs<TElement> = {
25
25
  key: unknown,
26
26
  skip: boolean,
27
27
  memo: unknown,
@@ -36,36 +36,47 @@ declare module 'ajo' {
36
36
  [key: `attr:${string}`]: unknown
37
37
  }
38
38
 
39
- type Stateless<TArguments extends Props = {}> = (args: TArguments) => Children
39
+ type Stateless<TArguments extends Args = {}> = (args: TArguments) => Children
40
40
 
41
- type Stateful<TArguments extends Props = {}, TTag extends Tag = 'div'> = {
42
- (this: StatefulElement<TArguments, TTag>, args: StatefulProps<TArguments, TTag>): Iterator<Children>
43
- } & (TTag extends 'div' ? { is?: TTag } : { is: TTag }) & { attrs?: Partial<PropSetter<TTag>> & Props, args?: Partial<TArguments> }
41
+ type Stateful<TArguments extends Args = {}, TTag extends Tag = 'div'> = {
42
+ (this: StatefulElement<TArguments, TTag>, args: StatefulArgs<TArguments, TTag>): Iterator<Children>
43
+ } & (TTag extends 'div' ? { is?: TTag } : { is: TTag }) & {
44
+ attrs?: Partial<PropSetter<TTag>> & Args,
45
+ args?: Partial<TArguments>,
46
+ src?: string,
47
+ fallback?: Children,
48
+ }
44
49
 
45
- type StatefulProps<TArguments, TTag> =
46
- Partial<SpecialProps<StatefulElement<TArguments, TTag>> & PropSetter<TTag>> &
50
+ type StatefulArgs<TArguments, TTag> =
51
+ Partial<SpecialAttrs<StatefulElement<TArguments, TTag>> & PropSetter<TTag>> &
47
52
  AttrSetter &
48
53
  TArguments
49
54
 
50
55
  type StatefulElement<TArguments, TTag> = ElementType<TTag> & {
51
- next: (fn?: (this: StatefulElement<TArguments, TTag>, args: StatefulProps<TArguments, TTag>) => void) => void,
56
+ next: (fn?: (this: StatefulElement<TArguments, TTag>, args: StatefulArgs<TArguments, TTag>) => void) => void,
52
57
  throw: (value?: unknown) => void,
53
58
  return: () => void,
54
59
  }
55
60
 
56
61
  type IntrinsicElements = {
57
- [TTag in Tag]: Partial<PropSetter<TTag> & SpecialProps<ElementType<TTag>>> & Props
62
+ [TTag in Tag]: Partial<PropSetter<TTag> & SpecialAttrs<ElementType<TTag>>> & Args
58
63
  }
59
64
 
60
65
  type ElementChildrenAttribute = { children: Children }
61
66
 
67
+ type WithChildren<T extends Args = {}> = T & Partial<ElementChildrenAttribute>
68
+
62
69
  function Fragment({ children }: ElementChildrenAttribute): typeof children
63
- function h(tag: Type, props?: Props | null, ...children: Children[]): VNode<Type, Props>
64
- function render(h: Children, el: Element, child?: Node, ref?: Node): void
70
+ function h(tag: Type, attrs?: Args | null, ...children: Children[]): VNode<Type, Args>
71
+ function render(h: Children, el: ParentNode, child?: ChildNode, ref?: ChildNode): void
65
72
  }
66
73
 
67
74
  declare module 'ajo/context' {
68
- function context<T>(fallback?: T): (value?: T) => T
75
+ function context<T>(fallback?: T): {
76
+ (): T
77
+ <V extends T>(value: V): V
78
+ }
79
+ function current(): import('ajo').StatefulElement<any, any> | null
69
80
  }
70
81
 
71
82
  declare module 'ajo/html' {