discovery-media-player 0.1.34 → 0.1.36

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.34",
3
+ "version": "0.1.36",
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||\"\",f=e.internal||null,m=e.sessionId||He(),b=e.send||Oe(o,n),p={},h=0,M=0,u=0,v=0,g=null,S=r(),_=!1,N=!1,A=[],he=()=>!!c||!!f,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&&(p[h]=(p[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 p)i+=p[l];return g!=null&&(i+=oe(g)/1e3),Math.round(i)},ie=()=>{let i={};for(let l in p)i[l]=Math.round(p[l]);return i},E=()=>{S=r(),_&&(_=!1,V())},se=(i,l)=>{f||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()};f?(i.internal=!0,i.docId=f.docId,i.email=f.email,i.name=f.name):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:()=>d,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 d(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=\"${d(e)}\" alt=\"\">`:d(fe(n))}function Z(e){let n=d(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,f=-1/0,m=null,b=!1;function p(){if(b||a){c=!0;return}let u=t-(r()-f);if(u>0){h(u);return}a=!0,c=!1,f=r();let v=!1;e(()=>{v||(v=!0,a=!1,c&&!b&&p())})}function h(u){b||m!==null||(m=o(()=>{m=null,p()},Math.max(0,u)))}return{signaler(){b||p()},maintenant(){b||(f=-1/0,p())},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=\"${d(s)}\">${d(s)} ${a.length}</button>`}return o}function ge(e){if(!e||!e.url)return\"\";let n=d(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=d(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>${d(e.reply_name||\"\")}</b> ${d(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>${d(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: "4238739f09f2f24c" };
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
@@ -1725,7 +1726,7 @@ function viewerHtml(share, nonce, logoUrl, pitch) {
1725
1726
  // En aperçu interne, on embarque de quoi démarrer une présentation live (URL Storage brute + métadonnées).
1726
1727
  // `fileName` : c'est LUI qui dit la nature du document côté page. L'URL publique est
1727
1728
  // `/api/doc?slug=…&file=1`, sans extension — sans ce champ, une image partait dans pdf.js.
1728
- const cfg = JSON.stringify({ brand: PLAYER.branding.name, slug: preview ? "" : share.slug, fileUrl, fileName: share.file_name || "", pdfjs: PDFJS, title, preview, embed, embedded, bot: botOn, botGuided: !preview && !!share.bot_enabled && share.bot_guided !== false, botAv: (!preview && share.bot_enabled && share.bot_avatar) || "", botName: (!preview && share.bot_enabled && share.bot_name) || "", botGreet: (!preview && share.bot_enabled && share.bot_greeting) || "", botGreetDoc: (!preview && share.bot_enabled && share.bot_greeting_doc) || "", dl: share.allow_download !== false, autoPresent: !!share.auto_present, botAnim: share.bot_page_anim !== false, botVoice: !preview && !!share.bot_enabled && !!process.env.ELEVENLABS_API_KEY, vIcOn: ICONS.sound, vIcOff: ICONS.mute, kStyle: (!preview && share.bot_enabled && share.bot_karaoke) || "classic", vLayout: (!preview && share.bot_enabled && share.video_layout) || "", vClips: !preview && !!share.bot_vclips, botVAv: (!preview && share.bot_enabled && share.bot_vphoto) || "", resumeSlug: preview ? (share.resume_slug || "") : "", supaUrl: preview ? (share.supa_url || "") : "", supaKey: preview ? (share.supa_key || "") : "", internal: preview && share.internal_email ? { email: share.internal_email, name: share.presenter_name || "", docId: share.doc_id || "" } : null, present: preview ? { url: share.raw_url || "", name: share.file_name || "", title: share.doc_title || "", docId: share.doc_id || "", by: share.presenter_name || "", email: share.internal_email || "", av: share.presenter_avatar || "" } : null });
1729
+ const cfg = JSON.stringify({ brand: PLAYER.branding.name, slug: preview ? "" : share.slug, fileUrl, fileName: share.file_name || "", pdfjs: PDFJS, title, preview, embed, embedded, bot: botOn, botGuided: !preview && !!share.bot_enabled && share.bot_guided !== false, botAv: (!preview && share.bot_enabled && share.bot_avatar) || "", botName: (!preview && share.bot_enabled && share.bot_name) || "", botGreet: (!preview && share.bot_enabled && share.bot_greeting) || "", botGreetDoc: (!preview && share.bot_enabled && share.bot_greeting_doc) || "", dl: share.allow_download !== false, autoPresent: !!share.auto_present, botAnim: share.bot_page_anim !== false, botVoice: !preview && !!share.bot_enabled && !!process.env.ELEVENLABS_API_KEY, vIcOn: ICONS.sound, vIcOff: ICONS.mute, kStyle: (!preview && share.bot_enabled && share.bot_karaoke) || "classic", vLayout: (!preview && share.bot_enabled && share.video_layout) || "", vClips: !preview && !!share.bot_vclips, botVAv: (!preview && share.bot_enabled && share.bot_vphoto) || "", resumeSlug: preview ? (share.resume_slug || "") : "", supaUrl: preview ? (share.supa_url || "") : "", supaKey: preview ? (share.supa_key || "") : "", internal: preview && share.internal_email ? { email: share.internal_email, name: share.presenter_name || "", docId: share.doc_id || "", it: share.internal_token || "" } : null, present: preview ? { url: share.raw_url || "", name: share.file_name || "", title: share.doc_title || "", docId: share.doc_id || "", by: share.presenter_name || "", email: share.internal_email || "", av: share.presenter_avatar || "" } : null });
1729
1730
  return `<!doctype html><html lang=fr><head><meta charset=utf-8>
1730
1731
  <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=3,viewport-fit=cover,interactive-widget=resizes-content">
1731
1732
  <meta name=robots content="noindex,nofollow">
@@ -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
  }
@@ -3288,7 +3309,7 @@ async function handler(req, res) {
3288
3309
  }
3289
3310
  const supaUrl = (PLAYER.config && PLAYER.config.supabaseUrl) || "";
3290
3311
  const supaKey = (PLAYER.config && PLAYER.config.supabasePublishableKey) || "";
3291
- const pseudo = { preview: true, embed, slug: "", file_name: String(q.name || "document.pdf"), doc_title: String(q.title || q.name || "Document"), raw_url: url, doc_id: String(q.docId || ""), presenter_name: String(q.by || ""), presenter_avatar: String(q.av || ""), internal_email: String(q.uemail || ""), supa_url: supaUrl, supa_key: supaKey, auto_present: String(q.autopresent || "") === "1", resume_slug: String(q.resume || ""), stream_url: `/api/doc?preview=1&stream=1&url=${encodeURIComponent(url)}&name=${encodeURIComponent(String(q.name || ""))}` };
3312
+ const pseudo = { preview: true, embed, slug: "", file_name: String(q.name || "document.pdf"), doc_title: String(q.title || q.name || "Document"), raw_url: url, doc_id: String(q.docId || ""), presenter_name: String(q.by || ""), presenter_avatar: String(q.av || ""), internal_email: String(q.uemail || ""), internal_token: String(q.it || ""), supa_url: supaUrl, supa_key: supaKey, auto_present: String(q.autopresent || "") === "1", resume_slug: String(q.resume || ""), stream_url: `/api/doc?preview=1&stream=1&url=${encodeURIComponent(url)}&name=${encodeURIComponent(String(q.name || ""))}` };
3292
3313
  let plogo = ""; try { plogo = await PLAYER.branding.logo(); } catch { /* sans logo */ }
3293
3314
  const pnonce = crypto.randomBytes(16).toString("base64");
3294
3315
  // Aperçu interne : CSP relâchée (supabase-js jsdelivr + Realtime wss) pour la présence + le chat live,
@@ -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
@@ -348,7 +348,28 @@ async function upsertInternalSession(p, { ip, ua }) {
348
348
  const num = (v) => (Number.isFinite(+v) ? Math.trunc(+v) : null);
349
349
  const borne = (v, max) => { const n = num(v); return n == null ? null : Math.max(0, Math.min(n, max)); };
350
350
  const sessionId = String(p.sessionId || "").slice(0, 64);
351
- if (!sessionId || !p.docId) return;
351
+ // ⚠️ UN REJET MUET A COÛTÉ DES SEMAINES À UN HÔTE.
352
+ //
353
+ // Cette garde est juste — une session sans document ne mesure rien — mais elle ne DISAIT rien.
354
+ // Le second hôte a monté son suivi interne, l'a cru en service, et a découvert bien plus tard que
355
+ // la table était vide : son `docId` ne partait pas, et chaque battement était jeté ici en silence.
356
+ //
357
+ // ⚠️ Ce n'est pas la garde qui était en cause, c'est son mutisme. Une mesure qui ne remonte rien
358
+ // est indistinguable d'une mesure qui n'a rien à remonter : personne ne va chercher une panne
359
+ // qu'aucun signal n'annonce. La même leçon que le trou de session interne signalé une fois par
360
+ // heure en 0.1.22 — un état anormal qu'on ne dit pas devient l'état normal.
361
+ //
362
+ // Une fois par heure suffit : le but est qu'un exploitant qui ouvre ses journaux tombe dessus,
363
+ // pas de compter les rejets. Demandé par le second hôte, à qui ça aurait fait gagner des semaines.
364
+ if (!sessionId || !p.docId) {
365
+ try {
366
+ if (await PLAYER.limits.allow("intsess:jetee", 1, 3600)) {
367
+ const manque = !sessionId ? "sessionId" : "docId";
368
+ PLAYER.errors.capture(new Error(`session interne jetée : ${manque} absent — rien ne sera mesuré tant qu'il manque`), { route: "internal-session" });
369
+ }
370
+ } catch { /* un journal ne doit jamais empêcher une lecture */ }
371
+ return;
372
+ }
352
373
  const { device, os, browser } = parseUa(ua);
353
374
  const row = {
354
375
  session_id: sessionId, doc_id: String(p.docId).slice(0, 200), user_email: low(p.userEmail).slice(0, 160) || null,