tina4js 1.0.4 → 1.0.6

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/core.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./signal.cjs.js"),_=require("./component.cjs.js"),h=new WeakMap,d="t4:";function A(t,...e){let n=h.get(t);if(!n){n=document.createElement("template");let c="";for(let s=0;s<t.length;s++)c+=t[s],s<e.length&&(T(c)?c+=`__t4_${s}__`:c+=`<!--${d}${s}-->`);n.innerHTML=c,h.set(t,n)}const o=n.content.cloneNode(!0),i=N(o);for(const{marker:c,index:s}of i)y(c,e[s]);const f=b(o);for(const c of f)S(c,e);return o}function N(t){const e=[];return u(t,n=>{if(n.nodeType===8){const o=n.data;if(o&&o.startsWith(d)){const i=parseInt(o.slice(d.length),10);e.push({marker:n,index:i})}}}),e}function b(t){const e=[];return u(t,n=>{n.nodeType===1&&e.push(n)}),e}function u(t,e){const n=t.childNodes;for(let o=0;o<n.length;o++){const i=n[o];e(i),u(i,e)}}function y(t,e){const n=t.parentNode;if(n)if(a.isSignal(e)){const o=document.createTextNode("");n.replaceChild(o,t),a.effect(()=>{o.data=String(e.value??"")})}else if(typeof e=="function"){const o=document.createComment("");n.replaceChild(o,t);let i=[];a.effect(()=>{var l;const f=e();for(const r of i)(l=r.parentNode)==null||l.removeChild(r);i=[];const c=m(f),s=o.parentNode;for(const r of c)s.insertBefore(r,o),i.push(r)})}else if(p(e))n.replaceChild(e,t);else if(e instanceof Node)n.replaceChild(e,t);else if(Array.isArray(e)){const o=document.createDocumentFragment();for(const i of e){const f=m(i);for(const c of f)o.appendChild(c)}n.replaceChild(o,t)}else{const o=document.createTextNode(String(e??""));n.replaceChild(o,t)}}function S(t,e){const n=[];for(const o of Array.from(t.attributes)){const i=o.name,f=o.value;if(i.startsWith("@")){const s=i.slice(1),l=f.match(/__t4_(\d+)__/);if(l){const r=e[parseInt(l[1],10)];typeof r=="function"&&t.addEventListener(s,r)}n.push(i);continue}if(i.startsWith("?")){const s=i.slice(1),l=f.match(/__t4_(\d+)__/);if(l){const r=e[parseInt(l[1],10)];if(a.isSignal(r)){const g=r;a.effect(()=>{g.value?t.setAttribute(s,""):t.removeAttribute(s)})}else r&&t.setAttribute(s,"")}n.push(i);continue}if(i.startsWith(".")){const s=i.slice(1),l=f.match(/__t4_(\d+)__/);if(l){const r=e[parseInt(l[1],10)];a.isSignal(r)?a.effect(()=>{t[s]=r.value}):t[s]=r}n.push(i);continue}const c=f.match(/__t4_(\d+)__/);if(c){const s=e[parseInt(c[1],10)];if(a.isSignal(s)){const l=s;a.effect(()=>{t.setAttribute(i,String(l.value??""))})}else typeof s=="function"?a.effect(()=>{t.setAttribute(i,String(s()??""))}):t.setAttribute(i,String(s??""))}}for(const o of n)t.removeAttribute(o)}function m(t){if(t==null||t===!1)return[];if(p(t))return Array.from(t.childNodes);if(t instanceof Node)return[t];if(Array.isArray(t)){const e=[];for(const n of t)e.push(...m(n));return e}return[document.createTextNode(String(t))]}function p(t){return t!=null&&typeof t=="object"&&t.nodeType===11}function T(t){let e=!1,n=!1,o=!1;for(let i=0;i<t.length;i++){const f=t[i];f==="<"&&!e&&!n&&(o=!0),f===">"&&!e&&!n&&(o=!1),o&&(f==='"'&&!e&&(n=!n),f==="'"&&!n&&(e=!e))}return o}exports.batch=a.batch;exports.computed=a.computed;exports.effect=a.effect;exports.isSignal=a.isSignal;exports.signal=a.signal;exports.Tina4Element=_.Tina4Element;exports.html=A;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./signal.cjs.js"),N=require("./component.cjs.js"),_=new WeakMap,m="t4:";function b(t,...e){let n=_.get(t);if(!n){n=document.createElement("template");let f="";for(let i=0;i<t.length;i++)f+=t[i],i<e.length&&(E(f)?f+=`__t4_${i}__`:f+=`<!--${m}${i}-->`);n.innerHTML=f,_.set(t,n)}const o=n.content.cloneNode(!0),c=C(o);for(const{marker:f,index:i}of c)S(f,e[i]);const r=y(o);for(const f of r)T(f,e);return o}function C(t){const e=[];return p(t,n=>{if(n.nodeType===8){const o=n.data;if(o&&o.startsWith(m)){const c=parseInt(o.slice(m.length),10);e.push({marker:n,index:c})}}}),e}function y(t){const e=[];return p(t,n=>{n.nodeType===1&&e.push(n)}),e}function p(t,e){const n=t.childNodes;for(let o=0;o<n.length;o++){const c=n[o];e(c),p(c,e)}}function S(t,e){const n=t.parentNode;if(n)if(s.isSignal(e)){const o=document.createTextNode("");n.replaceChild(o,t),s.effect(()=>{o.data=String(e.value??"")})}else if(typeof e=="function"){const o=document.createComment("");n.replaceChild(o,t);let c=[],r=[];s.effect(()=>{var g;for(const d of r)d();r=[];const f=[],i=s._getEffectCollector();s._setEffectCollector(f);const l=e();s._setEffectCollector(i),r=f;for(const d of c)(g=d.parentNode)==null||g.removeChild(d);c=[];const a=h(l),u=o.parentNode;if(u)for(const d of a)u.insertBefore(d,o),c.push(d)})}else if(A(e))n.replaceChild(e,t);else if(e instanceof Node)n.replaceChild(e,t);else if(Array.isArray(e)){const o=document.createDocumentFragment();for(const c of e){const r=h(c);for(const f of r)o.appendChild(f)}n.replaceChild(o,t)}else{const o=document.createTextNode(String(e??""));n.replaceChild(o,t)}}function T(t,e){const n=[];for(const o of Array.from(t.attributes)){const c=o.name,r=o.value;if(c.startsWith("@")){const i=c.slice(1),l=r.match(/__t4_(\d+)__/);if(l){const a=e[parseInt(l[1],10)];typeof a=="function"&&t.addEventListener(i,a)}n.push(c);continue}if(c.startsWith("?")){const i=c.slice(1),l=r.match(/__t4_(\d+)__/);if(l){const a=e[parseInt(l[1],10)];if(s.isSignal(a)){const u=a;s.effect(()=>{u.value?t.setAttribute(i,""):t.removeAttribute(i)})}else a&&t.setAttribute(i,"")}n.push(c);continue}if(c.startsWith(".")){const i=c.slice(1),l=r.match(/__t4_(\d+)__/);if(l){const a=e[parseInt(l[1],10)];s.isSignal(a)?s.effect(()=>{t[i]=a.value}):t[i]=a}n.push(c);continue}const f=r.match(/__t4_(\d+)__/);if(f){const i=e[parseInt(f[1],10)];if(s.isSignal(i)){const l=i;s.effect(()=>{t.setAttribute(c,String(l.value??""))})}else typeof i=="function"?s.effect(()=>{t.setAttribute(c,String(i()??""))}):t.setAttribute(c,String(i??""))}}for(const o of n)t.removeAttribute(o)}function h(t){if(t==null||t===!1)return[];if(A(t))return Array.from(t.childNodes);if(t instanceof Node)return[t];if(Array.isArray(t)){const e=[];for(const n of t)e.push(...h(n));return e}return[document.createTextNode(String(t))]}function A(t){return t!=null&&typeof t=="object"&&t.nodeType===11}function E(t){let e=!1,n=!1,o=!1;for(let c=0;c<t.length;c++){const r=t[c];r==="<"&&!e&&!n&&(o=!0),r===">"&&!e&&!n&&(o=!1),o&&(r==='"'&&!e&&(n=!n),r==="'"&&!n&&(e=!e))}return o}exports.batch=s.batch;exports.computed=s.computed;exports.effect=s.effect;exports.isSignal=s.isSignal;exports.signal=s.signal;exports.Tina4Element=N.Tina4Element;exports.html=b;
package/dist/core.es.js CHANGED
@@ -1,53 +1,53 @@
1
- import { i as l, e as d } from "./signal.es.js";
2
- import { b as I, c as W, s as V } from "./signal.es.js";
3
- import { T as D } from "./component.es.js";
4
- const u = /* @__PURE__ */ new WeakMap(), m = "t4:";
5
- function x(t, ...e) {
6
- let n = u.get(t);
1
+ import { i as p, e as d, a as b, d as A } from "./signal.es.js";
2
+ import { b as w, c as M, s as R } from "./signal.es.js";
3
+ import { T as F } from "./component.es.js";
4
+ const N = /* @__PURE__ */ new WeakMap(), u = "t4:";
5
+ function W(t, ...e) {
6
+ let n = N.get(t);
7
7
  if (!n) {
8
8
  n = document.createElement("template");
9
- let r = "";
10
- for (let i = 0; i < t.length; i++)
11
- r += t[i], i < e.length && (T(r) ? r += `__t4_${i}__` : r += `<!--${m}${i}-->`);
12
- n.innerHTML = r, u.set(t, n);
9
+ let i = "";
10
+ for (let r = 0; r < t.length; r++)
11
+ i += t[r], r < e.length && (S(i) ? i += `__t4_${r}__` : i += `<!--${u}${r}-->`);
12
+ n.innerHTML = i, N.set(t, n);
13
13
  }
14
- const o = n.content.cloneNode(!0), s = A(o);
15
- for (const { marker: r, index: i } of s)
16
- b(r, e[i]);
17
- const f = N(o);
18
- for (const r of f)
19
- y(r, e);
14
+ const o = n.content.cloneNode(!0), s = y(o);
15
+ for (const { marker: i, index: r } of s)
16
+ x(i, e[r]);
17
+ const c = T(o);
18
+ for (const i of c)
19
+ E(i, e);
20
20
  return o;
21
21
  }
22
- function A(t) {
22
+ function y(t) {
23
23
  const e = [];
24
- return h(t, (n) => {
24
+ return _(t, (n) => {
25
25
  if (n.nodeType === 8) {
26
26
  const o = n.data;
27
- if (o && o.startsWith(m)) {
28
- const s = parseInt(o.slice(m.length), 10);
27
+ if (o && o.startsWith(u)) {
28
+ const s = parseInt(o.slice(u.length), 10);
29
29
  e.push({ marker: n, index: s });
30
30
  }
31
31
  }
32
32
  }), e;
33
33
  }
34
- function N(t) {
34
+ function T(t) {
35
35
  const e = [];
36
- return h(t, (n) => {
36
+ return _(t, (n) => {
37
37
  n.nodeType === 1 && e.push(n);
38
38
  }), e;
39
39
  }
40
- function h(t, e) {
40
+ function _(t, e) {
41
41
  const n = t.childNodes;
42
42
  for (let o = 0; o < n.length; o++) {
43
43
  const s = n[o];
44
- e(s), h(s, e);
44
+ e(s), _(s, e);
45
45
  }
46
46
  }
47
- function b(t, e) {
47
+ function x(t, e) {
48
48
  const n = t.parentNode;
49
49
  if (n)
50
- if (l(e)) {
50
+ if (p(e)) {
51
51
  const o = document.createTextNode("");
52
52
  n.replaceChild(o, t), d(() => {
53
53
  o.data = String(e.value ?? "");
@@ -55,25 +55,31 @@ function b(t, e) {
55
55
  } else if (typeof e == "function") {
56
56
  const o = document.createComment("");
57
57
  n.replaceChild(o, t);
58
- let s = [];
58
+ let s = [], c = [];
59
59
  d(() => {
60
- var a;
60
+ var g;
61
+ for (const l of c) l();
62
+ c = [];
63
+ const i = [], r = b();
64
+ A(i);
61
65
  const f = e();
62
- for (const c of s) (a = c.parentNode) == null || a.removeChild(c);
66
+ A(r), c = i;
67
+ for (const l of s) (g = l.parentNode) == null || g.removeChild(l);
63
68
  s = [];
64
- const r = p(f), i = o.parentNode;
65
- for (const c of r)
66
- i.insertBefore(c, o), s.push(c);
69
+ const a = h(f), m = o.parentNode;
70
+ if (m)
71
+ for (const l of a)
72
+ m.insertBefore(l, o), s.push(l);
67
73
  });
68
- } else if (_(e))
74
+ } else if (C(e))
69
75
  n.replaceChild(e, t);
70
76
  else if (e instanceof Node)
71
77
  n.replaceChild(e, t);
72
78
  else if (Array.isArray(e)) {
73
79
  const o = document.createDocumentFragment();
74
80
  for (const s of e) {
75
- const f = p(s);
76
- for (const r of f) o.appendChild(r);
81
+ const c = h(s);
82
+ for (const i of c) o.appendChild(i);
77
83
  }
78
84
  n.replaceChild(o, t);
79
85
  } else {
@@ -81,88 +87,88 @@ function b(t, e) {
81
87
  n.replaceChild(o, t);
82
88
  }
83
89
  }
84
- function y(t, e) {
90
+ function E(t, e) {
85
91
  const n = [];
86
92
  for (const o of Array.from(t.attributes)) {
87
- const s = o.name, f = o.value;
93
+ const s = o.name, c = o.value;
88
94
  if (s.startsWith("@")) {
89
- const i = s.slice(1), a = f.match(/__t4_(\d+)__/);
90
- if (a) {
91
- const c = e[parseInt(a[1], 10)];
92
- typeof c == "function" && t.addEventListener(i, c);
95
+ const r = s.slice(1), f = c.match(/__t4_(\d+)__/);
96
+ if (f) {
97
+ const a = e[parseInt(f[1], 10)];
98
+ typeof a == "function" && t.addEventListener(r, a);
93
99
  }
94
100
  n.push(s);
95
101
  continue;
96
102
  }
97
103
  if (s.startsWith("?")) {
98
- const i = s.slice(1), a = f.match(/__t4_(\d+)__/);
99
- if (a) {
100
- const c = e[parseInt(a[1], 10)];
101
- if (l(c)) {
102
- const g = c;
104
+ const r = s.slice(1), f = c.match(/__t4_(\d+)__/);
105
+ if (f) {
106
+ const a = e[parseInt(f[1], 10)];
107
+ if (p(a)) {
108
+ const m = a;
103
109
  d(() => {
104
- g.value ? t.setAttribute(i, "") : t.removeAttribute(i);
110
+ m.value ? t.setAttribute(r, "") : t.removeAttribute(r);
105
111
  });
106
112
  } else
107
- c && t.setAttribute(i, "");
113
+ a && t.setAttribute(r, "");
108
114
  }
109
115
  n.push(s);
110
116
  continue;
111
117
  }
112
118
  if (s.startsWith(".")) {
113
- const i = s.slice(1), a = f.match(/__t4_(\d+)__/);
114
- if (a) {
115
- const c = e[parseInt(a[1], 10)];
116
- l(c) ? d(() => {
117
- t[i] = c.value;
118
- }) : t[i] = c;
119
+ const r = s.slice(1), f = c.match(/__t4_(\d+)__/);
120
+ if (f) {
121
+ const a = e[parseInt(f[1], 10)];
122
+ p(a) ? d(() => {
123
+ t[r] = a.value;
124
+ }) : t[r] = a;
119
125
  }
120
126
  n.push(s);
121
127
  continue;
122
128
  }
123
- const r = f.match(/__t4_(\d+)__/);
124
- if (r) {
125
- const i = e[parseInt(r[1], 10)];
126
- if (l(i)) {
127
- const a = i;
129
+ const i = c.match(/__t4_(\d+)__/);
130
+ if (i) {
131
+ const r = e[parseInt(i[1], 10)];
132
+ if (p(r)) {
133
+ const f = r;
128
134
  d(() => {
129
- t.setAttribute(s, String(a.value ?? ""));
135
+ t.setAttribute(s, String(f.value ?? ""));
130
136
  });
131
- } else typeof i == "function" ? d(() => {
132
- t.setAttribute(s, String(i() ?? ""));
133
- }) : t.setAttribute(s, String(i ?? ""));
137
+ } else typeof r == "function" ? d(() => {
138
+ t.setAttribute(s, String(r() ?? ""));
139
+ }) : t.setAttribute(s, String(r ?? ""));
134
140
  }
135
141
  }
136
142
  for (const o of n) t.removeAttribute(o);
137
143
  }
138
- function p(t) {
144
+ function h(t) {
139
145
  if (t == null || t === !1) return [];
140
- if (_(t)) return Array.from(t.childNodes);
146
+ if (C(t)) return Array.from(t.childNodes);
141
147
  if (t instanceof Node) return [t];
142
148
  if (Array.isArray(t)) {
143
149
  const e = [];
144
- for (const n of t) e.push(...p(n));
150
+ for (const n of t) e.push(...h(n));
145
151
  return e;
146
152
  }
147
153
  return [document.createTextNode(String(t))];
148
154
  }
149
- function _(t) {
155
+ function C(t) {
150
156
  return t != null && typeof t == "object" && t.nodeType === 11;
151
157
  }
152
- function T(t) {
158
+ function S(t) {
153
159
  let e = !1, n = !1, o = !1;
154
160
  for (let s = 0; s < t.length; s++) {
155
- const f = t[s];
156
- f === "<" && !e && !n && (o = !0), f === ">" && !e && !n && (o = !1), o && (f === '"' && !e && (n = !n), f === "'" && !n && (e = !e));
161
+ const c = t[s];
162
+ c === "<" && !e && !n && (o = !0), c === ">" && !e && !n && (o = !1), o && (c === '"' && !e && (n = !n), c === "'" && !n && (e = !e));
157
163
  }
158
164
  return o;
159
165
  }
160
166
  export {
161
- D as Tina4Element,
162
- I as batch,
163
- W as computed,
167
+ F as Tina4Element,
168
+ w as batch,
169
+ M as computed,
164
170
  d as effect,
165
- x as html,
166
- l as isSignal,
167
- V as signal
171
+ W as html,
172
+ p as isSignal,
173
+ R as signal
168
174
  };
package/dist/debug.cjs.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const C=require("./signal.cjs.js"),A=require("./component.cjs.js"),_=require("./index.cjs.js"),m=require("./api.cjs.js"),d=[],p={add(t,o){const e=t._debugInfo;d.push({ref:new WeakRef(t),label:o,createdAt:(e==null?void 0:e.createdAt)??Date.now(),updateCount:0,subs:new WeakRef((e==null?void 0:e.subs)??new Set)})},onUpdate(t){for(const o of d)if(o.ref.deref()===t){o.updateCount++;break}},getAll(){var o;const t=[];for(let e=d.length-1;e>=0;e--){const n=d[e],r=n.ref.deref();if(!r){d.splice(e,1);continue}const s=n.subs.deref();t.push({label:n.label,value:r.peek(),subscriberCount:s?s.size:0,updateCount:((o=r._debugInfo)==null?void 0:o.updateCount)??n.updateCount,alive:!0})}return t},get count(){return d.length}},a=[],b={onMount(t){a.push({ref:new WeakRef(t),tagName:t.tagName.toLowerCase(),mountedAt:Date.now()})},onUnmount(t){const o=a.findIndex(e=>e.ref.deref()===t);o>=0&&a.splice(o,1)},getAll(){const t=[];for(let o=a.length-1;o>=0;o--){const e=a[o],n=e.ref.deref();if(!n||!n.isConnected){a.splice(o,1);continue}const r={},s=n.constructor;if(s.props)for(const i of Object.keys(s.props))try{r[i]=n.prop(i).peek()}catch{}t.push({tagName:e.tagName,props:r,alive:!0})}return t},get count(){return a.length}},l=[],D=50,f={onNavigate(t){l.unshift({path:t.path,pattern:t.pattern,params:t.params,durationMs:t.durationMs,timestamp:Date.now()}),l.length>D&&l.pop()},getHistory(){return l},get count(){return l.length}};let R=0;const c=[],g=new Map,M=100,h={onRequest(t){var r;const o=++R,e={id:o,method:t.method??"GET",url:"",hasAuth:!!((r=t.headers)!=null&&r.Authorization),timestamp:Date.now(),pending:!0},n=String(o);g.set(n,e),c.unshift(e),c.length>M&&c.pop()},onResponse(t){for(const[o,e]of g)if(e.pending){e.status=t.status,e.durationMs=Date.now()-e.timestamp,e.pending=!1,t.ok||(e.error=`HTTP ${t.status}`),g.delete(o);break}},getLog(){return c},get count(){return c.length}},x=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("./signal.cjs.js"),A=require("./component.cjs.js"),x=require("./index.cjs.js"),y=require("./api.cjs.js"),l=[],b={add(t,o){const e=t._debugInfo;l.push({ref:new WeakRef(t),label:o,createdAt:(e==null?void 0:e.createdAt)??Date.now(),updateCount:0,subs:new WeakRef((e==null?void 0:e.subs)??new Set)})},onUpdate(t){for(const o of l)if(o.ref.deref()===t){o.updateCount++;break}},getAll(){var o;const t=[];for(let e=l.length-1;e>=0;e--){const n=l[e],r=n.ref.deref();if(!r){l.splice(e,1);continue}const s=n.subs.deref();t.push({label:n.label,value:r.peek(),subscriberCount:s?s.size:0,updateCount:((o=r._debugInfo)==null?void 0:o.updateCount)??n.updateCount,alive:!0})}return t},get count(){return l.length}},a=[],h={onMount(t){a.push({ref:new WeakRef(t),tagName:t.tagName.toLowerCase(),mountedAt:Date.now()})},onUnmount(t){const o=a.findIndex(e=>e.ref.deref()===t);o>=0&&a.splice(o,1)},getAll(){const t=[];for(let o=a.length-1;o>=0;o--){const e=a[o],n=e.ref.deref();if(!n||!n.isConnected){a.splice(o,1);continue}const r={},s=n.constructor;if(s.props)for(const i of Object.keys(s.props))try{r[i]=n.prop(i).peek()}catch{}t.push({tagName:e.tagName,props:r,alive:!0})}return t},get count(){return a.length}},d=[],D=50;let f=null;const p={setGetRoutes(t){f=t},getRegisteredRoutes(){return f?f():[]},onNavigate(t){d.unshift({path:t.path,pattern:t.pattern,params:t.params,durationMs:t.durationMs,timestamp:Date.now()}),d.length>D&&d.pop()},getHistory(){return d},get count(){return d.length}};let M=0;const c=[],m=new Map,E=100,g={onRequest(t){var r;const o=++M,e={id:o,method:t.method??"GET",url:"",hasAuth:!!((r=t.headers)!=null&&r.Authorization),timestamp:Date.now(),pending:!0},n=String(o);m.set(n,e),c.unshift(e),c.length>E&&c.pop()},onResponse(t){for(const[o,e]of m)if(e.pending){e.status=t.status,e.durationMs=Date.now()-e.timestamp,e.pending=!1,t.ok||(e.error=`HTTP ${t.status}`),m.delete(o);break}},getLog(){return c},get count(){return c.length}},w=`
2
2
  :host {
3
3
  all: initial;
4
4
  position: fixed;
@@ -204,36 +204,36 @@ tr:hover td { background: rgba(255,255,255,0.02); }
204
204
  border-radius: 50%;
205
205
  background: #66bb6a;
206
206
  }
207
- `;function E(t){if(t==null)return{text:String(t),cls:"val-null"};if(typeof t=="string")return{text:`"${t.length>30?t.slice(0,30)+"...":t}"`,cls:"val-string"};if(typeof t=="number")return{text:String(t),cls:"val-number"};if(typeof t=="boolean")return{text:String(t),cls:"val-boolean"};if(Array.isArray(t))return{text:`Array(${t.length})`,cls:"val-object"};if(typeof t=="object")try{return{text:JSON.stringify(t).slice(0,40),cls:"val-object"}}catch{}return{text:String(t),cls:"val-object"}}function L(){const t=p.getAll();if(t.length===0)return'<div class="t4-empty">No signals tracked yet.<br>Signals created after debug is enabled will appear here.</div>';let o="";for(let e=0;e<t.length;e++){const n=t[e],{text:r,cls:s}=E(n.value);o+=`<tr>
207
+ `;function L(t){if(t==null)return{text:String(t),cls:"val-null"};if(typeof t=="string")return{text:`"${t.length>30?t.slice(0,30)+"...":t}"`,cls:"val-string"};if(typeof t=="number")return{text:String(t),cls:"val-number"};if(typeof t=="boolean")return{text:String(t),cls:"val-boolean"};if(Array.isArray(t))return{text:`Array(${t.length})`,cls:"val-object"};if(typeof t=="object")try{return{text:JSON.stringify(t).slice(0,40),cls:"val-object"}}catch{}return{text:String(t),cls:"val-object"}}function j(){const t=b.getAll();if(t.length===0)return'<div class="t4-empty">No signals tracked yet.<br>Signals created after debug is enabled will appear here.</div>';let o="";for(let e=0;e<t.length;e++){const n=t[e],{text:r,cls:s}=L(n.value);o+=`<tr>
208
208
  <td>${n.label||`signal_${e}`}</td>
209
- <td><span class="${s}">${j(r)}</span></td>
209
+ <td><span class="${s}">${H(r)}</span></td>
210
210
  <td>${n.subscriberCount}</td>
211
211
  <td>${n.updateCount}</td>
212
212
  </tr>`}return`<table>
213
213
  <thead><tr><th>Label</th><th>Value</th><th>Subs</th><th>Updates</th></tr></thead>
214
214
  <tbody>${o}</tbody>
215
- </table>`}function j(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function H(){const t=b.getAll();if(t.length===0)return'<div class="t4-empty">No Tina4Elements mounted.<br>Custom elements extending Tina4Element will appear here.</div>';let o="";for(const e of t){const n=Object.keys(e.props).length>0?Object.entries(e.props).map(([r,s])=>`${r}=${JSON.stringify(s)??"null"}`).join(", "):"—";o+=`<tr>
216
- <td>&lt;${y(e.tagName)}&gt;</td>
217
- <td>${y(n.length>60?n.slice(0,60)+"...":n)}</td>
215
+ </table>`}function H(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function z(){const t=h.getAll();if(t.length===0)return'<div class="t4-empty">No Tina4Elements mounted.<br>Custom elements extending Tina4Element will appear here.</div>';let o="";for(const e of t){const n=Object.keys(e.props).length>0?Object.entries(e.props).map(([r,s])=>`${r}=${JSON.stringify(s)??"null"}`).join(", "):"—";o+=`<tr>
216
+ <td>&lt;${v(e.tagName)}&gt;</td>
217
+ <td>${v(n.length>60?n.slice(0,60)+"...":n)}</td>
218
218
  </tr>`}return`<table>
219
219
  <thead><tr><th>Element</th><th>Props</th></tr></thead>
220
220
  <tbody>${o}</tbody>
221
- </table>`}function y(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function z(t){const o=t<1?"<1ms":`${Math.round(t)}ms`,e=t<5?"duration fast":t<50?"duration":t<200?"duration slow":"duration very-slow";return{text:o,cls:e}}function N(t){return new Date(t).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function P(){const t=_._getRoutes(),o=f.getHistory();let e="";if(t.length>0){let n="";for(const r of t)n+=`<tr>
222
- <td><span class="route-pattern">${w(r.pattern)}</span></td>
221
+ </table>`}function v(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function N(t){const o=t<1?"<1ms":`${Math.round(t)}ms`,e=t<5?"duration fast":t<50?"duration":t<200?"duration slow":"duration very-slow";return{text:o,cls:e}}function P(t){return new Date(t).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function I(){const t=p.getRegisteredRoutes(),o=p.getHistory();let e="";if(t.length>0){let n="";for(const r of t)n+=`<tr>
222
+ <td><span class="route-pattern">${_(r.pattern)}</span></td>
223
223
  <td>${r.hasGuard?"Yes":"—"}</td>
224
224
  </tr>`;e+=`<table>
225
225
  <thead><tr><th>Pattern</th><th>Guard</th></tr></thead>
226
226
  <tbody>${n}</tbody>
227
- </table>`}if(o.length>0){e+='<div style="margin-top:8px;padding-top:8px;border-top:1px solid #333;">';let n="";for(const r of o){const{text:s,cls:i}=z(r.durationMs),k=Object.keys(r.params).length>0?Object.entries(r.params).map(([T,S])=>`<span class="route-param">${T}=${S}</span>`).join(" "):"";n+=`<tr>
228
- <td>${N(r.timestamp)}</td>
229
- <td>${w(r.path)}</td>
230
- <td>${k||"—"}</td>
227
+ </table>`}if(o.length>0){e+='<div style="margin-top:8px;padding-top:8px;border-top:1px solid #333;">';let n="";for(const r of o){const{text:s,cls:i}=N(r.durationMs),T=Object.keys(r.params).length>0?Object.entries(r.params).map(([S,C])=>`<span class="route-param">${S}=${C}</span>`).join(" "):"";n+=`<tr>
228
+ <td>${P(r.timestamp)}</td>
229
+ <td>${_(r.path)}</td>
230
+ <td>${T||"—"}</td>
231
231
  <td><span class="${i}">${s}</span></td>
232
232
  </tr>`}e+=`<table>
233
233
  <thead><tr><th>Time</th><th>Path</th><th>Params</th><th>Duration</th></tr></thead>
234
234
  <tbody>${n}</tbody>
235
- </table></div>`}else t.length===0&&(e='<div class="t4-empty">No routes registered yet.</div>');return e}function w(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function I(t){if(t===void 0)return{text:"...",cls:"status-pending"};const o=t<1?"<1ms":`${Math.round(t)}ms`,e=t<100?"duration fast":t<500?"duration":t<2e3?"duration slow":"duration very-slow";return{text:o,cls:e}}function q(t,o){return o?{text:"pending",cls:"status-pending"}:t?t>=200&&t<300?{text:String(t),cls:"status-ok"}:{text:String(t),cls:"status-err"}:{text:"—",cls:""}}function O(t){return new Date(t).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function U(){const t=h.getLog();if(t.length===0)return'<div class="t4-empty">No API calls yet.<br>Requests made via api.get/post/put/patch/delete will appear here.</div>';let o="";for(const e of t){const{text:n,cls:r}=q(e.status,e.pending),{text:s,cls:i}=I(e.durationMs);o+=`<tr>
236
- <td>${O(e.timestamp)}</td>
235
+ </table></div>`}else t.length===0&&(e='<div class="t4-empty">No routes registered yet.</div>');return e}function _(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function q(t){if(t===void 0)return{text:"...",cls:"status-pending"};const o=t<1?"<1ms":`${Math.round(t)}ms`,e=t<100?"duration fast":t<500?"duration":t<2e3?"duration slow":"duration very-slow";return{text:o,cls:e}}function O(t,o){return o?{text:"pending",cls:"status-pending"}:t?t>=200&&t<300?{text:String(t),cls:"status-ok"}:{text:String(t),cls:"status-err"}:{text:"—",cls:""}}function U(t){return new Date(t).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function G(){const t=g.getLog();if(t.length===0)return'<div class="t4-empty">No API calls yet.<br>Requests made via api.get/post/put/patch/delete will appear here.</div>';let o="";for(const e of t){const{text:n,cls:r}=O(e.status,e.pending),{text:s,cls:i}=q(e.durationMs);o+=`<tr>
236
+ <td>${U(e.timestamp)}</td>
237
237
  <td><strong>${e.method}</strong></td>
238
238
  <td>${B(e.url||"(url)")}</td>
239
239
  <td><span class="${r}">${n}</span></td>
@@ -242,8 +242,8 @@ tr:hover td { background: rgba(255,255,255,0.02); }
242
242
  </tr>`}return`<table>
243
243
  <thead><tr><th>Time</th><th>Method</th><th>URL</th><th>Status</th><th>Duration</th><th>Auth</th></tr></thead>
244
244
  <tbody>${o}</tbody>
245
- </table>`}function B(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}class G extends HTMLElement{constructor(){super(),this._visible=!0,this._activeTab="signals",this._refreshTimer=null,this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){this._render(),this._startAutoRefresh()}disconnectedCallback(){this._stopAutoRefresh()}toggle(){this._visible=!this._visible,this._render()}show(){this._visible=!0,this._render()}hide(){this._visible=!1,this._render()}_startAutoRefresh(){this._refreshTimer=window.setInterval(()=>{this._visible&&this._renderBody()},1e3)}_stopAutoRefresh(){this._refreshTimer!==null&&(clearInterval(this._refreshTimer),this._refreshTimer=null)}_switchTab(o){this._activeTab=o,this._render()}_getTabContent(){switch(this._activeTab){case"signals":return L();case"components":return H();case"routes":return P();case"api":return U()}}_renderBody(){const o=this._shadow.querySelector(".t4-body");o&&(o.innerHTML=this._getTabContent()),this._updateTabCounts()}_updateTabCounts(){const o={signals:p.count,components:b.count,routes:f.count,api:h.count};for(const[e,n]of Object.entries(o)){const r=this._shadow.querySelector(`[data-tab-count="${e}"]`);r&&(r.textContent=n>0?`(${n})`:"")}}_render(){var n,r;const o=[{id:"signals",label:"Signals"},{id:"components",label:"Components"},{id:"routes",label:"Routes"},{id:"api",label:"API"}];if(!this._visible){this._shadow.innerHTML=`
246
- <style>${x}</style>
245
+ </table>`}function B(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}class F extends HTMLElement{constructor(){super(),this._visible=!0,this._activeTab="signals",this._refreshTimer=null,this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){this._render(),this._startAutoRefresh()}disconnectedCallback(){this._stopAutoRefresh()}toggle(){this._visible=!this._visible,this._render()}show(){this._visible=!0,this._render()}hide(){this._visible=!1,this._render()}_startAutoRefresh(){this._refreshTimer=window.setInterval(()=>{this._visible&&this._renderBody()},1e3)}_stopAutoRefresh(){this._refreshTimer!==null&&(clearInterval(this._refreshTimer),this._refreshTimer=null)}_switchTab(o){this._activeTab=o,this._render()}_getTabContent(){switch(this._activeTab){case"signals":return j();case"components":return z();case"routes":return I();case"api":return G()}}_renderBody(){const o=this._shadow.querySelector(".t4-body");o&&(o.innerHTML=this._getTabContent()),this._updateTabCounts()}_updateTabCounts(){const o={signals:b.count,components:h.count,routes:p.count,api:g.count};for(const[e,n]of Object.entries(o)){const r=this._shadow.querySelector(`[data-tab-count="${e}"]`);r&&(r.textContent=n>0?`(${n})`:"")}}_render(){var n,r;const o=[{id:"signals",label:"Signals"},{id:"components",label:"Components"},{id:"routes",label:"Routes"},{id:"api",label:"API"}];if(!this._visible){this._shadow.innerHTML=`
246
+ <style>${w}</style>
247
247
  <div class="t4-mini" id="t4-mini">
248
248
  <span class="t4-mini-dot"></span>
249
249
  T4 Debug
@@ -251,7 +251,7 @@ tr:hover td { background: rgba(255,255,255,0.02); }
251
251
  `,(n=this._shadow.getElementById("t4-mini"))==null||n.addEventListener("click",()=>this.show());return}const e=o.map(s=>`<button class="t4-tab${this._activeTab===s.id?" active":""}" data-tab="${s.id}">
252
252
  ${s.label}<span class="t4-tab-count" data-tab-count="${s.id}"></span>
253
253
  </button>`).join("");this._shadow.innerHTML=`
254
- <style>${x}</style>
254
+ <style>${w}</style>
255
255
  <div class="t4-debug">
256
256
  <div class="t4-header">
257
257
  <div>
@@ -265,4 +265,4 @@ tr:hover td { background: rgba(255,255,255,0.02); }
265
265
  <div class="t4-tabs">${e}</div>
266
266
  <div class="t4-body">${this._getTabContent()}</div>
267
267
  </div>
268
- `,(r=this._shadow.getElementById("t4-close"))==null||r.addEventListener("click",()=>this.hide());for(const s of this._shadow.querySelectorAll(".t4-tab"))s.addEventListener("click",()=>{this._switchTab(s.dataset.tab)});this._updateTabCounts()}}function V(){typeof customElements<"u"&&!customElements.get("tina4-debug")&&customElements.define("tina4-debug",G)}let u=null,v=!1;function $(){v||(v=!0,C.__setDebugSignalHooks((t,o)=>p.add(t,o),t=>p.onUpdate(t)),A.__setDebugComponentHooks(t=>b.onMount(t),t=>b.onUnmount(t)),_.router.on("change",t=>{f.onNavigate(t)}),m.api.intercept("request",t=>(h.onRequest(t),t)),m.api.intercept("response",t=>(h.onResponse(t),t)),typeof document<"u"&&(V(),u=document.createElement("tina4-debug"),document.body.appendChild(u),document.addEventListener("keydown",t=>{t.ctrlKey&&t.shiftKey&&t.key==="D"&&(t.preventDefault(),u==null||u.toggle())})),console.log("%c[tina4] %cDebug overlay enabled %c(Ctrl+Shift+D to toggle)","color:#00d4ff;font-weight:bold","color:#e0e0e0","color:#888"))}$();exports.enableDebug=$;
268
+ `,(r=this._shadow.getElementById("t4-close"))==null||r.addEventListener("click",()=>this.hide());for(const s of this._shadow.querySelectorAll(".t4-tab"))s.addEventListener("click",()=>{this._switchTab(s.dataset.tab)});this._updateTabCounts()}}function V(){typeof customElements<"u"&&!customElements.get("tina4-debug")&&customElements.define("tina4-debug",F)}let u=null,$=!1;function k(){$||($=!0,R.__setDebugSignalHooks((t,o)=>b.add(t,o),t=>b.onUpdate(t)),A.__setDebugComponentHooks(t=>h.onMount(t),t=>h.onUnmount(t)),p.setGetRoutes(x._getRoutes),x.router.on("change",t=>{p.onNavigate(t)}),y.api.intercept("request",t=>(g.onRequest(t),t)),y.api.intercept("response",t=>(g.onResponse(t),t)),typeof document<"u"&&(V(),u=document.createElement("tina4-debug"),document.body.appendChild(u),document.addEventListener("keydown",t=>{t.ctrlKey&&t.shiftKey&&t.key==="D"&&(t.preventDefault(),u==null||u.toggle())})),console.log("%c[tina4] %cDebug overlay enabled %c(Ctrl+Shift+D to toggle)","color:#00d4ff;font-weight:bold","color:#e0e0e0","color:#888"))}k();exports.enableDebug=k;
package/dist/debug.es.js CHANGED
@@ -1,8 +1,8 @@
1
- import { _ as T } from "./signal.es.js";
2
- import { _ as C } from "./component.es.js";
3
- import { _ as S, a as A } from "./index.es.js";
4
- import { api as m } from "./api.es.js";
5
- const d = [], p = {
1
+ import { _ as C } from "./signal.es.js";
2
+ import { _ as S } from "./component.es.js";
3
+ import { _ as R, a as A } from "./index.es.js";
4
+ import { api as x } from "./api.es.js";
5
+ const d = [], b = {
6
6
  add(t, o) {
7
7
  const e = t._debugInfo;
8
8
  d.push({
@@ -43,7 +43,7 @@ const d = [], p = {
43
43
  get count() {
44
44
  return d.length;
45
45
  }
46
- }, a = [], b = {
46
+ }, a = [], h = {
47
47
  onMount(t) {
48
48
  a.push({
49
49
  ref: new WeakRef(t),
@@ -77,7 +77,16 @@ const d = [], p = {
77
77
  get count() {
78
78
  return a.length;
79
79
  }
80
- }, l = [], D = 50, f = {
80
+ }, l = [], D = 50;
81
+ let f = null;
82
+ const p = {
83
+ /** Set the function that reads registered routes (avoids direct import from router). */
84
+ setGetRoutes(t) {
85
+ f = t;
86
+ },
87
+ getRegisteredRoutes() {
88
+ return f ? f() : [];
89
+ },
81
90
  onNavigate(t) {
82
91
  l.unshift({
83
92
  path: t.path,
@@ -94,11 +103,11 @@ const d = [], p = {
94
103
  return l.length;
95
104
  }
96
105
  };
97
- let R = 0;
98
- const c = [], g = /* @__PURE__ */ new Map(), E = 100, h = {
106
+ let E = 0;
107
+ const c = [], m = /* @__PURE__ */ new Map(), M = 100, g = {
99
108
  onRequest(t) {
100
109
  var r;
101
- const o = ++R, e = {
110
+ const o = ++E, e = {
102
111
  id: o,
103
112
  method: t.method ?? "GET",
104
113
  url: "",
@@ -107,12 +116,12 @@ const c = [], g = /* @__PURE__ */ new Map(), E = 100, h = {
107
116
  timestamp: Date.now(),
108
117
  pending: !0
109
118
  }, n = String(o);
110
- g.set(n, e), c.unshift(e), c.length > E && c.pop();
119
+ m.set(n, e), c.unshift(e), c.length > M && c.pop();
111
120
  },
112
121
  onResponse(t) {
113
- for (const [o, e] of g)
122
+ for (const [o, e] of m)
114
123
  if (e.pending) {
115
- e.status = t.status, e.durationMs = Date.now() - e.timestamp, e.pending = !1, t.ok || (e.error = `HTTP ${t.status}`), g.delete(o);
124
+ e.status = t.status, e.durationMs = Date.now() - e.timestamp, e.pending = !1, t.ok || (e.error = `HTTP ${t.status}`), m.delete(o);
116
125
  break;
117
126
  }
118
127
  },
@@ -122,7 +131,7 @@ const c = [], g = /* @__PURE__ */ new Map(), E = 100, h = {
122
131
  get count() {
123
132
  return c.length;
124
133
  }
125
- }, x = (
134
+ }, y = (
126
135
  /* css */
127
136
  `
128
137
  :host {
@@ -332,7 +341,7 @@ tr:hover td { background: rgba(255,255,255,0.02); }
332
341
  }
333
342
  `
334
343
  );
335
- function M(t) {
344
+ function L(t) {
336
345
  if (t == null) return { text: String(t), cls: "val-null" };
337
346
  if (typeof t == "string") return { text: `"${t.length > 30 ? t.slice(0, 30) + "..." : t}"`, cls: "val-string" };
338
347
  if (typeof t == "number") return { text: String(t), cls: "val-number" };
@@ -345,16 +354,16 @@ function M(t) {
345
354
  }
346
355
  return { text: String(t), cls: "val-object" };
347
356
  }
348
- function L() {
349
- const t = p.getAll();
357
+ function H() {
358
+ const t = b.getAll();
350
359
  if (t.length === 0)
351
360
  return '<div class="t4-empty">No signals tracked yet.<br>Signals created after debug is enabled will appear here.</div>';
352
361
  let o = "";
353
362
  for (let e = 0; e < t.length; e++) {
354
- const n = t[e], { text: r, cls: s } = M(n.value);
363
+ const n = t[e], { text: r, cls: s } = L(n.value);
355
364
  o += `<tr>
356
365
  <td>${n.label || `signal_${e}`}</td>
357
- <td><span class="${s}">${H(r)}</span></td>
366
+ <td><span class="${s}">${j(r)}</span></td>
358
367
  <td>${n.subscriberCount}</td>
359
368
  <td>${n.updateCount}</td>
360
369
  </tr>`;
@@ -364,19 +373,19 @@ function L() {
364
373
  <tbody>${o}</tbody>
365
374
  </table>`;
366
375
  }
367
- function H(t) {
376
+ function j(t) {
368
377
  return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
369
378
  }
370
- function j() {
371
- const t = b.getAll();
379
+ function z() {
380
+ const t = h.getAll();
372
381
  if (t.length === 0)
373
382
  return '<div class="t4-empty">No Tina4Elements mounted.<br>Custom elements extending Tina4Element will appear here.</div>';
374
383
  let o = "";
375
384
  for (const e of t) {
376
385
  const n = Object.keys(e.props).length > 0 ? Object.entries(e.props).map(([r, s]) => `${r}=${JSON.stringify(s) ?? "null"}`).join(", ") : "—";
377
386
  o += `<tr>
378
- <td>&lt;${y(e.tagName)}&gt;</td>
379
- <td>${y(n.length > 60 ? n.slice(0, 60) + "..." : n)}</td>
387
+ <td>&lt;${w(e.tagName)}&gt;</td>
388
+ <td>${w(n.length > 60 ? n.slice(0, 60) + "..." : n)}</td>
380
389
  </tr>`;
381
390
  }
382
391
  return `<table>
@@ -384,24 +393,24 @@ function j() {
384
393
  <tbody>${o}</tbody>
385
394
  </table>`;
386
395
  }
387
- function y(t) {
396
+ function w(t) {
388
397
  return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
389
398
  }
390
- function z(t) {
399
+ function N(t) {
391
400
  const o = t < 1 ? "<1ms" : `${Math.round(t)}ms`, e = t < 5 ? "duration fast" : t < 50 ? "duration" : t < 200 ? "duration slow" : "duration very-slow";
392
401
  return { text: o, cls: e };
393
402
  }
394
- function N(t) {
403
+ function I(t) {
395
404
  return new Date(t).toLocaleTimeString("en-US", { hour12: !1, hour: "2-digit", minute: "2-digit", second: "2-digit" });
396
405
  }
397
- function I() {
398
- const t = S(), o = f.getHistory();
406
+ function P() {
407
+ const t = p.getRegisteredRoutes(), o = p.getHistory();
399
408
  let e = "";
400
409
  if (t.length > 0) {
401
410
  let n = "";
402
411
  for (const r of t)
403
412
  n += `<tr>
404
- <td><span class="route-pattern">${w(r.pattern)}</span></td>
413
+ <td><span class="route-pattern">${_(r.pattern)}</span></td>
405
414
  <td>${r.hasGuard ? "Yes" : "—"}</td>
406
415
  </tr>`;
407
416
  e += `<table>
@@ -413,11 +422,11 @@ function I() {
413
422
  e += '<div style="margin-top:8px;padding-top:8px;border-top:1px solid #333;">';
414
423
  let n = "";
415
424
  for (const r of o) {
416
- const { text: s, cls: i } = z(r.durationMs), _ = Object.keys(r.params).length > 0 ? Object.entries(r.params).map(([$, k]) => `<span class="route-param">${$}=${k}</span>`).join(" ") : "";
425
+ const { text: s, cls: i } = N(r.durationMs), $ = Object.keys(r.params).length > 0 ? Object.entries(r.params).map(([k, T]) => `<span class="route-param">${k}=${T}</span>`).join(" ") : "";
417
426
  n += `<tr>
418
- <td>${N(r.timestamp)}</td>
419
- <td>${w(r.path)}</td>
420
- <td>${_ || "—"}</td>
427
+ <td>${I(r.timestamp)}</td>
428
+ <td>${_(r.path)}</td>
429
+ <td>${$ || "—"}</td>
421
430
  <td><span class="${i}">${s}</span></td>
422
431
  </tr>`;
423
432
  }
@@ -428,29 +437,29 @@ function I() {
428
437
  } else t.length === 0 && (e = '<div class="t4-empty">No routes registered yet.</div>');
429
438
  return e;
430
439
  }
431
- function w(t) {
440
+ function _(t) {
432
441
  return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
433
442
  }
434
- function P(t) {
443
+ function O(t) {
435
444
  if (t === void 0) return { text: "...", cls: "status-pending" };
436
445
  const o = t < 1 ? "<1ms" : `${Math.round(t)}ms`, e = t < 100 ? "duration fast" : t < 500 ? "duration" : t < 2e3 ? "duration slow" : "duration very-slow";
437
446
  return { text: o, cls: e };
438
447
  }
439
- function O(t, o) {
448
+ function U(t, o) {
440
449
  return o ? { text: "pending", cls: "status-pending" } : t ? t >= 200 && t < 300 ? { text: String(t), cls: "status-ok" } : { text: String(t), cls: "status-err" } : { text: "—", cls: "" };
441
450
  }
442
- function U(t) {
451
+ function q(t) {
443
452
  return new Date(t).toLocaleTimeString("en-US", { hour12: !1, hour: "2-digit", minute: "2-digit", second: "2-digit" });
444
453
  }
445
- function q() {
446
- const t = h.getLog();
454
+ function G() {
455
+ const t = g.getLog();
447
456
  if (t.length === 0)
448
457
  return '<div class="t4-empty">No API calls yet.<br>Requests made via api.get/post/put/patch/delete will appear here.</div>';
449
458
  let o = "";
450
459
  for (const e of t) {
451
- const { text: n, cls: r } = O(e.status, e.pending), { text: s, cls: i } = P(e.durationMs);
460
+ const { text: n, cls: r } = U(e.status, e.pending), { text: s, cls: i } = O(e.durationMs);
452
461
  o += `<tr>
453
- <td>${U(e.timestamp)}</td>
462
+ <td>${q(e.timestamp)}</td>
454
463
  <td><strong>${e.method}</strong></td>
455
464
  <td>${B(e.url || "(url)")}</td>
456
465
  <td><span class="${r}">${n}</span></td>
@@ -466,7 +475,7 @@ function q() {
466
475
  function B(t) {
467
476
  return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
468
477
  }
469
- class G extends HTMLElement {
478
+ class F extends HTMLElement {
470
479
  constructor() {
471
480
  super(), this._visible = !0, this._activeTab = "signals", this._refreshTimer = null, this._shadow = this.attachShadow({ mode: "open" });
472
481
  }
@@ -499,13 +508,13 @@ class G extends HTMLElement {
499
508
  _getTabContent() {
500
509
  switch (this._activeTab) {
501
510
  case "signals":
502
- return L();
511
+ return H();
503
512
  case "components":
504
- return j();
513
+ return z();
505
514
  case "routes":
506
- return I();
515
+ return P();
507
516
  case "api":
508
- return q();
517
+ return G();
509
518
  }
510
519
  }
511
520
  _renderBody() {
@@ -514,10 +523,10 @@ class G extends HTMLElement {
514
523
  }
515
524
  _updateTabCounts() {
516
525
  const o = {
517
- signals: p.count,
518
- components: b.count,
519
- routes: f.count,
520
- api: h.count
526
+ signals: b.count,
527
+ components: h.count,
528
+ routes: p.count,
529
+ api: g.count
521
530
  };
522
531
  for (const [e, n] of Object.entries(o)) {
523
532
  const r = this._shadow.querySelector(`[data-tab-count="${e}"]`);
@@ -534,7 +543,7 @@ class G extends HTMLElement {
534
543
  ];
535
544
  if (!this._visible) {
536
545
  this._shadow.innerHTML = `
537
- <style>${x}</style>
546
+ <style>${y}</style>
538
547
  <div class="t4-mini" id="t4-mini">
539
548
  <span class="t4-mini-dot"></span>
540
549
  T4 Debug
@@ -548,7 +557,7 @@ class G extends HTMLElement {
548
557
  </button>`
549
558
  ).join("");
550
559
  this._shadow.innerHTML = `
551
- <style>${x}</style>
560
+ <style>${y}</style>
552
561
  <div class="t4-debug">
553
562
  <div class="t4-header">
554
563
  <div>
@@ -571,19 +580,19 @@ class G extends HTMLElement {
571
580
  }
572
581
  }
573
582
  function V() {
574
- typeof customElements < "u" && !customElements.get("tina4-debug") && customElements.define("tina4-debug", G);
583
+ typeof customElements < "u" && !customElements.get("tina4-debug") && customElements.define("tina4-debug", F);
575
584
  }
576
585
  let u = null, v = !1;
577
586
  function W() {
578
- v || (v = !0, T(
579
- (t, o) => p.add(t, o),
580
- (t) => p.onUpdate(t)
581
- ), C(
582
- (t) => b.onMount(t),
583
- (t) => b.onUnmount(t)
584
- ), A.on("change", (t) => {
585
- f.onNavigate(t);
586
- }), m.intercept("request", (t) => (h.onRequest(t), t)), m.intercept("response", (t) => (h.onResponse(t), t)), typeof document < "u" && (V(), u = document.createElement("tina4-debug"), document.body.appendChild(u), document.addEventListener("keydown", (t) => {
587
+ v || (v = !0, C(
588
+ (t, o) => b.add(t, o),
589
+ (t) => b.onUpdate(t)
590
+ ), S(
591
+ (t) => h.onMount(t),
592
+ (t) => h.onUnmount(t)
593
+ ), p.setGetRoutes(R), A.on("change", (t) => {
594
+ p.onNavigate(t);
595
+ }), x.intercept("request", (t) => (g.onRequest(t), t)), x.intercept("response", (t) => (g.onResponse(t), t)), typeof document < "u" && (V(), u = document.createElement("tina4-debug"), document.body.appendChild(u), document.addEventListener("keydown", (t) => {
587
596
  t.ctrlKey && t.shiftKey && t.key === "D" && (t.preventDefault(), u == null || u.toggle());
588
597
  })), console.log(
589
598
  "%c[tina4] %cDebug overlay enabled %c(Ctrl+Shift+D to toggle)",
package/dist/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as y } from "./signal.es.js";
1
+ import { d as y } from "./signal.es.js";
2
2
  let p = [], a = null, u = "history";
3
3
  const c = [];
4
4
  let l = [], w = 0;
@@ -1 +1 @@
1
- "use strict";let s=null,f=null;function p(e){f=e}let i=null,a=null;function S(e,t){i=e,a=t}let c=0;const d=new Set;function b(e,t){let n=e;const u=new Set,o={_t4:!0,get value(){return s&&u.add(s),n},set value(r){if(Object.is(r,n))return;const g=n;if(n=r,o._debugInfo&&o._debugInfo.updateCount++,a&&a(o,g,r),c>0)for(const l of u)d.add(l);else for(const l of[...u])l()},_subscribe(r){return u.add(r),()=>{u.delete(r)}},peek(){return n}};return i&&(o._debugInfo={label:t,createdAt:Date.now(),updateCount:0,subs:u},i(o,t)),o}function h(e){const t=b(void 0);return _(()=>{t.value=e()}),{_t4:!0,get value(){return t.value},set value(n){throw new Error("[tina4] computed signals are read-only")},_subscribe(n){return t._subscribe(n)},peek(){return t.peek()}}}function _(e){let t=!1;const n=()=>{if(t)return;const o=s;s=n;try{e()}finally{s=o}};n();const u=()=>{t=!0};return f&&f.push(u),u}function v(e){c++;try{e()}finally{if(c--,c===0){const t=[...d];d.clear();for(const n of t)n()}}}function y(e){return e!==null&&typeof e=="object"&&e._t4===!0}exports.__setDebugSignalHooks=S;exports._setEffectCollector=p;exports.batch=v;exports.computed=h;exports.effect=_;exports.isSignal=y;exports.signal=b;
1
+ "use strict";let c=null,s=null,i=null;function C(t){i=t}function h(){return i}let d=null,_=null;function v(t,n){d=t,_=n}let f=0;const b=new Set;function g(t,n){let e=t;const o=new Set,r={_t4:!0,get value(){if(c&&(o.add(c),s)){const u=c;s.push(()=>o.delete(u))}return e},set value(u){if(Object.is(u,e))return;const a=e;if(e=u,r._debugInfo&&r._debugInfo.updateCount++,_&&_(r,a,u),f>0)for(const l of o)b.add(l);else for(const l of[...o])l()},_subscribe(u){return o.add(u),()=>{o.delete(u)}},peek(){return e}};return d&&(r._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:o},d(r,n)),r}function S(t){const n=g(void 0);return p(()=>{n.value=t()}),{_t4:!0,get value(){return n.value},set value(e){throw new Error("[tina4] computed signals are read-only")},_subscribe(e){return n._subscribe(e)},peek(){return n.peek()}}}function p(t){let n=!1,e=[];const o=()=>{if(n)return;for(const l of e)l();e=[];const u=c,a=s;c=o,s=e;try{t()}finally{c=u,s=a}};o();const r=()=>{n=!0;for(const u of e)u();e=[]};return i&&i.push(r),r}function y(t){f++;try{t()}finally{if(f--,f===0){const n=[...b];b.clear();for(const e of n)e()}}}function E(t){return t!==null&&typeof t=="object"&&t._t4===!0}exports.__setDebugSignalHooks=v;exports._getEffectCollector=h;exports._setEffectCollector=C;exports.batch=y;exports.computed=S;exports.effect=p;exports.isSignal=E;exports.signal=g;
package/dist/signal.es.js CHANGED
@@ -1,98 +1,110 @@
1
- let s = null, a = null;
2
- function g(e) {
3
- a = e;
1
+ let s = null, l = null, a = null;
2
+ function v(t) {
3
+ a = t;
4
4
  }
5
- let f = null, i = null;
6
- function v(e, t) {
7
- f = e, i = t;
5
+ function C() {
6
+ return a;
8
7
  }
9
- let c = 0;
10
- const d = /* @__PURE__ */ new Set();
11
- function _(e, t) {
12
- let n = e;
13
- const u = /* @__PURE__ */ new Set(), r = {
8
+ let d = null, b = null;
9
+ function h(t, n) {
10
+ d = t, b = n;
11
+ }
12
+ let f = 0;
13
+ const p = /* @__PURE__ */ new Set();
14
+ function _(t, n) {
15
+ let e = t;
16
+ const o = /* @__PURE__ */ new Set(), r = {
14
17
  _t4: !0,
15
18
  get value() {
16
- return s && u.add(s), n;
19
+ if (s && (o.add(s), l)) {
20
+ const u = s;
21
+ l.push(() => o.delete(u));
22
+ }
23
+ return e;
17
24
  },
18
- set value(o) {
19
- if (Object.is(o, n)) return;
20
- const b = n;
21
- if (n = o, r._debugInfo && r._debugInfo.updateCount++, i && i(r, b, o), c > 0)
22
- for (const l of u) d.add(l);
25
+ set value(u) {
26
+ if (Object.is(u, e)) return;
27
+ const i = e;
28
+ if (e = u, r._debugInfo && r._debugInfo.updateCount++, b && b(r, i, u), f > 0)
29
+ for (const c of o) p.add(c);
23
30
  else
24
- for (const l of [...u]) l();
31
+ for (const c of [...o]) c();
25
32
  },
26
- _subscribe(o) {
27
- return u.add(o), () => {
28
- u.delete(o);
33
+ _subscribe(u) {
34
+ return o.add(u), () => {
35
+ o.delete(u);
29
36
  };
30
37
  },
31
38
  peek() {
32
- return n;
39
+ return e;
33
40
  }
34
41
  };
35
- return f && (r._debugInfo = { label: t, createdAt: Date.now(), updateCount: 0, subs: u }, f(r, t)), r;
42
+ return d && (r._debugInfo = { label: n, createdAt: Date.now(), updateCount: 0, subs: o }, d(r, n)), r;
36
43
  }
37
- function h(e) {
38
- const t = _(void 0);
39
- return p(() => {
40
- t.value = e();
44
+ function y(t) {
45
+ const n = _(void 0);
46
+ return g(() => {
47
+ n.value = t();
41
48
  }), {
42
49
  _t4: !0,
43
50
  get value() {
44
- return t.value;
51
+ return n.value;
45
52
  },
46
- set value(n) {
53
+ set value(e) {
47
54
  throw new Error("[tina4] computed signals are read-only");
48
55
  },
49
- _subscribe(n) {
50
- return t._subscribe(n);
56
+ _subscribe(e) {
57
+ return n._subscribe(e);
51
58
  },
52
59
  peek() {
53
- return t.peek();
60
+ return n.peek();
54
61
  }
55
62
  };
56
63
  }
57
- function p(e) {
58
- let t = !1;
59
- const n = () => {
60
- if (t) return;
61
- const r = s;
62
- s = n;
64
+ function g(t) {
65
+ let n = !1, e = [];
66
+ const o = () => {
67
+ if (n) return;
68
+ for (const c of e) c();
69
+ e = [];
70
+ const u = s, i = l;
71
+ s = o, l = e;
63
72
  try {
64
- e();
73
+ t();
65
74
  } finally {
66
- s = r;
75
+ s = u, l = i;
67
76
  }
68
77
  };
69
- n();
70
- const u = () => {
71
- t = !0;
78
+ o();
79
+ const r = () => {
80
+ n = !0;
81
+ for (const u of e) u();
82
+ e = [];
72
83
  };
73
- return a && a.push(u), u;
84
+ return a && a.push(r), r;
74
85
  }
75
- function y(e) {
76
- c++;
86
+ function S(t) {
87
+ f++;
77
88
  try {
78
- e();
89
+ t();
79
90
  } finally {
80
- if (c--, c === 0) {
81
- const t = [...d];
82
- d.clear();
83
- for (const n of t) n();
91
+ if (f--, f === 0) {
92
+ const n = [...p];
93
+ p.clear();
94
+ for (const e of n) e();
84
95
  }
85
96
  }
86
97
  }
87
- function S(e) {
88
- return e !== null && typeof e == "object" && e._t4 === !0;
98
+ function w(t) {
99
+ return t !== null && typeof t == "object" && t._t4 === !0;
89
100
  }
90
101
  export {
91
- v as _,
92
- g as a,
93
- y as b,
94
- h as c,
95
- p as e,
96
- S as i,
102
+ h as _,
103
+ C as a,
104
+ S as b,
105
+ y as c,
106
+ v as d,
107
+ g as e,
108
+ w as i,
97
109
  _ as s
98
110
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4js",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Sub-3KB reactive framework — signals, web components, routing, PWA",
5
5
  "type": "module",
6
6
  "main": "dist/tina4.cjs.js",
package/readme.md CHANGED
@@ -240,6 +240,11 @@ npm run dev # dev server
240
240
 
241
241
  ## Changelog
242
242
 
243
+ ### 1.0.5
244
+ - **Fix:** Effects now properly unsubscribe from signals on dispose — prevents stale subscriptions accumulating in signal subscriber sets across navigations
245
+ - **Fix:** Function bindings in `html` templates now dispose inner effects when re-evaluated — fixes duplicate DOM nodes from nested reactive lists and conditionals
246
+ - Added 9 new tests covering effect subscription cleanup, inner effect disposal, and multi-navigation accumulation (116 total)
247
+
243
248
  ### 1.0.4
244
249
  - Added router reactive effect cleanup tests (navigate away/back, stale effects, async handlers, stale async discard)
245
250
  - Added debug overlay documentation to README and TINA4.md