termbeam 1.22.4 → 1.23.0
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/CHANGELOG.md +11 -0
- package/README.md +4 -2
- package/package.json +2 -2
- package/public/assets/{_basePickBy-Q45O29Rp.js → _basePickBy-DyywFBt7.js} +1 -1
- package/public/assets/{_baseUniq-DveOFRWk.js → _baseUniq-8tmjrEbB.js} +1 -1
- package/public/assets/{arc-BxuxWgHK.js → arc-CHQhvBsi.js} +1 -1
- package/public/assets/{architectureDiagram-Q4EWVU46-ByQ5j_Jw.js → architectureDiagram-Q4EWVU46-MEcrgEqw.js} +1 -1
- package/public/assets/{blockDiagram-DXYQGD6D-DrElm25z.js → blockDiagram-DXYQGD6D-EtZXyKmn.js} +1 -1
- package/public/assets/{c4Diagram-AHTNJAMY-BATf25U2.js → c4Diagram-AHTNJAMY-BuvkD-Hg.js} +1 -1
- package/public/assets/channel-a7jr0in8.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-BksLrXVs.js → chunk-4BX2VUAB-m4pupC7U.js} +1 -1
- package/public/assets/{chunk-4TB4RGXK-D5KX6fMH.js → chunk-4TB4RGXK-C-BdMhbz.js} +1 -1
- package/public/assets/{chunk-55IACEB6-DLtS129C.js → chunk-55IACEB6-BhvwRyIq.js} +1 -1
- package/public/assets/{chunk-EDXVE4YY-CtqMCguR.js → chunk-EDXVE4YY-BqButrNC.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-BkeSsxqP.js → chunk-FMBD7UC4-54Zrs_SG.js} +1 -1
- package/public/assets/{chunk-OYMX7WX6-Dl4p17KI.js → chunk-OYMX7WX6-Zxqgp5PC.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-CBMUqRwi.js → chunk-QZHKN3VN-BOkHXJLa.js} +1 -1
- package/public/assets/{chunk-YZCP3GAM-D4sWv3YN.js → chunk-YZCP3GAM-BFo64bhH.js} +1 -1
- package/public/assets/classDiagram-6PBFFD2Q-DHBlyv18.js +1 -0
- package/public/assets/classDiagram-v2-HSJHXN6E-DHBlyv18.js +1 -0
- package/public/assets/clone-D-YD4Hfu.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-BW1nNLLk.js → cose-bilkent-S5V4N54A-CdRKoWQm.js} +1 -1
- package/public/assets/{dagre-KV5264BT-CDXKs5yo.js → dagre-KV5264BT-CqYqJqNz.js} +1 -1
- package/public/assets/{diagram-5BDNPKRD-DfSZsVAD.js → diagram-5BDNPKRD-KXbdPm8R.js} +1 -1
- package/public/assets/{diagram-G4DWMVQ6-CIKqapDb.js → diagram-G4DWMVQ6-BjcXJ3-Q.js} +1 -1
- package/public/assets/{diagram-MMDJMWI5-CWwOGFbE.js → diagram-MMDJMWI5-Da91D4Jv.js} +1 -1
- package/public/assets/{diagram-TYMM5635-CAMd8OZP.js → diagram-TYMM5635-CilekWFu.js} +1 -1
- package/public/assets/{erDiagram-SMLLAGMA-BCpXZJei.js → erDiagram-SMLLAGMA-COlP9qIO.js} +1 -1
- package/public/assets/{flowDiagram-DWJPFMVM-DpRY38iu.js → flowDiagram-DWJPFMVM-BebPntFK.js} +1 -1
- package/public/assets/{ganttDiagram-T4ZO3ILL-DJrwFHlO.js → ganttDiagram-T4ZO3ILL-C-JweidK.js} +1 -1
- package/public/assets/{gitGraphDiagram-UUTBAWPF-CpEmQcDN.js → gitGraphDiagram-UUTBAWPF-CmtVcokM.js} +1 -1
- package/public/assets/{graph-C-meUi4Z.js → graph-DGYmOqb_.js} +1 -1
- package/public/assets/index-Bap8dJMF.js +463 -0
- package/public/assets/index-CTMMjkRB.css +32 -0
- package/public/assets/{infoDiagram-42DDH7IO-sXN9f7L8.js → infoDiagram-42DDH7IO-JWuRQxiM.js} +1 -1
- package/public/assets/{ishikawaDiagram-UXIWVN3A-D_2gGEMI.js → ishikawaDiagram-UXIWVN3A-Buhn0bpg.js} +1 -1
- package/public/assets/{journeyDiagram-VCZTEJTY-CUv-hh0I.js → journeyDiagram-VCZTEJTY-CKm_djiT.js} +1 -1
- package/public/assets/{kanban-definition-6JOO6SKY-Cx3Plwl2.js → kanban-definition-6JOO6SKY-C_Sj08cN.js} +1 -1
- package/public/assets/{layout-CpGcFwHw.js → layout-BQRZ4_Il.js} +1 -1
- package/public/assets/{linear-Bw9XpXJW.js → linear-wWmlfvSH.js} +1 -1
- package/public/assets/{mindmap-definition-QFDTVHPH-Swj4jh3Z.js → mindmap-definition-QFDTVHPH-CwCTVEhc.js} +1 -1
- package/public/assets/{pieDiagram-DEJITSTG-Ck2h10zd.js → pieDiagram-DEJITSTG-C6ng_7H-.js} +1 -1
- package/public/assets/{quadrantDiagram-34T5L4WZ-B1ZrruGm.js → quadrantDiagram-34T5L4WZ-tAb9lK_2.js} +1 -1
- package/public/assets/{requirementDiagram-MS252O5E-B8JKwgDX.js → requirementDiagram-MS252O5E-DO7a12OA.js} +1 -1
- package/public/assets/{sankeyDiagram-XADWPNL6-b442XRH8.js → sankeyDiagram-XADWPNL6-DjRA6Gx_.js} +1 -1
- package/public/assets/{sequenceDiagram-FGHM5R23-DY9IFA-F.js → sequenceDiagram-FGHM5R23-DmTXUCCq.js} +1 -1
- package/public/assets/{stateDiagram-FHFEXIEX-IGjwW9Pw.js → stateDiagram-FHFEXIEX-cpBtM_eo.js} +1 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-xe4-E4yV.js +1 -0
- package/public/assets/{timeline-definition-GMOUNBTQ-B0JwGAT7.js → timeline-definition-GMOUNBTQ-BOPg9ppv.js} +1 -1
- package/public/assets/{vennDiagram-DHZGUBPP-DKLiidvh.js → vennDiagram-DHZGUBPP-C1wYmABu.js} +1 -1
- package/public/assets/{wardley-RL74JXVD-C1HnYfXI.js → wardley-RL74JXVD-BsEwEn5-.js} +1 -1
- package/public/assets/{wardleyDiagram-NUSXRM2D-BhbO6L2Q.js → wardleyDiagram-NUSXRM2D-Dt0kKYbS.js} +1 -1
- package/public/assets/{xychartDiagram-5P7HB3ND-CSZSmQ9N.js → xychartDiagram-5P7HB3ND-CqzYOgsH.js} +1 -1
- package/public/index.html +2 -2
- package/public/sw.js +1 -1
- package/src/server/index.js +107 -9
- package/src/server/preferences.js +280 -0
- package/src/server/routes.js +10 -1
- package/src/server/sessions.js +73 -6
- package/src/server/websocket.js +43 -22
- package/public/assets/channel-RrT8pJmM.js +0 -1
- package/public/assets/classDiagram-6PBFFD2Q-BaJ5lA7J.js +0 -1
- package/public/assets/classDiagram-v2-HSJHXN6E-BaJ5lA7J.js +0 -1
- package/public/assets/clone-BFbWnZ1G.js +0 -1
- package/public/assets/index-CK9vCujh.css +0 -32
- package/public/assets/index-Do67tk4s.js +0 -463
- package/public/assets/stateDiagram-v2-QKLJ7IA2-2xQCbec6.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 v(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 i=new URL(s,location.href);return{cacheKey:i.href,url:i.href}}const{revision:e,url:t}=s;if(!t)throw new h("add-to-cache-list-unexpected-type",{entry:s});if(!e){const i=new URL(t,location.href);return{cacheKey:i.href,url:i.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(),i={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},r=ee()?n.body:await n.blob();return new Response(r,i)}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 i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),r=await s.keys(e,i);for(const c of r){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 ie(){for(const s of B)await s()}function re(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 r=await t.preloadResponse;if(r)return r}const a=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const r of this.iterateCallbacks("requestWillFetch"))n=await r({request:n.clone(),event:t})}catch(r){if(r instanceof Error)throw new h("plugin-error-request-will-fetch",{thrownErrorMessage:r.message})}const i=n.clone();try{let r;r=await fetch(n,n.mode==="navigate"?void 0:this._strategy.fetchOptions);for(const c of this.iterateCallbacks("fetchDidSucceed"))r=await c({event:t,request:i,response:r});return r}catch(r){throw a&&await this.runCallbacks("fetchDidFail",{error:r,event:t,originalRequest:a.clone(),request:i.clone()}),r}}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:i}=this._strategy,r=await this.getCacheKey(t,"read"),c=Object.assign(Object.assign({},i),{cacheName:a});n=await caches.match(r,c);for(const o of this.iterateCallbacks("cachedResponseWillBeUsed"))n=await o({cacheName:a,matchOptions:i,cachedResponse:n,request:r,event:this.event})||void 0;return n}async cachePut(e,t){const n=C(e);await re(0);const a=await this.getCacheKey(n,"write");if(!t)throw new h("cache-put-with-no-response",{url:se(a.url)});const i=await this._ensureResponseSafeToCache(t);if(!i)return!1;const{cacheName:r,matchOptions:c}=this._strategy,o=await self.caches.open(r),l=this.hasCallback("cacheDidUpdate"),g=l?await ne(o,a.clone(),["__WB_REVISION__"],c):null;try{await o.put(a,l?i.clone():i)}catch(u){if(u instanceof Error)throw u.name==="QuotaExceededError"&&await ie(),u}for(const u of this.iterateCallbacks("cacheDidUpdate"))await u({cacheName:r,oldResponse:g,newResponse:i.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 i of this.iterateCallbacks("cacheKeyWillBeUsed"))a=C(await i({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 i=>{const r=Object.assign(Object.assign({},i),{state:n});return t[e](r)}}}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,i=new ce(this,{event:t,request:n,params:a}),r=this._getResponse(i,n,t),c=this._awaitComplete(r,i,n,t);return[r,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(i){if(i instanceof Error){for(const r of e.iterateCallbacks("handlerDidError"))if(a=await r({error:i,event:n,request:t}),a)break}if(!a)throw i}for(const i of e.iterateCallbacks("handlerWillRespond"))a=await i({event:n,request:t,response:a});return a}async _awaitComplete(e,t,n,a){let i,r;try{i=await e}catch{}try{await t.runCallbacks("handlerDidRespond",{event:a,request:n,response:i}),await t.doneWaiting()}catch(c){c instanceof Error&&(r=c)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:n,response:i,error:r}),t.destroy(),r)throw r}}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 i=a.integrity,r=e.integrity,c=!r||r===i;n=await t.fetch(new Request(e,{integrity:e.mode!=="no-cors"?r||i:void 0})),i&&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:i}=X(n),r=typeof n!="string"&&n.revision?"reload":"default";if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==a)throw new h("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(i),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:i});this._cacheKeysToIntegrities.set(a,n.integrity)}if(this._urlsToCacheKeys.set(i,a),this._urlsToCacheModes.set(i,r),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 v(e,async()=>{const t=new Y;this.strategy.plugins.push(t);for(const[i,r]of this._urlsToCacheKeys){const c=this._cacheKeysToIntegrities.get(r),o=this._urlsToCacheModes.get(i),l=new Request(i,{integrity:c,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:r},request:l,event:e}))}const{updatedURLs:n,notUpdatedURLs:a}=t;return{updatedURLs:n,notUpdatedURLs:a}})}activate(e){return v(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),n=await t.keys(),a=new Set(this._urlsToCacheKeys.values()),i=[];for(const r of n)a.has(r.url)||(await t.delete(r),i.push(r.url));return{deletedURLs:i}})}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 D;const j=()=>(D||(D=new oe),D);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:i})=>{const r=e.exec(i.href);if(r&&!(i.origin!==location.origin&&r.index!==0))return r.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 i=new Request(...a);return this.handleRequest({request:i,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:i,route:r}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:n});let c=r&&r.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:i})}catch(u){l=Promise.reject(u)}const g=r&&r.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:i})}catch(N){N instanceof Error&&(u=N)}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 i=this._routes.get(n.method)||[];for(const r of i){let c;const o=r.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:r,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 y;const ue=()=>(y||(y=new he,y.addFetchListener(),y.addCacheListener()),y);function q(s,e,t){let n;if(typeof s=="string"){const i=new URL(s,location.href),r=({url:c})=>c.href===i.href;n=new R(r,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 i=new URL(s,location.href);i.hash="",yield i.href;const r=de(i,e);if(yield r.href,t&&r.pathname.endsWith("/")){const c=new URL(r.href);c.pathname+=t,yield c.href}if(n){const c=new URL(r.href);c.pathname+=".html",yield c.href}if(a){const c=a({url:i});for(const o of c)yield o.href}}class pe extends R{constructor(e,t){const n=({request:a})=>{const i=e.getURLsToCacheKeys();for(const r of fe(a.url,t)){const c=i.get(r);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-",ye=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 we(){self.addEventListener("activate",(s=>{const e=b.getPrecacheName();s.waitUntil(ye(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(i){i instanceof Error&&(a=i)}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,L=new WeakMap,P=new WeakMap;function De(s){const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("success",i),s.removeEventListener("error",r)},i=()=>{t(f(s.result)),a()},r=()=>{n(s.error),a()};s.addEventListener("success",i),s.addEventListener("error",r)});return e.then(t=>{t instanceof IDBCursor&&V.set(t,s)}).catch(()=>{}),P.set(e,s),e}function Le(s){if(k.has(s))return;const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("complete",i),s.removeEventListener("error",r),s.removeEventListener("abort",r)},i=()=>{t(),a()},r=()=>{n(s.error||new DOMException("AbortError","AbortError")),a()};s.addEventListener("complete",i),s.addEventListener("error",r),s.addEventListener("abort",r)});k.set(s,e)}let I={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){I=s(I)}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&&Le(s),Ce(s,xe())?new Proxy(s,I):s)}function f(s){if(s instanceof IDBRequest)return De(s);if(L.has(s))return L.get(s);const e=ke(s);return e!==s&&(L.set(s,e),P.set(e,s)),e}const U=s=>P.get(s);function Ie(s,e,{blocked:t,upgrade:n,blocking:a,terminated:i}={}){const r=indexedDB.open(s,e),c=f(r);return n&&r.addEventListener("upgradeneeded",o=>{n(f(r.result),o.oldVersion,o.newVersion,f(r.transaction),o)}),t&&r.addEventListener("blocked",o=>t(o.oldVersion,o.newVersion,o)),c.then(o=>{i&&o.addEventListener("close",()=>i()),a&&o.addEventListener("versionchange",l=>a(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}function Pe(s,{blocked:e}={}){const t=indexedDB.deleteDatabase(s);return e&&t.addEventListener("blocked",n=>e(n.oldVersion,n)),f(t).then(()=>{})}const Ne=["get","getKey","getAll","getAllKeys","count"],ve=["put","add","delete","clear"],T=new Map;function O(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=ve.includes(t);if(!(t in(n?IDBIndex:IDBObjectStore).prototype)||!(a||Ne.includes(t)))return;const i=async function(r,...c){const o=this.transaction(r,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,i),i}Ue(s=>({...s,get:(e,t,n)=>O(e,t)||s.get(e,t,n),has:(e,t)=>!!O(e,t)||s.has(e,t)}));try{self["workbox:expiration:7.3.0"]&&_()}catch{}const Me="workbox-expiration",w="cache-entries",S=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(w,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&Pe(this._cacheName)}async setTimestamp(e,t){e=S(e);const n={url:e,timestamp:t,cacheName:this._cacheName,id:this._getId(e)},i=(await this.getDb()).transaction(w,"readwrite",{durability:"relaxed"});await i.store.put(n),await i.done}async getTimestamp(e){const n=await(await this.getDb()).get(w,this._getId(e));return n==null?void 0:n.timestamp}async expireEntries(e,t){const n=await this.getDb();let a=await n.transaction(w).store.index("timestamp").openCursor(null,"prev");const i=[];let r=0;for(;a;){const o=a.value;o.cacheName===this._cacheName&&(e&&o.timestamp<e||t&&r>=t?i.push(a.value):r++),a=await a.continue()}const c=[];for(const o of i)await n.delete(w,o.id),c.push(o.url);return c}_getId(e){return this._cacheName+"|"+S(e)}async getDb(){return this._db||(this._db=await Ie(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 Oe(s){B.add(s)}class Se{constructor(e={}){this.cachedResponseWillBeUsed=async({event:t,request:n,cacheName:a,cachedResponse:i})=>{if(!i)return null;const r=this._isResponseDateFresh(i),c=this._getCacheExpiration(a);H(c.expireEntries());const o=c.updateTimestamp(n.url);if(t)try{t.waitUntil(o)}catch{}return r?i: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&&Oe(()=>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-5P7HB3ND-CSZSmQ9N.js"},{"revision":null,"url":"assets/wardleyDiagram-NUSXRM2D-BhbO6L2Q.js"},{"revision":null,"url":"assets/wardley-RL74JXVD-C1HnYfXI.js"},{"revision":null,"url":"assets/vennDiagram-DHZGUBPP-DKLiidvh.js"},{"revision":null,"url":"assets/timeline-definition-GMOUNBTQ-B0JwGAT7.js"},{"revision":null,"url":"assets/stateDiagram-v2-QKLJ7IA2-2xQCbec6.js"},{"revision":null,"url":"assets/stateDiagram-FHFEXIEX-IGjwW9Pw.js"},{"revision":null,"url":"assets/sequenceDiagram-FGHM5R23-DY9IFA-F.js"},{"revision":null,"url":"assets/sankeyDiagram-XADWPNL6-b442XRH8.js"},{"revision":null,"url":"assets/requirementDiagram-MS252O5E-B8JKwgDX.js"},{"revision":null,"url":"assets/quadrantDiagram-34T5L4WZ-B1ZrruGm.js"},{"revision":null,"url":"assets/pieDiagram-DEJITSTG-Ck2h10zd.js"},{"revision":null,"url":"assets/ordinal-Cboi1Yqb.js"},{"revision":null,"url":"assets/mindmap-definition-QFDTVHPH-Swj4jh3Z.js"},{"revision":null,"url":"assets/linear-Bw9XpXJW.js"},{"revision":null,"url":"assets/layout-CpGcFwHw.js"},{"revision":null,"url":"assets/katex-B1X10hvy.js"},{"revision":null,"url":"assets/kanban-definition-6JOO6SKY-Cx3Plwl2.js"},{"revision":null,"url":"assets/journeyDiagram-VCZTEJTY-CUv-hh0I.js"},{"revision":null,"url":"assets/ishikawaDiagram-UXIWVN3A-D_2gGEMI.js"},{"revision":null,"url":"assets/init-Gi6I4Gst.js"},{"revision":null,"url":"assets/infoDiagram-42DDH7IO-sXN9f7L8.js"},{"revision":null,"url":"assets/index-Do67tk4s.js"},{"revision":null,"url":"assets/index-CK9vCujh.css"},{"revision":null,"url":"assets/graph-C-meUi4Z.js"},{"revision":null,"url":"assets/gitGraphDiagram-UUTBAWPF-CpEmQcDN.js"},{"revision":null,"url":"assets/ganttDiagram-T4ZO3ILL-DJrwFHlO.js"},{"revision":null,"url":"assets/flowDiagram-DWJPFMVM-DpRY38iu.js"},{"revision":null,"url":"assets/erDiagram-SMLLAGMA-BCpXZJei.js"},{"revision":null,"url":"assets/diagram-TYMM5635-CAMd8OZP.js"},{"revision":null,"url":"assets/diagram-MMDJMWI5-CWwOGFbE.js"},{"revision":null,"url":"assets/diagram-G4DWMVQ6-CIKqapDb.js"},{"revision":null,"url":"assets/diagram-5BDNPKRD-DfSZsVAD.js"},{"revision":null,"url":"assets/defaultLocale-DX6XiGOO.js"},{"revision":null,"url":"assets/dagre-KV5264BT-CDXKs5yo.js"},{"revision":null,"url":"assets/cytoscape.esm-BQaXIfA_.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-BW1nNLLk.js"},{"revision":null,"url":"assets/clone-BFbWnZ1G.js"},{"revision":null,"url":"assets/classDiagram-v2-HSJHXN6E-BaJ5lA7J.js"},{"revision":null,"url":"assets/classDiagram-6PBFFD2Q-BaJ5lA7J.js"},{"revision":null,"url":"assets/chunk-YZCP3GAM-D4sWv3YN.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-CBMUqRwi.js"},{"revision":null,"url":"assets/chunk-OYMX7WX6-Dl4p17KI.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-BkeSsxqP.js"},{"revision":null,"url":"assets/chunk-EDXVE4YY-CtqMCguR.js"},{"revision":null,"url":"assets/chunk-55IACEB6-DLtS129C.js"},{"revision":null,"url":"assets/chunk-4TB4RGXK-D5KX6fMH.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-BksLrXVs.js"},{"revision":null,"url":"assets/channel-RrT8pJmM.js"},{"revision":null,"url":"assets/c4Diagram-AHTNJAMY-BATf25U2.js"},{"revision":null,"url":"assets/blockDiagram-DXYQGD6D-DrElm25z.js"},{"revision":null,"url":"assets/architectureDiagram-Q4EWVU46-ByQ5j_Jw.js"},{"revision":null,"url":"assets/arc-BxuxWgHK.js"},{"revision":null,"url":"assets/_baseUniq-DveOFRWk.js"},{"revision":null,"url":"assets/_basePickBy-Q45O29Rp.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"}]);we();q(({url:s})=>s.hostname==="cdn.jsdelivr.net"&&s.pathname.endsWith(".ttf"),new be({cacheName:"termbeam-fonts",plugins:[new Se({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(i=>i.url.includes(self.location.origin)&&i.focused))return;const a={body:e.body||"A command has completed",icon:"/icons/icon-192.png",badge:"/icons/icon-192.png",tag:e.tag||"termbeam-cmd",renotify:!0,data:{url:e.url||"/",type:e.type||"command-complete",sessionId:e.sessionId},vibrate:[200,100,200]};try{await self.navigator.setAppBadge(1)}catch{}return self.registration.showNotification(e.title||"Command finished",a)}))});self.addEventListener("notificationclick",s=>{s.notification.close();try{self.navigator.clearAppBadge()}catch{}const e=s.notification.data||{},t=e.url||"/",n=e.type||"";s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(a=>{for(const i of a)if(i.url.includes(self.location.origin)&&"focus"in i)return n&&i.postMessage({type:"NOTIFICATION_CLICKED",notificationType:n,sessionId:e.sessionId}),i.focus();return self.clients.openWindow(t)}))});
|
|
2
|
+
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(c)}}}install(e){return v(e,async()=>{const t=new Y;this.strategy.plugins.push(t);for(const[i,r]of this._urlsToCacheKeys){const c=this._cacheKeysToIntegrities.get(r),o=this._urlsToCacheModes.get(i),l=new Request(i,{integrity:c,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:r},request:l,event:e}))}const{updatedURLs:n,notUpdatedURLs:a}=t;return{updatedURLs:n,notUpdatedURLs:a}})}activate(e){return v(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),n=await t.keys(),a=new Set(this._urlsToCacheKeys.values()),i=[];for(const r of n)a.has(r.url)||(await t.delete(r),i.push(r.url));return{deletedURLs:i}})}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 D;const j=()=>(D||(D=new oe),D);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:i})=>{const r=e.exec(i.href);if(r&&!(i.origin!==location.origin&&r.index!==0))return r.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 i=new Request(...a);return this.handleRequest({request:i,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:i,route:r}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:n});let c=r&&r.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:i})}catch(u){l=Promise.reject(u)}const g=r&&r.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:i})}catch(N){N instanceof Error&&(u=N)}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 i=this._routes.get(n.method)||[];for(const r of i){let c;const o=r.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:r,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 y;const ue=()=>(y||(y=new he,y.addFetchListener(),y.addCacheListener()),y);function q(s,e,t){let n;if(typeof s=="string"){const i=new URL(s,location.href),r=({url:c})=>c.href===i.href;n=new R(r,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 i=new URL(s,location.href);i.hash="",yield i.href;const r=de(i,e);if(yield r.href,t&&r.pathname.endsWith("/")){const c=new URL(r.href);c.pathname+=t,yield c.href}if(n){const c=new URL(r.href);c.pathname+=".html",yield c.href}if(a){const c=a({url:i});for(const o of c)yield o.href}}class pe extends R{constructor(e,t){const n=({request:a})=>{const i=e.getURLsToCacheKeys();for(const r of fe(a.url,t)){const c=i.get(r);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-",ye=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 we(){self.addEventListener("activate",(s=>{const e=b.getPrecacheName();s.waitUntil(ye(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(i){i instanceof Error&&(a=i)}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,L=new WeakMap,P=new WeakMap;function De(s){const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("success",i),s.removeEventListener("error",r)},i=()=>{t(f(s.result)),a()},r=()=>{n(s.error),a()};s.addEventListener("success",i),s.addEventListener("error",r)});return e.then(t=>{t instanceof IDBCursor&&V.set(t,s)}).catch(()=>{}),P.set(e,s),e}function Le(s){if(k.has(s))return;const e=new Promise((t,n)=>{const a=()=>{s.removeEventListener("complete",i),s.removeEventListener("error",r),s.removeEventListener("abort",r)},i=()=>{t(),a()},r=()=>{n(s.error||new DOMException("AbortError","AbortError")),a()};s.addEventListener("complete",i),s.addEventListener("error",r),s.addEventListener("abort",r)});k.set(s,e)}let I={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){I=s(I)}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&&Le(s),Ce(s,xe())?new Proxy(s,I):s)}function f(s){if(s instanceof IDBRequest)return De(s);if(L.has(s))return L.get(s);const e=ke(s);return e!==s&&(L.set(s,e),P.set(e,s)),e}const U=s=>P.get(s);function Ie(s,e,{blocked:t,upgrade:n,blocking:a,terminated:i}={}){const r=indexedDB.open(s,e),c=f(r);return n&&r.addEventListener("upgradeneeded",o=>{n(f(r.result),o.oldVersion,o.newVersion,f(r.transaction),o)}),t&&r.addEventListener("blocked",o=>t(o.oldVersion,o.newVersion,o)),c.then(o=>{i&&o.addEventListener("close",()=>i()),a&&o.addEventListener("versionchange",l=>a(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}function Pe(s,{blocked:e}={}){const t=indexedDB.deleteDatabase(s);return e&&t.addEventListener("blocked",n=>e(n.oldVersion,n)),f(t).then(()=>{})}const Ne=["get","getKey","getAll","getAllKeys","count"],ve=["put","add","delete","clear"],T=new Map;function O(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=ve.includes(t);if(!(t in(n?IDBIndex:IDBObjectStore).prototype)||!(a||Ne.includes(t)))return;const i=async function(r,...c){const o=this.transaction(r,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,i),i}Ue(s=>({...s,get:(e,t,n)=>O(e,t)||s.get(e,t,n),has:(e,t)=>!!O(e,t)||s.has(e,t)}));try{self["workbox:expiration:7.3.0"]&&_()}catch{}const Me="workbox-expiration",w="cache-entries",S=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(w,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&Pe(this._cacheName)}async setTimestamp(e,t){e=S(e);const n={url:e,timestamp:t,cacheName:this._cacheName,id:this._getId(e)},i=(await this.getDb()).transaction(w,"readwrite",{durability:"relaxed"});await i.store.put(n),await i.done}async getTimestamp(e){const n=await(await this.getDb()).get(w,this._getId(e));return n==null?void 0:n.timestamp}async expireEntries(e,t){const n=await this.getDb();let a=await n.transaction(w).store.index("timestamp").openCursor(null,"prev");const i=[];let r=0;for(;a;){const o=a.value;o.cacheName===this._cacheName&&(e&&o.timestamp<e||t&&r>=t?i.push(a.value):r++),a=await a.continue()}const c=[];for(const o of i)await n.delete(w,o.id),c.push(o.url);return c}_getId(e){return this._cacheName+"|"+S(e)}async getDb(){return this._db||(this._db=await Ie(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 Oe(s){B.add(s)}class Se{constructor(e={}){this.cachedResponseWillBeUsed=async({event:t,request:n,cacheName:a,cachedResponse:i})=>{if(!i)return null;const r=this._isResponseDateFresh(i),c=this._getCacheExpiration(a);H(c.expireEntries());const o=c.updateTimestamp(n.url);if(t)try{t.waitUntil(o)}catch{}return r?i: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&&Oe(()=>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-5P7HB3ND-CqzYOgsH.js"},{"revision":null,"url":"assets/wardleyDiagram-NUSXRM2D-Dt0kKYbS.js"},{"revision":null,"url":"assets/wardley-RL74JXVD-BsEwEn5-.js"},{"revision":null,"url":"assets/vennDiagram-DHZGUBPP-C1wYmABu.js"},{"revision":null,"url":"assets/timeline-definition-GMOUNBTQ-BOPg9ppv.js"},{"revision":null,"url":"assets/stateDiagram-v2-QKLJ7IA2-xe4-E4yV.js"},{"revision":null,"url":"assets/stateDiagram-FHFEXIEX-cpBtM_eo.js"},{"revision":null,"url":"assets/sequenceDiagram-FGHM5R23-DmTXUCCq.js"},{"revision":null,"url":"assets/sankeyDiagram-XADWPNL6-DjRA6Gx_.js"},{"revision":null,"url":"assets/requirementDiagram-MS252O5E-DO7a12OA.js"},{"revision":null,"url":"assets/quadrantDiagram-34T5L4WZ-tAb9lK_2.js"},{"revision":null,"url":"assets/pieDiagram-DEJITSTG-C6ng_7H-.js"},{"revision":null,"url":"assets/ordinal-Cboi1Yqb.js"},{"revision":null,"url":"assets/mindmap-definition-QFDTVHPH-CwCTVEhc.js"},{"revision":null,"url":"assets/linear-wWmlfvSH.js"},{"revision":null,"url":"assets/layout-BQRZ4_Il.js"},{"revision":null,"url":"assets/katex-B1X10hvy.js"},{"revision":null,"url":"assets/kanban-definition-6JOO6SKY-C_Sj08cN.js"},{"revision":null,"url":"assets/journeyDiagram-VCZTEJTY-CKm_djiT.js"},{"revision":null,"url":"assets/ishikawaDiagram-UXIWVN3A-Buhn0bpg.js"},{"revision":null,"url":"assets/init-Gi6I4Gst.js"},{"revision":null,"url":"assets/infoDiagram-42DDH7IO-JWuRQxiM.js"},{"revision":null,"url":"assets/index-CTMMjkRB.css"},{"revision":null,"url":"assets/index-Bap8dJMF.js"},{"revision":null,"url":"assets/graph-DGYmOqb_.js"},{"revision":null,"url":"assets/gitGraphDiagram-UUTBAWPF-CmtVcokM.js"},{"revision":null,"url":"assets/ganttDiagram-T4ZO3ILL-C-JweidK.js"},{"revision":null,"url":"assets/flowDiagram-DWJPFMVM-BebPntFK.js"},{"revision":null,"url":"assets/erDiagram-SMLLAGMA-COlP9qIO.js"},{"revision":null,"url":"assets/diagram-TYMM5635-CilekWFu.js"},{"revision":null,"url":"assets/diagram-MMDJMWI5-Da91D4Jv.js"},{"revision":null,"url":"assets/diagram-G4DWMVQ6-BjcXJ3-Q.js"},{"revision":null,"url":"assets/diagram-5BDNPKRD-KXbdPm8R.js"},{"revision":null,"url":"assets/defaultLocale-DX6XiGOO.js"},{"revision":null,"url":"assets/dagre-KV5264BT-CqYqJqNz.js"},{"revision":null,"url":"assets/cytoscape.esm-BQaXIfA_.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-CdRKoWQm.js"},{"revision":null,"url":"assets/clone-D-YD4Hfu.js"},{"revision":null,"url":"assets/classDiagram-v2-HSJHXN6E-DHBlyv18.js"},{"revision":null,"url":"assets/classDiagram-6PBFFD2Q-DHBlyv18.js"},{"revision":null,"url":"assets/chunk-YZCP3GAM-BFo64bhH.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-BOkHXJLa.js"},{"revision":null,"url":"assets/chunk-OYMX7WX6-Zxqgp5PC.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-54Zrs_SG.js"},{"revision":null,"url":"assets/chunk-EDXVE4YY-BqButrNC.js"},{"revision":null,"url":"assets/chunk-55IACEB6-BhvwRyIq.js"},{"revision":null,"url":"assets/chunk-4TB4RGXK-C-BdMhbz.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-m4pupC7U.js"},{"revision":null,"url":"assets/channel-a7jr0in8.js"},{"revision":null,"url":"assets/c4Diagram-AHTNJAMY-BuvkD-Hg.js"},{"revision":null,"url":"assets/blockDiagram-DXYQGD6D-EtZXyKmn.js"},{"revision":null,"url":"assets/architectureDiagram-Q4EWVU46-MEcrgEqw.js"},{"revision":null,"url":"assets/arc-CHQhvBsi.js"},{"revision":null,"url":"assets/_baseUniq-8tmjrEbB.js"},{"revision":null,"url":"assets/_basePickBy-DyywFBt7.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"}]);we();q(({url:s})=>s.hostname==="cdn.jsdelivr.net"&&s.pathname.endsWith(".ttf"),new be({cacheName:"termbeam-fonts",plugins:[new Se({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(i=>i.url.includes(self.location.origin)&&i.focused))return;const a={body:e.body||"A command has completed",icon:"/icons/icon-192.png",badge:"/icons/icon-192.png",tag:e.tag||"termbeam-cmd",renotify:!0,data:{url:e.url||"/",type:e.type||"command-complete",sessionId:e.sessionId},vibrate:[200,100,200]};try{await self.navigator.setAppBadge(1)}catch{}return self.registration.showNotification(e.title||"Command finished",a)}))});self.addEventListener("notificationclick",s=>{s.notification.close();try{self.navigator.clearAppBadge()}catch{}const e=s.notification.data||{},t=e.url||"/",n=e.type||"";s.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(a=>{for(const i of a)if(i.url.includes(self.location.origin)&&"focus"in i)return n&&i.postMessage({type:"NOTIFICATION_CLICKED",notificationType:n,sessionId:e.sessionId}),i.focus();return self.clients.openWindow(t)}))});
|
package/src/server/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const { createPreviewProxy } = require('./preview');
|
|
|
24
24
|
const { writeConnectionConfig, removeConnectionConfig } = require('../cli/resume');
|
|
25
25
|
const { checkForUpdate, detectInstallMethod } = require('../utils/update-check');
|
|
26
26
|
const { PushManager } = require('./push');
|
|
27
|
+
const { readPreferences } = require('./preferences');
|
|
27
28
|
|
|
28
29
|
// --- Helpers ---
|
|
29
30
|
function getLocalIP() {
|
|
@@ -60,7 +61,8 @@ function createTermBeamServer(overrides = {}) {
|
|
|
60
61
|
const sessions = new SessionManager();
|
|
61
62
|
|
|
62
63
|
// Push notification manager
|
|
63
|
-
const configDir =
|
|
64
|
+
const configDir =
|
|
65
|
+
config.configDir || process.env.TERMBEAM_CONFIG_DIR || path.join(os.homedir(), '.termbeam');
|
|
64
66
|
const pushManager = new PushManager(configDir);
|
|
65
67
|
pushManager.init().catch((err) => {
|
|
66
68
|
log.warn(`Push notification init failed: ${err.message}`);
|
|
@@ -115,7 +117,7 @@ function createTermBeamServer(overrides = {}) {
|
|
|
115
117
|
|
|
116
118
|
const state = { shareBaseUrl: null, updateInfo: null, wss, tunnelStatus: null, getLoginInfo };
|
|
117
119
|
app.use('/preview', auth.middleware, createPreviewProxy());
|
|
118
|
-
setupRoutes(app, { auth, sessions, config, state, pushManager, copilotService });
|
|
120
|
+
setupRoutes(app, { auth, sessions, config, state, pushManager, copilotService, configDir });
|
|
119
121
|
setupWebSocket(wss, { auth, sessions, copilotService });
|
|
120
122
|
|
|
121
123
|
// --- Lifecycle ---
|
|
@@ -224,12 +226,104 @@ function createTermBeamServer(overrides = {}) {
|
|
|
224
226
|
/* non-critical — resume will fall back to defaults */
|
|
225
227
|
}
|
|
226
228
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
// Decide which sessions to spawn at startup. Server-side autoboot
|
|
230
|
+
// means: deleting sessions client-side stays sticky (refreshing the
|
|
231
|
+
// page won't re-spawn them — only a fresh server start does).
|
|
232
|
+
//
|
|
233
|
+
// Source-of-truth precedence:
|
|
234
|
+
// 1. A named workspace flagged default:true with sessions
|
|
235
|
+
// 2. The single named workspace (implicit default) with sessions
|
|
236
|
+
// 3. Legacy startupWorkspace.enabled with sessions
|
|
237
|
+
// 4. Otherwise: a single default session in config.cwd
|
|
238
|
+
let workspaceSessions = null;
|
|
239
|
+
try {
|
|
240
|
+
const { prefs } = readPreferences(configDir);
|
|
241
|
+
const named = prefs.workspaces || [];
|
|
242
|
+
const explicitDefault = named.find(
|
|
243
|
+
(w) => w.default && Array.isArray(w.sessions) && w.sessions.length > 0,
|
|
244
|
+
);
|
|
245
|
+
const onlyNamed =
|
|
246
|
+
named.length === 1 && Array.isArray(named[0].sessions) && named[0].sessions.length > 0
|
|
247
|
+
? named[0]
|
|
248
|
+
: null;
|
|
249
|
+
const legacy = prefs.startupWorkspace;
|
|
250
|
+
if (explicitDefault) {
|
|
251
|
+
workspaceSessions = explicitDefault.sessions;
|
|
252
|
+
} else if (onlyNamed) {
|
|
253
|
+
workspaceSessions = onlyNamed.sessions;
|
|
254
|
+
} else if (
|
|
255
|
+
legacy &&
|
|
256
|
+
legacy.enabled &&
|
|
257
|
+
Array.isArray(legacy.sessions) &&
|
|
258
|
+
legacy.sessions.length > 0
|
|
259
|
+
) {
|
|
260
|
+
workspaceSessions = legacy.sessions;
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
// best-effort: if prefs read fails, fall back to single default
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let defaultId;
|
|
267
|
+
const detectedShells = (() => {
|
|
268
|
+
try {
|
|
269
|
+
return require('../utils/shells').detectShells();
|
|
270
|
+
} catch {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
})();
|
|
274
|
+
const validateShell = (shell) => {
|
|
275
|
+
if (!shell) return undefined;
|
|
276
|
+
if (detectedShells.some((s) => s.path === shell || s.cmd === shell)) return shell;
|
|
277
|
+
return undefined;
|
|
278
|
+
};
|
|
279
|
+
const spawnDefault = () => {
|
|
280
|
+
// Wrapped in try/catch so a bad shell/cwd doesn't crash the whole
|
|
281
|
+
// server start — it'll come up with no sessions and the user can
|
|
282
|
+
// create one themselves once they connect.
|
|
283
|
+
try {
|
|
284
|
+
return sessions.create({
|
|
285
|
+
name: path.basename(config.cwd),
|
|
286
|
+
shell: config.shell,
|
|
287
|
+
args: config.shellArgs,
|
|
288
|
+
cwd: config.cwd,
|
|
289
|
+
});
|
|
290
|
+
} catch (err) {
|
|
291
|
+
log.warn(`Default session failed to spawn: ${err.message}`);
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
if (workspaceSessions) {
|
|
296
|
+
let spawned = 0;
|
|
297
|
+
for (const s of workspaceSessions) {
|
|
298
|
+
try {
|
|
299
|
+
const cwd = s.cwd && path.isAbsolute(s.cwd) ? s.cwd : config.cwd;
|
|
300
|
+
const id = sessions.create({
|
|
301
|
+
name: s.name || path.basename(cwd),
|
|
302
|
+
shell: validateShell(s.shell) || config.shell,
|
|
303
|
+
args: config.shellArgs,
|
|
304
|
+
cwd,
|
|
305
|
+
initialCommand: (s.initialCommand || '').trim() || null,
|
|
306
|
+
color: s.color || null,
|
|
307
|
+
});
|
|
308
|
+
if (!defaultId) defaultId = id;
|
|
309
|
+
spawned += 1;
|
|
310
|
+
} catch (err) {
|
|
311
|
+
log.warn(`Workspace session "${s.name}" failed to spawn: ${err.message}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
log.info(
|
|
315
|
+
`Spawned ${spawned}/${workspaceSessions.length} workspace session(s) at startup`,
|
|
316
|
+
);
|
|
317
|
+
// If every workspace session failed (e.g. all configured shells
|
|
318
|
+
// are gone after a host migration), fall back to the safe default
|
|
319
|
+
// so the user lands on something rather than an empty hub.
|
|
320
|
+
if (spawned === 0) {
|
|
321
|
+
log.warn('All workspace sessions failed; falling back to default session');
|
|
322
|
+
defaultId = spawnDefault();
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
defaultId = spawnDefault();
|
|
326
|
+
}
|
|
233
327
|
|
|
234
328
|
const lp = '\x1b[38;5;141m'; // light purple
|
|
235
329
|
const rs = '\x1b[0m'; // reset
|
|
@@ -314,7 +408,11 @@ function createTermBeamServer(overrides = {}) {
|
|
|
314
408
|
}
|
|
315
409
|
|
|
316
410
|
console.log(` Shell: ${config.shell}`);
|
|
317
|
-
|
|
411
|
+
if (defaultId) {
|
|
412
|
+
console.log(` Session: ${defaultId}`);
|
|
413
|
+
} else {
|
|
414
|
+
console.log(` Session: (workspace auto-launches on connect)`);
|
|
415
|
+
}
|
|
318
416
|
console.log(` Auth: ${config.password ? `${gn}🔒 password${rs}` : '🔓 none'}`);
|
|
319
417
|
if (isLanReachable) {
|
|
320
418
|
console.log(` Bind: ${config.host} (LAN accessible)`);
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const log = require('../utils/logger');
|
|
4
|
+
|
|
5
|
+
const PREFS_FILENAME = 'prefs.json';
|
|
6
|
+
|
|
7
|
+
// Maximum allowed JSON body size in bytes for a PUT. Body parsing happens
|
|
8
|
+
// globally in src/server/index.js with a default ~100 KB limit, which is well
|
|
9
|
+
// above what any reasonable prefs payload should be.
|
|
10
|
+
|
|
11
|
+
// Defaults reflect the previous-implicit defaults from individual stores.
|
|
12
|
+
// Keep this list narrow; only add fields here that we actually round-trip
|
|
13
|
+
// through the API. Unknown keys from the client are dropped.
|
|
14
|
+
const DEFAULTS = Object.freeze({
|
|
15
|
+
themeId: 'dark',
|
|
16
|
+
fontSize: 14,
|
|
17
|
+
notifications: false,
|
|
18
|
+
haptics: true,
|
|
19
|
+
defaultFolder: '',
|
|
20
|
+
defaultInitialCommand: '',
|
|
21
|
+
touchBarCollapsed: false,
|
|
22
|
+
touchBarKeys: null, // null = use built-in defaults
|
|
23
|
+
startupWorkspace: { enabled: false, sessions: [] },
|
|
24
|
+
workspaces: [],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const FONT_MIN = 2;
|
|
28
|
+
const FONT_MAX = 32;
|
|
29
|
+
const MAX_STARTUP_SESSIONS = 16;
|
|
30
|
+
const MAX_TOUCHBAR_KEYS = 32;
|
|
31
|
+
const MAX_WORKSPACES = 16;
|
|
32
|
+
const MAX_STRING_LEN = 4096;
|
|
33
|
+
const MAX_SEND_LEN = 64;
|
|
34
|
+
|
|
35
|
+
const VALID_KEY_ACTIONS = new Set(['mic', 'copy', 'paste', 'cancel', 'newline']);
|
|
36
|
+
// Accept BOTH the new vocabulary (plain/accent/danger/custom) and the legacy
|
|
37
|
+
// values from older clients. The client-side normalize() migrates legacy
|
|
38
|
+
// values to the new ones on read, so persisted prefs converge over time.
|
|
39
|
+
const VALID_KEY_LOOKS = new Set([
|
|
40
|
+
'plain',
|
|
41
|
+
'accent',
|
|
42
|
+
'danger',
|
|
43
|
+
'custom',
|
|
44
|
+
// Legacy — accepted but client will rewrite to the new vocab on next PUT
|
|
45
|
+
'default',
|
|
46
|
+
'special',
|
|
47
|
+
'modifier',
|
|
48
|
+
'icon',
|
|
49
|
+
'enter',
|
|
50
|
+
]);
|
|
51
|
+
const VALID_KEY_MODIFIERS = new Set(['ctrl', 'alt', 'shift', 'meta']);
|
|
52
|
+
const VALID_KEY_SIZES = new Set([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
53
|
+
|
|
54
|
+
function clampNumber(n, min, max, fallback) {
|
|
55
|
+
if (typeof n !== 'number' || !Number.isFinite(n)) return fallback;
|
|
56
|
+
return Math.max(min, Math.min(max, Math.round(n)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function asString(v, fallback, maxLen = MAX_STRING_LEN) {
|
|
60
|
+
if (typeof v !== 'string') return fallback;
|
|
61
|
+
return v.length > maxLen ? v.slice(0, maxLen) : v;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function asBool(v, fallback) {
|
|
65
|
+
return typeof v === 'boolean' ? v : fallback;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sanitizeTouchBarKeys(input) {
|
|
69
|
+
if (input === null) return null;
|
|
70
|
+
if (!Array.isArray(input)) return null;
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const entry of input.slice(0, MAX_TOUCHBAR_KEYS)) {
|
|
73
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
74
|
+
const id = asString(entry.id, '', 64);
|
|
75
|
+
const label = asString(entry.label, '', 16);
|
|
76
|
+
const send = asString(entry.send, '', MAX_SEND_LEN);
|
|
77
|
+
// Only `id` is required — empty label is allowed so the user can
|
|
78
|
+
// clear the field while editing without losing the key.
|
|
79
|
+
if (!id) continue;
|
|
80
|
+
const key = { id, label, send };
|
|
81
|
+
if (typeof entry.modifier === 'string') {
|
|
82
|
+
const mod = entry.modifier.toLowerCase();
|
|
83
|
+
if (VALID_KEY_MODIFIERS.has(mod)) key.modifier = mod;
|
|
84
|
+
}
|
|
85
|
+
if (typeof entry.action === 'string' && VALID_KEY_ACTIONS.has(entry.action)) {
|
|
86
|
+
key.action = entry.action;
|
|
87
|
+
}
|
|
88
|
+
if (VALID_KEY_SIZES.has(entry.size)) {
|
|
89
|
+
key.size = entry.size;
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
typeof entry.row === 'number' &&
|
|
93
|
+
Number.isInteger(entry.row) &&
|
|
94
|
+
entry.row >= 1 &&
|
|
95
|
+
entry.row <= 3
|
|
96
|
+
) {
|
|
97
|
+
key.row = entry.row;
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
typeof entry.col === 'number' &&
|
|
101
|
+
Number.isInteger(entry.col) &&
|
|
102
|
+
entry.col >= 1 &&
|
|
103
|
+
entry.col <= 8
|
|
104
|
+
) {
|
|
105
|
+
key.col = entry.col;
|
|
106
|
+
}
|
|
107
|
+
if (typeof entry.bg === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(entry.bg)) {
|
|
108
|
+
key.bg = entry.bg;
|
|
109
|
+
}
|
|
110
|
+
if (typeof entry.color === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(entry.color)) {
|
|
111
|
+
key.color = entry.color;
|
|
112
|
+
}
|
|
113
|
+
if (typeof entry.style === 'string' && VALID_KEY_LOOKS.has(entry.style)) {
|
|
114
|
+
key.style = entry.style;
|
|
115
|
+
}
|
|
116
|
+
out.push(key);
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function sanitizeStartupSession(s) {
|
|
122
|
+
if (!s || typeof s !== 'object') return null;
|
|
123
|
+
const id = asString(s.id, '', 64);
|
|
124
|
+
const name = asString(s.name, '', 128);
|
|
125
|
+
const kindRaw = asString(s.kind, 'shell', 16);
|
|
126
|
+
const kind = kindRaw === 'agent' ? 'agent' : 'shell';
|
|
127
|
+
const cwd = asString(s.cwd, '', 1024);
|
|
128
|
+
const initialCommand = asString(s.initialCommand, '', 1024);
|
|
129
|
+
if (!id || !name) return null;
|
|
130
|
+
const entry = { id, name, kind, cwd, initialCommand };
|
|
131
|
+
if (kind === 'agent' && typeof s.agentId === 'string') {
|
|
132
|
+
entry.agentId = asString(s.agentId, '', 128);
|
|
133
|
+
}
|
|
134
|
+
if (typeof s.shell === 'string' && s.shell) {
|
|
135
|
+
entry.shell = asString(s.shell, '', 256);
|
|
136
|
+
}
|
|
137
|
+
if (typeof s.color === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(s.color)) {
|
|
138
|
+
entry.color = s.color;
|
|
139
|
+
}
|
|
140
|
+
return entry;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function sanitizeStartupWorkspace(input) {
|
|
144
|
+
if (!input || typeof input !== 'object') return { ...DEFAULTS.startupWorkspace };
|
|
145
|
+
const enabled = asBool(input.enabled, false);
|
|
146
|
+
const rawSessions = Array.isArray(input.sessions) ? input.sessions : [];
|
|
147
|
+
const sessions = [];
|
|
148
|
+
for (const s of rawSessions.slice(0, MAX_STARTUP_SESSIONS)) {
|
|
149
|
+
const entry = sanitizeStartupSession(s);
|
|
150
|
+
if (entry) sessions.push(entry);
|
|
151
|
+
}
|
|
152
|
+
return { enabled, sessions };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function sanitizeWorkspaces(input) {
|
|
156
|
+
if (!Array.isArray(input)) return [];
|
|
157
|
+
const out = [];
|
|
158
|
+
let defaultsSeen = 0;
|
|
159
|
+
for (const w of input.slice(0, MAX_WORKSPACES)) {
|
|
160
|
+
if (!w || typeof w !== 'object') continue;
|
|
161
|
+
const id = asString(w.id, '', 64);
|
|
162
|
+
const name = asString(w.name, '', 128);
|
|
163
|
+
if (!id || !name) continue;
|
|
164
|
+
const sessions = [];
|
|
165
|
+
const rawSessions = Array.isArray(w.sessions) ? w.sessions : [];
|
|
166
|
+
for (const s of rawSessions.slice(0, MAX_STARTUP_SESSIONS)) {
|
|
167
|
+
const entry = sanitizeStartupSession(s);
|
|
168
|
+
if (entry) sessions.push(entry);
|
|
169
|
+
}
|
|
170
|
+
const entry = { id, name, sessions };
|
|
171
|
+
if (asBool(w.default, false) && defaultsSeen === 0) {
|
|
172
|
+
entry.default = true;
|
|
173
|
+
defaultsSeen += 1;
|
|
174
|
+
}
|
|
175
|
+
out.push(entry);
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Coerce arbitrary client input into a known-good preferences object.
|
|
182
|
+
* Unknown keys are dropped; bad types fall back to defaults.
|
|
183
|
+
*/
|
|
184
|
+
function sanitize(prefs) {
|
|
185
|
+
const src = prefs && typeof prefs === 'object' ? prefs : {};
|
|
186
|
+
return {
|
|
187
|
+
themeId: asString(src.themeId, DEFAULTS.themeId, 64),
|
|
188
|
+
fontSize: clampNumber(src.fontSize, FONT_MIN, FONT_MAX, DEFAULTS.fontSize),
|
|
189
|
+
notifications: asBool(src.notifications, DEFAULTS.notifications),
|
|
190
|
+
haptics: asBool(src.haptics, DEFAULTS.haptics),
|
|
191
|
+
defaultFolder: asString(src.defaultFolder, DEFAULTS.defaultFolder, 1024),
|
|
192
|
+
defaultInitialCommand: asString(
|
|
193
|
+
src.defaultInitialCommand,
|
|
194
|
+
DEFAULTS.defaultInitialCommand,
|
|
195
|
+
1024,
|
|
196
|
+
),
|
|
197
|
+
touchBarCollapsed: asBool(src.touchBarCollapsed, DEFAULTS.touchBarCollapsed),
|
|
198
|
+
touchBarKeys: sanitizeTouchBarKeys(src.touchBarKeys),
|
|
199
|
+
startupWorkspace: sanitizeStartupWorkspace(src.startupWorkspace),
|
|
200
|
+
workspaces: sanitizeWorkspaces(src.workspaces),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getDefaults() {
|
|
205
|
+
return sanitize({});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function prefsPath(configDir) {
|
|
209
|
+
return path.join(configDir, PREFS_FILENAME);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function readPreferences(configDir) {
|
|
213
|
+
const file = prefsPath(configDir);
|
|
214
|
+
try {
|
|
215
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
216
|
+
const parsed = JSON.parse(raw);
|
|
217
|
+
const prefs = sanitize(parsed && parsed.prefs);
|
|
218
|
+
const version = Number.isInteger(parsed && parsed.version) ? parsed.version : 0;
|
|
219
|
+
return { prefs, version };
|
|
220
|
+
} catch (err) {
|
|
221
|
+
if (err.code !== 'ENOENT') {
|
|
222
|
+
log.warn(`prefs: failed to read ${file}: ${err.message}`);
|
|
223
|
+
}
|
|
224
|
+
return { prefs: getDefaults(), version: 0 };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function writePreferences(configDir, prefs) {
|
|
229
|
+
const sanitized = sanitize(prefs);
|
|
230
|
+
const current = readPreferences(configDir);
|
|
231
|
+
const next = { prefs: sanitized, version: current.version + 1 };
|
|
232
|
+
const file = prefsPath(configDir);
|
|
233
|
+
try {
|
|
234
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
235
|
+
fs.writeFileSync(file, JSON.stringify(next, null, 2), { mode: 0o600 });
|
|
236
|
+
// Ensure mode is correct even when the file already existed with different
|
|
237
|
+
// permissions (writeFileSync mode is only honored on create on some FS).
|
|
238
|
+
try {
|
|
239
|
+
fs.chmodSync(file, 0o600);
|
|
240
|
+
} catch {
|
|
241
|
+
// best-effort
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
log.error(`prefs: failed to write ${file}: ${err.message}`);
|
|
245
|
+
throw err;
|
|
246
|
+
}
|
|
247
|
+
return next;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function setupPreferenceRoutes(app, { auth, configDir, apiRateLimit }) {
|
|
251
|
+
app.get('/api/preferences', apiRateLimit, auth.middleware, (_req, res) => {
|
|
252
|
+
const state = readPreferences(configDir);
|
|
253
|
+
res.json(state);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
app.put('/api/preferences', apiRateLimit, auth.middleware, (req, res) => {
|
|
257
|
+
const body = req.body;
|
|
258
|
+
if (!body || typeof body !== 'object' || !body.prefs || typeof body.prefs !== 'object') {
|
|
259
|
+
return res.status(400).json({ error: 'Body must be { prefs: object }' });
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const next = writePreferences(configDir, body.prefs);
|
|
263
|
+
res.json(next);
|
|
264
|
+
} catch {
|
|
265
|
+
res.status(500).json({ error: 'Failed to persist preferences' });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// `express.json` body parser is applied globally in index.js; this module
|
|
271
|
+
// just defines route handlers.
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
setupPreferenceRoutes,
|
|
275
|
+
readPreferences,
|
|
276
|
+
writePreferences,
|
|
277
|
+
sanitize,
|
|
278
|
+
getDefaults,
|
|
279
|
+
PREFS_FILENAME,
|
|
280
|
+
};
|
package/src/server/routes.js
CHANGED
|
@@ -9,6 +9,7 @@ const { getAgentSessions, getResumeCommand } = require('../utils/agent-sessions'
|
|
|
9
9
|
const log = require('../utils/logger');
|
|
10
10
|
const { getGitInfo } = require('../utils/git');
|
|
11
11
|
const rateLimit = require('express-rate-limit');
|
|
12
|
+
const { setupPreferenceRoutes } = require('./preferences');
|
|
12
13
|
|
|
13
14
|
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
|
|
14
15
|
|
|
@@ -97,7 +98,10 @@ function validateMagicBytes(buffer, contentType) {
|
|
|
97
98
|
return true;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
function setupRoutes(
|
|
101
|
+
function setupRoutes(
|
|
102
|
+
app,
|
|
103
|
+
{ auth, sessions, config, state, pushManager, copilotService, configDir },
|
|
104
|
+
) {
|
|
101
105
|
const noopLimit = (_req, _res, next) => next();
|
|
102
106
|
const pageRateLimit = config.disableRateLimit
|
|
103
107
|
? noopLimit
|
|
@@ -129,6 +133,11 @@ function setupRoutes(app, { auth, sessions, config, state, pushManager, copilotS
|
|
|
129
133
|
});
|
|
130
134
|
app.use(express.static(PUBLIC_DIR, { index: false }));
|
|
131
135
|
|
|
136
|
+
// Preferences API (mounted after rate limiters so it picks up apiRateLimit).
|
|
137
|
+
if (configDir) {
|
|
138
|
+
setupPreferenceRoutes(app, { auth, configDir, apiRateLimit });
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
// Login page
|
|
133
142
|
app.get('/login', (_req, res) => {
|
|
134
143
|
if (!auth.password) return res.redirect('/');
|