discovery-media-player 0.1.83 → 0.1.85
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/context/standalone.js +20 -9
- package/package.json +1 -1
- package/server/browser.generated.js +1 -1
- package/server/gabarit-live.js +5 -1
- package/server/presentations.js +11 -4
- package/server/routes-direct.js +13 -2
- package/server/routes-liens.js +10 -1
- package/server/shared.generated.js +1 -1
- package/server/shares.js +18 -3
package/context/standalone.js
CHANGED
|
@@ -159,16 +159,27 @@ function creerLimites(db, journal) {
|
|
|
159
159
|
let partageDisponible = null; // null = pas encore demandé
|
|
160
160
|
let signale = false;
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
// ⚠️ FENÊTRE FIXE, O(1) PAR DÉCISION (P1 performance). L'ancien compteur gardait un TABLEAU de
|
|
163
|
+
// timestamps par clé et le re-filtrait à chaque appel — quadratique quand une clé accumule (une
|
|
164
|
+
// clé à 88 400 entrées scannait 88 400 valeurs même pour un REFUS). Ici, par clé : { debut,
|
|
165
|
+
// compte }. La fenêtre écoulée remet le compteur à zéro ; sinon on compare et on incrémente.
|
|
166
|
+
// Une fenêtre fixe laisse au pire 2×max au JOINT de deux fenêtres — acceptable pour un plafond
|
|
167
|
+
// anti-inondation, et c'est le compromis explicitement recommandé.
|
|
168
|
+
const PLAFOND_CLES = 5000;
|
|
163
169
|
function localAutorise(cle, max, fenetreSecondes) {
|
|
164
170
|
const maintenant = Date.now();
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
seaux.
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
const fenetreMs = fenetreSecondes * 1000;
|
|
172
|
+
let e = seaux.get(cle);
|
|
173
|
+
if (!e || maintenant - e.debut >= fenetreMs) e = { debut: maintenant, compte: 0 };
|
|
174
|
+
// LRU : re-poser la clé la remet en fin d'ordre d'insertion (la Map préserve cet ordre).
|
|
175
|
+
seaux.delete(cle);
|
|
176
|
+
const permis = e.compte < max;
|
|
177
|
+
if (permis) e.compte += 1;
|
|
178
|
+
seaux.set(cle, e);
|
|
179
|
+
// ⚠️ PLAFOND DUR : on évince la plus ANCIENNE (tête de la Map) tant qu'on dépasse — l'ancien
|
|
180
|
+
// code n'enlevait que les clés expirées, donc 5 001 clés actives restaient en mémoire.
|
|
181
|
+
while (seaux.size > PLAFOND_CLES) seaux.delete(seaux.keys().next().value);
|
|
182
|
+
return permis;
|
|
172
183
|
}
|
|
173
184
|
|
|
174
185
|
let deja = "";
|
|
@@ -624,4 +635,4 @@ function createStandaloneContext(env = process.env) {
|
|
|
624
635
|
};
|
|
625
636
|
}
|
|
626
637
|
|
|
627
|
-
module.exports = { createStandaloneContext };
|
|
638
|
+
module.exports = { createStandaloneContext, creerLimites };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
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 C=Object.defineProperty,Ue=Object.defineProperties,De=Object.getOwnPropertyDescriptor,We=Object.getOwnPropertyDescriptors,Ve=Object.getOwnPropertyNames,he=Object.getOwnPropertySymbols;var Me=Object.prototype.hasOwnProperty,ze=Object.prototype.propertyIsEnumerable;var ve=(e,n,t)=>n in e?C(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,G=(e,n)=>{for(var t in n||(n={}))Me.call(n,t)&&ve(e,t,n[t]);if(he)for(var t of he(n))ze.call(n,t)&&ve(e,t,n[t]);return e},we=(e,n)=>Ue(e,We(n));var v=(e,n)=>{for(var t in n)C(e,t,{get:n[t],enumerable:!0})},Fe=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Ve(n))!Me.call(e,o)&&o!==t&&C(e,o,{get:()=>n[o],enumerable:!(r=De(n,o))||r.enumerable});return e};var Be=e=>Fe(C({},\"__esModule\",{value:!0}),e);var Bn={};v(Bn,{bridge:()=>N,cadence:()=>L,chat:()=>H,live:()=>$,presentation:()=>U,presentationState:()=>W,tracking:()=>O,viewer:()=>D});var N={};v(N,{onHostMessage:()=>en,onPlayerMessage:()=>Je,parseHostMessage:()=>_e,parsePlayerMessage:()=>Te,sendToHost:()=>qe,sendToPlayer:()=>Qe,toWire:()=>Y});var K=\"3dd-doc-\",je=/^[A-Za-z0-9_-]{1,64}$/,Ge=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ke=[\"embed-denied\"],Ye=[\"present-invite\",\"present-handover\",\"present-switch\"],Ze=[\"handover-done\"];function Y(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 xe(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(Ke.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 i=e.slug;return typeof i!=\"string\"||!je.test(i)?null:{type:o,slug:i}}function Te(e){return xe(e,Ge,Ye)}function _e(e){return xe(e,Ze,[])}function qe(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(Y(e),\"*\")}catch(t){}}function Qe(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(Y(n),\"*\")}catch(r){}}function Xe(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function Je(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=i=>{if(!Xe(t,i.source))return;let a=Te(i.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function en(e,n){var i;let t=n||(typeof window!=\"undefined\"?window:null);if(!t)return()=>{};let r=(i=t.parent)!=null?i:null,o=a=>{if(!r||r===t||a.source!==r)return;let l=_e(a.data);l&&e(l)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var O={};v(O,{createTracker:()=>ln});var L={};v(L,{PRESENTER_ACTIONS_PER_HOUR:()=>X,PRESENT_CACHE_MS:()=>rn,PRESENT_QUOTA_MARGIN:()=>Ae,PRESENT_QUOTA_PER_HOUR:()=>on,PRESENT_READS_PER_HOUR:()=>Re,PRESENT_READ_BURST:()=>Pe,PRESENT_READ_COALESCE_MS:()=>Ie,PRESENT_RESYNC_MS:()=>Se,PRESENT_SIGNAL_BUDGET_PER_HOUR:()=>tn,PRESENT_WRITE_TIMEOUT_MS:()=>J,READERS_PER_EGRESS:()=>q,RESYNC_READS_PER_HOUR:()=>ke,SESSION_IDLE_MS:()=>Q,SESSION_INTERVAL_MS:()=>Z,SESSION_QUOTA_PER_HOUR:()=>nn,SESSION_WRITES_PER_HOUR:()=>Ee});var Z=12e3,Ee=Math.ceil(300),q=25,nn=Ee*q,Q=18e4,Se=25e3,ke=Math.ceil(36e5/Se),X=720,Re=ke+X,tn=X,Pe=20,J=1e4,Ie=400,rn=Ie,Ae=4,on=(Re+Pe)*q*Ae;var sn=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function an(){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 un(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 ln(e={}){var ge,be;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\",i=(ge=e.idleMs)!=null?ge:Q,a=(be=e.sessionEveryMs)!=null?be:12e3,l=e.slug||\"\",d=e.internal||null,m=e.sessionId||an(),p=e.send||un(o,n),c={},u=0,g=0,f=0,M=0,h=null,E=r(),S=!1,V=!1,I=[],He=()=>!!l||!!d,ce=s=>{if(He())try{p(s)}catch(b){}},z=()=>!!t&&t.visibilityState===\"visible\"&&!S,de=s=>{let b=r()-s,x=Math.max(0,E-s)+i;return Math.max(0,Math.min(b,x))},F=()=>{h!=null&&u>0&&(c[u]=(c[u]||0)+de(h)/1e3),h=z()?r():null},pe=()=>{F(),h=null},B=()=>{h==null&&z()&&(h=r())},j=()=>{let s=0;for(let b in c)s+=c[b];return h!=null&&(s+=de(h)/1e3),Math.round(s)},fe=()=>{let s={};for(let b in c)s[b]=Math.round(c[b]);return s},k=()=>{E=r(),S&&(S=!1,B())},me=(s,b)=>{d||ce({slug:l,event:s,page:b||g,maxPage:f,seconds:j(),sessionId:m})},A=()=>{F();let s={event:\"session\",sessionId:m,numPages:M,maxPage:f,totalSeconds:j(),pagesTime:fe()};d?(s.internal=!0,s.docId=d.docId,s.email=d.email,s.name=d.name,d.it&&(s.it=d.it)):s.slug=l,ce(s)},R=(s,b,x,ye)=>{!s||typeof s.addEventListener!=\"function\"||(s.addEventListener(b,x,ye),I.push(()=>{s.removeEventListener(b,x,ye)}))};return{sessionId:m,totalSeconds:j,maxPage:()=>f,pageTimes:fe,start(){if(V)return;V=!0,me(\"open\",1),u=1,E=r(),h=z()?r():null,R(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(pe(),A()):(k(),B())}),R(n,\"focus\",()=>{k(),B()});for(let x of sn)R(n,x,k,{passive:!0});R(e.scrollElement,\"scroll\",k,{passive:!0}),R(n,\"beforeunload\",A);let s=n.setInterval(()=>{!S&&r()-E>i&&(S=!0,pe())},5e3),b=n.setInterval(A,a);I.push(()=>{n.clearInterval(s),n.clearInterval(b)})},setPage(s){!s||s===g||(E=r(),F(),u=s,g=s,s>f&&(f=s,me(\"page\",s)))},setPageCount(s){M=s||0},noteActivity:k,flush:A,stop(){for(;I.length;){let s=I.pop();try{s==null||s()}catch(b){}}V=!1}}}var $={};v($,{STORAGE_KEYS:()=>w,attendeeKey:()=>fn,authorToken:()=>mn,avatarHtml:()=>ee,canModerate:()=>oe,createBudget:()=>wn,createFileEcritures:()=>xn,createScheduler:()=>vn,escapeHtml:()=>y,fetchBorne:()=>Mn,flattenPresence:()=>cn,formatMessageBody:()=>ne,initials:()=>Ce,isMentioned:()=>bn,isMine:()=>T,presenceId:()=>Tn,reactorId:()=>re,referenceAuteur:()=>gn,shouldNotify:()=>yn,unreadLabel:()=>hn});var w={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\",presenceId:\"3dd-present-uid\"};function y(e){return String(e==null?\"\":e).replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\")}function Ce(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function ee(e,n){return e?`<img src=\"${y(e)}\" alt=\"\">`:y(Ce(n))}function ne(e){let n=y(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 cn(e){let n=new Map;for(let t in e||{})for(let r of(e||{})[t]||[]){let o=String(r.uid||r.email||r.name||\"\").toLowerCase()||`_${t}_${n.size}`;(!n.get(o)||r.role===\"presenter\")&&n.set(o,r)}return[...n.values()]}function dn(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function pn(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function fn(e,n=\"anon\",t=te){try{if(!e)return`anon-${n}`;let r=e.getItem(w.attendeeKey);if(r)return r;let o=`anon-${t()}`;return e.setItem(w.attendeeKey,o),o}catch(r){return`anon-${n}`}}function te(){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 mn(e,n=te){let t=dn(e,w.authorToken);if(t)return t;let r=n();return pn(e,w.authorToken,r),r}async function gn(e,n){let t=n!==void 0?n:typeof globalThis!=\"undefined\"&&globalThis.crypto?globalThis.crypto.subtle:null;if(!t||!e)return\"\";try{let r=async o=>{let i=await t.digest(\"SHA-256\",new TextEncoder().encode(o));return Array.from(new Uint8Array(i),a=>a.toString(16).padStart(2,\"0\")).join(\"\")};return(await r(\"ref:\"+await r(e))).slice(0,16)}catch(r){return\"\"}}function re(e){return String(e&&e.ref||\"\")}function T(e,n){return!e||!n||!e.author_ref||!n.ref?!1:e.author_ref===n.ref}function oe(e){return!!(e&&e.role===\"presenter\")}function bn(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 yn(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||T(n,t)?!1:o}function hn(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function vn(e,n={}){var g;let t=(g=n.minMs)!=null?g:300,r=n.now||(()=>Date.now()),o=n.setTimer||((f,M)=>setTimeout(f,M)),i=n.clearTimer||(f=>clearTimeout(f)),a=!1,l=!1,d=-1/0,m=null,p=!1;function c(){if(p||a){l=!0;return}let f=t-(r()-d);if(f>0){u(f);return}a=!0,l=!1,d=r();let M=!1;e(()=>{M||(M=!0,a=!1,l&&!p&&c())})}function u(f){p||m!==null||(m=o(()=>{m=null,c()},Math.max(0,f)))}return{signaler(){p||c()},maintenant(){p||(d=-1/0,c())},arreter(){p=!0,m!==null&&(i(m),m=null)}}}function Mn(e,n,t=J,r={}){let o=r.fetch||(typeof fetch==\"function\"?fetch:void 0);if(!o)return Promise.reject(new Error(\"fetch indisponible\"));let i=r.setTimer||((u,g)=>setTimeout(u,g)),a=r.clearTimer||(u=>clearTimeout(u)),l=null;try{l=new AbortController}catch(u){}let d=l?we(G({},n||{}),{signal:l.signal}):G({},n||{}),m=null,p=new Promise((u,g)=>{m=i(()=>{try{l==null||l.abort()}catch(f){}g(new Error(\"delai\"))},t)}),c=()=>{try{a(m)}catch(u){}};return Promise.race([o(e,d),p]).then(u=>(c(),u),u=>{throw c(),u})}function wn(e){let n=e.now||(()=>Date.now()),t=Math.max(1,e.rafale),r=t,o=n();return{prendre(){let i=n();return r=Math.min(t,r+(i-o)*e.parHeure/36e5),o=i,r<1?!1:(r-=1,!0)},restants:()=>r}}function xn(e={}){var m;let n=(m=e.minMs)!=null?m:0,t=e.now||(()=>Date.now()),r=e.setTimer||((p,c)=>setTimeout(p,c)),o=new Map,i=!1,a=-1/0,l=!1;function d(){if(i||o.size===0)return;let p=n-(t()-a);if(p>0){if(l)return;l=!0,r(()=>{l=!1,d()},p);return}let c=o.keys().next().value,u=o.get(c);o.delete(c),i=!0,a=t(),Promise.resolve().then(u.tache).then(g=>u.regler(g),()=>u.regler(null)).then(()=>{i=!1,d()})}return{poser(p,c){return new Promise(u=>{let g=o.get(p);o.set(p,{tache:c,regler:f=>{g&&g.regler(f),u(f)}}),d()})},enAttente:()=>o.size}}function Tn(e,n=te){try{if(e){let t=e.getItem(w.presenceId);if(t)return t;let r=`p-${n()}`;return e.setItem(w.presenceId,r),r}}catch(t){}return`p-${n()}`}var H={};v(H,{messageClassName:()=>En,renderActions:()=>Oe,renderAttachment:()=>Le,renderMessage:()=>_n,renderReactions:()=>Ne});function Ne(e,n){let t=e&&e.reactions||{},r=re(n),o=\"\";for(let i in t){let a=t[i]||[];if(!a.length)continue;let l=a.indexOf(r)>=0;o+=`<button class=\"re-chip${l?\" on\":\"\"}\" data-e=\"${y(i)}\">${y(i)} ${a.length}</button>`}return o}function Le(e){if(!e||!e.url)return\"\";let n=y(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=y(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 Oe(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||oe(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function _n(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>${y(e.reply_name||\"\")}</b> ${y(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${ne(e.body)}${r}</div>`:\"\",i=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${ee(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${y(e.author_name||\"Invit\\xE9\")}</b>${i}</div>`+t+o+Le(e.attachment)+`<div class=cm-re>${Ne(e,n.me)}</div></span><span class=cm-act>${Oe(e,n)}</span>`}function En(e,n,t){return\"cm\"+(T(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var U={};v(U,{DEFAULT_CENTER:()=>ae,DEFAULT_ZOOM:()=>ue,MAP_TYPES:()=>P,cycleMapType:()=>Sn,initialMapContent:()=>Rn,mapTypeLabel:()=>kn,sanitizeContent:()=>le});var P=[\"roadmap\",\"satellite\",\"hybrid\"],ae=[46.6,2.5],ue=6;function _(e){let n=Number(e);return Number.isFinite(n)?n:null}function se(e){if(!Array.isArray(e))return null;let n=_(e[0]),t=_(e[1]);return n!=null&&t!=null?[n,t]:null}var ie=(e,n,t)=>Math.max(n,Math.min(t,e));function le(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=se(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:_(r.heading)||0,pitch:ie(_(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:ie(_(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:se(n.center)||[...ae],zoom:ie(Math.trunc(_(n.zoom)||ue),1,21),marker:se(n.marker),mapType:P.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function Sn(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function kn(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function Rn(){return{kind:\"map\",center:[...ae],zoom:ue,marker:null,mapType:null,label:null}}var D={};v(D,{CURRENT_PAGE_MARGIN:()=>Ln,DEFAULT_ASPECT:()=>An,MAX_ZOOM:()=>In,MIN_PAGE_WIDTH:()=>Cn,MIN_ZOOM:()=>Pn,PRERENDER_MARGIN:()=>Nn,arrowState:()=>Hn,averageColor:()=>Wn,clampPage:()=>$e,clampZoom:()=>On,fitWidth:()=>Dn,isImageDocument:()=>Vn,progressPercent:()=>$n});var Pn=.5,In=3,An=1.35,Cn=280,Nn=\"500px 0px\",Ln=\"-48% 0px -48% 0px\";function On(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function $e(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function $n(e,n){return n?Math.round($e(e,n)/n*100):0}function Hn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var Un=28;function Dn(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,i=((e.containerHeight||600)-Un-(e.overlap||0)-(e.reserve||0))/r;return i>120?Math.min(t,i):t}function Wn(e){let n=e.length/4;if(!n)return\"rgb(0,0,0)\";let t=0,r=0,o=0;for(let i=0;i<e.length;i+=4)t+=e[i],r+=e[i+1],o+=e[i+2];return`rgb(${Math.round(t/n)},${Math.round(r/n)},${Math.round(o/n)})`}function Vn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var W={};v(W,{presentationTransition:()=>zn,switchDocUrl:()=>Fn});function zn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=le(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 Fn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Be(Bn);})();", SOURCE_HASH: "6e69736fc03cc5f0" };
|
|
7
|
+
module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var C=Object.defineProperty,De=Object.defineProperties,We=Object.getOwnPropertyDescriptor,Ve=Object.getOwnPropertyDescriptors,ze=Object.getOwnPropertyNames,ve=Object.getOwnPropertySymbols;var we=Object.prototype.hasOwnProperty,Fe=Object.prototype.propertyIsEnumerable;var Me=(e,n,t)=>n in e?C(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,G=(e,n)=>{for(var t in n||(n={}))we.call(n,t)&&Me(e,t,n[t]);if(ve)for(var t of ve(n))Fe.call(n,t)&&Me(e,t,n[t]);return e},xe=(e,n)=>De(e,Ve(n));var M=(e,n)=>{for(var t in n)C(e,t,{get:n[t],enumerable:!0})},Be=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of ze(n))!we.call(e,o)&&o!==t&&C(e,o,{get:()=>n[o],enumerable:!(r=We(n,o))||r.enumerable});return e};var je=e=>Be(C({},\"__esModule\",{value:!0}),e);var jn={};M(jn,{bridge:()=>N,cadence:()=>L,chat:()=>H,live:()=>$,presentation:()=>U,presentationState:()=>W,tracking:()=>O,viewer:()=>D});var N={};M(N,{onHostMessage:()=>nn,onPlayerMessage:()=>en,parseHostMessage:()=>Ee,parsePlayerMessage:()=>_e,sendToHost:()=>Qe,sendToPlayer:()=>Xe,toWire:()=>Y});var K=\"3dd-doc-\",Ge=/^[A-Za-z0-9_-]{1,64}$/,Ke=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ye=[\"embed-denied\"],Ze=[\"present-invite\",\"present-handover\",\"present-switch\"],qe=[\"handover-done\"];function Y(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 Te(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(Ye.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\"||!Ge.test(s)?null:{type:o,slug:s}}function _e(e){return Te(e,Ke,Ze)}function Ee(e){return Te(e,qe,[])}function Qe(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(Y(e),\"*\")}catch(t){}}function Xe(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(Y(n),\"*\")}catch(r){}}function Je(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function en(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!Je(t,s.source))return;let a=_e(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function nn(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 l=Ee(a.data);l&&e(l)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var O={};M(O,{createTracker:()=>cn});var L={};M(L,{PRESENTER_ACTIONS_PER_HOUR:()=>X,PRESENT_CACHE_MS:()=>on,PRESENT_QUOTA_MARGIN:()=>Ce,PRESENT_QUOTA_PER_HOUR:()=>sn,PRESENT_READS_PER_HOUR:()=>Pe,PRESENT_READ_BURST:()=>Ie,PRESENT_READ_COALESCE_MS:()=>Ae,PRESENT_RESYNC_MS:()=>ke,PRESENT_SIGNAL_BUDGET_PER_HOUR:()=>rn,PRESENT_WRITE_TIMEOUT_MS:()=>J,READERS_PER_EGRESS:()=>q,RESYNC_READS_PER_HOUR:()=>Re,SESSION_IDLE_MS:()=>Q,SESSION_INTERVAL_MS:()=>Z,SESSION_QUOTA_PER_HOUR:()=>tn,SESSION_WRITES_PER_HOUR:()=>Se});var Z=3e4,Se=Math.ceil(120),q=25,tn=Se*q,Q=18e4,ke=25e3,Re=Math.ceil(36e5/ke),X=720,Pe=Re+X,rn=X,Ie=20,J=1e4,Ae=400,on=Ae,Ce=4,sn=(Pe+Ie)*q*Ce;var an=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function un(){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 ln(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 cn(e={}){var ye,be;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=(ye=e.idleMs)!=null?ye:Q,a=(be=e.sessionEveryMs)!=null?be:3e4,l=e.slug||\"\",p=e.internal||null,g=e.sessionId||un(),f=e.send||ln(o,n),c={},u=0,y=0,d=0,w=0,v=null,E=r(),S=!1,V=!1,I=[],Ue=()=>!!l||!!p,ce=i=>{if(Ue())try{f(i)}catch(m){}},z=()=>!!t&&t.visibilityState===\"visible\"&&!S,de=i=>{let m=r()-i,b=Math.max(0,E-i)+s;return Math.max(0,Math.min(m,b))},F=()=>{v!=null&&u>0&&(c[u]=(c[u]||0)+de(v)/1e3),v=z()?r():null},pe=()=>{F(),v=null},B=()=>{v==null&&z()&&(v=r())},j=()=>{let i=0;for(let m in c)i+=c[m];return v!=null&&(i+=de(v)/1e3),Math.round(i)},fe=()=>{let i={};for(let m in c)i[m]=Math.round(c[m]);return i},k=()=>{E=r(),S&&(S=!1,B())},me=(i,m)=>{p||ce({slug:l,event:i,page:m||y,maxPage:d,seconds:j(),sessionId:g})},ge=\"\",A=()=>{F();let i=j(),m=`${i}|${d}|${w}`;if(m===ge)return;let b={event:\"session\",sessionId:g,numPages:w,maxPage:d,totalSeconds:i,pagesTime:fe()};p?(b.internal=!0,b.docId=p.docId,b.email=p.email,b.name=p.name,p.it&&(b.it=p.it)):b.slug=l,ge=m,ce(b)},R=(i,m,b,he)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(m,b,he),I.push(()=>{i.removeEventListener(m,b,he)}))};return{sessionId:g,totalSeconds:j,maxPage:()=>d,pageTimes:fe,start(){if(V)return;V=!0,me(\"open\",1),u=1,E=r(),v=z()?r():null,R(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(pe(),A()):(k(),B())}),R(n,\"focus\",()=>{k(),B()});for(let b of an)R(n,b,k,{passive:!0});R(e.scrollElement,\"scroll\",k,{passive:!0}),R(n,\"beforeunload\",A);let i=n.setInterval(()=>{!S&&r()-E>s&&(S=!0,pe())},5e3),m=n.setInterval(A,a);I.push(()=>{n.clearInterval(i),n.clearInterval(m)})},setPage(i){!i||i===y||(E=r(),F(),u=i,y=i,i>d&&(d=i,me(\"page\",i)))},setPageCount(i){w=i||0},noteActivity:k,flush:A,stop(){for(;I.length;){let i=I.pop();try{i==null||i()}catch(m){}}V=!1}}}var $={};M($,{STORAGE_KEYS:()=>x,attendeeKey:()=>mn,authorToken:()=>gn,avatarHtml:()=>ee,canModerate:()=>oe,createBudget:()=>xn,createFileEcritures:()=>Tn,createScheduler:()=>Mn,escapeHtml:()=>h,fetchBorne:()=>wn,flattenPresence:()=>dn,formatMessageBody:()=>ne,initials:()=>Ne,isMentioned:()=>bn,isMine:()=>T,presenceId:()=>_n,reactorId:()=>re,referenceAuteur:()=>yn,shouldNotify:()=>hn,unreadLabel:()=>vn});var x={attendeeKey:\"3dd-present-attkey\",authorToken:\"3dd-present-authtoken\",chatMuted:\"3dd-present-mute\",presenceId:\"3dd-present-uid\"};function h(e){return String(e==null?\"\":e).replace(/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\")}function Ne(e){let n=(e||\"?\").trim().split(/\\s+/);return(((n[0]||\"\")[0]||\"?\")+((n[1]||\"\")[0]||\"\")).toUpperCase()}function ee(e,n){return e?`<img src=\"${h(e)}\" alt=\"\">`:h(Ne(n))}function ne(e){let n=h(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 dn(e){let n=new Map;for(let t in e||{})for(let r of(e||{})[t]||[]){let o=String(r.uid||r.email||r.name||\"\").toLowerCase()||`_${t}_${n.size}`;(!n.get(o)||r.role===\"presenter\")&&n.set(o,r)}return[...n.values()]}function pn(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function fn(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function mn(e,n=\"anon\",t=te){try{if(!e)return`anon-${n}`;let r=e.getItem(x.attendeeKey);if(r)return r;let o=`anon-${t()}`;return e.setItem(x.attendeeKey,o),o}catch(r){return`anon-${n}`}}function te(){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 gn(e,n=te){let t=pn(e,x.authorToken);if(t)return t;let r=n();return fn(e,x.authorToken,r),r}async function yn(e,n){let t=n!==void 0?n:typeof globalThis!=\"undefined\"&&globalThis.crypto?globalThis.crypto.subtle:null;if(!t||!e)return\"\";try{let r=async o=>{let s=await t.digest(\"SHA-256\",new TextEncoder().encode(o));return Array.from(new Uint8Array(s),a=>a.toString(16).padStart(2,\"0\")).join(\"\")};return(await r(\"ref:\"+await r(e))).slice(0,16)}catch(r){return\"\"}}function re(e){return String(e&&e.ref||\"\")}function T(e,n){return!e||!n||!e.author_ref||!n.ref?!1:e.author_ref===n.ref}function oe(e){return!!(e&&e.role===\"presenter\")}function bn(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 hn(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||T(n,t)?!1:o}function vn(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function Mn(e,n={}){var y;let t=(y=n.minMs)!=null?y:300,r=n.now||(()=>Date.now()),o=n.setTimer||((d,w)=>setTimeout(d,w)),s=n.clearTimer||(d=>clearTimeout(d)),a=!1,l=!1,p=-1/0,g=null,f=!1;function c(){if(f||a){l=!0;return}let d=t-(r()-p);if(d>0){u(d);return}a=!0,l=!1,p=r();let w=!1;e(()=>{w||(w=!0,a=!1,l&&!f&&c())})}function u(d){f||g!==null||(g=o(()=>{g=null,c()},Math.max(0,d)))}return{signaler(){f||c()},maintenant(){f||(p=-1/0,c())},arreter(){f=!0,g!==null&&(s(g),g=null)}}}function wn(e,n,t=J,r={}){let o=r.fetch||(typeof fetch==\"function\"?fetch:void 0);if(!o)return Promise.reject(new Error(\"fetch indisponible\"));let s=r.setTimer||((u,y)=>setTimeout(u,y)),a=r.clearTimer||(u=>clearTimeout(u)),l=null;try{l=new AbortController}catch(u){}let p=l?xe(G({},n||{}),{signal:l.signal}):G({},n||{}),g=null,f=new Promise((u,y)=>{g=s(()=>{try{l==null||l.abort()}catch(d){}y(new Error(\"delai\"))},t)}),c=()=>{try{a(g)}catch(u){}};return Promise.race([o(e,p),f]).then(u=>(c(),u),u=>{throw c(),u})}function xn(e){let n=e.now||(()=>Date.now()),t=Math.max(1,e.rafale),r=t,o=n();return{prendre(){let s=n();return r=Math.min(t,r+(s-o)*e.parHeure/36e5),o=s,r<1?!1:(r-=1,!0)},restants:()=>r}}function Tn(e={}){var g;let n=(g=e.minMs)!=null?g:0,t=e.now||(()=>Date.now()),r=e.setTimer||((f,c)=>setTimeout(f,c)),o=new Map,s=!1,a=-1/0,l=!1;function p(){if(s||o.size===0)return;let f=n-(t()-a);if(f>0){if(l)return;l=!0,r(()=>{l=!1,p()},f);return}let c=o.keys().next().value,u=o.get(c);o.delete(c),s=!0,a=t(),Promise.resolve().then(u.tache).then(y=>u.regler(y),()=>u.regler(null)).then(()=>{s=!1,p()})}return{poser(f,c){return new Promise(u=>{let y=o.get(f);o.set(f,{tache:c,regler:d=>{y&&y.regler(d),u(d)}}),p()})},enAttente:()=>o.size}}function _n(e,n=te){try{if(e){let t=e.getItem(x.presenceId);if(t)return t;let r=`p-${n()}`;return e.setItem(x.presenceId,r),r}}catch(t){}return`p-${n()}`}var H={};M(H,{messageClassName:()=>Sn,renderActions:()=>$e,renderAttachment:()=>Oe,renderMessage:()=>En,renderReactions:()=>Le});function Le(e,n){let t=e&&e.reactions||{},r=re(n),o=\"\";for(let s in t){let a=t[s]||[];if(!a.length)continue;let l=a.indexOf(r)>=0;o+=`<button class=\"re-chip${l?\" on\":\"\"}\" data-e=\"${h(s)}\">${h(s)} ${a.length}</button>`}return o}function Oe(e){if(!e||!e.url)return\"\";let n=h(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=h(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 $e(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||oe(n.me))&&(r+='<button class=cm-del-btn title=\"Supprimer\">\\u{1F5D1}</button>'),r}function En(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>${h(e.reply_name||\"\")}</b> ${h(e.reply_text||\"\")}</div>`:\"\",r=e.edited?\" <span class=cm-ed>(modifi\\xE9)</span>\":\"\",o=e.body?`<div class=txt>${ne(e.body)}${r}</div>`:\"\",s=e.is_presenter?\" <span class=tag>pr\\xE9sentateur</span>\":\"\";return`<span class=a>${ee(e.author_avatar,e.author_name)}</span><span class=b><div class=who><b>${h(e.author_name||\"Invit\\xE9\")}</b>${s}</div>`+t+o+Oe(e.attachment)+`<div class=cm-re>${Le(e,n.me)}</div></span><span class=cm-act>${$e(e,n)}</span>`}function Sn(e,n,t){return\"cm\"+(T(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var U={};M(U,{DEFAULT_CENTER:()=>ae,DEFAULT_ZOOM:()=>ue,MAP_TYPES:()=>P,cycleMapType:()=>kn,initialMapContent:()=>Pn,mapTypeLabel:()=>Rn,sanitizeContent:()=>le});var P=[\"roadmap\",\"satellite\",\"hybrid\"],ae=[46.6,2.5],ue=6;function _(e){let n=Number(e);return Number.isFinite(n)?n:null}function se(e){if(!Array.isArray(e))return null;let n=_(e[0]),t=_(e[1]);return n!=null&&t!=null?[n,t]:null}var ie=(e,n,t)=>Math.max(n,Math.min(t,e));function le(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=se(n.position);if(!t)return null;let r=n.pov&&typeof n.pov==\"object\"?n.pov:null,o=r?{heading:_(r.heading)||0,pitch:ie(_(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:ie(_(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:se(n.center)||[...ae],zoom:ie(Math.trunc(_(n.zoom)||ue),1,21),marker:se(n.marker),mapType:P.includes(n.mapType)?n.mapType:null,label:String(n.label||\"\").slice(0,160)||null}}function kn(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function Rn(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function Pn(){return{kind:\"map\",center:[...ae],zoom:ue,marker:null,mapType:null,label:null}}var D={};M(D,{CURRENT_PAGE_MARGIN:()=>On,DEFAULT_ASPECT:()=>Cn,MAX_ZOOM:()=>An,MIN_PAGE_WIDTH:()=>Nn,MIN_ZOOM:()=>In,PRERENDER_MARGIN:()=>Ln,arrowState:()=>Un,averageColor:()=>Vn,clampPage:()=>He,clampZoom:()=>$n,fitWidth:()=>Wn,isImageDocument:()=>zn,progressPercent:()=>Hn});var In=.5,An=3,Cn=1.35,Nn=280,Ln=\"500px 0px\",On=\"-48% 0px -48% 0px\";function $n(e){let n=Math.round((Number(e)||0)*10)/10;return Math.max(.5,Math.min(3,n))}function He(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function Hn(e,n){return n?Math.round(He(e,n)/n*100):0}function Un(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var Dn=28;function Wn(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)-Dn-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function Vn(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 zn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var W={};M(W,{presentationTransition:()=>Fn,switchDocUrl:()=>Bn});function Fn(e,n={}){if(!e)return[{kind:\"nothing\"}];if(e.active===!1)return[{kind:\"ended\"}];let t=le(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 Bn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return je(jn);})();", SOURCE_HASH: "95b81425c70778a0" };
|
package/server/gabarit-live.js
CHANGED
|
@@ -568,7 +568,11 @@ var Live=(function(){
|
|
|
568
568
|
ch.on('broadcast',{event:'map'},function(){if(_onMap)_onMap();});
|
|
569
569
|
ch.subscribe(function(st){if(st==='SUBSCRIBED'){REFPRETE.then(function(){ME.ref=MOIREF;ch.track({name:me.name,ref:MOIREF,avatar:me.avatar,role:me.role,member:!!me.member,uid:presId()});sendAttend();});}});
|
|
570
570
|
_tyIv=setInterval(renderTyping,1500);
|
|
571
|
-
|
|
571
|
+
// ⚠️ Battement JITTERÉ (±15 %), pas un setInterval fixe (P1 performance) : 250 onglets
|
|
572
|
+
// ouverts à la même seconde produiraient une rafale synchronisée toutes les 25 s. Un
|
|
573
|
+
// setTimeout qui se replanifie avec un délai varié étale la charge. clearInterval(_atIv)
|
|
574
|
+
// efface aussi un id de setTimeout (même espace de timers dans le navigateur).
|
|
575
|
+
(function planAttend(){ _atIv=setTimeout(function(){ try{sendAttend();}catch(e){} planAttend(); }, 25000*(0.85+Math.random()*0.30)); })();
|
|
572
576
|
// Filet de sécurité : au déchargement de la page/iframe (fermeture, reload, switch), on retire la présence
|
|
573
577
|
// → évite les fantômes (« je me vois deux fois » au retour). Une seule fois.
|
|
574
578
|
if(!_phWired){_phWired=true;window.addEventListener('pagehide',function(){try{clearInterval(_filet);_ordEtat.arreter();_ordChat.arreter();}catch(e){}try{if(ch){ch.untrack();ch.unsubscribe();ch=null;}}catch(e){}});}
|
package/server/presentations.js
CHANGED
|
@@ -582,8 +582,12 @@ async function addMessage(slug, { name, email, avatar, isPresenter, isMember, bo
|
|
|
582
582
|
* garde qui dépend de ce qu'on n'a pas demandé cède au premier champ qu'on demande.
|
|
583
583
|
*/
|
|
584
584
|
async function listMessages(slug) {
|
|
585
|
-
|
|
586
|
-
|
|
585
|
+
// ⚠️ LES 300 PLUS RÉCENTS, PAS LES 300 PLUS ANCIENS (P1 performance). Avec `asc&limit=300`, au
|
|
586
|
+
// 301e message les nouveaux ne revenaient JAMAIS à ceux qui relisaient — seul l'auteur le voyait,
|
|
587
|
+
// par la réponse du POST. On trie `desc` (id départage created_at à la milliseconde) puis on
|
|
588
|
+
// REND en ordre chronologique, comme l'affichage l'attend.
|
|
589
|
+
const rows = await PLAYER.db.request(`doc_presentation_messages?slug=eq.${enc(String(slug || ""))}&select=id,author_name,author_avatar,author_hash,is_presenter,is_member,body,attachment,reactions,reply_to,reply_name,reply_text,deleted,edited,created_at&order=created_at.desc,id.desc&limit=300`);
|
|
590
|
+
return Array.isArray(rows) ? rows.slice().reverse().map(messagePublic) : [];
|
|
587
591
|
}
|
|
588
592
|
|
|
589
593
|
// Éditer son message (jeton d'auteur requis). Ne touche pas aux messages supprimés.
|
|
@@ -727,9 +731,12 @@ async function toggleReaction(slug, msgId, emoji, reactor, etat) {
|
|
|
727
731
|
// Heartbeat d'un participant : upsert de sa ligne d'assistance. On accumule le temps de présence (intervalles
|
|
728
732
|
// < 60 s → un aller-retour ne gonfle pas total_ms) et l'ensemble des pages vues (page courante de la présentation).
|
|
729
733
|
const ATTEND_MAX_GAP_MS = 60 * 1000;
|
|
730
|
-
async function recordAttendance(slug, { key, name, email, avatar, isMember, isPresenter }) {
|
|
734
|
+
async function recordAttendance(slug, presDejaCharge, { key, name, email, avatar, isMember, isPresenter }) {
|
|
731
735
|
if (!slug || !key) return { ok: false, status: 400 };
|
|
732
|
-
|
|
736
|
+
// ⚠️ La route vient DÉJÀ de charger la présentation (contrôle présentateur) : on la reçoit plutôt
|
|
737
|
+
// que d'en refaire une lecture par battement (P1 performance — un battement = un aller-retour DB
|
|
738
|
+
// de moins). Repli sur une lecture si l'appelant ne la fournit pas.
|
|
739
|
+
const pres = presDejaCharge || await getPresentation(slug);
|
|
733
740
|
if (!pres) return { ok: false, status: 404 };
|
|
734
741
|
// ⚠️ La lecture est DÉJÀ faite ici : on s'en sert plutôt que d'en refaire une. Un battement de
|
|
735
742
|
// présence sur une session close n'a rien à mettre à jour — et il en arrive à chaque onglet
|
package/server/routes-direct.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
const { adresseAppelant, lcMembre, cleAnonyme, profilDuJeton } = require("./appelant");
|
|
5
5
|
|
|
6
6
|
const { createPresentation, getPresentation, setPage, endPresentation, addMessage, toggleReaction, editMessage, deleteMessage, setChatLock, createUploadUrl, reclaimPresentation, touchPresentation, listActivePresentations, handoverPresentation, endPresentationByOwner, recordAttendance, presentationStats, listPresentationsForDoc, switchPresentationDoc, setPresentationContent } = require("./presentations");
|
|
7
|
+
// Cadence de présence et cible de mutualisation par IP (P1 performance). L'intervalle DOIT
|
|
8
|
+
// refléter celui du navigateur (gabarit-live.js) ; la cible est un vrai événement en salle.
|
|
9
|
+
const ATTENDANCE_INTERVAL_MS = 25_000;
|
|
10
|
+
const ATTENDEES_PER_EGRESS = 250;
|
|
7
11
|
let PLAYER = null;
|
|
8
12
|
const init = (ctx) => { PLAYER = ctx; };
|
|
9
13
|
// Même délégation que dans handler : la règle vit chez l'hôte.
|
|
@@ -55,7 +59,14 @@ async function traiter(req, res, body, _slug) {
|
|
|
55
59
|
const jp = (status, obj) => { res.statusCode = status; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify(obj)); };
|
|
56
60
|
try {
|
|
57
61
|
const ip = adresseAppelant(req) || "anon";
|
|
58
|
-
|
|
62
|
+
// ⚠️ QUOTA DÉRIVÉ DE LA CADENCE, PAS 1000 EN DUR (P1 performance). Un participant émet un
|
|
63
|
+
// battement toutes les ATTENDANCE_INTERVAL_MS ; le quota vise ATTENDEES_PER_EGRESS
|
|
64
|
+
// participants derrière une même IP (bureau, Wi-Fi d'événement partagent une sortie).
|
|
65
|
+
// 1000/h ne couvrait que ~6 participants — le 7e prenait des 429. Marge ×1,3 pour les
|
|
66
|
+
// battements immédiats (arrivée, changement de page, pagehide).
|
|
67
|
+
const battementsParH = Math.ceil(3_600_000 / ATTENDANCE_INTERVAL_MS);
|
|
68
|
+
const quotaPresence = Math.ceil(battementsParH * ATTENDEES_PER_EGRESS * 1.3);
|
|
69
|
+
const allowed = await PLAYER.limits.allow(`patt:${ip}`, quotaPresence, 3600);
|
|
59
70
|
if (!allowed) return jp(429, { ok: false, error: "rate" });
|
|
60
71
|
// ⚠️ CES DEUX BOOLÉENS ÉTAIENT CEUX DE L'APPELANT. `isMember` sépare la population interne
|
|
61
72
|
// de celle des prospects — c'est la promesse même du produit — et `isPresenter` accorde le
|
|
@@ -83,7 +94,7 @@ async function traiter(req, res, body, _slug) {
|
|
|
83
94
|
// On dérive donc la clé de ce qui est prouvé, et jamais de ce qui est affirmé. Un anonyme,
|
|
84
95
|
// lui, ne peut rien prouver : sa clé reste la sienne, mais enfermée dans un espace de noms
|
|
85
96
|
// dont elle ne peut pas sortir — elle ne pourra jamais ressembler à l'e-mail d'un membre.
|
|
86
|
-
const r = await recordAttendance(String(body.slug || ""), {
|
|
97
|
+
const r = await recordAttendance(String(body.slug || ""), pres, {
|
|
87
98
|
// ⚠️ MINUSCULÉE, ET CE DÉTAIL EST UNE LIGNE DE PRÉSENCE. La clé cliente que 0.1.42 a
|
|
88
99
|
// remplacée l'était (`me.email.toLowerCase()`) ; la clé dérivée ne l'était pas, et la
|
|
89
100
|
// ligne se retrouve par `attendee_key=eq.` — une correspondance EXACTE. Un hôte dont
|
package/server/routes-liens.js
CHANGED
|
@@ -443,7 +443,16 @@ async function traiter(req, res, body, slug) {
|
|
|
443
443
|
return;
|
|
444
444
|
}
|
|
445
445
|
const share = await getShareBySlug(body.slug || slug);
|
|
446
|
-
|
|
446
|
+
// ⚠️ PLAFOND ANTI-INONDATION PAR IP (P1 performance). Le chemin analytique externe écrivait
|
|
447
|
+
// SANS quota : un slug public permettait des écritures illimitées (base gonflée, stats
|
|
448
|
+
// faussées). Même quota dérivé de la cadence que l'interne (SESSION_QUOTA_PER_HOUR par IP).
|
|
449
|
+
// Dépassé : on n'écrit pas — mais on répond 200, car une mesure ne doit pas casser une lecture.
|
|
450
|
+
const ipTrack = ip0 || "anon";
|
|
451
|
+
const sousQuota = await PLAYER.limits.allow(`sess:${ipTrack}`, SESSION_QUOTA_PER_HOUR, 3600);
|
|
452
|
+
if (share && !share.is_test && !sousQuota) {
|
|
453
|
+
try { if (await PLAYER.limits.allow("sess:quota-avert", 1, 3600)) PLAYER.errors.capture(new Error(`télémétrie externe refusée : quota horaire atteint (${SESSION_QUOTA_PER_HOUR}/h par adresse)`), { route: "track" }); } catch { /* jamais bloquant */ }
|
|
454
|
+
}
|
|
455
|
+
if (share && !share.is_test && sousQuota) { // répétition générale : la lecture de test ne compte pas dans les stats
|
|
447
456
|
try {
|
|
448
457
|
// 'session' = résumé riche (temps par page, appareil) → upsert ; open/page/heartbeat → journal léger (funnel/overview).
|
|
449
458
|
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 });
|
|
@@ -101,7 +101,7 @@ function initialMapContent() {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// src/cadence.ts
|
|
104
|
-
var SESSION_INTERVAL_MS =
|
|
104
|
+
var SESSION_INTERVAL_MS = 3e4;
|
|
105
105
|
var SESSION_WRITES_PER_HOUR = Math.ceil(36e5 / SESSION_INTERVAL_MS);
|
|
106
106
|
var READERS_PER_EGRESS = 25;
|
|
107
107
|
var SESSION_QUOTA_PER_HOUR = SESSION_WRITES_PER_HOUR * READERS_PER_EGRESS;
|
package/server/shares.js
CHANGED
|
@@ -277,14 +277,14 @@ function parseUa(ua) {
|
|
|
277
277
|
// Upsert d'une session de consultation (résumé envoyé périodiquement par la visionneuse). Stocke le temps
|
|
278
278
|
// PAR page (cumulatif côté client → on remplace), totaux, appareil. Conserve started_at (insert) via merge.
|
|
279
279
|
async function upsertSession(share, p, { ip, ua }) {
|
|
280
|
-
const num = (v) => (Number.isFinite(+v) ? Math.trunc(+v) : null);
|
|
281
280
|
const sessionId = String(p.sessionId || "").slice(0, 64);
|
|
282
281
|
if (!sessionId) return;
|
|
283
|
-
const pagesTime = (p.pagesTime && typeof p.pagesTime === "object") ? p.pagesTime : {};
|
|
284
282
|
const { device, os, browser } = parseUa(ua);
|
|
285
283
|
const row = {
|
|
286
284
|
session_id: sessionId, slug: share.slug, doc_id: share.doc_id, recipient_email: share.recipient_email,
|
|
287
|
-
|
|
285
|
+
// Bornées comme la session INTERNE : plafond d'entrées, clés/valeurs numériques, totaux capés.
|
|
286
|
+
num_pages: bornerNombre(p.numPages, BORNES.pages), max_page: bornerNombre(p.maxPage, BORNES.pages),
|
|
287
|
+
total_seconds: bornerNombre(p.totalSeconds, BORNES.secondes) || 0, pages_time: bornerPagesTime(p.pagesTime),
|
|
288
288
|
ua: String(ua || "").slice(0, 300), ip: String(ip || "").slice(0, 60), device, os, browser, last_at: new Date().toISOString(),
|
|
289
289
|
};
|
|
290
290
|
// started_at non touché par l'upsert (default à l'insert ; merge ne l'écrase pas car absent du body).
|
|
@@ -370,6 +370,21 @@ async function setShareAuth(slug, requireAuth) {
|
|
|
370
370
|
// que l'hôte se porte garant de l'identité (jeton signé) : c'est le second volet, et il est
|
|
371
371
|
// signalé au point d'entrée plutôt qu'ici.
|
|
372
372
|
const BORNES = { pages: 10_000, secondes: 24 * 3600, entreesPagesTime: 2_000 };
|
|
373
|
+
// ⚠️ BORNER CE QUI VIENT DU DEHORS — les DEUX chemins de session (interne ET externe). Un objet
|
|
374
|
+
// libre sans plafond laisse un seul appel écrire un JSON de la taille qu'il veut (P1 performance).
|
|
375
|
+
const bornerNombre = (v, max) => { const n = Number.isFinite(+v) ? Math.trunc(+v) : null; return n == null ? null : Math.max(0, Math.min(n, max)); };
|
|
376
|
+
function bornerPagesTime(brut) {
|
|
377
|
+
const src = (brut && typeof brut === "object" && !Array.isArray(brut)) ? brut : {};
|
|
378
|
+
const out = {};
|
|
379
|
+
let n = 0;
|
|
380
|
+
for (const k in src) {
|
|
381
|
+
if (++n > BORNES.entreesPagesTime) break;
|
|
382
|
+
const page = Number.isFinite(+k) ? Math.trunc(+k) : null;
|
|
383
|
+
if (page == null || page < 0 || page > BORNES.pages) continue;
|
|
384
|
+
out[String(page)] = bornerNombre(src[k], BORNES.secondes) || 0;
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
373
388
|
|
|
374
389
|
// ⚠️ `ip` N'EST PLUS LU, ET LA SIGNATURE LE DIT. L'appelant continue de le passer — il ne sait pas
|
|
375
390
|
// ce que chaque table conserve, et ce n'est pas à lui de le savoir. Mais le garder en paramètre
|