@cosmo-frameworks/paywall-react 0.1.0 → 0.1.2
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/CHANGELOG.md +18 -0
- package/README.md +1 -65
- package/dist/index.cjs +1 -1
- package/dist/index.js +111 -103
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ y el versionado es [SemVer](https://semver.org/lang/es/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.2] - 2026-08-22
|
|
11
|
+
|
|
12
|
+
### Corregido
|
|
13
|
+
|
|
14
|
+
- **Bucle de peticiones al montar.** El efecto que programaba la recarga tenía
|
|
15
|
+
el estado entre sus dependencias, así que cada respuesta lo reprogramaba y
|
|
16
|
+
disparaba otra petición, encadenando llamadas hasta agotar el rate limit del
|
|
17
|
+
servidor (HTTP 429). Ahora la cadencia vive en una referencia y el temporizador
|
|
18
|
+
se encadena con `setTimeout`, de modo que el efecto solo depende de la función
|
|
19
|
+
de carga. Con 429 el SDK caía además en fail-open, así que el bloqueo dejaba
|
|
20
|
+
de aplicarse: quien actualice desde 0.1.x debería hacerlo.
|
|
21
|
+
|
|
22
|
+
## [0.1.1] - 2026-08-21
|
|
23
|
+
|
|
24
|
+
### Añadido
|
|
25
|
+
|
|
26
|
+
Cambiado el provider a npm
|
|
27
|
+
|
|
10
28
|
## [0.1.0] - 2026-08-21
|
|
11
29
|
|
|
12
30
|
Primera versión.
|
package/README.md
CHANGED
|
@@ -158,68 +158,4 @@ Solo la license key. La respuesta no incluye importes ni datos personales salvo
|
|
|
158
158
|
que la licencia lo autorice explícitamente.
|
|
159
159
|
|
|
160
160
|
**¿Y si borro `localStorage`?**
|
|
161
|
-
Se dispara una consulta nueva, que es más estricta, no menos.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## Desarrollo
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
npm install
|
|
169
|
-
npm test # Vitest
|
|
170
|
-
npm run build # ESM + CJS + tipos
|
|
171
|
-
npm pack # tarball para probar la integración en otro proyecto
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Publicación
|
|
175
|
-
|
|
176
|
-
El código vive en `cosmo-frameworks/paywall-react` (repositorio privado) y el
|
|
177
|
-
paquete se publica en **npmjs.com** bajo el scope `@cosmo-frameworks`.
|
|
178
|
-
|
|
179
|
-
Se eligió npmjs y no GitHub Packages porque este último exige un token de
|
|
180
|
-
acceso **incluso para paquetes públicos**, lo que obligaría a cada cliente a
|
|
181
|
-
configurar un `.npmrc` con credenciales y a resolver la autenticación dentro de
|
|
182
|
-
su Dockerfile. En npmjs un paquete público se instala sin nada.
|
|
183
|
-
|
|
184
|
-
La publicación usa **trusted publishing (OIDC)**: el workflow obtiene una
|
|
185
|
-
credencial efímera de npm y no hay ningún secreto guardado en el repositorio.
|
|
186
|
-
Se eligió así porque los tokens de npm ya no son una opción sostenible — los
|
|
187
|
-
classic fueron revocados en diciembre de 2025, los granulares de escritura
|
|
188
|
-
caducan a los 90 días, y los de bypass-2FA pierden la capacidad de publicar en
|
|
189
|
-
enero de 2027.
|
|
190
|
-
|
|
191
|
-
Requisitos, una sola vez:
|
|
192
|
-
|
|
193
|
-
- La organización `cosmo-frameworks` creada en npmjs.com (gratis para paquetes
|
|
194
|
-
públicos).
|
|
195
|
-
- El **trusted publisher** configurado en los ajustes del paquete, apuntando a
|
|
196
|
-
este repositorio y a `.github/workflows/publish.yml`.
|
|
197
|
-
- El primer `npm publish` hecho a mano desde tu máquina, porque el trusted
|
|
198
|
-
publisher se configura sobre un paquete que ya existe.
|
|
199
|
-
|
|
200
|
-
La procedencia firmada se genera sola en este modo: no hace falta
|
|
201
|
-
`--provenance`.
|
|
202
|
-
|
|
203
|
-
> **No ejecutes `git init` aquí mientras la carpeta siga dentro del monorepo.**
|
|
204
|
-
> Un repositorio anidado hace que el monorepo trate esta carpeta como repo
|
|
205
|
-
> externo y deje de versionar su contenido **en silencio**: los commits pasan
|
|
206
|
-
> sin error y el paquete no viaja. Si alguna herramienta gráfica (GitKraken y
|
|
207
|
-
> similares crean un `.git` al detectar carpetas nuevas) lo ha creado, bórralo
|
|
208
|
-
> con `rm -rf packages/paywall-react/.git` antes de commitear.
|
|
209
|
-
|
|
210
|
-
El **repositorio es privado** y el **paquete es público**: el `src/` y el
|
|
211
|
-
historial no se ven, y solo se distribuye lo que declara `files` — el bundle de
|
|
212
|
-
`dist/` y este README, sin sourcemaps, que embeberían el TypeScript original.
|
|
213
|
-
|
|
214
|
-
Primera publicación, ya con la carpeta **fuera** del monorepo:
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
cd paywall-react
|
|
218
|
-
git init && git add . && git commit -m "chore: primera versión"
|
|
219
|
-
git remote add origin git@github.com:cosmo-frameworks/paywall-react.git
|
|
220
|
-
git push -u origin main
|
|
221
|
-
git tag v0.1.0 && git push --tags # dispara el workflow
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
A partir de ahí, ese repositorio es la fuente de verdad y esta carpeta puede
|
|
225
|
-
eliminarse del monorepo.
|
|
161
|
+
Se dispara una consulta nueva, que es más estricta, no menos.
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react"),t=require("react/jsx-runtime");var n=`/api/public/license/v1/status`,r=`cpl:state:`,
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react"),t=require("react/jsx-runtime");var n=`/api/public/license/v1/status`,r=9e5,i=`cpl:state:`,a=1e4,o=1e3,s=3e4,c=()=>typeof window<`u`&&window.document!==void 0,l=new Map,u=e=>`${i}${e.slice(0,12)}`,d=e=>(e.storage??`local`)===`local`&&c(),f=e=>{if(typeof e!=`object`||!e)return!1;let t=e;return t.version!==1||typeof t.receivedAt!=`number`?!1:typeof t.payload?.status==`string`},p=(e,t,n)=>{if((n.storage??`local`)===`none`)return;let r={version:1,payload:t,receivedAt:Date.now()},i=u(e);if(l.set(i,r),d(n))try{window.localStorage.setItem(i,JSON.stringify(r))}catch{}},m=(e,t)=>{if((t.storage??`local`)===`none`)return null;let n=u(e),r=l.get(n)??null;if(!r&&d(t))try{let e=window.localStorage.getItem(n);if(e){let t=JSON.parse(e);f(t)&&(r=t)}}catch{}if(!r)return null;let i=t.maxCacheAgeMs??6048e5;return Date.now()-r.receivedAt>i?(h(e),null):r},h=e=>{let t=u(e);if(l.delete(t),c())try{window.localStorage.removeItem(t)}catch{}},g=1,_=e=>{if(typeof e!=`object`||!e)return!1;let t=e;return t.v===g&&typeof t.status==`string`&&typeof t.licenseId==`string`},v=async e=>{let{licenseKey:t}=e;if(!t)return y(e,Error(`[paywall] Falta licenseKey en la configuración.`));let r=e.fetchImpl??globalThis.fetch;if(typeof r!=`function`)return y(e,Error(`[paywall] No hay implementación de fetch disponible.`));let i=`${(e.apiUrl??`https://api.clients.ridel.dev`).replace(/\/+$/,``)}${n}?key=${encodeURIComponent(t)}&t=${Date.now()}`,o=new AbortController,s=setTimeout(()=>o.abort(),a);try{let n=await r(i,{method:`GET`,credentials:`omit`,signal:e.signal??o.signal}),a=await n.json();if(!n.ok){let t=a?.error??`HTTP ${n.status}`;return y(e,Error(`[paywall] El servidor rechazó la licencia: ${t}`))}return _(a)?(p(t,a,e),{payload:a,source:`network`,isStale:!1,error:null}):y(e,Error(`[paywall] Respuesta con un formato que esta versión del SDK no entiende. Actualiza el paquete.`))}catch(t){return y(e,b(t))}finally{clearTimeout(s)}},y=(e,t)=>{let n=e.licenseKey?m(e.licenseKey,e):null;return n?{payload:n.payload,source:`cache`,isStale:Date.parse(n.payload.expiresAt)<Date.now(),error:t}:{payload:null,source:`none`,isStale:!1,error:t}},b=e=>e instanceof Error?e:Error(`[paywall] Fallo al consultar la licencia: ${String(e)}`),x=(0,e.createContext)(null),S=e=>e?new Date(e):null,C=new Map,w=e=>{if(e.failures>0)return Math.min(s,o*2**Math.min(e.failures,5));let t=e.configuredMs??9e5;return e.serverHintSeconds?Math.min(t,e.serverHintSeconds*1e3):t},T=(0,e.memo)(({config:n,children:i})=>{let[a,o]=(0,e.useState)(()=>{let e=c()?m(n.licenseKey,n):null;return{payload:e?.payload??null,source:e?`cache`:`none`,isStale:e?Date.parse(e.payload.expiresAt)<Date.now():!1,isLoading:!0,error:null}}),s=(0,e.useRef)(0),l=(0,e.useRef)(null),u=(0,e.useRef)(r),d=(0,e.useRef)(n.onStatusChange);d.current=n.onStatusChange;let f=(0,e.useCallback)(async()=>{if(!c())return;l.current?.abort();let e=new AbortController;l.current=e;let t=`${n.apiUrl??``}|${n.licenseKey}`,r=C.get(t),i=r??v({licenseKey:n.licenseKey,apiUrl:n.apiUrl,storage:n.storage,maxCacheAgeMs:n.maxCacheAgeMs,fetchImpl:n.fetchImpl,signal:e.signal});r||C.set(t,i);try{let t=await i;if(e.signal.aborted)return;s.current=t.source===`network`?0:s.current+1,u.current=w({failures:s.current,serverHintSeconds:t.payload?.refreshAfterSeconds,configuredMs:n.refreshIntervalMs}),o({payload:t.payload,source:t.source,isStale:t.isStale,isLoading:!1,error:t.error}),n.debug&&console.info(`[paywall]`,t.source,t.payload?.status,t.error)}finally{r||C.delete(t)}},[n.apiUrl,n.licenseKey,n.storage,n.maxCacheAgeMs,n.fetchImpl,n.debug,n.refreshIntervalMs]);(0,e.useEffect)(()=>{if(!c())return;let e=!1,t,n=()=>{e||(t=setTimeout(()=>{f().then(n)},u.current))};return f().then(n),()=>{e=!0,t&&clearTimeout(t),l.current?.abort()}},[f]),(0,e.useEffect)(()=>{if(!c())return;let e=()=>{document.visibilityState===`visible`&&f()};return document.addEventListener(`visibilitychange`,e),window.addEventListener(`online`,e),()=>{document.removeEventListener(`visibilitychange`,e),window.removeEventListener(`online`,e)}},[f]);let p=(0,e.useMemo)(()=>{let e=a.payload;return{state:e?.status??`OK`,reason:e?.reason??`NONE`,isBlocked:e?.status===`BLOCKED`,isLoading:a.isLoading,isStale:a.isStale,source:a.source,graceUntil:S(e?.graceUntil),nextTransitionAt:S(e?.nextTransitionAt),message:e?.message??null,contactUrl:e?.contactUrl??null,payload:e,error:a.error,refresh:()=>void f()}},[a,f]);(0,e.useEffect)(()=>{d.current?.(p)},[p]);let h=(0,e.useMemo)(()=>({status:p,config:n}),[p,n]);return(0,t.jsx)(x.Provider,{value:h,children:i})});T.displayName=`PaywallProvider`;var E=[`BLOCKED`],D={OK:`allow`,WARNING:`warn`,GRACE:`grace`,BLOCKED:`grace`},O=(e,t)=>{let n=D[e]??`allow`;return e===`OK`?`allow`:(t.blockOn??E).includes(e)?`block`:n},k=`system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif`,A={position:`fixed`,inset:0,zIndex:2147483647,display:`flex`,alignItems:`center`,justifyContent:`center`,padding:`24px`,background:`rgba(9, 9, 11, 0.92)`,backdropFilter:`blur(6px)`,fontFamily:k,color:`#fafafa`,overflowY:`auto`},j={width:`100%`,maxWidth:`440px`,background:`#18181b`,border:`1px solid #27272a`,borderRadius:`16px`,padding:`32px`,boxShadow:`0 24px 48px rgba(0, 0, 0, 0.45)`,textAlign:`center`},M={display:`inline-flex`,alignItems:`center`,justifyContent:`center`,width:`48px`,height:`48px`,borderRadius:`50%`,background:`rgba(239, 68, 68, 0.14)`,color:`#f87171`,fontSize:`24px`,marginBottom:`20px`},N={margin:`0 0 12px`,fontSize:`20px`,fontWeight:600,lineHeight:1.3},P={margin:`0 0 24px`,fontSize:`15px`,lineHeight:1.55,color:`#a1a1aa`},F={display:`inline-block`,padding:`11px 22px`,borderRadius:`10px`,background:`#fafafa`,color:`#18181b`,fontSize:`14px`,fontWeight:600,textDecoration:`none`},I={margin:`20px 0 0`,fontSize:`12px`,color:`#71717a`},L={position:`sticky`,top:0,zIndex:2147483e3,display:`flex`,alignItems:`center`,gap:`12px`,padding:`10px 16px`,fontFamily:k,fontSize:`14px`,lineHeight:1.4,boxSizing:`border-box`},R={...L,background:`#fef3c7`,color:`#78350f`,borderBottom:`1px solid #fcd34d`},z={...L,background:`#fee2e2`,color:`#7f1d1d`,borderBottom:`1px solid #fca5a5`},B={color:`inherit`,fontWeight:600,textDecoration:`underline`,whiteSpace:`nowrap`},V={marginLeft:`auto`,padding:`2px 8px`,border:`none`,borderRadius:`6px`,background:`transparent`,color:`inherit`,fontSize:`18px`,lineHeight:1,cursor:`pointer`},H={flex:1,minWidth:0},U=`Este servicio está temporalmente suspendido. Ponte en contacto con el proveedor para restablecerlo.`,W=(0,e.memo)(({status:n,config:r})=>{let i=(0,e.useMemo)(()=>r.productName?`${r.productName} no está disponible`:`Servicio no disponible`,[r.productName]);return(0,t.jsx)(`div`,{style:A,role:`alertdialog`,"aria-modal":`true`,children:(0,t.jsxs)(`div`,{style:j,children:[(0,t.jsx)(`div`,{style:M,"aria-hidden":`true`,children:`⚠`}),(0,t.jsx)(`h1`,{style:N,children:i}),(0,t.jsx)(`p`,{style:P,children:n.message??U}),n.contactUrl?(0,t.jsx)(`a`,{style:F,href:n.contactUrl,target:`_blank`,rel:`noreferrer noopener`,children:`Resolver ahora`}):null,r.contactEmail?(0,t.jsxs)(`p`,{style:I,children:[`¿Dudas? Escribe a`,` `,(0,t.jsx)(`a`,{href:`mailto:${r.contactEmail}`,style:B,children:r.contactEmail})]}):null]})})});W.displayName=`BlockedOverlay`;var G=e=>{if(!e)return null;let t=e.getTime()-Date.now();if(t<=0)return null;let n=Math.floor(t/864e5);if(n>=1)return n===1?`1 día`:`${n} días`;let r=Math.max(1,Math.floor(t/36e5));return r===1?`1 hora`:`${r} horas`},K=(0,e.memo)(({status:n,variant:r})=>{let[i,a]=(0,e.useState)(!1),o=(0,e.useMemo)(()=>G(n.graceUntil),[n.graceUntil]);if(i)return null;let s=r===`grace`?z:R;return(0,t.jsxs)(`div`,{style:s,role:`status`,children:[(0,t.jsxs)(`span`,{style:H,children:[n.message,o?(0,t.jsxs)(`strong`,{children:[` El servicio se suspenderá en `,o,`.`]}):null]}),n.contactUrl?(0,t.jsx)(`a`,{style:B,href:n.contactUrl,target:`_blank`,rel:`noreferrer noopener`,children:`Resolver`}):null,r===`warning`?(0,t.jsx)(`button`,{type:`button`,style:V,onClick:()=>a(!0),"aria-label":`Descartar aviso`,children:`×`}):null]})});K.displayName=`StatusBanner`;var q=(0,e.memo)(({children:n,renderBlocked:r,renderWarning:i,renderGrace:a,showBanners:o=!0})=>{let s=(0,e.useContext)(x);if(!s)throw Error(`[paywall] <PaywallGate> debe usarse dentro de <PaywallProvider>.`);let{status:c,config:l}=s,u=(0,e.useMemo)(()=>O(c.state,l),[c.state,l]);if(u===`block`)return(0,t.jsx)(t.Fragment,{children:r?r(c):(0,t.jsx)(W,{status:c,config:l})});let d=o?u===`warn`?i?i(c):(0,t.jsx)(K,{status:c,variant:`warning`}):u===`grace`?a?a(c):(0,t.jsx)(K,{status:c,variant:`grace`}):null:null;return(0,t.jsxs)(t.Fragment,{children:[d,n]})});q.displayName=`PaywallGate`;var J=()=>{let t=(0,e.useContext)(x);if(!t)throw Error(`[paywall] useLicenseStatus debe usarse dentro de <PaywallProvider>.`);return t.status};exports.PaywallGate=q,exports.PaywallProvider=T,exports.clearLicenseCache=h,exports.useLicenseStatus=J;
|
package/dist/index.js
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createContext as e, memo as t, useCallback as n, useContext as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
|
|
3
3
|
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
4
|
-
var d = "/api/public/license/v1/status", f = "cpl:state:",
|
|
4
|
+
var d = "/api/public/license/v1/status", f = 9e5, p = "cpl:state:", m = 1e4, h = 1e3, g = 3e4, _ = () => typeof window < "u" && window.document !== void 0, v = /* @__PURE__ */ new Map(), y = (e) => `${p}${e.slice(0, 12)}`, b = (e) => (e.storage ?? "local") === "local" && _(), x = (e) => {
|
|
5
5
|
if (typeof e != "object" || !e) return !1;
|
|
6
6
|
let t = e;
|
|
7
7
|
return t.version !== 1 || typeof t.receivedAt != "number" ? !1 : typeof t.payload?.status == "string";
|
|
8
|
-
},
|
|
8
|
+
}, S = (e, t, n) => {
|
|
9
9
|
if ((n.storage ?? "local") === "none") return;
|
|
10
10
|
let r = {
|
|
11
11
|
version: 1,
|
|
12
12
|
payload: t,
|
|
13
13
|
receivedAt: Date.now()
|
|
14
|
-
}, i =
|
|
15
|
-
if (
|
|
14
|
+
}, i = y(e);
|
|
15
|
+
if (v.set(i, r), b(n)) try {
|
|
16
16
|
window.localStorage.setItem(i, JSON.stringify(r));
|
|
17
17
|
} catch {}
|
|
18
|
-
},
|
|
18
|
+
}, C = (e, t) => {
|
|
19
19
|
if ((t.storage ?? "local") === "none") return null;
|
|
20
|
-
let n =
|
|
21
|
-
if (!r &&
|
|
20
|
+
let n = y(e), r = v.get(n) ?? null;
|
|
21
|
+
if (!r && b(t)) try {
|
|
22
22
|
let e = window.localStorage.getItem(n);
|
|
23
23
|
if (e) {
|
|
24
24
|
let t = JSON.parse(e);
|
|
25
|
-
|
|
25
|
+
x(t) && (r = t);
|
|
26
26
|
}
|
|
27
27
|
} catch {}
|
|
28
28
|
if (!r) return null;
|
|
29
29
|
let i = t.maxCacheAgeMs ?? 6048e5;
|
|
30
|
-
return Date.now() - r.receivedAt > i ? (
|
|
31
|
-
},
|
|
32
|
-
let t =
|
|
33
|
-
if (
|
|
30
|
+
return Date.now() - r.receivedAt > i ? (w(e), null) : r;
|
|
31
|
+
}, w = (e) => {
|
|
32
|
+
let t = y(e);
|
|
33
|
+
if (v.delete(t), _()) try {
|
|
34
34
|
window.localStorage.removeItem(t);
|
|
35
35
|
} catch {}
|
|
36
|
-
},
|
|
36
|
+
}, T = 1, E = (e) => {
|
|
37
37
|
if (typeof e != "object" || !e) return !1;
|
|
38
38
|
let t = e;
|
|
39
|
-
return t.v ===
|
|
40
|
-
},
|
|
39
|
+
return t.v === T && typeof t.status == "string" && typeof t.licenseId == "string";
|
|
40
|
+
}, D = async (e) => {
|
|
41
41
|
let { licenseKey: t } = e;
|
|
42
|
-
if (!t) return
|
|
42
|
+
if (!t) return O(e, /* @__PURE__ */ Error("[paywall] Falta licenseKey en la configuración."));
|
|
43
43
|
let n = e.fetchImpl ?? globalThis.fetch;
|
|
44
|
-
if (typeof n != "function") return
|
|
45
|
-
let r = `${(e.apiUrl ?? "https://api.clients.ridel.dev").replace(/\/+$/, "")}${d}?key=${encodeURIComponent(t)}&t=${Date.now()}`, i = new AbortController(), a = setTimeout(() => i.abort(),
|
|
44
|
+
if (typeof n != "function") return O(e, /* @__PURE__ */ Error("[paywall] No hay implementación de fetch disponible."));
|
|
45
|
+
let r = `${(e.apiUrl ?? "https://api.clients.ridel.dev").replace(/\/+$/, "")}${d}?key=${encodeURIComponent(t)}&t=${Date.now()}`, i = new AbortController(), a = setTimeout(() => i.abort(), m);
|
|
46
46
|
try {
|
|
47
47
|
let a = await n(r, {
|
|
48
48
|
method: "GET",
|
|
@@ -51,21 +51,21 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
51
51
|
}), o = await a.json();
|
|
52
52
|
if (!a.ok) {
|
|
53
53
|
let t = o?.error ?? `HTTP ${a.status}`;
|
|
54
|
-
return
|
|
54
|
+
return O(e, /* @__PURE__ */ Error(`[paywall] El servidor rechazó la licencia: ${t}`));
|
|
55
55
|
}
|
|
56
|
-
return
|
|
56
|
+
return E(o) ? (S(t, o, e), {
|
|
57
57
|
payload: o,
|
|
58
58
|
source: "network",
|
|
59
59
|
isStale: !1,
|
|
60
60
|
error: null
|
|
61
|
-
}) :
|
|
61
|
+
}) : O(e, /* @__PURE__ */ Error("[paywall] Respuesta con un formato que esta versión del SDK no entiende. Actualiza el paquete."));
|
|
62
62
|
} catch (t) {
|
|
63
|
-
return
|
|
63
|
+
return O(e, ee(t));
|
|
64
64
|
} finally {
|
|
65
65
|
clearTimeout(a);
|
|
66
66
|
}
|
|
67
|
-
},
|
|
68
|
-
let n = e.licenseKey ?
|
|
67
|
+
}, O = (e, t) => {
|
|
68
|
+
let n = e.licenseKey ? C(e.licenseKey, e) : null;
|
|
69
69
|
return n ? {
|
|
70
70
|
payload: n.payload,
|
|
71
71
|
source: "cache",
|
|
@@ -77,9 +77,13 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
77
77
|
isStale: !1,
|
|
78
78
|
error: t
|
|
79
79
|
};
|
|
80
|
-
},
|
|
80
|
+
}, ee = (e) => e instanceof Error ? e : /* @__PURE__ */ Error(`[paywall] Fallo al consultar la licencia: ${String(e)}`), k = e(null), A = (e) => e ? new Date(e) : null, j = /* @__PURE__ */ new Map(), M = (e) => {
|
|
81
|
+
if (e.failures > 0) return Math.min(g, h * 2 ** Math.min(e.failures, 5));
|
|
82
|
+
let t = e.configuredMs ?? 9e5;
|
|
83
|
+
return e.serverHintSeconds ? Math.min(t, e.serverHintSeconds * 1e3) : t;
|
|
84
|
+
}, N = t(({ config: e, children: t }) => {
|
|
81
85
|
let [r, c] = s(() => {
|
|
82
|
-
let t =
|
|
86
|
+
let t = _() ? C(e.licenseKey, e) : null;
|
|
83
87
|
return {
|
|
84
88
|
payload: t?.payload ?? null,
|
|
85
89
|
source: t ? "cache" : "none",
|
|
@@ -87,14 +91,14 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
87
91
|
isLoading: !0,
|
|
88
92
|
error: null
|
|
89
93
|
};
|
|
90
|
-
}), u = o(0), d = o(null), f = o(e.onStatusChange);
|
|
91
|
-
|
|
92
|
-
let
|
|
93
|
-
if (!
|
|
94
|
+
}), u = o(0), d = o(null), p = o(f), m = o(e.onStatusChange);
|
|
95
|
+
m.current = e.onStatusChange;
|
|
96
|
+
let h = n(async () => {
|
|
97
|
+
if (!_()) return;
|
|
94
98
|
d.current?.abort();
|
|
95
99
|
let t = new AbortController();
|
|
96
100
|
d.current = t;
|
|
97
|
-
let n = `${e.apiUrl ?? ""}|${e.licenseKey}`, r =
|
|
101
|
+
let n = `${e.apiUrl ?? ""}|${e.licenseKey}`, r = j.get(n), i = r ?? D({
|
|
98
102
|
licenseKey: e.licenseKey,
|
|
99
103
|
apiUrl: e.apiUrl,
|
|
100
104
|
storage: e.storage,
|
|
@@ -102,11 +106,15 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
102
106
|
fetchImpl: e.fetchImpl,
|
|
103
107
|
signal: t.signal
|
|
104
108
|
});
|
|
105
|
-
r ||
|
|
109
|
+
r || j.set(n, i);
|
|
106
110
|
try {
|
|
107
111
|
let n = await i;
|
|
108
112
|
if (t.signal.aborted) return;
|
|
109
|
-
u.current = n.source === "network" ? 0 : u.current + 1,
|
|
113
|
+
u.current = n.source === "network" ? 0 : u.current + 1, p.current = M({
|
|
114
|
+
failures: u.current,
|
|
115
|
+
serverHintSeconds: n.payload?.refreshAfterSeconds,
|
|
116
|
+
configuredMs: e.refreshIntervalMs
|
|
117
|
+
}), c({
|
|
110
118
|
payload: n.payload,
|
|
111
119
|
source: n.source,
|
|
112
120
|
isStale: n.isStale,
|
|
@@ -114,7 +122,7 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
114
122
|
error: n.error
|
|
115
123
|
}), e.debug && console.info("[paywall]", n.source, n.payload?.status, n.error);
|
|
116
124
|
} finally {
|
|
117
|
-
r ||
|
|
125
|
+
r || j.delete(n);
|
|
118
126
|
}
|
|
119
127
|
}, [
|
|
120
128
|
e.apiUrl,
|
|
@@ -122,29 +130,29 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
122
130
|
e.storage,
|
|
123
131
|
e.maxCacheAgeMs,
|
|
124
132
|
e.fetchImpl,
|
|
125
|
-
e.debug
|
|
133
|
+
e.debug,
|
|
134
|
+
e.refreshIntervalMs
|
|
126
135
|
]);
|
|
127
136
|
i(() => {
|
|
128
|
-
if (!
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
if (!_()) return;
|
|
138
|
+
let e = !1, t, n = () => {
|
|
139
|
+
e || (t = setTimeout(() => {
|
|
140
|
+
h().then(n);
|
|
141
|
+
}, p.current));
|
|
133
142
|
};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!g()) return;
|
|
143
|
+
return h().then(n), () => {
|
|
144
|
+
e = !0, t && clearTimeout(t), d.current?.abort();
|
|
145
|
+
};
|
|
146
|
+
}, [h]), i(() => {
|
|
147
|
+
if (!_()) return;
|
|
140
148
|
let e = () => {
|
|
141
|
-
document.visibilityState === "visible" &&
|
|
149
|
+
document.visibilityState === "visible" && h();
|
|
142
150
|
};
|
|
143
151
|
return document.addEventListener("visibilitychange", e), window.addEventListener("online", e), () => {
|
|
144
152
|
document.removeEventListener("visibilitychange", e), window.removeEventListener("online", e);
|
|
145
153
|
};
|
|
146
|
-
}, [
|
|
147
|
-
let
|
|
154
|
+
}, [h]);
|
|
155
|
+
let g = a(() => {
|
|
148
156
|
let e = r.payload;
|
|
149
157
|
return {
|
|
150
158
|
state: e?.status ?? "OK",
|
|
@@ -153,39 +161,39 @@ var d = "/api/public/license/v1/status", f = "cpl:state:", p = 1e4, m = 1e3, h =
|
|
|
153
161
|
isLoading: r.isLoading,
|
|
154
162
|
isStale: r.isStale,
|
|
155
163
|
source: r.source,
|
|
156
|
-
graceUntil:
|
|
157
|
-
nextTransitionAt:
|
|
164
|
+
graceUntil: A(e?.graceUntil),
|
|
165
|
+
nextTransitionAt: A(e?.nextTransitionAt),
|
|
158
166
|
message: e?.message ?? null,
|
|
159
167
|
contactUrl: e?.contactUrl ?? null,
|
|
160
168
|
payload: e,
|
|
161
169
|
error: r.error,
|
|
162
|
-
refresh: () => void
|
|
170
|
+
refresh: () => void h()
|
|
163
171
|
};
|
|
164
|
-
}, [r,
|
|
172
|
+
}, [r, h]);
|
|
165
173
|
i(() => {
|
|
166
|
-
|
|
167
|
-
}, [
|
|
174
|
+
m.current?.(g);
|
|
175
|
+
}, [g]);
|
|
168
176
|
let v = a(() => ({
|
|
169
|
-
status:
|
|
177
|
+
status: g,
|
|
170
178
|
config: e
|
|
171
|
-
}), [
|
|
172
|
-
return /* @__PURE__ */ l(
|
|
179
|
+
}), [g, e]);
|
|
180
|
+
return /* @__PURE__ */ l(k.Provider, {
|
|
173
181
|
value: v,
|
|
174
182
|
children: t
|
|
175
183
|
});
|
|
176
184
|
});
|
|
177
|
-
|
|
185
|
+
N.displayName = "PaywallProvider";
|
|
178
186
|
//#endregion
|
|
179
187
|
//#region src/core/resolveGateDecision.ts
|
|
180
|
-
var
|
|
188
|
+
var te = ["BLOCKED"], P = {
|
|
181
189
|
OK: "allow",
|
|
182
190
|
WARNING: "warn",
|
|
183
191
|
GRACE: "grace",
|
|
184
192
|
BLOCKED: "grace"
|
|
185
|
-
},
|
|
186
|
-
let n =
|
|
187
|
-
return e === "OK" ? "allow" : (t.blockOn ??
|
|
188
|
-
},
|
|
193
|
+
}, F = (e, t) => {
|
|
194
|
+
let n = P[e] ?? "allow";
|
|
195
|
+
return e === "OK" ? "allow" : (t.blockOn ?? te).includes(e) ? "block" : n;
|
|
196
|
+
}, I = "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif", L = {
|
|
189
197
|
position: "fixed",
|
|
190
198
|
inset: 0,
|
|
191
199
|
zIndex: 2147483647,
|
|
@@ -195,10 +203,10 @@ var M = ["BLOCKED"], N = {
|
|
|
195
203
|
padding: "24px",
|
|
196
204
|
background: "rgba(9, 9, 11, 0.92)",
|
|
197
205
|
backdropFilter: "blur(6px)",
|
|
198
|
-
fontFamily:
|
|
206
|
+
fontFamily: I,
|
|
199
207
|
color: "#fafafa",
|
|
200
208
|
overflowY: "auto"
|
|
201
|
-
},
|
|
209
|
+
}, R = {
|
|
202
210
|
width: "100%",
|
|
203
211
|
maxWidth: "440px",
|
|
204
212
|
background: "#18181b",
|
|
@@ -207,7 +215,7 @@ var M = ["BLOCKED"], N = {
|
|
|
207
215
|
padding: "32px",
|
|
208
216
|
boxShadow: "0 24px 48px rgba(0, 0, 0, 0.45)",
|
|
209
217
|
textAlign: "center"
|
|
210
|
-
},
|
|
218
|
+
}, z = {
|
|
211
219
|
display: "inline-flex",
|
|
212
220
|
alignItems: "center",
|
|
213
221
|
justifyContent: "center",
|
|
@@ -218,17 +226,17 @@ var M = ["BLOCKED"], N = {
|
|
|
218
226
|
color: "#f87171",
|
|
219
227
|
fontSize: "24px",
|
|
220
228
|
marginBottom: "20px"
|
|
221
|
-
},
|
|
229
|
+
}, B = {
|
|
222
230
|
margin: "0 0 12px",
|
|
223
231
|
fontSize: "20px",
|
|
224
232
|
fontWeight: 600,
|
|
225
233
|
lineHeight: 1.3
|
|
226
|
-
},
|
|
234
|
+
}, V = {
|
|
227
235
|
margin: "0 0 24px",
|
|
228
236
|
fontSize: "15px",
|
|
229
237
|
lineHeight: 1.55,
|
|
230
238
|
color: "#a1a1aa"
|
|
231
|
-
},
|
|
239
|
+
}, H = {
|
|
232
240
|
display: "inline-block",
|
|
233
241
|
padding: "11px 22px",
|
|
234
242
|
borderRadius: "10px",
|
|
@@ -237,11 +245,11 @@ var M = ["BLOCKED"], N = {
|
|
|
237
245
|
fontSize: "14px",
|
|
238
246
|
fontWeight: 600,
|
|
239
247
|
textDecoration: "none"
|
|
240
|
-
},
|
|
248
|
+
}, U = {
|
|
241
249
|
margin: "20px 0 0",
|
|
242
250
|
fontSize: "12px",
|
|
243
251
|
color: "#71717a"
|
|
244
|
-
},
|
|
252
|
+
}, W = {
|
|
245
253
|
position: "sticky",
|
|
246
254
|
top: 0,
|
|
247
255
|
zIndex: 2147483e3,
|
|
@@ -249,26 +257,26 @@ var M = ["BLOCKED"], N = {
|
|
|
249
257
|
alignItems: "center",
|
|
250
258
|
gap: "12px",
|
|
251
259
|
padding: "10px 16px",
|
|
252
|
-
fontFamily:
|
|
260
|
+
fontFamily: I,
|
|
253
261
|
fontSize: "14px",
|
|
254
262
|
lineHeight: 1.4,
|
|
255
263
|
boxSizing: "border-box"
|
|
256
|
-
},
|
|
257
|
-
...
|
|
264
|
+
}, G = {
|
|
265
|
+
...W,
|
|
258
266
|
background: "#fef3c7",
|
|
259
267
|
color: "#78350f",
|
|
260
268
|
borderBottom: "1px solid #fcd34d"
|
|
261
|
-
},
|
|
262
|
-
...
|
|
269
|
+
}, K = {
|
|
270
|
+
...W,
|
|
263
271
|
background: "#fee2e2",
|
|
264
272
|
color: "#7f1d1d",
|
|
265
273
|
borderBottom: "1px solid #fca5a5"
|
|
266
|
-
},
|
|
274
|
+
}, q = {
|
|
267
275
|
color: "inherit",
|
|
268
276
|
fontWeight: 600,
|
|
269
277
|
textDecoration: "underline",
|
|
270
278
|
whiteSpace: "nowrap"
|
|
271
|
-
},
|
|
279
|
+
}, J = {
|
|
272
280
|
marginLeft: "auto",
|
|
273
281
|
padding: "2px 8px",
|
|
274
282
|
border: "none",
|
|
@@ -278,46 +286,46 @@ var M = ["BLOCKED"], N = {
|
|
|
278
286
|
fontSize: "18px",
|
|
279
287
|
lineHeight: 1,
|
|
280
288
|
cursor: "pointer"
|
|
281
|
-
},
|
|
289
|
+
}, Y = {
|
|
282
290
|
flex: 1,
|
|
283
291
|
minWidth: 0
|
|
284
|
-
},
|
|
292
|
+
}, X = "Este servicio está temporalmente suspendido. Ponte en contacto con el proveedor para restablecerlo.", Z = t(({ status: e, config: t }) => {
|
|
285
293
|
let n = a(() => t.productName ? `${t.productName} no está disponible` : "Servicio no disponible", [t.productName]);
|
|
286
294
|
return /* @__PURE__ */ l("div", {
|
|
287
|
-
style:
|
|
295
|
+
style: L,
|
|
288
296
|
role: "alertdialog",
|
|
289
297
|
"aria-modal": "true",
|
|
290
298
|
children: /* @__PURE__ */ u("div", {
|
|
291
|
-
style:
|
|
299
|
+
style: R,
|
|
292
300
|
children: [
|
|
293
301
|
/* @__PURE__ */ l("div", {
|
|
294
|
-
style:
|
|
302
|
+
style: z,
|
|
295
303
|
"aria-hidden": "true",
|
|
296
304
|
children: "⚠"
|
|
297
305
|
}),
|
|
298
306
|
/* @__PURE__ */ l("h1", {
|
|
299
|
-
style:
|
|
307
|
+
style: B,
|
|
300
308
|
children: n
|
|
301
309
|
}),
|
|
302
310
|
/* @__PURE__ */ l("p", {
|
|
303
|
-
style:
|
|
304
|
-
children: e.message ??
|
|
311
|
+
style: V,
|
|
312
|
+
children: e.message ?? X
|
|
305
313
|
}),
|
|
306
314
|
e.contactUrl ? /* @__PURE__ */ l("a", {
|
|
307
|
-
style:
|
|
315
|
+
style: H,
|
|
308
316
|
href: e.contactUrl,
|
|
309
317
|
target: "_blank",
|
|
310
318
|
rel: "noreferrer noopener",
|
|
311
319
|
children: "Resolver ahora"
|
|
312
320
|
}) : null,
|
|
313
321
|
t.contactEmail ? /* @__PURE__ */ u("p", {
|
|
314
|
-
style:
|
|
322
|
+
style: U,
|
|
315
323
|
children: [
|
|
316
324
|
"¿Dudas? Escribe a",
|
|
317
325
|
" ",
|
|
318
326
|
/* @__PURE__ */ l("a", {
|
|
319
327
|
href: `mailto:${t.contactEmail}`,
|
|
320
|
-
style:
|
|
328
|
+
style: q,
|
|
321
329
|
children: t.contactEmail
|
|
322
330
|
})
|
|
323
331
|
]
|
|
@@ -326,10 +334,10 @@ var M = ["BLOCKED"], N = {
|
|
|
326
334
|
})
|
|
327
335
|
});
|
|
328
336
|
});
|
|
329
|
-
|
|
337
|
+
Z.displayName = "BlockedOverlay";
|
|
330
338
|
//#endregion
|
|
331
339
|
//#region src/react/ui/StatusBanner.tsx
|
|
332
|
-
var
|
|
340
|
+
var ne = (e) => {
|
|
333
341
|
if (!e) return null;
|
|
334
342
|
let t = e.getTime() - Date.now();
|
|
335
343
|
if (t <= 0) return null;
|
|
@@ -338,13 +346,13 @@ var Z = (e) => {
|
|
|
338
346
|
let r = Math.max(1, Math.floor(t / 36e5));
|
|
339
347
|
return r === 1 ? "1 hora" : `${r} horas`;
|
|
340
348
|
}, Q = t(({ status: e, variant: t }) => {
|
|
341
|
-
let [n, r] = s(!1), i = a(() =>
|
|
349
|
+
let [n, r] = s(!1), i = a(() => ne(e.graceUntil), [e.graceUntil]);
|
|
342
350
|
return n ? null : /* @__PURE__ */ u("div", {
|
|
343
|
-
style: t === "grace" ?
|
|
351
|
+
style: t === "grace" ? K : G,
|
|
344
352
|
role: "status",
|
|
345
353
|
children: [
|
|
346
354
|
/* @__PURE__ */ u("span", {
|
|
347
|
-
style:
|
|
355
|
+
style: Y,
|
|
348
356
|
children: [e.message, i ? /* @__PURE__ */ u("strong", { children: [
|
|
349
357
|
" El servicio se suspenderá en ",
|
|
350
358
|
i,
|
|
@@ -352,7 +360,7 @@ var Z = (e) => {
|
|
|
352
360
|
] }) : null]
|
|
353
361
|
}),
|
|
354
362
|
e.contactUrl ? /* @__PURE__ */ l("a", {
|
|
355
|
-
style:
|
|
363
|
+
style: q,
|
|
356
364
|
href: e.contactUrl,
|
|
357
365
|
target: "_blank",
|
|
358
366
|
rel: "noreferrer noopener",
|
|
@@ -360,7 +368,7 @@ var Z = (e) => {
|
|
|
360
368
|
}) : null,
|
|
361
369
|
t === "warning" ? /* @__PURE__ */ l("button", {
|
|
362
370
|
type: "button",
|
|
363
|
-
style:
|
|
371
|
+
style: J,
|
|
364
372
|
onClick: () => r(!0),
|
|
365
373
|
"aria-label": "Descartar aviso",
|
|
366
374
|
children: "×"
|
|
@@ -372,10 +380,10 @@ Q.displayName = "StatusBanner";
|
|
|
372
380
|
//#endregion
|
|
373
381
|
//#region src/react/PaywallGate.tsx
|
|
374
382
|
var $ = t(({ children: e, renderBlocked: t, renderWarning: n, renderGrace: i, showBanners: o = !0 }) => {
|
|
375
|
-
let s = r(
|
|
383
|
+
let s = r(k);
|
|
376
384
|
if (!s) throw Error("[paywall] <PaywallGate> debe usarse dentro de <PaywallProvider>.");
|
|
377
|
-
let { status: d, config: f } = s, p = a(() =>
|
|
378
|
-
if (p === "block") return /* @__PURE__ */ l(c, { children: t ? t(d) : /* @__PURE__ */ l(
|
|
385
|
+
let { status: d, config: f } = s, p = a(() => F(d.state, f), [d.state, f]);
|
|
386
|
+
if (p === "block") return /* @__PURE__ */ l(c, { children: t ? t(d) : /* @__PURE__ */ l(Z, {
|
|
379
387
|
status: d,
|
|
380
388
|
config: f
|
|
381
389
|
}) });
|
|
@@ -391,10 +399,10 @@ var $ = t(({ children: e, renderBlocked: t, renderWarning: n, renderGrace: i, sh
|
|
|
391
399
|
$.displayName = "PaywallGate";
|
|
392
400
|
//#endregion
|
|
393
401
|
//#region src/react/useLicenseStatus.ts
|
|
394
|
-
var
|
|
395
|
-
let e = r(
|
|
402
|
+
var re = () => {
|
|
403
|
+
let e = r(k);
|
|
396
404
|
if (!e) throw Error("[paywall] useLicenseStatus debe usarse dentro de <PaywallProvider>.");
|
|
397
405
|
return e.status;
|
|
398
406
|
};
|
|
399
407
|
//#endregion
|
|
400
|
-
export { $ as PaywallGate,
|
|
408
|
+
export { $ as PaywallGate, N as PaywallProvider, w as clearLicenseCache, re as useLicenseStatus };
|