@samyx/preview-stacks-ui 0.22.0 → 0.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.
Files changed (82) hide show
  1. package/dist/assets/{ActionButton.vue_vue_type_script_setup_true_lang-D06apM-6.js → ActionButton.vue_vue_type_script_setup_true_lang-CeingrPV.js} +2 -2
  2. package/dist/assets/{ActionButton.vue_vue_type_script_setup_true_lang-D06apM-6.js.map → ActionButton.vue_vue_type_script_setup_true_lang-CeingrPV.js.map} +1 -1
  3. package/dist/assets/{AxesTab-CCB3wdSX.js → AxesTab-vIQWJMZm.js} +2 -2
  4. package/dist/assets/{AxesTab-CCB3wdSX.js.map → AxesTab-vIQWJMZm.js.map} +1 -1
  5. package/dist/assets/{ConfigTab-Cyqzeq3C.js → ConfigTab-BeAIDTnl.js} +2 -2
  6. package/dist/assets/{ConfigTab-Cyqzeq3C.js.map → ConfigTab-BeAIDTnl.js.map} +1 -1
  7. package/dist/assets/{ConflictNote.vue_vue_type_script_setup_true_lang-CtwJE2eJ.js → ConflictNote.vue_vue_type_script_setup_true_lang-fprimIle.js} +2 -2
  8. package/dist/assets/{ConflictNote.vue_vue_type_script_setup_true_lang-CtwJE2eJ.js.map → ConflictNote.vue_vue_type_script_setup_true_lang-fprimIle.js.map} +1 -1
  9. package/dist/assets/{DangerTab-Cdr9luUy.js → DangerTab-CcYJAftw.js} +2 -2
  10. package/dist/assets/{DangerTab-Cdr9luUy.js.map → DangerTab-CcYJAftw.js.map} +1 -1
  11. package/dist/assets/{DashboardView-S4eiNX8X.js → DashboardView-Cvh03qbj.js} +2 -2
  12. package/dist/assets/{DashboardView-S4eiNX8X.js.map → DashboardView-Cvh03qbj.js.map} +1 -1
  13. package/dist/assets/{DeploymentDetailView-CD4RVr06.js → DeploymentDetailView-BzEQBKhd.js} +2 -2
  14. package/dist/assets/{DeploymentDetailView-CD4RVr06.js.map → DeploymentDetailView-BzEQBKhd.js.map} +1 -1
  15. package/dist/assets/{DeploymentsView-DIX7IA8g.js → DeploymentsView-f3RgqUkQ.js} +2 -2
  16. package/dist/assets/{DeploymentsView-DIX7IA8g.js.map → DeploymentsView-f3RgqUkQ.js.map} +1 -1
  17. package/dist/assets/{EquivalentCommand-NHzDrg5d.js → EquivalentCommand-D0Bfgz63.js} +3 -3
  18. package/dist/assets/{EquivalentCommand-NHzDrg5d.js.map → EquivalentCommand-D0Bfgz63.js.map} +1 -1
  19. package/dist/assets/{ErrorNote.vue_vue_type_script_setup_true_lang-BFuRxw1l.js → ErrorNote.vue_vue_type_script_setup_true_lang-DT5SbSbT.js} +2 -2
  20. package/dist/assets/{ErrorNote.vue_vue_type_script_setup_true_lang-BFuRxw1l.js.map → ErrorNote.vue_vue_type_script_setup_true_lang-DT5SbSbT.js.map} +1 -1
  21. package/dist/assets/{HelpModal.vue_vue_type_script_setup_true_lang-DwwXY7EX.js → HelpModal.vue_vue_type_script_setup_true_lang-2ev6wLk8.js} +2 -2
  22. package/dist/assets/{HelpModal.vue_vue_type_script_setup_true_lang-DwwXY7EX.js.map → HelpModal.vue_vue_type_script_setup_true_lang-2ev6wLk8.js.map} +1 -1
  23. package/dist/assets/JobDetailView-B5OFSqGO.js +2 -0
  24. package/dist/assets/{JobDetailView-BsCnnkPz.js.map → JobDetailView-B5OFSqGO.js.map} +1 -1
  25. package/dist/assets/{JobsView-Bd_XHRkd.js → JobsView-B6RvX8WS.js} +2 -2
  26. package/dist/assets/{JobsView-Bd_XHRkd.js.map → JobsView-B6RvX8WS.js.map} +1 -1
  27. package/dist/assets/{LogViewer.vue_vue_type_script_setup_true_lang-7Ggcxz1R.js → LogViewer.vue_vue_type_script_setup_true_lang-B5K26k3c.js} +3 -3
  28. package/dist/assets/{LogViewer.vue_vue_type_script_setup_true_lang-7Ggcxz1R.js.map → LogViewer.vue_vue_type_script_setup_true_lang-B5K26k3c.js.map} +1 -1
  29. package/dist/assets/{LoginView-Bj7uMFvv.js → LoginView-NzylzAuv.js} +2 -2
  30. package/dist/assets/{LoginView-Bj7uMFvv.js.map → LoginView-NzylzAuv.js.map} +1 -1
  31. package/dist/assets/{LogsTab-DRp76oC1.js → LogsTab-DxhpYJY3.js} +3 -3
  32. package/dist/assets/{LogsTab-DRp76oC1.js.map → LogsTab-DxhpYJY3.js.map} +1 -1
  33. package/dist/assets/{NotFoundView-D-A8YFJw.js → NotFoundView-Cg_jPEQi.js} +2 -2
  34. package/dist/assets/{NotFoundView-D-A8YFJw.js.map → NotFoundView-Cg_jPEQi.js.map} +1 -1
  35. package/dist/assets/{NotifiersView-CChDRHSy.js → NotifiersView-B53fERhd.js} +2 -2
  36. package/dist/assets/{NotifiersView-CChDRHSy.js.map → NotifiersView-B53fERhd.js.map} +1 -1
  37. package/dist/assets/{OverviewTab-jE_kOZI7.js → OverviewTab-Cy7_mGTt.js} +2 -2
  38. package/dist/assets/{OverviewTab-jE_kOZI7.js.map → OverviewTab-Cy7_mGTt.js.map} +1 -1
  39. package/dist/assets/{RegistriesView-D3Nh0dbq.js → RegistriesView-dc-04uci.js} +2 -2
  40. package/dist/assets/{RegistriesView-D3Nh0dbq.js.map → RegistriesView-dc-04uci.js.map} +1 -1
  41. package/dist/assets/{RelativeTime.vue_vue_type_script_setup_true_lang-mVrRpkV_.js → RelativeTime.vue_vue_type_script_setup_true_lang-D7n5E9jw.js} +2 -2
  42. package/dist/assets/{RelativeTime.vue_vue_type_script_setup_true_lang-mVrRpkV_.js.map → RelativeTime.vue_vue_type_script_setup_true_lang-D7n5E9jw.js.map} +1 -1
  43. package/dist/assets/{RequiresTab-BgGIZ1Tn.js → RequiresTab-DDWsrZig.js} +2 -2
  44. package/dist/assets/{RequiresTab-BgGIZ1Tn.js.map → RequiresTab-DDWsrZig.js.map} +1 -1
  45. package/dist/assets/{RoutingView-D2StZawf.js → RoutingView-BheJjs2g.js} +3 -3
  46. package/dist/assets/{RoutingView-D2StZawf.js.map → RoutingView-BheJjs2g.js.map} +1 -1
  47. package/dist/assets/RuntimeTab-D4sJaoqc.js +2 -0
  48. package/dist/assets/RuntimeTab-D4sJaoqc.js.map +1 -0
  49. package/dist/assets/{SelectMenu.vue_vue_type_script_setup_true_lang-qGkx2MwW.js → SelectMenu.vue_vue_type_script_setup_true_lang-CSQS6ben.js} +2 -2
  50. package/dist/assets/{SelectMenu.vue_vue_type_script_setup_true_lang-qGkx2MwW.js.map → SelectMenu.vue_vue_type_script_setup_true_lang-CSQS6ben.js.map} +1 -1
  51. package/dist/assets/SettingsView-CQIMBeQh.js +2 -0
  52. package/dist/assets/{SettingsView-Cu1Zkufg.js.map → SettingsView-CQIMBeQh.js.map} +1 -1
  53. package/dist/assets/{SkeletonList.vue_vue_type_script_setup_true_lang-BYHXRCNR.js → SkeletonList.vue_vue_type_script_setup_true_lang-DwoeliUC.js} +2 -2
  54. package/dist/assets/{SkeletonList.vue_vue_type_script_setup_true_lang-BYHXRCNR.js.map → SkeletonList.vue_vue_type_script_setup_true_lang-DwoeliUC.js.map} +1 -1
  55. package/dist/assets/{SpecDetailView-DIi21wo1.js → SpecDetailView-DSlpITFw.js} +2 -2
  56. package/dist/assets/{SpecDetailView-DIi21wo1.js.map → SpecDetailView-DSlpITFw.js.map} +1 -1
  57. package/dist/assets/{SpecsView-B49LI4Ut.js → SpecsView-Cq_Ny0u3.js} +2 -2
  58. package/dist/assets/{SpecsView-B49LI4Ut.js.map → SpecsView-Cq_Ny0u3.js.map} +1 -1
  59. package/dist/assets/{StateBadge.vue_vue_type_script_setup_true_lang-DANvveJ6.js → StateBadge.vue_vue_type_script_setup_true_lang-BsDpZsUH.js} +2 -2
  60. package/dist/assets/{StateBadge.vue_vue_type_script_setup_true_lang-DANvveJ6.js.map → StateBadge.vue_vue_type_script_setup_true_lang-BsDpZsUH.js.map} +1 -1
  61. package/dist/assets/{SubmitView-ZYGApCXE.js → SubmitView-DqOEabaL.js} +5 -5
  62. package/dist/assets/{SubmitView-ZYGApCXE.js.map → SubmitView-DqOEabaL.js.map} +1 -1
  63. package/dist/assets/{Teleport-d5PoAxFM.js → Teleport-BXE_6A8N.js} +3 -3
  64. package/dist/assets/{Teleport-d5PoAxFM.js.map → Teleport-BXE_6A8N.js.map} +1 -1
  65. package/dist/assets/{TerminalTab-DAZpn3jK.js → TerminalTab-lb8FY01Y.js} +2 -2
  66. package/dist/assets/{TerminalTab-DAZpn3jK.js.map → TerminalTab-lb8FY01Y.js.map} +1 -1
  67. package/dist/assets/{UsersView-BfQKf8Sf.js → UsersView-mOC1UteJ.js} +2 -2
  68. package/dist/assets/{UsersView-BfQKf8Sf.js.map → UsersView-mOC1UteJ.js.map} +1 -1
  69. package/dist/assets/{VarEditor.vue_vue_type_script_setup_true_lang-D--Rd-Mv.js → VarEditor.vue_vue_type_script_setup_true_lang-ChzmZbEl.js} +2 -2
  70. package/dist/assets/{VarEditor.vue_vue_type_script_setup_true_lang-D--Rd-Mv.js.map → VarEditor.vue_vue_type_script_setup_true_lang-ChzmZbEl.js.map} +1 -1
  71. package/dist/assets/{VariablesView-Bej9ZZmS.js → VariablesView-NBIFfXQ5.js} +2 -2
  72. package/dist/assets/{VariablesView-Bej9ZZmS.js.map → VariablesView-NBIFfXQ5.js.map} +1 -1
  73. package/dist/assets/{index-DVCVnKbf.js → index-wjzRhuU3.js} +4 -4
  74. package/dist/assets/{index-DVCVnKbf.js.map → index-wjzRhuU3.js.map} +1 -1
  75. package/dist/assets/{useDeployment-752fTjLE.js → useDeployment-B7kMYmcq.js} +2 -2
  76. package/dist/assets/{useDeployment-752fTjLE.js.map → useDeployment-B7kMYmcq.js.map} +1 -1
  77. package/dist/index.html +1 -1
  78. package/package.json +1 -1
  79. package/dist/assets/JobDetailView-BsCnnkPz.js +0 -2
  80. package/dist/assets/RuntimeTab-B0D_MxFQ.js +0 -2
  81. package/dist/assets/RuntimeTab-B0D_MxFQ.js.map +0 -1
  82. package/dist/assets/SettingsView-Cu1Zkufg.js +0 -2
@@ -1,2 +1,2 @@
1
- import{S as n,C as i,A as d,B as l,K as p,U as c}from"./index-DVCVnKbf.js";import{w as u,r as y}from"./useVars-VN9gYdR6.js";const e=n({id:"",detail:null,error:"",loading:!1,vars:[]}),f=i(()=>p(e.vars)),k=i(()=>c(e.id)),b=i(()=>{var a;return((a=e.detail)==null?void 0:a.kind)==="shared"}),h=i(()=>{var a;return(((a=e.detail)==null?void 0:a.axes)??[]).filter(s=>!s.verifiable).map(s=>s.name)});async function t(){const a=e.id;e.loading=!0;const s=await d.get(`/api/deployments/${encodeURIComponent(a)}${f.value}`);if(e.id!==a)return;if(e.loading=!1,!s.ok){e.detail=null,e.error=l(s,`GET /api/deployments/${a}`);return}e.error="";const r=s.body;e.detail={id:r.id??a,kind:r.kind==="shared"?"shared":"isolated",createdAt:r.createdAt,updatedAt:r.updatedAt,stack:typeof r.stack=="string"?r.stack:"",busy:r.busy??null,compose:r.compose?{file:r.compose.file??"?",profiles:r.compose.profiles??[],overlays:r.compose.overlays??[]}:null,requires:Array.isArray(r.requires)?r.requires:[],axes:(Array.isArray(r.axes)?r.axes:[]).map(o=>({name:o.name,hooks:Array.isArray(o.hooks)?o.hooks:[],verifiable:o.verifiable??(Array.isArray(o.hooks)&&o.hooks.includes("assert_gone"))})),env:Array.isArray(r.env)?r.env:[]}}function g(a){e.id===a&&(e.detail||e.error)||(e.id=a,e.detail=null,e.error="",e.vars=y(a),t())}function m(){u(e.id,e.vars)}function x(){m(),t()}export{x as a,e as d,b as i,g as o,m as p,k as r,h as u,f as v};
2
- //# sourceMappingURL=useDeployment-752fTjLE.js.map
1
+ import{S as n,C as i,A as d,B as l,L as p,U as c}from"./index-wjzRhuU3.js";import{w as u,r as y}from"./useVars-VN9gYdR6.js";const e=n({id:"",detail:null,error:"",loading:!1,vars:[]}),f=i(()=>p(e.vars)),k=i(()=>c(e.id)),b=i(()=>{var a;return((a=e.detail)==null?void 0:a.kind)==="shared"}),h=i(()=>{var a;return(((a=e.detail)==null?void 0:a.axes)??[]).filter(s=>!s.verifiable).map(s=>s.name)});async function t(){const a=e.id;e.loading=!0;const s=await d.get(`/api/deployments/${encodeURIComponent(a)}${f.value}`);if(e.id!==a)return;if(e.loading=!1,!s.ok){e.detail=null,e.error=l(s,`GET /api/deployments/${a}`);return}e.error="";const r=s.body;e.detail={id:r.id??a,kind:r.kind==="shared"?"shared":"isolated",createdAt:r.createdAt,updatedAt:r.updatedAt,stack:typeof r.stack=="string"?r.stack:"",busy:r.busy??null,compose:r.compose?{file:r.compose.file??"?",profiles:r.compose.profiles??[],overlays:r.compose.overlays??[]}:null,requires:Array.isArray(r.requires)?r.requires:[],axes:(Array.isArray(r.axes)?r.axes:[]).map(o=>({name:o.name,hooks:Array.isArray(o.hooks)?o.hooks:[],verifiable:o.verifiable??(Array.isArray(o.hooks)&&o.hooks.includes("assert_gone"))})),env:Array.isArray(r.env)?r.env:[]}}function g(a){e.id===a&&(e.detail||e.error)||(e.id=a,e.detail=null,e.error="",e.vars=y(a),t())}function m(){u(e.id,e.vars)}function x(){m(),t()}export{x as a,e as d,b as i,g as o,m as p,k as r,h as u,f as v};
2
+ //# sourceMappingURL=useDeployment-B7kMYmcq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDeployment-752fTjLE.js","sources":["../../src/composables/useDeployment.ts"],"sourcesContent":["/**\n * The deployment currently open in the detail view.\n *\n * A module singleton rather than provide/inject, because the six tabs are CHILD ROUTES: they are\n * siblings of each other and are mounted and unmounted independently, so passing the loaded\n * deployment down as a prop would mean re-fetching it on every tab change. Switching tabs must not\n * re-fetch or blank the panel — only opening a DIFFERENT deployment does.\n *\n * `vars` here are REQUEST-TIME variables, sent as `?query` on every route that resolves this\n * deployment. See `useVars.ts` for why they matter and what they can get wrong.\n */\n\nimport { computed, reactive } from 'vue';\nimport { api, problem, query } from '../api/client';\nimport type { Deployment, VarPair } from '../api/types';\nimport { readVars, writeVars } from './useVars';\nimport { rowFor } from './useControlPlane';\n\nexport const dep = reactive({\n id: '',\n detail: null as Deployment | null,\n /** The resolve failure, verbatim. It is a 400 that NAMES the missing variable. */\n error: '',\n loading: false,\n vars: [] as VarPair[],\n});\n\nexport const varsQuery = computed(() => query(dep.vars));\n\n/** The list row, which is the only place the server exposes stored `vars` and `specName` today. */\nexport const row = computed(() => rowFor(dep.id));\n\nexport const isShared = computed(() => dep.detail?.kind === 'shared');\n\nexport const unverifiableAxes = computed(() =>\n (dep.detail?.axes ?? []).filter((a) => !a.verifiable).map((a) => a.name),\n);\n\nexport async function loadDetail(): Promise<void> {\n const id = dep.id;\n dep.loading = true;\n const r = await api.get<Deployment>(`/api/deployments/${encodeURIComponent(id)}${varsQuery.value}`);\n // Navigated to another deployment while this was in flight: drop the answer rather than render\n // one deployment's spec under another's id.\n if (dep.id !== id) return;\n dep.loading = false;\n\n if (!r.ok) {\n dep.detail = null;\n dep.error = problem(r, `GET /api/deployments/${id}`);\n return;\n }\n dep.error = '';\n const b = r.body;\n dep.detail = {\n id: b.id ?? id,\n kind: b.kind === 'shared' ? 'shared' : 'isolated',\n createdAt: b.createdAt,\n updatedAt: b.updatedAt,\n stack: typeof b.stack === 'string' ? b.stack : '',\n busy: b.busy ?? null,\n compose: b.compose\n ? {\n file: b.compose.file ?? '?',\n profiles: b.compose.profiles ?? [],\n overlays: b.compose.overlays ?? [],\n }\n : null,\n requires: Array.isArray(b.requires) ? b.requires : [],\n axes: (Array.isArray(b.axes) ? b.axes : []).map((a) => ({\n name: a.name,\n hooks: Array.isArray(a.hooks) ? a.hooks : [],\n // Derive it if an older build omits the field. Defaulting to `false` would claim an axis is\n // unverifiable when it may define a perfectly good `assert_gone`.\n verifiable: a.verifiable ?? (Array.isArray(a.hooks) && a.hooks.includes('assert_gone')),\n })),\n env: Array.isArray(b.env) ? b.env : [],\n };\n}\n\n/** Point the singleton at a deployment. Resets everything a stale id could leak into the new one. */\nexport function openDeployment(id: string): void {\n if (dep.id === id && (dep.detail || dep.error)) return; // a tab change, not a navigation\n dep.id = id;\n dep.detail = null;\n dep.error = '';\n dep.vars = readVars(id);\n void loadDetail();\n}\n\nexport function persistVars(): void {\n writeVars(dep.id, dep.vars);\n}\n\nexport function applyVars(): void {\n persistVars();\n void loadDetail();\n}\n"],"names":["dep","reactive","varsQuery","computed","query","row","rowFor","isShared","_a","unverifiableAxes","a","loadDetail","id","r","api","problem","b","openDeployment","readVars","persistVars","writeVars","applyVars"],"mappings":"4HAkBO,MAAMA,EAAMC,EAAS,CAC1B,GAAI,GACJ,OAAQ,KAER,MAAO,GACP,QAAS,GACT,KAAM,CAAA,CACR,CAAC,EAEYC,EAAYC,EAAS,IAAMC,EAAMJ,EAAI,IAAI,CAAC,EAG1CK,EAAMF,EAAS,IAAMG,EAAON,EAAI,EAAE,CAAC,EAEnCO,EAAWJ,EAAS,IAAA,OAAM,QAAAK,EAAAR,EAAI,SAAJ,YAAAQ,EAAY,QAAS,SAAQ,EAEvDC,EAAmBN,EAAS,IAAA,OACtC,SAAAK,EAAAR,EAAI,SAAJ,YAAAQ,EAAY,OAAQ,CAAA,GAAI,OAAQE,GAAM,CAACA,EAAE,UAAU,EAAE,IAAKA,GAAMA,EAAE,IAAI,EACzE,EAEA,eAAsBC,GAA4B,CAChD,MAAMC,EAAKZ,EAAI,GACfA,EAAI,QAAU,GACd,MAAMa,EAAI,MAAMC,EAAI,IAAgB,oBAAoB,mBAAmBF,CAAE,CAAC,GAAGV,EAAU,KAAK,EAAE,EAGlG,GAAIF,EAAI,KAAOY,EAAI,OAGnB,GAFAZ,EAAI,QAAU,GAEV,CAACa,EAAE,GAAI,CACTb,EAAI,OAAS,KACbA,EAAI,MAAQe,EAAQF,EAAG,wBAAwBD,CAAE,EAAE,EACnD,MACF,CACAZ,EAAI,MAAQ,GACZ,MAAMgB,EAAIH,EAAE,KACZb,EAAI,OAAS,CACX,GAAIgB,EAAE,IAAMJ,EACZ,KAAMI,EAAE,OAAS,SAAW,SAAW,WACvC,UAAWA,EAAE,UACb,UAAWA,EAAE,UACb,MAAO,OAAOA,EAAE,OAAU,SAAWA,EAAE,MAAQ,GAC/C,KAAMA,EAAE,MAAQ,KAChB,QAASA,EAAE,QACP,CACE,KAAMA,EAAE,QAAQ,MAAQ,IACxB,SAAUA,EAAE,QAAQ,UAAY,CAAA,EAChC,SAAUA,EAAE,QAAQ,UAAY,CAAA,CAAC,EAEnC,KACJ,SAAU,MAAM,QAAQA,EAAE,QAAQ,EAAIA,EAAE,SAAW,CAAA,EACnD,MAAO,MAAM,QAAQA,EAAE,IAAI,EAAIA,EAAE,KAAO,CAAA,GAAI,IAAKN,IAAO,CACtD,KAAMA,EAAE,KACR,MAAO,MAAM,QAAQA,EAAE,KAAK,EAAIA,EAAE,MAAQ,CAAA,EAG1C,WAAYA,EAAE,aAAe,MAAM,QAAQA,EAAE,KAAK,GAAKA,EAAE,MAAM,SAAS,aAAa,EAAA,EACrF,EACF,IAAK,MAAM,QAAQM,EAAE,GAAG,EAAIA,EAAE,IAAM,CAAA,CAAC,CAEzC,CAGO,SAASC,EAAeL,EAAkB,CAC3CZ,EAAI,KAAOY,IAAOZ,EAAI,QAAUA,EAAI,SACxCA,EAAI,GAAKY,EACTZ,EAAI,OAAS,KACbA,EAAI,MAAQ,GACZA,EAAI,KAAOkB,EAASN,CAAE,EACjBD,EAAA,EACP,CAEO,SAASQ,GAAoB,CAClCC,EAAUpB,EAAI,GAAIA,EAAI,IAAI,CAC5B,CAEO,SAASqB,GAAkB,CAChCF,EAAA,EACKR,EAAA,CACP"}
1
+ {"version":3,"file":"useDeployment-B7kMYmcq.js","sources":["../../src/composables/useDeployment.ts"],"sourcesContent":["/**\n * The deployment currently open in the detail view.\n *\n * A module singleton rather than provide/inject, because the six tabs are CHILD ROUTES: they are\n * siblings of each other and are mounted and unmounted independently, so passing the loaded\n * deployment down as a prop would mean re-fetching it on every tab change. Switching tabs must not\n * re-fetch or blank the panel — only opening a DIFFERENT deployment does.\n *\n * `vars` here are REQUEST-TIME variables, sent as `?query` on every route that resolves this\n * deployment. See `useVars.ts` for why they matter and what they can get wrong.\n */\n\nimport { computed, reactive } from 'vue';\nimport { api, problem, query } from '../api/client';\nimport type { Deployment, VarPair } from '../api/types';\nimport { readVars, writeVars } from './useVars';\nimport { rowFor } from './useControlPlane';\n\nexport const dep = reactive({\n id: '',\n detail: null as Deployment | null,\n /** The resolve failure, verbatim. It is a 400 that NAMES the missing variable. */\n error: '',\n loading: false,\n vars: [] as VarPair[],\n});\n\nexport const varsQuery = computed(() => query(dep.vars));\n\n/** The list row, which is the only place the server exposes stored `vars` and `specName` today. */\nexport const row = computed(() => rowFor(dep.id));\n\nexport const isShared = computed(() => dep.detail?.kind === 'shared');\n\nexport const unverifiableAxes = computed(() =>\n (dep.detail?.axes ?? []).filter((a) => !a.verifiable).map((a) => a.name),\n);\n\nexport async function loadDetail(): Promise<void> {\n const id = dep.id;\n dep.loading = true;\n const r = await api.get<Deployment>(`/api/deployments/${encodeURIComponent(id)}${varsQuery.value}`);\n // Navigated to another deployment while this was in flight: drop the answer rather than render\n // one deployment's spec under another's id.\n if (dep.id !== id) return;\n dep.loading = false;\n\n if (!r.ok) {\n dep.detail = null;\n dep.error = problem(r, `GET /api/deployments/${id}`);\n return;\n }\n dep.error = '';\n const b = r.body;\n dep.detail = {\n id: b.id ?? id,\n kind: b.kind === 'shared' ? 'shared' : 'isolated',\n createdAt: b.createdAt,\n updatedAt: b.updatedAt,\n stack: typeof b.stack === 'string' ? b.stack : '',\n busy: b.busy ?? null,\n compose: b.compose\n ? {\n file: b.compose.file ?? '?',\n profiles: b.compose.profiles ?? [],\n overlays: b.compose.overlays ?? [],\n }\n : null,\n requires: Array.isArray(b.requires) ? b.requires : [],\n axes: (Array.isArray(b.axes) ? b.axes : []).map((a) => ({\n name: a.name,\n hooks: Array.isArray(a.hooks) ? a.hooks : [],\n // Derive it if an older build omits the field. Defaulting to `false` would claim an axis is\n // unverifiable when it may define a perfectly good `assert_gone`.\n verifiable: a.verifiable ?? (Array.isArray(a.hooks) && a.hooks.includes('assert_gone')),\n })),\n env: Array.isArray(b.env) ? b.env : [],\n };\n}\n\n/** Point the singleton at a deployment. Resets everything a stale id could leak into the new one. */\nexport function openDeployment(id: string): void {\n if (dep.id === id && (dep.detail || dep.error)) return; // a tab change, not a navigation\n dep.id = id;\n dep.detail = null;\n dep.error = '';\n dep.vars = readVars(id);\n void loadDetail();\n}\n\nexport function persistVars(): void {\n writeVars(dep.id, dep.vars);\n}\n\nexport function applyVars(): void {\n persistVars();\n void loadDetail();\n}\n"],"names":["dep","reactive","varsQuery","computed","query","row","rowFor","isShared","_a","unverifiableAxes","a","loadDetail","id","r","api","problem","b","openDeployment","readVars","persistVars","writeVars","applyVars"],"mappings":"4HAkBO,MAAMA,EAAMC,EAAS,CAC1B,GAAI,GACJ,OAAQ,KAER,MAAO,GACP,QAAS,GACT,KAAM,CAAA,CACR,CAAC,EAEYC,EAAYC,EAAS,IAAMC,EAAMJ,EAAI,IAAI,CAAC,EAG1CK,EAAMF,EAAS,IAAMG,EAAON,EAAI,EAAE,CAAC,EAEnCO,EAAWJ,EAAS,IAAA,OAAM,QAAAK,EAAAR,EAAI,SAAJ,YAAAQ,EAAY,QAAS,SAAQ,EAEvDC,EAAmBN,EAAS,IAAA,OACtC,SAAAK,EAAAR,EAAI,SAAJ,YAAAQ,EAAY,OAAQ,CAAA,GAAI,OAAQE,GAAM,CAACA,EAAE,UAAU,EAAE,IAAKA,GAAMA,EAAE,IAAI,EACzE,EAEA,eAAsBC,GAA4B,CAChD,MAAMC,EAAKZ,EAAI,GACfA,EAAI,QAAU,GACd,MAAMa,EAAI,MAAMC,EAAI,IAAgB,oBAAoB,mBAAmBF,CAAE,CAAC,GAAGV,EAAU,KAAK,EAAE,EAGlG,GAAIF,EAAI,KAAOY,EAAI,OAGnB,GAFAZ,EAAI,QAAU,GAEV,CAACa,EAAE,GAAI,CACTb,EAAI,OAAS,KACbA,EAAI,MAAQe,EAAQF,EAAG,wBAAwBD,CAAE,EAAE,EACnD,MACF,CACAZ,EAAI,MAAQ,GACZ,MAAMgB,EAAIH,EAAE,KACZb,EAAI,OAAS,CACX,GAAIgB,EAAE,IAAMJ,EACZ,KAAMI,EAAE,OAAS,SAAW,SAAW,WACvC,UAAWA,EAAE,UACb,UAAWA,EAAE,UACb,MAAO,OAAOA,EAAE,OAAU,SAAWA,EAAE,MAAQ,GAC/C,KAAMA,EAAE,MAAQ,KAChB,QAASA,EAAE,QACP,CACE,KAAMA,EAAE,QAAQ,MAAQ,IACxB,SAAUA,EAAE,QAAQ,UAAY,CAAA,EAChC,SAAUA,EAAE,QAAQ,UAAY,CAAA,CAAC,EAEnC,KACJ,SAAU,MAAM,QAAQA,EAAE,QAAQ,EAAIA,EAAE,SAAW,CAAA,EACnD,MAAO,MAAM,QAAQA,EAAE,IAAI,EAAIA,EAAE,KAAO,CAAA,GAAI,IAAKN,IAAO,CACtD,KAAMA,EAAE,KACR,MAAO,MAAM,QAAQA,EAAE,KAAK,EAAIA,EAAE,MAAQ,CAAA,EAG1C,WAAYA,EAAE,aAAe,MAAM,QAAQA,EAAE,KAAK,GAAKA,EAAE,MAAM,SAAS,aAAa,EAAA,EACrF,EACF,IAAK,MAAM,QAAQM,EAAE,GAAG,EAAIA,EAAE,IAAM,CAAA,CAAC,CAEzC,CAGO,SAASC,EAAeL,EAAkB,CAC3CZ,EAAI,KAAOY,IAAOZ,EAAI,QAAUA,EAAI,SACxCA,EAAI,GAAKY,EACTZ,EAAI,OAAS,KACbA,EAAI,MAAQ,GACZA,EAAI,KAAOkB,EAASN,CAAE,EACjBD,EAAA,EACP,CAEO,SAASQ,GAAoB,CAClCC,EAAUpB,EAAI,GAAIA,EAAI,IAAI,CAC5B,CAEO,SAASqB,GAAkB,CAChCF,EAAA,EACKR,EAAA,CACP"}
package/dist/index.html CHANGED
@@ -27,7 +27,7 @@
27
27
  /* private mode: fall through to the OS preference */
28
28
  }
29
29
  </script>
30
- <script type="module" crossorigin src="/assets/index-DVCVnKbf.js"></script>
30
+ <script type="module" crossorigin src="/assets/index-wjzRhuU3.js"></script>
31
31
  <link rel="stylesheet" crossorigin href="/assets/index-Cyc_WOgY.css">
32
32
  </head>
33
33
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samyx/preview-stacks-ui",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "private": false,
5
5
  "description": "The advanced web UI for preview-stacks — an opt-in SPA that talks to the pstack HTTP API.",
6
6
  "type": "module",
@@ -1,2 +0,0 @@
1
- import{d as q,c as d,a as t,F as j,s as H,a1 as K,o as l,D as M,E as S,u as v,t as u,H as O,J as G,e as C,w as I,f as i,g as c,x as L,r as b,C as N,j as P,A as R,B as J,N as z}from"./index-DVCVnKbf.js";import{a as Q,c as E,t as X}from"./useFormat-DVXTfIS-.js";import{_ as Y}from"./LogViewer.vue_vue_type_script_setup_true_lang-7Ggcxz1R.js";import{_ as Z}from"./StateBadge.vue_vue_type_script_setup_true_lang-DANvveJ6.js";import{_ as ee}from"./ActionButton.vue_vue_type_script_setup_true_lang-D06apM-6.js";import{_ as te}from"./ErrorNote.vue_vue_type_script_setup_true_lang-BFuRxw1l.js";function y(n){var r;return(r=n.message)!=null&&r.startsWith("unverifiable")?"unverifiable":n.ok?"ok":n.phase==="assert_gone"?"leaked":"failed"}function se(n){return{ok:"✓",unverifiable:"?",leaked:"!",failed:"✗"}[y(n)]}function ae(n){return n.message?n.message:n.skipped?"skipped":y(n)==="ok"?"ok":`exit ${n.code}`}function ne(n){return n.filter(r=>r.phase==="assert_gone"&&!r.ok).map(r=>r.axis)}function oe(n){return n.filter(r=>y(r)==="unverifiable").length}const le={class:"steps stagger"},re={class:"who"},ie={class:"dim",style:{"font-size":"var(--t-sm)"}},ue=q({__name:"StepList",props:{steps:{}},setup(n){return(r,s)=>(l(),d("div",null,[t("ul",le,[(l(!0),d(j,null,H(n.steps,(o,m)=>(l(),d("li",{key:`${o.phase}-${o.axis}-${m}`,class:"step",style:M({"--i":m})},[t("span",{class:S(["mark",`s-${v(y)(o)}`]),"aria-hidden":"true"},u(v(se)(o)),3),t("span",null,[t("span",re,u(o.axis),1),t("span",ie," · "+u(o.phase),1),t("span",{class:S(["badge",v(y)(o)]),style:{"margin-left":"var(--s2)"}},u(v(y)(o)),3),t("div",{class:S(["msg",`s-${v(y)(o)}`])},u(v(ae)(o)),3)])],4))),128))]),s[0]||(s[0]=K('<p class="hint"><span class="s-ok">✓ ok</span> · <span class="s-failed">✗ failed</span> · <span class="s-leaked">! leaked — survived teardown</span> · <span class="s-unverifiable">? unverifiable — nothing to check, not a pass</span>. A <code>down</code> step marked ok may still carry a <code>non-fatal:</code> note: teardown is best-effort by design, so aborting halfway would leave more behind than continuing. </p>',1))]))}}),de={class:"page-head"},ce={class:"sub"},pe={key:0,class:"panel banner-panel warn"},ve={key:2,class:"panel leaked-banner"},me={key:3,class:"panel"},fe={class:"pre"},ge={class:"panel"},ye={key:4,class:"panel"},be={class:"phead"},ke={key:0,class:"mute"},Se=q({__name:"JobDetailView",props:{jobId:{}},setup(n){const r=n,s=b(null),o=b([]),m=b(""),x=b(!0),_=b(!1),F=N(()=>{const a=[];let e="";for(const p of o.value){const h=new Date(p.at),g=h.toLocaleDateString("sv");g!==e&&(a.push({key:`day-${g}-${p.seq}`,separator:!0,text:g}),e=g),a.push({key:p.seq,gutter:h.toTimeString().slice(0,8),gutterTitle:E(p.at),text:p.message,tone:p.level})}return a});let f=null;const A=N(()=>{var a;return(a=s.value)!=null&&a.outcome?ne(s.value.outcome.steps):[]}),B=N(()=>{var a;return(a=s.value)!=null&&a.outcome?oe(s.value.outcome.steps):0});async function k(){var e;const a=await R.get(`/api/jobs/${encodeURIComponent(r.jobId)}`);return x.value=!1,a.ok?(m.value="",s.value=a.body.job,(e=a.body.job.log)!=null&&e.length&&o.value.length===0&&(o.value=[...a.body.job.log]),a.body.job):(m.value=J(a,"load job"),null)}function w(){f==null||f.close(),f=null,_.value=!1}function T(){w(),o.value=[],f=new EventSource(R.url(`/api/jobs/${encodeURIComponent(r.jobId)}/stream`)),_.value=!0,f.onmessage=a=>{let e;try{e=JSON.parse(a.data)}catch{return}if(e.done){w(),k();return}o.value.push(e)},f.onerror=()=>{w(),k()}}O(()=>r.jobId,async()=>{o.value=[],s.value=null,x.value=!0;const a=await k();a&&a.state==="running"&&T()},{immediate:!0});const $=b(!1);async function W(){$.value=!0;const a=await R.post(`/api/jobs/${encodeURIComponent(r.jobId)}/cancel`);if($.value=!1,!a.ok){z(a.status===409?"info":"error",J(a,"stop this job")),k();return}z("ok",a.body.warning??"Stopped."),k()}return G(w),(a,e)=>{var h,g,V,D;const p=P("RouterLink");return l(),d("div",null,[t("div",de,[t("div",null,[t("h1",null,[C(p,{to:"/jobs",class:"mute"},{default:I(()=>[...e[0]||(e[0]=[i("Jobs",-1)])]),_:1}),e[1]||(e[1]=t("span",{class:"mute"}," / ",-1)),i(u(s.value?v(Q)(s.value.action):"…"),1)]),t("div",ce,[s.value?(l(),d(j,{key:0},[t("code",null,u(s.value.stack),1),i(" · started "+u(v(E)(s.value.startedAt))+" ",1),s.value.endedAt?(l(),d(j,{key:0},[i(" · took "+u(v(X)(s.value.startedAt,s.value.endedAt)),1)],64)):c("",!0)],64)):(l(),d(j,{key:1},[i(u(n.jobId),1)],64))])]),e[3]||(e[3]=t("span",{class:"grow"},null,-1)),s.value?(l(),L(Z,{key:0,state:s.value.state},null,8,["state"])):c("",!0),((h=s.value)==null?void 0:h.state)==="running"?(l(),L(ee,{key:1,variant:"danger",pending:$.value,confirm:"Stop it? Nothing is undone.",title:"Kills the command in flight. Anything already created or destroyed stays that way.",onRun:W},{default:I(()=>[...e[2]||(e[2]=[i(" Stop ",-1)])]),_:1},8,["pending"])):c("",!0)]),((g=s.value)==null?void 0:g.state)==="cancelled"?(l(),d("section",pe,[t("b",null,"Stopped by "+u(s.value.cancelledBy??"an operator")+" — nothing was undone.",1),e[4]||(e[4]=t("p",null,[i(" Whatever this job had already created or destroyed is still that way. Run "),t("b",null,"Verify"),i(" on the deployment to see what actually exists. ")],-1))])):c("",!0),m.value?(l(),L(te,{key:1,text:m.value},null,8,["text"])):c("",!0),A.value.length?(l(),d("section",ve,[e[5]||(e[5]=t("strong",null,"Leaked:",-1)),i(" "+u(A.value.join(", "))+" ",1),e[6]||(e[6]=t("div",{class:"mute"},[i(" These axes' "),t("code",null,"assert_gone"),i(" failed, so the resources are still present. They will not be retried — tear them down by hand, then re-run "),t("code",null,"verify"),i(". ")],-1))])):c("",!0),(V=s.value)!=null&&V.error?(l(),d("section",me,[e[7]||(e[7]=t("strong",{class:"bad"},"The job crashed",-1)),t("pre",fe,u(s.value.error),1)])):c("",!0),t("section",ge,[e[8]||(e[8]=t("div",{class:"phead"},[t("h2",{class:"phead-title"},"Log")],-1)),C(Y,{rows:F.value,live:_.value,"empty-text":x.value?"Loading…":"No output."},{actions:I(()=>{var U;return[!_.value&&((U=s.value)==null?void 0:U.state)==="running"?(l(),d("button",{key:0,class:"ghost sm",onClick:T}," Reconnect ")):c("",!0)]}),_:1},8,["rows","live","empty-text"])]),(D=s.value)!=null&&D.outcome?(l(),d("section",ye,[t("div",be,[e[11]||(e[11]=t("h2",{class:"phead-title"},"Steps",-1)),B.value?(l(),d("span",ke,[i(u(B.value)+" unverifiable — no ",1),e[9]||(e[9]=t("code",null,"assert_gone",-1)),e[10]||(e[10]=i(", so nothing was checked ",-1))])):c("",!0)]),C(ue,{steps:s.value.outcome.steps},null,8,["steps"])])):c("",!0)])}}});export{Se as default};
2
- //# sourceMappingURL=JobDetailView-BsCnnkPz.js.map
@@ -1,2 +0,0 @@
1
- import{s as S}from"./useFormat-DVXTfIS-.js";import{d as U,H as A,p as D,c as o,x as w,g as u,F as h,a as t,s as p,f as a,t as r,e as k,w as f,_ as P,r as $,C as g,j as B,o as n,D as I,E as R,u as y,A as E,B as V}from"./index-DVCVnKbf.js";import{d as b,v as F}from"./useDeployment-752fTjLE.js";import{_ as K}from"./SkeletonList.vue_vue_type_script_setup_true_lang-BYHXRCNR.js";import{_ as W}from"./ErrorNote.vue_vue_type_script_setup_true_lang-BFuRxw1l.js";import"./useVars-VN9gYdR6.js";const H={key:0,class:"banner warn"},M={key:0,class:"banner ok"},O={class:"panel"},j={class:"phead"},Q={class:"mute",style:{"font-size":"var(--t-sm)"}},q={key:0,class:"cards"},G={class:"stagger"},J={"data-label":"url"},X=["href"],Y={key:1,class:"mono mute"},Z={key:0,class:"mute"},ee={"data-label":"forwards to"},te={key:0,class:"mono"},se={key:1,class:"s-failed"},ne={class:"mute",style:{"font-size":"var(--t-sm)"}},oe={"data-label":"router"},re={key:0,class:"mute",style:{"font-size":"var(--t-sm)"}},ae={"data-label":"tls"},le={key:0,class:"badge ok"},ie={key:1,class:"badge off"},de={key:1,class:"banner warn"},ue={class:"panel"},he={class:"phead"},me={class:"mute",style:{"font-size":"var(--t-sm)"}},pe={key:0,class:"cards"},ce={class:"stagger"},fe={class:"name","data-label":"service"},ve={class:"mute",style:{"font-size":"var(--t-sm)"}},ke={"data-label":"state"},ge={"data-label":"ports"},ye={class:"mono"},be={key:0,class:"mute"},_e={key:0,class:"mute"},we={"data-label":"networks"},$e={key:0,class:"mute mono",style:{"font-size":"var(--t-sm)"}},Re={class:"dim","data-label":"image"},Ce={class:"row-actions","data-label":""},Te=["href"],Se={key:1,class:"mute"},Ae=U({__name:"RuntimeTab",setup(Pe){const l=$(null),v=$(""),_=$(!0);async function C(){const i=await E.get(`/api/deployments/${encodeURIComponent(b.id)}/runtime${F.value}`);if(_.value=!1,!i.ok){v.value=V(i,"inspect what is running");return}v.value="",l.value=i.body}A(()=>b.id,()=>{_.value=!0,C()},{immediate:!0}),D(()=>void C(),8e3);const N=g(()=>{var i;return((i=l.value)==null?void 0:i.findings.filter(e=>e.level==="error"))??[]}),x=g(()=>{var i;return((i=l.value)==null?void 0:i.findings.filter(e=>e.level==="warn"))??[]}),L=g(()=>{var i;return((i=l.value)==null?void 0:i.findings.filter(e=>e.level==="info"))??[]}),z=g(()=>{var i;return((i=l.value)==null?void 0:i.containers.filter(e=>e.state==="running").length)??0});function T(i){var c;const e=(c=l.value)==null?void 0:c.routes.filter(s=>s.container===i.name).flatMap(s=>s.hosts).find(s=>!s.startsWith("(pattern)"));return e?`https://${e}`:null}return(i,e)=>{const c=B("RouterLink");return n(),o("div",null,[v.value?(n(),w(W,{key:0,text:v.value,title:"Could not inspect this deployment."},null,8,["text"])):u("",!0),_.value&&!l.value?(n(),w(K,{key:1,rows:5})):l.value?(n(),o(h,{key:2},[l.value.reachable?(n(),o(h,{key:1},[(n(!0),o(h,null,p(N.value,(s,m)=>(n(),o("div",{key:`e${m}`,class:"banner failed"},[e[1]||(e[1]=t("b",null,"Blocking",-1)),t("p",null,r(s.message),1)]))),128)),(n(!0),o(h,null,p(x.value,(s,m)=>(n(),o("div",{key:`w${m}`,class:"banner warn"},[e[2]||(e[2]=t("b",null,"Worth checking",-1)),t("p",null,r(s.message),1)]))),128)),(n(!0),o(h,null,p(L.value,(s,m)=>(n(),o("div",{key:`i${m}`,class:"banner plain"},[t("p",null,r(s.message),1)]))),128)),!l.value.findings.length&&l.value.containers.length?(n(),o("div",M,[...e[3]||(e[3]=[t("b",null,"Nothing looks wrong.",-1),t("p",null," Every container is attached to the ingress network and every router has a rule and a port. If a hostname still fails, the next suspects are DNS and the certificate. ",-1)])])):u("",!0),t("section",O,[t("div",j,[e[5]||(e[5]=t("h2",{class:"section"},"Routing",-1)),e[6]||(e[6]=t("span",{class:"grow"},null,-1)),t("span",Q,[a(" TLS: "+r(l.value.challenge==="unknown"?"unknown":l.value.challenge)+" ",1),k(P,{label:"why the TLS mode matters here",align:"end"},{default:f(()=>[...e[4]||(e[4]=[a(" Under HTTP-01 every per-PR router needs ",-1),t("code",null,"tls.certresolver=le",-1),a(", because each hostname resolves its own certificate. Under DNS-01 that inverts — one always-on router holds the wildcard, and a per-PR certresolver makes each PR order its own and burn the weekly limit. Read from the running Traefik's own flags, not configured here. ",-1)])]),_:1})])]),l.value.routes.length?(n(),o("table",q,[t("thead",null,[t("tr",null,[e[9]||(e[9]=t("th",null,"URL",-1)),t("th",null,[e[8]||(e[8]=a(" Forwards to ",-1)),k(P,{label:"how a URL reaches a port"},{default:f(()=>[...e[7]||(e[7]=[a(" Traefik's docker provider resolves the container's IP on ",-1),t("code",null,"preview-ingress",-1),a(" and dials it on the port from ",-1),t("code",null,"loadbalancer.server.port",-1),a(" — it does ",-1),t("em",null,"not",-1),a(" use ",-1),t("code",null,"service:port",-1),a(" DNS. So the port here is the one inside the container, and publishing a host port is unnecessary. ",-1)])]),_:1})]),e[10]||(e[10]=t("th",null,"Router",-1)),e[11]||(e[11]=t("th",null,"TLS",-1))])]),t("tbody",G,[(n(!0),o(h,null,p(l.value.routes,(s,m)=>(n(),o("tr",{key:s.router,style:I({"--i":m})},[t("td",J,[(n(!0),o(h,null,p(s.hosts,d=>(n(),o("div",{key:d},[d.startsWith("(pattern)")?(n(),o("span",Y,r(d),1)):(n(),o("a",{key:0,href:`https://${d}`,target:"_blank",rel:"noreferrer"},r(d),9,X))]))),128)),s.hosts.length?u("",!0):(n(),o("span",Z,"no host in the rule"))]),t("td",ee,[s.target?(n(),o("span",te,r(s.target),1)):(n(),o("span",se,r(s.port===null?"no port declared":"not on the ingress network"),1)),t("div",ne,r(s.container),1)]),t("td",oe,[a(r(s.router)+" ",1),s.priority?(n(),o("div",re," priority "+r(s.priority),1)):u("",!0)]),t("td",ae,[s.tls?(n(),o("span",le,r(s.certresolver||"inherited"),1)):(n(),o("span",ie,"off"))])],4))),128))])])):(n(),o("div",de,[e[17]||(e[17]=t("b",null,"No routes. Traefik has not been told about this deployment.",-1)),t("p",null,[e[13]||(e[13]=a(" Routes come from labels in ",-1)),e[14]||(e[14]=t("em",null,"your",-1)),e[15]||(e[15]=a(" compose file, not from pstack and not from the ",-1)),k(c,{to:"/routing"},{default:f(()=>[...e[12]||(e[12]=[a("Routing",-1)])]),_:1}),e[16]||(e[16]=a(" page (that is Traefik's file provider — middleware and TLS options, not per-PR routers). A reachable service needs four things: ",-1))]),e[18]||(e[18]=t("pre",{class:"code"},"labels:\n - traefik.enable=true # the host runs exposedbydefault=false\n - traefik.docker.network=preview-ingress # which network Traefik should dial\n - traefik.http.routers.<name>-${STACK}.rule=Host(`<name>-${STACK}.${PREVIEW_DOMAIN}`)\n - traefik.http.services.<name>-${STACK}.loadbalancer.server.port=80\nnetworks: [default, preview-ingress] # and preview-ingress: { external: true }",-1)),e[19]||(e[19]=t("p",{class:"mute"},[a(" Put "),t("code",null,"${STACK}"),a(" in the router and service names: Traefik's namespace is global across the host, so two deployments sharing a router name serve each other's containers. ")],-1))]))]),t("section",ue,[t("div",he,[e[20]||(e[20]=t("h2",{class:"section"},"Containers",-1)),e[21]||(e[21]=t("span",{class:"grow"},null,-1)),t("span",me,r(z.value)+" of "+r(l.value.containers.length)+" running ",1)]),l.value.containers.length?(n(),o("table",pe,[e[24]||(e[24]=t("thead",null,[t("tr",null,[t("th",null,"Service"),t("th",null,"State"),t("th",null,"Ports"),t("th",null,"Networks"),t("th",null,"Image"),t("th",{"aria-label":"Actions"})])],-1)),t("tbody",ce,[(n(!0),o(h,null,p(l.value.containers,(s,m)=>(n(),o("tr",{key:s.id,style:I({"--i":m})},[t("td",fe,[a(r(s.service??s.name)+" ",1),t("div",ve,r(s.name),1)]),t("td",ke,[t("span",{class:R(s.state==="running"?"s-ok":"s-failed")},r(y(S)(s.state)),3),s.health?(n(),o("div",{key:0,class:R(["mute",s.health==="healthy"?"s-ok":"s-failed"]),style:{"font-size":"var(--t-sm)"}},r(y(S)(s.health)),3)):u("",!0)]),t("td",ge,[(n(!0),o(h,null,p(s.ports,d=>(n(),o("div",{key:`${d.containerPort}/${d.protocol}`},[t("span",ye,r(d.containerPort),1),d.hostPort?(n(),o("span",be," ← host "+r(d.hostPort),1)):u("",!0)]))),128)),s.ports.length?u("",!0):(n(),o("span",_e,"none exposed"))]),t("td",we,[(n(!0),o(h,null,p(s.networks,d=>(n(),o("div",{key:d},[t("span",{class:R(d==="preview-ingress"?"s-ok":"")},r(d),3)]))),128)),s.ingressIp?(n(),o("div",$e,r(s.ingressIp),1)):u("",!0)]),t("td",Re,r(s.image),1),t("td",Ce,[k(c,{class:"btn sm ghost",to:`/deployments/${encodeURIComponent(y(b).id)}/logs?container=${encodeURIComponent(s.name)}`},{default:f(()=>[...e[22]||(e[22]=[a(" Logs ",-1)])]),_:1},8,["to"]),s.state==="running"?(n(),w(c,{key:0,class:"btn sm ghost",to:`/deployments/${encodeURIComponent(y(b).id)}/terminal?container=${encodeURIComponent(s.name)}`},{default:f(()=>[...e[23]||(e[23]=[a(" Shell ",-1)])]),_:1},8,["to"])):u("",!0),T(s)?(n(),o("a",{key:1,class:"btn sm ghost",href:T(s),target:"_blank",rel:"noreferrer"}," Open ",8,Te)):u("",!0)])],4))),128))])])):(n(),o("p",Se," No containers for this stack. If you expected some, check that their profile is one the spec selects — a service behind a profile that is not enabled is never started. "))])],64)):(n(),o("div",H,[...e[0]||(e[0]=[t("b",null,"Docker did not answer.",-1),t("p",null," Nothing below could be determined — this is not the same as nothing running. Check the daemon and the socket mount, then refresh. ",-1)])]))],64)):u("",!0)])}}});export{Ae as default};
2
- //# sourceMappingURL=RuntimeTab-B0D_MxFQ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RuntimeTab-B0D_MxFQ.js","sources":["../../src/views/tabs/RuntimeTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * What is actually running, and what Traefik was actually told about it.\n *\n * THE QUESTION THIS ANSWERS. \"The hostname does not work.\" Nothing in this UI could answer it: the\n * registry shows what was *submitted*, the logs show what the container *says*, and the reason a\n * request 404s is in neither — it is in the container's Traefik labels, which pstack never writes.\n *\n * The routing table is the direct answer to \"which URL maps to which port\". Traefik does **not** dial\n * `service_name:port`: its docker provider resolves the container's IP on the ingress network and\n * dials that, on the port from the `loadbalancer.server.port` label. That is why a service must be\n * attached to `preview-ingress`, why the port is the *container-internal* one, and why publishing a\n * host port is unnecessary. The `target` column shows the address Traefik assembled, so a missing\n * half is visible rather than inferred.\n *\n * Findings come first because they are the reason anyone opened this tab.\n */\nimport { computed, ref, watch } from 'vue';\nimport { sentence } from '../../composables/useFormat';\nimport { api, problem } from '../../api/client';\nimport type { RuntimeContainer, RuntimeResponse } from '../../api/types';\nimport { dep, varsQuery } from '../../composables/useDeployment';\nimport { usePolling } from '../../composables/usePolling';\nimport SkeletonList from '../../components/SkeletonList.vue';\nimport ErrorNote from '../../components/ErrorNote.vue';\nimport InfoHint from '../../components/InfoHint.vue';\n\nconst rt = ref<RuntimeResponse | null>(null);\nconst error = ref('');\nconst loading = ref(true);\n\nasync function load(): Promise<void> {\n const r = await api.get<RuntimeResponse>(\n `/api/deployments/${encodeURIComponent(dep.id)}/runtime${varsQuery.value}`,\n );\n loading.value = false;\n if (!r.ok) {\n error.value = problem(r, 'inspect what is running');\n return;\n }\n error.value = '';\n rt.value = r.body;\n}\n\nwatch(() => dep.id, () => { loading.value = true; void load(); }, { immediate: true });\n// Containers change state during a deploy; the poll pauses itself when the tab is hidden.\nusePolling(() => void load(), 8000);\n\nconst errors = computed(() => rt.value?.findings.filter((f) => f.level === 'error') ?? []);\nconst warns = computed(() => rt.value?.findings.filter((f) => f.level === 'warn') ?? []);\nconst infos = computed(() => rt.value?.findings.filter((f) => f.level === 'info') ?? []);\nconst running = computed(() => rt.value?.containers.filter((c) => c.state === 'running').length ?? 0);\n\n/**\n * The hostname Traefik actually routes to this container, or null.\n *\n * Built from the ROUTES rather than guessed from the container: a router's rule is the only thing\n * that decides what URL reaches it, and a link assembled from a service name and a domain would be\n * a plausible-looking 404. Pattern hosts (`HostRegexp`) are skipped for the same reason — there is\n * no single address to send someone to.\n */\nfunction urlFor(c: RuntimeContainer): string | null {\n const host = rt.value?.routes\n .filter((r) => r.container === c.name)\n .flatMap((r) => r.hosts)\n .find((h) => !h.startsWith('(pattern)'));\n return host ? `https://${host}` : null;\n}\n</script>\n\n<template>\n <div>\n <ErrorNote v-if=\"error\" :text=\"error\" title=\"Could not inspect this deployment.\" />\n <SkeletonList v-if=\"loading && !rt\" :rows=\"5\" />\n\n <template v-else-if=\"rt\">\n <!--\n `reachable: false` is not \"nothing is running\". The stack may be perfectly healthy while this\n process cannot reach the Docker socket, and reporting that as empty is how a UI tells someone\n their live preview is gone.\n -->\n <div v-if=\"!rt.reachable\" class=\"banner warn\">\n <b>Docker did not answer.</b>\n <p>\n Nothing below could be determined — this is not the same as nothing running. Check the\n daemon and the socket mount, then refresh.\n </p>\n </div>\n\n <template v-else>\n <!-- Findings first: this tab exists because something is not working. -->\n <div v-for=\"(f, i) in errors\" :key=\"`e${i}`\" class=\"banner failed\">\n <b>Blocking</b>\n <p>{{ f.message }}</p>\n </div>\n <div v-for=\"(f, i) in warns\" :key=\"`w${i}`\" class=\"banner warn\">\n <b>Worth checking</b>\n <p>{{ f.message }}</p>\n </div>\n <div v-for=\"(f, i) in infos\" :key=\"`i${i}`\" class=\"banner plain\">\n <p>{{ f.message }}</p>\n </div>\n <div v-if=\"!rt.findings.length && rt.containers.length\" class=\"banner ok\">\n <b>Nothing looks wrong.</b>\n <p>\n Every container is attached to the ingress network and every router has a rule and a port.\n If a hostname still fails, the next suspects are DNS and the certificate.\n </p>\n </div>\n\n <!-- ============================ routing ============================ -->\n <section class=\"panel\">\n <div class=\"phead\">\n <h2 class=\"section\">Routing</h2>\n <span class=\"grow\" />\n <span class=\"mute\" style=\"font-size: var(--t-sm)\">\n TLS: {{ rt.challenge === 'unknown' ? 'unknown' : rt.challenge }}\n <InfoHint label=\"why the TLS mode matters here\" align=\"end\">\n Under HTTP-01 every per-PR router needs <code>tls.certresolver=le</code>, because each\n hostname resolves its own certificate. Under DNS-01 that inverts — one always-on router\n holds the wildcard, and a per-PR certresolver makes each PR order its own and burn the\n weekly limit. Read from the running Traefik's own flags, not configured here.\n </InfoHint>\n </span>\n </div>\n\n <table v-if=\"rt.routes.length\" class=\"cards\">\n <thead>\n <tr>\n <th>URL</th>\n <th>\n Forwards to\n <InfoHint label=\"how a URL reaches a port\">\n Traefik's docker provider resolves the container's IP on\n <code>preview-ingress</code> and dials it on the port from\n <code>loadbalancer.server.port</code> — it does <em>not</em> use\n <code>service:port</code> DNS. So the port here is the one inside the container,\n and publishing a host port is unnecessary.\n </InfoHint>\n </th>\n <th>Router</th>\n <th>TLS</th>\n </tr>\n </thead>\n <tbody class=\"stagger\">\n <tr v-for=\"(r, i) in rt.routes\" :key=\"r.router\" :style=\"{ '--i': i }\">\n <td data-label=\"url\">\n <div v-for=\"h in r.hosts\" :key=\"h\">\n <a v-if=\"!h.startsWith('(pattern)')\" :href=\"`https://${h}`\" target=\"_blank\" rel=\"noreferrer\">\n {{ h }}\n </a>\n <span v-else class=\"mono mute\">{{ h }}</span>\n </div>\n <span v-if=\"!r.hosts.length\" class=\"mute\">no host in the rule</span>\n </td>\n <td data-label=\"forwards to\">\n <span v-if=\"r.target\" class=\"mono\">{{ r.target }}</span>\n <span v-else class=\"s-failed\">\n {{ r.port === null ? 'no port declared' : 'not on the ingress network' }}\n </span>\n <div class=\"mute\" style=\"font-size: var(--t-sm)\">{{ r.container }}</div>\n </td>\n <td data-label=\"router\">\n {{ r.router }}\n <div v-if=\"r.priority\" class=\"mute\" style=\"font-size: var(--t-sm)\">\n priority {{ r.priority }}\n </div>\n </td>\n <td data-label=\"tls\">\n <span v-if=\"r.tls\" class=\"badge ok\">{{ r.certresolver || 'inherited' }}</span>\n <span v-else class=\"badge off\">off</span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!--\n The reported failure, stated where it will be looked for. pstack does not write these\n labels — the deployment's own compose file does — so the fix is in the user's file.\n -->\n <div v-else class=\"banner warn\">\n <b>No routes. Traefik has not been told about this deployment.</b>\n <p>\n Routes come from labels in <em>your</em> compose file, not from pstack and not from the\n <RouterLink to=\"/routing\">Routing</RouterLink> page (that is Traefik's file provider —\n middleware and TLS options, not per-PR routers). A reachable service needs four things:\n </p>\n <pre class=\"code\">labels:\n - traefik.enable=true # the host runs exposedbydefault=false\n - traefik.docker.network=preview-ingress # which network Traefik should dial\n - traefik.http.routers.&lt;name&gt;-${STACK}.rule=Host(`&lt;name&gt;-${STACK}.${PREVIEW_DOMAIN}`)\n - traefik.http.services.&lt;name&gt;-${STACK}.loadbalancer.server.port=80\nnetworks: [default, preview-ingress] # and preview-ingress: { external: true }</pre>\n <p class=\"mute\">\n Put <code>${STACK}</code> in the router and service names: Traefik's namespace is global\n across the host, so two deployments sharing a router name serve each other's containers.\n </p>\n </div>\n </section>\n\n <!-- ============================ containers ============================ -->\n <section class=\"panel\">\n <div class=\"phead\">\n <h2 class=\"section\">Containers</h2>\n <span class=\"grow\" />\n <span class=\"mute\" style=\"font-size: var(--t-sm)\">\n {{ running }} of {{ rt.containers.length }} running\n </span>\n </div>\n\n <table v-if=\"rt.containers.length\" class=\"cards\">\n <thead>\n <tr>\n <th>Service</th>\n <th>State</th>\n <th>Ports</th>\n <th>Networks</th>\n <th>Image</th>\n <th aria-label=\"Actions\" />\n </tr>\n </thead>\n <tbody class=\"stagger\">\n <tr v-for=\"(c, i) in rt.containers\" :key=\"c.id\" :style=\"{ '--i': i }\">\n <td class=\"name\" data-label=\"service\">\n {{ c.service ?? c.name }}\n <div class=\"mute\" style=\"font-size: var(--t-sm)\">{{ c.name }}</div>\n </td>\n <td data-label=\"state\">\n <span :class=\"c.state === 'running' ? 's-ok' : 's-failed'\">{{ sentence(c.state) }}</span>\n <div\n v-if=\"c.health\"\n class=\"mute\"\n style=\"font-size: var(--t-sm)\"\n :class=\"c.health === 'healthy' ? 's-ok' : 's-failed'\"\n >\n {{ sentence(c.health) }}\n </div>\n </td>\n <td data-label=\"ports\">\n <div v-for=\"p in c.ports\" :key=\"`${p.containerPort}/${p.protocol}`\">\n <span class=\"mono\">{{ p.containerPort }}</span>\n <span v-if=\"p.hostPort\" class=\"mute\"> ← host {{ p.hostPort }}</span>\n </div>\n <span v-if=\"!c.ports.length\" class=\"mute\">none exposed</span>\n </td>\n <td data-label=\"networks\">\n <div v-for=\"n in c.networks\" :key=\"n\">\n <span :class=\"n === 'preview-ingress' ? 's-ok' : ''\">{{ n }}</span>\n </div>\n <div v-if=\"c.ingressIp\" class=\"mute mono\" style=\"font-size: var(--t-sm)\">\n {{ c.ingressIp }}\n </div>\n </td>\n <td class=\"dim\" data-label=\"image\">{{ c.image }}</td>\n <!--\n The three things anyone wants from a container row, without hunting for the tab\n that does it. Open goes to the URL Traefik actually assembled for THIS container\n (see `urlFor`), so it is absent rather than wrong when no router points here.\n -->\n <td class=\"row-actions\" data-label=\"\">\n <RouterLink\n class=\"btn sm ghost\"\n :to=\"`/deployments/${encodeURIComponent(dep.id)}/logs?container=${encodeURIComponent(c.name)}`\"\n >\n Logs\n </RouterLink>\n <RouterLink\n v-if=\"c.state === 'running'\"\n class=\"btn sm ghost\"\n :to=\"`/deployments/${encodeURIComponent(dep.id)}/terminal?container=${encodeURIComponent(c.name)}`\"\n >\n Shell\n </RouterLink>\n <a\n v-if=\"urlFor(c)\"\n class=\"btn sm ghost\"\n :href=\"urlFor(c)!\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n Open\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <p v-else class=\"mute\">\n No containers for this stack. If you expected some, check that their profile is one the\n spec selects — a service behind a profile that is not enabled is never started.\n </p>\n </section>\n </template>\n </template>\n </div>\n</template>\n"],"names":["rt","ref","error","loading","load","r","api","dep","varsQuery","problem","watch","usePolling","errors","computed","_a","f","warns","infos","running","c","urlFor","host","h","_createElementBlock","_createBlock","ErrorNote","SkeletonList","_Fragment","_openBlock","_renderList","i","_cache","_createElementVNode","_toDisplayString","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_createVNode","InfoHint","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_10","_hoisted_9","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_component_RouterLink","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_createTextVNode","_hoisted_28","_hoisted_29","_normalizeClass","_unref","sentence","_hoisted_30","p","_hoisted_31","_hoisted_32","_hoisted_33","_hoisted_34","n","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","_hoisted_1"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{d as _,c as d,a as t,i as f,v as x,u as s,R as o,f as n,e as k,w as v,_ as c,a3 as B,t as u,g as m,x as E,F as S,y as l,r as y,C as g,a5 as L,N,o as r}from"./index-DVCVnKbf.js";import{_ as T}from"./ActionButton.vue_vue_type_script_setup_true_lang-D06apM-6.js";import{_ as b}from"./SelectMenu.vue_vue_type_script_setup_true_lang-qGkx2MwW.js";import"./Teleport-d5PoAxFM.js";const U={class:"panel settings-form"},$={class:"field"},A={class:"mute hint"},D={class:"field"},R=["type"],F={class:"row",style:{"margin-top":"var(--s2)"}},M={key:2,class:"mute",style:{"font-size":"var(--t-xs)"}},q={class:"mute hint"},H={class:"row",style:{gap:"8px","margin-top":"4px"}},I=["disabled"],P={key:0,class:"mute"},j={key:1,class:"bad"},z={class:"panel"},O={class:"field"},W={class:"field"},Y=_({__name:"SettingsView",setup(G){const p=y(!1),h=y(!1),w=g(()=>{const i=o.token;return i.startsWith("pstack_pat_")?"Looks like a personal token.":/^[0-9a-f]{40,}$/i.test(i)?"Looks like a machine token.":`${i.length} characters — this does not look like a pstack token.`}),C=g(()=>{var i;return((i=l.health)==null?void 0:i.authEnforced)??null});async function V(){p.value=!0;try{await L(),N(l.healthError?"error":"ok",l.healthError||"Control plane reachable")}finally{p.value=!1}}return(i,e)=>(r(),d("div",null,[e[19]||(e[19]=t("div",{class:"page-head"},[t("div",null,[t("h1",null,"Settings"),t("div",{class:"sub"},"Stored in this browser only. Nothing here is sent to the server.")])],-1)),t("section",U,[e[14]||(e[14]=t("h2",{class:"phead-title"},"Connection",-1)),t("div",$,[e[8]||(e[8]=t("label",{for:"apiBase"},"API base URL",-1)),f(t("input",{id:"apiBase","onUpdate:modelValue":e[0]||(e[0]=a=>s(o).apiBase=a),type:"url",spellcheck:"false",placeholder:"(same origin)"},null,512),[[x,s(o).apiBase,void 0,{trim:!0}]]),t("div",A,[e[7]||(e[7]=n(" Leave it empty unless you are pointing this page at another host. ",-1)),k(c,{label:"about the API base URL"},{default:v(()=>[...e[6]||(e[6]=[n(" Empty means same-origin, which is how the control stack serves this page — the browser never makes a cross-origin request, so there is no CORS to configure. Set an absolute URL only for a development session against a remote host. ",-1)])]),_:1})])]),t("div",D,[e[11]||(e[11]=t("label",{for:"token"},"Access token",-1)),f(t("input",{id:"token","onUpdate:modelValue":e[1]||(e[1]=a=>s(o).token=a),type:h.value||!s(o).token?"text":"password",name:"pstack-access-token",autocomplete:"off","data-1p-ignore":"","data-lpignore":"true","data-bwignore":"","data-form-type":"other",spellcheck:"false",placeholder:"paste the token from pstack init"},null,8,R),[[B,s(o).token,void 0,{trim:!0}]]),t("div",F,[s(o).token?(r(),d("button",{key:0,class:"ghost sm",onClick:e[2]||(e[2]=a=>h.value=!h.value)},u(h.value?"Hide":"Show"),1)):m("",!0),s(o).token?(r(),E(T,{key:1,variant:"ghost",class:"sm",confirm:"Clear it?",onRun:e[3]||(e[3]=a=>s(o).token="")},{default:v(()=>[...e[9]||(e[9]=[n(" Clear ",-1)])]),_:1})):m("",!0),s(o).token?(r(),d("span",M,u(w.value),1)):m("",!0)]),t("div",q,[e[10]||(e[10]=n(" Authenticates every request — signing in with an account does the same job, so set this only for token-based access (the machine token, or a personal token from your account). ",-1)),C.value===!1?(r(),d(S,{key:0},[n(" This server is not asking for one — it only accepts connections from this machine, so a token here is harmless but unnecessary. ")],64)):m("",!0)])]),t("div",H,[t("button",{class:"btn",disabled:p.value,onClick:V},u(p.value?"Checking…":"Test connection"),9,I),s(l).health?(r(),d("span",P,[n(" Connected to pstack "+u(s(l).health.version)+" ",1),k(c,{label:"where data is stored"},{default:v(()=>[e[12]||(e[12]=n(" Deployment records and stored specs live in ",-1)),t("code",null,u(s(l).health.dataDir),1),e[13]||(e[13]=n(" on the host. ",-1))]),_:1})])):s(l).healthError?(r(),d("span",j,u(s(l).healthError),1)):m("",!0)])]),t("section",z,[e[18]||(e[18]=t("h2",{class:"phead-title"},"Appearance",-1)),t("div",O,[e[15]||(e[15]=t("label",{for:"theme"},"Theme",-1)),k(b,{modelValue:s(o).theme,"onUpdate:modelValue":e[4]||(e[4]=a=>s(o).theme=a),id:"theme",label:"Theme",options:[{value:"system",label:"Follow the system"},{value:"dark",label:"Dark"},{value:"light",label:"Light"}]},null,8,["modelValue"])]),t("div",W,[e[16]||(e[16]=t("label",{for:"motion"},"Motion",-1)),k(b,{modelValue:s(o).motion,"onUpdate:modelValue":e[5]||(e[5]=a=>s(o).motion=a),id:"motion",label:"Motion",options:[{value:"system",label:"Follow the system"},{value:"full",label:"Always animate"},{value:"none",label:"No animation"}]},null,8,["modelValue"]),e[17]||(e[17]=t("div",{class:"mute hint"}," “Follow the system” uses your device's reduce-motion setting. The other two override it, in either direction. ",-1))])])]))}});export{Y as default};
2
- //# sourceMappingURL=SettingsView-Cu1Zkufg.js.map