@hienlh/ppm 0.6.0 → 0.6.2
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 +21 -0
- package/bun.lock +3 -0
- package/dist/web/assets/{chat-tab-C24nbKz1.js → chat-tab-BdiG3Gnr.js} +3 -3
- package/dist/web/assets/code-editor-soN1frMc.js +1 -0
- package/dist/web/assets/{diff-viewer-BnvcXY3g.js → diff-viewer-DJEB1zOd.js} +1 -1
- package/dist/web/assets/dist-CJbcT4CK.js +16 -0
- package/dist/web/assets/{git-graph-iAf_zaqe.js → git-graph-CrU7vGxw.js} +1 -1
- package/dist/web/assets/{index-BwLVvoev.js → index-CmrE0Xoy.js} +5 -5
- package/dist/web/assets/index-g11aaU-x.css +2 -0
- package/dist/web/assets/{input-DV4tynJq.js → input-DMu1FA4M.js} +1 -1
- package/dist/web/assets/{markdown-renderer-CIfiE3o8.js → markdown-renderer-BPKEwysz.js} +1 -1
- package/dist/web/assets/postgres-viewer-lBV4F44Q.js +1 -0
- package/dist/web/assets/{settings-store-BGF8--S9.js → settings-store-BRFvbsHd.js} +1 -1
- package/dist/web/assets/settings-tab-Div5NL2d.js +1 -0
- package/dist/web/assets/sqlite-viewer-BbgWU-v3.js +1 -0
- package/dist/web/assets/{tab-store-L0a7ao4c.js → tab-store-Bf9z6T8D.js} +1 -1
- package/dist/web/assets/{terminal-tab-4-DINw_B.js → terminal-tab-Dt9bjwC8.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-RFoGvnp0.js → use-monaco-theme-yxUtuNlu.js} +1 -1
- package/dist/web/index.html +8 -8
- package/dist/web/sw.js +1 -1
- package/package.json +2 -1
- package/src/server/index.ts +2 -0
- package/src/server/routes/postgres.ts +92 -0
- package/src/services/postgres.service.ts +170 -0
- package/src/services/sqlite.service.ts +5 -4
- package/src/web/components/editor/code-editor.tsx +8 -0
- package/src/web/components/layout/command-palette.tsx +2 -0
- package/src/web/components/layout/editor-panel.tsx +1 -0
- package/src/web/components/layout/mobile-nav.tsx +1 -1
- package/src/web/components/layout/tab-bar.tsx +1 -0
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/postgres/postgres-viewer.tsx +264 -0
- package/src/web/components/postgres/use-postgres.ts +128 -0
- package/src/web/components/sqlite/sqlite-data-grid.tsx +1 -0
- package/src/web/components/sqlite/use-sqlite.ts +1 -1
- package/src/web/stores/tab-store.ts +1 -0
- package/dist/web/assets/code-editor-DjIL6ta3.js +0 -1
- package/dist/web/assets/index-CP_2zE5O.css +0 -2
- package/dist/web/assets/settings-tab-B_QwULcp.js +0 -1
- package/dist/web/assets/sqlite-viewer-DpGb3i2g.js +0 -16
- /package/dist/web/assets/{api-client-ANLU-Irq.js → api-client-DPWUomlf.js} +0 -0
- /package/dist/web/assets/{react-WvgCEYPV.js → react-Bo97Lrzq.js} +0 -0
- /package/dist/web/assets/{rotate-ccw-BesidNnx.js → rotate-ccw-Dx0ShAKj.js} +0 -0
- /package/dist/web/assets/{utils-C2KxHr1H.js → utils-CAPYyGV3.js} +0 -0
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{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){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let 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;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({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),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let 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){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{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`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(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 t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"9ec634d85a6c0943ed84d647fe438850","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/utils-C2KxHr1H.js"},{"revision":null,"url":"assets/use-monaco-theme-RFoGvnp0.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/terminal-tab-4-DINw_B.js"},{"revision":null,"url":"assets/tab-store-L0a7ao4c.js"},{"revision":null,"url":"assets/sqlite-viewer-DpGb3i2g.js"},{"revision":null,"url":"assets/settings-tab-B_QwULcp.js"},{"revision":null,"url":"assets/settings-store-BGF8--S9.js"},{"revision":null,"url":"assets/rotate-ccw-BesidNnx.js"},{"revision":null,"url":"assets/react-WvgCEYPV.js"},{"revision":null,"url":"assets/markdown-renderer-CIfiE3o8.js"},{"revision":null,"url":"assets/jsx-runtime-B4BJKQ1u.js"},{"revision":null,"url":"assets/input-DV4tynJq.js"},{"revision":null,"url":"assets/index-CP_2zE5O.css"},{"revision":null,"url":"assets/index-BwLVvoev.js"},{"revision":null,"url":"assets/git-graph-iAf_zaqe.js"},{"revision":null,"url":"assets/diff-viewer-BnvcXY3g.js"},{"revision":null,"url":"assets/code-editor-DjIL6ta3.js"},{"revision":null,"url":"assets/chat-tab-C24nbKz1.js"},{"revision":null,"url":"assets/api-client-ANLU-Irq.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
|
1
|
+
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{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){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let 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;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({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),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let 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){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{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`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(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 t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a94b0b0ec228c540fde874e78b907aa5","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/utils-CAPYyGV3.js"},{"revision":null,"url":"assets/use-monaco-theme-yxUtuNlu.js"},{"revision":null,"url":"assets/terminal-tab-Dt9bjwC8.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tab-store-Bf9z6T8D.js"},{"revision":null,"url":"assets/sqlite-viewer-BbgWU-v3.js"},{"revision":null,"url":"assets/settings-tab-Div5NL2d.js"},{"revision":null,"url":"assets/settings-store-BRFvbsHd.js"},{"revision":null,"url":"assets/rotate-ccw-Dx0ShAKj.js"},{"revision":null,"url":"assets/react-Bo97Lrzq.js"},{"revision":null,"url":"assets/postgres-viewer-lBV4F44Q.js"},{"revision":null,"url":"assets/markdown-renderer-BPKEwysz.js"},{"revision":null,"url":"assets/jsx-runtime-B4BJKQ1u.js"},{"revision":null,"url":"assets/input-DMu1FA4M.js"},{"revision":null,"url":"assets/index-g11aaU-x.css"},{"revision":null,"url":"assets/index-CmrE0Xoy.js"},{"revision":null,"url":"assets/git-graph-CrU7vGxw.js"},{"revision":null,"url":"assets/dist-CJbcT4CK.js"},{"revision":null,"url":"assets/diff-viewer-DJEB1zOd.js"},{"revision":null,"url":"assets/code-editor-soN1frMc.js"},{"revision":null,"url":"assets/chat-tab-BdiG3Gnr.js"},{"revision":null,"url":"assets/api-client-DPWUomlf.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hienlh/ppm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Personal Project Manager — mobile-first web IDE with AI assistance",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"lucide-react": "^0.577.0",
|
|
55
55
|
"marked": "^17.0.4",
|
|
56
56
|
"next-themes": "^0.4.6",
|
|
57
|
+
"postgres": "^3.4.8",
|
|
57
58
|
"qrcode-terminal": "^0.12.0",
|
|
58
59
|
"qrcode.react": "^4.2.0",
|
|
59
60
|
"radix-ui": "^1.4.3",
|
package/src/server/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { pushRoutes } from "./routes/push.ts";
|
|
|
9
9
|
import { tunnelRoutes } from "./routes/tunnel.ts";
|
|
10
10
|
import { staticRoutes } from "./routes/static.ts";
|
|
11
11
|
import { projectScopedRouter } from "./routes/project-scoped.ts";
|
|
12
|
+
import { postgresRoutes } from "./routes/postgres.ts";
|
|
12
13
|
import { terminalWebSocket } from "./ws/terminal.ts";
|
|
13
14
|
import { chatWebSocket } from "./ws/chat.ts";
|
|
14
15
|
import { ok, err } from "../types/api.ts";
|
|
@@ -183,6 +184,7 @@ app.route("/api/tunnel", tunnelRoutes);
|
|
|
183
184
|
app.route("/api/push", pushRoutes);
|
|
184
185
|
app.route("/api/projects", projectRoutes);
|
|
185
186
|
app.route("/api/project/:projectName", projectScopedRouter);
|
|
187
|
+
app.route("/api/postgres", postgresRoutes);
|
|
186
188
|
|
|
187
189
|
// Static files / SPA fallback (non-API routes)
|
|
188
190
|
app.route("/", staticRoutes);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { postgresService } from "../../services/postgres.service.ts";
|
|
3
|
+
import { ok, err } from "../../types/api.ts";
|
|
4
|
+
|
|
5
|
+
export const postgresRoutes = new Hono();
|
|
6
|
+
|
|
7
|
+
/** POST /postgres/test — body: { connectionString } */
|
|
8
|
+
postgresRoutes.post("/test", async (c) => {
|
|
9
|
+
try {
|
|
10
|
+
const body = await c.req.json<{ connectionString: string }>();
|
|
11
|
+
if (!body.connectionString) return c.json(err("Missing connectionString"), 400);
|
|
12
|
+
const result = await postgresService.testConnection(body.connectionString);
|
|
13
|
+
return c.json(ok(result));
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return c.json(err((e as Error).message), 500);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/** POST /postgres/tables — body: { connectionString } */
|
|
20
|
+
postgresRoutes.post("/tables", async (c) => {
|
|
21
|
+
try {
|
|
22
|
+
const body = await c.req.json<{ connectionString: string }>();
|
|
23
|
+
if (!body.connectionString) return c.json(err("Missing connectionString"), 400);
|
|
24
|
+
const tables = await postgresService.getTables(body.connectionString);
|
|
25
|
+
return c.json(ok(tables));
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return c.json(err((e as Error).message), 500);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/** POST /postgres/schema — body: { connectionString, table, schema? } */
|
|
32
|
+
postgresRoutes.post("/schema", async (c) => {
|
|
33
|
+
try {
|
|
34
|
+
const body = await c.req.json<{ connectionString: string; table: string; schema?: string }>();
|
|
35
|
+
if (!body.connectionString || !body.table) return c.json(err("Missing connectionString or table"), 400);
|
|
36
|
+
const schema = await postgresService.getTableSchema(body.connectionString, body.table, body.schema);
|
|
37
|
+
return c.json(ok(schema));
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return c.json(err((e as Error).message), 500);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** POST /postgres/data — body: { connectionString, table, schema?, page?, limit?, orderBy?, orderDir? } */
|
|
44
|
+
postgresRoutes.post("/data", async (c) => {
|
|
45
|
+
try {
|
|
46
|
+
const body = await c.req.json<{
|
|
47
|
+
connectionString: string; table: string; schema?: string;
|
|
48
|
+
page?: number; limit?: number; orderBy?: string; orderDir?: string;
|
|
49
|
+
}>();
|
|
50
|
+
if (!body.connectionString || !body.table) return c.json(err("Missing connectionString or table"), 400);
|
|
51
|
+
const limit = Math.min(body.limit ?? 100, 1000);
|
|
52
|
+
const data = await postgresService.getTableData(
|
|
53
|
+
body.connectionString, body.table, body.schema, body.page ?? 1, limit,
|
|
54
|
+
body.orderBy, (body.orderDir as "ASC" | "DESC") ?? "ASC",
|
|
55
|
+
);
|
|
56
|
+
return c.json(ok(data));
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return c.json(err((e as Error).message), 500);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** POST /postgres/query — body: { connectionString, sql } */
|
|
63
|
+
postgresRoutes.post("/query", async (c) => {
|
|
64
|
+
try {
|
|
65
|
+
const body = await c.req.json<{ connectionString: string; sql: string }>();
|
|
66
|
+
if (!body.connectionString || !body.sql) return c.json(err("Missing connectionString or sql"), 400);
|
|
67
|
+
const result = await postgresService.executeQuery(body.connectionString, body.sql);
|
|
68
|
+
return c.json(ok(result));
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return c.json(err((e as Error).message), 500);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/** POST /postgres/cell — body: { connectionString, table, schema?, pkColumn, pkValue, column, value } */
|
|
75
|
+
postgresRoutes.post("/cell", async (c) => {
|
|
76
|
+
try {
|
|
77
|
+
const body = await c.req.json<{
|
|
78
|
+
connectionString: string; table: string; schema?: string;
|
|
79
|
+
pkColumn: string; pkValue: unknown; column: string; value: unknown;
|
|
80
|
+
}>();
|
|
81
|
+
if (!body.connectionString || !body.table || !body.pkColumn || !body.column) {
|
|
82
|
+
return c.json(err("Missing required fields"), 400);
|
|
83
|
+
}
|
|
84
|
+
await postgresService.updateCell(
|
|
85
|
+
body.connectionString, body.table, body.schema,
|
|
86
|
+
body.pkColumn, body.pkValue, body.column, body.value,
|
|
87
|
+
);
|
|
88
|
+
return c.json(ok({ updated: true }));
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return c.json(err((e as Error).message), 500);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import postgres from "postgres";
|
|
2
|
+
|
|
3
|
+
export interface PgTableInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
schema: string;
|
|
6
|
+
rowCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PgColumnInfo {
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
nullable: boolean;
|
|
13
|
+
pk: boolean;
|
|
14
|
+
defaultValue: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PgQueryResult {
|
|
18
|
+
columns: string[];
|
|
19
|
+
rows: Record<string, unknown>[];
|
|
20
|
+
rowsAffected: number;
|
|
21
|
+
changeType: "select" | "modify";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Auto-close idle connections after 5 minutes */
|
|
25
|
+
const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
26
|
+
|
|
27
|
+
interface CachedConn {
|
|
28
|
+
sql: postgres.Sql;
|
|
29
|
+
timer: ReturnType<typeof setTimeout>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class PostgresService {
|
|
33
|
+
private cache = new Map<string, CachedConn>();
|
|
34
|
+
|
|
35
|
+
/** Get or create a cached connection */
|
|
36
|
+
private connect(connectionString: string): postgres.Sql {
|
|
37
|
+
const cached = this.cache.get(connectionString);
|
|
38
|
+
if (cached) {
|
|
39
|
+
clearTimeout(cached.timer);
|
|
40
|
+
cached.timer = setTimeout(() => this.disconnect(connectionString), IDLE_TIMEOUT_MS);
|
|
41
|
+
return cached.sql;
|
|
42
|
+
}
|
|
43
|
+
const sql = postgres(connectionString, { max: 3, idle_timeout: 60 });
|
|
44
|
+
const timer = setTimeout(() => this.disconnect(connectionString), IDLE_TIMEOUT_MS);
|
|
45
|
+
this.cache.set(connectionString, { sql, timer });
|
|
46
|
+
return sql;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Close and remove from cache */
|
|
50
|
+
private async disconnect(connectionString: string) {
|
|
51
|
+
const cached = this.cache.get(connectionString);
|
|
52
|
+
if (!cached) return;
|
|
53
|
+
clearTimeout(cached.timer);
|
|
54
|
+
try { await cached.sql.end(); } catch { /* already closed */ }
|
|
55
|
+
this.cache.delete(connectionString);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Test connection */
|
|
59
|
+
async testConnection(connectionString: string): Promise<{ ok: boolean; error?: string }> {
|
|
60
|
+
try {
|
|
61
|
+
const sql = this.connect(connectionString);
|
|
62
|
+
await sql`SELECT 1`;
|
|
63
|
+
return { ok: true };
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return { ok: false, error: (e as Error).message };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** List all user tables with row counts */
|
|
70
|
+
async getTables(connectionString: string): Promise<PgTableInfo[]> {
|
|
71
|
+
const sql = this.connect(connectionString);
|
|
72
|
+
const tables = await sql`
|
|
73
|
+
SELECT t.schemaname as schema, t.tablename as name,
|
|
74
|
+
COALESCE(s.n_live_tup, 0)::int as row_count
|
|
75
|
+
FROM pg_tables t
|
|
76
|
+
LEFT JOIN pg_stat_user_tables s ON t.schemaname = s.schemaname AND t.tablename = s.relname
|
|
77
|
+
WHERE t.schemaname NOT IN ('pg_catalog', 'information_schema')
|
|
78
|
+
ORDER BY t.schemaname, t.tablename
|
|
79
|
+
`;
|
|
80
|
+
return tables.map((t) => ({
|
|
81
|
+
name: t.name as string, schema: t.schema as string, rowCount: t.row_count as number,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Get column schema for a table */
|
|
86
|
+
async getTableSchema(connectionString: string, table: string, schema = "public"): Promise<PgColumnInfo[]> {
|
|
87
|
+
const sql = this.connect(connectionString);
|
|
88
|
+
const cols = await sql`
|
|
89
|
+
SELECT c.column_name as name, c.data_type as type,
|
|
90
|
+
c.is_nullable = 'YES' as nullable, c.column_default as default_value,
|
|
91
|
+
COALESCE(tc.constraint_type = 'PRIMARY KEY', false) as pk
|
|
92
|
+
FROM information_schema.columns c
|
|
93
|
+
LEFT JOIN information_schema.key_column_usage kcu
|
|
94
|
+
ON c.table_schema = kcu.table_schema AND c.table_name = kcu.table_name AND c.column_name = kcu.column_name
|
|
95
|
+
LEFT JOIN information_schema.table_constraints tc
|
|
96
|
+
ON kcu.constraint_name = tc.constraint_name AND tc.constraint_type = 'PRIMARY KEY'
|
|
97
|
+
WHERE c.table_schema = ${schema} AND c.table_name = ${table}
|
|
98
|
+
ORDER BY c.ordinal_position
|
|
99
|
+
`;
|
|
100
|
+
return cols.map((c) => ({
|
|
101
|
+
name: c.name as string,
|
|
102
|
+
type: c.type as string,
|
|
103
|
+
nullable: c.nullable as boolean,
|
|
104
|
+
pk: c.pk as boolean,
|
|
105
|
+
defaultValue: c.default_value as string | null,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Get paginated rows from a table */
|
|
110
|
+
async getTableData(
|
|
111
|
+
connectionString: string, table: string, schema = "public",
|
|
112
|
+
page = 1, limit = 100, orderBy?: string, orderDir: "ASC" | "DESC" = "ASC",
|
|
113
|
+
): Promise<{ columns: string[]; rows: Record<string, unknown>[]; total: number; page: number; limit: number }> {
|
|
114
|
+
const sql = this.connect(connectionString);
|
|
115
|
+
const fullTable = sql(`${schema}.${table}`);
|
|
116
|
+
|
|
117
|
+
const [countRow] = await sql`SELECT COUNT(*)::int as cnt FROM ${fullTable}`;
|
|
118
|
+
const total = (countRow?.cnt as number) ?? 0;
|
|
119
|
+
const offset = (page - 1) * limit;
|
|
120
|
+
|
|
121
|
+
let rows: postgres.RowList<postgres.Row[]>;
|
|
122
|
+
if (orderBy) {
|
|
123
|
+
const orderCol = sql(orderBy);
|
|
124
|
+
rows = orderDir === "DESC"
|
|
125
|
+
? await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} DESC LIMIT ${limit} OFFSET ${offset}`
|
|
126
|
+
: await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} ASC LIMIT ${limit} OFFSET ${offset}`;
|
|
127
|
+
} else {
|
|
128
|
+
rows = await sql`SELECT * FROM ${fullTable} LIMIT ${limit} OFFSET ${offset}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
|
|
132
|
+
return { columns, rows: rows as unknown as Record<string, unknown>[], total, page, limit };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Execute arbitrary SQL */
|
|
136
|
+
async executeQuery(connectionString: string, sqlText: string): Promise<PgQueryResult> {
|
|
137
|
+
const sql = this.connect(connectionString);
|
|
138
|
+
const trimmed = sqlText.trim().toUpperCase();
|
|
139
|
+
const isSelect = trimmed.startsWith("SELECT") || trimmed.startsWith("EXPLAIN") ||
|
|
140
|
+
trimmed.startsWith("SHOW") || trimmed.startsWith("\\D");
|
|
141
|
+
|
|
142
|
+
if (isSelect) {
|
|
143
|
+
const rows = await sql.unsafe(sqlText);
|
|
144
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
|
|
145
|
+
return { columns, rows: rows as unknown as Record<string, unknown>[], rowsAffected: 0, changeType: "select" };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const result = await sql.unsafe(sqlText);
|
|
149
|
+
return { columns: [], rows: [], rowsAffected: result.count ?? 0, changeType: "modify" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Update a single cell value */
|
|
153
|
+
async updateCell(
|
|
154
|
+
connectionString: string, table: string, schema = "public",
|
|
155
|
+
pkColumn: string, pkValue: unknown, column: string, value: unknown,
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
const sql = this.connect(connectionString);
|
|
158
|
+
await sql.unsafe(
|
|
159
|
+
`UPDATE "${schema}"."${table}" SET "${column}" = $1 WHERE "${pkColumn}" = $2`,
|
|
160
|
+
[value as never, pkValue as never],
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Close all cached connections */
|
|
165
|
+
async closeAll() {
|
|
166
|
+
for (const key of this.cache.keys()) await this.disconnect(key);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const postgresService = new PostgresService();
|
|
@@ -34,10 +34,11 @@ interface CachedDb {
|
|
|
34
34
|
class SqliteService {
|
|
35
35
|
private cache = new Map<string, CachedDb>();
|
|
36
36
|
|
|
37
|
-
/** Resolve
|
|
37
|
+
/** Resolve db path — supports both project-relative and absolute paths */
|
|
38
38
|
private resolvePath(projectPath: string, dbRelPath: string): string {
|
|
39
|
-
const
|
|
40
|
-
|
|
39
|
+
const isAbsolute = /^(\/|[A-Za-z]:[/\\])/.test(dbRelPath);
|
|
40
|
+
const abs = isAbsolute ? dbRelPath : resolve(projectPath, dbRelPath);
|
|
41
|
+
if (!isAbsolute && !abs.startsWith(projectPath)) throw new Error("Access denied: path outside project");
|
|
41
42
|
if (!existsSync(abs)) throw new Error(`Database not found: ${dbRelPath}`);
|
|
42
43
|
return abs;
|
|
43
44
|
}
|
|
@@ -117,7 +118,7 @@ class SqliteService {
|
|
|
117
118
|
if (isSelect) {
|
|
118
119
|
const stmt = db.query(sql);
|
|
119
120
|
const rows = stmt.all() as Record<string, unknown>[];
|
|
120
|
-
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
121
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
|
|
121
122
|
return { columns, rows, rowsAffected: 0, changeType: "select" };
|
|
122
123
|
}
|
|
123
124
|
|
|
@@ -11,6 +11,8 @@ import { Loader2, FileWarning, ExternalLink, Code, Eye, WrapText } from "lucide-
|
|
|
11
11
|
|
|
12
12
|
/** Image extensions renderable inline */
|
|
13
13
|
const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "ico"]);
|
|
14
|
+
/** SQLite extensions — redirect to sqlite viewer */
|
|
15
|
+
const SQLITE_EXTS = new Set(["db", "sqlite", "sqlite3"]);
|
|
14
16
|
|
|
15
17
|
function getFileExt(filename: string): string {
|
|
16
18
|
return filename.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -54,9 +56,15 @@ export function CodeEditor({ metadata, tabId }: CodeEditorProps) {
|
|
|
54
56
|
const ext = filePath ? getFileExt(filePath) : "";
|
|
55
57
|
const isImage = IMAGE_EXTS.has(ext);
|
|
56
58
|
const isPdf = ext === "pdf";
|
|
59
|
+
const isSqlite = SQLITE_EXTS.has(ext);
|
|
57
60
|
const isMarkdown = ext === "md" || ext === "mdx";
|
|
58
61
|
const [mdMode, setMdMode] = useState<"edit" | "preview">("preview");
|
|
59
62
|
|
|
63
|
+
// Redirect .db files to sqlite viewer by changing tab type
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (isSqlite && tabId) updateTab(tabId, { type: "sqlite" });
|
|
66
|
+
}, [isSqlite, tabId, updateTab]);
|
|
67
|
+
|
|
60
68
|
// Detect external (absolute) file path — not relative to project
|
|
61
69
|
const isExternalFile = filePath ? /^(\/|[A-Za-z]:[/\\])/.test(filePath) : false;
|
|
62
70
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
GitBranch,
|
|
6
6
|
GitCommitHorizontal,
|
|
7
7
|
Settings,
|
|
8
|
+
Database,
|
|
8
9
|
Search,
|
|
9
10
|
FileCode,
|
|
10
11
|
FolderOpen,
|
|
@@ -116,6 +117,7 @@ export function CommandPalette({ open, onClose, initialQuery = "" }: { open: boo
|
|
|
116
117
|
{ id: "terminal", label: "New Terminal", icon: Terminal, action: openNewTab("terminal", "Terminal"), keywords: "bash shell console", group: "action" },
|
|
117
118
|
{ id: "chat", label: "New AI Chat", icon: MessageSquare, action: openNewTab("chat", "AI Chat"), keywords: "ai assistant claude", group: "action" },
|
|
118
119
|
{ id: "git-graph", label: "Git Graph", icon: GitBranch, action: openNewTab("git-graph", "Git Graph"), keywords: "branch history log", group: "action" },
|
|
120
|
+
{ id: "postgres", label: "PostgreSQL", icon: Database, action: openNewTab("postgres", "PostgreSQL"), keywords: "database pg sql query", group: "action" },
|
|
119
121
|
{ id: "git-status", label: "Git Status", icon: GitCommitHorizontal, action: () => { setSidebarActiveTab("git"); onClose(); }, keywords: "changes diff staged", group: "action" },
|
|
120
122
|
{
|
|
121
123
|
id: "settings", label: "Settings", icon: Settings,
|
|
@@ -18,6 +18,7 @@ const TAB_COMPONENTS: Record<TabType, React.LazyExoticComponent<React.ComponentT
|
|
|
18
18
|
chat: lazy(() => import("@/components/chat/chat-tab").then((m) => ({ default: m.ChatTab }))),
|
|
19
19
|
editor: lazy(() => import("@/components/editor/code-editor").then((m) => ({ default: m.CodeEditor }))),
|
|
20
20
|
sqlite: lazy(() => import("@/components/sqlite/sqlite-viewer").then((m) => ({ default: m.SqliteViewer }))),
|
|
21
|
+
postgres: lazy(() => import("@/components/postgres/postgres-viewer").then((m) => ({ default: m.PostgresViewer }))),
|
|
21
22
|
"git-graph": lazy(() => import("@/components/git/git-graph").then((m) => ({ default: m.GitGraph }))),
|
|
22
23
|
"git-diff": lazy(() => import("@/components/editor/diff-viewer").then((m) => ({ default: m.DiffViewer }))),
|
|
23
24
|
settings: lazy(() => import("@/components/settings/settings-tab").then((m) => ({ default: m.SettingsTab }))),
|
|
@@ -21,7 +21,7 @@ const NEW_TAB_OPTIONS: { type: TabType; label: string }[] = [
|
|
|
21
21
|
const NEW_TAB_LABELS: Partial<Record<TabType, string>> = Object.fromEntries(NEW_TAB_OPTIONS.map((o) => [o.type, o.label]));
|
|
22
22
|
|
|
23
23
|
const TAB_ICONS: Record<TabType, React.ElementType> = {
|
|
24
|
-
terminal: Terminal, chat: MessageSquare, editor: FileCode, sqlite: Database,
|
|
24
|
+
terminal: Terminal, chat: MessageSquare, editor: FileCode, sqlite: Database, postgres: Database,
|
|
25
25
|
"git-graph": GitBranch, "git-diff": FileDiff, settings: Settings,
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -23,6 +23,11 @@ const TAB_COMPONENTS: Record<TabType, React.LazyExoticComponent<React.ComponentT
|
|
|
23
23
|
default: m.SqliteViewer,
|
|
24
24
|
})),
|
|
25
25
|
),
|
|
26
|
+
postgres: lazy(() =>
|
|
27
|
+
import("@/components/postgres/postgres-viewer").then((m) => ({
|
|
28
|
+
default: m.PostgresViewer,
|
|
29
|
+
})),
|
|
30
|
+
),
|
|
26
31
|
"git-graph": lazy(() =>
|
|
27
32
|
import("@/components/git/git-graph").then((m) => ({
|
|
28
33
|
default: m.GitGraph,
|