tina4ruby 3.13.107 → 3.13.108

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab8e75446e8ed41f28be0078b1f16de1e1c0be39ff83cefea98f2a5cc9a78cc2
4
- data.tar.gz: b8d786867f6a37c87bbc0e242a615c7df8962ae8fd188d8bb8c381061ecba319
3
+ metadata.gz: c940eabf57b7ba299db01f414b5d24d949af99cf970d49fa677e69f77b9192c9
4
+ data.tar.gz: 83fb893d4db991b8393f22e727d3b7b9e220553f9118eb8a8a52a176dbea390b
5
5
  SHA512:
6
- metadata.gz: 7badcea95d972dfbc0171ed400722626d7d6565ff1b9328b1183dcedebe591c2c4fb2510fa67f8851614c4cd866c5b4db021351fdeea1bd016ce639ea1cb185f
7
- data.tar.gz: b22a17a9907c81b37f1c22cb2d40e63c77eb62802f9db33ae749a747aa3d5eac5427523b2d5cec0a7ebda2b5d3c62ba88219fd906f0fab62da748a076f4b01e2
6
+ metadata.gz: 7a04a51dbdd80fb05f7213c3983a03e5bf351eebf8ad5ef197e8e80c6489e47ace86a0417131cc2d3a8bbca6554c42dc96016f66c098cc103a5c4f411980137f
7
+ data.tar.gz: f4e952bb42149b90c62b4bba12695c02234fdf11c19f30bb0744031190144bc0b02c13c7d55916ac1bc9c18f130e66a504e071383ac221413a3b7980e2d1f5ac
data/README.md CHANGED
@@ -4,11 +4,10 @@
4
4
  <h1 align="center">Tina4 Ruby</h1>
5
5
  <h3 align="center">TINA4: The Intelligent Native Application 4ramework</h3>
6
6
  <p align="center"><em>Simple. Fast. Human. &nbsp;|&nbsp; Built for AI. Built for you.</em></p>
7
- <p align="center">98 built-in features. Zero runtime dependencies. One require, everything works.</p>
7
+ <p align="center">Zero runtime dependencies. One require, everything works.</p>
8
8
  <p align="center">
9
9
  <a href="https://rubygems.org/gems/tina4ruby"><img src="https://img.shields.io/gem/v/tina4ruby?color=7b1fa2&label=RubyGems" alt="RubyGems"></a>
10
- <img src="https://img.shields.io/badge/tests-2%2C508%20passing-brightgreen" alt="Tests">
11
- <img src="https://img.shields.io/badge/features-98-blue" alt="Features">
10
+ <a href="https://github.com/tina4stack/tina4-ruby/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/tina4stack/tina4-ruby/test.yml?label=tests" alt="Tests"></a>
12
11
  <img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="Zero Deps">
13
12
  <a href="https://tina4.com"><img src="https://img.shields.io/badge/docs-tina4.com-7b1fa2" alt="Docs"></a>
14
13
  </p>
@@ -262,12 +262,6 @@ module Tina4
262
262
  CONNECT_TIMEOUT_VAR = "TINA4_DATABASE_CONNECT_TIMEOUT"
263
263
  DEFAULT_CONNECT_TIMEOUT_SECONDS = 10
264
264
 
265
- # Clock slack when deciding whether a failed connect was OUR bound expiring.
266
- # A native bound of 10s is measured back as 9.998s often enough to matter,
267
- # and without the slack the contract error would degrade into the raw driver
268
- # error at random.
269
- CONNECT_TIMEOUT_SLACK_SECONDS = 0.25
270
-
271
265
  # Seconds to bound a connect by, or nil when the operator disabled the bound.
272
266
  def self.connect_timeout_seconds
273
267
  seconds = Tina4::Env.float(CONNECT_TIMEOUT_VAR, default: DEFAULT_CONNECT_TIMEOUT_SECONDS)
@@ -275,12 +269,20 @@ module Tina4
275
269
  end
276
270
 
277
271
  # Whole seconds for the native options that accept only an integer (libpq,
278
- # libmysqlclient, FreeTDS). Rounds UP and never below 1: libpq reads
279
- # connect_timeout=0 as "wait forever", so rounding 0.4 DOWN to 0 would
280
- # silently disable the very bound being set.
272
+ # libmysqlclient, FreeTDS). STRICTLY greater than the bound, never below 1.
273
+ #
274
+ # floor(s) + 1, not ceil(s): the native option must land AFTER our bound so
275
+ # the driver's own timer fires first and bounding_connect gets to translate
276
+ # its failure. ceil(N) == N for a whole-second bound - and the shipped
277
+ # default of 10 is whole - which put the driver's deadline ON our bound
278
+ # instead of after it, so which message reached the caller came down to which
279
+ # clock ticked first. floor(s) + 1 is strictly greater for every input,
280
+ # whole or fractional, at a cost of at most one extra second on a path that
281
+ # has already failed. (libpq also reads connect_timeout=0 as "wait forever",
282
+ # so the +1 doubles as the guard against rounding a sub-second bound to 0.)
281
283
  def self.connect_timeout_whole_seconds
282
284
  seconds = connect_timeout_seconds
283
- seconds && [seconds.ceil, 1].max
285
+ seconds && [seconds.floor + 1, 1].max
284
286
  end
285
287
 
286
288
  # The one error a timed-out connect raises: it names the host, the port, the
@@ -294,6 +296,27 @@ module Tina4
294
296
  "indefinitely).#{detail}"
295
297
  end
296
298
 
299
+ # Did a connect that FAILED take at least the configured bound?
300
+ #
301
+ # Two readings, because the framework and the driver do not share a clock.
302
+ # bounding_connect times on CLOCK_MONOTONIC; libpq times its own
303
+ # connect_timeout on gettimeofday - CLOCK_REALTIME (libmysqlclient and
304
+ # FreeTDS likewise measure against the wall clock). NTP slews and steps
305
+ # realtime and never touches monotonic, so a forward step or slew can make
306
+ # the driver abort while a monotonic reading over the very same connect is
307
+ # still short of the bound - and then the driver's own message, which names
308
+ # no tunable, reaches the caller.
309
+ #
310
+ # Taking the LARGER of the two readings covers both directions: the realtime
311
+ # reading catches a forward jump, and keeping the monotonic reading means a
312
+ # BACKWARD jump cannot hide a timeout that really did happen. Pure, so the
313
+ # decision is testable without faking a clock.
314
+ def self.bound_reached?(elapsed_monotonic, elapsed_realtime, seconds)
315
+ return false if seconds.nil?
316
+
317
+ [elapsed_monotonic, elapsed_realtime].max >= seconds
318
+ end
319
+
297
320
  # Run a driver's natively-bounded connect and translate an expiry into the
298
321
  # contract error above. The NATIVE option does the bounding; this only names
299
322
  # it. Whether the bound expired is decided by ELAPSED TIME, not by matching
@@ -301,17 +324,26 @@ module Tina4
301
324
  # ("timeout expired", "waiting for initial communication packet", "TDS
302
325
  # server connection timed out", "Connection timed out"), and a marker table
303
326
  # is one more thing to drift and MISS. A missed timeout is the whole defect.
327
+ #
328
+ # The elapsed time is read on BOTH clocks and compared through
329
+ # bound_reached?, because the driver measured its own deadline on the wall
330
+ # clock while we started ours on the monotonic one - see that method. The
331
+ # strictly-greater native option (connect_timeout_whole_seconds) leaves the
332
+ # driver's deadline after ours, so on an undisturbed clock the larger reading
333
+ # comfortably reaches the bound with no slack to tune.
304
334
  def self.bounding_connect(host, port)
305
- started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
335
+ started_monotonic = Process.clock_gettime(Process::CLOCK_MONOTONIC)
336
+ started_realtime = Process.clock_gettime(Process::CLOCK_REALTIME)
306
337
  yield
307
338
  rescue StandardError => error
308
339
  bound = connect_timeout_seconds
309
340
  raise if bound.nil?
310
341
 
311
- elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
312
- raise if elapsed < bound - CONNECT_TIMEOUT_SLACK_SECONDS
342
+ elapsed_monotonic = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_monotonic
343
+ elapsed_realtime = Process.clock_gettime(Process::CLOCK_REALTIME) - started_realtime
344
+ raise unless bound_reached?(elapsed_monotonic, elapsed_realtime, bound)
313
345
 
314
- connect_timed_out!(host, port, elapsed, error)
346
+ connect_timed_out!(host, port, [elapsed_monotonic, elapsed_realtime].max, error)
315
347
  end
316
348
 
317
349
  # Did this driver actually OVERRIDE the contract method, or is it inheriting
@@ -1,4 +1,4 @@
1
- "use strict";var Tina4=(()=>{var X=Object.defineProperty;var Be=Object.getOwnPropertyDescriptor;var Ve=Object.getOwnPropertyNames;var Ge=Object.prototype.hasOwnProperty;var Ze=(e,n)=>{for(var t in n)X(e,t,{get:n[t],enumerable:!0})},Qe=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of Ve(n))!Ge.call(e,o)&&o!==t&&X(e,o,{get:()=>n[o],enumerable:!(r=Be(n,o))||r.enumerable});return e};var Xe=e=>Qe(X({},"__esModule",{value:!0}),e);var Et={};Ze(Et,{Tina4Element:()=>P,api:()=>_e,batch:()=>V,clearPersistedKeys:()=>je,computed:()=>me,createI18n:()=>le,effect:()=>R,html:()=>ve,i18n:()=>We,isSignal:()=>I,navigate:()=>G,persist:()=>Ue,pwa:()=>Me,route:()=>Te,router:()=>Ce,rtc:()=>Ie,rtcConfig:()=>Z,signal:()=>k,sse:()=>Oe,ws:()=>W});var L=null,q=null,$=null,J=null;function O(e){J=e}function B(){return J}var fe=null,ge=null,pe=[],Ye=512;var z=0,Y=new Set;function k(e,n){let t=e,r=new Set,o={_t4:!0,get value(){if(L&&(r.add(L),q)){let s=L;q.push(()=>r.delete(s))}return t},set value(s){if(Object.is(s,t))return;let i=t;if(t=s,o._debugInfo&&o._debugInfo.updateCount++,ge&&ge(o,i,s),z>0)for(let l of r)Y.add(l);else{let l;for(let d of[...r])try{d()}catch(f){l===void 0&&(l=f)}if(l!==void 0)throw l}},_subscribe(s){return r.add(s),()=>{r.delete(s)}},peek(){return t}};return fe?(o._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:r},fe(o,n)):pe.length<Ye&&pe.push({ref:new WeakRef(o),label:n,createdAt:Date.now(),subs:r}),o}function me(e){let n=k(void 0);return R(()=>{n.value=e()}),{_t4:!0,get value(){return n.value},set value(t){throw new Error("[tina4] computed signals are read-only")},_subscribe(t){return n._subscribe(t)},peek(){return n.peek()}}}function R(e){let n=!1,t=[],r=[],o=()=>{for(let l of r)l();r=[]},s=()=>{if(n)return;for(let h of t)h();t=[],o();let l=L,d=q,f=$;L=s,q=t,$=r;try{e()}finally{L=l,q=d,$=f}};s();let i=()=>{n=!0;for(let l of t)l();t=[],o()};return $&&$.push(i),J&&J.push(i),i}function V(e){z++;try{e()}finally{if(z--,z===0){let n=[...Y];Y.clear();let t;for(let r of n)try{r()}catch(o){t===void 0&&(t=o)}if(t!==void 0)throw t}}}function I(e){return e!==null&&typeof e=="object"&&e._t4===!0}var he=new WeakMap,ee="t4:";function ve(e,...n){let t=he.get(e);if(!t){t=document.createElement("template");let i="";for(let l=0;l<e.length;l++)i+=e[l],l<n.length&&(ot(i)?i+=`__t4_${l}__`:i+=`<!--${ee}${l}-->`);t.innerHTML=i,he.set(e,t)}let r=t.content.cloneNode(!0),o=et(r);for(let{marker:i,index:l}of o)nt(i,n[l]);let s=tt(r);for(let i of s)rt(i,n);return r}function et(e){let n=[];return ne(e,t=>{if(t.nodeType===8){let r=t.data;if(r&&r.startsWith(ee)){let o=parseInt(r.slice(ee.length),10);n.push({marker:t,index:o})}}}),n}function tt(e){let n=[];return ne(e,t=>{t.nodeType===1&&n.push(t)}),n}function ne(e,n){let t=e.childNodes;for(let r=0;r<t.length;r++){let o=t[r];n(o),ne(o,n)}}function nt(e,n){let t=e.parentNode;if(t)if(I(n)){let r=document.createTextNode("");t.replaceChild(r,e),R(()=>{r.data=String(n.value??"")})}else if(typeof n=="function"){let r=document.createComment("");t.replaceChild(r,e);let o=[],s=[];R(()=>{for(let w of s)w();s=[];let i=[],l=B();O(i);let d=n();O(l),s=i;for(let w of o)w.parentNode?.removeChild(w);o=[];let f=te(d),h=r.parentNode;if(h)for(let w of f)h.insertBefore(w,r),o.push(w)})}else if(ye(n))t.replaceChild(n,e);else if(n instanceof Node)t.replaceChild(n,e);else if(Array.isArray(n)){let r=document.createDocumentFragment();for(let o of n){let s=te(o);for(let i of s)r.appendChild(i)}t.replaceChild(r,e)}else{let r=document.createTextNode(String(n??""));t.replaceChild(r,e)}}function rt(e,n){let t=[];for(let r of Array.from(e.attributes)){let o=r.name,s=r.value;if(o.startsWith("@")){let l=o.slice(1),d=s.match(/__t4_(\d+)__/);if(d){let f=n[parseInt(d[1],10)];typeof f=="function"&&e.addEventListener(l,h=>V(()=>f(h)))}t.push(o);continue}if(o.startsWith("?")){let l=o.slice(1),d=s.match(/__t4_(\d+)__/);if(d){let f=n[parseInt(d[1],10)];if(I(f)){let h=f;R(()=>{h.value?e.setAttribute(l,""):e.removeAttribute(l)})}else typeof f=="function"?R(()=>{f()?e.setAttribute(l,""):e.removeAttribute(l)}):f&&e.setAttribute(l,"")}t.push(o);continue}if(o.startsWith(".")){let l=o.slice(1),d=s.match(/__t4_(\d+)__/);if(d){let f=n[parseInt(d[1],10)];I(f)?R(()=>{e[l]=f.value}):typeof f=="function"?R(()=>{e[l]=f()??""}):e[l]=f}t.push(o);continue}let i=s.match(/__t4_(\d+)__/);if(i){let l=n[parseInt(i[1],10)];if(I(l)){let d=l;R(()=>{e.setAttribute(o,String(d.value??""))})}else typeof l=="function"?R(()=>{e.setAttribute(o,String(l()??""))}):e.setAttribute(o,String(l??""))}}for(let r of t)e.removeAttribute(r)}function te(e){if(e==null||e===!1)return[];if(ye(e))return Array.from(e.childNodes);if(e instanceof Node)return[e];if(Array.isArray(e)){let n=[];for(let t of e)n.push(...te(t));return n}return[document.createTextNode(String(e))]}function ye(e){return e!=null&&typeof e=="object"&&e.nodeType===11}function ot(e){let n=!1,t=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="<"&&!n&&!t&&(r=!0),s===">"&&!n&&!t&&(r=!1),r&&(s==='"'&&!n&&(t=!t),s==="'"&&!t&&(n=!n))}return r}var be=null,Se=null;var P=class extends HTMLElement{constructor(){super();this._props={};this._rendered=!1;this._disposeRender=null;this._innerDisposers=[];let t=this.constructor;this._root=t.shadow?this.attachShadow({mode:"open"}):this;for(let[r,o]of Object.entries(t.props))this._props[r]=k(this._coerce(this.getAttribute(r),o))}static get observedAttributes(){return Object.keys(this.props)}connectedCallback(){if(this._rendered)return;this._rendered=!0;let t=this.constructor,r=null;if(t.styles&&t.shadow&&this._root instanceof ShadowRoot){let o=document.createElement("style");o.textContent=t.styles,this._root.appendChild(o),r=o}this._disposeRender=R(()=>{this._innerDisposers.splice(0).forEach(d=>d());let o=[],s=B();O(o);let i=this.render();O(s),this._innerDisposers=o;let l=Array.from(this._root.childNodes);for(let d of l)d!==r&&this._root.removeChild(d);i&&this._root.appendChild(i)}),this.onMount(),be&&be(this)}disconnectedCallback(){this._disposeRender&&(this._disposeRender(),this._disposeRender=null),this._innerDisposers.splice(0).forEach(t=>t()),this.onUnmount(),Se&&Se(this)}attributeChangedCallback(t,r,o){let i=this.constructor.props[t];i&&this._props[t]&&(this._props[t].value=this._coerce(o,i))}prop(t){if(!this._props[t])throw new Error(`[tina4] Prop '${t}' not declared in static props of <${this.tagName.toLowerCase()}>`);return this._props[t]}emit(t,r){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,...r}))}onMount(){}onUnmount(){}_coerce(t,r){return r===Boolean?t!==null:r===Number?t!==null?Number(t):0:t??""}};P.props={},P.styles="",P.shadow=!0;var oe=[],D=null,U="history",st=!1,j=[],re=[],ke=0;function Te(e,n){let t=[],r;e==="*"?r=".*":r=e.replace(/\{(\w+)\}/g,(s,i)=>(t.push(i),"([^/]+)"));let o=new RegExp(`^${r}$`);typeof n=="function"?oe.push({pattern:e,regex:o,paramNames:t,handler:n}):oe.push({pattern:e,regex:o,paramNames:t,handler:n.handler,guard:n.guard})}function G(e,n){if(U==="hash")if(n?.replace){let t=new URL(location.href);t.hash="#"+e,history.replaceState(null,"",t.toString()),H()}else location.hash="#"+e;else n?.replace?history.replaceState(null,"",e):history.pushState(null,"",e),H()}function H(){if(!D)return;let e=performance.now(),n=++ke,t=U==="hash"?location.hash.slice(1)||"/":location.pathname;for(let r of oe){let o=t.match(r.regex);if(!o)continue;let s={};if(r.paramNames.forEach((d,f)=>{s[d]=decodeURIComponent(o[f+1])}),r.guard){let d=r.guard();if(d===!1)return;if(typeof d=="string"){G(d,{replace:!0});return}}re.splice(0).forEach(d=>d()),D.innerHTML="";let i=[];O(i);let l=r.handler(s);if(l instanceof Promise)l.then(d=>{if(O(null),n!==ke){for(let h of i)h();return}we(D,d),re=i;let f=performance.now()-e;for(let h of j)h({path:t,params:s,pattern:r.pattern,durationMs:f})});else{O(null),we(D,l),re=i;let d=performance.now()-e;for(let f of j)f({path:t,params:s,pattern:r.pattern,durationMs:d})}return}}function we(e,n){n instanceof DocumentFragment||n instanceof Node?e.replaceChildren(n):typeof n=="string"?e.innerHTML=n:n!=null&&e.replaceChildren(document.createTextNode(String(n)))}var Ce={start(e){if(D=document.querySelector(e.target),!D)throw new Error(`[tina4] Router target '${e.target}' not found in DOM`);U=e.mode??"history",st=!0,window.addEventListener("popstate",H),U==="hash"&&window.addEventListener("hashchange",H),document.addEventListener("click",n=>{if(n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)return;let t=n.target.closest("a[href]");if(!t||t.origin!==location.origin||t.hasAttribute("target")||t.hasAttribute("download")||t.getAttribute("rel")?.includes("external"))return;n.preventDefault();let r=U==="hash"?t.getAttribute("href"):t.pathname;G(r)}),H()},on(e,n){return j.push(n),()=>{let t=j.indexOf(n);t>=0&&j.splice(t,1)}}};var x={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},se=[],ie=[],it=0;function ae(){try{return localStorage.getItem(x.tokenKey)}catch{return null}}function at(e){try{localStorage.setItem(x.tokenKey,e)}catch{}}function Ee(e,n){let t=Object.entries(n).map(([r,o])=>`${encodeURIComponent(r)}=${encodeURIComponent(String(o))}`).join("&");return e+(e.includes("?")?"&":"?")+t}async function Re(e,n){e._url=n,e._requestId=++it;for(let l of se){let d=l(e);d&&(e=d)}let t=await fetch(n,e),r=t.headers.get("FreshToken");r&&at(r);let o=t.headers.get("Content-Type")??"",s;o.includes("json")?s=await t.json():s=await t.text();let i={status:t.status,data:s,ok:t.ok,headers:t.headers,_requestId:e._requestId};for(let l of ie){let d=l(i);d&&(i=d)}if(!t.ok)throw i;return i.data}async function F(e,n,t,r){let o={method:e,credentials:"same-origin",headers:{"Content-Type":"application/json",...x.headers}};if(x.auth){let s=ae();s&&(o.headers.Authorization=`Bearer ${s}`)}if(t!==void 0&&e!=="GET"){let s=typeof t=="object"&&t!==null?{...t}:t;if(x.auth&&typeof s=="object"&&s!==null){let i=ae();i&&(s.formToken=i)}o.body=JSON.stringify(s)}return r?.headers&&Object.assign(o.headers,r.headers),r?.params&&(n=Ee(n,r.params)),Re(o,x.baseUrl+n)}var _e={configure(e){Object.assign(x,e)},get(e,n){return F("GET",e,void 0,n)},post(e,n,t){return F("POST",e,n,t)},put(e,n,t){return F("PUT",e,n,t)},patch(e,n,t){return F("PATCH",e,n,t)},delete(e,n){return F("DELETE",e,void 0,n)},async graphql(e,n,t,r){return F("POST",e,{query:n,variables:t||{}},r)},async upload(e,n,t){let r={method:"POST",headers:{...x.headers},body:n};if(delete r.headers["Content-Type"],delete r.headers["content-type"],x.auth){let o=ae();o&&(r.headers.Authorization=`Bearer ${o}`)}return t?.headers&&Object.assign(r.headers,t.headers),t?.params&&(e=Ee(e,t.params)),Re(r,x.baseUrl+e)},intercept(e,n){e==="request"?se.push(n):ie.push(n)},_reset(){x.baseUrl="",x.auth=!1,x.tokenKey="tina4_token",x.headers={},se.length=0,ie.length=0}};function lt(e){let n=e.cacheStrategy??"network-first",t=JSON.stringify(e.precache??[]),r=e.offlineRoute?`'${e.offlineRoute}'`:"null";return`
1
+ "use strict";var Tina4=(()=>{var ee=Object.defineProperty;var Xe=Object.getOwnPropertyDescriptor;var Ye=Object.getOwnPropertyNames;var et=Object.prototype.hasOwnProperty;var tt=(e,n)=>{for(var t in n)ee(e,t,{get:n[t],enumerable:!0})},nt=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of Ye(n))!et.call(e,o)&&o!==t&&ee(e,o,{get:()=>n[o],enumerable:!(r=Xe(n,o))||r.enumerable});return e};var rt=e=>nt(ee({},"__esModule",{value:!0}),e);var $t={};tt($t,{Tina4Element:()=>N,api:()=>Ae,batch:()=>G,clearPersistedKeys:()=>Ve,computed:()=>ye,createI18n:()=>de,effect:()=>E,html:()=>be,i18n:()=>Je,isSignal:()=>I,navigate:()=>Q,persist:()=>ze,pwa:()=>Ne,route:()=>_e,router:()=>xe,rtc:()=>De,rtcConfig:()=>X,signal:()=>w,sse:()=>Pe,ws:()=>K});var L=null,H=null,U=null,B=null;function O(e){B=e}function J(){return B}var me=null,he=null,ve=[],ot=512;var V=0,te=new Set;function w(e,n){let t=e,r=new Set,o={_t4:!0,get value(){if(L&&(r.add(L),H)){let s=L;H.push(()=>r.delete(s))}return t},set value(s){if(Object.is(s,t))return;let i=t;if(t=s,o._debugInfo&&o._debugInfo.updateCount++,he&&he(o,i,s),V>0)for(let c of r)te.add(c);else{let c;for(let a of[...r])try{a()}catch(l){c===void 0&&(c=l)}if(c!==void 0)throw c}},_subscribe(s){return r.add(s),()=>{r.delete(s)}},peek(){return t}};return me?(o._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:r},me(o,n)):ve.length<ot&&ve.push({ref:new WeakRef(o),label:n,createdAt:Date.now(),subs:r}),o}function ye(e){let n=w(void 0);return E(()=>{n.value=e()}),{_t4:!0,get value(){return n.value},set value(t){throw new Error("[tina4] computed signals are read-only")},_subscribe(t){return n._subscribe(t)},peek(){return n.peek()}}}function E(e){let n=!1,t=[],r=[],o=()=>{for(let c of r)c();r=[]},s=()=>{if(n)return;for(let g of t)g();t=[],o();let c=L,a=H,l=U;L=s,H=t,U=r;try{e()}finally{L=c,H=a,U=l}};s();let i=()=>{n=!0;for(let c of t)c();t=[],o()};return U&&U.push(i),B&&B.push(i),i}function G(e){V++;try{e()}finally{if(V--,V===0){let n=[...te];te.clear();let t;for(let r of n)try{r()}catch(o){t===void 0&&(t=o)}if(t!==void 0)throw t}}}function I(e){return e!==null&&typeof e=="object"&&e._t4===!0}var Se=new WeakMap,ne="t4:";function be(e,...n){let t=Se.get(e);if(!t){let i=document.createElement("template"),c=new Map,a="";for(let l=0;l<e.length;l++)if(a+=e[l],l<n.length)if(dt(a)){let m=ut(e[l]);m&&c.set(l,m),a+=`__t4_${l}__`}else a+=`<!--${ne}${l}-->`;i.innerHTML=a,t={template:i,propertyNames:c},Se.set(e,t)}let r=t.template.content.cloneNode(!0),o=st(r);for(let{marker:i,index:c}of o)at(i,n[c]);let s=it(r);for(let i of s)ct(i,n,t.propertyNames);return r}function st(e){let n=[];return se(e,t=>{if(t.nodeType===8){let r=t.data;if(r&&r.startsWith(ne)){let o=parseInt(r.slice(ne.length),10);n.push({marker:t,index:o})}}}),n}function it(e){let n=[];return se(e,t=>{t.nodeType===1&&n.push(t)}),n}function se(e,n){let t=e.childNodes;for(let r=0;r<t.length;r++){let o=t[r];n(o),se(o,n)}}function at(e,n){let t=e.parentNode;if(t)if(I(n)){let r=document.createTextNode("");t.replaceChild(r,e),E(()=>{r.data=String(n.value??"")})}else if(typeof n=="function"){let r=document.createComment("");t.replaceChild(r,e);let o=[],s=[];E(()=>{for(let u of s)u();s=[];let i=[],c=J();O(i);let a=n();O(c),s=i;for(let u of o)u.parentNode?.removeChild(u);o=[];let l=oe(a),g=r.parentNode;if(!g)return;let m=Z(g);for(let u of l){let d=m?D(u,m):u;g.insertBefore(d,r),o.push(d)}})}else if(Te(n)){let r=Z(t);if(r){let o=document.createDocumentFragment();for(let s of Array.from(n.childNodes))o.appendChild(D(s,r));t.replaceChild(o,e)}else t.replaceChild(n,e)}else if(n instanceof Node){let r=Z(t);t.replaceChild(r?D(n,r):n,e)}else if(Array.isArray(n)){let r=Z(t),o=document.createDocumentFragment();for(let s of n){let i=oe(s);for(let c of i)o.appendChild(r?D(c,r):c)}t.replaceChild(o,e)}else{let r=document.createTextNode(String(n??""));t.replaceChild(r,e)}}function ct(e,n,t){let r=[];for(let o of Array.from(e.attributes)){let s=o.name,i=o.value;if(s.startsWith("@")){let a=s.slice(1),l=i.match(/__t4_(\d+)__/);if(l){let g=n[parseInt(l[1],10)];typeof g=="function"&&e.addEventListener(a,m=>G(()=>g(m)))}r.push(s);continue}if(s.startsWith("?")){let a=s.slice(1),l=i.match(/__t4_(\d+)__/);if(l){let g=n[parseInt(l[1],10)];if(I(g)){let m=g;E(()=>{m.value?e.setAttribute(a,""):e.removeAttribute(a)})}else typeof g=="function"?E(()=>{g()?e.setAttribute(a,""):e.removeAttribute(a)}):g&&e.setAttribute(a,"")}r.push(s);continue}if(s.startsWith(".")){let a=i.match(/__t4_(\d+)__/);if(a){let l=parseInt(a[1],10),g=t.get(l)??s.slice(1),m=n[l];I(m)?E(()=>{e[g]=m.value}):typeof m=="function"?E(()=>{e[g]=m()??""}):e[g]=m}r.push(s);continue}let c=i.match(/__t4_(\d+)__/);if(c){let a=n[parseInt(c[1],10)];if(I(a)){let l=a;E(()=>{e.setAttribute(s,String(l.value??""))})}else typeof a=="function"?E(()=>{e.setAttribute(s,String(a()??""))}):e.setAttribute(s,String(a??""))}}for(let o of r)e.removeAttribute(o)}var re="http://www.w3.org/2000/svg",lt="http://www.w3.org/1998/Math/MathML",we="http://www.w3.org/1999/xhtml";function Z(e){let n=e;for(;n&&n.nodeType===1;){let t=n,r=t.namespaceURI;if(r===re&&t.localName==="foreignObject")return null;if(r===re||r===lt)return r;if(r===we)return null;n=n.parentNode}return null}function D(e,n){if(e.nodeType!==1)return e;let t=e;if(t.namespaceURI===n){for(let s of Array.from(t.childNodes)){let i=D(s,n);i!==s&&t.replaceChild(i,s)}return t}let r=document.createElementNS(n,t.localName);for(let s of Array.from(t.attributes))r.setAttribute(s.name,s.value);let o=n===re&&t.localName==="foreignObject"?we:n;for(let s of Array.from(t.childNodes))r.appendChild(D(s,o));return r}function ut(e){return e.match(/\.([^\s"'<>/=]+)\s*=\s*["']?$/)?.[1]}function oe(e){if(e==null||e===!1)return[];if(Te(e))return Array.from(e.childNodes);if(e instanceof Node)return[e];if(Array.isArray(e)){let n=[];for(let t of e)n.push(...oe(t));return n}return[document.createTextNode(String(e))]}function Te(e){return e!=null&&typeof e=="object"&&e.nodeType===11}function dt(e){let n=!1,t=!1,r=!1;for(let o=0;o<e.length;o++){if(!r&&e.startsWith("<!--",o)){let i=e.indexOf("-->",o+4);if(i===-1)return!1;o=i+2;continue}let s=e[o];s==="<"&&!n&&!t&&(r=!0),s===">"&&!n&&!t&&(r=!1),r&&(s==='"'&&!n&&(t=!t),s==="'"&&!t&&(n=!n))}return r}var ke=null,Ce=null;var N=class extends HTMLElement{constructor(){super();this._props={};this._rendered=!1;this._disposeRender=null;this._innerDisposers=[];let t=this.constructor;this._root=t.shadow?this.attachShadow({mode:"open"}):this;for(let[r,o]of Object.entries(t.props))this._props[r]=w(this._coerce(this.getAttribute(r),o))}static get observedAttributes(){return Object.keys(this.props)}connectedCallback(){if(this._rendered)return;this._rendered=!0;let t=this.constructor,r=null;if(t.styles&&t.shadow&&this._root instanceof ShadowRoot){let o=document.createElement("style");o.textContent=t.styles,this._root.appendChild(o),r=o}this._disposeRender=E(()=>{this._innerDisposers.splice(0).forEach(a=>a());let o=[],s=J();O(o);let i=this.render();O(s),this._innerDisposers=o;let c=Array.from(this._root.childNodes);for(let a of c)a!==r&&this._root.removeChild(a);i&&this._root.appendChild(i)}),this.onMount(),ke&&ke(this)}disconnectedCallback(){this._disposeRender&&(this._disposeRender(),this._disposeRender=null),this._innerDisposers.splice(0).forEach(t=>t()),this.onUnmount(),Ce&&Ce(this)}attributeChangedCallback(t,r,o){let i=this.constructor.props[t];i&&this._props[t]&&(this._props[t].value=this._coerce(o,i))}prop(t){if(!this._props[t])throw new Error(`[tina4] Prop '${t}' not declared in static props of <${this.tagName.toLowerCase()}>`);return this._props[t]}emit(t,r){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,...r}))}onMount(){}onUnmount(){}_coerce(t,r){return r===Boolean?t!==null:r===Number?t!==null?Number(t):0:t??""}};N.props={},N.styles="",N.shadow=!0;var ae=[],F=null,j="history",ft=!1,W=[],ie=[],Ee=0;function _e(e,n){let t=[],r;e==="*"?r=".*":r=e.replace(/\{(\w+)\}/g,(s,i)=>(t.push(i),"([^/]+)"));let o=new RegExp(`^${r}$`);typeof n=="function"?ae.push({pattern:e,regex:o,paramNames:t,handler:n}):ae.push({pattern:e,regex:o,paramNames:t,handler:n.handler,guard:n.guard})}function Q(e,n){if(j==="hash")if(n?.replace){let t=new URL(location.href);t.hash="#"+e,history.replaceState(null,"",t.toString()),$()}else{let t=new URL(location.href);t.hash="#"+e,history.pushState(null,"",t.toString()),$()}else n?.replace?history.replaceState(null,"",e):history.pushState(null,"",e),$()}function $(){if(!F)return;let e=performance.now(),n=++Ee,t=j==="hash"?location.hash.slice(1)||"/":location.pathname;for(let r of ae){let o=t.match(r.regex);if(!o)continue;let s={};if(r.paramNames.forEach((a,l)=>{s[a]=decodeURIComponent(o[l+1])}),r.guard){let a=r.guard();if(a===!1)return;if(typeof a=="string"){Q(a,{replace:!0});return}}ie.splice(0).forEach(a=>a()),F.innerHTML="";let i=[];O(i);let c=r.handler(s);if(c instanceof Promise)c.then(a=>{if(O(null),n!==Ee){for(let g of i)g();return}Re(F,a),ie=i;let l=performance.now()-e;for(let g of W)g({path:t,params:s,pattern:r.pattern,durationMs:l})});else{O(null),Re(F,c),ie=i;let a=performance.now()-e;for(let l of W)l({path:t,params:s,pattern:r.pattern,durationMs:a})}return}}function Re(e,n){n instanceof DocumentFragment||n instanceof Node?e.replaceChildren(n):typeof n=="string"?e.innerHTML=n:n!=null&&e.replaceChildren(document.createTextNode(String(n)))}var xe={start(e){if(F=document.querySelector(e.target),!F)throw new Error(`[tina4] Router target '${e.target}' not found in DOM`);j=e.mode??"history",ft=!0,window.addEventListener("popstate",$),j==="hash"&&window.addEventListener("hashchange",$),document.addEventListener("click",n=>{if(n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)return;let t=n.target.closest("a[href]");if(!t||t.origin!==location.origin||t.hasAttribute("target")||t.hasAttribute("download")||t.getAttribute("rel")?.includes("external"))return;n.preventDefault();let r=j==="hash"?t.getAttribute("href"):t.pathname;Q(r)}),$()},on(e,n){return W.push(n),()=>{let t=W.indexOf(n);t>=0&&W.splice(t,1)}}};var _={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},ce=[],le=[],gt=0;function ue(){try{return localStorage.getItem(_.tokenKey)}catch{return null}}function pt(e){try{localStorage.setItem(_.tokenKey,e)}catch{}}function Me(e,n){let t=Object.entries(n).map(([r,o])=>`${encodeURIComponent(r)}=${encodeURIComponent(String(o))}`).join("&");return e+(e.includes("?")?"&":"?")+t}async function Oe(e,n){e._url=n,e._requestId=++gt;for(let c of ce){let a=c(e);a&&(e=a)}let t=await fetch(n,e),r=t.headers.get("FreshToken");r&&pt(r);let o=t.headers.get("Content-Type")??"",s;o.includes("json")?s=await t.json():s=await t.text();let i={status:t.status,data:s,ok:t.ok,headers:t.headers,_requestId:e._requestId};for(let c of le){let a=c(i);a&&(i=a)}if(!t.ok)throw i;return i.data}async function q(e,n,t,r){let o={method:e,credentials:"same-origin",headers:{"Content-Type":"application/json",..._.headers}};if(_.auth){let s=ue();s&&(o.headers.Authorization=`Bearer ${s}`)}if(t!==void 0&&e!=="GET"){let s=typeof t=="object"&&t!==null?{...t}:t;if(_.auth&&typeof s=="object"&&s!==null){let i=ue();i&&(s.formToken=i)}o.body=JSON.stringify(s)}return r?.headers&&Object.assign(o.headers,r.headers),r?.params&&(n=Me(n,r.params)),Oe(o,_.baseUrl+n)}var Ae={configure(e){Object.assign(_,e)},get(e,n){return q("GET",e,void 0,n)},post(e,n,t){return q("POST",e,n,t)},put(e,n,t){return q("PUT",e,n,t)},patch(e,n,t){return q("PATCH",e,n,t)},delete(e,n){return q("DELETE",e,void 0,n)},async graphql(e,n,t,r){return q("POST",e,{query:n,variables:t||{}},r)},async upload(e,n,t){let r={method:"POST",headers:{..._.headers},body:n};if(delete r.headers["Content-Type"],delete r.headers["content-type"],_.auth){let o=ue();o&&(r.headers.Authorization=`Bearer ${o}`)}return t?.headers&&Object.assign(r.headers,t.headers),t?.params&&(e=Me(e,t.params)),Oe(r,_.baseUrl+e)},intercept(e,n){e==="request"?ce.push(n):le.push(n)},_reset(){_.baseUrl="",_.auth=!1,_.tokenKey="tina4_token",_.headers={},ce.length=0,le.length=0}};function mt(e){let n=e.cacheStrategy??"network-first",t=JSON.stringify(e.precache??[]),r=e.offlineRoute?`'${e.offlineRoute}'`:"null";return`
2
2
  const CACHE = 'tina4-v1';
3
3
  const PRECACHE = ${t};
4
4
  const OFFLINE = ${r};
@@ -44,5 +44,5 @@ self.addEventListener('fetch', (e) => {
44
44
  ))
45
45
  );`}
46
46
  });
47
- `.trim()}function xe(e){let n={name:e.name,short_name:e.shortName??e.name,start_url:"/",display:e.display??"standalone",background_color:e.backgroundColor??"#ffffff",theme_color:e.themeColor??"#000000"};return e.icon&&(n.icons=[{src:e.icon,sizes:"192x192",type:"image/png"},{src:e.icon,sizes:"512x512",type:"image/png"}]),n}var Me={register(e){let n=xe(e),t=new Blob([JSON.stringify(n)],{type:"application/json"}),r=document.createElement("link");r.rel="manifest",r.href=URL.createObjectURL(t),document.head.appendChild(r);let o=document.querySelector('meta[name="theme-color"]');o||(o=document.createElement("meta"),o.name="theme-color",document.head.appendChild(o)),o.content=e.themeColor??"#000000","serviceWorker"in navigator&&(e.swUrl?navigator.serviceWorker.register(e.swUrl).catch(s=>{console.warn("[tina4] Service worker registration failed:",s)}):navigator.serviceWorker.register("/sw.js").catch(()=>{console.info("[tina4] No service worker at /sw.js. Use pwa.generateServiceWorker() to create one, or pass swUrl in config.")}))},generateServiceWorker(e){return lt(e)},generateManifest(e){return xe(e)}};var ct={reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,protocols:[],token:""};function ut(e){let n=Array.isArray(e.protocols)?e.protocols:e.protocols?[e.protocols]:[];return e.token?["bearer",e.token,...n]:e.protocols}function dt(e,n={}){let t={...ct,...n},r=k("connecting"),o=k(!1),s=k(null),i=k(null),l=k(0),d={message:[],open:[],close:[],error:[]},f=null,h=!1,w=t.reconnectDelay,u=null,a=0;function c(m){if(typeof m!="string")return m;try{return JSON.parse(m)}catch{return m}}function g(){r.value=a>0?"reconnecting":"connecting";try{f=new WebSocket(e,ut(t))}catch{r.value="closed",o.value=!1;return}f.onopen=()=>{r.value="open",o.value=!0,i.value=null,a=0,w=t.reconnectDelay,l.value=0;for(let m of d.open)m()},f.onmessage=m=>{let T=c(m.data);s.value=T;for(let _ of d.message)_(T)},f.onclose=m=>{r.value="closed",o.value=!1;for(let T of d.close)T(m.code,m.reason);!h&&t.reconnect&&a<t.reconnectAttempts&&y()},f.onerror=m=>{i.value=m;for(let T of d.error)T(m)}}function y(){a++,l.value=a,r.value="reconnecting",u=setTimeout(()=>{u=null,g()},w),w=Math.min(w*2,t.reconnectMaxDelay)}let C={status:r,connected:o,lastMessage:s,error:i,reconnectCount:l,send(m){if(!f||f.readyState!==WebSocket.OPEN)throw new Error("[tina4] WebSocket is not connected");let T=typeof m=="string"?m:JSON.stringify(m);f.send(T)},on(m,T){return d[m].push(T),()=>{let _=d[m],A=_.indexOf(T);A>=0&&_.splice(A,1)}},pipe(m,T){let _=A=>{m.value=T(A,m.value)};return C.on("message",_)},close(m,T){h=!0,u&&(clearTimeout(u),u=null),f&&f.close(m??1e3,T??""),r.value="closed",o.value=!1}};return g(),C}var W={connect:dt};var ft={mode:"eventsource",method:"GET",headers:{},body:void 0,reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,events:[],json:!0};function gt(e,n={}){let t={...ft,...n},r=k("connecting"),o=k(!1),s=k(null),i=k(null),l=k(null),d=k(0),f={message:[],open:[],close:[],error:[]},h=null,w=null,u=!1,a=t.reconnectDelay,c=null,g=0;function y(p){if(!t.json||typeof p!="string")return p;try{return JSON.parse(p)}catch{return p}}function C(p,b){s.value=p,i.value=b;for(let E of f.message)E(p,b??void 0)}function m(){r.value="open",o.value=!0,l.value=null,g=0,a=t.reconnectDelay,d.value=0;for(let p of f.open)p()}function T(){r.value="closed",o.value=!1;for(let p of f.close)p();!u&&t.reconnect&&g<t.reconnectAttempts&&Q()}function _(p){l.value=p;for(let b of f.error)b(p)}function A(){r.value=g>0?"reconnecting":"connecting";try{h=new EventSource(e)}catch{r.value="closed",o.value=!1;return}h.onopen=()=>m(),h.onmessage=p=>{C(y(p.data),null)};for(let p of t.events)h.addEventListener(p,b=>{C(y(b.data),p)});h.onerror=p=>{_(p),h&&h.readyState===2&&(h=null,T())}}function K(){r.value=g>0?"reconnecting":"connecting",w=new AbortController;let p={method:t.method,headers:t.headers,signal:w.signal};t.body!==void 0&&(p.body=typeof t.body=="string"?t.body:JSON.stringify(t.body)),fetch(e,p).then(async b=>{if(!b.ok){_(new Error(`[tina4] SSE fetch ${b.status}`)),T();return}m();let E=b.body.getReader(),M=new TextDecoder,N="";for(;;){let{done:Ke,value:ze}=await E.read();if(Ke)break;N+=M.decode(ze,{stream:!0});let ue=N.split(`
48
- `);N=ue.pop();for(let Je of ue){let de=Je.trim();de&&C(y(de),null)}}let ce=N.trim();ce&&C(y(ce),null),w=null,T()}).catch(b=>{b.name!=="AbortError"&&(w=null,_(b),T())})}function Q(){g++,d.value=g,r.value="reconnecting",c=setTimeout(()=>{c=null,S()},a),a=Math.min(a*2,t.reconnectMaxDelay)}function S(){t.mode==="fetch"?K():A()}let v={status:r,connected:o,lastMessage:s,lastEvent:i,error:l,reconnectCount:d,on(p,b){return f[p].push(b),()=>{let E=f[p],M=E.indexOf(b);M>=0&&E.splice(M,1)}},pipe(p,b){let E=M=>{p.value=b(M,p.value)};return v.on("message",E)},close(){u=!0,c&&(clearTimeout(c),c=null),h&&(h.close(),h=null),w&&(w.abort(),w=null),r.value="closed",o.value=!1}};return S(),v}var Oe={connect:gt};async function Z(e="/api/rtc/config"){let n=await fetch(e);if(!n.ok)throw new Error(`[tina4] rtc config fetch failed: ${n.status}`);return n.json()}function pt(){let e=window.location;return`${e.protocol==="https:"?"wss:":"ws:"}//${e.host}`}function Ae(e){return/^wss?:\/\//.test(e)?e:pt()+(e.startsWith("/")?e:"/"+e)}function mt(){let e=globalThis.crypto;if(e&&"randomUUID"in e)return e.randomUUID().slice(0,8);let n="";for(let t=0;t<8;t++)n+=Math.floor(16*(.5+t)).toString(16);return n+Date.now().toString(16).slice(-4)}async function ht(e,n={}){let t=k("connecting"),r=k(null),o=k([]),s=k(!1),i=k(null),l=mt(),d=n.config??await Z(n.configUrl),f=n.iceServers??d.iceServers??[],h=n.signallingUrl??d.signalling??"/ws/rtc",w=Ae(h.includes("{room}")?h.replace("{room}",e):`${h}/${e}`),u=null;n.media instanceof MediaStream?u=n.media:n.media!==!1&&(u=await navigator.mediaDevices.getUserMedia(n.media??{audio:!0,video:!0})),r.value=u;let a=u?.getVideoTracks()[0]??null,c=new Map,g=W.connect(w);function y(){o.value=[...c.entries()].map(([S,v])=>({id:S,stream:v.stream}))}function C(S){try{g.send({...S,from:l})}catch{}}function m(S){let v=c.get(S);if(v)return v;let p=new RTCPeerConnection({iceServers:f}),b={pc:p,polite:l<S,makingOffer:!1,ignoreOffer:!1,stream:null};if(c.set(S,b),u)for(let E of u.getTracks())p.addTrack(E,u);return p.onnegotiationneeded=async()=>{try{b.makingOffer=!0,await p.setLocalDescription(),C({type:"desc",to:S,description:p.localDescription})}catch(E){i.value=E}finally{b.makingOffer=!1}},p.onicecandidate=({candidate:E})=>{E&&C({type:"ice",to:S,candidate:E})},p.ontrack=({streams:E})=>{b.stream=E[0]??null,y()},p.onconnectionstatechange=()=>{["failed","closed"].includes(p.connectionState)?T(S):p.connectionState==="connected"&&(t.value="connected")},y(),b}function T(S){let v=c.get(S);if(v){try{v.pc.close()}catch{}c.delete(S),y()}}async function _(S){let v=S,p=v.from;if(!p||p===l||v.to&&v.to!==l)return;if(v.type==="hello"){m(p),C({type:"welcome",to:p});return}if(v.type==="welcome"){m(p);return}if(v.type==="bye"){T(p);return}let b=m(p),E=b.pc;if(v.type==="desc"){let M=v.description,N=M.type==="offer"&&(b.makingOffer||E.signalingState!=="stable");if(b.ignoreOffer=!b.polite&&N,b.ignoreOffer)return;await E.setRemoteDescription(M),M.type==="offer"&&(await E.setLocalDescription(),C({type:"desc",to:p,description:E.localDescription}))}else if(v.type==="ice")try{await E.addIceCandidate(v.candidate)}catch(M){b.ignoreOffer||(i.value=M)}}g.on("message",S=>{_(S)}),g.on("open",()=>{C({type:"hello"})});async function A(S){if(S)for(let{pc:v}of c.values()){let p=v.getSenders().find(b=>b.track?.kind==="video");p&&await p.replaceTrack(S)}}async function K(){await A(a),s.value=!1}async function Q(){let v=(await navigator.mediaDevices.getDisplayMedia({video:!0})).getVideoTracks()[0];await A(v),v.onended=()=>{K()},s.value=!0}return{status:t,localStream:r,peers:o,screenSharing:s,error:i,id:l,shareScreen:Q,stopScreen:K,toggleAudio(S){let v=u?.getAudioTracks()[0];return v?(v.enabled=S??!v.enabled,v.enabled):!1},toggleVideo(S){let v=u?.getVideoTracks()[0];return v?(v.enabled=S??!v.enabled,v.enabled):!1},leave(){C({type:"bye"});for(let S of[...c.keys()])T(S);if(u)for(let S of u.getTracks())S.stop();g.close(),t.value="closed"}}}function vt(e,n={}){let t=k([]),r=k([]),o=k([]),s=new Map,i=n.typingTimeout??3e3,l=n.url??"/ws/chat",d=Ae(l.includes("{channel}")?l.replace("{channel}",String(e)):`${l}/${e}`),f=W.connect(d,{token:n.token});function h(a){o.value.includes(a)||(o.value=[...o.value,a]);let c=s.get(a);c&&clearTimeout(c),s.set(a,setTimeout(()=>{o.value=o.value.filter(g=>g!==a),s.delete(a)},i))}f.on("message",a=>{let c=a;switch(c.type){case"message":t.value=[...t.value,c.message];break;case"presence":c.event==="roster"?r.value=c.users??[]:c.event==="join"&&c.user_id?r.value=[...new Set([...r.value,c.user_id])]:c.event==="leave"&&(r.value=r.value.filter(g=>g!==c.user_id));break;case"typing":c.user_id&&h(c.user_id);break}});let w=n.apiBase??"",u=n.messagesPath??"/api/channels/{id}/messages";return{status:f.status,connected:f.connected,messages:t,presence:r,typing:o,send(a,c){f.send({type:"message",body:a,thread_id:c??null})},sendTyping(){f.send({type:"typing"})},markRead(){f.send({type:"read"})},async history(a,c=50){let g=u.replace("{id}",String(e)),y=new URLSearchParams({limit:String(c)});a&&y.set("before",String(a));let C={};n.token&&(C.Authorization=`Bearer ${n.token}`);let m=await fetch(`${w}${g}?${y}`,{headers:C});if(!m.ok)throw new Error(`[tina4] chat history failed: ${m.status}`);let T=await m.json(),_=[...T].reverse();return t.value=[..._,...t.value],T},close(){for(let a of s.values())clearTimeout(a);s.clear(),f.close()}}}async function yt(e,n,t={}){let r=t.filesPath??"/api/files",o=new FormData;o.append("channel_id",String(e)),o.append("file",n,n.name??"file");let s={};t.token&&(s.Authorization=`Bearer ${t.token}`);let i=await fetch(`${t.apiBase??""}${r}`,{method:"POST",body:o,headers:s});if(!i.ok)throw new Error(`[tina4] file upload failed: ${i.status}`);return i.json()}async function bt(e,n={}){let t=/^https?:\/\//.test(e)?e:`${n.apiBase??""}${n.filesPath??"/api/files"}/${e}`,r={};n.token&&(r.Authorization=`Bearer ${n.token}`);let o=await fetch(t,{headers:r});if(!o.ok)throw new Error(`[tina4] file fetch failed: ${o.status}`);return URL.createObjectURL(await o.blob())}var Ie={config:Z,call:ht,chat:vt,upload:yt,fetchBlob:bt};var Pe={read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},Ne=/(token|password|passwd|secret|api[_-]?key|apikey|auth(?!or)|credential|jwt|bearer|otp|seed|private[_-]?key|session[_-]?id)/i,St=/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}$/,kt=/^[A-Za-z0-9+/_=-]{40,}$/,Le=new Set;function De(e,n){if(Ne.test(e))return`key name "${e}" looks like a credential`;if(typeof n=="string"){if(St.test(n))return"value looks like a JWT";if(n.length>=40&&kt.test(n))return"value looks like a long base64 / token"}if(n&&typeof n=="object"&&!Array.isArray(n)){for(let t of Object.keys(n))if(Ne.test(t))return`object contains a credential-shape field "${t}"`}return null}function Fe(e,n){Le.has(n)||(Le.add(n),console.warn(`[tina4 persist] ${e} (key: ${JSON.stringify(n)}). localStorage is XSS-readable and never appropriate for credentials, tokens, passwords, personal data, or secrets. See STORAGE.md.`))}function qe(e){if(typeof globalThis>"u")return null;try{let n=e==="local"?globalThis.localStorage:globalThis.sessionStorage;return!n||typeof n.getItem!="function"?null:n}catch{return null}}function Ue(e,n){let{key:t,storage:r="local",serializer:o=Pe,version:s=1,migrate:i,syncTabs:l=!1,silenceCredentialWarning:d=!1}=n;if(!t||typeof t!="string")throw new Error("[tina4 persist] options.key is required and must be a string");let f=o===Pe,h=qe(r);if(!h)return $e(e,()=>{},()=>{});try{let a=h.getItem(t);if(a!==null){let c,g;try{let y=JSON.parse(a);y&&typeof y=="object"&&"value"in y?(c=y.v,g=y.value):g=y}catch{g=f?a:o.read(a)}if(c===s||c===void 0){let y=f?g:o.read(typeof g=="string"?g:JSON.stringify(g));e.value=y}else if(i)try{e.value=i(g,c)}catch(y){console.warn(`[tina4 persist] migrate() threw for key "${t}":`,y)}else console.warn(`[tina4 persist] stored version ${c} does not match current ${s} for key "${t}", and no migrate() was provided. Discarding the stored value.`)}}catch(a){console.warn(`[tina4 persist] failed to read key "${t}":`,a)}if(!d){let a=De(t,e.peek());a&&Fe(a,t)}let w=R(()=>{let a=e.value;if(!d){let c=De(t,a);c&&Fe(c,t)}try{let g=JSON.stringify(f?{v:s,value:a}:{v:s,value:o.write(a)});h.setItem(t,g)}catch(c){console.warn(`[tina4 persist] failed to write key "${t}":`,c)}}),u=null;if(l&&typeof globalThis<"u"&&"addEventListener"in globalThis){let a=c=>{let g=c;if(g.storageArea===h&&g.key===t&&g.newValue!==null)try{let y=JSON.parse(g.newValue),C=y&&typeof y=="object"&&"v"in y?y.v:void 0,m=C!==void 0?y.value:y;C!==void 0&&C!==s&&i?e.value=i(m,C):(C===s||C===void 0)&&(e.value=f?m:o.read(typeof m=="string"?m:JSON.stringify(m)))}catch(y){console.warn(`[tina4 persist] failed to parse storage event for key "${t}":`,y)}};globalThis.addEventListener?.("storage",a),u=()=>{globalThis.removeEventListener?.("storage",a)}}return $e(e,()=>{try{h.removeItem(t)}catch(a){console.warn(`[tina4 persist] failed to clear key "${t}":`,a)}},()=>{w(),u&&u()})}function $e(e,n,t){return Object.assign(e,{clear:n,dispose:t})}function je(e,n="local"){let t=qe(n);if(t)for(let r of e)try{t.removeItem(r)}catch(o){console.warn(`[tina4 persist] failed to clear key "${r}":`,o)}}var wt=["ar","he","fa","ur","ps","dv","syr","ckb","yi"];function Tt(){return globalThis.navigator?.language||"en"}function He(e,n="",t={}){for(let[r,o]of Object.entries(e)){let s=n?`${n}.${r}`:r;if(o!==null&&typeof o=="object"&&!Array.isArray(o))He(o,s,t);else{let i=String(o);t[s]=i,r in t||(t[r]=i)}}return t}function Ct(e,n){return e.replace(/\{(\w+)\}/g,(t,r)=>Object.prototype.hasOwnProperty.call(n,r)?String(n[r]):t)}function le(e={}){let n=e.locale||Tt(),t=e.fallbackLocale||n,r=new Set([...wt,...e.rtlLocales||[]]),o=k(n,"i18n.locale"),s=new Map,i=new Map;function l(u,a){let c=He(a),g=s.get(u);s.set(u,g?{...g,...c}:c)}if(e.messages)for(let[u,a]of Object.entries(e.messages))l(u,a);function d(u,a){return s.get(u)?.[a]}function f(u,a){let c=`n|${u}|${JSON.stringify(a||{})}`,g=i.get(c);return g||(g=new Intl.NumberFormat(u,a),i.set(c,g)),g}function h(u,a){let c=`d|${u}|${JSON.stringify(a||{})}`,g=i.get(c);return g||(g=new Intl.DateTimeFormat(u,a),i.set(c,g)),g}function w(u,a){let c=`r|${u}|${JSON.stringify(a||{})}`,g=i.get(c);return g||(g=new Intl.RelativeTimeFormat(u,a),i.set(c,g)),g}return{locale:o,t(u,a){let c=o.value,g=d(c,u);return g===void 0&&t!==c&&(g=d(t,u)),g===void 0&&(g=u),a?Ct(g,a):g},setLocale(u){o.value=u},getLocale(){return o.value},addMessages:l,hasLocale(u){return s.has(u)},availableLocales(){return[...s.keys()].sort()},async loadMessages(u,a){let c=await fetch(a);if(!c.ok)throw new Error(`[tina4 i18n] failed to load "${u}" from ${a}: ${c.status}`);l(u,await c.json())},number(u,a){return f(o.value,a).format(u)},currency(u,a,c){return f(o.value,{style:"currency",currency:a,...c}).format(u)},date(u,a){let c=u instanceof Date?u:new Date(u);return h(o.value,a).format(c)},relativeTime(u,a,c){return w(o.value,c||{numeric:"auto"}).format(u,a)},isRTL(){return r.has(o.value.split("-")[0].toLowerCase())},dir(){return this.isRTL()?"rtl":"ltr"}}}var We=le();return Xe(Et);})();
47
+ `.trim()}function Ie(e){let n={name:e.name,short_name:e.shortName??e.name,start_url:"/",display:e.display??"standalone",background_color:e.backgroundColor??"#ffffff",theme_color:e.themeColor??"#000000"};return e.icon&&(n.icons=[{src:e.icon,sizes:"192x192",type:"image/png"},{src:e.icon,sizes:"512x512",type:"image/png"}]),n}var Ne={register(e){let n=Ie(e),t=new Blob([JSON.stringify(n)],{type:"application/json"}),r=document.createElement("link");r.rel="manifest",r.href=URL.createObjectURL(t),document.head.appendChild(r);let o=document.querySelector('meta[name="theme-color"]');o||(o=document.createElement("meta"),o.name="theme-color",document.head.appendChild(o)),o.content=e.themeColor??"#000000","serviceWorker"in navigator&&(e.swUrl?navigator.serviceWorker.register(e.swUrl).catch(s=>{console.warn("[tina4] Service worker registration failed:",s)}):navigator.serviceWorker.register("/sw.js").catch(()=>{console.info("[tina4] No service worker at /sw.js. Use pwa.generateServiceWorker() to create one, or pass swUrl in config.")}))},generateServiceWorker(e){return mt(e)},generateManifest(e){return Ie(e)}};var ht={reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,protocols:[],token:""};function vt(e){let n=Array.isArray(e.protocols)?e.protocols:e.protocols?[e.protocols]:[];return e.token?["bearer",e.token,...n]:e.protocols}function yt(e,n={}){let t={...ht,...n},r=w("connecting"),o=w(!1),s=w(null),i=w(null),c=w(0),a={message:[],open:[],close:[],error:[]},l=null,g=!1,m=t.reconnectDelay,u=null,d=0;function f(v){if(typeof v!="string")return v;try{return JSON.parse(v)}catch{return v}}function h(){r.value=d>0?"reconnecting":"connecting";try{l=new WebSocket(e,vt(t))}catch{r.value="closed",o.value=!1;return}l.onopen=()=>{r.value="open",o.value=!0,i.value=null,d=0,m=t.reconnectDelay,c.value=0;for(let v of a.open)v()},l.onmessage=v=>{let T=f(v.data);s.value=T;for(let R of a.message)R(T)},l.onclose=v=>{r.value="closed",o.value=!1;for(let T of a.close)T(v.code,v.reason);!g&&t.reconnect&&d<t.reconnectAttempts&&x()},l.onerror=v=>{i.value=v;for(let T of a.error)T(v)}}function x(){d++,c.value=d,r.value="reconnecting",u=setTimeout(()=>{u=null,h()},m),m=Math.min(m*2,t.reconnectMaxDelay)}let C={status:r,connected:o,lastMessage:s,error:i,reconnectCount:c,send(v){if(!l||l.readyState!==WebSocket.OPEN)throw new Error("[tina4] WebSocket is not connected");let T=typeof v=="string"?v:JSON.stringify(v);l.send(T)},on(v,T){return a[v].push(T),()=>{let R=a[v],A=R.indexOf(T);A>=0&&R.splice(A,1)}},pipe(v,T){let R=A=>{v.value=T(A,v.value)};return C.on("message",R)},close(v,T){g=!0,u&&(clearTimeout(u),u=null),l&&l.close(v??1e3,T??""),r.value="closed",o.value=!1}};return h(),C}var K={connect:yt};var St={mode:"eventsource",method:"GET",headers:{},body:void 0,reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,events:[],json:!0};function bt(e,n={}){let t={...St,...n},r=w("connecting"),o=w(!1),s=w(null),i=w(null),c=w(null),a=w(0),l={message:[],open:[],close:[],error:[]},g=null,m=null,u=!1,d=t.reconnectDelay,f=null,h=0;function x(p){if(!t.json||typeof p!="string")return p;try{return JSON.parse(p)}catch{return p}}function C(p,S){s.value=p,i.value=S;for(let k of l.message)k(p,S??void 0)}function v(){r.value="open",o.value=!0,c.value=null,h=0,d=t.reconnectDelay,a.value=0;for(let p of l.open)p()}function T(){r.value="closed",o.value=!1;for(let p of l.close)p();!u&&t.reconnect&&h<t.reconnectAttempts&&Y()}function R(p){c.value=p;for(let S of l.error)S(p)}function A(){r.value=h>0?"reconnecting":"connecting";try{g=new EventSource(e)}catch{r.value="closed",o.value=!1;return}g.onopen=()=>v(),g.onmessage=p=>{C(x(p.data),null)};for(let p of t.events)g.addEventListener(p,S=>{C(x(S.data),p)});g.onerror=p=>{R(p),g&&g.readyState===2&&(g=null,T())}}function z(){r.value=h>0?"reconnecting":"connecting",m=new AbortController;let p={method:t.method,headers:t.headers,signal:m.signal};t.body!==void 0&&(p.body=typeof t.body=="string"?t.body:JSON.stringify(t.body)),fetch(e,p).then(async S=>{if(!S.ok){R(new Error(`[tina4] SSE fetch ${S.status}`)),T();return}v();let k=S.body.getReader(),M=new TextDecoder,P="";for(;;){let{done:Ge,value:Ze}=await k.read();if(Ge)break;P+=M.decode(Ze,{stream:!0});let ge=P.split(`
48
+ `);P=ge.pop();for(let Qe of ge){let pe=Qe.trim();pe&&C(x(pe),null)}}let fe=P.trim();fe&&C(x(fe),null),m=null,T()}).catch(S=>{S.name!=="AbortError"&&(m=null,R(S),T())})}function Y(){h++,a.value=h,r.value="reconnecting",f=setTimeout(()=>{f=null,b()},d),d=Math.min(d*2,t.reconnectMaxDelay)}function b(){t.mode==="fetch"?z():A()}let y={status:r,connected:o,lastMessage:s,lastEvent:i,error:c,reconnectCount:a,on(p,S){return l[p].push(S),()=>{let k=l[p],M=k.indexOf(S);M>=0&&k.splice(M,1)}},pipe(p,S){let k=M=>{p.value=S(M,p.value)};return y.on("message",k)},close(){u=!0,f&&(clearTimeout(f),f=null),g&&(g.close(),g=null),m&&(m.abort(),m=null),r.value="closed",o.value=!1}};return b(),y}var Pe={connect:bt};async function X(e="/api/rtc/config"){let n=await fetch(e);if(!n.ok)throw new Error(`[tina4] rtc config fetch failed: ${n.status}`);return n.json()}function wt(){let e=window.location;return`${e.protocol==="https:"?"wss:":"ws:"}//${e.host}`}function Le(e){return/^wss?:\/\//.test(e)?e:wt()+(e.startsWith("/")?e:"/"+e)}function Tt(){let e=globalThis.crypto;if(e&&"randomUUID"in e)return e.randomUUID().slice(0,8);let n="";for(let t=0;t<8;t++)n+=Math.floor(16*(.5+t)).toString(16);return n+Date.now().toString(16).slice(-4)}async function kt(e,n={}){let t=w("connecting"),r=w(null),o=w([]),s=w(!1),i=w(null),c=Tt(),a=n.config??await X(n.configUrl),l=n.iceServers??a.iceServers??[],g=n.signallingUrl??a.signalling??"/ws/rtc",m=Le(g.includes("{room}")?g.replace("{room}",e):`${g}/${e}`),u=null;n.media instanceof MediaStream?u=n.media:n.media!==!1&&(u=await navigator.mediaDevices.getUserMedia(n.media??{audio:!0,video:!0})),r.value=u;let d=u?.getVideoTracks()[0]??null,f=new Map,h=K.connect(m);function x(){o.value=[...f.entries()].map(([b,y])=>({id:b,stream:y.stream}))}function C(b){try{h.send({...b,from:c})}catch{}}function v(b){let y=f.get(b);if(y)return y;let p=new RTCPeerConnection({iceServers:l}),S={pc:p,polite:c<b,makingOffer:!1,ignoreOffer:!1,stream:null};if(f.set(b,S),u)for(let k of u.getTracks())p.addTrack(k,u);return p.onnegotiationneeded=async()=>{try{S.makingOffer=!0,await p.setLocalDescription(),C({type:"desc",to:b,description:p.localDescription})}catch(k){i.value=k}finally{S.makingOffer=!1}},p.onicecandidate=({candidate:k})=>{k&&C({type:"ice",to:b,candidate:k})},p.ontrack=({streams:k})=>{S.stream=k[0]??null,x()},p.onconnectionstatechange=()=>{["failed","closed"].includes(p.connectionState)?T(b):p.connectionState==="connected"&&(t.value="connected")},x(),S}function T(b){let y=f.get(b);if(y){try{y.pc.close()}catch{}f.delete(b),x()}}async function R(b){let y=b,p=y.from;if(!p||p===c||y.to&&y.to!==c)return;if(y.type==="hello"){v(p),C({type:"welcome",to:p});return}if(y.type==="welcome"){v(p);return}if(y.type==="bye"){T(p);return}let S=v(p),k=S.pc;if(y.type==="desc"){let M=y.description,P=M.type==="offer"&&(S.makingOffer||k.signalingState!=="stable");if(S.ignoreOffer=!S.polite&&P,S.ignoreOffer)return;await k.setRemoteDescription(M),M.type==="offer"&&(await k.setLocalDescription(),C({type:"desc",to:p,description:k.localDescription}))}else if(y.type==="ice")try{await k.addIceCandidate(y.candidate)}catch(M){S.ignoreOffer||(i.value=M)}}h.on("message",b=>{R(b)}),h.on("open",()=>{C({type:"hello"})});async function A(b){if(b)for(let{pc:y}of f.values()){let p=y.getSenders().find(S=>S.track?.kind==="video");p&&await p.replaceTrack(b)}}async function z(){await A(d),s.value=!1}async function Y(){let y=(await navigator.mediaDevices.getDisplayMedia({video:!0})).getVideoTracks()[0];await A(y),y.onended=()=>{z()},s.value=!0}return{status:t,localStream:r,peers:o,screenSharing:s,error:i,id:c,shareScreen:Y,stopScreen:z,toggleAudio(b){let y=u?.getAudioTracks()[0];return y?(y.enabled=b??!y.enabled,y.enabled):!1},toggleVideo(b){let y=u?.getVideoTracks()[0];return y?(y.enabled=b??!y.enabled,y.enabled):!1},leave(){C({type:"bye"});for(let b of[...f.keys()])T(b);if(u)for(let b of u.getTracks())b.stop();h.close(),t.value="closed"}}}function Ct(e,n={}){let t=w([]),r=w([]),o=w([]),s=new Map,i=n.typingTimeout??3e3,c=n.url??"/ws/chat",a=Le(c.includes("{channel}")?c.replace("{channel}",String(e)):`${c}/${e}`),l=K.connect(a,{token:n.token});function g(d){o.value.includes(d)||(o.value=[...o.value,d]);let f=s.get(d);f&&clearTimeout(f),s.set(d,setTimeout(()=>{o.value=o.value.filter(h=>h!==d),s.delete(d)},i))}l.on("message",d=>{let f=d;switch(f.type){case"message":t.value=[...t.value,f.message];break;case"presence":f.event==="roster"?r.value=f.users??[]:f.event==="join"&&f.user_id?r.value=[...new Set([...r.value,f.user_id])]:f.event==="leave"&&(r.value=r.value.filter(h=>h!==f.user_id));break;case"typing":f.user_id&&g(f.user_id);break}});let m=n.apiBase??"",u=n.messagesPath??"/api/channels/{id}/messages";return{status:l.status,connected:l.connected,messages:t,presence:r,typing:o,send(d,f){l.send({type:"message",body:d,thread_id:f??null})},sendTyping(){l.send({type:"typing"})},markRead(){l.send({type:"read"})},async history(d,f=50){let h=u.replace("{id}",String(e)),x=new URLSearchParams({limit:String(f)});d&&x.set("before",String(d));let C={};n.token&&(C.Authorization=`Bearer ${n.token}`);let v=await fetch(`${m}${h}?${x}`,{headers:C});if(!v.ok)throw new Error(`[tina4] chat history failed: ${v.status}`);let T=await v.json(),R=[...T].reverse();return t.value=[...R,...t.value],T},close(){for(let d of s.values())clearTimeout(d);s.clear(),l.close()}}}async function Et(e,n,t={}){let r=t.filesPath??"/api/files",o=new FormData;o.append("channel_id",String(e)),o.append("file",n,n.name??"file");let s={};t.token&&(s.Authorization=`Bearer ${t.token}`);let i=await fetch(`${t.apiBase??""}${r}`,{method:"POST",body:o,headers:s});if(!i.ok)throw new Error(`[tina4] file upload failed: ${i.status}`);return i.json()}async function Rt(e,n={}){let t=/^https?:\/\//.test(e)?e:`${n.apiBase??""}${n.filesPath??"/api/files"}/${e}`,r={};n.token&&(r.Authorization=`Bearer ${n.token}`);let o=await fetch(t,{headers:r});if(!o.ok)throw new Error(`[tina4] file fetch failed: ${o.status}`);return URL.createObjectURL(await o.blob())}var De={config:X,call:kt,chat:Ct,upload:Et,fetchBlob:Rt};var Fe={read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},$e=/(token|password|passwd|secret|api[_-]?key|apikey|auth(?!or)|credential|jwt|bearer|otp|seed|private[_-]?key|session[_-]?id)/i,_t=/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}$/,xt=/^[A-Za-z0-9+/_=-]{40,}$/,qe=new Set;function Mt(e,n){if($e.test(e))return`key name "${e}" looks like a credential`;if(typeof n=="string"){if(_t.test(n))return"value looks like a JWT";if(n.length>=40&&xt.test(n))return"value looks like a long base64 / token"}if(n&&typeof n=="object"&&!Array.isArray(n)){for(let t of Object.keys(n))if($e.test(t))return`object contains a credential-shape field "${t}"`}return null}function Ot(e,n){qe.has(n)||(qe.add(n),console.warn(`[tina4 persist] ${e} (key: ${JSON.stringify(n)}). localStorage is XSS-readable and never appropriate for credentials, tokens, passwords, personal data, or secrets. See STORAGE.md.`))}function He(e){if(typeof globalThis>"u")return null;try{let n=e==="local"?globalThis.localStorage:globalThis.sessionStorage;return!n||typeof n.getItem!="function"?null:n}catch{return null}}function je(e,n,t){try{let r=JSON.parse(e);return r&&typeof r=="object"&&"value"in r?{version:r.v,payload:r.value}:{version:void 0,payload:r}}catch{return{version:void 0,payload:t?e:n.read(e)}}}function We(e,n,t){return t?e:n.read(typeof e=="string"?e:JSON.stringify(e))}function At(e,n,t,r,o,s,i){try{let c=n.getItem(t);if(c===null)return;let a=je(c,o,s);if(a.version===r||a.version===void 0){e.value=We(a.payload,o,s);return}if(i){try{e.value=i(a.payload,a.version)}catch(l){console.warn(`[tina4 persist] migrate() threw for key "${t}":`,l)}return}console.warn(`[tina4 persist] stored version ${a.version} does not match current ${r} for key "${t}", and no migrate() was provided. Discarding the stored value.`)}catch(c){console.warn(`[tina4 persist] failed to read key "${t}":`,c)}}function Ke(e,n,t){if(t)return;let r=Mt(e,n);r&&Ot(r,e)}function It(e,n,t,r,o,s,i){return E(()=>{let c=e.value;Ke(t,c,i);try{let a=JSON.stringify(s?{v:r,value:c}:{v:r,value:o.write(c)});n.setItem(t,a)}catch(a){console.warn(`[tina4 persist] failed to write key "${t}":`,a)}})}function Nt(e,n,t,r,o,s,i,c){if(!c||typeof globalThis>"u"||!("addEventListener"in globalThis))return null;let a=l=>{let g=l;if(!(g.storageArea!==n||g.key!==t||g.newValue===null))try{let m=je(g.newValue,o,s);m.version!==void 0&&m.version!==r&&i?e.value=i(m.payload,m.version):(m.version===r||m.version===void 0)&&(e.value=We(m.payload,o,s))}catch(m){console.warn(`[tina4 persist] failed to parse storage event for key "${t}":`,m)}};return globalThis.addEventListener?.("storage",a),()=>{globalThis.removeEventListener?.("storage",a)}}function Pt(e,n){try{e.removeItem(n)}catch(t){console.warn(`[tina4 persist] failed to clear key "${n}":`,t)}}function ze(e,n){let{key:t,storage:r="local",serializer:o=Fe,version:s=1,migrate:i,syncTabs:c=!1,silenceCredentialWarning:a=!1}=n;if(!t||typeof t!="string")throw new Error("[tina4 persist] options.key is required and must be a string");let l=o===Fe,g=He(r);if(!g)return Ue(e,()=>{},()=>{});At(e,g,t,s,o,l,i),Ke(t,e.peek(),a);let m=It(e,g,t,s,o,l,a),u=Nt(e,g,t,s,o,l,i,c);return Ue(e,()=>Pt(g,t),()=>{m(),u&&u()})}function Ue(e,n,t){return Object.assign(e,{clear:n,dispose:t})}function Ve(e,n="local"){let t=He(n);if(t)for(let r of e)try{t.removeItem(r)}catch(o){console.warn(`[tina4 persist] failed to clear key "${r}":`,o)}}var Lt=["ar","he","fa","ur","ps","dv","syr","ckb","yi"];function Dt(){return globalThis.navigator?.language||"en"}function Be(e,n="",t={}){for(let[r,o]of Object.entries(e)){let s=n?`${n}.${r}`:r;if(o!==null&&typeof o=="object"&&!Array.isArray(o))Be(o,s,t);else{let i=String(o);t[s]=i,r in t||(t[r]=i)}}return t}function Ft(e,n){return e.replace(/\{(\w+)\}/g,(t,r)=>Object.prototype.hasOwnProperty.call(n,r)?String(n[r]):t)}function de(e={}){let n=e.locale||Dt(),t=e.fallbackLocale||n,r=new Set([...Lt,...e.rtlLocales||[]]),o=w(n,"i18n.locale"),s=new Map,i=new Map;function c(u,d){let f=Be(d),h=s.get(u);s.set(u,h?{...h,...f}:f)}if(e.messages)for(let[u,d]of Object.entries(e.messages))c(u,d);function a(u,d){return s.get(u)?.[d]}function l(u,d){let f=`n|${u}|${JSON.stringify(d||{})}`,h=i.get(f);return h||(h=new Intl.NumberFormat(u,d),i.set(f,h)),h}function g(u,d){let f=`d|${u}|${JSON.stringify(d||{})}`,h=i.get(f);return h||(h=new Intl.DateTimeFormat(u,d),i.set(f,h)),h}function m(u,d){let f=`r|${u}|${JSON.stringify(d||{})}`,h=i.get(f);return h||(h=new Intl.RelativeTimeFormat(u,d),i.set(f,h)),h}return{locale:o,t(u,d){let f=o.value,h=a(f,u);return h===void 0&&t!==f&&(h=a(t,u)),h===void 0&&(h=u),d?Ft(h,d):h},setLocale(u){o.value=u},getLocale(){return o.value},addMessages:c,hasLocale(u){return s.has(u)},availableLocales(){return[...s.keys()].sort()},async loadMessages(u,d){let f=await fetch(d);if(!f.ok)throw new Error(`[tina4 i18n] failed to load "${u}" from ${d}: ${f.status}`);c(u,await f.json())},number(u,d){return l(o.value,d).format(u)},currency(u,d,f){return l(o.value,{style:"currency",currency:d,...f}).format(u)},date(u,d){let f=u instanceof Date?u:new Date(u);return g(o.value,d).format(f)},relativeTime(u,d,f){return m(o.value,f||{numeric:"auto"}).format(u,d)},isRTL(){return r.has(o.value.split("-")[0].toLowerCase())},dir(){return this.isRTL()?"rtl":"ltr"}}}var Je=de();return rt($t);})();
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.107"
4
+ VERSION = "3.13.108"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.107
4
+ version: 3.13.108
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-20 00:00:00.000000000 Z
11
+ date: 2026-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack