discovery-media-player 0.1.35 → 0.1.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discovery-media-player",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
5
5
  "keywords": [
6
6
  "pdf-viewer",
@@ -4,4 +4,4 @@
4
4
  // Injecté par api/doc.js dans un <script nonce=…> de la visionneuse. Le code navigateur du
5
5
  // player vit en modules TypeScript testés sous player/src/ — c'est ici qu'il atterrit.
6
6
  "use strict";
7
- module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var j=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var y=(e,n)=>{for(var t in n)j(e,t,{get:n[t],enumerable:!0})},xe=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Me(n))!we.call(e,o)&&o!==t&&j(e,o,{get:()=>n[o],enumerable:!(r=ve(n,o))||r.enumerable});return e};var ke=e=>xe(j({},\"__esModule\",{value:!0}),e);var gn={};y(gn,{bridge:()=>R,chat:()=>H,live:()=>$,presentation:()=>O,presentationState:()=>W,tracking:()=>L,viewer:()=>D});var R={};y(R,{onHostMessage:()=>Le,onPlayerMessage:()=>Re,parseHostMessage:()=>pe,parsePlayerMessage:()=>de,sendToHost:()=>Se,sendToPlayer:()=>Ae,toWire:()=>G});var K=\"3dd-doc-\",Te=/^[A-Za-z0-9_-]{1,64}$/,_e=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ee=[\"embed-denied\"],Ie=[\"present-invite\",\"present-handover\",\"present-switch\"],Pe=[\"handover-done\"];function G(e){let n={type:K+e.type};return\"slug\"in e&&e.slug&&(n.slug=e.slug),\"reason\"in e&&e.reason&&(n.reason=String(e.reason).slice(0,40)),n}function ce(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(K))return null;let o=r.slice(K.length);if(n.includes(o))return{type:o};if(Ee.includes(o)){let a=e.reason;return{type:o,reason:/^[a-z-]{1,40}$/.test(String(a||\"\"))?String(a):\"unknown\"}}if(!t.includes(o))return null;let s=e.slug;return typeof s!=\"string\"||!Te.test(s)?null:{type:o,slug:s}}function de(e){return ce(e,_e,Ie)}function pe(e){return ce(e,Pe,[])}function Se(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(G(e),\"*\")}catch(t){}}function Ae(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(G(n),\"*\")}catch(r){}}function Ce(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Re(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Ce(t,s.source))return;let a=de(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function Le(e,n){var s;let t=n||(typeof window!=\"undefined\"?window:null);if(!t)return()=>{};let r=(s=t.parent)!=null?s:null,o=a=>{if(!r||r===t||a.source!==r)return;let c=pe(a.data);c&&e(c)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var L={};y(L,{createTracker:()=>De});var $e=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function He(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(16));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}return Math.random().toString(36).slice(2)+Date.now().toString(36)}function Oe(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;o&&typeof o.sendBeacon==\"function\"?o.sendBeacon(e,new Blob([r],{type:\"application/json\"})):n.fetch(e,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:r,keepalive:!0})}catch(r){}}}function De(e={}){var ae,le;let n=e.win||(typeof window!=\"undefined\"?window:null),t=e.doc||n&&n.document,r=e.now||(()=>Date.now()),o=e.endpoint||\"/api/doc\",s=(ae=e.idleMs)!=null?ae:6e4,a=(le=e.sessionEveryMs)!=null?le:12e3,c=e.slug||\"\",d=e.internal||null,m=e.sessionId||He(),b=e.send||Oe(o,n),f={},h=0,M=0,u=0,v=0,g=null,S=r(),_=!1,N=!1,A=[],he=()=>!!c||!!d,te=i=>{if(he())try{b(i)}catch(l){}},re=()=>!!t&&t.visibilityState===\"visible\"&&(typeof t.hasFocus==\"function\"?t.hasFocus():!0)&&!_,oe=i=>{let l=r()-i,x=Math.max(0,S-i)+s;return Math.max(0,Math.min(l,x))},U=()=>{g!=null&&h>0&&(f[h]=(f[h]||0)+oe(g)/1e3),g=re()?r():null},F=()=>{U(),g=null},V=()=>{g==null&&re()&&(g=r())},z=()=>{let i=0;for(let l in f)i+=f[l];return g!=null&&(i+=oe(g)/1e3),Math.round(i)},ie=()=>{let i={};for(let l in f)i[l]=Math.round(f[l]);return i},E=()=>{S=r(),_&&(_=!1,V())},se=(i,l)=>{d||te({slug:c,event:i,page:l||M,maxPage:u,seconds:z(),sessionId:m})},C=()=>{U();let i={event:\"session\",sessionId:m,numPages:v,maxPage:u,totalSeconds:z(),pagesTime:ie()};d?(i.internal=!0,i.docId=d.docId,i.email=d.email,i.name=d.name,d.it&&(i.it=d.it)):i.slug=c,te(i)},w=(i,l,x,ue)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(l,x,ue),A.push(()=>{i.removeEventListener(l,x,ue)}))};return{sessionId:m,totalSeconds:z,maxPage:()=>u,pageTimes:ie,start(){if(N)return;N=!0,se(\"open\",1),h=1,g=r(),S=r(),w(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(F(),C()):(E(),V())}),w(n,\"blur\",F),w(n,\"focus\",()=>{E(),V()});for(let x of $e)w(n,x,E,{passive:!0});w(e.scrollElement,\"scroll\",E,{passive:!0}),w(n,\"beforeunload\",C);let i=n.setInterval(()=>{!_&&r()-S>s&&(_=!0,F())},5e3),l=n.setInterval(C,a);A.push(()=>{n.clearInterval(i),n.clearInterval(l)})},setPage(i){!i||i===M||(U(),h=i,M=i,i>u&&(u=i,se(\"page\",i)))},setPageCount(i){v=i||0},noteActivity:E,flush:C,stop(){for(;A.length;){let i=A.pop();try{i==null||i()}catch(l){}}N=!1}}}var $={};y($,{STORAGE_KEYS:()=>I,attendeeKey:()=>Fe,authorToken:()=>ze,avatarHtml:()=>B,canModerate:()=>q,createScheduler:()=>Be,escapeHtml:()=>p,flattenPresence:()=>We,formatMessageBody:()=>Z,initials:()=>fe,isMentioned:()=>je,isMine:()=>k,reactorId:()=>Y,shouldNotify:()=>Ke,unreadLabel:()=>Ge});var I={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function p(e){return String(e==null?\"\":e).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")}function fe(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function B(e,n){return e?`<img src=\"${p(e)}\" alt=\"\">`:p(fe(n))}function Z(e){let n=p(e);return n=n.replace(/(https?:\\/\\/[^\\s<]+)/g,t=>`<a href=\"${t}\" target=_blank rel=noreferrer class=cm-link>${t}</a>`),n=n.replace(/(^|\\s)@([\\p{L}0-9_'.-]+)/gu,(t,r,o)=>`${r}<span class=cm-mention>@${o}</span>`),n}function We(e){let n={},t=[];for(let r in e||{})for(let o of(e||{})[r]||[]){let s=String(o.uid||o.email||o.name||\"\").toLowerCase()||`_${r}_${t.length}`;n[s]?o.role===\"presenter\"&&(n[s]=o):(n[s]=o,t.push(s))}return t.map(r=>n[r])}function Ne(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function Ue(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function Fe(e,n,t=\"anon\",r=()=>Math.random().toString(36).slice(2,10)){if(e!=null&&e.email)return String(e.email).toLowerCase();try{if(!n)return`anon-${t}`;let o=n.getItem(I.attendeeKey);if(o)return o;let s=`anon-${r()}`;return n.setItem(I.attendeeKey,s),s}catch(o){return`anon-${t}`}}function Ve(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(24));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}try{console.warn(\"[player] crypto indisponible : jeton d'auteur affaibli\")}catch(n){}return Math.random().toString(36).slice(2)+Math.random().toString(36).slice(2)}function ze(e,n=Ve){let t=Ne(e,I.authorToken);if(t)return t;let r=n();return Ue(e,I.authorToken,r),r}function Y(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function k(e,n){return!e||!n?!1:e.author_email&&n.email?e.author_email===n.email:!e.author_email&&!n.email?e.author_name===n.name:!1}function q(e){return!!(e&&e.role===\"presenter\")}function je(e,n){if(!n||!n.name)return!1;let t=(n.name.split(/\\s+/)[0]||\"\").toLowerCase();return t?String(e&&e.body||\"\").toLowerCase().includes(`@${t}`)&&!k(e,n):!1}function Ke(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||k(n,t)?!1:o}function Ge(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function Be(e,n={}){var M;let t=(M=n.minMs)!=null?M:300,r=n.now||(()=>Date.now()),o=n.setTimer||((u,v)=>setTimeout(u,v)),s=n.clearTimer||(u=>clearTimeout(u)),a=!1,c=!1,d=-1/0,m=null,b=!1;function f(){if(b||a){c=!0;return}let u=t-(r()-d);if(u>0){h(u);return}a=!0,c=!1,d=r();let v=!1;e(()=>{v||(v=!0,a=!1,c&&!b&&f())})}function h(u){b||m!==null||(m=o(()=>{m=null,f()},Math.max(0,u)))}return{signaler(){b||f()},maintenant(){b||(d=-1/0,f())},arreter(){b=!0,m!==null&&(s(m),m=null)}}}var H={};y(H,{messageClassName:()=>Ye,renderActions:()=>be,renderAttachment:()=>ge,renderMessage:()=>Ze,renderReactions:()=>me});function me(e,n){let t=e&&e.reactions||{},r=Y(n),o=\"\";for(let s in t){let a=t[s]||[];if(!a.length)continue;let c=a.indexOf(r)>=0;o+=`<button class=\"re-chip${c?\" on\":\"\"}\" data-e=\"${p(s)}\">${p(s)} ${a.length}</button>`}return o}function ge(e){if(!e||!e.url)return\"\";let n=p(e.url);if(e.kind===\"image\")return`<a class=cm-att href=\"${n}\" target=_blank rel=noreferrer><img src=\"${n}\" alt=\"\" loading=lazy></a>`;let t=p(e.name||\"Document.pdf\");return`<a class=cm-att-pdf href=\"${n}\" target=_blank rel=noreferrer data-pdf=\"${n}\"><span class=cm-att-ph></span><span class=cm-pdflabel>${t}</span></a>`}function be(e,n){let t=k(e,n.me),r=`<button class=cm-react title=\"R\\xE9agir\">${n.reactIcon||\"\"}</button><button class=cm-reply title=\"R\\xE9pondre\">\\u21A9</button>`;return t&&(r+='<button class=cm-edit title=\"Modifier\">\\u270E</button>'),(t||q(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function Ze(e,n){if(e.deleted)return'<span class=a></span><span class=b><div class=\"txt cm-del\">Message supprim\\xE9</div></span>';let t=e.reply_to?`<div class=cm-q><b>${p(e.reply_name||\"\")}</b> ${p(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${Z(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${B(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${p(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+ge(e.attachment)+`<div class=cm-re>${me(e,n.me)}</div></span><span class=cm-act>${be(e,n)}</span>`}function Ye(e,n,t){return\"cm\"+(k(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var O={};y(O,{DEFAULT_CENTER:()=>Q,DEFAULT_ZOOM:()=>ee,MAP_TYPES:()=>P,cycleMapType:()=>qe,initialMapContent:()=>Je,mapTypeLabel:()=>Xe,sanitizeContent:()=>ne});var P=[\"roadmap\",\"satellite\",\"hybrid\"],Q=[46.6,2.5],ee=6;function T(e){let n=Number(e);return Number.isFinite(n)?n:null}function X(e){if(!Array.isArray(e))return null;let n=T(e[0]),t=T(e[1]);return n!=null&&t!=null?[n,t]:null}var J=(e,n,t)=>Math.max(n,Math.min(t,e));function ne(e){if(!e||typeof e!=\"object\")return null;let n=e;if(n.kind===\"pdf\"||n.kind==null)return null;if(n.kind===\"streetview\"){let t=X(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:T(r.heading)||0,pitch:J(T(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:J(T(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:X(n.center)||[...Q],zoom:J(Math.trunc(T(n.zoom)||ee),1,21),marker:X(n.marker),mapType:P.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function qe(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function Xe(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function Je(){return{kind:\"map\",center:[...Q],zoom:ee,marker:null,mapType:null,label:null}}var D={};y(D,{CURRENT_PAGE_MARGIN:()=>on,DEFAULT_ASPECT:()=>nn,MAX_ZOOM:()=>en,MIN_PAGE_WIDTH:()=>tn,MIN_ZOOM:()=>Qe,PRERENDER_MARGIN:()=>rn,arrowState:()=>ln,averageColor:()=>dn,clampPage:()=>ye,clampZoom:()=>sn,fitWidth:()=>cn,isImageDocument:()=>pn,progressPercent:()=>an});var Qe=.5,en=3,nn=1.35,tn=280,rn=\"500px 0px\",on=\"-48% 0px -48% 0px\";function sn(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function ye(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function an(e,n){return n?Math.round(ye(e,n)/n*100):0}function ln(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var un=28;function cn(e){var a;let n=(a=e.zoom)!=null?a:1,t=Math.max(280,e.containerWidth||900)*n;if(!e.onePage)return t;let r=e.aspect||1.35,s=((e.containerHeight||600)-un-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function dn(e){let n=e.length/4;if(!n)return\"rgb(0,0,0)\";let t=0,r=0,o=0;for(let s=0;s<e.length;s+=4)t+=e[s],r+=e[s+1],o+=e[s+2];return`rgb(${Math.round(t/n)},${Math.round(r/n)},${Math.round(o/n)})`}function pn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var W={};y(W,{presentationTransition:()=>fn,switchDocUrl:()=>mn});function fn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=ne(e.content);if(t)return[{kind:\"show-map\",content:t}];let r=[{kind:\"leave-map\"}];return e.file_url&&e.file_url!==n.docUrl?(r.push({kind:\"switch-doc\",url:e.file_url,name:e.file_name||void 0,title:e.doc_title||void 0,updatedAt:e.updated_at||void 0}),r):(e.current_page&&r.push({kind:\"show-page\",page:e.current_page}),r)}function mn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return ke(gn);})();", SOURCE_HASH: "8cba7313f9bb5ecc" };
7
+ module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var j=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var xe=Object.prototype.hasOwnProperty;var y=(e,n)=>{for(var t in n)j(e,t,{get:n[t],enumerable:!0})},we=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Me(n))!xe.call(e,o)&&o!==t&&j(e,o,{get:()=>n[o],enumerable:!(r=ve(n,o))||r.enumerable});return e};var Te=e=>we(j({},\"__esModule\",{value:!0}),e);var hn={};y(hn,{bridge:()=>C,chat:()=>O,live:()=>$,presentation:()=>H,presentationState:()=>U,tracking:()=>L,viewer:()=>N});var C={};y(C,{onHostMessage:()=>Le,onPlayerMessage:()=>Ce,parseHostMessage:()=>pe,parsePlayerMessage:()=>de,sendToHost:()=>Pe,sendToPlayer:()=>Ae,toWire:()=>K});var G=\"3dd-doc-\",ke=/^[A-Za-z0-9_-]{1,64}$/,_e=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Se=[\"embed-denied\"],Ee=[\"present-invite\",\"present-handover\",\"present-switch\"],Ie=[\"handover-done\"];function K(e){let n={type:G+e.type};return\"slug\"in e&&e.slug&&(n.slug=e.slug),\"reason\"in e&&e.reason&&(n.reason=String(e.reason).slice(0,40)),n}function ce(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(G))return null;let o=r.slice(G.length);if(n.includes(o))return{type:o};if(Se.includes(o)){let a=e.reason;return{type:o,reason:/^[a-z-]{1,40}$/.test(String(a||\"\"))?String(a):\"unknown\"}}if(!t.includes(o))return null;let s=e.slug;return typeof s!=\"string\"||!ke.test(s)?null:{type:o,slug:s}}function de(e){return ce(e,_e,Ee)}function pe(e){return ce(e,Ie,[])}function Pe(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(K(e),\"*\")}catch(t){}}function Ae(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(K(n),\"*\")}catch(r){}}function Re(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Ce(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Re(t,s.source))return;let a=de(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function Le(e,n){var s;let t=n||(typeof window!=\"undefined\"?window:null);if(!t)return()=>{};let r=(s=t.parent)!=null?s:null,o=a=>{if(!r||r===t||a.source!==r)return;let c=pe(a.data);c&&e(c)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var L={};y(L,{createTracker:()=>We});var $e=Math.ceil(300),Oe=25,Mn=$e*Oe;var Ne=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function Ue(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(16));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}return Math.random().toString(36).slice(2)+Date.now().toString(36)}function De(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;o&&typeof o.sendBeacon==\"function\"?o.sendBeacon(e,new Blob([r],{type:\"application/json\"})):n.fetch(e,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:r,keepalive:!0})}catch(r){}}}function We(e={}){var ae,le;let n=e.win||(typeof window!=\"undefined\"?window:null),t=e.doc||n&&n.document,r=e.now||(()=>Date.now()),o=e.endpoint||\"/api/doc\",s=(ae=e.idleMs)!=null?ae:6e4,a=(le=e.sessionEveryMs)!=null?le:12e3,c=e.slug||\"\",d=e.internal||null,m=e.sessionId||Ue(),b=e.send||De(o,n),f={},h=0,M=0,u=0,v=0,g=null,P=r(),_=!1,D=!1,A=[],he=()=>!!c||!!d,te=i=>{if(he())try{b(i)}catch(l){}},re=()=>!!t&&t.visibilityState===\"visible\"&&(typeof t.hasFocus==\"function\"?t.hasFocus():!0)&&!_,oe=i=>{let l=r()-i,w=Math.max(0,P-i)+s;return Math.max(0,Math.min(l,w))},W=()=>{g!=null&&h>0&&(f[h]=(f[h]||0)+oe(g)/1e3),g=re()?r():null},F=()=>{W(),g=null},V=()=>{g==null&&re()&&(g=r())},z=()=>{let i=0;for(let l in f)i+=f[l];return g!=null&&(i+=oe(g)/1e3),Math.round(i)},ie=()=>{let i={};for(let l in f)i[l]=Math.round(f[l]);return i},S=()=>{P=r(),_&&(_=!1,V())},se=(i,l)=>{d||te({slug:c,event:i,page:l||M,maxPage:u,seconds:z(),sessionId:m})},R=()=>{W();let i={event:\"session\",sessionId:m,numPages:v,maxPage:u,totalSeconds:z(),pagesTime:ie()};d?(i.internal=!0,i.docId=d.docId,i.email=d.email,i.name=d.name,d.it&&(i.it=d.it)):i.slug=c,te(i)},x=(i,l,w,ue)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(l,w,ue),A.push(()=>{i.removeEventListener(l,w,ue)}))};return{sessionId:m,totalSeconds:z,maxPage:()=>u,pageTimes:ie,start(){if(D)return;D=!0,se(\"open\",1),h=1,g=r(),P=r(),x(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(F(),R()):(S(),V())}),x(n,\"blur\",F),x(n,\"focus\",()=>{S(),V()});for(let w of Ne)x(n,w,S,{passive:!0});x(e.scrollElement,\"scroll\",S,{passive:!0}),x(n,\"beforeunload\",R);let i=n.setInterval(()=>{!_&&r()-P>s&&(_=!0,F())},5e3),l=n.setInterval(R,a);A.push(()=>{n.clearInterval(i),n.clearInterval(l)})},setPage(i){!i||i===M||(W(),h=i,M=i,i>u&&(u=i,se(\"page\",i)))},setPageCount(i){v=i||0},noteActivity:S,flush:R,stop(){for(;A.length;){let i=A.pop();try{i==null||i()}catch(l){}}D=!1}}}var $={};y($,{STORAGE_KEYS:()=>E,attendeeKey:()=>je,authorToken:()=>Ke,avatarHtml:()=>B,canModerate:()=>q,createScheduler:()=>qe,escapeHtml:()=>p,flattenPresence:()=>Fe,formatMessageBody:()=>Z,initials:()=>fe,isMentioned:()=>Be,isMine:()=>T,reactorId:()=>Y,shouldNotify:()=>Ze,unreadLabel:()=>Ye});var E={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\"};function p(e){return String(e==null?\"\":e).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")}function fe(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function B(e,n){return e?`<img src=\"${p(e)}\" alt=\"\">`:p(fe(n))}function Z(e){let n=p(e);return n=n.replace(/(https?:\\/\\/[^\\s<]+)/g,t=>`<a href=\"${t}\" target=_blank rel=noreferrer class=cm-link>${t}</a>`),n=n.replace(/(^|\\s)@([\\p{L}0-9_'.-]+)/gu,(t,r,o)=>`${r}<span class=cm-mention>@${o}</span>`),n}function Fe(e){let n={},t=[];for(let r in e||{})for(let o of(e||{})[r]||[]){let s=String(o.uid||o.email||o.name||\"\").toLowerCase()||`_${r}_${t.length}`;n[s]?o.role===\"presenter\"&&(n[s]=o):(n[s]=o,t.push(s))}return t.map(r=>n[r])}function Ve(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function ze(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function je(e,n,t=\"anon\",r=()=>Math.random().toString(36).slice(2,10)){if(e!=null&&e.email)return String(e.email).toLowerCase();try{if(!n)return`anon-${t}`;let o=n.getItem(E.attendeeKey);if(o)return o;let s=`anon-${r()}`;return n.setItem(E.attendeeKey,s),s}catch(o){return`anon-${t}`}}function Ge(){let e=typeof globalThis!=\"undefined\"?globalThis.crypto:void 0;if(e&&typeof e.randomUUID==\"function\")return e.randomUUID().replace(/-/g,\"\");if(e&&typeof e.getRandomValues==\"function\"){let n=e.getRandomValues(new Uint8Array(24));return Array.from(n,t=>t.toString(16).padStart(2,\"0\")).join(\"\")}try{console.warn(\"[player] crypto indisponible : jeton d'auteur affaibli\")}catch(n){}return Math.random().toString(36).slice(2)+Math.random().toString(36).slice(2)}function Ke(e,n=Ge){let t=Ve(e,E.authorToken);if(t)return t;let r=n();return ze(e,E.authorToken,r),r}function Y(e){return String(e&&(e.email||e.name)||\"\").toLowerCase()}function T(e,n){return!e||!n?!1:e.author_email&&n.email?e.author_email===n.email:!e.author_email&&!n.email?e.author_name===n.name:!1}function q(e){return!!(e&&e.role===\"presenter\")}function Be(e,n){if(!n||!n.name)return!1;let t=(n.name.split(/\\s+/)[0]||\"\").toLowerCase();return t?String(e&&e.body||\"\").toLowerCase().includes(`@${t}`)&&!T(e,n):!1}function Ze(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||T(n,t)?!1:o}function Ye(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function qe(e,n={}){var M;let t=(M=n.minMs)!=null?M:300,r=n.now||(()=>Date.now()),o=n.setTimer||((u,v)=>setTimeout(u,v)),s=n.clearTimer||(u=>clearTimeout(u)),a=!1,c=!1,d=-1/0,m=null,b=!1;function f(){if(b||a){c=!0;return}let u=t-(r()-d);if(u>0){h(u);return}a=!0,c=!1,d=r();let v=!1;e(()=>{v||(v=!0,a=!1,c&&!b&&f())})}function h(u){b||m!==null||(m=o(()=>{m=null,f()},Math.max(0,u)))}return{signaler(){b||f()},maintenant(){b||(d=-1/0,f())},arreter(){b=!0,m!==null&&(s(m),m=null)}}}var O={};y(O,{messageClassName:()=>Je,renderActions:()=>be,renderAttachment:()=>ge,renderMessage:()=>Xe,renderReactions:()=>me});function me(e,n){let t=e&&e.reactions||{},r=Y(n),o=\"\";for(let s in t){let a=t[s]||[];if(!a.length)continue;let c=a.indexOf(r)>=0;o+=`<button class=\"re-chip${c?\" on\":\"\"}\" data-e=\"${p(s)}\">${p(s)} ${a.length}</button>`}return o}function ge(e){if(!e||!e.url)return\"\";let n=p(e.url);if(e.kind===\"image\")return`<a class=cm-att href=\"${n}\" target=_blank rel=noreferrer><img src=\"${n}\" alt=\"\" loading=lazy></a>`;let t=p(e.name||\"Document.pdf\");return`<a class=cm-att-pdf href=\"${n}\" target=_blank rel=noreferrer data-pdf=\"${n}\"><span class=cm-att-ph></span><span class=cm-pdflabel>${t}</span></a>`}function be(e,n){let t=T(e,n.me),r=`<button class=cm-react title=\"R\\xE9agir\">${n.reactIcon||\"\"}</button><button class=cm-reply title=\"R\\xE9pondre\">\\u21A9</button>`;return t&&(r+='<button class=cm-edit title=\"Modifier\">\\u270E</button>'),(t||q(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function Xe(e,n){if(e.deleted)return'<span class=a></span><span class=b><div class=\"txt cm-del\">Message supprim\\xE9</div></span>';let t=e.reply_to?`<div class=cm-q><b>${p(e.reply_name||\"\")}</b> ${p(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${Z(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${B(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${p(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+ge(e.attachment)+`<div class=cm-re>${me(e,n.me)}</div></span><span class=cm-act>${be(e,n)}</span>`}function Je(e,n,t){return\"cm\"+(T(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var H={};y(H,{DEFAULT_CENTER:()=>Q,DEFAULT_ZOOM:()=>ee,MAP_TYPES:()=>I,cycleMapType:()=>Qe,initialMapContent:()=>nn,mapTypeLabel:()=>en,sanitizeContent:()=>ne});var I=[\"roadmap\",\"satellite\",\"hybrid\"],Q=[46.6,2.5],ee=6;function k(e){let n=Number(e);return Number.isFinite(n)?n:null}function X(e){if(!Array.isArray(e))return null;let n=k(e[0]),t=k(e[1]);return n!=null&&t!=null?[n,t]:null}var J=(e,n,t)=>Math.max(n,Math.min(t,e));function ne(e){if(!e||typeof e!=\"object\")return null;let n=e;if(n.kind===\"pdf\"||n.kind==null)return null;if(n.kind===\"streetview\"){let t=X(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:k(r.heading)||0,pitch:J(k(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:J(k(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:X(n.center)||[...Q],zoom:J(Math.trunc(k(n.zoom)||ee),1,21),marker:X(n.marker),mapType:I.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function Qe(e){let n=I.indexOf(e);return I[(n+1)%I.length]}function en(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function nn(){return{kind:\"map\",center:[...Q],zoom:ee,marker:null,mapType:null,label:null}}var N={};y(N,{CURRENT_PAGE_MARGIN:()=>ln,DEFAULT_ASPECT:()=>on,MAX_ZOOM:()=>rn,MIN_PAGE_WIDTH:()=>sn,MIN_ZOOM:()=>tn,PRERENDER_MARGIN:()=>an,arrowState:()=>dn,averageColor:()=>mn,clampPage:()=>ye,clampZoom:()=>un,fitWidth:()=>fn,isImageDocument:()=>gn,progressPercent:()=>cn});var tn=.5,rn=3,on=1.35,sn=280,an=\"500px 0px\",ln=\"-48% 0px -48% 0px\";function un(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function ye(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function cn(e,n){return n?Math.round(ye(e,n)/n*100):0}function dn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var pn=28;function fn(e){var a;let n=(a=e.zoom)!=null?a:1,t=Math.max(280,e.containerWidth||900)*n;if(!e.onePage)return t;let r=e.aspect||1.35,s=((e.containerHeight||600)-pn-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function mn(e){let n=e.length/4;if(!n)return\"rgb(0,0,0)\";let t=0,r=0,o=0;for(let s=0;s<e.length;s+=4)t+=e[s],r+=e[s+1],o+=e[s+2];return`rgb(${Math.round(t/n)},${Math.round(r/n)},${Math.round(o/n)})`}function gn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var U={};y(U,{presentationTransition:()=>bn,switchDocUrl:()=>yn});function bn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=ne(e.content);if(t)return[{kind:\"show-map\",content:t}];let r=[{kind:\"leave-map\"}];return e.file_url&&e.file_url!==n.docUrl?(r.push({kind:\"switch-doc\",url:e.file_url,name:e.file_name||void 0,title:e.doc_title||void 0,updatedAt:e.updated_at||void 0}),r):(e.current_page&&r.push({kind:\"show-page\",page:e.current_page}),r)}function yn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Te(hn);})();", SOURCE_HASH: "df6e7a255b95e364" };
package/server/handler.js CHANGED
@@ -6,6 +6,7 @@
6
6
  const crypto = require("crypto");
7
7
  const { getShareBySlug, logView, upsertSession, createReshare, sendReshareEmail, upsertInternalSession,
8
8
  createShare, revokeShare, setShareAuth, overview: docOverview, listSharesForDoc, listSessionsForDoc, internalStatsForDoc } = require("./shares");
9
+ const { SESSION_QUOTA_PER_HOUR } = require("./shared.generated.js");
9
10
  const { createPresentation, getPresentation, setPage, endPresentation, addMessage, listMessages, toggleReaction, editMessage, deleteMessage, setChatLock, createUploadUrl, reclaimPresentation, touchPresentation, listActivePresentations, handoverPresentation, endPresentationByOwner, recordAttendance, presentationStats, listPresentationsForDoc, switchPresentationDoc, setPresentationContent } = require("./presentations");
10
11
  // CONTEXTE INJECTÉ : tout ce que le player emprunte à l'application hôte passe par ici — stockage,
11
12
  // base, identité, limites, marque, journalisation — et rien d'autre. C'est la frontière qui permettra
@@ -3090,7 +3091,27 @@ async function handler(req, res) {
3090
3091
  // disant, avec le verrou fourni, est une transition.
3091
3092
  if (body.internal && body.event === "session") {
3092
3093
  const ipInt = ip0 || "anon";
3093
- if (!(await PLAYER.limits.allow(`intsess:${ipInt}`, 120, 3600))) {
3094
+ // ⚠️ LE QUOTA SE DÉDUIT DE LA CADENCE, IL NE S'ÉCRIT PLUS À LA MAIN.
3095
+ //
3096
+ // Il valait 120/h alors que le navigateur émet 300/h POUR UN SEUL LECTEUR : la limite ne
3097
+ // tenait pas 0,4 lecteur, et refusait tout après 24 minutes de lecture continue. La clé
3098
+ // étant l'adresse, une équipe derrière une sortie unique — le cas ordinaire d'une
3099
+ // entreprise — se partageait ce que même une personne dépasse.
3100
+ //
3101
+ // ⚠️ La garde était juste dans sa forme et fausse dans son chiffre : c'est pour ça que
3102
+ // personne ne l'a relue. On relit ce qui a l'air douteux, pas ce qui a l'air raisonnable.
3103
+ //
3104
+ // Détail et arithmétique : src/cadence.ts. Trouvé par le second hôte.
3105
+ if (!(await PLAYER.limits.allow(`intsess:${ipInt}`, SESSION_QUOTA_PER_HOUR, 3600))) {
3106
+ // ⚠️ UN REFUS MUET EST UNE TABLE VIDE SANS CAUSE NOMMÉE. Le 429 n'apparaît que dans la
3107
+ // console du lecteur ; l'exploitant, lui, voyait sa mesure ne pas monter et n'avait rien
3108
+ // à quoi le rattacher. C'est la règle des gardes muettes appliquée au quota lui-même —
3109
+ // et le signalement passe AVANT le `return`, sans quoi il ne s'exécuterait jamais.
3110
+ try {
3111
+ if (await PLAYER.limits.allow("intsess:quota-avert", 1, 3600)) {
3112
+ PLAYER.errors.capture(new Error(`session interne refusée : quota horaire atteint (${SESSION_QUOTA_PER_HOUR}/h par adresse) — la mesure s'arrête tant qu'il l'est`), { route: "internal-session" });
3113
+ }
3114
+ } catch { /* un journal ne doit jamais empêcher une lecture */ }
3094
3115
  res.statusCode = 429; res.setHeader("Content-Type", "application/json"); res.end('{"ok":false,"error":"rate"}');
3095
3116
  return;
3096
3117
  }
@@ -3118,7 +3139,29 @@ async function handler(req, res) {
3118
3139
  userName: jeton ? (jeton.name || body.name) : body.name,
3119
3140
  numPages: body.numPages, maxPage: body.maxPage, totalSeconds: body.totalSeconds, pagesTime: body.pagesTime,
3120
3141
  }, { ip: ip0, ua: ua0 });
3121
- } catch { /* best-effort */ }
3142
+ } catch (e) {
3143
+ // ⚠️ CE `catch` A COÛTÉ UNE JOURNÉE À UN HÔTE, ET IL ÉTAIT À TROIS LIGNES DU COMMENTAIRE
3144
+ // QUI DIT DE NE PAS FAIRE ÇA.
3145
+ //
3146
+ // La ligne écrite portait `ua` et `ip` ; la table des sessions internes ne les a pas.
3147
+ // PostgREST refusait, ce `catch` avalait le refus, et la route répondait `{"ok":true}`.
3148
+ // Ni le second hôte ni nous n'avions une seule ligne mesurée — notre table de production
3149
+ // en comptait ZÉRO — et rien, nulle part, ne le disait.
3150
+ //
3151
+ // « best-effort » est une intention juste : une mesure ne doit jamais empêcher de lire un
3152
+ // document. Mais best-effort ne veut pas dire MUET. Ce qu'on rattrape ici, c'est le droit
3153
+ // de continuer — pas le droit de ne rien dire.
3154
+ //
3155
+ // ⚠️ La leçon dépasse ce bloc : une règle écrite dans un commentaire ne protège pas le
3156
+ // code qui la suit. C'est pourquoi elle est aussi devenue un test —
3157
+ // server/__tests__/ecritureMuette.test.js refuse tout `catch` vide autour d'une écriture
3158
+ // de mesure.
3159
+ try {
3160
+ if (await PLAYER.limits.allow("intsess:echec", 1, 3600)) {
3161
+ PLAYER.errors.capture(new Error(`écriture de session interne refusée : ${e && e.message ? e.message : "cause inconnue"} — la mesure ne s'enregistre pas`), { route: "internal-session" });
3162
+ }
3163
+ } catch { /* un journal ne doit jamais empêcher une lecture */ }
3164
+ }
3122
3165
  res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.end('{"ok":true}');
3123
3166
  return;
3124
3167
  }
@@ -3128,7 +3171,19 @@ async function handler(req, res) {
3128
3171
  // 'session' = résumé riche (temps par page, appareil) → upsert ; open/page/heartbeat → journal léger (funnel/overview).
3129
3172
  if (body.event === "session") await upsertSession(share, { sessionId: body.sessionId, numPages: body.numPages, maxPage: body.maxPage, totalSeconds: body.totalSeconds, pagesTime: body.pagesTime }, { ip: ip0, ua: ua0 });
3130
3173
  else await logView(share, { event: body.event, page: body.page, maxPage: body.maxPage, seconds: body.seconds, sessionId: body.sessionId, ua: ua0 });
3131
- } catch { /* best-effort */ }
3174
+ } catch (e) {
3175
+ // ⚠️ LE MÊME `catch` MUET, SUR LE CHEMIN EXTERNE. Trouvé par la garde écrite pour
3176
+ // l'interne : une fois la règle devenue un test, elle a désigné son jumeau.
3177
+ //
3178
+ // Celui-ci n'a rien cassé jusqu'ici — la table des sessions externes a bien les colonnes
3179
+ // qu'on lui envoie. Mais il aurait avalé le prochain écart de la même façon, et personne
3180
+ // n'aurait rien vu : c'est ce qui rend la classe dangereuse, pas l'instance.
3181
+ try {
3182
+ if (await PLAYER.limits.allow("sess:echec", 1, 3600)) {
3183
+ PLAYER.errors.capture(new Error(`écriture de mesure refusée : ${e && e.message ? e.message : "cause inconnue"} — la lecture n'est pas comptée`), { route: "track" });
3184
+ }
3185
+ } catch { /* un journal ne doit jamais empêcher une lecture */ }
3186
+ }
3132
3187
  }
3133
3188
  res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.end('{"ok":true}');
3134
3189
  return;
@@ -1,5 +1,5 @@
1
1
  // GÉNÉRÉ par `npm run build:player` — NE PAS ÉDITER À LA MAIN.
2
- // Sources : src/presentation-content.ts
2
+ // Sources : src/shared.ts, src/presentation-content.ts, src/cadence.ts
3
3
  //
4
4
  // Contrats partagés entre le navigateur et les fonctions serverless. Un seul exemplaire :
5
5
  // deux implémentations d'un même contrat finissent toujours par diverger en silence.
@@ -22,18 +22,24 @@ var __copyProps = (to, from, except, desc) => {
22
22
  };
23
23
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
24
 
25
- // src/presentation-content.ts
26
- var presentation_content_exports = {};
27
- __export(presentation_content_exports, {
25
+ // src/shared.ts
26
+ var shared_exports = {};
27
+ __export(shared_exports, {
28
28
  DEFAULT_CENTER: () => DEFAULT_CENTER,
29
29
  DEFAULT_ZOOM: () => DEFAULT_ZOOM,
30
30
  MAP_TYPES: () => MAP_TYPES,
31
+ READERS_PER_EGRESS: () => READERS_PER_EGRESS,
32
+ SESSION_INTERVAL_MS: () => SESSION_INTERVAL_MS,
33
+ SESSION_QUOTA_PER_HOUR: () => SESSION_QUOTA_PER_HOUR,
34
+ SESSION_WRITES_PER_HOUR: () => SESSION_WRITES_PER_HOUR,
31
35
  cycleMapType: () => cycleMapType,
32
36
  initialMapContent: () => initialMapContent,
33
37
  mapTypeLabel: () => mapTypeLabel,
34
38
  sanitizeContent: () => sanitizeContent
35
39
  });
36
- module.exports = __toCommonJS(presentation_content_exports);
40
+ module.exports = __toCommonJS(shared_exports);
41
+
42
+ // src/presentation-content.ts
37
43
  var MAP_TYPES = ["roadmap", "satellite", "hybrid"];
38
44
  var DEFAULT_CENTER = [46.6, 2.5];
39
45
  var DEFAULT_ZOOM = 6;
@@ -81,11 +87,21 @@ function mapTypeLabel(current) {
81
87
  function initialMapContent() {
82
88
  return { kind: "map", center: [...DEFAULT_CENTER], zoom: DEFAULT_ZOOM, marker: null, mapType: null, label: null };
83
89
  }
90
+
91
+ // src/cadence.ts
92
+ var SESSION_INTERVAL_MS = 12e3;
93
+ var SESSION_WRITES_PER_HOUR = Math.ceil(36e5 / SESSION_INTERVAL_MS);
94
+ var READERS_PER_EGRESS = 25;
95
+ var SESSION_QUOTA_PER_HOUR = SESSION_WRITES_PER_HOUR * READERS_PER_EGRESS;
84
96
  // Annotate the CommonJS export names for ESM import in node:
85
97
  0 && (module.exports = {
86
98
  DEFAULT_CENTER,
87
99
  DEFAULT_ZOOM,
88
100
  MAP_TYPES,
101
+ READERS_PER_EGRESS,
102
+ SESSION_INTERVAL_MS,
103
+ SESSION_QUOTA_PER_HOUR,
104
+ SESSION_WRITES_PER_HOUR,
89
105
  cycleMapType,
90
106
  initialMapContent,
91
107
  mapTypeLabel,
package/server/shares.js CHANGED
@@ -344,7 +344,11 @@ async function setShareAuth(slug, requireAuth) {
344
344
  // signalé au point d'entrée plutôt qu'ici.
345
345
  const BORNES = { pages: 10_000, secondes: 24 * 3600, entreesPagesTime: 2_000 };
346
346
 
347
- async function upsertInternalSession(p, { ip, ua }) {
347
+ // ⚠️ `ip` N'EST PLUS LU, ET LA SIGNATURE LE DIT. L'appelant continue de le passer — il ne sait pas
348
+ // ce que chaque table conserve, et ce n'est pas à lui de le savoir. Mais le garder en paramètre
349
+ // nommé laisserait croire qu'il sert : c'est comme ça qu'une donnée revient dans une ligne où elle
350
+ // n'a rien à faire. Une lecture interne, c'est un collègue ; on ne conserve pas son adresse.
351
+ async function upsertInternalSession(p, { ip: _ip, ua }) {
348
352
  const num = (v) => (Number.isFinite(+v) ? Math.trunc(+v) : null);
349
353
  const borne = (v, max) => { const n = num(v); return n == null ? null : Math.max(0, Math.min(n, max)); };
350
354
  const sessionId = String(p.sessionId || "").slice(0, 64);
@@ -390,7 +394,24 @@ async function upsertInternalSession(p, { ip, ua }) {
390
394
  }
391
395
  return out;
392
396
  })(),
393
- ua: String(ua || "").slice(0, 300), ip: String(ip || "").slice(0, 60), device, os, browser, last_at: new Date().toISOString(),
397
+ // ⚠️ NI `ua` NI `ip` ICI, ET C'EST LE CORRECTIF PAS UNE COLONNE À AJOUTER.
398
+ //
399
+ // Ces deux champs partaient vers une table qui ne les a pas. PostgREST refusait l'insertion
400
+ // (« column "ua" ... does not exist »), le `catch` de l'appelant avalait le refus, et la route
401
+ // répondait `{"ok":true}`. Le suivi interne n'a donc JAMAIS rien écrit — ni chez le second
402
+ // hôte, ni chez nous : notre table de production comptait zéro ligne.
403
+ //
404
+ // ⚠️ Le schéma avait raison, c'est le code qui mentait. Une lecture INTERNE, c'est un collègue :
405
+ // `device`, `os` et `browser` — dérivés — suffisent, et on ne conserve pas l'agent complet ni
406
+ // l'adresse de ses propres équipes. La table des sessions EXTERNES les porte, elle, parce que
407
+ // ce n'est ni la même population ni la même promesse.
408
+ //
409
+ // Corriger en AJOUTANT les colonnes aurait fait l'inverse : mettre le schéma au niveau du code
410
+ // au lieu du code au niveau de l'intention. Ce qu'on garde sur ses propres équipes ne se décide
411
+ // pas par un message d'erreur PostgREST.
412
+ //
413
+ // Trouvé par le second hôte, vérifié en rejouant l'insertion.
414
+ device, os, browser, last_at: new Date().toISOString(),
394
415
  };
395
416
  await PLAYER.db.request("commercial_doc_internal_sessions?on_conflict=session_id", { method: "POST", headers: { Prefer: "resolution=merge-duplicates,return=minimal" }, body: [row] });
396
417
  }