termbeam 1.16.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/public/assets/{_basePickBy-CYR9pyOe.js → _basePickBy-BpcK6jhC.js} +1 -1
- package/public/assets/{_baseUniq-DeFSIx-P.js → _baseUniq-CmudMx6k.js} +1 -1
- package/public/assets/{arc-CUEX1fu7.js → arc-BBjpe_hN.js} +1 -1
- package/public/assets/{architectureDiagram-2XIMDMQ5-BHhXPzZJ.js → architectureDiagram-2XIMDMQ5-BFCoYW28.js} +1 -1
- package/public/assets/{blockDiagram-WCTKOSBZ-RsOwF2Ow.js → blockDiagram-WCTKOSBZ-Dh31wJRm.js} +1 -1
- package/public/assets/{c4Diagram-IC4MRINW-B7KKaZ1J.js → c4Diagram-IC4MRINW-CPV76ZUM.js} +1 -1
- package/public/assets/channel-spGqRIlf.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-DOUcZxxl.js → chunk-4BX2VUAB-eU5lnLp-.js} +1 -1
- package/public/assets/{chunk-55IACEB6-bPgkuqF0.js → chunk-55IACEB6-CRSrEoDB.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-BWT_ExWr.js → chunk-FMBD7UC4-cK52FAx0.js} +1 -1
- package/public/assets/{chunk-JSJVCQXG-Df0AgfkZ.js → chunk-JSJVCQXG-CudZY5_F.js} +1 -1
- package/public/assets/{chunk-KX2RTZJC-DnYuhgK5.js → chunk-KX2RTZJC-BZlXzthI.js} +1 -1
- package/public/assets/{chunk-NQ4KR5QH-Dge50UUS.js → chunk-NQ4KR5QH-BP6XJFdX.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-BT0knyhA.js → chunk-QZHKN3VN-DDY6gqIZ.js} +1 -1
- package/public/assets/{chunk-WL4C6EOR-DutXGT-d.js → chunk-WL4C6EOR-DY15q7Tk.js} +1 -1
- package/public/assets/classDiagram-VBA2DB6C-CwKQYDdP.js +1 -0
- package/public/assets/classDiagram-v2-RAHNMMFH-CwKQYDdP.js +1 -0
- package/public/assets/clone-BQhUsBH5.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-doAicD_V.js → cose-bilkent-S5V4N54A-CTeXZoSq.js} +1 -1
- package/public/assets/{dagre-KLK3FWXG-O4cFm_hK.js → dagre-KLK3FWXG-BOfJIpm3.js} +1 -1
- package/public/assets/{diagram-E7M64L7V-BifAzLVq.js → diagram-E7M64L7V-yhW3nXxA.js} +1 -1
- package/public/assets/{diagram-IFDJBPK2-BfnxORJG.js → diagram-IFDJBPK2-B7r-HJa0.js} +1 -1
- package/public/assets/{diagram-P4PSJMXO-DTr1JYXb.js → diagram-P4PSJMXO-Cl1dGq__.js} +1 -1
- package/public/assets/{erDiagram-INFDFZHY-l1N_y881.js → erDiagram-INFDFZHY-xb7jd7iV.js} +1 -1
- package/public/assets/{flowDiagram-PKNHOUZH-CLGWYVco.js → flowDiagram-PKNHOUZH-7__LLCFp.js} +1 -1
- package/public/assets/{ganttDiagram-A5KZAMGK-9ERk2sFV.js → ganttDiagram-A5KZAMGK-BRH4oYpz.js} +1 -1
- package/public/assets/{gitGraphDiagram-K3NZZRJ6-BxK3Z85E.js → gitGraphDiagram-K3NZZRJ6-CZ0c5437.js} +1 -1
- package/public/assets/{graph-PwJPsvsO.js → graph-CaPDDY3I.js} +1 -1
- package/public/assets/index-7DPrKRHX.css +32 -0
- package/public/assets/{index-0a9Qn-A1.js → index-pqtccC7s.js} +115 -115
- package/public/assets/{infoDiagram-LFFYTUFH-DygGOypU.js → infoDiagram-LFFYTUFH-B50sX0Jk.js} +1 -1
- package/public/assets/{ishikawaDiagram-PHBUUO56-CySga9vu.js → ishikawaDiagram-PHBUUO56-BTx9nUR_.js} +1 -1
- package/public/assets/{journeyDiagram-4ABVD52K-ZIZNkXyJ.js → journeyDiagram-4ABVD52K-DOKjshE4.js} +1 -1
- package/public/assets/{kanban-definition-K7BYSVSG-IxWUQjiQ.js → kanban-definition-K7BYSVSG-DlsqM5ac.js} +1 -1
- package/public/assets/{layout-DbFs-9Gp.js → layout-C4hgRWBc.js} +1 -1
- package/public/assets/{linear-F1crC_h8.js → linear-pPVtYfoA.js} +1 -1
- package/public/assets/{mindmap-definition-YRQLILUH-BwXWnIOB.js → mindmap-definition-YRQLILUH-KXwvxrd9.js} +1 -1
- package/public/assets/{pieDiagram-SKSYHLDU-CTKX_qGt.js → pieDiagram-SKSYHLDU-Db_hnpTO.js} +1 -1
- package/public/assets/{quadrantDiagram-337W2JSQ-C9hYwyla.js → quadrantDiagram-337W2JSQ-DBgEkZee.js} +1 -1
- package/public/assets/{requirementDiagram-Z7DCOOCP-eOJ_I7lS.js → requirementDiagram-Z7DCOOCP-DzYp2J9t.js} +1 -1
- package/public/assets/{sankeyDiagram-WA2Y5GQK-CNYzy0Z-.js → sankeyDiagram-WA2Y5GQK-DqRGFuVJ.js} +1 -1
- package/public/assets/{sequenceDiagram-2WXFIKYE-ChHor0a9.js → sequenceDiagram-2WXFIKYE-CCATgMDC.js} +1 -1
- package/public/assets/{stateDiagram-RAJIS63D-CF2B1sp8.js → stateDiagram-RAJIS63D-DBbQtnkh.js} +1 -1
- package/public/assets/stateDiagram-v2-FVOUBMTO-Dr2a5PWq.js +1 -0
- package/public/assets/{timeline-definition-YZTLITO2-H72vBjMX.js → timeline-definition-YZTLITO2-AB47RPpS.js} +1 -1
- package/public/assets/{treemap-KZPCXAKY-CWEZDi_m.js → treemap-KZPCXAKY-BwGtQefY.js} +1 -1
- package/public/assets/{vennDiagram-LZ73GAT5-BEqSLv2W.js → vennDiagram-LZ73GAT5-CQy8NUxW.js} +1 -1
- package/public/assets/{xychartDiagram-JWTSCODW-enJM4ByX.js → xychartDiagram-JWTSCODW-D1U0PBps.js} +1 -1
- package/public/index.html +2 -2
- package/public/sw.js +1 -1
- package/src/server/index.js +13 -1
- package/src/server/routes.js +123 -4
- package/src/utils/update-check.js +139 -9
- package/src/utils/update-executor.js +340 -0
- package/public/assets/channel-CBJEzKmm.js +0 -1
- package/public/assets/classDiagram-VBA2DB6C-C-rOD9EU.js +0 -1
- package/public/assets/classDiagram-v2-RAHNMMFH-C-rOD9EU.js +0 -1
- package/public/assets/clone-DIyhZC23.js +0 -1
- package/public/assets/index-Z_lybSmO.css +0 -32
- package/public/assets/stateDiagram-v2-FVOUBMTO-Doxped-1.js +0 -1
package/public/sw.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
try{self["workbox:core:7.3.0"]&&_()}catch{}const G=(s,...e)=>{let t=s;return e.length>0&&(t+=` :: ${JSON.stringify(e)}`),t},Q=G;class h extends Error{constructor(e,t){const n=Q(e,t);super(n),this.name=e,this.details=t}}const d={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:typeof registration<"u"?registration.scope:""},E=s=>[d.prefix,s,d.suffix].filter(e=>e&&e.length>0).join("-"),z=s=>{for(const e of Object.keys(d))s(e)},b={updateDetails:s=>{z(e=>{typeof s[e]=="string"&&(d[e]=s[e])})},getGoogleAnalyticsName:s=>s||E(d.googleAnalytics),getPrecacheName:s=>s||E(d.precache),getPrefix:()=>d.prefix,getRuntimeName:s=>s||E(d.runtime),getSuffix:()=>d.suffix};function N(s,e){const t=e();return s.waitUntil(t),t}try{self["workbox:precaching:7.3.0"]&&_()}catch{}const J="__WB_REVISION__";function X(s){if(!s)throw new h("add-to-cache-list-unexpected-type",{entry:s});if(typeof s=="string"){const r=new URL(s,location.href);return{cacheKey:r.href,url:r.href}}const{revision:e,url:t}=s;if(!t)throw new h("add-to-cache-list-unexpected-type",{entry:s});if(!e){const r=new URL(t,location.href);return{cacheKey:r.href,url:r.href}}const n=new URL(t,location.href),a=new URL(t,location.href);return n.searchParams.set(J,e),{cacheKey:n.href,url:a.href}}class Y{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type==="install"&&t&&t.originalRequest&&t.originalRequest instanceof Request){const a=t.originalRequest.url;n?this.notUpdatedURLs.push(a):this.updatedURLs.push(a)}return n}}}class Z{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:t,params:n})=>{const a=(n==null?void 0:n.cacheKey)||this._precacheController.getCacheKeyForURL(t.url);return a?new Request(a,{headers:t.headers}):t},this._precacheController=e}}let m;function ee(){if(m===void 0){const s=new Response("");if("body"in s)try{new Response(s.body),m=!0}catch{m=!1}m=!1}return m}async function te(s,e){let t=null;if(s.url&&(t=new URL(s.url).origin),t!==self.location.origin)throw new h("cross-origin-copy-response",{origin:t});const n=s.clone(),r={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},i=ee()?n.body:await n.blob();return new Response(i,r)}const se=s=>new URL(String(s),location.href).href.replace(new RegExp(`^${location.origin}`),"");function M(s,e){const t=new URL(s);for(const n of e)t.searchParams.delete(n);return t.href}async function ne(s,e,t,n){const a=M(e.url,t);if(e.url===a)return s.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),i=await s.keys(e,r);for(const c of i){const o=M(c.url,t);if(a===o)return s.match(c,n)}}class ae{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}}const B=new Set;async function re(){for(const s of B)await s()}function ie(s){return new Promise(e=>setTimeout(e,s))}try{self["workbox:strategies:7.3.0"]&&_()}catch{}function C(s){return typeof s=="string"?new Request(s):s}class ce{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new ae,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(const n of this._plugins)this._pluginStateMap.set(n,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){const{event:t}=this;let n=C(e);if(n.mode==="navigate"&&t instanceof FetchEvent&&t.preloadResponse){const i=await t.preloadResponse;if(i)return i}const a=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const i of this.iterateCallbacks("requestWillFetch"))n=await i({request:n.clone(),event:t})}catch(i){if(i instanceof Error)throw new h("plugin-error-request-will-fetch",{thrownErrorMessage:i.message})}const r=n.clone();try{let i;i=await fetch(n,n.mode==="navigate"?void 0:this._strategy.fetchOptions);for(const c of this.iterateCallbacks("fetchDidSucceed"))i=await c({event:t,request:r,response:i});return i}catch(i){throw a&&await this.runCallbacks("fetchDidFail",{error:i,event:t,originalRequest:a.clone(),request:r.clone()}),i}}async fetchAndCachePut(e){const t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){const t=C(e);let n;const{cacheName:a,matchOptions:r}=this._strategy,i=await this.getCacheKey(t,"read"),c=Object.assign(Object.assign({},r),{cacheName:a});n=await caches.match(i,c);for(const o of this.iterateCallbacks("cachedResponseWillBeUsed"))n=await o({cacheName:a,matchOptions:r,cachedResponse:n,request:i,event:this.event})||void 0;return n}async cachePut(e,t){const n=C(e);await ie(0);const a=await this.getCacheKey(n,"write");if(!t)throw new h("cache-put-with-no-response",{url:se(a.url)});const r=await this._ensureResponseSafeToCache(t);if(!r)return!1;const{cacheName:i,matchOptions:c}=this._strategy,o=await self.caches.open(i),l=this.hasCallback("cacheDidUpdate"),g=l?await ne(o,a.clone(),["__WB_REVISION__"],c):null;try{await o.put(a,l?r.clone():r)}catch(u){if(u instanceof Error)throw u.name==="QuotaExceededError"&&await re(),u}for(const u of this.iterateCallbacks("cacheDidUpdate"))await u({cacheName:i,oldResponse:g,newResponse:r.clone(),request:a,event:this.event});return!0}async getCacheKey(e,t){const n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let a=e;for(const r of this.iterateCallbacks("cacheKeyWillBeUsed"))a=C(await r({mode:t,request:a,event:this.event,params:this.params}));this._cacheKeys[n]=a}return this._cacheKeys[n]}hasCallback(e){for(const t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(const n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(const t of this._strategy.plugins)if(typeof t[e]=="function"){const n=this._pluginStateMap.get(t);yield r=>{const i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){const e=this._extendLifetimePromises.splice(0),n=(await Promise.allSettled(e)).find(a=>a.status==="rejected");if(n)throw n.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(const a of this.iterateCallbacks("cacheWillUpdate"))if(t=await a({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}}class W{constructor(e={}){this.cacheName=b.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){const[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});const t=e.event,n=typeof e.request=="string"?new Request(e.request):e.request,a="params"in e?e.params:void 0,r=new ce(this,{event:t,request:n,params:a}),i=this._getResponse(r,n,t),c=this._awaitComplete(i,r,n,t);return[i,c]}async _getResponse(e,t,n){await e.runCallbacks("handlerWillStart",{event:n,request:t});let a;try{if(a=await this._handle(t,e),!a||a.type==="error")throw new h("no-response",{url:t.url})}catch(r){if(r instanceof Error){for(const i of e.iterateCallbacks("handlerDidError"))if(a=await i({error:r,event:n,request:t}),a)break}if(!a)throw r}for(const r of e.iterateCallbacks("handlerWillRespond"))a=await r({event:n,request:t,response:a});return a}async _awaitComplete(e,t,n,a){let r,i;try{r=await e}catch{}try{await t.runCallbacks("handlerDidRespond",{event:a,request:n,response:r}),await t.doneWaiting()}catch(c){c instanceof Error&&(i=c)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:n,response:r,error:i}),t.destroy(),i)throw i}}class p extends W{constructor(e={}){e.cacheName=b.getPrecacheName(e.cacheName),super(e),this._fallbackToNetwork=e.fallbackToNetwork!==!1,this.plugins.push(p.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){const n=await t.cacheMatch(e);return n||(t.event&&t.event.type==="install"?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,t){let n;const a=t.params||{};if(this._fallbackToNetwork){const r=a.integrity,i=e.integrity,c=!i||i===r;n=await t.fetch(new Request(e,{integrity:e.mode!=="no-cors"?i||r:void 0})),r&&c&&e.mode!=="no-cors"&&(this._useDefaultCacheabilityPluginIfNeeded(),await t.cachePut(e,n.clone()))}else throw new h("missing-precache-entry",{cacheName:this.cacheName,url:e.url});return n}async _handleInstall(e,t){this._useDefaultCacheabilityPluginIfNeeded();const n=await t.fetch(e);if(!await t.cachePut(e,n.clone()))throw new h("bad-precaching-response",{url:e.url,status:n.status});return n}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(const[n,a]of this.plugins.entries())a!==p.copyRedirectedCacheableResponsesPlugin&&(a===p.defaultPrecacheCacheabilityPlugin&&(e=n),a.cacheWillUpdate&&t++);t===0?this.plugins.push(p.defaultPrecacheCacheabilityPlugin):t>1&&e!==null&&this.plugins.splice(e,1)}}p.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:s}){return!s||s.status>=400?null:s}};p.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:s}){return s.redirected?await te(s):s}};class oe{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new p({cacheName:b.getPrecacheName(e),plugins:[...t,new Z({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){const t=[];for(const n of e){typeof n=="string"?t.push(n):n&&n.revision===void 0&&t.push(n.url);const{cacheKey:a,url:r}=X(n),i=typeof n!="string"&&n.revision?"reload":"default";if(this._urlsToCacheKeys.has(r)&&this._urlsToCacheKeys.get(r)!==a)throw new h("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(r),secondEntry:a});if(typeof n!="string"&&n.integrity){if(this._cacheKeysToIntegrities.has(a)&&this._cacheKeysToIntegrities.get(a)!==n.integrity)throw new h("add-to-cache-list-conflicting-integrities",{url:r});this._cacheKeysToIntegrities.set(a,n.integrity)}if(this._urlsToCacheKeys.set(r,a),this._urlsToCacheModes.set(r,i),t.length>0){const c=`Workbox is precaching URLs without revision info: ${t.join(", ")}
|
|
2
|
-
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(c)}}}install(e){return N(e,async()=>{const t=new Y;this.strategy.plugins.push(t);for(const[r,i]of this._urlsToCacheKeys){const c=this._cacheKeysToIntegrities.get(i),o=this._urlsToCacheModes.get(r),l=new Request(r,{integrity:c,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:i},request:l,event:e}))}const{updatedURLs:n,notUpdatedURLs:a}=t;return{updatedURLs:n,notUpdatedURLs:a}})}activate(e){return N(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),n=await t.keys(),a=new Set(this._urlsToCacheKeys.values()),r=[];for(const i of n)a.has(i.url)||(await t.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){const t=this.getCacheKeyForURL(e);if(!t)throw new h("non-precached-url",{url:e});return n=>(n.request=new Request(e),n.params=Object.assign({cacheKey:t},n.params),this.strategy.handle(n))}}let L;const j=()=>(L||(L=new oe),L);try{self["workbox:routing:7.3.0"]&&_()}catch{}const F="GET",x=s=>s&&typeof s=="object"?s:{handle:s};class R{constructor(e,t,n=F){this.handler=x(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=x(e)}}class le extends R{constructor(e,t,n){const a=({url:r})=>{const i=e.exec(r.href);if(i&&!(r.origin!==location.origin&&i.index!==0))return i.slice(1)};super(a,t,n)}}class he{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&e.data.type==="CACHE_URLS"){const{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(a=>{typeof a=="string"&&(a=[a]);const r=new Request(...a);return this.handleRequest({request:r,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){const n=new URL(e.url,location.href);if(!n.protocol.startsWith("http"))return;const a=n.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:n});let c=i&&i.handler;const o=e.method;if(!c&&this._defaultHandlerMap.has(o)&&(c=this._defaultHandlerMap.get(o)),!c)return;let l;try{l=c.handle({url:n,request:e,event:t,params:r})}catch(u){l=Promise.reject(u)}const g=i&&i.catchHandler;return l instanceof Promise&&(this._catchHandler||g)&&(l=l.catch(async u=>{if(g)try{return await g.handle({url:n,request:e,event:t,params:r})}catch(I){I instanceof Error&&(u=I)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw u})),l}findMatchingRoute({url:e,sameOrigin:t,request:n,event:a}){const r=this._routes.get(n.method)||[];for(const i of r){let c;const o=i.match({url:e,sameOrigin:t,request:n,event:a});if(o)return c=o,(Array.isArray(c)&&c.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o=="boolean")&&(c=void 0),{route:i,params:c}}return{}}setDefaultHandler(e,t=F){this._defaultHandlerMap.set(t,x(e))}setCatchHandler(e){this._catchHandler=x(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new h("unregister-route-but-not-found-with-method",{method:e.method});const t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new h("unregister-route-route-not-registered")}}let w;const ue=()=>(w||(w=new he,w.addFetchListener(),w.addCacheListener()),w);function q(s,e,t){let n;if(typeof s=="string"){const r=new URL(s,location.href),i=({url:c})=>c.href===r.href;n=new R(i,e,t)}else if(s instanceof RegExp)n=new le(s,e,t);else if(typeof s=="function")n=new R(s,e,t);else if(s instanceof R)n=s;else throw new h("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return ue().registerRoute(n),n}function de(s,e=[]){for(const t of[...s.searchParams.keys()])e.some(n=>n.test(t))&&s.searchParams.delete(t);return s}function*fe(s,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:t="index.html",cleanURLs:n=!0,urlManipulation:a}={}){const r=new URL(s,location.href);r.hash="",yield r.href;const i=de(r,e);if(yield i.href,t&&i.pathname.endsWith("/")){const c=new URL(i.href);c.pathname+=t,yield c.href}if(n){const c=new URL(i.href);c.pathname+=".html",yield c.href}if(a){const c=a({url:r});for(const o of c)yield o.href}}class pe extends R{constructor(e,t){const n=({request:a})=>{const r=e.getURLsToCacheKeys();for(const i of fe(a.url,t)){const c=r.get(i);if(c){const o=e.getIntegrityForCacheKey(c);return{cacheKey:c,integrity:o}}}};super(n,e.strategy)}}function ge(s){const e=j(),t=new pe(e,s);q(t)}const me="-precache-",we=async(s,e=me)=>{const n=(await self.caches.keys()).filter(a=>a.includes(e)&&a.includes(self.registration.scope)&&a!==s);return await Promise.all(n.map(a=>self.caches.delete(a))),n};function ye(){self.addEventListener("activate",(s=>{const e=b.getPrecacheName();s.waitUntil(we(e).then(t=>{}))}))}function _e(s){j().precache(s)}function Re(s,e){_e(s),ge(e)}class be extends W{async _handle(e,t){let n=await t.cacheMatch(e),a;if(!n)try{n=await t.fetchAndCachePut(e)}catch(r){r instanceof Error&&(a=r)}if(!n)throw new h("no-response",{url:e.url,error:a});return n}}function H(s){s.then(()=>{})}const Ce=(s,e)=>e.some(t=>s instanceof t);let A,K;function xe(){return A||(A=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function Ee(){return K||(K=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const V=new WeakMap,k=new WeakMap,$=new WeakMap,D=new WeakMap,v=new WeakMap;function Le(s){const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("success",r),s.removeEventListener("error",i)},r=()=>{t(f(s.result)),a()},i=()=>{n(s.error),a()};s.addEventListener("success",r),s.addEventListener("error",i)});return e.then(t=>{t instanceof IDBCursor&&V.set(t,s)}).catch(()=>{}),v.set(e,s),e}function De(s){if(k.has(s))return;const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("complete",r),s.removeEventListener("error",i),s.removeEventListener("abort",i)},r=()=>{t(),a()},i=()=>{n(s.error||new DOMException("AbortError","AbortError")),a()};s.addEventListener("complete",r),s.addEventListener("error",i),s.addEventListener("abort",i)});k.set(s,e)}let P={get(s,e,t){if(s instanceof IDBTransaction){if(e==="done")return k.get(s);if(e==="objectStoreNames")return s.objectStoreNames||$.get(s);if(e==="store")return t.objectStoreNames[1]?void 0:t.objectStore(t.objectStoreNames[0])}return f(s[e])},set(s,e,t){return s[e]=t,!0},has(s,e){return s instanceof IDBTransaction&&(e==="done"||e==="store")?!0:e in s}};function Ue(s){P=s(P)}function Te(s){return s===IDBDatabase.prototype.transaction&&!("objectStoreNames"in IDBTransaction.prototype)?function(e,...t){const n=s.call(U(this),e,...t);return $.set(n,e.sort?e.sort():[e]),f(n)}:Ee().includes(s)?function(...e){return s.apply(U(this),e),f(V.get(this))}:function(...e){return f(s.apply(U(this),e))}}function ke(s){return typeof s=="function"?Te(s):(s instanceof IDBTransaction&&De(s),Ce(s,xe())?new Proxy(s,P):s)}function f(s){if(s instanceof IDBRequest)return Le(s);if(D.has(s))return D.get(s);const e=ke(s);return e!==s&&(D.set(s,e),v.set(e,s)),e}const U=s=>v.get(s);function Pe(s,e,{blocked:t,upgrade:n,blocking:a,terminated:r}={}){const i=indexedDB.open(s,e),c=f(i);return n&&i.addEventListener("upgradeneeded",o=>{n(f(i.result),o.oldVersion,o.newVersion,f(i.transaction),o)}),t&&i.addEventListener("blocked",o=>t(o.oldVersion,o.newVersion,o)),c.then(o=>{r&&o.addEventListener("close",()=>r()),a&&o.addEventListener("versionchange",l=>a(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}function ve(s,{blocked:e}={}){const t=indexedDB.deleteDatabase(s);return e&&t.addEventListener("blocked",n=>e(n.oldVersion,n)),f(t).then(()=>{})}const Ie=["get","getKey","getAll","getAllKeys","count"],Ne=["put","add","delete","clear"],T=new Map;function S(s,e){if(!(s instanceof IDBDatabase&&!(e in s)&&typeof e=="string"))return;if(T.get(e))return T.get(e);const t=e.replace(/FromIndex$/,""),n=e!==t,a=Ne.includes(t);if(!(t in(n?IDBIndex:IDBObjectStore).prototype)||!(a||Ie.includes(t)))return;const r=async function(i,...c){const o=this.transaction(i,a?"readwrite":"readonly");let l=o.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[t](...c),a&&o.done]))[0]};return T.set(e,r),r}Ue(s=>({...s,get:(e,t,n)=>S(e,t)||s.get(e,t,n),has:(e,t)=>!!S(e,t)||s.has(e,t)}));try{self["workbox:expiration:7.3.0"]&&_()}catch{}const Me="workbox-expiration",y="cache-entries",O=s=>{const e=new URL(s,location.href);return e.hash="",e.href};class Ae{constructor(e){this._db=null,this._cacheName=e}_upgradeDb(e){const t=e.createObjectStore(y,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&ve(this._cacheName)}async setTimestamp(e,t){e=O(e);const n={url:e,timestamp:t,cacheName:this._cacheName,id:this._getId(e)},r=(await this.getDb()).transaction(y,"readwrite",{durability:"relaxed"});await r.store.put(n),await r.done}async getTimestamp(e){const n=await(await this.getDb()).get(y,this._getId(e));return n==null?void 0:n.timestamp}async expireEntries(e,t){const n=await this.getDb();let a=await n.transaction(y).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;a;){const o=a.value;o.cacheName===this._cacheName&&(e&&o.timestamp<e||t&&i>=t?r.push(a.value):i++),a=await a.continue()}const c=[];for(const o of r)await n.delete(y,o.id),c.push(o.url);return c}_getId(e){return this._cacheName+"|"+O(e)}async getDb(){return this._db||(this._db=await Pe(Me,1,{upgrade:this._upgradeDbAndDeleteOldDbs.bind(this)})),this._db}}class Ke{constructor(e,t={}){this._isRunning=!1,this._rerunRequested=!1,this._maxEntries=t.maxEntries,this._maxAgeSeconds=t.maxAgeSeconds,this._matchOptions=t.matchOptions,this._cacheName=e,this._timestampModel=new Ae(e)}async expireEntries(){if(this._isRunning){this._rerunRequested=!0;return}this._isRunning=!0;const e=this._maxAgeSeconds?Date.now()-this._maxAgeSeconds*1e3:0,t=await this._timestampModel.expireEntries(e,this._maxEntries),n=await self.caches.open(this._cacheName);for(const a of t)await n.delete(a,this._matchOptions);this._isRunning=!1,this._rerunRequested&&(this._rerunRequested=!1,H(this.expireEntries()))}async updateTimestamp(e){await this._timestampModel.setTimestamp(e,Date.now())}async isURLExpired(e){if(this._maxAgeSeconds){const t=await this._timestampModel.getTimestamp(e),n=Date.now()-this._maxAgeSeconds*1e3;return t!==void 0?t<n:!0}else return!1}async delete(){this._rerunRequested=!1,await this._timestampModel.expireEntries(1/0)}}function Se(s){B.add(s)}class Oe{constructor(e={}){this.cachedResponseWillBeUsed=async({event:t,request:n,cacheName:a,cachedResponse:r})=>{if(!r)return null;const i=this._isResponseDateFresh(r),c=this._getCacheExpiration(a);H(c.expireEntries());const o=c.updateTimestamp(n.url);if(t)try{t.waitUntil(o)}catch{}return i?r:null},this.cacheDidUpdate=async({cacheName:t,request:n})=>{const a=this._getCacheExpiration(t);await a.updateTimestamp(n.url),await a.expireEntries()},this._config=e,this._maxAgeSeconds=e.maxAgeSeconds,this._cacheExpirations=new Map,e.purgeOnQuotaError&&Se(()=>this.deleteCacheAndMetadata())}_getCacheExpiration(e){if(e===b.getRuntimeName())throw new h("expire-custom-caches-only");let t=this._cacheExpirations.get(e);return t||(t=new Ke(e,this._config),this._cacheExpirations.set(e,t)),t}_isResponseDateFresh(e){if(!this._maxAgeSeconds)return!0;const t=this._getDateHeaderTimestamp(e);if(t===null)return!0;const n=Date.now();return t>=n-this._maxAgeSeconds*1e3}_getDateHeaderTimestamp(e){if(!e.headers.has("date"))return null;const t=e.headers.get("date"),a=new Date(t).getTime();return isNaN(a)?null:a}async deleteCacheAndMetadata(){for(const[e,t]of this._cacheExpirations)await self.caches.delete(e),await t.delete();this._cacheExpirations=new Map}}Re([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"0f93805414c783a30b72e0ce88f2c7c4","url":"icons/icon.svg"},{"revision":"5dee5ddcd8e8533edb348118334d9756","url":"icons/icon-512.png"},{"revision":"75734e6b38a556148f51b9e10eeb01c8","url":"icons/icon-192.png"},{"revision":"0362e0eb00358646fd4c97674945adba","url":"icons/icon-180.png"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-enJM4ByX.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-BEqSLv2W.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-CWEZDi_m.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-H72vBjMX.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-Doxped-1.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-CF2B1sp8.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-ChHor0a9.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-CNYzy0Z-.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-eOJ_I7lS.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-C9hYwyla.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-CTKX_qGt.js"},{"revision":null,"url":"assets/ordinal-Cboi1Yqb.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BwXWnIOB.js"},{"revision":null,"url":"assets/linear-F1crC_h8.js"},{"revision":null,"url":"assets/layout-DbFs-9Gp.js"},{"revision":null,"url":"assets/katex-B1X10hvy.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-IxWUQjiQ.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-ZIZNkXyJ.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-CySga9vu.js"},{"revision":null,"url":"assets/init-Gi6I4Gst.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-DygGOypU.js"},{"revision":null,"url":"assets/index-Z_lybSmO.css"},{"revision":null,"url":"assets/index-0a9Qn-A1.js"},{"revision":null,"url":"assets/graph-PwJPsvsO.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BxK3Z85E.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-9ERk2sFV.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CLGWYVco.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-l1N_y881.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-DTr1JYXb.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-BfnxORJG.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-BifAzLVq.js"},{"revision":null,"url":"assets/defaultLocale-DX6XiGOO.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-O4cFm_hK.js"},{"revision":null,"url":"assets/cytoscape.esm-BQaXIfA_.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-doAicD_V.js"},{"revision":null,"url":"assets/clone-DIyhZC23.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-C-rOD9EU.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-C-rOD9EU.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-DutXGT-d.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-BT0knyhA.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-Dge50UUS.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-DnYuhgK5.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-Df0AgfkZ.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-BWT_ExWr.js"},{"revision":null,"url":"assets/chunk-55IACEB6-bPgkuqF0.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-DOUcZxxl.js"},{"revision":null,"url":"assets/channel-CBJEzKmm.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW-B7KKaZ1J.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-RsOwF2Ow.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-BHhXPzZJ.js"},{"revision":null,"url":"assets/arc-CUEX1fu7.js"},{"revision":null,"url":"assets/_baseUniq-DeFSIx-P.js"},{"revision":null,"url":"assets/_basePickBy-CYR9pyOe.js"},{"revision":"0362e0eb00358646fd4c97674945adba","url":"icons/icon-180.png"},{"revision":"75734e6b38a556148f51b9e10eeb01c8","url":"icons/icon-192.png"},{"revision":"5dee5ddcd8e8533edb348118334d9756","url":"icons/icon-512.png"},{"revision":"b0747e8d6cde0d45e05be8e5c731729e","url":"manifest.webmanifest"}]);ye();q(({url:s})=>s.hostname==="cdn.jsdelivr.net"&&s.pathname.endsWith(".ttf"),new be({cacheName:"termbeam-fonts",plugins:[new Oe({maxEntries:5,maxAgeSeconds:365*24*60*60})]}));self.addEventListener("message",s=>{var e;((e=s.data)==null?void 0:e.type)==="CLEAR_CACHES"&&caches.keys().then(t=>{for(const n of t)n!=="workbox-precache-v2"&&caches.delete(n)})});self.addEventListener("install",()=>{self.skipWaiting()});self.addEventListener("activate",s=>{s.waitUntil(caches.delete("termbeam-navigation").then(()=>self.clients.claim()))});self.addEventListener("push",s=>{let e={title:"Command finished",body:"TermBeam"};if(s.data)try{e=s.data.json()}catch{}s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(async t=>{if(t.some(r=>r.url.includes(self.location.origin)&&r.focused))return;const a={body:e.body||"A command has completed",icon:"/icons/icon-192.png",badge:"/icons/icon-192.png",tag:"termbeam-cmd",renotify:!0,data:{url:"/"},vibrate:[200,100,200]};try{await self.navigator.setAppBadge(1)}catch{}return self.registration.showNotification(e.title||"Command finished",a)}))});self.addEventListener("notificationclick",s=>{var t;s.notification.close();try{self.navigator.clearAppBadge()}catch{}const e=((t=s.notification.data)==null?void 0:t.url)||"/";s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(n=>{for(const a of n)if(a.url.includes(self.location.origin)&&"focus"in a)return a.focus();return self.clients.openWindow(e)}))});
|
|
2
|
+
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(c)}}}install(e){return N(e,async()=>{const t=new Y;this.strategy.plugins.push(t);for(const[r,i]of this._urlsToCacheKeys){const c=this._cacheKeysToIntegrities.get(i),o=this._urlsToCacheModes.get(r),l=new Request(r,{integrity:c,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:i},request:l,event:e}))}const{updatedURLs:n,notUpdatedURLs:a}=t;return{updatedURLs:n,notUpdatedURLs:a}})}activate(e){return N(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),n=await t.keys(),a=new Set(this._urlsToCacheKeys.values()),r=[];for(const i of n)a.has(i.url)||(await t.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){const t=this.getCacheKeyForURL(e);if(!t)throw new h("non-precached-url",{url:e});return n=>(n.request=new Request(e),n.params=Object.assign({cacheKey:t},n.params),this.strategy.handle(n))}}let L;const j=()=>(L||(L=new oe),L);try{self["workbox:routing:7.3.0"]&&_()}catch{}const F="GET",x=s=>s&&typeof s=="object"?s:{handle:s};class R{constructor(e,t,n=F){this.handler=x(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=x(e)}}class le extends R{constructor(e,t,n){const a=({url:r})=>{const i=e.exec(r.href);if(i&&!(r.origin!==location.origin&&i.index!==0))return i.slice(1)};super(a,t,n)}}class he{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&e.data.type==="CACHE_URLS"){const{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(a=>{typeof a=="string"&&(a=[a]);const r=new Request(...a);return this.handleRequest({request:r,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){const n=new URL(e.url,location.href);if(!n.protocol.startsWith("http"))return;const a=n.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:n});let c=i&&i.handler;const o=e.method;if(!c&&this._defaultHandlerMap.has(o)&&(c=this._defaultHandlerMap.get(o)),!c)return;let l;try{l=c.handle({url:n,request:e,event:t,params:r})}catch(u){l=Promise.reject(u)}const g=i&&i.catchHandler;return l instanceof Promise&&(this._catchHandler||g)&&(l=l.catch(async u=>{if(g)try{return await g.handle({url:n,request:e,event:t,params:r})}catch(I){I instanceof Error&&(u=I)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw u})),l}findMatchingRoute({url:e,sameOrigin:t,request:n,event:a}){const r=this._routes.get(n.method)||[];for(const i of r){let c;const o=i.match({url:e,sameOrigin:t,request:n,event:a});if(o)return c=o,(Array.isArray(c)&&c.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o=="boolean")&&(c=void 0),{route:i,params:c}}return{}}setDefaultHandler(e,t=F){this._defaultHandlerMap.set(t,x(e))}setCatchHandler(e){this._catchHandler=x(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new h("unregister-route-but-not-found-with-method",{method:e.method});const t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new h("unregister-route-route-not-registered")}}let w;const ue=()=>(w||(w=new he,w.addFetchListener(),w.addCacheListener()),w);function q(s,e,t){let n;if(typeof s=="string"){const r=new URL(s,location.href),i=({url:c})=>c.href===r.href;n=new R(i,e,t)}else if(s instanceof RegExp)n=new le(s,e,t);else if(typeof s=="function")n=new R(s,e,t);else if(s instanceof R)n=s;else throw new h("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return ue().registerRoute(n),n}function de(s,e=[]){for(const t of[...s.searchParams.keys()])e.some(n=>n.test(t))&&s.searchParams.delete(t);return s}function*fe(s,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:t="index.html",cleanURLs:n=!0,urlManipulation:a}={}){const r=new URL(s,location.href);r.hash="",yield r.href;const i=de(r,e);if(yield i.href,t&&i.pathname.endsWith("/")){const c=new URL(i.href);c.pathname+=t,yield c.href}if(n){const c=new URL(i.href);c.pathname+=".html",yield c.href}if(a){const c=a({url:r});for(const o of c)yield o.href}}class pe extends R{constructor(e,t){const n=({request:a})=>{const r=e.getURLsToCacheKeys();for(const i of fe(a.url,t)){const c=r.get(i);if(c){const o=e.getIntegrityForCacheKey(c);return{cacheKey:c,integrity:o}}}};super(n,e.strategy)}}function ge(s){const e=j(),t=new pe(e,s);q(t)}const me="-precache-",we=async(s,e=me)=>{const n=(await self.caches.keys()).filter(a=>a.includes(e)&&a.includes(self.registration.scope)&&a!==s);return await Promise.all(n.map(a=>self.caches.delete(a))),n};function ye(){self.addEventListener("activate",(s=>{const e=b.getPrecacheName();s.waitUntil(we(e).then(t=>{}))}))}function _e(s){j().precache(s)}function Re(s,e){_e(s),ge(e)}class be extends W{async _handle(e,t){let n=await t.cacheMatch(e),a;if(!n)try{n=await t.fetchAndCachePut(e)}catch(r){r instanceof Error&&(a=r)}if(!n)throw new h("no-response",{url:e.url,error:a});return n}}function H(s){s.then(()=>{})}const Ce=(s,e)=>e.some(t=>s instanceof t);let A,K;function xe(){return A||(A=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function Ee(){return K||(K=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const V=new WeakMap,k=new WeakMap,$=new WeakMap,D=new WeakMap,v=new WeakMap;function Le(s){const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("success",r),s.removeEventListener("error",i)},r=()=>{t(f(s.result)),a()},i=()=>{n(s.error),a()};s.addEventListener("success",r),s.addEventListener("error",i)});return e.then(t=>{t instanceof IDBCursor&&V.set(t,s)}).catch(()=>{}),v.set(e,s),e}function De(s){if(k.has(s))return;const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("complete",r),s.removeEventListener("error",i),s.removeEventListener("abort",i)},r=()=>{t(),a()},i=()=>{n(s.error||new DOMException("AbortError","AbortError")),a()};s.addEventListener("complete",r),s.addEventListener("error",i),s.addEventListener("abort",i)});k.set(s,e)}let P={get(s,e,t){if(s instanceof IDBTransaction){if(e==="done")return k.get(s);if(e==="objectStoreNames")return s.objectStoreNames||$.get(s);if(e==="store")return t.objectStoreNames[1]?void 0:t.objectStore(t.objectStoreNames[0])}return f(s[e])},set(s,e,t){return s[e]=t,!0},has(s,e){return s instanceof IDBTransaction&&(e==="done"||e==="store")?!0:e in s}};function Ue(s){P=s(P)}function Te(s){return s===IDBDatabase.prototype.transaction&&!("objectStoreNames"in IDBTransaction.prototype)?function(e,...t){const n=s.call(U(this),e,...t);return $.set(n,e.sort?e.sort():[e]),f(n)}:Ee().includes(s)?function(...e){return s.apply(U(this),e),f(V.get(this))}:function(...e){return f(s.apply(U(this),e))}}function ke(s){return typeof s=="function"?Te(s):(s instanceof IDBTransaction&&De(s),Ce(s,xe())?new Proxy(s,P):s)}function f(s){if(s instanceof IDBRequest)return Le(s);if(D.has(s))return D.get(s);const e=ke(s);return e!==s&&(D.set(s,e),v.set(e,s)),e}const U=s=>v.get(s);function Pe(s,e,{blocked:t,upgrade:n,blocking:a,terminated:r}={}){const i=indexedDB.open(s,e),c=f(i);return n&&i.addEventListener("upgradeneeded",o=>{n(f(i.result),o.oldVersion,o.newVersion,f(i.transaction),o)}),t&&i.addEventListener("blocked",o=>t(o.oldVersion,o.newVersion,o)),c.then(o=>{r&&o.addEventListener("close",()=>r()),a&&o.addEventListener("versionchange",l=>a(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}function ve(s,{blocked:e}={}){const t=indexedDB.deleteDatabase(s);return e&&t.addEventListener("blocked",n=>e(n.oldVersion,n)),f(t).then(()=>{})}const Ie=["get","getKey","getAll","getAllKeys","count"],Ne=["put","add","delete","clear"],T=new Map;function S(s,e){if(!(s instanceof IDBDatabase&&!(e in s)&&typeof e=="string"))return;if(T.get(e))return T.get(e);const t=e.replace(/FromIndex$/,""),n=e!==t,a=Ne.includes(t);if(!(t in(n?IDBIndex:IDBObjectStore).prototype)||!(a||Ie.includes(t)))return;const r=async function(i,...c){const o=this.transaction(i,a?"readwrite":"readonly");let l=o.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[t](...c),a&&o.done]))[0]};return T.set(e,r),r}Ue(s=>({...s,get:(e,t,n)=>S(e,t)||s.get(e,t,n),has:(e,t)=>!!S(e,t)||s.has(e,t)}));try{self["workbox:expiration:7.3.0"]&&_()}catch{}const Me="workbox-expiration",y="cache-entries",O=s=>{const e=new URL(s,location.href);return e.hash="",e.href};class Ae{constructor(e){this._db=null,this._cacheName=e}_upgradeDb(e){const t=e.createObjectStore(y,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&ve(this._cacheName)}async setTimestamp(e,t){e=O(e);const n={url:e,timestamp:t,cacheName:this._cacheName,id:this._getId(e)},r=(await this.getDb()).transaction(y,"readwrite",{durability:"relaxed"});await r.store.put(n),await r.done}async getTimestamp(e){const n=await(await this.getDb()).get(y,this._getId(e));return n==null?void 0:n.timestamp}async expireEntries(e,t){const n=await this.getDb();let a=await n.transaction(y).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;a;){const o=a.value;o.cacheName===this._cacheName&&(e&&o.timestamp<e||t&&i>=t?r.push(a.value):i++),a=await a.continue()}const c=[];for(const o of r)await n.delete(y,o.id),c.push(o.url);return c}_getId(e){return this._cacheName+"|"+O(e)}async getDb(){return this._db||(this._db=await Pe(Me,1,{upgrade:this._upgradeDbAndDeleteOldDbs.bind(this)})),this._db}}class Ke{constructor(e,t={}){this._isRunning=!1,this._rerunRequested=!1,this._maxEntries=t.maxEntries,this._maxAgeSeconds=t.maxAgeSeconds,this._matchOptions=t.matchOptions,this._cacheName=e,this._timestampModel=new Ae(e)}async expireEntries(){if(this._isRunning){this._rerunRequested=!0;return}this._isRunning=!0;const e=this._maxAgeSeconds?Date.now()-this._maxAgeSeconds*1e3:0,t=await this._timestampModel.expireEntries(e,this._maxEntries),n=await self.caches.open(this._cacheName);for(const a of t)await n.delete(a,this._matchOptions);this._isRunning=!1,this._rerunRequested&&(this._rerunRequested=!1,H(this.expireEntries()))}async updateTimestamp(e){await this._timestampModel.setTimestamp(e,Date.now())}async isURLExpired(e){if(this._maxAgeSeconds){const t=await this._timestampModel.getTimestamp(e),n=Date.now()-this._maxAgeSeconds*1e3;return t!==void 0?t<n:!0}else return!1}async delete(){this._rerunRequested=!1,await this._timestampModel.expireEntries(1/0)}}function Se(s){B.add(s)}class Oe{constructor(e={}){this.cachedResponseWillBeUsed=async({event:t,request:n,cacheName:a,cachedResponse:r})=>{if(!r)return null;const i=this._isResponseDateFresh(r),c=this._getCacheExpiration(a);H(c.expireEntries());const o=c.updateTimestamp(n.url);if(t)try{t.waitUntil(o)}catch{}return i?r:null},this.cacheDidUpdate=async({cacheName:t,request:n})=>{const a=this._getCacheExpiration(t);await a.updateTimestamp(n.url),await a.expireEntries()},this._config=e,this._maxAgeSeconds=e.maxAgeSeconds,this._cacheExpirations=new Map,e.purgeOnQuotaError&&Se(()=>this.deleteCacheAndMetadata())}_getCacheExpiration(e){if(e===b.getRuntimeName())throw new h("expire-custom-caches-only");let t=this._cacheExpirations.get(e);return t||(t=new Ke(e,this._config),this._cacheExpirations.set(e,t)),t}_isResponseDateFresh(e){if(!this._maxAgeSeconds)return!0;const t=this._getDateHeaderTimestamp(e);if(t===null)return!0;const n=Date.now();return t>=n-this._maxAgeSeconds*1e3}_getDateHeaderTimestamp(e){if(!e.headers.has("date"))return null;const t=e.headers.get("date"),a=new Date(t).getTime();return isNaN(a)?null:a}async deleteCacheAndMetadata(){for(const[e,t]of this._cacheExpirations)await self.caches.delete(e),await t.delete();this._cacheExpirations=new Map}}Re([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"0f93805414c783a30b72e0ce88f2c7c4","url":"icons/icon.svg"},{"revision":"5dee5ddcd8e8533edb348118334d9756","url":"icons/icon-512.png"},{"revision":"75734e6b38a556148f51b9e10eeb01c8","url":"icons/icon-192.png"},{"revision":"0362e0eb00358646fd4c97674945adba","url":"icons/icon-180.png"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-D1U0PBps.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-CQy8NUxW.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-BwGtQefY.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-AB47RPpS.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-Dr2a5PWq.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-DBbQtnkh.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-CCATgMDC.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-DqRGFuVJ.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-DzYp2J9t.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-DBgEkZee.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-Db_hnpTO.js"},{"revision":null,"url":"assets/ordinal-Cboi1Yqb.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-KXwvxrd9.js"},{"revision":null,"url":"assets/linear-pPVtYfoA.js"},{"revision":null,"url":"assets/layout-C4hgRWBc.js"},{"revision":null,"url":"assets/katex-B1X10hvy.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-DlsqM5ac.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-DOKjshE4.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-BTx9nUR_.js"},{"revision":null,"url":"assets/init-Gi6I4Gst.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-B50sX0Jk.js"},{"revision":null,"url":"assets/index-pqtccC7s.js"},{"revision":null,"url":"assets/index-7DPrKRHX.css"},{"revision":null,"url":"assets/graph-CaPDDY3I.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-CZ0c5437.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BRH4oYpz.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-7__LLCFp.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-xb7jd7iV.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-Cl1dGq__.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-B7r-HJa0.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-yhW3nXxA.js"},{"revision":null,"url":"assets/defaultLocale-DX6XiGOO.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-BOfJIpm3.js"},{"revision":null,"url":"assets/cytoscape.esm-BQaXIfA_.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-CTeXZoSq.js"},{"revision":null,"url":"assets/clone-BQhUsBH5.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-CwKQYDdP.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-CwKQYDdP.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-DY15q7Tk.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-DDY6gqIZ.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-BP6XJFdX.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-BZlXzthI.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-CudZY5_F.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-cK52FAx0.js"},{"revision":null,"url":"assets/chunk-55IACEB6-CRSrEoDB.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-eU5lnLp-.js"},{"revision":null,"url":"assets/channel-spGqRIlf.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW-CPV76ZUM.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-Dh31wJRm.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-BFCoYW28.js"},{"revision":null,"url":"assets/arc-BBjpe_hN.js"},{"revision":null,"url":"assets/_baseUniq-CmudMx6k.js"},{"revision":null,"url":"assets/_basePickBy-BpcK6jhC.js"},{"revision":"0362e0eb00358646fd4c97674945adba","url":"icons/icon-180.png"},{"revision":"75734e6b38a556148f51b9e10eeb01c8","url":"icons/icon-192.png"},{"revision":"5dee5ddcd8e8533edb348118334d9756","url":"icons/icon-512.png"},{"revision":"b0747e8d6cde0d45e05be8e5c731729e","url":"manifest.webmanifest"}]);ye();q(({url:s})=>s.hostname==="cdn.jsdelivr.net"&&s.pathname.endsWith(".ttf"),new be({cacheName:"termbeam-fonts",plugins:[new Oe({maxEntries:5,maxAgeSeconds:365*24*60*60})]}));self.addEventListener("message",s=>{var e;((e=s.data)==null?void 0:e.type)==="CLEAR_CACHES"&&caches.keys().then(t=>{for(const n of t)n!=="workbox-precache-v2"&&caches.delete(n)})});self.addEventListener("install",()=>{self.skipWaiting()});self.addEventListener("activate",s=>{s.waitUntil(caches.delete("termbeam-navigation").then(()=>self.clients.claim()))});self.addEventListener("push",s=>{let e={title:"Command finished",body:"TermBeam"};if(s.data)try{e=s.data.json()}catch{}s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(async t=>{if(t.some(r=>r.url.includes(self.location.origin)&&r.focused))return;const a={body:e.body||"A command has completed",icon:"/icons/icon-192.png",badge:"/icons/icon-192.png",tag:"termbeam-cmd",renotify:!0,data:{url:"/"},vibrate:[200,100,200]};try{await self.navigator.setAppBadge(1)}catch{}return self.registration.showNotification(e.title||"Command finished",a)}))});self.addEventListener("notificationclick",s=>{var t;s.notification.close();try{self.navigator.clearAppBadge()}catch{}const e=((t=s.notification.data)==null?void 0:t.url)||"/";s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(n=>{for(const a of n)if(a.url.includes(self.location.origin)&&"focus"in a)return a.focus();return self.clients.openWindow(e)}))});
|
package/src/server/index.js
CHANGED
|
@@ -96,7 +96,7 @@ function createTermBeamServer(overrides = {}) {
|
|
|
96
96
|
const server = http.createServer(app);
|
|
97
97
|
const wss = new WebSocketServer({ server, path: '/ws', maxPayload: 1 * 1024 * 1024 });
|
|
98
98
|
|
|
99
|
-
const state = { shareBaseUrl: null, updateInfo: null };
|
|
99
|
+
const state = { shareBaseUrl: null, updateInfo: null, wss };
|
|
100
100
|
app.use('/preview', auth.middleware, createPreviewProxy());
|
|
101
101
|
setupRoutes(app, { auth, sessions, config, state, pushManager });
|
|
102
102
|
setupWebSocket(wss, { auth, sessions });
|
|
@@ -298,6 +298,18 @@ function createTermBeamServer(overrides = {}) {
|
|
|
298
298
|
);
|
|
299
299
|
console.log('');
|
|
300
300
|
|
|
301
|
+
// Show post-update success message if this is the first start after an update
|
|
302
|
+
const { readUpdateResult, clearUpdateResult } = require('../utils/update-executor');
|
|
303
|
+
const updateResult = readUpdateResult();
|
|
304
|
+
if (updateResult) {
|
|
305
|
+
const gn2 = '\x1b[38;5;114m';
|
|
306
|
+
console.log(
|
|
307
|
+
` ${gn2}✓ Successfully updated from v${updateResult.fromVersion} to v${updateResult.toVersion}${rs}`,
|
|
308
|
+
);
|
|
309
|
+
console.log('');
|
|
310
|
+
clearUpdateResult();
|
|
311
|
+
}
|
|
312
|
+
|
|
301
313
|
// Non-blocking update check — runs after banner, never delays startup.
|
|
302
314
|
// Skip under the Node test runner and CI to avoid network requests in tests.
|
|
303
315
|
// Accept any version containing a semver-like pattern (including dev builds).
|
package/src/server/routes.js
CHANGED
|
@@ -101,23 +101,142 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager }) {
|
|
|
101
101
|
|
|
102
102
|
try {
|
|
103
103
|
const info = await checkForUpdate({ currentVersion: config.version, force });
|
|
104
|
-
const
|
|
105
|
-
state.updateInfo = { ...info, ...
|
|
104
|
+
const { installCmd, installArgs, ...publicInstallInfo } = detectInstallMethod();
|
|
105
|
+
state.updateInfo = { ...info, ...publicInstallInfo };
|
|
106
106
|
res.json(state.updateInfo);
|
|
107
107
|
} catch (err) {
|
|
108
108
|
log.warn(`Update check failed: ${err.message}`);
|
|
109
|
-
const
|
|
109
|
+
const { installCmd, installArgs, ...publicInstallInfo } = detectInstallMethod();
|
|
110
110
|
const fallback = {
|
|
111
111
|
current: config.version,
|
|
112
112
|
latest: null,
|
|
113
113
|
updateAvailable: false,
|
|
114
|
-
...
|
|
114
|
+
...publicInstallInfo,
|
|
115
115
|
};
|
|
116
116
|
state.updateInfo = fallback;
|
|
117
117
|
res.json(fallback);
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
+
// Trigger update — rate limited to 1 per 5 minutes
|
|
122
|
+
const updateTriggerLimit = rateLimit({
|
|
123
|
+
windowMs: 5 * 60 * 1000,
|
|
124
|
+
max: 1,
|
|
125
|
+
standardHeaders: true,
|
|
126
|
+
legacyHeaders: false,
|
|
127
|
+
handler: (_req, res) =>
|
|
128
|
+
res
|
|
129
|
+
.status(429)
|
|
130
|
+
.json({ error: 'Update already attempted recently. Try again in a few minutes.' }),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
app.post('/api/update', auth.middleware, updateTriggerLimit, async (req, res) => {
|
|
134
|
+
const { detectInstallMethod } = require('../utils/update-check');
|
|
135
|
+
const { getUpdateState, executeUpdate, resetState } = require('../utils/update-executor');
|
|
136
|
+
|
|
137
|
+
const currentState = getUpdateState();
|
|
138
|
+
if (currentState.status !== 'idle' && currentState.status !== 'failed') {
|
|
139
|
+
return res.status(409).json({ error: 'Update already in progress', state: currentState });
|
|
140
|
+
}
|
|
141
|
+
// Reset state if retrying after a failure
|
|
142
|
+
if (currentState.status === 'failed') resetState();
|
|
143
|
+
|
|
144
|
+
const installInfo = detectInstallMethod();
|
|
145
|
+
if (!installInfo.canAutoUpdate) {
|
|
146
|
+
return res.status(400).json({
|
|
147
|
+
error: 'Auto-update not available for this installation method',
|
|
148
|
+
method: installInfo.method,
|
|
149
|
+
command: installInfo.command,
|
|
150
|
+
canAutoUpdate: false,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Respond immediately — update runs in background
|
|
155
|
+
res.json({ status: 'updating', method: installInfo.method });
|
|
156
|
+
|
|
157
|
+
// Broadcast progress to WebSocket clients
|
|
158
|
+
const broadcastProgress = (updateStatus) => {
|
|
159
|
+
if (state.wss) {
|
|
160
|
+
const msg = JSON.stringify({ type: 'update-progress', ...updateStatus });
|
|
161
|
+
state.wss.clients.forEach((client) => {
|
|
162
|
+
if (client.readyState === 1) {
|
|
163
|
+
try {
|
|
164
|
+
client.send(msg);
|
|
165
|
+
} catch {
|
|
166
|
+
// Client may have disconnected
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Build the restart handler
|
|
174
|
+
const performRestart = async () => {
|
|
175
|
+
if (installInfo.restartStrategy === 'pm2') {
|
|
176
|
+
// PM2 restart — PM2 will bring the process back up
|
|
177
|
+
const { execFile: execFileCb } = require('child_process');
|
|
178
|
+
const serviceName = process.env.pm_id || 'termbeam';
|
|
179
|
+
broadcastProgress({
|
|
180
|
+
status: 'restarting',
|
|
181
|
+
phase: 'Restarting via PM2...',
|
|
182
|
+
restartStrategy: 'pm2',
|
|
183
|
+
});
|
|
184
|
+
// Give WS messages time to reach clients
|
|
185
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
186
|
+
// Use async execFile so WS messages can flush before the restart
|
|
187
|
+
execFileCb('pm2', ['restart', serviceName], { timeout: 10000, stdio: 'ignore' }, (err) => {
|
|
188
|
+
if (err) {
|
|
189
|
+
log.warn(`PM2 restart failed: ${err.message}`);
|
|
190
|
+
// Fall back to exit
|
|
191
|
+
sessions.shutdown();
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
} else {
|
|
196
|
+
// Exit strategy — clean shutdown, user must restart manually
|
|
197
|
+
broadcastProgress({
|
|
198
|
+
status: 'restarting',
|
|
199
|
+
phase: 'Update installed. Server shutting down...',
|
|
200
|
+
restartStrategy: 'exit',
|
|
201
|
+
});
|
|
202
|
+
// Close all WS connections with "Service Restart" close code
|
|
203
|
+
if (state.wss) {
|
|
204
|
+
state.wss.clients.forEach((client) => {
|
|
205
|
+
try {
|
|
206
|
+
client.close(1012, 'Server updated — please restart');
|
|
207
|
+
} catch {
|
|
208
|
+
// ignore
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Give WS close frames time to be sent
|
|
213
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
214
|
+
sessions.shutdown();
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Execute update in background (don't await in request handler)
|
|
220
|
+
executeUpdate({
|
|
221
|
+
currentVersion: config.version,
|
|
222
|
+
installCmd: installInfo.installCmd,
|
|
223
|
+
installArgs: installInfo.installArgs,
|
|
224
|
+
command: installInfo.command,
|
|
225
|
+
method: installInfo.method,
|
|
226
|
+
restartStrategy: installInfo.restartStrategy,
|
|
227
|
+
onProgress: broadcastProgress,
|
|
228
|
+
performRestart,
|
|
229
|
+
}).catch((err) => {
|
|
230
|
+
log.error(`Update execution error: ${err.message}`);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Poll update status (fallback for when WS isn't connected)
|
|
235
|
+
app.get('/api/update/status', apiRateLimit, auth.middleware, (_req, res) => {
|
|
236
|
+
const { getUpdateState } = require('../utils/update-executor');
|
|
237
|
+
res.json(getUpdateState());
|
|
238
|
+
});
|
|
239
|
+
|
|
121
240
|
// Share token auto-login middleware: validates ?ott= param, sets session cookie, redirects to clean URL
|
|
122
241
|
function autoLogin(req, res, next) {
|
|
123
242
|
const { ott } = req.query;
|
|
@@ -73,6 +73,8 @@ function normalizeVersion(version) {
|
|
|
73
73
|
/**
|
|
74
74
|
* Compare two semver version strings (e.g. "1.10.2" vs "1.11.0").
|
|
75
75
|
* Returns true if `latest` is newer than `current`.
|
|
76
|
+
* Pre-release versions (e.g. "1.15.3-rc.1") are considered older than
|
|
77
|
+
* the same stable version ("1.15.3"), so an update will be offered.
|
|
76
78
|
* Returns false if either version cannot be parsed.
|
|
77
79
|
*/
|
|
78
80
|
function isNewerVersion(current, latest) {
|
|
@@ -83,9 +85,25 @@ function isNewerVersion(current, latest) {
|
|
|
83
85
|
if (lat[i] > cur[i]) return true;
|
|
84
86
|
if (lat[i] < cur[i]) return false;
|
|
85
87
|
}
|
|
88
|
+
// Same base version — if current is a pre-release but latest is stable,
|
|
89
|
+
// the stable release is newer (e.g. 1.15.3-rc.1 → 1.15.3)
|
|
90
|
+
if (isPreRelease(current) && !isPreRelease(latest)) return true;
|
|
86
91
|
return false;
|
|
87
92
|
}
|
|
88
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Check if a version string contains pre-release metadata (e.g. "-rc.1", "-dev.5").
|
|
96
|
+
*/
|
|
97
|
+
function isPreRelease(version) {
|
|
98
|
+
if (typeof version !== 'string') return false;
|
|
99
|
+
let v = version.trim();
|
|
100
|
+
if (v[0] === 'v' || v[0] === 'V') v = v.slice(1);
|
|
101
|
+
// Strip build metadata first (+foo)
|
|
102
|
+
const plusIdx = v.indexOf('+');
|
|
103
|
+
if (plusIdx !== -1) v = v.slice(0, plusIdx);
|
|
104
|
+
return v.includes('-');
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
/**
|
|
90
108
|
* Strip ANSI escape sequences and control characters from a string.
|
|
91
109
|
* Prevents terminal injection if the registry returns malicious data.
|
|
@@ -222,39 +240,151 @@ async function checkForUpdate({ currentVersion, force = false } = {}) {
|
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
/**
|
|
225
|
-
* Detect how TermBeam was installed and return the appropriate update command
|
|
226
|
-
*
|
|
243
|
+
* Detect how TermBeam was installed and return the appropriate update command,
|
|
244
|
+
* whether it can auto-update, and the restart strategy.
|
|
245
|
+
* @returns {{ method: string, command: string, canAutoUpdate: boolean, restartStrategy: 'pm2'|'exit'|'none', installCmd: string|null, installArgs: string[]|null }}
|
|
246
|
+
* Note: installCmd/installArgs are internal — stripped before sending to API clients.
|
|
227
247
|
*/
|
|
228
248
|
function detectInstallMethod() {
|
|
229
249
|
// npx / npm exec — npm sets npm_command=exec
|
|
230
250
|
if (process.env.npm_command === 'exec') {
|
|
231
251
|
log.debug('Install method: npx');
|
|
232
|
-
return {
|
|
252
|
+
return {
|
|
253
|
+
method: 'npx',
|
|
254
|
+
command: 'npx termbeam@latest',
|
|
255
|
+
installCmd: 'npx',
|
|
256
|
+
installArgs: ['termbeam@latest'],
|
|
257
|
+
canAutoUpdate: false,
|
|
258
|
+
restartStrategy: 'none',
|
|
259
|
+
};
|
|
233
260
|
}
|
|
234
261
|
|
|
262
|
+
// PM2 managed — detect via PM2 environment variables
|
|
263
|
+
const isPm2 = isRunningUnderPm2();
|
|
264
|
+
|
|
235
265
|
// Detect package manager from npm_execpath (set during npm/yarn/pnpm lifecycle)
|
|
266
|
+
// Check this before file-system checks since env vars are more reliable
|
|
236
267
|
const execPath = process.env.npm_execpath || '';
|
|
237
268
|
if (execPath.includes('yarn')) {
|
|
238
|
-
log.debug(
|
|
239
|
-
return {
|
|
269
|
+
log.debug(`Install method: yarn${isPm2 ? ' (PM2)' : ''}`);
|
|
270
|
+
return {
|
|
271
|
+
method: 'yarn',
|
|
272
|
+
command: 'yarn global add termbeam@latest',
|
|
273
|
+
installCmd: 'yarn',
|
|
274
|
+
installArgs: ['global', 'add', 'termbeam@latest'],
|
|
275
|
+
canAutoUpdate: true,
|
|
276
|
+
restartStrategy: isPm2 ? 'pm2' : 'exit',
|
|
277
|
+
};
|
|
240
278
|
}
|
|
241
279
|
if (execPath.includes('pnpm')) {
|
|
242
|
-
log.debug(
|
|
243
|
-
return {
|
|
280
|
+
log.debug(`Install method: pnpm${isPm2 ? ' (PM2)' : ''}`);
|
|
281
|
+
return {
|
|
282
|
+
method: 'pnpm',
|
|
283
|
+
command: 'pnpm add -g termbeam@latest',
|
|
284
|
+
installCmd: 'pnpm',
|
|
285
|
+
installArgs: ['add', '-g', 'termbeam@latest'],
|
|
286
|
+
canAutoUpdate: true,
|
|
287
|
+
restartStrategy: isPm2 ? 'pm2' : 'exit',
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Development / git clone — not in node_modules and .git exists
|
|
292
|
+
// Check before Docker: a git checkout running inside a container (CI/devcontainers)
|
|
293
|
+
// should be treated as source, not Docker
|
|
294
|
+
if (isRunningFromSource()) {
|
|
295
|
+
log.debug('Install method: source');
|
|
296
|
+
return {
|
|
297
|
+
method: 'source',
|
|
298
|
+
command: 'git pull && npm install && npm run build:frontend',
|
|
299
|
+
installCmd: null,
|
|
300
|
+
installArgs: null,
|
|
301
|
+
canAutoUpdate: false,
|
|
302
|
+
restartStrategy: 'none',
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Docker — check for /.dockerenv or /proc/1/cgroup containing docker
|
|
307
|
+
if (isRunningInDocker()) {
|
|
308
|
+
log.debug('Install method: docker');
|
|
309
|
+
return {
|
|
310
|
+
method: 'docker',
|
|
311
|
+
command: 'docker pull termbeam:latest && docker-compose up -d',
|
|
312
|
+
installCmd: null,
|
|
313
|
+
installArgs: null,
|
|
314
|
+
canAutoUpdate: false,
|
|
315
|
+
restartStrategy: 'none',
|
|
316
|
+
};
|
|
244
317
|
}
|
|
245
318
|
|
|
246
319
|
// Default: npm global install
|
|
247
|
-
log.debug(
|
|
248
|
-
return {
|
|
320
|
+
log.debug(`Install method: npm${isPm2 ? ' (PM2)' : ''}`);
|
|
321
|
+
return {
|
|
322
|
+
method: 'npm',
|
|
323
|
+
command: 'npm install -g termbeam@latest',
|
|
324
|
+
installCmd: 'npm',
|
|
325
|
+
installArgs: ['install', '-g', 'termbeam@latest'],
|
|
326
|
+
canAutoUpdate: true,
|
|
327
|
+
restartStrategy: isPm2 ? 'pm2' : 'exit',
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Detect if running inside a Docker container.
|
|
333
|
+
*/
|
|
334
|
+
function isRunningInDocker() {
|
|
335
|
+
try {
|
|
336
|
+
if (fs.existsSync('/.dockerenv')) return true;
|
|
337
|
+
} catch {
|
|
338
|
+
// ignore
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
const cgroup = fs.readFileSync('/proc/1/cgroup', 'utf8');
|
|
342
|
+
if (cgroup.includes('docker') || cgroup.includes('containerd')) return true;
|
|
343
|
+
} catch {
|
|
344
|
+
// Not Linux or no access — not Docker
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Detect if running from a git source checkout (not installed as a package).
|
|
351
|
+
* Walks upward from __dirname looking for .git to avoid fragile fixed-depth assumptions.
|
|
352
|
+
*/
|
|
353
|
+
function isRunningFromSource() {
|
|
354
|
+
// If __dirname is inside node_modules, it's a package install
|
|
355
|
+
if (__dirname.includes('node_modules')) return false;
|
|
356
|
+
try {
|
|
357
|
+
let currentDir = __dirname;
|
|
358
|
+
for (let i = 0; i < 10; i++) {
|
|
359
|
+
if (fs.existsSync(path.join(currentDir, '.git'))) return true;
|
|
360
|
+
const parentDir = path.dirname(currentDir);
|
|
361
|
+
if (!parentDir || parentDir === currentDir) break;
|
|
362
|
+
currentDir = parentDir;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
} catch {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Detect if running under PM2 process manager.
|
|
372
|
+
*/
|
|
373
|
+
function isRunningUnderPm2() {
|
|
374
|
+
return !!(process.env.PM2_HOME || process.env.pm_id || process.env.PM2_USAGE);
|
|
249
375
|
}
|
|
250
376
|
|
|
251
377
|
module.exports = {
|
|
252
378
|
checkForUpdate,
|
|
253
379
|
isNewerVersion,
|
|
380
|
+
isPreRelease,
|
|
254
381
|
normalizeVersion,
|
|
255
382
|
fetchLatestVersion,
|
|
256
383
|
readCache,
|
|
257
384
|
writeCache,
|
|
258
385
|
sanitizeVersion,
|
|
259
386
|
detectInstallMethod,
|
|
387
|
+
isRunningInDocker,
|
|
388
|
+
isRunningFromSource,
|
|
389
|
+
isRunningUnderPm2,
|
|
260
390
|
};
|