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.
Files changed (62) hide show
  1. package/README.md +1 -0
  2. package/package.json +1 -1
  3. package/public/assets/{_basePickBy-CYR9pyOe.js → _basePickBy-BpcK6jhC.js} +1 -1
  4. package/public/assets/{_baseUniq-DeFSIx-P.js → _baseUniq-CmudMx6k.js} +1 -1
  5. package/public/assets/{arc-CUEX1fu7.js → arc-BBjpe_hN.js} +1 -1
  6. package/public/assets/{architectureDiagram-2XIMDMQ5-BHhXPzZJ.js → architectureDiagram-2XIMDMQ5-BFCoYW28.js} +1 -1
  7. package/public/assets/{blockDiagram-WCTKOSBZ-RsOwF2Ow.js → blockDiagram-WCTKOSBZ-Dh31wJRm.js} +1 -1
  8. package/public/assets/{c4Diagram-IC4MRINW-B7KKaZ1J.js → c4Diagram-IC4MRINW-CPV76ZUM.js} +1 -1
  9. package/public/assets/channel-spGqRIlf.js +1 -0
  10. package/public/assets/{chunk-4BX2VUAB-DOUcZxxl.js → chunk-4BX2VUAB-eU5lnLp-.js} +1 -1
  11. package/public/assets/{chunk-55IACEB6-bPgkuqF0.js → chunk-55IACEB6-CRSrEoDB.js} +1 -1
  12. package/public/assets/{chunk-FMBD7UC4-BWT_ExWr.js → chunk-FMBD7UC4-cK52FAx0.js} +1 -1
  13. package/public/assets/{chunk-JSJVCQXG-Df0AgfkZ.js → chunk-JSJVCQXG-CudZY5_F.js} +1 -1
  14. package/public/assets/{chunk-KX2RTZJC-DnYuhgK5.js → chunk-KX2RTZJC-BZlXzthI.js} +1 -1
  15. package/public/assets/{chunk-NQ4KR5QH-Dge50UUS.js → chunk-NQ4KR5QH-BP6XJFdX.js} +1 -1
  16. package/public/assets/{chunk-QZHKN3VN-BT0knyhA.js → chunk-QZHKN3VN-DDY6gqIZ.js} +1 -1
  17. package/public/assets/{chunk-WL4C6EOR-DutXGT-d.js → chunk-WL4C6EOR-DY15q7Tk.js} +1 -1
  18. package/public/assets/classDiagram-VBA2DB6C-CwKQYDdP.js +1 -0
  19. package/public/assets/classDiagram-v2-RAHNMMFH-CwKQYDdP.js +1 -0
  20. package/public/assets/clone-BQhUsBH5.js +1 -0
  21. package/public/assets/{cose-bilkent-S5V4N54A-doAicD_V.js → cose-bilkent-S5V4N54A-CTeXZoSq.js} +1 -1
  22. package/public/assets/{dagre-KLK3FWXG-O4cFm_hK.js → dagre-KLK3FWXG-BOfJIpm3.js} +1 -1
  23. package/public/assets/{diagram-E7M64L7V-BifAzLVq.js → diagram-E7M64L7V-yhW3nXxA.js} +1 -1
  24. package/public/assets/{diagram-IFDJBPK2-BfnxORJG.js → diagram-IFDJBPK2-B7r-HJa0.js} +1 -1
  25. package/public/assets/{diagram-P4PSJMXO-DTr1JYXb.js → diagram-P4PSJMXO-Cl1dGq__.js} +1 -1
  26. package/public/assets/{erDiagram-INFDFZHY-l1N_y881.js → erDiagram-INFDFZHY-xb7jd7iV.js} +1 -1
  27. package/public/assets/{flowDiagram-PKNHOUZH-CLGWYVco.js → flowDiagram-PKNHOUZH-7__LLCFp.js} +1 -1
  28. package/public/assets/{ganttDiagram-A5KZAMGK-9ERk2sFV.js → ganttDiagram-A5KZAMGK-BRH4oYpz.js} +1 -1
  29. package/public/assets/{gitGraphDiagram-K3NZZRJ6-BxK3Z85E.js → gitGraphDiagram-K3NZZRJ6-CZ0c5437.js} +1 -1
  30. package/public/assets/{graph-PwJPsvsO.js → graph-CaPDDY3I.js} +1 -1
  31. package/public/assets/index-7DPrKRHX.css +32 -0
  32. package/public/assets/{index-0a9Qn-A1.js → index-pqtccC7s.js} +115 -115
  33. package/public/assets/{infoDiagram-LFFYTUFH-DygGOypU.js → infoDiagram-LFFYTUFH-B50sX0Jk.js} +1 -1
  34. package/public/assets/{ishikawaDiagram-PHBUUO56-CySga9vu.js → ishikawaDiagram-PHBUUO56-BTx9nUR_.js} +1 -1
  35. package/public/assets/{journeyDiagram-4ABVD52K-ZIZNkXyJ.js → journeyDiagram-4ABVD52K-DOKjshE4.js} +1 -1
  36. package/public/assets/{kanban-definition-K7BYSVSG-IxWUQjiQ.js → kanban-definition-K7BYSVSG-DlsqM5ac.js} +1 -1
  37. package/public/assets/{layout-DbFs-9Gp.js → layout-C4hgRWBc.js} +1 -1
  38. package/public/assets/{linear-F1crC_h8.js → linear-pPVtYfoA.js} +1 -1
  39. package/public/assets/{mindmap-definition-YRQLILUH-BwXWnIOB.js → mindmap-definition-YRQLILUH-KXwvxrd9.js} +1 -1
  40. package/public/assets/{pieDiagram-SKSYHLDU-CTKX_qGt.js → pieDiagram-SKSYHLDU-Db_hnpTO.js} +1 -1
  41. package/public/assets/{quadrantDiagram-337W2JSQ-C9hYwyla.js → quadrantDiagram-337W2JSQ-DBgEkZee.js} +1 -1
  42. package/public/assets/{requirementDiagram-Z7DCOOCP-eOJ_I7lS.js → requirementDiagram-Z7DCOOCP-DzYp2J9t.js} +1 -1
  43. package/public/assets/{sankeyDiagram-WA2Y5GQK-CNYzy0Z-.js → sankeyDiagram-WA2Y5GQK-DqRGFuVJ.js} +1 -1
  44. package/public/assets/{sequenceDiagram-2WXFIKYE-ChHor0a9.js → sequenceDiagram-2WXFIKYE-CCATgMDC.js} +1 -1
  45. package/public/assets/{stateDiagram-RAJIS63D-CF2B1sp8.js → stateDiagram-RAJIS63D-DBbQtnkh.js} +1 -1
  46. package/public/assets/stateDiagram-v2-FVOUBMTO-Dr2a5PWq.js +1 -0
  47. package/public/assets/{timeline-definition-YZTLITO2-H72vBjMX.js → timeline-definition-YZTLITO2-AB47RPpS.js} +1 -1
  48. package/public/assets/{treemap-KZPCXAKY-CWEZDi_m.js → treemap-KZPCXAKY-BwGtQefY.js} +1 -1
  49. package/public/assets/{vennDiagram-LZ73GAT5-BEqSLv2W.js → vennDiagram-LZ73GAT5-CQy8NUxW.js} +1 -1
  50. package/public/assets/{xychartDiagram-JWTSCODW-enJM4ByX.js → xychartDiagram-JWTSCODW-D1U0PBps.js} +1 -1
  51. package/public/index.html +2 -2
  52. package/public/sw.js +1 -1
  53. package/src/server/index.js +13 -1
  54. package/src/server/routes.js +123 -4
  55. package/src/utils/update-check.js +139 -9
  56. package/src/utils/update-executor.js +340 -0
  57. package/public/assets/channel-CBJEzKmm.js +0 -1
  58. package/public/assets/classDiagram-VBA2DB6C-C-rOD9EU.js +0 -1
  59. package/public/assets/classDiagram-v2-RAHNMMFH-C-rOD9EU.js +0 -1
  60. package/public/assets/clone-DIyhZC23.js +0 -1
  61. package/public/assets/index-Z_lybSmO.css +0 -32
  62. 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)}))});
@@ -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).
@@ -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 installInfo = detectInstallMethod();
105
- state.updateInfo = { ...info, ...installInfo };
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 installInfo = detectInstallMethod();
109
+ const { installCmd, installArgs, ...publicInstallInfo } = detectInstallMethod();
110
110
  const fallback = {
111
111
  current: config.version,
112
112
  latest: null,
113
113
  updateAvailable: false,
114
- ...installInfo,
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
- * @returns {{ method: string, command: string }}
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 { method: 'npx', command: 'npx termbeam@latest' };
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('Install method: yarn');
239
- return { method: 'yarn', command: 'yarn global add termbeam@latest' };
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('Install method: pnpm');
243
- return { method: 'pnpm', command: 'pnpm add -g termbeam@latest' };
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('Install method: npm');
248
- return { method: 'npm', command: 'npm install -g termbeam@latest' };
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
  };