@eiaifactory/monitor 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -81,7 +81,7 @@ Instalado, captura solo los errores no manejados (`error`) y las promesas rechaz
81
81
  ## Edge functions (Supabase)
82
82
 
83
83
  ```ts
84
- import { initMonitor, withHandler } from 'npm:@eiaifactory/monitor@0.1.0/deno';
84
+ import { initMonitor, marcarFalla, withHandler } from 'npm:@eiaifactory/monitor@0.1.3/deno';
85
85
 
86
86
  const monitor = initMonitor({
87
87
  clave: Deno.env.get('MONITOR_CLAVE'),
@@ -109,6 +109,28 @@ respuestas 5xx (como `cobrar: respondió 503`, **sin el cuerpo**: puede traer da
109
109
  la función contestando que no, y no se reportan. Lo que se capture a mano con
110
110
  `monitor.capturar(error)` durante el request también sale al final.
111
111
 
112
+ **Qué paso falló (0.1.3).** Una 5xx que la función arma a mano puede decir en qué paso falló y con
113
+ qué código, sin que el body ni un header lo lleven. El body trae el mensaje crudo de Postgres, y un
114
+ header llegaría al navegador. Va fuera de banda:
115
+
116
+ ```ts
117
+ if (rpc.error) {
118
+ return marcarFalla(
119
+ new Response(JSON.stringify({ error: 'db_error' }), { status: 500, headers: corsHeaders }),
120
+ { paso: 'stock_loss_apply', codigo: rpc.error.code },
121
+ );
122
+ }
123
+ ```
124
+
125
+ `marcarFalla` devuelve la misma respuesta sin tocarla. `withHandler` reporta
126
+ `cobrar: respondió 500 en stock_loss_apply (57014)` con `codigo: '57014'`.
127
+ - El paso es un identificador fijo del código: la RPC, la tabla o el servicio de afuera
128
+ (`[A-Za-z_][\w.]*`, lo que finance lee del mensaje).
129
+ - El código es un SQLSTATE o un `PGRSTnnn`.
130
+ - Lo que no tenga esa forma se descarta y queda el mensaje de siempre, así nunca viaja un valor.
131
+ - Sin código, `codigo` es el status. Un `PT4xx` queda sólo en el mensaje: el usuario vio un 5xx, y
132
+ como `codigo` lo descartaría como rechazo de negocio.
133
+
112
134
  | Opción de `withHandler` | |
113
135
  |---|---|
114
136
  | `esEsperado(error)` | Además de los esperados de siempre. |
@@ -131,7 +153,7 @@ Decisión del spec: los rechazos de negocio y los `toast.error` no son incidente
131
153
 
132
154
  | | Navegador | Servidor |
133
155
  |---|---|---|
134
- | Colapso | El mismo error en 10 s viaja una vez, con `repeticiones` | — |
156
+ | Colapso | El mismo error, en el mismo lugar (`ubicacion`) y con el mismo `codigo`, en 10 s viaja una vez, con `repeticiones` | — |
135
157
  | Tope | 20 eventos nuevos por minuto (`maxPorMinuto`) | — |
136
158
  | Ráfaga | Hasta 100 errores esperando a ser procesados; lo que llega con eso lleno se pierde | — |
137
159
  | Cola | 50 (`maxBuffer`) en `localStorage`, hasta 128 KB; descarta lo más viejo | 50 en memoria |
package/dist/browser.js CHANGED
@@ -1,2 +1,2 @@
1
1
  function ue(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return t>>>0}function N(e){let t=(e.stack??"").split(`
2
- `).find(n=>/^\s*at\s|@/.test(n))??"";return ue([e.origen,e.tipo_error??"",e.mensaje,t.trim()].join("|"))}function I(){return{items:[],minuto:0,enMinuto:0}}function L(e,t,n,r,i){let o=N(t),l=e.items.find(v=>!v.v&&v.h===o&&n-v.t<1e4);if(l)return l.e.repeticiones=(l.e.repeticiones??1)+1,"colapsado";let P=Math.floor(n/6e4);return P!==e.minuto&&(e.minuto=P,e.enMinuto=0),e.enMinuto>=i?"tope_por_minuto":(e.enMinuto++,e.items.push({e:t,h:o,t:n}),e.items.length>r&&e.items.splice(0,e.items.length-r),"encolado")}var le=128*1024;function V(e){let t=e.map(({e:i,h:o,t:l})=>JSON.stringify({e:i,h:o,t:l})),n=2,r=t.length;for(;r>0&&(n+t[r-1].length+1)*2<=le;)n+=t[--r].length+1;return`[${t.slice(r).join(",")}]`}function U(e){try{let t=e?JSON.parse(e):[];return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&!!n.e&&typeof n.e.id_externo=="string"&&typeof n.e.mensaje=="string"&&typeof n.h=="number"&&typeof n.t=="number"):[]}catch{return[]}}var z="https://qoyskdjcjyjxcnhruhni.supabase.co/functions/v1/monitor-ingesta";var s={mensaje:1024,tipo_error:128,codigo:64,stack:8192,ubicacion:256,ruta:512,release:64,usuario_id:64,rol:64,dispositivo_id:64,user_agent:512,huella_manual:128},D="\u2026[cortado]";function g(e,t){return e.length<=t?e:e.slice(0,t-D.length)+D}var k=[1e3,2e3,4e3,3e4],X=e=>new TextEncoder().encode(e).length;function B(e,t=20){let n=[],r=X('{"eventos":[]}');for(let i of e){if(n.length>=t)break;let o=X(JSON.stringify(i.e))+(n.length?1:0);if(n.length&&r+o>61440)break;r+=o,n.push(i)}return n}function de(e){return 6e4-e%6e4}function fe(e,t,n){if(e>=200&&e<300)return{r:"ok"};if(e===429){let r=Number(t);return{r:"cuota",esperaMs:t&&r>0?r*1e3:de(n)}}return e===401?{r:"apagar"}:e===413?{r:"partir"}:e>=500?{r:"reintentar"}:{r:"descartar"}}async function $(e){let t;try{let n=typeof AbortController=="function"?new AbortController:void 0,r=new Promise(o=>{t=setTimeout(()=>{e.debug?.("timeout"),n?.abort(),o({r:"reintentar"})},e.timeoutMs)}),i=(async()=>{try{let o=await fetch(e.url,{method:"POST",headers:{authorization:`Bearer ${e.clave}`,"content-type":"application/json"},body:JSON.stringify({eventos:e.eventos}),keepalive:!!e.keepalive,signal:n?.signal}),l=await o.text();return e.debug?.(o.status,l),fe(o.status,o.headers.get("retry-after"),Date.now())}catch(o){return e.debug?.("sin respuesta",o),{r:"reintentar"}}})();return await Promise.race([i,r])}catch{return{r:"reintentar"}}finally{clearTimeout(t)}}function pe(e){return e.status??e.statusCode??e.context?.status}function J(e){try{if(!e||typeof e!="object")return!1;let t=e;if(t.name==="AbortError"||typeof t.code=="string"&&t.code.startsWith("PT4"))return!0;let n=Number(pe(t));return n===401||n===403?!0:typeof t.name=="string"&&t.name.startsWith("Auth")&&n>=400&&n<500}catch{return!1}}function q(e,t){return t.some(n=>typeof n=="string"?e.includes(n):e.search(n)>=0)}function m(){let e=typeof crypto>"u"?void 0:crypto;if(e&&typeof e.randomUUID=="function")try{return e.randomUUID()}catch{}let t=new Uint8Array(16);if(e&&typeof e.getRandomValues=="function")e.getRandomValues(t);else for(let r=0;r<16;r++)t[r]=Math.random()*256|0;t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var x="[redactado]",ge=/mail|tel|phone|dni|cuit|pass|token|secret|key|clave/i,me=/[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,xe=/(^|[^\w.-])\+?\d(?:[\s-]?\d){7,14}(?![\w.-])/g;function R(e){return e.replace(me,x).replace(xe,`$1${x}`)}function K(e){return e===void 0?e:R(e)}function Y(e){let t;if(e.contexto){t={};for(let n of Object.keys(e.contexto)){let r=e.contexto[n];t[n]=ge.test(n)?x:typeof r=="string"?R(r):r}}return{...e,mensaje:R(e.mensaje),stack:K(e.stack),ruta:K(e.ruta),usuario_id:e.usuario_id&&e.usuario_id.includes("@")?x:e.usuario_id,contexto:t}}function u(e,t){if((typeof e=="number"||typeof e=="boolean")&&(e=String(e)),typeof e!="string")return;let n=e.trim();return n?g(n,t):void 0}function be(e){let t=Object.prototype.toString.call(e);if(t==="[object Object]"||t==="[object Array]")try{let n=JSON.stringify(e);if(n&&n!=="{}")return n}catch{}return t}function Ee(e){try{if(e===null||typeof e!="object"&&typeof e!="function")return{mensaje:u(String(e),s.mensaje)??"Error sin mensaje"};let t=e,n=t.context,r=typeof t.code=="string"&&("details"in t||"hint"in t);return{mensaje:u(t.message,s.mensaje)??g(be(t),s.mensaje),tipo_error:u(t.name,s.tipo_error)??(r?"PostgrestError":void 0),codigo:u(t.code??t.status??t.statusCode??n?.status,s.codigo),stack:u(t.stack,s.stack)}}catch{return{mensaje:"Error no legible"}}}function _e(e){if(!e||typeof e!="object")return;let t={};for(let r of Object.keys(e)){let i=e[r];i===null||typeof i=="boolean"?t[r]=i:typeof i=="number"?t[r]=Number.isFinite(i)?i:null:typeof i=="string"&&(t[r]=g(i,256))}let n=Object.keys(t);if(n.length){for(;n.length&&new TextEncoder().encode(JSON.stringify(t)).length>2048;)delete t[n.pop()],t._cortado=!0;return t}}function he(e){return u(e?.split(/[?#]/)[0],s.ruta)}function F(e){return Y({id_externo:m(),origen:e.origen,...Ee(e.error),ocurrido_at:new Date(e.ocurrido).toISOString(),ubicacion:u(e.ubicacion,s.ubicacion),ruta:he(e.ruta),release:u(e.release,s.release),usuario_id:u(e.usuario_id,s.usuario_id),rol:u(e.rol,s.rol),dispositivo_id:u(e.dispositivo_id,s.dispositivo_id),user_agent:u(e.user_agent,s.user_agent),contexto:_e(e.contexto),repeticiones:1})}var ee="eiai_monitor_cola",Z="eiai_monitor_dispositivo",te=2e3,ne=10,ve=3e3,Oe=100,a,b=[],c,f,T={},M,y=!1,w=!1,d,G=0,W,E=!1,H=0,C=0,S=20;function p(...e){try{a?.debug&&console.debug("[monitor]",...e)}catch{}}function Q(e){try{return localStorage.getItem(e)}catch{return null}}function re(e,t){try{localStorage.setItem(e,t)}catch{}}function Fe(e){try{if(a)return;if(typeof window>"u"||!e?.clave){e?.debug&&console.debug("[monitor] no-op: sin clave");return}a={clave:e.clave,url:e.url??z,release:e.release,ignorar:e.ignorar??[],maxBuffer:e.maxBuffer??50,maxPorMinuto:e.maxPorMinuto??20,debug:!!e.debug},addEventListener("error",ke),addEventListener("unhandledrejection",Re),addEventListener("pagehide",ie),document.addEventListener("visibilitychange",Se),ae(),p("iniciado",a.url)}catch{}}function h(e,t){try{if(!a||E||y||b.length>=Oe)return;b.push({error:e,extra:t,t:Date.now(),ruta:location.pathname,contexto:T,usuario:M}),ae()}catch{}}function Ze(e){try{T={...T,...e}}catch{}}function Ge(e){M=e}function We(){M=void 0}async function He(){try{if(!a)return;for(_();;){let e=await j();if(!e||e.r!=="ok"&&e.r!=="descartar"&&e.r!=="partir")return}}catch{}}function Qe(e,t){try{h(e,{ubicacion:oe(t?.queryKey)})}catch{}}function et(e,t,n,r){try{h(e,{ubicacion:oe(r?.options?.mutationKey)})}catch{}}function oe(e){return Array.isArray(e)&&typeof e[0]=="string"?e[0]:void 0}function ke(e){try{h(e.error??{message:e.message,stack:e.filename?`at ${e.filename}:${e.lineno}:${e.colno}`:void 0})}catch{}}function Re(e){try{h(e.reason)}catch{}}function ie(){try{_(),clearTimeout(d),d=void 0,j(!0)}catch{}}function Se(){document.visibilityState==="hidden"&&ie()}function ae(){w||(w=!0,typeof requestIdleCallback=="function"?requestIdleCallback(_,{timeout:1e3}):setTimeout(_,0))}function Te(){return c||(c=I(),c.items=U(Q(ee)),f=Q(Z)??void 0,f||(f=m(),re(Z,f)),c.items.length&&p("recuperados",c.items.length),c)}function se(){c&&re(ee,V(c.items))}function _(){if(w=!1,!(!a||y)){y=!0;try{let e=Te(),t=b;b=[];let n=0;for(let r of t)try{if(J(r.error))continue;let i=F({error:r.error,origen:"browser",ocurrido:r.t,ruta:r.extra?.ruta??r.ruta,ubicacion:r.extra?.ubicacion,release:a.release,usuario_id:r.usuario?.id,rol:r.usuario?.rol,dispositivo_id:f,user_agent:navigator.userAgent,contexto:{...r.contexto,...r.extra?.contexto}});if(q(i.mensaje,a.ignorar))continue;let o=L(e,i,r.t,a.maxBuffer,a.maxPorMinuto);p(o,i.mensaje),o!=="tope_por_minuto"&&n++}catch{}n&&se(),e.items.length&&A(e.items.length>=ne?0:te)}catch{}finally{y=!1}}}function A(e){if(E)return;let t=Math.max(Date.now()+e,C);if(d!==void 0){if(t>=G)return;clearTimeout(d)}G=t,d=setTimeout(()=>{d=void 0,j()},t-Date.now())}function j(e=!1){return W??(W=we(e).catch(()=>{}).finally(()=>{W=void 0}))}async function we(e){try{if(!a||!c||E)return;let t=c;if(Date.now()<C){A(0);return}let n=B(t.items,S);if(!n.length)return;for(let o of n)o.v=!0;let r=await $({url:a.url,clave:a.clave,eventos:n.map(o=>o.e),timeoutMs:ve,keepalive:e,debug:a.debug?p:void 0});for(let o of n)o.v=!1;let i=()=>{t.items=t.items.filter(o=>!n.includes(o))};return r.r==="ok"||r.r==="descartar"||r.r==="partir"&&n.length===1?(i(),r.r==="ok"&&(H=0),S=20):r.r==="partir"?S=n.length>>1:r.r==="apagar"?(E=!0,clearTimeout(d),p("clave rechazada: apagado")):C=Date.now()+(r.r==="cuota"?r.esperaMs:k[Math.min(H++,k.length-1)]),se(),t.items.length&&A(t.items.length>=ne||r.r!=="ok"&&r.r!=="descartar"?0:te),r}catch{return}}export{h as capturar,J as esEsperado,He as flush,Fe as initMonitor,We as limpiarUsuario,et as onErrorMutation,Qe as onErrorQuery,Ze as setContext,Ge as setUsuario};
2
+ `).find(n=>/^\s*at\s|@/.test(n))??"";return ue([e.origen,e.tipo_error??"",e.mensaje,t.trim(),e.ubicacion??"",e.codigo??""].join("|"))}function I(){return{items:[],minuto:0,enMinuto:0}}function L(e,t,n,r,i){let o=N(t),l=e.items.find(v=>!v.v&&v.h===o&&n-v.t<1e4);if(l)return l.e.repeticiones=(l.e.repeticiones??1)+1,"colapsado";let P=Math.floor(n/6e4);return P!==e.minuto&&(e.minuto=P,e.enMinuto=0),e.enMinuto>=i?"tope_por_minuto":(e.enMinuto++,e.items.push({e:t,h:o,t:n}),e.items.length>r&&e.items.splice(0,e.items.length-r),"encolado")}var le=128*1024;function V(e){let t=e.map(({e:i,h:o,t:l})=>JSON.stringify({e:i,h:o,t:l})),n=2,r=t.length;for(;r>0&&(n+t[r-1].length+1)*2<=le;)n+=t[--r].length+1;return`[${t.slice(r).join(",")}]`}function U(e){try{let t=e?JSON.parse(e):[];return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&!!n.e&&typeof n.e.id_externo=="string"&&typeof n.e.mensaje=="string"&&typeof n.h=="number"&&typeof n.t=="number"):[]}catch{return[]}}var z="https://qoyskdjcjyjxcnhruhni.supabase.co/functions/v1/monitor-ingesta";var s={mensaje:1024,tipo_error:128,codigo:64,stack:8192,ubicacion:256,ruta:512,release:64,usuario_id:64,rol:64,dispositivo_id:64,user_agent:512,huella_manual:128},D="\u2026[cortado]";function g(e,t){return e.length<=t?e:e.slice(0,t-D.length)+D}var k=[1e3,2e3,4e3,3e4],X=e=>new TextEncoder().encode(e).length;function B(e,t=20){let n=[],r=X('{"eventos":[]}');for(let i of e){if(n.length>=t)break;let o=X(JSON.stringify(i.e))+(n.length?1:0);if(n.length&&r+o>61440)break;r+=o,n.push(i)}return n}function de(e){return 6e4-e%6e4}function fe(e,t,n){if(e>=200&&e<300)return{r:"ok"};if(e===429){let r=Number(t);return{r:"cuota",esperaMs:t&&r>0?r*1e3:de(n)}}return e===401?{r:"apagar"}:e===413?{r:"partir"}:e>=500?{r:"reintentar"}:{r:"descartar"}}async function $(e){let t;try{let n=typeof AbortController=="function"?new AbortController:void 0,r=new Promise(o=>{t=setTimeout(()=>{e.debug?.("timeout"),n?.abort(),o({r:"reintentar"})},e.timeoutMs)}),i=(async()=>{try{let o=await fetch(e.url,{method:"POST",headers:{authorization:`Bearer ${e.clave}`,"content-type":"application/json"},body:JSON.stringify({eventos:e.eventos}),keepalive:!!e.keepalive,signal:n?.signal}),l=await o.text();return e.debug?.(o.status,l),fe(o.status,o.headers.get("retry-after"),Date.now())}catch(o){return e.debug?.("sin respuesta",o),{r:"reintentar"}}})();return await Promise.race([i,r])}catch{return{r:"reintentar"}}finally{clearTimeout(t)}}function pe(e){return e.status??e.statusCode??e.context?.status}function J(e){try{if(!e||typeof e!="object")return!1;let t=e;if(t.name==="AbortError"||typeof t.code=="string"&&t.code.startsWith("PT4"))return!0;let n=Number(pe(t));return n===401||n===403?!0:typeof t.name=="string"&&t.name.startsWith("Auth")&&n>=400&&n<500}catch{return!1}}function q(e,t){return t.some(n=>typeof n=="string"?e.includes(n):e.search(n)>=0)}function m(){let e=typeof crypto>"u"?void 0:crypto;if(e&&typeof e.randomUUID=="function")try{return e.randomUUID()}catch{}let t=new Uint8Array(16);if(e&&typeof e.getRandomValues=="function")e.getRandomValues(t);else for(let r=0;r<16;r++)t[r]=Math.random()*256|0;t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var x="[redactado]",ge=/mail|tel|phone|dni|cuit|pass|token|secret|key|clave/i,me=/[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,xe=/(^|[^\w.-])\+?\d(?:[\s-]?\d){7,14}(?![\w.-])/g;function R(e){return e.replace(me,x).replace(xe,`$1${x}`)}function K(e){return e===void 0?e:R(e)}function Y(e){let t;if(e.contexto){t={};for(let n of Object.keys(e.contexto)){let r=e.contexto[n];t[n]=ge.test(n)?x:typeof r=="string"?R(r):r}}return{...e,mensaje:R(e.mensaje),stack:K(e.stack),ruta:K(e.ruta),usuario_id:e.usuario_id&&e.usuario_id.includes("@")?x:e.usuario_id,contexto:t}}function u(e,t){if((typeof e=="number"||typeof e=="boolean")&&(e=String(e)),typeof e!="string")return;let n=e.trim();return n?g(n,t):void 0}function be(e){let t=Object.prototype.toString.call(e);if(t==="[object Object]"||t==="[object Array]")try{let n=JSON.stringify(e);if(n&&n!=="{}")return n}catch{}return t}function Ee(e){try{if(e===null||typeof e!="object"&&typeof e!="function")return{mensaje:u(String(e),s.mensaje)??"Error sin mensaje"};let t=e,n=t.context,r=typeof t.code=="string"&&("details"in t||"hint"in t);return{mensaje:u(t.message,s.mensaje)??g(be(t),s.mensaje),tipo_error:u(t.name,s.tipo_error)??(r?"PostgrestError":void 0),codigo:u(t.code??t.status??t.statusCode??n?.status,s.codigo),stack:u(t.stack,s.stack)}}catch{return{mensaje:"Error no legible"}}}function _e(e){if(!e||typeof e!="object")return;let t={};for(let r of Object.keys(e)){let i=e[r];i===null||typeof i=="boolean"?t[r]=i:typeof i=="number"?t[r]=Number.isFinite(i)?i:null:typeof i=="string"&&(t[r]=g(i,256))}let n=Object.keys(t);if(n.length){for(;n.length&&new TextEncoder().encode(JSON.stringify(t)).length>2048;)delete t[n.pop()],t._cortado=!0;return t}}function he(e){return u(e?.split(/[?#]/)[0],s.ruta)}function F(e){return Y({id_externo:m(),origen:e.origen,...Ee(e.error),ocurrido_at:new Date(e.ocurrido).toISOString(),ubicacion:u(e.ubicacion,s.ubicacion),ruta:he(e.ruta),release:u(e.release,s.release),usuario_id:u(e.usuario_id,s.usuario_id),rol:u(e.rol,s.rol),dispositivo_id:u(e.dispositivo_id,s.dispositivo_id),user_agent:u(e.user_agent,s.user_agent),contexto:_e(e.contexto),repeticiones:1})}var ee="eiai_monitor_cola",Z="eiai_monitor_dispositivo",te=2e3,ne=10,ve=3e3,Oe=100,a,b=[],c,f,T={},M,y=!1,w=!1,d,G=0,W,E=!1,H=0,C=0,S=20;function p(...e){try{a?.debug&&console.debug("[monitor]",...e)}catch{}}function Q(e){try{return localStorage.getItem(e)}catch{return null}}function re(e,t){try{localStorage.setItem(e,t)}catch{}}function Fe(e){try{if(a)return;if(typeof window>"u"||!e?.clave){e?.debug&&console.debug("[monitor] no-op: sin clave");return}a={clave:e.clave,url:e.url??z,release:e.release,ignorar:e.ignorar??[],maxBuffer:e.maxBuffer??50,maxPorMinuto:e.maxPorMinuto??20,debug:!!e.debug},addEventListener("error",ke),addEventListener("unhandledrejection",Re),addEventListener("pagehide",ie),document.addEventListener("visibilitychange",Se),ae(),p("iniciado",a.url)}catch{}}function h(e,t){try{if(!a||E||y||b.length>=Oe)return;b.push({error:e,extra:t,t:Date.now(),ruta:location.pathname,contexto:T,usuario:M}),ae()}catch{}}function Ze(e){try{T={...T,...e}}catch{}}function Ge(e){M=e}function We(){M=void 0}async function He(){try{if(!a)return;for(_();;){let e=await j();if(!e||e.r!=="ok"&&e.r!=="descartar"&&e.r!=="partir")return}}catch{}}function Qe(e,t){try{h(e,{ubicacion:oe(t?.queryKey)})}catch{}}function et(e,t,n,r){try{h(e,{ubicacion:oe(r?.options?.mutationKey)})}catch{}}function oe(e){return Array.isArray(e)&&typeof e[0]=="string"?e[0]:void 0}function ke(e){try{h(e.error??{message:e.message,stack:e.filename?`at ${e.filename}:${e.lineno}:${e.colno}`:void 0})}catch{}}function Re(e){try{h(e.reason)}catch{}}function ie(){try{_(),clearTimeout(d),d=void 0,j(!0)}catch{}}function Se(){document.visibilityState==="hidden"&&ie()}function ae(){w||(w=!0,typeof requestIdleCallback=="function"?requestIdleCallback(_,{timeout:1e3}):setTimeout(_,0))}function Te(){return c||(c=I(),c.items=U(Q(ee)),f=Q(Z)??void 0,f||(f=m(),re(Z,f)),c.items.length&&p("recuperados",c.items.length),c)}function se(){c&&re(ee,V(c.items))}function _(){if(w=!1,!(!a||y)){y=!0;try{let e=Te(),t=b;b=[];let n=0;for(let r of t)try{if(J(r.error))continue;let i=F({error:r.error,origen:"browser",ocurrido:r.t,ruta:r.extra?.ruta??r.ruta,ubicacion:r.extra?.ubicacion,release:a.release,usuario_id:r.usuario?.id,rol:r.usuario?.rol,dispositivo_id:f,user_agent:navigator.userAgent,contexto:{...r.contexto,...r.extra?.contexto}});if(q(i.mensaje,a.ignorar))continue;let o=L(e,i,r.t,a.maxBuffer,a.maxPorMinuto);p(o,i.mensaje),o!=="tope_por_minuto"&&n++}catch{}n&&se(),e.items.length&&A(e.items.length>=ne?0:te)}catch{}finally{y=!1}}}function A(e){if(E)return;let t=Math.max(Date.now()+e,C);if(d!==void 0){if(t>=G)return;clearTimeout(d)}G=t,d=setTimeout(()=>{d=void 0,j()},t-Date.now())}function j(e=!1){return W??(W=we(e).catch(()=>{}).finally(()=>{W=void 0}))}async function we(e){try{if(!a||!c||E)return;let t=c;if(Date.now()<C){A(0);return}let n=B(t.items,S);if(!n.length)return;for(let o of n)o.v=!0;let r=await $({url:a.url,clave:a.clave,eventos:n.map(o=>o.e),timeoutMs:ve,keepalive:e,debug:a.debug?p:void 0});for(let o of n)o.v=!1;let i=()=>{t.items=t.items.filter(o=>!n.includes(o))};return r.r==="ok"||r.r==="descartar"||r.r==="partir"&&n.length===1?(i(),r.r==="ok"&&(H=0),S=20):r.r==="partir"?S=n.length>>1:r.r==="apagar"?(E=!0,clearTimeout(d),p("clave rechazada: apagado")):C=Date.now()+(r.r==="cuota"?r.esperaMs:k[Math.min(H++,k.length-1)]),se(),t.items.length&&A(t.items.length>=ne||r.r!=="ok"&&r.r!=="descartar"?0:te),r}catch{return}}export{h as capturar,J as esEsperado,He as flush,Fe as initMonitor,We as limpiarUsuario,et as onErrorMutation,Qe as onErrorQuery,Ze as setContext,Ge as setUsuario};
package/dist/deno.d.ts CHANGED
@@ -30,12 +30,26 @@ interface OpcionesHandler {
30
30
  /** La respuesta cuando la función lanza (con los headers de CORS de la función). Default: 500 JSON. */
31
31
  aRespuesta?: (error: unknown) => Response;
32
32
  }
33
+ /** Qué paso de la función falló y con qué código: lo lee `withHandler` al reportar una 5xx. */
34
+ interface Falla {
35
+ /** Un identificador fijo del código: la RPC, la tabla o el servicio de afuera. Nunca un valor. */
36
+ paso: string;
37
+ /** El SQLSTATE (`57014`) o el código de PostgREST (`PGRST202`). */
38
+ codigo?: string;
39
+ }
40
+ /**
41
+ * Anota en qué paso falló una respuesta 5xx, sin tocarla: devuelve la misma instancia, con el mismo
42
+ * body y los mismos headers. Lo que no tiene forma de identificador o de código se descarta al
43
+ * reportar. Nunca lanza.
44
+ */
45
+ declare function marcarFalla(respuesta: Response, falla: Falla): Response;
33
46
  /**
34
47
  * Envuelve el handler de `Deno.serve`. Si responde, devuelve esa respuesta tal cual y reporta
35
- * las 5xx (sin el cuerpo: puede traer datos). Si lanza, lo reporta y devuelve `aRespuesta(error)`
48
+ * las 5xx (sin el cuerpo: puede traer datos), con el paso de `marcarFalla` si lo tiene. Si lanza,
49
+ * lo reporta y devuelve `aRespuesta(error)`
36
50
  * o un 500 JSON. Al final manda todo lo capturado en el request, también lo que la función
37
51
  * capturó a mano antes de contestar un 200.
38
52
  */
39
53
  declare function withHandler<A extends unknown[]>(monitor: Monitor, handler: (req: Request, ...args: A) => Response | Promise<Response>, opciones?: OpcionesHandler): (req: Request, ...args: A) => Promise<Response>;
40
54
 
41
- export { type Extra, type Monitor, type OpcionesHandler, type OpcionesServidor, ValorContexto, initMonitor, withHandler };
55
+ export { type Extra, type Falla, type Monitor, type OpcionesHandler, type OpcionesServidor, ValorContexto, initMonitor, marcarFalla, withHandler };
package/dist/deno.js CHANGED
@@ -1 +1 @@
1
- var E="https://qoyskdjcjyjxcnhruhni.supabase.co/functions/v1/monitor-ingesta";var s={mensaje:1024,tipo_error:128,codigo:64,stack:8192,ubicacion:256,ruta:512,release:64,usuario_id:64,rol:64,dispositivo_id:64,user_agent:512,huella_manual:128},m="\u2026[cortado]";function l(e,t){return e.length<=t?e:e.slice(0,t-m.length)+m}var x=e=>new TextEncoder().encode(e).length;function _(e,t=20){let n=[],r=x('{"eventos":[]}');for(let i of e){if(n.length>=t)break;let o=x(JSON.stringify(i.e))+(n.length?1:0);if(n.length&&r+o>61440)break;r+=o,n.push(i)}return n}function T(e){return 6e4-e%6e4}function S(e,t,n){if(e>=200&&e<300)return{r:"ok"};if(e===429){let r=Number(t);return{r:"cuota",esperaMs:t&&r>0?r*1e3:T(n)}}return e===401?{r:"apagar"}:e===413?{r:"partir"}:e>=500?{r:"reintentar"}:{r:"descartar"}}async function b(e){let t;try{let n=typeof AbortController=="function"?new AbortController:void 0,r=new Promise(o=>{t=setTimeout(()=>{e.debug?.("timeout"),n?.abort(),o({r:"reintentar"})},e.timeoutMs)}),i=(async()=>{try{let o=await fetch(e.url,{method:"POST",headers:{authorization:`Bearer ${e.clave}`,"content-type":"application/json"},body:JSON.stringify({eventos:e.eventos}),keepalive:!!e.keepalive,signal:n?.signal}),a=await o.text();return e.debug?.(o.status,a),S(o.status,o.headers.get("retry-after"),Date.now())}catch(o){return e.debug?.("sin respuesta",o),{r:"reintentar"}}})();return await Promise.race([i,r])}catch{return{r:"reintentar"}}finally{clearTimeout(t)}}function j(e){return e.status??e.statusCode??e.context?.status}function h(e){try{if(!e||typeof e!="object")return!1;let t=e;if(t.name==="AbortError"||typeof t.code=="string"&&t.code.startsWith("PT4"))return!0;let n=Number(j(t));return n===401||n===403?!0:typeof t.name=="string"&&t.name.startsWith("Auth")&&n>=400&&n<500}catch{return!1}}function R(){let e=typeof crypto>"u"?void 0:crypto;if(e&&typeof e.randomUUID=="function")try{return e.randomUUID()}catch{}let t=new Uint8Array(16);if(e&&typeof e.getRandomValues=="function")e.getRandomValues(t);else for(let r=0;r<16;r++)t[r]=Math.random()*256|0;t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var p="[redactado]",A=/mail|tel|phone|dni|cuit|pass|token|secret|key|clave/i,M=/[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,N=/(^|[^\w.-])\+?\d(?:[\s-]?\d){7,14}(?![\w.-])/g;function g(e){return e.replace(M,p).replace(N,`$1${p}`)}function O(e){return e===void 0?e:g(e)}function v(e){let t;if(e.contexto){t={};for(let n of Object.keys(e.contexto)){let r=e.contexto[n];t[n]=A.test(n)?p:typeof r=="string"?g(r):r}}return{...e,mensaje:g(e.mensaje),stack:O(e.stack),ruta:O(e.ruta),usuario_id:e.usuario_id&&e.usuario_id.includes("@")?p:e.usuario_id,contexto:t}}function u(e,t){if((typeof e=="number"||typeof e=="boolean")&&(e=String(e)),typeof e!="string")return;let n=e.trim();return n?l(n,t):void 0}function C(e){let t=Object.prototype.toString.call(e);if(t==="[object Object]"||t==="[object Array]")try{let n=JSON.stringify(e);if(n&&n!=="{}")return n}catch{}return t}function V(e){try{if(e===null||typeof e!="object"&&typeof e!="function")return{mensaje:u(String(e),s.mensaje)??"Error sin mensaje"};let t=e,n=t.context,r=typeof t.code=="string"&&("details"in t||"hint"in t);return{mensaje:u(t.message,s.mensaje)??l(C(t),s.mensaje),tipo_error:u(t.name,s.tipo_error)??(r?"PostgrestError":void 0),codigo:u(t.code??t.status??t.statusCode??n?.status,s.codigo),stack:u(t.stack,s.stack)}}catch{return{mensaje:"Error no legible"}}}function L(e){if(!e||typeof e!="object")return;let t={};for(let r of Object.keys(e)){let i=e[r];i===null||typeof i=="boolean"?t[r]=i:typeof i=="number"?t[r]=Number.isFinite(i)?i:null:typeof i=="string"&&(t[r]=l(i,256))}let n=Object.keys(t);if(n.length){for(;n.length&&new TextEncoder().encode(JSON.stringify(t)).length>2048;)delete t[n.pop()],t._cortado=!0;return t}}function I(e){return u(e?.split(/[?#]/)[0],s.ruta)}function w(e){return v({id_externo:R(),origen:e.origen,...V(e.error),ocurrido_at:new Date(e.ocurrido).toISOString(),ubicacion:u(e.ubicacion,s.ubicacion),ruta:I(e.ruta),release:u(e.release,s.release),usuario_id:u(e.usuario_id,s.usuario_id),rol:u(e.rol,s.rol),dispositivo_id:u(e.dispositivo_id,s.dispositivo_id),user_agent:u(e.user_agent,s.user_agent),contexto:L(e.contexto),repeticiones:1})}var X=2e3,U=50;function D(e){return new Promise(t=>setTimeout(t,e))}function re(e){let t=[],n=!e.clave,r,i=a=>b({url:e.url??E,clave:e.clave??"",eventos:a,timeoutMs:X});async function o(){let a=20;for(;t.length&&!n;){let c=_(t.map(f=>({e:f})),a).map(f=>f.e),d=await i(c);if((d.r==="reintentar"||d.r==="cuota")&&(await D(d.r==="cuota"?Math.min(d.esperaMs,1e4):1e3),d=await i(c)),d.r==="apagar"){n=!0,t.length=0;return}if(d.r==="partir"&&c.length>1){a=c.length>>1;continue}t.splice(0,c.length)}}return{funcion:e.funcion,capturar(a,c){try{if(n||h(a))return;t.length>=U&&t.shift(),t.push(w({error:a,origen:e.origen??"edge",ocurrido:Date.now(),ruta:c?.ruta,ubicacion:c?.ubicacion??e.funcion,release:e.release,contexto:c?.contexto}))}catch{}},flush(){return r??(r=o().catch(()=>{}).finally(()=>{r=void 0}))}}}function $(e){let t=e.flush();try{globalThis.EdgeRuntime?.waitUntil(t)}catch{}}function k(e){try{return new URL(e.url).pathname}catch{return}}function z(e,t){try{if(t.aRespuesta)return t.aRespuesta(e)}catch{}return new Response(JSON.stringify({error:"error_interno"}),{status:500,headers:{"content-type":"application/json"}})}function oe(e,t,n={}){return async(r,...i)=>{try{let o=await t(r,...i);return o.status>=500&&e.capturar({name:"HttpError",message:`${e.funcion}: respondi\xF3 ${o.status}`,status:o.status},{ruta:k(r)}),o}catch(o){let a=!1;try{a=!!n.esEsperado?.(o)}catch{}return a||e.capturar(o,{ruta:k(r)}),z(o,n)}finally{$(e)}}}export{h as esEsperado,re as initMonitor,oe as withHandler};
1
+ var x="https://qoyskdjcjyjxcnhruhni.supabase.co/functions/v1/monitor-ingesta";var s={mensaje:1024,tipo_error:128,codigo:64,stack:8192,ubicacion:256,ruta:512,release:64,usuario_id:64,rol:64,dispositivo_id:64,user_agent:512,huella_manual:128},m="\u2026[cortado]";function p(t,e){return t.length<=e?t:t.slice(0,e-m.length)+m}var E=t=>new TextEncoder().encode(t).length;function _(t,e=20){let n=[],r=E('{"eventos":[]}');for(let o of t){if(n.length>=e)break;let i=E(JSON.stringify(o.e))+(n.length?1:0);if(n.length&&r+i>61440)break;r+=i,n.push(o)}return n}function S(t){return 6e4-t%6e4}function A(t,e,n){if(t>=200&&t<300)return{r:"ok"};if(t===429){let r=Number(e);return{r:"cuota",esperaMs:e&&r>0?r*1e3:S(n)}}return t===401?{r:"apagar"}:t===413?{r:"partir"}:t>=500?{r:"reintentar"}:{r:"descartar"}}async function b(t){let e;try{let n=typeof AbortController=="function"?new AbortController:void 0,r=new Promise(i=>{e=setTimeout(()=>{t.debug?.("timeout"),n?.abort(),i({r:"reintentar"})},t.timeoutMs)}),o=(async()=>{try{let i=await fetch(t.url,{method:"POST",headers:{authorization:`Bearer ${t.clave}`,"content-type":"application/json"},body:JSON.stringify({eventos:t.eventos}),keepalive:!!t.keepalive,signal:n?.signal}),a=await i.text();return t.debug?.(i.status,a),A(i.status,i.headers.get("retry-after"),Date.now())}catch(i){return t.debug?.("sin respuesta",i),{r:"reintentar"}}})();return await Promise.race([o,r])}catch{return{r:"reintentar"}}finally{clearTimeout(e)}}function j(t){return t.status??t.statusCode??t.context?.status}function h(t){try{if(!t||typeof t!="object")return!1;let e=t;if(e.name==="AbortError"||typeof e.code=="string"&&e.code.startsWith("PT4"))return!0;let n=Number(j(e));return n===401||n===403?!0:typeof e.name=="string"&&e.name.startsWith("Auth")&&n>=400&&n<500}catch{return!1}}function R(){let t=typeof crypto>"u"?void 0:crypto;if(t&&typeof t.randomUUID=="function")try{return t.randomUUID()}catch{}let e=new Uint8Array(16);if(t&&typeof t.getRandomValues=="function")t.getRandomValues(e);else for(let r=0;r<16;r++)e[r]=Math.random()*256|0;e[6]=e[6]&15|64,e[8]=e[8]&63|128;let n=Array.from(e,r=>r.toString(16).padStart(2,"0")).join("");return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var l="[redactado]",P=/mail|tel|phone|dni|cuit|pass|token|secret|key|clave/i,M=/[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,N=/(^|[^\w.-])\+?\d(?:[\s-]?\d){7,14}(?![\w.-])/g;function g(t){return t.replace(M,l).replace(N,`$1${l}`)}function O(t){return t===void 0?t:g(t)}function w(t){let e;if(t.contexto){e={};for(let n of Object.keys(t.contexto)){let r=t.contexto[n];e[n]=P.test(n)?l:typeof r=="string"?g(r):r}}return{...t,mensaje:g(t.mensaje),stack:O(t.stack),ruta:O(t.ruta),usuario_id:t.usuario_id&&t.usuario_id.includes("@")?l:t.usuario_id,contexto:e}}function u(t,e){if((typeof t=="number"||typeof t=="boolean")&&(t=String(t)),typeof t!="string")return;let n=t.trim();return n?p(n,e):void 0}function V(t){let e=Object.prototype.toString.call(t);if(e==="[object Object]"||e==="[object Array]")try{let n=JSON.stringify(t);if(n&&n!=="{}")return n}catch{}return e}function $(t){try{if(t===null||typeof t!="object"&&typeof t!="function")return{mensaje:u(String(t),s.mensaje)??"Error sin mensaje"};let e=t,n=e.context,r=typeof e.code=="string"&&("details"in e||"hint"in e);return{mensaje:u(e.message,s.mensaje)??p(V(e),s.mensaje),tipo_error:u(e.name,s.tipo_error)??(r?"PostgrestError":void 0),codigo:u(e.code??e.status??e.statusCode??n?.status,s.codigo),stack:u(e.stack,s.stack)}}catch{return{mensaje:"Error no legible"}}}function I(t){if(!t||typeof t!="object")return;let e={};for(let r of Object.keys(t)){let o=t[r];o===null||typeof o=="boolean"?e[r]=o:typeof o=="number"?e[r]=Number.isFinite(o)?o:null:typeof o=="string"&&(e[r]=p(o,256))}let n=Object.keys(e);if(n.length){for(;n.length&&new TextEncoder().encode(JSON.stringify(e)).length>2048;)delete e[n.pop()],e._cortado=!0;return e}}function L(t){return u(t?.split(/[?#]/)[0],s.ruta)}function v(t){return w({id_externo:R(),origen:t.origen,...$(t.error),ocurrido_at:new Date(t.ocurrido).toISOString(),ubicacion:u(t.ubicacion,s.ubicacion),ruta:L(t.ruta),release:u(t.release,s.release),usuario_id:u(t.usuario_id,s.usuario_id),rol:u(t.rol,s.rol),dispositivo_id:u(t.dispositivo_id,s.dispositivo_id),user_agent:u(t.user_agent,s.user_agent),contexto:I(t.contexto),repeticiones:1})}var X=2e3,U=50;function D(t){return new Promise(e=>setTimeout(e,t))}function at(t){let e=[],n=!t.clave,r,o=a=>b({url:t.url??x,clave:t.clave??"",eventos:a,timeoutMs:X});async function i(){let a=20;for(;e.length&&!n;){let c=_(e.map(f=>({e:f})),a).map(f=>f.e),d=await o(c);if((d.r==="reintentar"||d.r==="cuota")&&(await D(d.r==="cuota"?Math.min(d.esperaMs,1e4):1e3),d=await o(c)),d.r==="apagar"){n=!0,e.length=0;return}if(d.r==="partir"&&c.length>1){a=c.length>>1;continue}e.splice(0,c.length)}}return{funcion:t.funcion,capturar(a,c){try{if(n||h(a))return;e.length>=U&&e.shift(),e.push(v({error:a,origen:t.origen??"edge",ocurrido:Date.now(),ruta:c?.ruta,ubicacion:c?.ubicacion??t.funcion,release:t.release,contexto:c?.contexto}))}catch{}},flush(){return r??(r=i().catch(()=>{}).finally(()=>{r=void 0}))}}}var T=new WeakMap,z=/^[A-Za-z_][\w.]{0,63}$/,F=/^(?:[0-9A-Z]{5}|PGRST\d{3})$/;function ut(t,e){try{T.set(t,e)}catch{}return t}function B(t,e){let n=T.get(e),r=typeof n?.paso=="string"&&z.test(n.paso)?n.paso:void 0,o=typeof n?.codigo=="string"&&F.test(n.codigo)?n.codigo:void 0;return{name:"HttpError",message:`${t}: respondi\xF3 ${e.status}${r?` en ${r}`:""}${o?` (${o})`:""}`,status:e.status,code:o&&!o.startsWith("PT4")?o:void 0}}function G(t){let e=t.flush();try{globalThis.EdgeRuntime?.waitUntil(e)}catch{}}function k(t){try{return new URL(t.url).pathname}catch{return}}function q(t,e){try{if(e.aRespuesta)return e.aRespuesta(t)}catch{}return new Response(JSON.stringify({error:"error_interno"}),{status:500,headers:{"content-type":"application/json"}})}function ct(t,e,n={}){return async(r,...o)=>{try{let i=await e(r,...o);if(i.status>=500)try{t.capturar(B(t.funcion,i),{ruta:k(r)})}catch{}return i}catch(i){let a=!1;try{a=!!n.esEsperado?.(i)}catch{}return a||t.capturar(i,{ruta:k(r)}),q(i,n)}finally{G(t)}}}export{h as esEsperado,at as initMonitor,ut as marcarFalla,ct as withHandler};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eiaifactory/monitor",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Manda los errores de un sistema de cliente al Monitor de EI AI Factory, sin que el usuario final lo note.",
5
5
  "license": "MIT",
6
6
  "type": "module",