tina4js 1.0.14 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/tina4.js CHANGED
@@ -201,7 +201,7 @@ route('/', homePage);
201
201
  route('/about', () => html\`
202
202
  <div class="page">
203
203
  <h1>About</h1>
204
- <p>Built with <a href="https://github.com/tina4stack/tina4-js">tina4-js</a> — a sub-3KB reactive framework.</p>
204
+ <p>Built with <a href="https://github.com/tina4stack/tina4-js">tina4-js</a> — a sub-3KB core, reactive framework.</p>
205
205
  <a href="/">Back home</a>
206
206
  </div>
207
207
  \`);
@@ -443,7 +443,7 @@ async def spa_catchall(path, request, response):
443
443
 
444
444
  function printHelp() {
445
445
  console.log(`
446
- ${c.bold('tina4js')} — Sub-3KB reactive framework
446
+ ${c.bold('tina4js')} — Sub-3KB core, reactive framework
447
447
 
448
448
  ${c.bold('Usage:')}
449
449
  tina4js create <name> Create a new project
@@ -84,6 +84,24 @@ export declare const api: {
84
84
  patch<T = unknown>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
85
85
  /** HTTP DELETE. */
86
86
  delete<T = unknown>(path: string, options?: RequestOptions): Promise<T>;
87
+ /**
88
+ * Upload files via FormData (multipart/form-data).
89
+ *
90
+ * Unlike `post()`, this does NOT JSON-stringify the body or set
91
+ * Content-Type — the browser sets the multipart boundary automatically.
92
+ * Auth uses the Bearer token header (formToken cannot be injected into FormData).
93
+ *
94
+ * @param path - API path relative to `baseUrl`.
95
+ * @param formData - A FormData instance containing files and fields.
96
+ * @param options - `{ params, headers }` — query string params and per-request headers.
97
+ *
98
+ * @example
99
+ * const form = new FormData();
100
+ * form.append('avatar', fileInput.files[0]);
101
+ * form.append('name', 'Alice');
102
+ * const result = await api.upload('/users/avatar', form);
103
+ */
104
+ upload<T = unknown>(path: string, formData: FormData, options?: RequestOptions): Promise<T>;
87
105
  /**
88
106
  * Register a request or response interceptor.
89
107
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/api/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;IACR,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,KAAK,CAAC,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,GAAG,IAAI,CAAC;AAC3J,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;AA+BhF,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AA+FD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,GAAG;IACd;;;;;;;;;;OAUG;iBACU,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;IAItC;;;;;;OAMG;QACC,CAAC,kBAAkB,MAAM,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpE;;;;;;;OAOG;SACE,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIrF,+BAA+B;QAC3B,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpF,mCAAmC;UAC7B,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAItF,mBAAmB;WACZ,CAAC,kBAAkB,MAAM,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIvE;;;;;;;;;;;;;OAaG;oBACa,SAAS,GAAG,UAAU,MAAM,kBAAkB,GAAG,mBAAmB,GAAG,IAAI;IAQ3F,yCAAyC;cAC/B,IAAI;CAQf,CAAC"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/api/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;IACR,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,KAAK,CAAC,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,GAAG,IAAI,CAAC;AAC3J,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;AA+BhF,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AA+FD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,GAAG;IACd;;;;;;;;;;OAUG;iBACU,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;IAItC;;;;;;OAMG;QACC,CAAC,kBAAkB,MAAM,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpE;;;;;;;OAOG;SACE,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIrF,+BAA+B;QAC3B,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpF,mCAAmC;UAC7B,CAAC,kBAAkB,MAAM,SAAS,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAItF,mBAAmB;WACZ,CAAC,kBAAkB,MAAM,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAIvE;;;;;;;;;;;;;;;;OAgBG;WACU,CAAC,kBAAkB,MAAM,YAAY,QAAQ,YAAY,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAiFjG;;;;;;;;;;;;;OAaG;oBACa,SAAS,GAAG,UAAU,MAAM,kBAAkB,GAAG,mBAAmB,GAAG,IAAI;IAQ3F,yCAAyC;cAC/B,IAAI;CAQf,CAAC"}
package/dist/api.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},d=[],h=[];let j=0;function T(){try{return localStorage.getItem(s.tokenKey)}catch{return null}}function I(e){try{localStorage.setItem(s.tokenKey,e)}catch{}}async function i(e,t,n,c){let o={method:e,headers:{"Content-Type":"application/json",...s.headers}};if(s.auth){const r=T();r&&(o.headers.Authorization=`Bearer ${r}`)}if(n!==void 0&&e!=="GET"){let r=typeof n=="object"&&n!==null?{...n}:n;if(s.auth&&typeof r=="object"&&r!==null){const a=T();a&&(r.formToken=a)}o.body=JSON.stringify(r)}if(c!=null&&c.headers&&Object.assign(o.headers,c.headers),c!=null&&c.params){const r=Object.entries(c.params).map(([a,y])=>`${encodeURIComponent(a)}=${encodeURIComponent(String(y))}`).join("&");t+=(t.includes("?")?"&":"?")+r}const g=s.baseUrl+t;o._url=g,o._requestId=++j;for(const r of d){const a=r(o);a&&(o=a)}const u=await fetch(g,o),k=u.headers.get("FreshToken");k&&I(k);const p=u.headers.get("Content-Type")??"";let f;p.includes("json")?f=await u.json():f=await u.text();let l={status:u.status,data:f,ok:u.ok,headers:u.headers,_requestId:o._requestId};for(const r of h){const a=r(l);a&&(l=a)}if(!u.ok)throw l;return l.data}const q={configure(e){Object.assign(s,e)},get(e,t){return i("GET",e,void 0,t)},post(e,t,n){return i("POST",e,t,n)},put(e,t,n){return i("PUT",e,t,n)},patch(e,t,n){return i("PATCH",e,t,n)},delete(e,t){return i("DELETE",e,void 0,t)},intercept(e,t){e==="request"?d.push(t):h.push(t)},_reset(){s.baseUrl="",s.auth=!1,s.tokenKey="tina4_token",s.headers={},d.length=0,h.length=0}};exports.api=q;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},k=[],T=[];let j=0;function y(){try{return localStorage.getItem(a.tokenKey)}catch{return null}}function I(e){try{localStorage.setItem(a.tokenKey,e)}catch{}}async function h(e,s,r,n){let c={method:e,headers:{"Content-Type":"application/json",...a.headers}};if(a.auth){const t=y();t&&(c.headers.Authorization=`Bearer ${t}`)}if(r!==void 0&&e!=="GET"){let t=typeof r=="object"&&r!==null?{...r}:r;if(a.auth&&typeof t=="object"&&t!==null){const u=y();u&&(t.formToken=u)}c.body=JSON.stringify(t)}if(n!=null&&n.headers&&Object.assign(c.headers,n.headers),n!=null&&n.params){const t=Object.entries(n.params).map(([u,m])=>`${encodeURIComponent(u)}=${encodeURIComponent(String(m))}`).join("&");s+=(s.includes("?")?"&":"?")+t}const d=a.baseUrl+s;c._url=d,c._requestId=++j;for(const t of k){const u=t(c);u&&(c=u)}const f=await fetch(d,c),g=f.headers.get("FreshToken");g&&I(g);const i=f.headers.get("Content-Type")??"";let l;i.includes("json")?l=await f.json():l=await f.text();let o={status:f.status,data:l,ok:f.ok,headers:f.headers,_requestId:c._requestId};for(const t of T){const u=t(o);u&&(o=u)}if(!f.ok)throw o;return o.data}const q={configure(e){Object.assign(a,e)},get(e,s){return h("GET",e,void 0,s)},post(e,s,r){return h("POST",e,s,r)},put(e,s,r){return h("PUT",e,s,r)},patch(e,s,r){return h("PATCH",e,s,r)},delete(e,s){return h("DELETE",e,void 0,s)},async upload(e,s,r){let n={method:"POST",headers:{...a.headers},body:s};if(delete n.headers["Content-Type"],delete n.headers["content-type"],a.auth){const o=y();o&&(n.headers.Authorization=`Bearer ${o}`)}if(r!=null&&r.headers&&Object.assign(n.headers,r.headers),r!=null&&r.params){const o=Object.entries(r.params).map(([t,u])=>`${encodeURIComponent(t)}=${encodeURIComponent(String(u))}`).join("&");e+=(e.includes("?")?"&":"?")+o}const c=a.baseUrl+e;n._url=c,n._requestId=++j;for(const o of k){const t=o(n);t&&(n=t)}const d=await fetch(c,n),f=d.headers.get("FreshToken");f&&I(f);const g=d.headers.get("Content-Type")??"";let i;g.includes("json")?i=await d.json():i=await d.text();let l={status:d.status,data:i,ok:d.ok,headers:d.headers,_requestId:n._requestId};for(const o of T){const t=o(l);t&&(l=t)}if(!d.ok)throw l;return l.data},intercept(e,s){e==="request"?k.push(s):T.push(s)},_reset(){a.baseUrl="",a.auth=!1,a.tokenKey="tina4_token",a.headers={},k.length=0,T.length=0}};exports.api=q;
package/dist/api.es.js CHANGED
@@ -1,74 +1,74 @@
1
- const s = {
1
+ const a = {
2
2
  baseUrl: "",
3
3
  auth: !1,
4
4
  tokenKey: "tina4_token",
5
5
  headers: {}
6
- }, h = [], d = [];
6
+ }, g = [], T = [];
7
7
  let I = 0;
8
- function T() {
8
+ function y() {
9
9
  try {
10
- return localStorage.getItem(s.tokenKey);
10
+ return localStorage.getItem(a.tokenKey);
11
11
  } catch {
12
12
  return null;
13
13
  }
14
14
  }
15
15
  function j(e) {
16
16
  try {
17
- localStorage.setItem(s.tokenKey, e);
17
+ localStorage.setItem(a.tokenKey, e);
18
18
  } catch {
19
19
  }
20
20
  }
21
- async function l(e, t, r, c) {
22
- let o = {
21
+ async function h(e, s, r, n) {
22
+ let c = {
23
23
  method: e,
24
24
  headers: {
25
25
  "Content-Type": "application/json",
26
- ...s.headers
26
+ ...a.headers
27
27
  }
28
28
  };
29
- if (s.auth) {
30
- const n = T();
31
- n && (o.headers.Authorization = `Bearer ${n}`);
29
+ if (a.auth) {
30
+ const t = y();
31
+ t && (c.headers.Authorization = `Bearer ${t}`);
32
32
  }
33
33
  if (r !== void 0 && e !== "GET") {
34
- let n = typeof r == "object" && r !== null ? { ...r } : r;
35
- if (s.auth && typeof n == "object" && n !== null) {
36
- const a = T();
37
- a && (n.formToken = a);
34
+ let t = typeof r == "object" && r !== null ? { ...r } : r;
35
+ if (a.auth && typeof t == "object" && t !== null) {
36
+ const u = y();
37
+ u && (t.formToken = u);
38
38
  }
39
- o.body = JSON.stringify(n);
39
+ c.body = JSON.stringify(t);
40
40
  }
41
- if (c != null && c.headers && Object.assign(o.headers, c.headers), c != null && c.params) {
42
- const n = Object.entries(c.params).map(([a, y]) => `${encodeURIComponent(a)}=${encodeURIComponent(String(y))}`).join("&");
43
- t += (t.includes("?") ? "&" : "?") + n;
41
+ if (n != null && n.headers && Object.assign(c.headers, n.headers), n != null && n.params) {
42
+ const t = Object.entries(n.params).map(([u, q]) => `${encodeURIComponent(u)}=${encodeURIComponent(String(q))}`).join("&");
43
+ s += (s.includes("?") ? "&" : "?") + t;
44
44
  }
45
- const g = s.baseUrl + t;
46
- o._url = g, o._requestId = ++I;
47
- for (const n of h) {
48
- const a = n(o);
49
- a && (o = a);
45
+ const d = a.baseUrl + s;
46
+ c._url = d, c._requestId = ++I;
47
+ for (const t of g) {
48
+ const u = t(c);
49
+ u && (c = u);
50
50
  }
51
- const u = await fetch(g, o), k = u.headers.get("FreshToken");
51
+ const f = await fetch(d, c), k = f.headers.get("FreshToken");
52
52
  k && j(k);
53
- const p = u.headers.get("Content-Type") ?? "";
54
- let i;
55
- p.includes("json") ? i = await u.json() : i = await u.text();
56
- let f = {
57
- status: u.status,
58
- data: i,
59
- ok: u.ok,
60
- headers: u.headers,
61
- _requestId: o._requestId
53
+ const i = f.headers.get("Content-Type") ?? "";
54
+ let l;
55
+ i.includes("json") ? l = await f.json() : l = await f.text();
56
+ let o = {
57
+ status: f.status,
58
+ data: l,
59
+ ok: f.ok,
60
+ headers: f.headers,
61
+ _requestId: c._requestId
62
62
  };
63
- for (const n of d) {
64
- const a = n(f);
65
- a && (f = a);
63
+ for (const t of T) {
64
+ const u = t(o);
65
+ u && (o = u);
66
66
  }
67
- if (!u.ok)
68
- throw f;
69
- return f.data;
67
+ if (!f.ok)
68
+ throw o;
69
+ return o.data;
70
70
  }
71
- const q = {
71
+ const m = {
72
72
  /**
73
73
  * Configure the API client. Call once at app startup.
74
74
  *
@@ -81,7 +81,7 @@ const q = {
81
81
  * });
82
82
  */
83
83
  configure(e) {
84
- Object.assign(s, e);
84
+ Object.assign(a, e);
85
85
  },
86
86
  /**
87
87
  * HTTP GET request.
@@ -90,8 +90,8 @@ const q = {
90
90
  * @example
91
91
  * const data = await api.get('/products', { params: { page: 2, limit: 20 } });
92
92
  */
93
- get(e, t) {
94
- return l("GET", e, void 0, t);
93
+ get(e, s) {
94
+ return h("GET", e, void 0, s);
95
95
  },
96
96
  /**
97
97
  * HTTP POST request.
@@ -101,20 +101,80 @@ const q = {
101
101
  * @example
102
102
  * await api.post('/users', { name: 'Alice', role: 'admin' });
103
103
  */
104
- post(e, t, r) {
105
- return l("POST", e, t, r);
104
+ post(e, s, r) {
105
+ return h("POST", e, s, r);
106
106
  },
107
107
  /** HTTP PUT — full replace. */
108
- put(e, t, r) {
109
- return l("PUT", e, t, r);
108
+ put(e, s, r) {
109
+ return h("PUT", e, s, r);
110
110
  },
111
111
  /** HTTP PATCH — partial update. */
112
- patch(e, t, r) {
113
- return l("PATCH", e, t, r);
112
+ patch(e, s, r) {
113
+ return h("PATCH", e, s, r);
114
114
  },
115
115
  /** HTTP DELETE. */
116
- delete(e, t) {
117
- return l("DELETE", e, void 0, t);
116
+ delete(e, s) {
117
+ return h("DELETE", e, void 0, s);
118
+ },
119
+ /**
120
+ * Upload files via FormData (multipart/form-data).
121
+ *
122
+ * Unlike `post()`, this does NOT JSON-stringify the body or set
123
+ * Content-Type — the browser sets the multipart boundary automatically.
124
+ * Auth uses the Bearer token header (formToken cannot be injected into FormData).
125
+ *
126
+ * @param path - API path relative to `baseUrl`.
127
+ * @param formData - A FormData instance containing files and fields.
128
+ * @param options - `{ params, headers }` — query string params and per-request headers.
129
+ *
130
+ * @example
131
+ * const form = new FormData();
132
+ * form.append('avatar', fileInput.files[0]);
133
+ * form.append('name', 'Alice');
134
+ * const result = await api.upload('/users/avatar', form);
135
+ */
136
+ async upload(e, s, r) {
137
+ let n = {
138
+ method: "POST",
139
+ headers: {
140
+ ...a.headers
141
+ // Do NOT set Content-Type — browser sets it with multipart boundary
142
+ },
143
+ body: s
144
+ };
145
+ if (delete n.headers["Content-Type"], delete n.headers["content-type"], a.auth) {
146
+ const o = y();
147
+ o && (n.headers.Authorization = `Bearer ${o}`);
148
+ }
149
+ if (r != null && r.headers && Object.assign(n.headers, r.headers), r != null && r.params) {
150
+ const o = Object.entries(r.params).map(([t, u]) => `${encodeURIComponent(t)}=${encodeURIComponent(String(u))}`).join("&");
151
+ e += (e.includes("?") ? "&" : "?") + o;
152
+ }
153
+ const c = a.baseUrl + e;
154
+ n._url = c, n._requestId = ++I;
155
+ for (const o of g) {
156
+ const t = o(n);
157
+ t && (n = t);
158
+ }
159
+ const d = await fetch(c, n), f = d.headers.get("FreshToken");
160
+ f && j(f);
161
+ const k = d.headers.get("Content-Type") ?? "";
162
+ let i;
163
+ k.includes("json") ? i = await d.json() : i = await d.text();
164
+ let l = {
165
+ status: d.status,
166
+ data: i,
167
+ ok: d.ok,
168
+ headers: d.headers,
169
+ _requestId: n._requestId
170
+ };
171
+ for (const o of T) {
172
+ const t = o(l);
173
+ t && (l = t);
174
+ }
175
+ if (!d.ok)
176
+ throw l;
177
+ return l.data;
118
178
  },
119
179
  /**
120
180
  * Register a request or response interceptor.
@@ -130,14 +190,14 @@ const q = {
130
190
  * if (res.status === 401) navigate('/login');
131
191
  * });
132
192
  */
133
- intercept(e, t) {
134
- e === "request" ? h.push(t) : d.push(t);
193
+ intercept(e, s) {
194
+ e === "request" ? g.push(s) : T.push(s);
135
195
  },
136
196
  /** @internal Reset state (for tests). */
137
197
  _reset() {
138
- s.baseUrl = "", s.auth = !1, s.tokenKey = "tina4_token", s.headers = {}, h.length = 0, d.length = 0;
198
+ a.baseUrl = "", a.auth = !1, a.tokenKey = "tina4_token", a.headers = {}, g.length = 0, T.length = 0;
139
199
  }
140
200
  };
141
201
  export {
142
- q as api
202
+ m as api
143
203
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tina4js — Sub-3KB reactive framework.
2
+ * tina4js — Sub-3KB core, reactive framework.
3
3
  *
4
4
  * Signals + tagged template rendering + web components + routing + API + PWA.
5
5
  */
@@ -16,4 +16,6 @@ export { pwa } from './pwa/pwa';
16
16
  export type { PWAConfig } from './pwa/pwa';
17
17
  export { ws } from './ws/ws';
18
18
  export type { SocketStatus, SocketOptions, ManagedSocket } from './ws/ws';
19
+ export { sse } from './sse/sse';
20
+ export type { StreamStatus, StreamOptions, ManagedStream } from './sse/sse';
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG1F,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG1E,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG1F,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG1E,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG1E,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * tina4js/sse — Signal-driven SSE / NDJSON streaming client.
3
+ */
4
+ export { sse } from './sse';
5
+ export type { StreamStatus, StreamOptions, ManagedStream } from './sse';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sse/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Tina4 SSE — Signal-driven Server-Sent Events / NDJSON streaming client.
3
+ *
4
+ * sse.connect(url, options?) — create a managed event stream
5
+ * stream.status — reactive signal: 'connecting' | 'open' | 'closed' | 'reconnecting'
6
+ * stream.connected — reactive signal: boolean
7
+ * stream.lastMessage — reactive signal: last parsed message
8
+ * stream.lastEvent — reactive signal: last SSE event name (or null)
9
+ * stream.on(event, handler) — listen for messages/open/close/error
10
+ * stream.pipe(signal, reducer) — pipe messages directly into a signal
11
+ * stream.close() — disconnect (stops reconnect)
12
+ */
13
+ import type { Signal } from '../core/signal';
14
+ export type StreamStatus = 'connecting' | 'open' | 'closed' | 'reconnecting';
15
+ export interface StreamOptions {
16
+ /** Transport: 'eventsource' (default) or 'fetch' for NDJSON/POST. */
17
+ mode?: 'eventsource' | 'fetch';
18
+ /** HTTP method for fetch mode (default: 'GET'). */
19
+ method?: string;
20
+ /** Custom headers for fetch mode. */
21
+ headers?: Record<string, string>;
22
+ /** Request body for fetch mode (objects are JSON.stringify'd). */
23
+ body?: unknown;
24
+ /** Enable auto-reconnect (default: true). */
25
+ reconnect?: boolean;
26
+ /** Initial reconnect delay in ms (default: 1000). */
27
+ reconnectDelay?: number;
28
+ /** Max reconnect delay in ms (default: 30000). */
29
+ reconnectMaxDelay?: number;
30
+ /** Max reconnect attempts (default: Infinity). */
31
+ reconnectAttempts?: number;
32
+ /** Named SSE events to listen for (eventsource mode only). */
33
+ events?: string[];
34
+ /** Parse each message as JSON (default: true). */
35
+ json?: boolean;
36
+ }
37
+ export type MessageHandler = (data: unknown, event?: string) => void;
38
+ export type OpenHandler = () => void;
39
+ export type CloseHandler = () => void;
40
+ export type ErrorHandler = (error: Event | Error) => void;
41
+ type EventMap = {
42
+ message: MessageHandler;
43
+ open: OpenHandler;
44
+ close: CloseHandler;
45
+ error: ErrorHandler;
46
+ };
47
+ export interface ManagedStream {
48
+ /** Reactive connection status. */
49
+ readonly status: Signal<StreamStatus>;
50
+ /** Reactive boolean — true when status is 'open'. */
51
+ readonly connected: Signal<boolean>;
52
+ /** Reactive — last received message (parsed JSON or raw string). */
53
+ readonly lastMessage: Signal<unknown>;
54
+ /** Reactive — last SSE event name, or null for unnamed/NDJSON. */
55
+ readonly lastEvent: Signal<string | null>;
56
+ /** Reactive — last error, or null. */
57
+ readonly error: Signal<Event | Error | null>;
58
+ /** Number of reconnect attempts so far. */
59
+ readonly reconnectCount: Signal<number>;
60
+ /** Listen for events. Returns unsubscribe function. */
61
+ on<K extends keyof EventMap>(event: K, handler: EventMap[K]): () => void;
62
+ /** Pipe messages into a signal via a reducer. */
63
+ pipe<T>(target: Signal<T>, reducer: (message: unknown, current: T) => T): () => void;
64
+ /** Close the connection and stop reconnecting. */
65
+ close(): void;
66
+ }
67
+ declare function createStream(url: string, options?: StreamOptions): ManagedStream;
68
+ export declare const sse: {
69
+ connect: typeof createStream;
70
+ };
71
+ export {};
72
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/sse/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAI7C,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,cAAc,CAAC;AAE7E,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAC/B,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AACrE,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AACrC,MAAM,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC;AACtC,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC;AAE1D,KAAK,QAAQ,GAAG;IACd,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IACtC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACtC,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;IAC7C,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAExC,uDAAuD;IACvD,EAAE,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAEzE,iDAAiD;IACjD,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;IAErF,kDAAkD;IAClD,KAAK,IAAI,IAAI,CAAC;CACf;AAmBD,iBAAS,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,aAAa,CAqO7E;AAID,eAAO,MAAM,GAAG;;CAEf,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./signal.cjs.js"),I={mode:"eventsource",method:"GET",headers:{},body:void 0,reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,events:[],json:!0};function N(E,k={}){const t={...I,...k},c=u.signal("connecting"),f=u.signal(!1),D=u.signal(null),T=u.signal(null),h=u.signal(null),b=u.signal(0),s={message:[],open:[],close:[],error:[]};let o=null,r=null,x=!1,v=t.reconnectDelay,d=null,a=0;function g(e){if(!t.json||typeof e!="string")return e;try{return JSON.parse(e)}catch{return e}}function m(e,n){D.value=e,T.value=n;for(const l of s.message)l(e,n??void 0)}function A(){c.value="open",f.value=!0,h.value=null,a=0,v=t.reconnectDelay,b.value=0;for(const e of s.open)e()}function y(){c.value="closed",f.value=!1;for(const e of s.close)e();!x&&t.reconnect&&a<t.reconnectAttempts&&L()}function S(e){h.value=e;for(const n of s.error)n(e)}function F(){c.value=a>0?"reconnecting":"connecting";try{o=new EventSource(E)}catch{c.value="closed",f.value=!1;return}o.onopen=()=>A(),o.onmessage=e=>{m(g(e.data),null)};for(const e of t.events)o.addEventListener(e,n=>{m(g(n.data),e)});o.onerror=e=>{S(e),o&&o.readyState===2&&(o=null,y())}}function J(){c.value=a>0?"reconnecting":"connecting",r=new AbortController;const e={method:t.method,headers:t.headers,signal:r.signal};t.body!==void 0&&(e.body=typeof t.body=="string"?t.body:JSON.stringify(t.body)),fetch(E,e).then(async n=>{if(!n.ok){S(new Error(`[tina4] SSE fetch ${n.status}`)),y();return}A();const l=n.body.getReader(),i=new TextDecoder;let p="";for(;;){const{done:R,value:q}=await l.read();if(R)break;p+=i.decode(q,{stream:!0});const j=p.split(`
2
+ `);p=j.pop();for(const G of j){const w=G.trim();w&&m(g(w),null)}}const C=p.trim();C&&m(g(C),null),r=null,y()}).catch(n=>{n.name!=="AbortError"&&(r=null,S(n),y())})}function L(){a++,b.value=a,c.value="reconnecting",d=setTimeout(()=>{d=null,M()},v),v=Math.min(v*2,t.reconnectMaxDelay)}function M(){t.mode==="fetch"?J():F()}const O={status:c,connected:f,lastMessage:D,lastEvent:T,error:h,reconnectCount:b,on(e,n){return s[e].push(n),()=>{const l=s[e],i=l.indexOf(n);i>=0&&l.splice(i,1)}},pipe(e,n){const l=i=>{e.value=n(i,e.value)};return O.on("message",l)},close(){x=!0,d&&(clearTimeout(d),d=null),o&&(o.close(),o=null),r&&(r.abort(),r=null),c.value="closed",f.value=!1}};return M(),O}const P={connect:N};exports.sse=P;
package/dist/sse.es.js ADDED
@@ -0,0 +1,137 @@
1
+ import { s as i } from "./signal.es.js";
2
+ const N = {
3
+ mode: "eventsource",
4
+ method: "GET",
5
+ headers: {},
6
+ body: void 0,
7
+ reconnect: !0,
8
+ reconnectDelay: 1e3,
9
+ reconnectMaxDelay: 3e4,
10
+ reconnectAttempts: 1 / 0,
11
+ events: [],
12
+ json: !0
13
+ };
14
+ function U(D, k = {}) {
15
+ const t = { ...N, ...k }, c = i("connecting"), f = i(!1), S = i(null), x = i(null), h = i(null), b = i(0), s = {
16
+ message: [],
17
+ open: [],
18
+ close: [],
19
+ error: []
20
+ };
21
+ let o = null, l = null, T = !1, v = t.reconnectDelay, d = null, a = 0;
22
+ function m(e) {
23
+ if (!t.json || typeof e != "string") return e;
24
+ try {
25
+ return JSON.parse(e);
26
+ } catch {
27
+ return e;
28
+ }
29
+ }
30
+ function p(e, n) {
31
+ S.value = e, x.value = n;
32
+ for (const r of s.message) r(e, n ?? void 0);
33
+ }
34
+ function A() {
35
+ c.value = "open", f.value = !0, h.value = null, a = 0, v = t.reconnectDelay, b.value = 0;
36
+ for (const e of s.open) e();
37
+ }
38
+ function y() {
39
+ c.value = "closed", f.value = !1;
40
+ for (const e of s.close) e();
41
+ !T && t.reconnect && a < t.reconnectAttempts && L();
42
+ }
43
+ function E(e) {
44
+ h.value = e;
45
+ for (const n of s.error) n(e);
46
+ }
47
+ function F() {
48
+ c.value = a > 0 ? "reconnecting" : "connecting";
49
+ try {
50
+ o = new EventSource(D);
51
+ } catch {
52
+ c.value = "closed", f.value = !1;
53
+ return;
54
+ }
55
+ o.onopen = () => A(), o.onmessage = (e) => {
56
+ p(m(e.data), null);
57
+ };
58
+ for (const e of t.events)
59
+ o.addEventListener(e, (n) => {
60
+ p(m(n.data), e);
61
+ });
62
+ o.onerror = (e) => {
63
+ E(e), o && o.readyState === 2 && (o = null, y());
64
+ };
65
+ }
66
+ function J() {
67
+ c.value = a > 0 ? "reconnecting" : "connecting", l = new AbortController();
68
+ const e = {
69
+ method: t.method,
70
+ headers: t.headers,
71
+ signal: l.signal
72
+ };
73
+ t.body !== void 0 && (e.body = typeof t.body == "string" ? t.body : JSON.stringify(t.body)), fetch(D, e).then(async (n) => {
74
+ if (!n.ok) {
75
+ E(new Error(`[tina4] SSE fetch ${n.status}`)), y();
76
+ return;
77
+ }
78
+ A();
79
+ const r = n.body.getReader(), u = new TextDecoder();
80
+ let g = "";
81
+ for (; ; ) {
82
+ const { done: R, value: G } = await r.read();
83
+ if (R) break;
84
+ g += u.decode(G, { stream: !0 });
85
+ const w = g.split(`
86
+ `);
87
+ g = w.pop();
88
+ for (const I of w) {
89
+ const j = I.trim();
90
+ j && p(m(j), null);
91
+ }
92
+ }
93
+ const O = g.trim();
94
+ O && p(m(O), null), l = null, y();
95
+ }).catch((n) => {
96
+ n.name !== "AbortError" && (l = null, E(n), y());
97
+ });
98
+ }
99
+ function L() {
100
+ a++, b.value = a, c.value = "reconnecting", d = setTimeout(() => {
101
+ d = null, C();
102
+ }, v), v = Math.min(v * 2, t.reconnectMaxDelay);
103
+ }
104
+ function C() {
105
+ t.mode === "fetch" ? J() : F();
106
+ }
107
+ const M = {
108
+ status: c,
109
+ connected: f,
110
+ lastMessage: S,
111
+ lastEvent: x,
112
+ error: h,
113
+ reconnectCount: b,
114
+ on(e, n) {
115
+ return s[e].push(n), () => {
116
+ const r = s[e], u = r.indexOf(n);
117
+ u >= 0 && r.splice(u, 1);
118
+ };
119
+ },
120
+ pipe(e, n) {
121
+ const r = (u) => {
122
+ e.value = n(u, e.value);
123
+ };
124
+ return M.on("message", r);
125
+ },
126
+ close() {
127
+ T = !0, d && (clearTimeout(d), d = null), o && (o.close(), o = null), l && (l.abort(), l = null), c.value = "closed", f.value = !1;
128
+ }
129
+ };
130
+ return C(), M;
131
+ }
132
+ const q = {
133
+ connect: U
134
+ };
135
+ export {
136
+ q as sse
137
+ };
package/dist/tina4.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signal.cjs.js"),r=require("./core.cjs.js"),i=require("./component.cjs.js"),t=require("./index.cjs.js"),n=require("./api.cjs.js"),a=require("./pwa.cjs.js"),o=require("./ws.cjs.js");exports.batch=e.batch;exports.computed=e.computed;exports.effect=e.effect;exports.isSignal=e.isSignal;exports.signal=e.signal;exports.html=r.html;exports.Tina4Element=i.Tina4Element;exports.navigate=t.navigate;exports.route=t.route;exports.router=t.router;exports.api=n.api;exports.pwa=a.pwa;exports.ws=o.ws;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signal.cjs.js"),r=require("./core.cjs.js"),i=require("./component.cjs.js"),t=require("./index.cjs.js"),s=require("./api.cjs.js"),n=require("./pwa.cjs.js"),o=require("./ws.cjs.js"),a=require("./sse.cjs.js");exports.batch=e.batch;exports.computed=e.computed;exports.effect=e.effect;exports.isSignal=e.isSignal;exports.signal=e.signal;exports.html=r.html;exports.Tina4Element=i.Tina4Element;exports.navigate=t.navigate;exports.route=t.route;exports.router=t.router;exports.api=s.api;exports.pwa=n.pwa;exports.ws=o.ws;exports.sse=a.sse;
package/dist/tina4.es.js CHANGED
@@ -1,14 +1,15 @@
1
- import { b as o, c as e, e as t, i as s, s as m } from "./signal.es.js";
1
+ import { b as a, c as e, e as t, i as s, s as m } from "./signal.es.js";
2
2
  import { html as f } from "./core.es.js";
3
- import { T as x } from "./component.es.js";
3
+ import { T as i } from "./component.es.js";
4
4
  import { n as c, r as l, a as g } from "./index.es.js";
5
5
  import { api as b } from "./api.es.js";
6
6
  import { pwa as w } from "./pwa.es.js";
7
7
  import { ws as d } from "./ws.es.js";
8
+ import { sse as E } from "./sse.es.js";
8
9
  export {
9
- x as Tina4Element,
10
+ i as Tina4Element,
10
11
  b as api,
11
- o as batch,
12
+ a as batch,
12
13
  e as computed,
13
14
  t as effect,
14
15
  f as html,
@@ -18,5 +19,6 @@ export {
18
19
  l as route,
19
20
  g as router,
20
21
  m as signal,
22
+ E as sse,
21
23
  d as ws
22
24
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tina4js",
3
- "version": "1.0.14",
4
- "description": "Sub-3KB reactive framework — signals, web components, routing, PWA",
3
+ "version": "1.1.0",
4
+ "description": "1.5KB core gzipped, reactive framework — signals, web components, routing, PWA",
5
5
  "type": "module",
6
6
  "main": "dist/tina4.cjs.js",
7
7
  "module": "dist/tina4.es.js",
@@ -35,6 +35,10 @@
35
35
  "./ws": {
36
36
  "import": "./dist/ws.es.js",
37
37
  "types": "./dist/ws/index.d.ts"
38
+ },
39
+ "./sse": {
40
+ "import": "./dist/sse.es.js",
41
+ "types": "./dist/sse/index.d.ts"
38
42
  }
39
43
  },
40
44
  "sideEffects": false,
package/readme.md CHANGED
@@ -3,10 +3,10 @@
3
3
  </p>
4
4
 
5
5
  <h1 align="center">tina4-js</h1>
6
- <h3 align="center">This Is Now A 4Framework</h3>
6
+ <h3 align="center">The Intelligent Native Application 4ramework</h3>
7
7
 
8
8
  <p align="center">
9
- Sub-3KB reactive frontend. Signals. Web Components. Zero dependencies.
9
+ Sub-3KB core, reactive frontend. Signals. Web Components. Zero dependencies.
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -311,4 +311,4 @@ npm run dev # dev server with HMR
311
311
 
312
312
  MIT
313
313
 
314
- *tina4-js — This is not a framework. [tina4.com](https://tina4.com)*
314
+ *tina4-js — The Intelligent Native Application 4ramework. [tina4.com](https://tina4.com)*