ajo 0.1.28 → 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"),j=Symbol.for("ajo.memo"),g=Symbol.for("ajo.ref"),p=Symbol.for("ajo.cache"),c=Symbol.for("ajo.generator"),a=Symbol.for("ajo.iterator"),b=Symbol.for("ajo.render"),f=Symbol.for("ajo.args"),N=t=>t.children,k=(t,e,...r)=>((e??={}).nodeName=t,!("children"in e)&&r.length&&(e.children=r.length==1?r[0]:r),e),l=(t,e,r=e.firstChild,i=null)=>{for(t of S(t)){const n=T(t,e,r);r==null?m(e,n,i):n==r?r=n.nextSibling:n==r.nextSibling?(m(e,r,i),r=n.nextSibling):m(e,n,r)}for(;r!=i;){const n=r.nextSibling;r.nodeType==1&&A(r),e.removeChild(r),r=n}},S=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*S(t);else"nodeName"in t?typeof t.nodeName=="function"?yield*E(t):yield t:yield String(t)},E=function*({nodeName:t,...e}){t.constructor.name=="GeneratorFunction"?yield O(t,e):yield*S(t(e))},O=function(t,e){const r={...t.attrs},i={...t.args};for(const n in e)n.startsWith("attr:")?r[n.slice(5)]=e[n]:n=="key"||n=="skip"||n=="memo"||n=="ref"||n.startsWith("set:")?r[n]=e[n]:i[n]=e[n];return{...r,nodeName:t.is??"div",[c]:t,[f]:i}},T=(t,e,r)=>typeof t=="string"?F(t,r):G(t,e,r),F=(t,e)=>{for(;e&&e.nodeType!=3;)e=e.nextSibling;return e?e.data!=t&&(e.data=t):e=document.createTextNode(t),e},G=({nodeName:t,children:e,key:r,skip:i,memo:n,ref:x,[c]:u,[f]:v,...w},C,s)=>{for(;s&&(s.localName!=t||s[y]!=null&&s[y]!=r||s[c]&&s[c]!=u);)s=s.nextSibling;return s??=document.createElementNS(w.xmlns??C.namespaceURI,t),r!=null&&(s[y]=r),(n==null||W(s[j],s[j]=n))&&(R(s[p]??B(s),s[p]=w,s),i||(u?I(u,v,s):l(e,s)),typeof x=="function"&&(s[g]=x)(s)),s},R=(t,e,r)=>{for(const i in{...t,...e})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]))},W=(t,e)=>Array.isArray(t)&&Array.isArray(e)?t.some((r,i)=>r!==e[i]):t!==e,B=t=>Array.from(t.attributes).reduce((e,r)=>(e[r.name]=r.value,e),{}),m=(t,e,r)=>{if(e.contains(document.activeElement)){const i=e.nextSibling;for(;r&&r!=e;){const n=r.nextSibling;t.insertBefore(r,i),r=n}}else t.insertBefore(e,r)},A=t=>{for(const e of t.children)A(e);typeof t.return=="function"&&t.return(),t[g]?.(null)},I=(t,e,r)=>{r[c]??=(M(r),t),Object.assign(r[f]??={},e),r[b]()},M=t=>{Object.assign(t,h),t[o.Context]=Object.create(o.current()?.[o.Context]??null)},h={[b](){const t=o.current();o.current(this);try{const{value:e,done:r}=(this[a]??=this[c].call(this,this[f])).next();l(e,this),this[g]?.(this),r&&this.return()}catch(e){this.throw(e)}finally{o.current(t)}},next(t){if(typeof t=="function")try{t.call(this,this[f])}catch(e){this.throw(e)}o.current()?.contains(this)||this[b]()},throw(t){for(let e=this;e;e=e.parentNode)if(typeof e[a]?.throw=="function")try{return l(e[a].throw(t).value,e)}catch(r){t=new Error(r instanceof Error?r.message:r,{cause:t})}throw t},return(){try{this[a]?.return()}catch(t){this.throw(t)}finally{this[a]=null}}};exports.Fragment=N;exports.h=k;exports.render=l;
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,86 +1,86 @@
1
- import { Context as j, current as f } from "./context.js";
2
- const u = Symbol.for("ajo.key"), p = Symbol.for("ajo.memo"), g = Symbol.for("ajo.ref"), A = Symbol.for("ajo.cache"), o = Symbol.for("ajo.generator"), a = Symbol.for("ajo.iterator"), m = Symbol.for("ajo.render"), c = Symbol.for("ajo.args"), U = (t) => t.children, h = (t, r, ...e) => ((r ??= {}).nodeName = t, !("children" in r) && e.length && (r.children = e.length == 1 ? e[0] : e), r), b = (t, r, e = r.firstChild, i = null) => {
3
- for (t of S(t)) {
4
- const n = G(t, r, e);
5
- e == null ? y(r, n, i) : n == e ? e = n.nextSibling : n == e.nextSibling ? (y(r, e, i), e = n.nextSibling) : y(r, n, e);
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);
6
6
  }
7
- for (; e != i; ) {
8
- const n = e.nextSibling;
9
- e.nodeType == 1 && v(e), r.removeChild(e), e = n;
7
+ for (; r != s; ) {
8
+ const n = r.nextSibling;
9
+ r.nodeType == 1 && N(r), t.removeChild(r), r = n;
10
10
  }
11
- }, S = function* (t) {
12
- if (t == null) return;
13
- const r = typeof t;
14
- if (r != "boolean")
15
- if (r == "string") yield t;
16
- else if (r == "number" || r == "bigint") yield String(t);
17
- else if (Symbol.iterator in t) for (t of t) yield* S(t);
18
- else "nodeName" in t ? typeof t.nodeName == "function" ? yield* C(t) : yield t : yield String(t);
19
- }, C = function* ({ nodeName: t, ...r }) {
20
- t.constructor.name == "GeneratorFunction" ? yield E(t, r) : yield* S(t(r));
21
- }, E = function(t, r) {
22
- const e = { ...t.attrs }, i = { ...t.args };
23
- for (const n in r)
24
- n.startsWith("attr:") ? e[n.slice(5)] = r[n] : n == "key" || n == "skip" || n == "memo" || n == "ref" || n.startsWith("set:") ? e[n] = r[n] : i[n] = r[n];
25
- return { ...e, nodeName: t.is ?? "div", [o]: t, [c]: i };
26
- }, G = (t, r, e) => typeof t == "string" ? O(t, e) : R(t, r, e), O = (t, r) => {
27
- for (; r && r.nodeType != 3; ) r = r.nextSibling;
28
- return r ? r.data != t && (r.data = t) : r = document.createTextNode(t), r;
29
- }, R = ({ nodeName: t, children: r, key: e, skip: i, memo: n, ref: x, [o]: l, [c]: N, ...w }, k, s) => {
30
- for (; s && (s.localName != t || s[u] != null && s[u] != e || s[o] && s[o] != l); ) s = s.nextSibling;
31
- return s ??= document.createElementNS(w.xmlns ?? k.namespaceURI, t), e != null && (s[u] = e), (n == null || W(s[p], s[p] = n)) && (T(s[A] ?? B(s), s[A] = w, s), i || (l ? F(l, N, s) : b(r, s)), typeof x == "function" && (s[g] = x)(s)), s;
32
- }, T = (t, r, e) => {
33
- for (const i in { ...t, ...r })
34
- t[i] !== r[i] && (i.startsWith("set:") ? e[i.slice(4)] = r[i] : r[i] == null || r[i] === !1 ? e.removeAttribute(i) : e.setAttribute(i, r[i] === !0 ? "" : r[i]));
35
- }, W = (t, r) => Array.isArray(t) && Array.isArray(r) ? t.some((e, i) => e !== r[i]) : t !== r, B = (t) => Array.from(t.attributes).reduce((r, e) => (r[e.name] = e.value, r), {}), y = (t, r, e) => {
36
- if (r.contains(document.activeElement)) {
37
- const i = r.nextSibling;
38
- for (; e && e != r; ) {
39
- const n = e.nextSibling;
40
- t.insertBefore(e, i), e = n;
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;
41
41
  }
42
- } else t.insertBefore(r, e);
43
- }, v = (t) => {
44
- for (const r of t.children) v(r);
45
- typeof t.return == "function" && t.return(), t[g]?.(null);
46
- }, F = (t, r, e) => {
47
- e[o] ??= (I(e), t), Object.assign(e[c] ??= {}, r), e[m]();
48
- }, I = (t) => {
49
- Object.assign(t, K), t[j] = Object.create(f()?.[j] ?? null);
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
50
  }, K = {
51
51
  [m]() {
52
- const t = f();
53
- f(this);
52
+ const e = c();
53
+ c(this);
54
54
  try {
55
- const { value: r, done: e } = (this[a] ??= this[o].call(this, this[c])).next();
56
- b(r, this), this[g]?.(this), e && this.return();
57
- } catch (r) {
58
- this.throw(r);
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);
59
59
  } finally {
60
- f(t);
60
+ c(e);
61
61
  }
62
62
  },
63
- next(t) {
64
- if (typeof t == "function") try {
65
- t.call(this, this[c]);
66
- } catch (r) {
67
- this.throw(r);
63
+ next(e) {
64
+ try {
65
+ e?.call(this, this[l]);
66
+ } catch (t) {
67
+ return this.throw(t);
68
68
  }
69
- f()?.contains(this) || this[m]();
69
+ c()?.contains(this) || this[m]();
70
70
  },
71
- throw(t) {
72
- for (let r = this; r; r = r.parentNode) if (typeof r[a]?.throw == "function") try {
73
- return b(r[a].throw(t).value, r);
74
- } catch (e) {
75
- t = new Error(e instanceof Error ? e.message : e, { cause: t });
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);
74
+ } catch (r) {
75
+ e = new Error(r?.message ?? r, { cause: e });
76
76
  }
77
- throw t;
77
+ throw e;
78
78
  },
79
79
  return() {
80
80
  try {
81
81
  this[a]?.return();
82
- } catch (t) {
83
- this.throw(t);
82
+ } catch (e) {
83
+ this.throw(e);
84
84
  } finally {
85
85
  this[a] = null;
86
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.1.28",
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.10.1",
37
- "happy-dom": "20.0.11",
38
- "vite": "7.2.4",
39
- "vite-tsconfig-paths": "5.1.4",
40
- "vitest": "4.0.14"
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,324 +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
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, child?: ChildNode, ref?: ChildNode): void`
187
- Renders JSX into a DOM container element. When `child` and `ref` are provided, only nodes between them (inclusive of `child`, exclusive of `ref`) are reconciled, leaving the rest of the container untouched.
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'))
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)
193
214
 
194
- // Update only the <main> region without touching header/footer
195
- const container = document.body
196
- render(<main>Updated</main>, container, container.querySelector('main'), container.querySelector('footer'))
197
- ```
215
+ <input type="checkbox" set:checked={bool} />
216
+ <video set:currentTime={0} set:muted />
198
217
 
199
- #### `h(type: Type, props?: Props, ...children: Children[]): VNode`
200
- JSX factory function (rarely used directly).
218
+ // innerHTML requires skip
219
+ <div set:innerHTML={html} skip />
220
+ ```
201
221
 
202
- #### `Fragment({ children }: { children: Children }): Children`
203
- JSX fragment component for rendering multiple elements without a wrapper.
222
+ ### `ref` - DOM Access
204
223
 
205
224
  ```javascript
206
- const List = () => (
207
- <>
208
- <li>Item 1</li>
209
- <li>Item 2</li>
210
- </>
211
- )
212
- ```
225
+ function* AutoFocus() {
226
+ let input = null
213
227
 
214
- ### 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
+ }
215
235
 
216
- 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
+ ```
217
241
 
218
- #### `this.next(callback?: (args: ComponentArgs) => void): void`
219
- 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
220
243
 
221
244
  ```javascript
222
- function* Counter(args) {
223
-
224
- 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
+ ```
225
249
 
226
- const increment = () => {
227
- // Simple re-render
228
- this.next(() => count++)
229
- }
250
+ ### `skip` - Third-Party DOM
230
251
 
231
- const incrementByStep = () => {
232
- // Access current props in callback
233
- this.next(({ step }) => count += step)
234
- }
252
+ ```javascript
253
+ function* Chart(args) {
254
+ let chart = null
235
255
 
236
- // ... rest of component
256
+ while (true) yield (
257
+ <div skip ref={el => el && (chart ??= new ChartLib(el, args.data))} />
258
+ )
237
259
  }
238
260
  ```
239
261
 
240
- #### `this.throw(error: unknown): void`
241
- Throws an error that can be caught by parent error boundaries.
262
+ ### `attr:` - Wrapper Attributes
242
263
 
243
- #### `this.return(): void`
244
- 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
+ ```
245
272
 
246
- ### Context Module (`ajo/context`)
273
+ ## Context API
247
274
 
248
- #### `context<T>(fallback?: T): ContextFunction<T>`
249
- Creates a context for sharing data across component trees.
275
+ Share data across component trees without prop drilling:
250
276
 
251
277
  ```javascript
252
278
  import { context } from 'ajo/context'
253
279
 
254
280
  const ThemeContext = context('light')
255
-
256
- // Set value
257
- ThemeContext('dark')
258
-
259
- // Get value
260
- const theme = ThemeContext() // 'dark'
261
281
  ```
262
282
 
263
- ### HTML Module (`ajo/html`)
264
-
265
- #### `render(children: Children): string`
266
- 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.
267
285
 
268
286
  ```javascript
269
- 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'
270
296
 
271
- 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
+ }
272
315
  ```
273
316
 
274
- #### `html(children: Children, hooks?: Hooks): IterableIterator<string>`
275
- 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
276
322
 
277
- ### 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 }))
278
327
 
279
- #### `stream(children: Children): AsyncIterableIterator<string>`
280
- 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
281
337
 
282
338
  ```javascript
339
+ // Static
340
+ import { render } from 'ajo/html'
341
+ const html = render(<App />)
342
+
343
+ // Streaming
283
344
  import { stream } from 'ajo/stream'
345
+ for await (const chunk of stream(<App />)) res.write(chunk)
284
346
 
285
- 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 }
286
350
  ```
287
351
 
288
- #### `hydrate(patch: Patch): Promise<void>`
289
- Client-side function for applying streamed patches during hydration.
352
+ ### Islands Architecture
290
353
 
291
354
  ```javascript
292
- 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
+ }
293
363
 
294
- 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
+ )
295
374
  ```
296
375
 
297
- ### TypeScript Support
376
+ ## TypeScript
298
377
 
299
378
  ```typescript
300
- // Component types
301
- type Stateless<Props = {}> = (props: Props) => Children
302
- type Stateful<Props = {}, Tag = 'div'> = {
303
- (this: StatefulElement<Tag>, props: Props): Iterator<Children>
304
- is?: Tag
305
- attrs?: Record<string, unknown>
306
- 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
+ }
307
397
  }
308
398
 
309
- // Stateful component instance
310
- type StatefulElement<Tag> = HTMLElement & {
311
- next: (callback?: (args: ComponentArgs) => void) => void
312
- throw: (error: unknown) => void
313
- return: () => void
314
- };
315
-
316
- // Element types
317
- type Children = unknown
318
- type VNode<Type, Props> = Props & {
319
- nodeName: Type
320
- children?: Children
321
- };
322
-
323
- // Special attributes
324
- type SpecialAttributes = {
325
- key?: unknown
326
- ref?: (element: Element | null) => void
327
- memo?: unknown[]
328
- skip?: boolean
329
- };
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} />
330
406
  ```
331
407
 
332
- ## 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
333
442
 
334
- For comprehensive guides, advanced patterns, and detailed examples, see [documentation.md](./documentation.md).
443
+ See [LLMs.md](./LLMs.md) for a condensed reference.
335
444
 
336
445
  ## License
337
446
 
338
- 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<TArguments extends Props = {}, TTag extends Tag = 'div'> = Tag | Stateless<TArguments> | Stateful<TArguments, TTag>
5
+ type Type<TArguments extends Args = {}, TTag extends Tag = 'div'> = Tag | Stateless<TArguments> | Stateful<TArguments, TTag>
6
6
 
7
- type Component<TArguments extends Props = {}> = Stateless<TArguments> | Stateful<TArguments>
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,37 +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>
70
+ function h(tag: Type, attrs?: Args | null, ...children: Children[]): VNode<Type, Args>
64
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
69
- function current(): import('ajo').Stateful | null
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
70
80
  }
71
81
 
72
82
  declare module 'ajo/html' {