discovery-media-player 0.1.116 → 0.1.118
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 +10 -0
- package/package.json +1 -1
- package/server/browser.generated.js +1 -1
- package/server/cache.js +60 -8
- package/server/handler.js +27 -6
- package/server/page-audience.js +70 -9
- package/server/shared.generated.js +10 -0
package/context/standalone.js
CHANGED
|
@@ -53,8 +53,18 @@ function creerDb(env) {
|
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
const methode = String(options.method || "GET").toUpperCase();
|
|
56
|
+
// ⚠️ UN ABANDON RÉEL, PAS UNE COURSE DE PROMESSES. Sans signal, une base qui ne répond pas
|
|
57
|
+
// immobilise la requête, sa socket ET la place d'admission du cache jusqu'à ce que la plateforme
|
|
58
|
+
// tue la fonction : le plafond d'admission se remplit alors et n'est jamais rendu, ce qui
|
|
59
|
+
// transforme un ralentissement en refus général. Une course `Promise.race` ne suffirait pas —
|
|
60
|
+
// elle rendrait la main sans ANNULER le fetch, donc sans libérer quoi que ce soit. L'hôte peut
|
|
61
|
+
// fournir son propre `signal` (opérations longues : purges, transferts). (Audit externe.)
|
|
62
|
+
const delai = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : 15000;
|
|
63
|
+
const signal = options.signal || (typeof AbortSignal !== "undefined" && AbortSignal.timeout
|
|
64
|
+
? AbortSignal.timeout(delai) : undefined);
|
|
56
65
|
const r = await fetch(`${url}/rest/v1/${chemin}`, {
|
|
57
66
|
method: methode,
|
|
67
|
+
...(signal ? { signal } : {}),
|
|
58
68
|
headers: {
|
|
59
69
|
apikey: cle,
|
|
60
70
|
Authorization: `Bearer ${cle}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.118",
|
|
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,We=Object.defineProperties,Ve=Object.getOwnPropertyDescriptor,ze=Object.getOwnPropertyDescriptors,Fe=Object.getOwnPropertyNames,ve=Object.getOwnPropertySymbols;var we=Object.prototype.hasOwnProperty,Be=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,K=(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))Be.call(n,t)&&Me(e,t,n[t]);return e},xe=(e,n)=>We(e,ze(n));var M=(e,n)=>{for(var t in n)C(e,t,{get:n[t],enumerable:!0})},je=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Fe(n))!we.call(e,o)&&o!==t&&C(e,o,{get:()=>n[o],enumerable:!(r=Ve(n,o))||r.enumerable});return e};var Ge=e=>je(C({},\"__esModule\",{value:!0}),e);var Kn={};M(Kn,{bridge:()=>N,cadence:()=>L,chat:()=>U,live:()=>H,presentation:()=>D,presentationState:()=>V,tracking:()=>$,viewer:()=>W});var N={};M(N,{onHostMessage:()=>tn,onPlayerMessage:()=>nn,parseHostMessage:()=>Te,parsePlayerMessage:()=>Ee,sendToHost:()=>Xe,sendToPlayer:()=>Je,toWire:()=>Z});var Y=\"3dd-doc-\",Ke=/^[A-Za-z0-9_-]{1,64}$/,Ye=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ze=[\"embed-denied\"],qe=[\"present-invite\",\"present-handover\",\"present-switch\"],Qe=[\"handover-done\"];function Z(e){let n={type:Y+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 _e(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(Y))return null;let o=r.slice(Y.length);if(n.includes(o))return{type:o};if(Ze.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 Ee(e){return _e(e,Ye,qe)}function Te(e){return _e(e,Qe,[])}function Xe(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(Z(e),\"*\")}catch(t){}}function Je(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(Z(n),\"*\")}catch(r){}}function en(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function nn(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!en(t,s.source))return;let a=Ee(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function tn(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=Te(a.data);l&&e(l)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var $={};M($,{createTracker:()=>pn});var L={};M(L,{PRESENTER_ACTIONS_PER_HOUR:()=>X,PRESENT_CACHE_MS:()=>an,PRESENT_QUOTA_MARGIN:()=>Ne,PRESENT_QUOTA_PER_HOUR:()=>un,PRESENT_READS_PER_HOUR:()=>Ie,PRESENT_READ_BURST:()=>Ae,PRESENT_READ_COALESCE_MS:()=>Ce,PRESENT_RESYNC_MS:()=>Re,PRESENT_SIGNAL_BUDGET_PER_HOUR:()=>sn,PRESENT_WRITE_TIMEOUT_MS:()=>J,READERS_PER_EGRESS:()=>O,RESYNC_READS_PER_HOUR:()=>Pe,SESSION_IDLE_MS:()=>Q,SESSION_INTERVAL_MS:()=>q,SESSION_QUOTA_PER_HOUR:()=>rn,SESSION_WRITES_PER_HOUR:()=>Se,VIEW_EVENTS_PER_READER_HOUR:()=>ke,VIEW_QUOTA_PER_HOUR:()=>on});var q=3e4,Se=Math.ceil(120),O=25,rn=Se*O,ke=720,on=ke*O,Q=18e4,Re=25e3,Pe=Math.ceil(36e5/Re),X=720,Ie=Pe+X,sn=X,Ae=20,J=1e4,Ce=400,an=Ce,Ne=4,un=(Ie+Ae)*O*Ne;var ln=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function cn(){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 dn(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;return 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}),!0)}catch(r){return!1}}}function pn(e={}){var be,ye;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=(be=e.idleMs)!=null?be:Q,a=(ye=e.sessionEveryMs)!=null?ye:3e4,l=e.slug||\"\",p=e.internal||null,g=e.sessionId||cn(),f=e.send||dn(o,n),c={},u=0,b=0,d=0,w=0,v=null,T=r(),S=!1,z=!1,I=[],De=()=>!!l||!!p,ce=i=>{if(!De())return!1;try{return f(i)!==!1}catch(m){return!1}},F=()=>!!t&&t.visibilityState===\"visible\"&&!S,de=i=>{let m=r()-i,y=Math.max(0,T-i)+s;return Math.max(0,Math.min(m,y))},B=()=>{v!=null&&u>0&&(c[u]=(c[u]||0)+de(v)/1e3),v=F()?r():null},pe=()=>{B(),v=null},j=()=>{v==null&&F()&&(v=r())},G=()=>{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=()=>{T=r(),S&&(S=!1,j())},me=(i,m)=>{p||ce({slug:l,event:i,page:m||b,maxPage:d,seconds:G(),sessionId:g})},ge=\"\",A=()=>{B();let i=G(),m=`${i}|${d}|${w}`;if(m===ge)return;let y={event:\"session\",sessionId:g,numPages:w,maxPage:d,totalSeconds:i,pagesTime:fe()};p?(y.internal=!0,y.docId=p.docId,y.email=p.email,y.name=p.name,p.it&&(y.it=p.it)):y.slug=l,ce(y)&&(ge=m)},R=(i,m,y,he)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(m,y,he),I.push(()=>{i.removeEventListener(m,y,he)}))};return{sessionId:g,totalSeconds:G,maxPage:()=>d,pageTimes:fe,start(){if(z)return;z=!0,me(\"open\",1),u=1,T=r(),v=F()?r():null,R(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(pe(),A()):(k(),j())}),R(n,\"focus\",()=>{k(),j()});for(let y of ln)R(n,y,k,{passive:!0});R(e.scrollElement,\"scroll\",k,{passive:!0}),R(n,\"beforeunload\",A);let i=n.setInterval(()=>{!S&&r()-T>s&&(S=!0,pe())},5e3),m=n.setInterval(A,a);I.push(()=>{n.clearInterval(i),n.clearInterval(m)})},setPage(i){!i||i===b||(T=r(),B(),u=i,b=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){}}z=!1}}}var H={};M(H,{STORAGE_KEYS:()=>x,attendeeKey:()=>bn,authorToken:()=>yn,avatarHtml:()=>ee,canModerate:()=>oe,createBudget:()=>En,createFileEcritures:()=>Tn,createScheduler:()=>xn,escapeHtml:()=>h,fetchBorne:()=>_n,flattenPresence:()=>fn,formatMessageBody:()=>ne,initials:()=>Oe,isMentioned:()=>vn,isMine:()=>_,presenceId:()=>Sn,reactorId:()=>re,referenceAuteur:()=>hn,shouldNotify:()=>Mn,unreadLabel:()=>wn});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 Oe(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(Oe(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 fn(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 mn(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function gn(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function bn(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 yn(e,n=te){let t=mn(e,x.authorToken);if(t)return t;let r=n();return gn(e,x.authorToken,r),r}async function hn(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 _(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 vn(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}`)&&!_(e,n):!1}function Mn(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||_(n,t)?!1:o}function wn(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function xn(e,n={}){var b;let t=(b=n.minMs)!=null?b: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 _n(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,b)=>setTimeout(u,b)),a=r.clearTimer||(u=>clearTimeout(u)),l=null;try{l=new AbortController}catch(u){}let p=l?xe(K({},n||{}),{signal:l.signal}):K({},n||{}),g=null,f=new Promise((u,b)=>{g=s(()=>{try{l==null||l.abort()}catch(d){}b(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 En(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(b=>u.regler(b),()=>u.regler(null)).then(()=>{s=!1,p()})}return{poser(f,c){return new Promise(u=>{let b=o.get(f);o.set(f,{tache:c,regler:d=>{b&&b.regler(d),u(d)}}),p()})},enAttente:()=>o.size}}function Sn(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 U={};M(U,{messageClassName:()=>Rn,renderActions:()=>He,renderAttachment:()=>$e,renderMessage:()=>kn,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 $e(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 He(e,n){let 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 kn(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+$e(e.attachment)+`<div class=cm-re>${Le(e,n.me)}</div></span><span class=cm-act>${He(e,n)}</span>`}function Rn(e,n,t){return\"cm\"+(_(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var D={};M(D,{DEFAULT_CENTER:()=>ae,DEFAULT_ZOOM:()=>ue,MAP_TYPES:()=>P,cycleMapType:()=>Pn,initialMapContent:()=>An,mapTypeLabel:()=>In,sanitizeContent:()=>le});var P=[\"roadmap\",\"satellite\",\"hybrid\"],ae=[46.6,2.5],ue=6;function E(e){let n=Number(e);return Number.isFinite(n)?n:null}function se(e){if(!Array.isArray(e))return null;let n=E(e[0]),t=E(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:E(r.heading)||0,pitch:ie(E(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:ie(E(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:se(n.center)||[...ae],zoom:ie(Math.trunc(E(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 Pn(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function In(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function An(){return{kind:\"map\",center:[...ae],zoom:ue,marker:null,mapType:null,label:null}}var W={};M(W,{CURRENT_PAGE_MARGIN:()=>Hn,DEFAULT_ASPECT:()=>On,MAX_ZOOM:()=>Nn,MIN_PAGE_WIDTH:()=>Ln,MIN_ZOOM:()=>Cn,PRERENDER_MARGIN:()=>$n,arrowState:()=>Wn,averageColor:()=>Fn,clampPage:()=>Ue,clampZoom:()=>Un,fitWidth:()=>zn,isImageDocument:()=>Bn,progressPercent:()=>Dn});var Cn=.5,Nn=3,On=1.35,Ln=280,$n=\"500px 0px\",Hn=\"-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 Ue(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function Dn(e,n){return n?Math.round(Ue(e,n)/n*100):0}function Wn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var Vn=28;function zn(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)-Vn-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function Fn(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 Bn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var V={};M(V,{presentationTransition:()=>jn,switchDocUrl:()=>Gn});function jn(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 Gn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Ge(Kn);})();", SOURCE_HASH: "9b51a94c89b8d54f" };
|
|
7
|
+
module.exports = { PLAYER_BROWSER_JS: "\"use strict\";var Player=(()=>{var C=Object.defineProperty,We=Object.defineProperties,Ve=Object.getOwnPropertyDescriptor,ze=Object.getOwnPropertyDescriptors,Fe=Object.getOwnPropertyNames,ve=Object.getOwnPropertySymbols;var we=Object.prototype.hasOwnProperty,Be=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,K=(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))Be.call(n,t)&&Me(e,t,n[t]);return e},xe=(e,n)=>We(e,ze(n));var M=(e,n)=>{for(var t in n)C(e,t,{get:n[t],enumerable:!0})},je=(e,n,t,r)=>{if(n&&typeof n==\"object\"||typeof n==\"function\")for(let o of Fe(n))!we.call(e,o)&&o!==t&&C(e,o,{get:()=>n[o],enumerable:!(r=Ve(n,o))||r.enumerable});return e};var Ge=e=>je(C({},\"__esModule\",{value:!0}),e);var Kn={};M(Kn,{bridge:()=>N,cadence:()=>L,chat:()=>U,live:()=>H,presentation:()=>D,presentationState:()=>V,tracking:()=>$,viewer:()=>W});var N={};M(N,{onHostMessage:()=>tn,onPlayerMessage:()=>nn,parseHostMessage:()=>Te,parsePlayerMessage:()=>Ee,sendToHost:()=>Xe,sendToPlayer:()=>Je,toWire:()=>Z});var Y=\"3dd-doc-\",Ke=/^[A-Za-z0-9_-]{1,64}$/,Ye=[\"close\",\"share\",\"embed-ready\",\"present-left\",\"present-denied\"],Ze=[\"embed-denied\"],qe=[\"present-invite\",\"present-handover\",\"present-switch\"],Qe=[\"handover-done\"];function Z(e){let n={type:Y+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 _e(e,n,t){if(!e||typeof e!=\"object\")return null;let r=e.type;if(typeof r!=\"string\"||!r.startsWith(Y))return null;let o=r.slice(Y.length);if(n.includes(o))return{type:o};if(Ze.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 Ee(e){return _e(e,Ye,qe)}function Te(e){return _e(e,Qe,[])}function Xe(e,n){try{let t=n||(typeof window!=\"undefined\"?window.parent:null);t&&t.postMessage(Z(e),\"*\")}catch(t){}}function Je(e,n){var t;try{(t=e==null?void 0:e.contentWindow)==null||t.postMessage(Z(n),\"*\")}catch(r){}}function en(e,n){var r;if(!e)return!0;let t=(r=e.contentWindow)!=null?r:e;return n===t}function nn(e,n,t){let r=n||(typeof window!=\"undefined\"?window:null);if(!r)return()=>{};let o=s=>{if(!en(t,s.source))return;let a=Ee(s.data);a&&e(a)};return r.addEventListener(\"message\",o),()=>r.removeEventListener(\"message\",o)}function tn(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=Te(a.data);l&&e(l)};return t.addEventListener(\"message\",o),()=>t.removeEventListener(\"message\",o)}var $={};M($,{createTracker:()=>pn});var L={};M(L,{PRESENTER_ACTIONS_PER_HOUR:()=>X,PRESENT_CACHE_MS:()=>an,PRESENT_QUOTA_MARGIN:()=>Ne,PRESENT_QUOTA_PER_HOUR:()=>un,PRESENT_READS_PER_HOUR:()=>Ie,PRESENT_READ_BURST:()=>Ae,PRESENT_READ_COALESCE_MS:()=>Ce,PRESENT_RESYNC_MS:()=>Re,PRESENT_SIGNAL_BUDGET_PER_HOUR:()=>sn,PRESENT_WRITE_TIMEOUT_MS:()=>J,READERS_PER_EGRESS:()=>O,RESYNC_READS_PER_HOUR:()=>Pe,SESSION_IDLE_MS:()=>Q,SESSION_INTERVAL_MS:()=>q,SESSION_QUOTA_PER_HOUR:()=>rn,SESSION_WRITES_PER_HOUR:()=>Se,VIEW_EVENTS_PER_READER_HOUR:()=>ke,VIEW_QUOTA_PER_HOUR:()=>on});var q=3e4,Se=Math.ceil(120),O=25,rn=Se*O,ke=720,on=ke*O,Q=18e4,Re=25e3,Pe=Math.ceil(36e5/Re),X=720,Ie=Pe+X,sn=X,Ae=20,J=1e4,Ce=400,an=Ce,Ne=4,un=(Ie+Ae)*O*Ne;var ln=[\"mousemove\",\"mousedown\",\"keydown\",\"wheel\",\"touchstart\",\"pointerdown\"];function cn(){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 dn(e,n){return t=>{try{let r=JSON.stringify(t),o=n.navigator;return 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}),!0)}catch(r){return!1}}}function pn(e={}){var be,ye;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=(be=e.idleMs)!=null?be:Q,a=(ye=e.sessionEveryMs)!=null?ye:3e4,l=e.slug||\"\",p=e.internal||null,g=e.sessionId||cn(),f=e.send||dn(o,n),c={},u=0,b=0,d=0,w=0,v=null,T=r(),S=!1,z=!1,I=[],De=()=>!!l||!!p,ce=i=>{if(!De())return!1;try{return f(i)!==!1}catch(m){return!1}},F=()=>!!t&&t.visibilityState===\"visible\"&&!S,de=i=>{let m=r()-i,y=Math.max(0,T-i)+s;return Math.max(0,Math.min(m,y))},B=()=>{v!=null&&u>0&&(c[u]=(c[u]||0)+de(v)/1e3),v=F()?r():null},pe=()=>{B(),v=null},j=()=>{v==null&&F()&&(v=r())},G=()=>{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=()=>{T=r(),S&&(S=!1,j())},me=(i,m)=>{p||ce({slug:l,event:i,page:m||b,maxPage:d,seconds:G(),sessionId:g})},ge=\"\",A=()=>{B();let i=G(),m=`${i}|${d}|${w}`;if(m===ge)return;let y={event:\"session\",sessionId:g,numPages:w,maxPage:d,totalSeconds:i,pagesTime:fe()};p?(y.internal=!0,y.docId=p.docId,y.email=p.email,y.name=p.name,p.it&&(y.it=p.it)):y.slug=l,ce(y)&&(ge=m)},R=(i,m,y,he)=>{!i||typeof i.addEventListener!=\"function\"||(i.addEventListener(m,y,he),I.push(()=>{i.removeEventListener(m,y,he)}))};return{sessionId:g,totalSeconds:G,maxPage:()=>d,pageTimes:fe,start(){if(z)return;z=!0,me(\"open\",1),u=1,T=r(),v=F()?r():null,R(t,\"visibilitychange\",()=>{t.visibilityState===\"hidden\"?(pe(),A()):(k(),j())}),R(n,\"focus\",()=>{k(),j()});for(let y of ln)R(n,y,k,{passive:!0});R(e.scrollElement,\"scroll\",k,{passive:!0}),R(n,\"beforeunload\",A);let i=n.setInterval(()=>{!S&&r()-T>s&&(S=!0,pe())},5e3),m=n.setInterval(A,a);I.push(()=>{n.clearInterval(i),n.clearInterval(m)})},setPage(i){!i||i===b||(T=r(),B(),u=i,b=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){}}z=!1}}}var H={};M(H,{STORAGE_KEYS:()=>x,attendeeKey:()=>bn,authorToken:()=>yn,avatarHtml:()=>ee,canModerate:()=>oe,createBudget:()=>En,createFileEcritures:()=>Tn,createScheduler:()=>xn,escapeHtml:()=>h,fetchBorne:()=>_n,flattenPresence:()=>fn,formatMessageBody:()=>ne,initials:()=>Oe,isMentioned:()=>vn,isMine:()=>_,presenceId:()=>Sn,reactorId:()=>re,referenceAuteur:()=>hn,shouldNotify:()=>Mn,unreadLabel:()=>wn});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 Oe(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(Oe(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 fn(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 mn(e,n){try{return e?e.getItem(n):null}catch(t){return null}}function gn(e,n,t){try{e==null||e.setItem(n,t)}catch(r){}}function bn(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 yn(e,n=te){let t=mn(e,x.authorToken);if(t)return t;let r=n();return gn(e,x.authorToken,r),r}async function hn(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 _(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 vn(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}`)&&!_(e,n):!1}function Mn(e){let{msg:n,me:t,historyLoaded:r,chatHidden:o}=e;return!r||!n||n.deleted||_(n,t)?!1:o}function wn(e){return!e||e<=0?\"\":e>9?\"9+\":String(e)}function xn(e,n={}){var b;let t=(b=n.minMs)!=null?b: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 _n(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,b)=>setTimeout(u,b)),a=r.clearTimer||(u=>clearTimeout(u)),l=null;try{l=new AbortController}catch(u){}let p=l?xe(K({},n||{}),{signal:l.signal}):K({},n||{}),g=null,f=new Promise((u,b)=>{g=s(()=>{try{l==null||l.abort()}catch(d){}b(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 En(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(b=>u.regler(b),()=>u.regler(null)).then(()=>{s=!1,p()})}return{poser(f,c){return new Promise(u=>{let b=o.get(f);o.set(f,{tache:c,regler:d=>{b&&b.regler(d),u(d)}}),p()})},enAttente:()=>o.size}}function Sn(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 U={};M(U,{messageClassName:()=>Rn,renderActions:()=>He,renderAttachment:()=>$e,renderMessage:()=>kn,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 $e(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 He(e,n){let 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 kn(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+$e(e.attachment)+`<div class=cm-re>${Le(e,n.me)}</div></span><span class=cm-act>${He(e,n)}</span>`}function Rn(e,n,t){return\"cm\"+(_(e,n)?\" me\":\"\")+(e.deleted?\" isdel\":\"\")+(t?\" mentioned\":\"\")}var D={};M(D,{DEFAULT_CENTER:()=>ae,DEFAULT_ZOOM:()=>ue,MAP_TYPES:()=>P,cycleMapType:()=>Pn,initialMapContent:()=>An,mapTypeLabel:()=>In,sanitizeContent:()=>le});var P=[\"roadmap\",\"satellite\",\"hybrid\"],ae=[46.6,2.5],ue=6;function E(e){let n=Number(e);return Number.isFinite(n)?n:null}function se(e){if(!Array.isArray(e))return null;let n=E(e[0]),t=E(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:E(r.heading)||0,pitch:ie(E(r.pitch)||0,-90,90)}:{heading:0,pitch:0};return{kind:\"streetview\",position:t,pov:o,zoom:ie(E(n.zoom)||1,0,5)}}return n.kind!==\"map\"?null:{kind:\"map\",center:se(n.center)||[...ae],zoom:ie(Math.trunc(E(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 Pn(e){let n=P.indexOf(e);return P[(n+1)%P.length]}function In(e){return e===\"roadmap\"?\"\\u{1F6F0} Satellite\":e===\"satellite\"?\"\\u{1F5FA} Hybride\":\"\\u{1F5FA} Plan\"}function An(){return{kind:\"map\",center:[...ae],zoom:ue,marker:null,mapType:null,label:null}}var W={};M(W,{CURRENT_PAGE_MARGIN:()=>Hn,DEFAULT_ASPECT:()=>On,MAX_ZOOM:()=>Nn,MIN_PAGE_WIDTH:()=>Ln,MIN_ZOOM:()=>Cn,PRERENDER_MARGIN:()=>$n,arrowState:()=>Wn,averageColor:()=>Fn,clampPage:()=>Ue,clampZoom:()=>Un,fitWidth:()=>zn,isImageDocument:()=>Bn,progressPercent:()=>Dn});var Cn=.5,Nn=3,On=1.35,Ln=280,$n=\"500px 0px\",Hn=\"-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 Ue(e,n){return Math.max(1,Math.min(n||1,Number(e)||1))}function Dn(e,n){return n?Math.round(Ue(e,n)/n*100):0}function Wn(e,n){return{prevDisabled:e<=1,nextDisabled:!!n&&e>=n}}var Vn=28;function zn(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)-Vn-(e.overlap||0)-(e.reserve||0))/r;return s>120?Math.min(t,s):t}function Fn(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 Bn(e,n){return/[.](png|jpe?g|webp|gif|avif)($|[?])/i.test(String(e||n||\"\"))}var V={};M(V,{presentationTransition:()=>jn,switchDocUrl:()=>Gn});function jn(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 Gn(e,n){let t=encodeURIComponent(n||String(Date.now()));return`${e}${e.includes(\"?\")?\"&\":\"?\"}v=${t}`}return Ge(Kn);})();", SOURCE_HASH: "24b1b68db3671718" };
|
package/server/cache.js
CHANGED
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
// 3. UN ÉCHEC NE SE MÉMORISE PAS. Servir une erreur pendant toute la fenêtre transformerait un
|
|
25
25
|
// hoquet en panne visible, et retarderait le rétablissement. On oublie la promesse rompue.
|
|
26
26
|
//
|
|
27
|
+
// 4. ⚠️ LE PLAFOND DES RÉSULTATS NE BORNE PAS LES DEMANDES EN VOL — et c'est par là que ça cédait.
|
|
28
|
+
// Le piège 2 était traité pour les valeurs RETENUES, mais une entrée en vol n'est jamais évincée
|
|
29
|
+
// (piège 1 : l'évincer casserait le regroupement). Tant que la base ne répond pas, RIEN ne
|
|
30
|
+
// bornait donc l'admission : 10 000 clés distinctes pendantes donnaient 10 000 entrées ET 10 000
|
|
31
|
+
// producteurs — donc 10 000 requêtes, sockets et promesses ouvertes, avec `max: 100`. Mesuré, pas
|
|
32
|
+
// supposé. Il faut un plafond SÉPARÉ sur les demandes en vol, et un REFUS quand il est atteint :
|
|
33
|
+
// borner l'admission est la seule chose qui borne une base lente. (Relevé par un audit externe.)
|
|
34
|
+
//
|
|
27
35
|
// ⚠️ CE QUE CE CACHE NE FAIT PAS : il vit dans la mémoire du PROCESSUS. En serverless, plusieurs
|
|
28
36
|
// instances servent en parallèle et démarrent à froid — l'effondrement est donc « une lecture par
|
|
29
37
|
// fenêtre ET PAR INSTANCE », pas « une lecture ». C'est la même limite que celle d'un compteur de
|
|
@@ -35,6 +43,21 @@
|
|
|
35
43
|
*
|
|
36
44
|
* @param {{ ttlMs: number, max?: number, now?: () => number }} options
|
|
37
45
|
*/
|
|
46
|
+
/**
|
|
47
|
+
* ⚠️ UNE ERREUR TYPÉE, PAS UN `null` NI UN THROW ANONYME. La route doit pouvoir distinguer « la base
|
|
48
|
+
* a échoué » (500) de « nous refusons d'admettre une demande de plus » (503, réessayable) : ce sont
|
|
49
|
+
* deux situations opposées pour l'appelant, et les confondre lui ferait abandonner une requête qui
|
|
50
|
+
* n'attendait qu'une seconde.
|
|
51
|
+
*/
|
|
52
|
+
const CODE_SATURATION = "cache-sature";
|
|
53
|
+
function erreurSaturation(plafond) {
|
|
54
|
+
const e = new Error(`cache saturé : ${plafond} demandes déjà en vol`);
|
|
55
|
+
e.code = CODE_SATURATION;
|
|
56
|
+
e.statusCode = 503;
|
|
57
|
+
e.retryAfter = 1;
|
|
58
|
+
return e;
|
|
59
|
+
}
|
|
60
|
+
|
|
38
61
|
function creerCache(options) {
|
|
39
62
|
const ttl = Math.max(0, Number(options && options.ttlMs) || 0);
|
|
40
63
|
// ⚠️ DEUX PLAFONDS, ET LE SECOND EST CELUI QUI COMPTE. Un plafond en NOMBRE d'entrées ne borne rien
|
|
@@ -44,15 +67,25 @@ function creerCache(options) {
|
|
|
44
67
|
// valeurs mises en cache ici sont des chaînes JSON. (Relevé par un audit externe.)
|
|
45
68
|
const max = Math.max(1, Number(options && options.max) || 100);
|
|
46
69
|
const maxOctets = Math.max(1, Number(options && options.maxOctets) || 8 * 1024 * 1024);
|
|
70
|
+
// ⚠️ LE TROISIÈME PLAFOND, ET C'EST LUI QUI TIENT SOUS UNE BASE LENTE. Les deux autres ne bornent
|
|
71
|
+
// que ce qui est RÉSOLU ; celui-ci borne ce qui est ADMIS. Une valeur volontairement basse devant
|
|
72
|
+
// `max` : une entrée en vol coûte une requête, une socket et une promesse, bien plus cher qu'un
|
|
73
|
+
// résultat en mémoire.
|
|
74
|
+
const maxEnVol = Math.max(1, Number(options && options.maxEnVol) || 128);
|
|
47
75
|
const now = (options && options.now) || (() => Date.now());
|
|
48
|
-
/** @type {Map<string, { echeance: number, promesse: Promise<unknown>, poids: number, enVol: boolean }>} */
|
|
76
|
+
/** @type {Map<string, { echeance: number, promesse: Promise<unknown>, poids: number, enVol: boolean, rendrePlace?: () => void }>} */
|
|
49
77
|
const entrees = new Map();
|
|
50
78
|
let poidsTotal = 0;
|
|
79
|
+
let nEnVol = 0; // tenu à jour à chaque transition — compter la Map à la demande serait O(n)
|
|
51
80
|
|
|
52
81
|
const oublier = (k) => {
|
|
53
82
|
const e = entrees.get(k);
|
|
54
83
|
if (!e) return;
|
|
55
84
|
poidsTotal -= e.poids;
|
|
85
|
+
// Une entrée en vol qu'on retire (promesse rompue) libère sa place d'admission — sans quoi le
|
|
86
|
+
// plafond se remplirait définitivement au premier incident de base. Le rendu passe par le verrou
|
|
87
|
+
// de la promesse : c'est lui qui garantit qu'une place n'est rendue qu'une fois.
|
|
88
|
+
if (e.enVol && typeof e.rendrePlace === "function") e.rendrePlace();
|
|
56
89
|
entrees.delete(k);
|
|
57
90
|
};
|
|
58
91
|
|
|
@@ -82,25 +115,41 @@ function creerCache(options) {
|
|
|
82
115
|
// AUTRE la pousse dehors : le cache gardait indéfiniment ce qu'il ne servait plus.
|
|
83
116
|
purger(t);
|
|
84
117
|
const vue = entrees.get(k);
|
|
85
|
-
//
|
|
118
|
+
// ⚠️ UNE CLÉ DÉJÀ EN VOL SE PARTAGE TOUJOURS, MÊME AU PLAFOND. C'est l'ordre de ces deux tests
|
|
119
|
+
// qui décide : refuser d'abord ferait échouer des appelants que le regroupement pouvait servir
|
|
120
|
+
// gratuitement — la saturation punirait alors la rafale légitime, exactement ce que ce cache
|
|
121
|
+
// existe pour absorber. On ne refuse que ce qui coûterait une requête DE PLUS.
|
|
86
122
|
if (vue && (vue.enVol || vue.echeance > t)) return vue.promesse;
|
|
87
123
|
|
|
124
|
+
if (nEnVol >= maxEnVol) throw erreurSaturation(maxEnVol);
|
|
125
|
+
|
|
88
126
|
const promesse = Promise.resolve().then(produire);
|
|
127
|
+
// ⚠️ UN DÉCOMPTE IDEMPOTENT PAR PROMESSE. Deux chemins libèrent la place (résolution, et
|
|
128
|
+
// `oublier` sur rejet) : sans ce verrou, une place pouvait être rendue DEUX fois, `nEnVol`
|
|
129
|
+
// passait sous zéro et le plafond cessait silencieusement de refuser — un plafond qui ne peut
|
|
130
|
+
// plus être atteint ne se distingue pas d'un plafond absent.
|
|
131
|
+
let placeRendue = false;
|
|
132
|
+
const rendrePlace = () => { if (!placeRendue) { placeRendue = true; nEnVol -= 1; } };
|
|
133
|
+
nEnVol += 1;
|
|
89
134
|
// ⚠️ L'ÉCHÉANCE PART DE LA RÉSOLUTION, PAS DE LA DEMANDE. Posée à la demande, elle expirait
|
|
90
135
|
// AVANT que la production ne réponde dès que celle-ci dépassait le TTL — et le regroupement ne
|
|
91
136
|
// servait alors plus à rien pour exactement les producteurs lents, les seuls qu'il valait la
|
|
92
137
|
// peine de mutualiser. Tant qu'elle est en vol, l'entrée ne périme pas.
|
|
93
|
-
entrees.set(k, { echeance: 0, promesse, poids: 0, enVol: true });
|
|
138
|
+
entrees.set(k, { echeance: 0, promesse, poids: 0, enVol: true, rendrePlace });
|
|
94
139
|
|
|
95
140
|
promesse.then(
|
|
96
141
|
(valeur) => {
|
|
97
142
|
const courante = entrees.get(k);
|
|
143
|
+
rendrePlace();
|
|
98
144
|
if (!courante || courante.promesse !== promesse) return;
|
|
99
145
|
courante.enVol = false;
|
|
100
146
|
courante.echeance = now() + ttl;
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
147
|
+
// ⚠️ DES OCTETS, PAS DES CARACTÈRES. `.length` compte des unités UTF-16 : « 界 » y vaut 1
|
|
148
|
+
// et pèse 3 octets en mémoire comme sur le fil. Un contenu non latin sous-estimait donc le
|
|
149
|
+
// poids d'un facteur 3 — un plafond « 8 Mo » en laissait passer 24. La valeur d'une autre
|
|
150
|
+
// forme compte pour un poids nominal : mieux vaut sous-estimer que prétendre mesurer ce
|
|
151
|
+
// qu'on ignore. (Relevé par un audit externe.)
|
|
152
|
+
courante.poids = typeof valeur === "string" ? Buffer.byteLength(valeur, "utf8") : 1;
|
|
104
153
|
poidsTotal += courante.poids;
|
|
105
154
|
borner();
|
|
106
155
|
},
|
|
@@ -109,6 +158,7 @@ function creerCache(options) {
|
|
|
109
158
|
// resterait servi pendant toute la fenêtre, et le rétablissement attendrait pour rien.
|
|
110
159
|
const courante = entrees.get(k);
|
|
111
160
|
if (courante && courante.promesse === promesse) oublier(k);
|
|
161
|
+
rendrePlace(); // idempotent : `oublier` a pu le faire, ou l'entrée avoir été remplacée
|
|
112
162
|
},
|
|
113
163
|
);
|
|
114
164
|
return promesse;
|
|
@@ -116,9 +166,11 @@ function creerCache(options) {
|
|
|
116
166
|
|
|
117
167
|
/** Pour les tests et l'exploitation : ce que la table contient réellement. */
|
|
118
168
|
taille: () => entrees.size,
|
|
119
|
-
/** Poids cumulé des résultats retenus, en
|
|
169
|
+
/** Poids cumulé des résultats retenus, en OCTETS UTF-8. */
|
|
120
170
|
poids: () => poidsTotal,
|
|
171
|
+
/** Demandes actuellement en vol — ce que le plafond d'admission borne. */
|
|
172
|
+
enVol: () => nEnVol,
|
|
121
173
|
};
|
|
122
174
|
}
|
|
123
175
|
|
|
124
|
-
module.exports = { creerCache };
|
|
176
|
+
module.exports = { creerCache, CODE_SATURATION };
|
package/server/handler.js
CHANGED
|
@@ -7,8 +7,8 @@ const crypto = require("crypto");
|
|
|
7
7
|
const { Readable } = require("node:stream");
|
|
8
8
|
const { pipeline } = require("node:stream/promises");
|
|
9
9
|
const { getShareBySlug } = require("./shares");
|
|
10
|
-
const { PRESENT_QUOTA_PER_HOUR, PRESENT_CACHE_MS } = require("./shared.generated.js");
|
|
11
|
-
const { creerCache } = require("./cache.js");
|
|
10
|
+
const { PRESENT_QUOTA_PER_HOUR, PRESENT_CACHE_MS, estSlug } = require("./shared.generated.js");
|
|
11
|
+
const { creerCache, CODE_SATURATION } = require("./cache.js");
|
|
12
12
|
|
|
13
13
|
// ⚠️ UN SEUL CACHE POUR TOUT LE PROCESSUS, ET C'EST LE POINT. Le créer par requête reviendrait à
|
|
14
14
|
// n'en avoir aucun : chaque appelant repartirait d'une table vide, et l'effondrement qu'on
|
|
@@ -797,10 +797,31 @@ async function handler(req, res) {
|
|
|
797
797
|
for (const lecture of LECTURES_PARTAGEES) {
|
|
798
798
|
if (String(q[lecture.param] || "") !== "1") continue;
|
|
799
799
|
const slugLu = String(q.present);
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
800
|
+
// ⚠️ LE CONTRAT S'APPLIQUE AVANT LE CACHE ET AVANT LA BASE. Le slug vient de l'URL : non
|
|
801
|
+
// validé, il entrait tel quel dans une CLÉ DE CACHE et dans une requête base. Un lecteur
|
|
802
|
+
// faisait donc varier les clés à volonté — c'est ce qui rendait l'admission du cache
|
|
803
|
+
// actionnable de l'extérieur. Le motif vit dans `shared.ts`, en un seul exemplaire.
|
|
804
|
+
if (!estSlug(slugLu)) return sendRefusal(res, "ended", embed);
|
|
805
|
+
let corps;
|
|
806
|
+
try {
|
|
807
|
+
corps = await cacheLecture.lire(`${lecture.param}:${slugLu}${lecture.suffixeCle ? lecture.suffixeCle() : ""}`, async () => {
|
|
808
|
+
const p = await getPresentation(slugLu);
|
|
809
|
+
return p ? JSON.stringify(await lecture.produire(p, slugLu)) : null;
|
|
810
|
+
});
|
|
811
|
+
} catch (e) {
|
|
812
|
+
// ⚠️ « NOUS REFUSONS D'ADMETTRE UNE DEMANDE DE PLUS » N'EST PAS « LA BASE A ÉCHOUÉ ». Un
|
|
813
|
+
// 503 réessayable dit à l'appelant d'attendre une seconde ; un 500 lui dit d'abandonner.
|
|
814
|
+
// Les confondre ferait renoncer une requête que le prochain battement aurait servie.
|
|
815
|
+
if (e && e.code === CODE_SATURATION) {
|
|
816
|
+
res.statusCode = 503;
|
|
817
|
+
res.setHeader("Retry-After", String(e.retryAfter || 1));
|
|
818
|
+
res.setHeader("Cache-Control", "no-store");
|
|
819
|
+
res.setHeader("Content-Type", "application/json");
|
|
820
|
+
res.end(JSON.stringify({ ok: false, error: "busy" }));
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
throw e;
|
|
824
|
+
}
|
|
804
825
|
if (!corps) return sendRefusal(res, "ended", embed);
|
|
805
826
|
res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.setHeader("Cache-Control", "no-store");
|
|
806
827
|
res.end(corps);
|
package/server/page-audience.js
CHANGED
|
@@ -94,6 +94,17 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
94
94
|
(function(){
|
|
95
95
|
var CFG=${cfg};
|
|
96
96
|
var PDF=null, total=0, cur=CFG.page||1, ready=false;
|
|
97
|
+
// ⚠️ DEUX GÉNÉRATIONS, PARCE QU'IL Y A DEUX COURSES DISTINCTES (P1 audit externe).
|
|
98
|
+
//
|
|
99
|
+
// docGen : le présentateur change de document pendant qu'un chargement est en cours. Le
|
|
100
|
+
// callback de l'ANCIEN document arrivait après le nouveau et posait ses pages par-dessus.
|
|
101
|
+
// renderGen : deux getPage() en vol (changement de page rapide, redimensionnement) peuvent
|
|
102
|
+
// se résoudre DANS LE DÉSORDRE — pdf.js ne garantit aucun ordre entre deux pages. Le plus lent
|
|
103
|
+
// gagnait, et l'audience restait sur une page que personne n'avait demandée.
|
|
104
|
+
//
|
|
105
|
+
// Une génération par course : les confondre ferait qu'un simple changement de page invaliderait
|
|
106
|
+
// le document, ou l'inverse.
|
|
107
|
+
var docGen=0, renderGen=0, tacheRendu=null, chargement=null;
|
|
97
108
|
var stage=document.getElementById('stage'), pageEl=document.getElementById('page');
|
|
98
109
|
function hideLoader(){ var l=document.getElementById('load'); if(l){ l.classList.add('hide'); setTimeout(function(){ if(l.parentNode)l.parentNode.removeChild(l); },450);} }
|
|
99
110
|
function ended(){ document.getElementById('ended').style.display='flex';
|
|
@@ -115,7 +126,14 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
115
126
|
pgi.innerHTML=''; pgi.appendChild(el);
|
|
116
127
|
return;
|
|
117
128
|
}
|
|
129
|
+
// ⚠️ ON ANNULE LE RENDU PRÉCÉDENT AVANT D'EN LANCER UN AUTRE. Le RenderTask était jeté :
|
|
130
|
+
// deux rendus peignaient alors en parallèle, et le plus lent finissait par l'emporter.
|
|
131
|
+
if(tacheRendu){ try{ tacheRendu.cancel(); }catch(e){} tacheRendu=null; }
|
|
132
|
+
var monDoc=docGen, monRendu=++renderGen;
|
|
118
133
|
PDF.getPage(n).then(function(page){
|
|
134
|
+
// Le document a changé, ou une page plus récente a été demandée pendant que celle-ci
|
|
135
|
+
// arrivait : ce résultat n'est plus celui qu'on attend. On ne le pose pas.
|
|
136
|
+
if(monDoc!==docGen || monRendu!==renderGen) return;
|
|
119
137
|
var availW=Math.max(120, stage.clientWidth-40), availH=Math.max(120, stage.clientHeight-40);
|
|
120
138
|
var v1=page.getViewport({scale:1});
|
|
121
139
|
var scale=Math.min(availW/v1.width, availH/v1.height); // fit entier → une page à l'écran
|
|
@@ -123,9 +141,18 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
123
141
|
var cv=document.createElement('canvas'); cv.width=Math.floor(vp.width*dpr); cv.height=Math.floor(vp.height*dpr);
|
|
124
142
|
cv.setAttribute('role','img'); cv.setAttribute('aria-label','Page '+n+(total>1?' sur '+total:''));
|
|
125
143
|
cv.style.width=vp.width+'px'; cv.style.height=vp.height+'px';
|
|
126
|
-
page.render({canvasContext:cv.getContext('2d'),viewport:vp,transform:dpr!==1?[dpr,0,0,dpr,0,0]:null});
|
|
127
|
-
|
|
128
|
-
|
|
144
|
+
var t=page.render({canvasContext:cv.getContext('2d'),viewport:vp,transform:dpr!==1?[dpr,0,0,dpr,0,0]:null});
|
|
145
|
+
tacheRendu=t;
|
|
146
|
+
// ⚠️ ON NE PUBLIE QU'APRÈS LE RENDU. L'ancien code posait le canvas AVANT que pdf.js n'ait
|
|
147
|
+
// peint : la page apparaissait blanche puis se remplissait, et un rendu périmé pouvait
|
|
148
|
+
// encore écraser le bon. On revérifie la génération APRÈS l'attente — c'est là qu'elle a pu
|
|
149
|
+
// changer, pas avant.
|
|
150
|
+
return t.promise.then(function(){
|
|
151
|
+
if(monDoc!==docGen || monRendu!==renderGen) return;
|
|
152
|
+
if(tacheRendu===t) tacheRendu=null;
|
|
153
|
+
pageEl.innerHTML=''; pageEl.appendChild(cv);
|
|
154
|
+
});
|
|
155
|
+
}).catch(function(){ /* rendu annulé ou page illisible : le rendu courant fait foi */ });
|
|
129
156
|
}
|
|
130
157
|
function boot(){
|
|
131
158
|
import(CFG.pdfjs).then(function(m){
|
|
@@ -170,35 +197,67 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
170
197
|
// réponse n est jamais « celui que le banc sait voir » : on garde le champ qui fait foi, puis on
|
|
171
198
|
// rend le banc capable de le distinguer — un cas où l URL ment et où seul le nom dit vrai.
|
|
172
199
|
function estImage(){ return Player.viewer.isImageDocument(CFG.fileName, CFG.docUrl); }
|
|
173
|
-
function chargerImage(){
|
|
200
|
+
function chargerImage(monDoc){
|
|
174
201
|
var im=new Image();
|
|
175
202
|
im.alt='Document présenté';
|
|
176
203
|
im.onload=function(){
|
|
204
|
+
// Le présentateur a pu rebasculer pendant le téléchargement : cette image n'est plus celle
|
|
205
|
+
// du document courant.
|
|
206
|
+
if(monDoc!==docGen) return;
|
|
177
207
|
total=1; ready=true; PDF={image:im};
|
|
178
208
|
var tot=document.getElementById('tot'); if(tot)tot.textContent=1;
|
|
179
209
|
show(1); hideLoader();
|
|
180
210
|
if(!CFG.active) ended();
|
|
181
211
|
};
|
|
182
|
-
im.onerror=function(){ var l=document.getElementById('load'); if(l)l.querySelector('.lsub').textContent="Document indisponible."; };
|
|
212
|
+
im.onerror=function(){ if(monDoc!==docGen) return; var l=document.getElementById('load'); if(l)l.querySelector('.lsub').textContent="Document indisponible."; };
|
|
183
213
|
im.src=CFG.fileUrl;
|
|
184
214
|
}
|
|
215
|
+
// ⚠️ ON LIBÈRE L'ANCIEN DOCUMENT. Un PDFDocumentProxy retient ses pages, ses canvas et son
|
|
216
|
+
// worker : enchaîner les bascules sans détruire faisait croître la mémoire sans borne, et c'est
|
|
217
|
+
// le mode de panne d'une présentation longue — celle qui dure, pas celle qu'on teste.
|
|
218
|
+
function libererDocument(){
|
|
219
|
+
if(tacheRendu){ try{ tacheRendu.cancel(); }catch(e){} tacheRendu=null; }
|
|
220
|
+
if(chargement){ try{ chargement.destroy(); }catch(e){} chargement=null; }
|
|
221
|
+
if(PDF && !PDF.image && typeof PDF.destroy==='function'){ try{ PDF.destroy(); }catch(e){} }
|
|
222
|
+
PDF=null;
|
|
223
|
+
}
|
|
185
224
|
function load(){
|
|
186
|
-
|
|
187
|
-
|
|
225
|
+
var monDoc=docGen;
|
|
226
|
+
if(estImage()){ chargerImage(monDoc); return; }
|
|
227
|
+
var t=pdfjsLib.getDocument({url:CFG.fileUrl,isEvalSupported:false});
|
|
228
|
+
chargement=t;
|
|
229
|
+
t.promise.then(function(pdf){
|
|
230
|
+
// Une bascule est survenue pendant le chargement : ce document n'est plus attendu, et le
|
|
231
|
+
// garder fuirait. On le détruit plutôt que de simplement l'ignorer.
|
|
232
|
+
if(monDoc!==docGen){ try{ pdf.destroy(); }catch(e){} return; }
|
|
188
233
|
PDF=pdf; total=pdf.numPages; ready=true;
|
|
189
234
|
var tot=document.getElementById('tot'); if(tot)tot.textContent=total;
|
|
190
235
|
show(cur); hideLoader();
|
|
191
236
|
if(!CFG.active) ended();
|
|
192
|
-
}).catch(function(){ var l=document.getElementById('load'); if(l)l.querySelector('.lsub').textContent="Document indisponible."; });
|
|
237
|
+
}).catch(function(){ if(monDoc!==docGen) return; var l=document.getElementById('load'); if(l)l.querySelector('.lsub').textContent="Document indisponible."; });
|
|
193
238
|
}
|
|
194
239
|
// Le présentateur a changé de document (même session) → recharger le PDF servi par le proxy (URL identique →
|
|
195
240
|
// cache-buster obligatoire), remettre à la page 1, mettre à jour le titre.
|
|
241
|
+
// ⚠️ TOUT CE QUI DÉCRIT LE DOCUMENT CHANGE ENSEMBLE, fileName COMPRIS — c'était le P1.
|
|
242
|
+
//
|
|
243
|
+
// fileName n'était PAS mis à jour, alors que c'est lui qui fait foi pour décider image ou PDF
|
|
244
|
+
// (estImage, et le commentaire plus bas dit pourquoi). Après une bascule, le nom restait celui
|
|
245
|
+
// du document précédent : un PDF→PNG envoyait donc le nouveau PNG à pdf.js, et un PNG→PDF
|
|
246
|
+
// chargeait le nouveau PDF avec new Image(). Dans les deux cas : « Document indisponible ».
|
|
247
|
+
//
|
|
248
|
+
// Une mise à jour partielle d'un état qui se lit comme un TOUT est la forme du défaut : il n'y a
|
|
249
|
+
// pas de moment où CFG doit décrire un document par son URL et un autre par son nom.
|
|
196
250
|
function switchDoc(row){
|
|
251
|
+
docGen++; // invalide tout callback du document précédent
|
|
252
|
+
libererDocument();
|
|
197
253
|
var t=document.getElementById('ptitle'); if(t) t.textContent=row.doc_title||row.file_name||'Document';
|
|
198
254
|
var pg=document.getElementById('page'); if(pg) pg.innerHTML='';
|
|
199
255
|
cur=1; total=0; ready=false;
|
|
200
256
|
var tot=document.getElementById('tot'); if(tot)tot.textContent='—';
|
|
201
257
|
var cu=document.getElementById('cur'); if(cu)cu.textContent='1';
|
|
258
|
+
if(row.file_url) CFG.docUrl=row.file_url;
|
|
259
|
+
CFG.fileName=row.file_name||'';
|
|
260
|
+
CFG.title=row.doc_title||row.file_name||'Document';
|
|
202
261
|
CFG.fileUrl='/api/doc?present='+encodeURIComponent(CFG.slug)+'&file=1&v='+encodeURIComponent(row.updated_at||String(row.current_page||1));
|
|
203
262
|
load();
|
|
204
263
|
}
|
|
@@ -229,7 +288,9 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
229
288
|
if(a.kind==='ended'){ ended(); return; }
|
|
230
289
|
if(a.kind==='show-map'){ if(window.Map3DD){ if(a.content.kind==='streetview') Map3DD.enterSV(a.content,false); else Map3DD.enter(a.content,false); } return; }
|
|
231
290
|
if(a.kind==='leave-map'){ if(window.Map3DD) Map3DD.exit(); }
|
|
232
|
-
|
|
291
|
+
// switchDoc pose LUI-MÊME docUrl/fileName/titre : les poser ici, en partie, était
|
|
292
|
+
// exactement ce qui laissait fileName en arrière.
|
|
293
|
+
if(a.kind==='switch-doc'){ switchDoc(row); return; }
|
|
233
294
|
if(a.kind==='show-page'){ show(a.page); }
|
|
234
295
|
}
|
|
235
296
|
}
|
|
@@ -44,9 +44,11 @@ __export(shared_exports, {
|
|
|
44
44
|
SESSION_INTERVAL_MS: () => SESSION_INTERVAL_MS,
|
|
45
45
|
SESSION_QUOTA_PER_HOUR: () => SESSION_QUOTA_PER_HOUR,
|
|
46
46
|
SESSION_WRITES_PER_HOUR: () => SESSION_WRITES_PER_HOUR,
|
|
47
|
+
SLUG_RE: () => SLUG_RE,
|
|
47
48
|
VIEW_EVENTS_PER_READER_HOUR: () => VIEW_EVENTS_PER_READER_HOUR,
|
|
48
49
|
VIEW_QUOTA_PER_HOUR: () => VIEW_QUOTA_PER_HOUR,
|
|
49
50
|
cycleMapType: () => cycleMapType,
|
|
51
|
+
estSlug: () => estSlug,
|
|
50
52
|
initialMapContent: () => initialMapContent,
|
|
51
53
|
mapTypeLabel: () => mapTypeLabel,
|
|
52
54
|
sanitizeContent: () => sanitizeContent
|
|
@@ -121,6 +123,12 @@ var PRESENT_READ_COALESCE_MS = 400;
|
|
|
121
123
|
var PRESENT_CACHE_MS = PRESENT_READ_COALESCE_MS;
|
|
122
124
|
var PRESENT_QUOTA_MARGIN = 4;
|
|
123
125
|
var PRESENT_QUOTA_PER_HOUR = (PRESENT_READS_PER_HOUR + PRESENT_READ_BURST) * READERS_PER_EGRESS * PRESENT_QUOTA_MARGIN;
|
|
126
|
+
|
|
127
|
+
// src/shared.ts
|
|
128
|
+
var SLUG_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
129
|
+
function estSlug(v) {
|
|
130
|
+
return typeof v === "string" && SLUG_RE.test(v);
|
|
131
|
+
}
|
|
124
132
|
// Annotate the CommonJS export names for ESM import in node:
|
|
125
133
|
0 && (module.exports = {
|
|
126
134
|
DEFAULT_CENTER,
|
|
@@ -142,9 +150,11 @@ var PRESENT_QUOTA_PER_HOUR = (PRESENT_READS_PER_HOUR + PRESENT_READ_BURST) * REA
|
|
|
142
150
|
SESSION_INTERVAL_MS,
|
|
143
151
|
SESSION_QUOTA_PER_HOUR,
|
|
144
152
|
SESSION_WRITES_PER_HOUR,
|
|
153
|
+
SLUG_RE,
|
|
145
154
|
VIEW_EVENTS_PER_READER_HOUR,
|
|
146
155
|
VIEW_QUOTA_PER_HOUR,
|
|
147
156
|
cycleMapType,
|
|
157
|
+
estSlug,
|
|
148
158
|
initialMapContent,
|
|
149
159
|
mapTypeLabel,
|
|
150
160
|
sanitizeContent
|