@knpkv/control-center 0.1.1 → 0.2.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 (115) hide show
  1. package/dist/client/.vite/manifest.json +127 -125
  2. package/dist/client/assets/{ActiveWorkPage-Bvji6Tys.js → ActiveWorkPage-C6QwDV1Y.js} +2 -2
  3. package/dist/client/assets/{ActiveWorkPage-Bvji6Tys.js.map → ActiveWorkPage-C6QwDV1Y.js.map} +1 -1
  4. package/dist/client/assets/{AgentPage-Bom41la2.js → AgentPage-OqwP6uRi.js} +2 -2
  5. package/dist/client/assets/{AgentPage-Bom41la2.js.map → AgentPage-OqwP6uRi.js.map} +1 -1
  6. package/dist/client/assets/{AtlassianOAuthCallbackPage-dr-B9-DD.js → AtlassianOAuthCallbackPage-Bm7UhU-h.js} +2 -2
  7. package/dist/client/assets/{AtlassianOAuthCallbackPage-dr-B9-DD.js.map → AtlassianOAuthCallbackPage-Bm7UhU-h.js.map} +1 -1
  8. package/dist/client/assets/{AuthorizedSharePage-C-P-PNfm.js → AuthorizedSharePage-DblP3USU.js} +2 -2
  9. package/dist/client/assets/{AuthorizedSharePage-C-P-PNfm.js.map → AuthorizedSharePage-DblP3USU.js.map} +1 -1
  10. package/dist/client/assets/{BrowserCrypto-BPiuCuW-.js → BrowserCrypto-BGGRC6iW.js} +2 -2
  11. package/dist/client/assets/{BrowserCrypto-BPiuCuW-.js.map → BrowserCrypto-BGGRC6iW.js.map} +1 -1
  12. package/dist/client/assets/{BrowserSessionHydrator-C8F_LGhg.js → BrowserSessionHydrator-39UDbHf0.js} +2 -2
  13. package/dist/client/assets/{BrowserSessionHydrator-C8F_LGhg.js.map → BrowserSessionHydrator-39UDbHf0.js.map} +1 -1
  14. package/dist/client/assets/{CommandSearch-D6iFcdvj.js → CommandSearch-D4Fnjsl-.js} +2 -2
  15. package/dist/client/assets/{CommandSearch-D6iFcdvj.js.map → CommandSearch-D4Fnjsl-.js.map} +1 -1
  16. package/dist/client/assets/{ItemsPage-x39U11an.js → ItemsPage-BZQiTh9b.js} +2 -2
  17. package/dist/client/assets/{ItemsPage-x39U11an.js.map → ItemsPage-BZQiTh9b.js.map} +1 -1
  18. package/dist/client/assets/{PairPage-D5vlz4OG.js → PairPage-Dvfse5ky.js} +2 -2
  19. package/dist/client/assets/{PairPage-D5vlz4OG.js.map → PairPage-Dvfse5ky.js.map} +1 -1
  20. package/dist/client/assets/{PortfolioOverview-DSUabOmm.js → PortfolioOverview-DeJcfXcw.js} +2 -2
  21. package/dist/client/assets/{PortfolioOverview-DSUabOmm.js.map → PortfolioOverview-DeJcfXcw.js.map} +1 -1
  22. package/dist/client/assets/{ReleaseRoute-DyDm5s85.js → ReleaseRoute-D9q8_sCk.js} +2 -2
  23. package/dist/client/assets/{ReleaseRoute-DyDm5s85.js.map → ReleaseRoute-D9q8_sCk.js.map} +1 -1
  24. package/dist/client/assets/ReleaseWorkset-Bs_FXlbC.js +2 -0
  25. package/dist/client/assets/ReleaseWorkset-Bs_FXlbC.js.map +1 -0
  26. package/dist/client/assets/{ServicesPage-CoMVH2Si.js → ServicesPage-Co6GJHgc.js} +2 -2
  27. package/dist/client/assets/{ServicesPage-CoMVH2Si.js.map → ServicesPage-Co6GJHgc.js.map} +1 -1
  28. package/dist/client/assets/{TimelinePage-D8Fj_351.js → TimelinePage-CM2og72M.js} +2 -2
  29. package/dist/client/assets/{TimelinePage-D8Fj_351.js.map → TimelinePage-CM2og72M.js.map} +1 -1
  30. package/dist/client/assets/{WorkspaceEntityLink-BRifdJHn.js → WorkspaceEntityLink-CTw_He-8.js} +2 -2
  31. package/dist/client/assets/{WorkspaceEntityLink-BRifdJHn.js.map → WorkspaceEntityLink-CTw_He-8.js.map} +1 -1
  32. package/dist/client/assets/{WorkspaceEntityRoute-BwpujSqk.css → WorkspaceEntityRoute-Bdmv6ki4.css} +1 -1
  33. package/dist/client/assets/WorkspaceEntityRoute-BnsmaeS2.js +30 -0
  34. package/dist/client/assets/WorkspaceEntityRoute-BnsmaeS2.js.map +1 -0
  35. package/dist/client/assets/{WorkspaceReleaseLayout-DDLdY0rU.js → WorkspaceReleaseLayout-Ba6keUju.js} +2 -2
  36. package/dist/client/assets/{WorkspaceReleaseLayout-DDLdY0rU.js.map → WorkspaceReleaseLayout-Ba6keUju.js.map} +1 -1
  37. package/dist/client/assets/{authenticatedMutationClient-D5yD0rch.js → authenticatedMutationClient-CJklf3iD.js} +2 -2
  38. package/dist/client/assets/{authenticatedMutationClient-D5yD0rch.js.map → authenticatedMutationClient-CJklf3iD.js.map} +1 -1
  39. package/dist/client/assets/{authorizedShareTransport-Dyj53kJf.js → authorizedShareTransport-Xg20SHJ5.js} +2 -2
  40. package/dist/client/assets/{authorizedShareTransport-Dyj53kJf.js.map → authorizedShareTransport-Xg20SHJ5.js.map} +1 -1
  41. package/dist/client/assets/{client-OmvShYdC.js → client-P7FaPwzJ.js} +2 -2
  42. package/dist/client/assets/{client-OmvShYdC.js.map → client-P7FaPwzJ.js.map} +1 -1
  43. package/dist/client/assets/{connectionTestTransport-c25QXx7A.js → connectionTestTransport-C9iPOkKM.js} +2 -2
  44. package/dist/client/assets/{connectionTestTransport-c25QXx7A.js.map → connectionTestTransport-C9iPOkKM.js.map} +1 -1
  45. package/dist/client/assets/{index-DLoOV6Hp.js → index-DpW_ckau.js} +3 -3
  46. package/dist/client/assets/{index-DLoOV6Hp.js.map → index-DpW_ckau.js.map} +1 -1
  47. package/dist/client/assets/{patterns-DEbg7R-A-Cw0A2uKS.js → patterns-DEbg7R-A-BhsMyh9D.js} +2 -2
  48. package/dist/client/assets/{patterns-DEbg7R-A-Cw0A2uKS.js.map → patterns-DEbg7R-A-BhsMyh9D.js.map} +1 -1
  49. package/dist/client/assets/{presentPortfolio-CkRqd_zz.js → presentPortfolio-D-v615bg.js} +2 -2
  50. package/dist/client/assets/{presentPortfolio-CkRqd_zz.js.map → presentPortfolio-D-v615bg.js.map} +1 -1
  51. package/dist/client/assets/presentWorkspaceItems-IAp5SnpB.js +2 -0
  52. package/dist/client/assets/presentWorkspaceItems-IAp5SnpB.js.map +1 -0
  53. package/dist/client/assets/{releaseRoutes-DSWkTh-E.js → releaseRoutes-CfaNElmG.js} +2 -2
  54. package/dist/client/assets/{releaseRoutes-DSWkTh-E.js.map → releaseRoutes-CfaNElmG.js.map} +1 -1
  55. package/dist/client/assets/{serviceOnboarding-B9pvdyWf.js → serviceOnboarding-BZ2U_Ugw.js} +2 -2
  56. package/dist/client/assets/{serviceOnboarding-B9pvdyWf.js.map → serviceOnboarding-BZ2U_Ugw.js.map} +1 -1
  57. package/dist/client/assets/{useWorkspaceItems-D0FuHGto.js → useWorkspaceItems-DQTYaq1R.js} +2 -2
  58. package/dist/client/assets/{useWorkspaceItems-D0FuHGto.js.map → useWorkspaceItems-DQTYaq1R.js.map} +1 -1
  59. package/dist/client/assets/workspaceEntityRoutes-Biw67kRR.js +2 -0
  60. package/dist/client/assets/workspaceEntityRoutes-Biw67kRR.js.map +1 -0
  61. package/dist/client/build-graph.json +32 -2
  62. package/dist/client/index.html +1 -1
  63. package/dist/server/api/deliveryGraph.d.ts +126 -0
  64. package/dist/server/api/deliveryGraph.d.ts.map +1 -1
  65. package/dist/server/api/index.js +2 -2
  66. package/dist/server/api/shares.d.ts +14 -0
  67. package/dist/server/api/shares.d.ts.map +1 -1
  68. package/dist/server/assets/{BindConfig-BjK10BEb.js → BindConfig-Dk3vjYZc.js} +7 -7
  69. package/dist/server/assets/{BindConfig-BjK10BEb.js.map → BindConfig-Dk3vjYZc.js.map} +1 -1
  70. package/dist/server/assets/{controlCenterApi-tZPvRPRH.js → controlCenterApi-Dsug4Rzi.js} +2 -2
  71. package/dist/server/assets/{controlCenterApi-tZPvRPRH.js.map → controlCenterApi-Dsug4Rzi.js.map} +1 -1
  72. package/dist/server/assets/{domainEvent-x7yeq-gU.js → domainEvent-CZLKMqgn.js} +13 -2
  73. package/dist/server/assets/{domainEvent-x7yeq-gU.js.map → domainEvent-CZLKMqgn.js.map} +1 -1
  74. package/dist/server/assets/{firstPartyServices-Y41P8uke.js → firstPartyServices-BWdrzdfD.js} +2 -2
  75. package/dist/server/assets/{firstPartyServices-Y41P8uke.js.map → firstPartyServices-BWdrzdfD.js.map} +1 -1
  76. package/dist/server/assets/{manualPluginSynchronization-C0aHB4UM.js → manualPluginSynchronization-CDnX9qXi.js} +64 -23
  77. package/dist/server/assets/manualPluginSynchronization-CDnX9qXi.js.map +1 -0
  78. package/dist/server/assets/{portfolioInvalidation-Ds9Xm4_P.js → portfolioInvalidation-iTUo5N4r.js} +26 -6
  79. package/dist/server/assets/portfolioInvalidation-iTUo5N4r.js.map +1 -0
  80. package/dist/server/assets/{readiness-CzU3vjYQ.js → readiness-CZBkcjLP.js} +2 -2
  81. package/dist/server/assets/{readiness-CzU3vjYQ.js.map → readiness-CZBkcjLP.js.map} +1 -1
  82. package/dist/server/build-graph.json +1 -0
  83. package/dist/server/domain/deliveryGraph.d.ts +14 -0
  84. package/dist/server/domain/deliveryGraph.d.ts.map +1 -1
  85. package/dist/server/domain/index.js +3 -3
  86. package/dist/server/index.js +4 -4
  87. package/dist/server/server/application/normalizedPluginPageMaterialization.d.ts +1 -1
  88. package/dist/server/server/application/normalizedPluginPageMaterialization.d.ts.map +1 -1
  89. package/dist/server/server/cli.js +4 -4
  90. package/dist/server/server/index.js +3 -3
  91. package/dist/server/server/persistence/Persistence.d.ts +35 -0
  92. package/dist/server/server/persistence/Persistence.d.ts.map +1 -1
  93. package/dist/server/server/persistence/repositories/delivery-graph/contract.d.ts +56 -0
  94. package/dist/server/server/persistence/repositories/delivery-graph/contract.d.ts.map +1 -1
  95. package/dist/server/server/persistence/repositories/delivery-graph/decode.d.ts +7 -0
  96. package/dist/server/server/persistence/repositories/delivery-graph/decode.d.ts.map +1 -1
  97. package/dist/server/server/persistence/repositories/delivery-graph/read.d.ts +35 -0
  98. package/dist/server/server/persistence/repositories/delivery-graph/read.d.ts.map +1 -1
  99. package/dist/server/server/persistence/repositories/delivery-graph/rows.d.ts +7 -0
  100. package/dist/server/server/persistence/repositories/delivery-graph/rows.d.ts.map +1 -1
  101. package/dist/server/server/persistence/repositories/delivery-graph/write.d.ts +14 -0
  102. package/dist/server/server/persistence/repositories/delivery-graph/write.d.ts.map +1 -1
  103. package/dist/server/server/persistence/repositories/deliveryGraphRepository.d.ts +35 -0
  104. package/dist/server/server/persistence/repositories/deliveryGraphRepository.d.ts.map +1 -1
  105. package/package.json +3 -3
  106. package/dist/client/assets/ReleaseWorkset-BhGacsgb.js +0 -2
  107. package/dist/client/assets/ReleaseWorkset-BhGacsgb.js.map +0 -1
  108. package/dist/client/assets/WorkspaceEntityRoute-DPci8Q8I.js +0 -30
  109. package/dist/client/assets/WorkspaceEntityRoute-DPci8Q8I.js.map +0 -1
  110. package/dist/client/assets/presentWorkspaceItems-BU__8LcC.js +0 -2
  111. package/dist/client/assets/presentWorkspaceItems-BU__8LcC.js.map +0 -1
  112. package/dist/client/assets/workspaceEntityRoutes-CRcfSq7c.js +0 -2
  113. package/dist/client/assets/workspaceEntityRoutes-CRcfSq7c.js.map +0 -1
  114. package/dist/server/assets/manualPluginSynchronization-C0aHB4UM.js.map +0 -1
  115. package/dist/server/assets/portfolioInvalidation-Ds9Xm4_P.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{J as e,K as t}from"./client-OmvShYdC.js";var n=[`Build`,`Verify`,`Production`],r={assembling:{label:`Assembling`,tone:`progress`},candidate:{label:`Candidate`,tone:`neutral`},deploying:{label:`Deploying`,tone:`progress`},released:{label:`Released`,tone:`positive`},cancelled:{label:`Cancelled`,tone:`caution`}},i={"release-owner":`Release owner`,"release-approver":`Release approver`},a={label:`Disabled`,message:`This source connection is disabled.`,tone:`neutral`,isUnhealthy:!0},o=e=>{switch(e){case`codecommit`:return`codecommit`;case`codepipeline`:return`codepipeline`;case`jira`:return`jira`;case`confluence`:return`confluence`;case`clockify`:return`clockify`}},s=e=>{if(e===null)return{label:`Not checked`,message:null,tone:`neutral`,isUnhealthy:!1};switch(e._tag){case`healthy`:return{label:`Healthy`,message:null,tone:`positive`,isUnhealthy:!1};case`degraded`:return{label:`Degraded`,message:e.safeMessage,tone:`caution`,isUnhealthy:!0};case`unavailable`:return{label:`Unavailable`,message:e.safeMessage,tone:`critical`,isUnhealthy:!0};case`disabled`:return a}},c=e=>{switch(e._tag){case`current`:return e.provenance._tag===`cache`?`cached`:`current`;case`stale`:return`stale`;case`missing`:return`missing`;case`unavailable`:return`unavailable`}},l=e=>e.provenance._tag===`none`?e.provenance.pluginConnectionId:e.provenance.sourceRevision.pluginConnectionId,u=e=>e.freshness.sourceObservedAt??e.freshness.synchronizedAt,d=t=>e(t,{day:`2-digit`,hour:`2-digit`,hour12:!1,locale:`en-GB`,minute:`2-digit`,month:`short`,timeZoneName:`short`}),f=(e,n)=>{let r=l(e.freshness),i=n.find(({pluginConnectionId:e})=>e===r),f=i===void 0?{label:`Unavailable`,message:`The source connection is missing from the current portfolio snapshot.`,tone:`critical`,isUnhealthy:!0}:i.isEnabled?s(i.health):a,p=c(e.freshness),m=u(e),h=e.freshness._tag===`stale`?f.message??`The source is not current. Preserved facts remain visible from the last successful sync.`:f.isUnhealthy?f.message??`The source needs attention. Saved release facts remain visible.`:null;return{displayName:i?.displayName??`Source connection unavailable`,freshness:p,freshnessDateTime:m===null?null:t(m),freshnessTime:m===null?null:d(m),healthLabel:f.label,healthTone:f.tone,service:i===void 0?null:o(i.providerId),warning:h}},p=e=>e.split(`-`).map(e=>`${e.charAt(0).toLocaleUpperCase(`en-US`)}${e.slice(1)}`).join(` `),m=e=>{if(e!==null)return e._tag===`count`?`${String(e.completed)} of ${String(e.total)} complete`:`${String(e.value)}% complete`},h=e=>{switch(e){case`failed`:return`critical`;case`held`:return`caution`;case`queued`:case`running`:return`progress`;case`succeeded`:return`positive`;case`not-started`:return`neutral`}},g=e=>{switch(e){case`failed`:return`critical`;case`held`:return`caution`;case`pending`:return`progress`;case`passed`:return`positive`;case`not-started`:return`neutral`}},_=e=>{switch(e){case`failed`:case`rolled-back`:return`critical`;case`held`:return`caution`;case`deploying`:case`waiting`:return`progress`;case`succeeded`:return`positive`;case`not-started`:return`neutral`}},v=()=>n.map(e=>({id:e.toLocaleLowerCase(`en-US`),name:e,reason:`No readiness evidence has been evaluated yet.`,state:`Not evaluated`,tone:`neutral`})),y=(e,t,n,r,i)=>{let a=m(i),o={id:e,name:t,state:p(n),tone:r};return a===void 0?o:{...o,reason:a}},b=e=>[y(`build`,`Build`,e.build.state,h(e.build.state),e.build.progress),y(`verify`,`Verify`,e.verify.state,g(e.verify.state),e.verify.progress),y(`production`,`Production`,e.production.state,_(e.production.state),e.production.progress)],x={blocked:{label:`Can't ship`,tone:`critical`},ready:{label:`Can ship`,tone:`positive`},deploying:{label:`Deploying`,tone:`progress`},building:{label:`Building`,tone:`progress`},shipped:{label:`Shipped`,tone:`positive`},held:{label:`Needs links`,tone:`caution`}},S=e=>{switch(e.code){case`input-incomplete`:return`Readiness inputs are incomplete.`;case`relationship-missing`:return`A required delivery link is missing.`;case`relationship-unverified`:return`A delivery link still needs verification.`;case`relationship-rejected`:return`A required delivery link was rejected.`;case`relationship-superseded`:return`A delivery link points to superseded work.`;case`approval-missing`:return`A required approval is missing.`;case`approval-pending`:return`A required approval is pending.`;case`approval-rejected`:return`A required approval was rejected.`;case`approval-expired`:return`A required approval expired.`;case`check-missing`:return`A required check is missing.`;case`check-pending`:return`Required checks are still running.`;case`check-failed`:return`A required check failed.`;case`check-cancelled`:return`A required check was cancelled.`;case`execution-missing`:return`A required pipeline run is missing.`;case`execution-pending`:return`A required pipeline is still running.`;case`execution-failed`:return`A required pipeline failed.`;case`execution-stopped`:return`A required pipeline was stopped.`;case`documentation-missing`:return`Required release documentation is missing.`;case`documentation-draft`:return`Release documentation is still a draft.`;case`documentation-stale`:return`Release documentation is stale.`;case`documentation-superseded`:return`Release documentation was superseded.`;case`deployment-missing`:return`Production deployment evidence is missing.`;case`deployment-failed`:return`Production deployment failed.`;case`deployment-rolled-back`:return`Production deployment was rolled back.`;case`source-stale`:return`A required source is stale.`;case`source-missing`:return`A required source is missing.`;case`source-unavailable`:return`A required source is unavailable.`;case`plugin-degraded`:return`A connected service is degraded.`;case`evidence-expired`:return`Required evidence has expired.`}},C=e=>{switch(e){case`blocked`:return`Required release evidence failed.`;case`ready`:return`All required evidence is verified.`;case`deploying`:return`Production rollout is in progress.`;case`building`:return`Build and verification are in progress.`;case`shipped`:return`Production deployment is verified.`;case`held`:return`Waiting for required delivery links.`}},w=(e,t)=>{let n=r[e.lifecycle],a=f(e,t),o=e.collaborators.map(({avatarFallback:e,displayName:t,personId:n,role:r})=>({avatarFallback:e,id:`${n}:${r}`,name:t,role:i[r]})),s=o.find(({role:e})=>e===i[`release-owner`]),c=o.find(({role:e})=>e===i[`release-approver`]),l=[{id:`service`,label:`Service`,value:e.serviceName},{id:`issues`,label:`Jira`,value:String(e.relationships.issues)},{id:`pull-requests`,label:`PRs`,value:String(e.relationships.pullRequests)},{id:`pipelines`,label:`Pipelines`,value:String(e.relationships.pipelineExecutions)},{id:`gaps`,label:`Gaps`,value:String(e.readiness?.gapCount??0)}],u=e.readiness,d=u===null?`No readiness evidence has been evaluated yet.`:u.authority===`pending`?`Evidence changed. Rechecking this release.`:u.primaryFinding===null?C(u.verdict):S(u.primaryFinding),p=u===null?null:x[u.verdict];return{collaborators:o,collaboratorCount:e.collaboratorCount,facts:l,id:e.releaseId,lifecycleLabel:n.label,lifecycleTone:n.tone,readinessVerdict:u?.verdict??`unknown`,readinessReason:d,release:{algorithm:e.relay.algorithm,...c===void 0?{}:{approver:c},codename:e.relay.codename,facts:l,freshness:a.freshness,...a.freshnessDateTime===null||a.freshnessTime===null?{}:{freshnessDateTime:a.freshnessDateTime,freshnessTime:a.freshnessTime},id:e.releaseId,...s===void 0?{}:{owner:s},reason:d,state:u?.verdict??`unknown`,symbolIndices:e.relay.symbolIndices,tone:p?.tone??`neutral`,verdict:p?.label??`Readiness not evaluated`,version:e.version},relay:{algorithm:e.relay.algorithm,codename:e.relay.codename,symbolIndices:e.relay.symbolIndices},serviceName:e.serviceName,source:a,stages:u===null?v():b(u.stages),targetEnvironmentIds:e.targetEnvironmentIds,version:e.version}},T=e=>({generatedAt:t(e.generatedAt),generatedTime:d(e.generatedAt),releases:e.releases.map(t=>w(t,e.plugins)),workspaceId:e.workspaceId});export{T as t};
2
- //# sourceMappingURL=presentPortfolio-CkRqd_zz.js.map
1
+ import{J as e,K as t}from"./client-P7FaPwzJ.js";var n=[`Build`,`Verify`,`Production`],r={assembling:{label:`Assembling`,tone:`progress`},candidate:{label:`Candidate`,tone:`neutral`},deploying:{label:`Deploying`,tone:`progress`},released:{label:`Released`,tone:`positive`},cancelled:{label:`Cancelled`,tone:`caution`}},i={"release-owner":`Release owner`,"release-approver":`Release approver`},a={label:`Disabled`,message:`This source connection is disabled.`,tone:`neutral`,isUnhealthy:!0},o=e=>{switch(e){case`codecommit`:return`codecommit`;case`codepipeline`:return`codepipeline`;case`jira`:return`jira`;case`confluence`:return`confluence`;case`clockify`:return`clockify`}},s=e=>{if(e===null)return{label:`Not checked`,message:null,tone:`neutral`,isUnhealthy:!1};switch(e._tag){case`healthy`:return{label:`Healthy`,message:null,tone:`positive`,isUnhealthy:!1};case`degraded`:return{label:`Degraded`,message:e.safeMessage,tone:`caution`,isUnhealthy:!0};case`unavailable`:return{label:`Unavailable`,message:e.safeMessage,tone:`critical`,isUnhealthy:!0};case`disabled`:return a}},c=e=>{switch(e._tag){case`current`:return e.provenance._tag===`cache`?`cached`:`current`;case`stale`:return`stale`;case`missing`:return`missing`;case`unavailable`:return`unavailable`}},l=e=>e.provenance._tag===`none`?e.provenance.pluginConnectionId:e.provenance.sourceRevision.pluginConnectionId,u=e=>e.freshness.sourceObservedAt??e.freshness.synchronizedAt,d=t=>e(t,{day:`2-digit`,hour:`2-digit`,hour12:!1,locale:`en-GB`,minute:`2-digit`,month:`short`,timeZoneName:`short`}),f=(e,n)=>{let r=l(e.freshness),i=n.find(({pluginConnectionId:e})=>e===r),f=i===void 0?{label:`Unavailable`,message:`The source connection is missing from the current portfolio snapshot.`,tone:`critical`,isUnhealthy:!0}:i.isEnabled?s(i.health):a,p=c(e.freshness),m=u(e),h=e.freshness._tag===`stale`?f.message??`The source is not current. Preserved facts remain visible from the last successful sync.`:f.isUnhealthy?f.message??`The source needs attention. Saved release facts remain visible.`:null;return{displayName:i?.displayName??`Source connection unavailable`,freshness:p,freshnessDateTime:m===null?null:t(m),freshnessTime:m===null?null:d(m),healthLabel:f.label,healthTone:f.tone,service:i===void 0?null:o(i.providerId),warning:h}},p=e=>e.split(`-`).map(e=>`${e.charAt(0).toLocaleUpperCase(`en-US`)}${e.slice(1)}`).join(` `),m=e=>{if(e!==null)return e._tag===`count`?`${String(e.completed)} of ${String(e.total)} complete`:`${String(e.value)}% complete`},h=e=>{switch(e){case`failed`:return`critical`;case`held`:return`caution`;case`queued`:case`running`:return`progress`;case`succeeded`:return`positive`;case`not-started`:return`neutral`}},g=e=>{switch(e){case`failed`:return`critical`;case`held`:return`caution`;case`pending`:return`progress`;case`passed`:return`positive`;case`not-started`:return`neutral`}},_=e=>{switch(e){case`failed`:case`rolled-back`:return`critical`;case`held`:return`caution`;case`deploying`:case`waiting`:return`progress`;case`succeeded`:return`positive`;case`not-started`:return`neutral`}},v=()=>n.map(e=>({id:e.toLocaleLowerCase(`en-US`),name:e,reason:`No readiness evidence has been evaluated yet.`,state:`Not evaluated`,tone:`neutral`})),y=(e,t,n,r,i)=>{let a=m(i),o={id:e,name:t,state:p(n),tone:r};return a===void 0?o:{...o,reason:a}},b=e=>[y(`build`,`Build`,e.build.state,h(e.build.state),e.build.progress),y(`verify`,`Verify`,e.verify.state,g(e.verify.state),e.verify.progress),y(`production`,`Production`,e.production.state,_(e.production.state),e.production.progress)],x={blocked:{label:`Can't ship`,tone:`critical`},ready:{label:`Can ship`,tone:`positive`},deploying:{label:`Deploying`,tone:`progress`},building:{label:`Building`,tone:`progress`},shipped:{label:`Shipped`,tone:`positive`},held:{label:`Needs links`,tone:`caution`}},S=e=>{switch(e.code){case`input-incomplete`:return`Readiness inputs are incomplete.`;case`relationship-missing`:return`A required delivery link is missing.`;case`relationship-unverified`:return`A delivery link still needs verification.`;case`relationship-rejected`:return`A required delivery link was rejected.`;case`relationship-superseded`:return`A delivery link points to superseded work.`;case`approval-missing`:return`A required approval is missing.`;case`approval-pending`:return`A required approval is pending.`;case`approval-rejected`:return`A required approval was rejected.`;case`approval-expired`:return`A required approval expired.`;case`check-missing`:return`A required check is missing.`;case`check-pending`:return`Required checks are still running.`;case`check-failed`:return`A required check failed.`;case`check-cancelled`:return`A required check was cancelled.`;case`execution-missing`:return`A required pipeline run is missing.`;case`execution-pending`:return`A required pipeline is still running.`;case`execution-failed`:return`A required pipeline failed.`;case`execution-stopped`:return`A required pipeline was stopped.`;case`documentation-missing`:return`Required release documentation is missing.`;case`documentation-draft`:return`Release documentation is still a draft.`;case`documentation-stale`:return`Release documentation is stale.`;case`documentation-superseded`:return`Release documentation was superseded.`;case`deployment-missing`:return`Production deployment evidence is missing.`;case`deployment-failed`:return`Production deployment failed.`;case`deployment-rolled-back`:return`Production deployment was rolled back.`;case`source-stale`:return`A required source is stale.`;case`source-missing`:return`A required source is missing.`;case`source-unavailable`:return`A required source is unavailable.`;case`plugin-degraded`:return`A connected service is degraded.`;case`evidence-expired`:return`Required evidence has expired.`}},C=e=>{switch(e){case`blocked`:return`Required release evidence failed.`;case`ready`:return`All required evidence is verified.`;case`deploying`:return`Production rollout is in progress.`;case`building`:return`Build and verification are in progress.`;case`shipped`:return`Production deployment is verified.`;case`held`:return`Waiting for required delivery links.`}},w=(e,t)=>{let n=r[e.lifecycle],a=f(e,t),o=e.collaborators.map(({avatarFallback:e,displayName:t,personId:n,role:r})=>({avatarFallback:e,id:`${n}:${r}`,name:t,role:i[r]})),s=o.find(({role:e})=>e===i[`release-owner`]),c=o.find(({role:e})=>e===i[`release-approver`]),l=[{id:`service`,label:`Service`,value:e.serviceName},{id:`issues`,label:`Jira`,value:String(e.relationships.issues)},{id:`pull-requests`,label:`PRs`,value:String(e.relationships.pullRequests)},{id:`pipelines`,label:`Pipelines`,value:String(e.relationships.pipelineExecutions)},{id:`gaps`,label:`Gaps`,value:String(e.readiness?.gapCount??0)}],u=e.readiness,d=u===null?`No readiness evidence has been evaluated yet.`:u.authority===`pending`?`Evidence changed. Rechecking this release.`:u.primaryFinding===null?C(u.verdict):S(u.primaryFinding),p=u===null?null:x[u.verdict];return{collaborators:o,collaboratorCount:e.collaboratorCount,facts:l,id:e.releaseId,lifecycleLabel:n.label,lifecycleTone:n.tone,readinessVerdict:u?.verdict??`unknown`,readinessReason:d,release:{algorithm:e.relay.algorithm,...c===void 0?{}:{approver:c},codename:e.relay.codename,facts:l,freshness:a.freshness,...a.freshnessDateTime===null||a.freshnessTime===null?{}:{freshnessDateTime:a.freshnessDateTime,freshnessTime:a.freshnessTime},id:e.releaseId,...s===void 0?{}:{owner:s},reason:d,state:u?.verdict??`unknown`,symbolIndices:e.relay.symbolIndices,tone:p?.tone??`neutral`,verdict:p?.label??`Readiness not evaluated`,version:e.version},relay:{algorithm:e.relay.algorithm,codename:e.relay.codename,symbolIndices:e.relay.symbolIndices},serviceName:e.serviceName,source:a,stages:u===null?v():b(u.stages),targetEnvironmentIds:e.targetEnvironmentIds,version:e.version}},T=e=>({generatedAt:t(e.generatedAt),generatedTime:d(e.generatedAt),releases:e.releases.map(t=>w(t,e.plugins)),workspaceId:e.workspaceId});export{T as t};
2
+ //# sourceMappingURL=presentPortfolio-D-v615bg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"presentPortfolio-CkRqd_zz.js","names":[],"sources":["../../../src/client/portfolio/presentPortfolio.ts"],"sourcesContent":["import type {\n RlyFreshnessState,\n RlyPerson,\n RlyReleasePresentation,\n RlyReleaseRelaySymbolIndices,\n RlyService,\n RlyStage\n} from \"@knpkv/rly/patterns\"\nimport type { RlyStateTone } from \"@knpkv/rly/primitives\"\nimport * as DateTime from \"effect/DateTime\"\n\nimport type { PortfolioReleaseRole, PortfolioSnapshot } from \"../../api/portfolio.js\"\nimport type { PluginHealth } from \"../../domain/freshness.js\"\nimport type {\n ReadinessFinding,\n ReadinessProgress,\n ReadinessStages,\n ReadinessVerdict\n} from \"../../domain/readiness/model.js\"\nimport type { ReleaseLifecycle } from \"../../domain/release.js\"\n\nconst READINESS_STAGES: ReadonlyArray<string> = [\"Build\", \"Verify\", \"Production\"]\n\nconst lifecyclePresentation: Readonly<\n Record<ReleaseLifecycle, { readonly label: string; readonly tone: RlyStateTone }>\n> = {\n assembling: { label: \"Assembling\", tone: \"progress\" },\n candidate: { label: \"Candidate\", tone: \"neutral\" },\n deploying: { label: \"Deploying\", tone: \"progress\" },\n released: { label: \"Released\", tone: \"positive\" },\n cancelled: { label: \"Cancelled\", tone: \"caution\" }\n}\n\nconst releaseRoleLabels: Readonly<Record<PortfolioReleaseRole, string>> = {\n \"release-owner\": \"Release owner\",\n \"release-approver\": \"Release approver\"\n}\n\ninterface SourceHealthPresentation {\n readonly label: string\n readonly message: string | null\n readonly tone: RlyStateTone\n readonly isUnhealthy: boolean\n}\n\nconst DISABLED_SOURCE_HEALTH = {\n label: \"Disabled\",\n message: \"This source connection is disabled.\",\n tone: \"neutral\",\n isUnhealthy: true\n} satisfies SourceHealthPresentation\n\nexport interface PortfolioSourcePresentation {\n readonly displayName: string\n readonly freshness: RlyFreshnessState\n readonly freshnessDateTime: string | null\n readonly freshnessTime: string | null\n readonly healthLabel: string\n readonly healthTone: RlyStateTone\n readonly service: RlyService | null\n readonly warning: string | null\n}\n\nexport interface PortfolioReleasePresentation {\n readonly collaborators: ReadonlyArray<RlyPerson>\n readonly collaboratorCount: number\n readonly facts: ReadonlyArray<{ readonly id: string; readonly label: string; readonly value: string }>\n readonly id: PortfolioSnapshot[\"releases\"][number][\"releaseId\"]\n readonly lifecycleLabel: string\n readonly lifecycleTone: RlyStateTone\n readonly readinessVerdict: ReadinessVerdict | \"unknown\"\n readonly readinessReason: string\n readonly release: RlyReleasePresentation\n readonly relay: {\n readonly algorithm: string\n readonly codename: string\n readonly symbolIndices: RlyReleaseRelaySymbolIndices\n }\n readonly serviceName: string\n readonly source: PortfolioSourcePresentation\n readonly stages: ReadonlyArray<RlyStage>\n readonly targetEnvironmentIds: PortfolioSnapshot[\"releases\"][number][\"targetEnvironmentIds\"]\n readonly version: string\n}\n\nexport interface PortfolioPresentation {\n readonly generatedAt: string\n readonly generatedTime: string\n readonly releases: ReadonlyArray<PortfolioReleasePresentation>\n readonly workspaceId: PortfolioSnapshot[\"workspaceId\"]\n}\n\nconst serviceForProvider = (providerId: PortfolioSnapshot[\"plugins\"][number][\"providerId\"]): RlyService => {\n switch (providerId) {\n case \"codecommit\":\n return \"codecommit\"\n case \"codepipeline\":\n return \"codepipeline\"\n case \"jira\":\n return \"jira\"\n case \"confluence\":\n return \"confluence\"\n case \"clockify\":\n return \"clockify\"\n }\n}\n\nconst healthPresentation = (health: PluginHealth | null): SourceHealthPresentation => {\n if (health === null) {\n return { label: \"Not checked\", message: null, tone: \"neutral\", isUnhealthy: false }\n }\n switch (health._tag) {\n case \"healthy\":\n return { label: \"Healthy\", message: null, tone: \"positive\", isUnhealthy: false }\n case \"degraded\":\n return { label: \"Degraded\", message: health.safeMessage, tone: \"caution\", isUnhealthy: true }\n case \"unavailable\":\n return { label: \"Unavailable\", message: health.safeMessage, tone: \"critical\", isUnhealthy: true }\n case \"disabled\":\n return DISABLED_SOURCE_HEALTH\n }\n}\n\nconst freshnessState = (freshness: PortfolioSnapshot[\"releases\"][number][\"freshness\"]): RlyFreshnessState => {\n switch (freshness._tag) {\n case \"current\":\n return freshness.provenance._tag === \"cache\" ? \"cached\" : \"current\"\n case \"stale\":\n return \"stale\"\n case \"missing\":\n return \"missing\"\n case \"unavailable\":\n return \"unavailable\"\n }\n}\n\nconst sourceConnectionId = (\n freshness: PortfolioSnapshot[\"releases\"][number][\"freshness\"]\n): PortfolioSnapshot[\"plugins\"][number][\"pluginConnectionId\"] =>\n freshness.provenance._tag === \"none\"\n ? freshness.provenance.pluginConnectionId\n : freshness.provenance.sourceRevision.pluginConnectionId\n\nconst freshnessTimestamp = (\n release: PortfolioSnapshot[\"releases\"][number]\n): PortfolioSnapshot[\"releases\"][number][\"updatedAt\"] | null =>\n release.freshness.sourceObservedAt ?? release.freshness.synchronizedAt\n\nconst formattedTime = (timestamp: PortfolioSnapshot[\"generatedAt\"]): string =>\n DateTime.formatUtc(timestamp, {\n day: \"2-digit\",\n hour: \"2-digit\",\n hour12: false,\n locale: \"en-GB\",\n minute: \"2-digit\",\n month: \"short\",\n timeZoneName: \"short\"\n })\n\nconst sourcePresentation = (\n release: PortfolioSnapshot[\"releases\"][number],\n plugins: PortfolioSnapshot[\"plugins\"]\n): PortfolioSourcePresentation => {\n const connectionId = sourceConnectionId(release.freshness)\n const plugin = plugins.find(({ pluginConnectionId }) => pluginConnectionId === connectionId)\n const health = plugin === undefined\n ? {\n label: \"Unavailable\",\n message: \"The source connection is missing from the current portfolio snapshot.\",\n tone: \"critical\",\n isUnhealthy: true\n } satisfies SourceHealthPresentation\n : plugin.isEnabled\n ? healthPresentation(plugin.health)\n : DISABLED_SOURCE_HEALTH\n const freshness = freshnessState(release.freshness)\n const timestamp = freshnessTimestamp(release)\n const hasStaleFacts = release.freshness._tag === \"stale\"\n const warning = hasStaleFacts\n ? health.message ?? \"The source is not current. Preserved facts remain visible from the last successful sync.\"\n : health.isUnhealthy\n ? health.message ?? \"The source needs attention. Saved release facts remain visible.\"\n : null\n\n return {\n displayName: plugin?.displayName ?? \"Source connection unavailable\",\n freshness,\n freshnessDateTime: timestamp === null ? null : DateTime.formatIso(timestamp),\n freshnessTime: timestamp === null ? null : formattedTime(timestamp),\n healthLabel: health.label,\n healthTone: health.tone,\n service: plugin === undefined ? null : serviceForProvider(plugin.providerId),\n warning\n }\n}\n\nconst stageStateLabel = (state: string): string =>\n state.split(\"-\").map((part) => `${part.charAt(0).toLocaleUpperCase(\"en-US\")}${part.slice(1)}`).join(\" \")\n\nconst progressReason = (progress: ReadinessProgress | null): string | undefined => {\n if (progress === null) return undefined\n return progress._tag === \"count\"\n ? `${String(progress.completed)} of ${String(progress.total)} complete`\n : `${String(progress.value)}% complete`\n}\n\nconst buildTone = (state: ReadinessStages[\"build\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"queued\":\n case \"running\":\n return \"progress\"\n case \"succeeded\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst verifyTone = (state: ReadinessStages[\"verify\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"pending\":\n return \"progress\"\n case \"passed\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst productionTone = (state: ReadinessStages[\"production\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n case \"rolled-back\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"deploying\":\n case \"waiting\":\n return \"progress\"\n case \"succeeded\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst unevaluatedStages = (): ReadonlyArray<RlyStage> =>\n READINESS_STAGES.map((name) => ({\n id: name.toLocaleLowerCase(\"en-US\"),\n name,\n reason: \"No readiness evidence has been evaluated yet.\",\n state: \"Not evaluated\",\n tone: \"neutral\"\n }))\n\nconst readinessStage = (\n id: string,\n name: string,\n state: string,\n tone: RlyStateTone,\n progress: ReadinessProgress | null\n): RlyStage => {\n const reason = progressReason(progress)\n const stage = { id, name, state: stageStateLabel(state), tone }\n return reason === undefined ? stage : { ...stage, reason }\n}\n\nconst readinessStages = (stages: ReadinessStages): ReadonlyArray<RlyStage> => [\n readinessStage(\"build\", \"Build\", stages.build.state, buildTone(stages.build.state), stages.build.progress),\n readinessStage(\"verify\", \"Verify\", stages.verify.state, verifyTone(stages.verify.state), stages.verify.progress),\n readinessStage(\n \"production\",\n \"Production\",\n stages.production.state,\n productionTone(stages.production.state),\n stages.production.progress\n )\n]\n\nconst verdictPresentation: Readonly<\n Record<ReadinessVerdict, { readonly label: string; readonly tone: RlyStateTone }>\n> = {\n blocked: { label: \"Can't ship\", tone: \"critical\" },\n ready: { label: \"Can ship\", tone: \"positive\" },\n deploying: { label: \"Deploying\", tone: \"progress\" },\n building: { label: \"Building\", tone: \"progress\" },\n shipped: { label: \"Shipped\", tone: \"positive\" },\n held: { label: \"Needs links\", tone: \"caution\" }\n}\n\nconst findingReason = (finding: ReadinessFinding): string => {\n switch (finding.code) {\n case \"input-incomplete\":\n return \"Readiness inputs are incomplete.\"\n case \"relationship-missing\":\n return \"A required delivery link is missing.\"\n case \"relationship-unverified\":\n return \"A delivery link still needs verification.\"\n case \"relationship-rejected\":\n return \"A required delivery link was rejected.\"\n case \"relationship-superseded\":\n return \"A delivery link points to superseded work.\"\n case \"approval-missing\":\n return \"A required approval is missing.\"\n case \"approval-pending\":\n return \"A required approval is pending.\"\n case \"approval-rejected\":\n return \"A required approval was rejected.\"\n case \"approval-expired\":\n return \"A required approval expired.\"\n case \"check-missing\":\n return \"A required check is missing.\"\n case \"check-pending\":\n return \"Required checks are still running.\"\n case \"check-failed\":\n return \"A required check failed.\"\n case \"check-cancelled\":\n return \"A required check was cancelled.\"\n case \"execution-missing\":\n return \"A required pipeline run is missing.\"\n case \"execution-pending\":\n return \"A required pipeline is still running.\"\n case \"execution-failed\":\n return \"A required pipeline failed.\"\n case \"execution-stopped\":\n return \"A required pipeline was stopped.\"\n case \"documentation-missing\":\n return \"Required release documentation is missing.\"\n case \"documentation-draft\":\n return \"Release documentation is still a draft.\"\n case \"documentation-stale\":\n return \"Release documentation is stale.\"\n case \"documentation-superseded\":\n return \"Release documentation was superseded.\"\n case \"deployment-missing\":\n return \"Production deployment evidence is missing.\"\n case \"deployment-failed\":\n return \"Production deployment failed.\"\n case \"deployment-rolled-back\":\n return \"Production deployment was rolled back.\"\n case \"source-stale\":\n return \"A required source is stale.\"\n case \"source-missing\":\n return \"A required source is missing.\"\n case \"source-unavailable\":\n return \"A required source is unavailable.\"\n case \"plugin-degraded\":\n return \"A connected service is degraded.\"\n case \"evidence-expired\":\n return \"Required evidence has expired.\"\n }\n}\n\nconst verdictReason = (verdict: ReadinessVerdict): string => {\n switch (verdict) {\n case \"blocked\":\n return \"Required release evidence failed.\"\n case \"ready\":\n return \"All required evidence is verified.\"\n case \"deploying\":\n return \"Production rollout is in progress.\"\n case \"building\":\n return \"Build and verification are in progress.\"\n case \"shipped\":\n return \"Production deployment is verified.\"\n case \"held\":\n return \"Waiting for required delivery links.\"\n }\n}\n\nconst releasePresentation = (\n release: PortfolioSnapshot[\"releases\"][number],\n plugins: PortfolioSnapshot[\"plugins\"]\n): PortfolioReleasePresentation => {\n const lifecycle = lifecyclePresentation[release.lifecycle]\n const source = sourcePresentation(release, plugins)\n const collaborators = release.collaborators.map(({ avatarFallback, displayName, personId, role }) => ({\n avatarFallback,\n id: `${personId}:${role}`,\n name: displayName,\n role: releaseRoleLabels[role]\n }))\n const owner = collaborators.find(({ role }) => role === releaseRoleLabels[\"release-owner\"])\n const approver = collaborators.find(({ role }) => role === releaseRoleLabels[\"release-approver\"])\n const facts = [\n { id: \"service\", label: \"Service\", value: release.serviceName },\n { id: \"issues\", label: \"Jira\", value: String(release.relationships.issues) },\n { id: \"pull-requests\", label: \"PRs\", value: String(release.relationships.pullRequests) },\n { id: \"pipelines\", label: \"Pipelines\", value: String(release.relationships.pipelineExecutions) },\n { id: \"gaps\", label: \"Gaps\", value: String(release.readiness?.gapCount ?? 0) }\n ]\n const readiness = release.readiness\n const readinessReason = readiness === null\n ? \"No readiness evidence has been evaluated yet.\"\n : readiness.authority === \"pending\"\n ? \"Evidence changed. Rechecking this release.\"\n : readiness.primaryFinding === null\n ? verdictReason(readiness.verdict)\n : findingReason(readiness.primaryFinding)\n const verdict = readiness === null ? null : verdictPresentation[readiness.verdict]\n return {\n collaborators,\n collaboratorCount: release.collaboratorCount,\n facts,\n id: release.releaseId,\n lifecycleLabel: lifecycle.label,\n lifecycleTone: lifecycle.tone,\n readinessVerdict: readiness?.verdict ?? \"unknown\",\n readinessReason,\n release: {\n algorithm: release.relay.algorithm,\n ...(approver === undefined ? {} : { approver }),\n codename: release.relay.codename,\n facts,\n freshness: source.freshness,\n ...(source.freshnessDateTime === null || source.freshnessTime === null\n ? {}\n : { freshnessDateTime: source.freshnessDateTime, freshnessTime: source.freshnessTime }),\n id: release.releaseId,\n ...(owner === undefined ? {} : { owner }),\n reason: readinessReason,\n state: readiness?.verdict ?? \"unknown\",\n symbolIndices: release.relay.symbolIndices,\n tone: verdict?.tone ?? \"neutral\",\n verdict: verdict?.label ?? \"Readiness not evaluated\",\n version: release.version\n },\n relay: {\n algorithm: release.relay.algorithm,\n codename: release.relay.codename,\n symbolIndices: release.relay.symbolIndices\n },\n serviceName: release.serviceName,\n source,\n stages: readiness === null ? unevaluatedStages() : readinessStages(readiness.stages),\n targetEnvironmentIds: release.targetEnvironmentIds,\n version: release.version\n }\n}\n\n/** Map one authoritative API snapshot into explicit rly component props without deriving readiness. */\nexport const presentPortfolio = (snapshot: PortfolioSnapshot): PortfolioPresentation => ({\n generatedAt: DateTime.formatIso(snapshot.generatedAt),\n generatedTime: formattedTime(snapshot.generatedAt),\n releases: snapshot.releases.map((release) => releasePresentation(release, snapshot.plugins)),\n workspaceId: snapshot.workspaceId\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"presentPortfolio-D-v615bg.js","names":[],"sources":["../../../src/client/portfolio/presentPortfolio.ts"],"sourcesContent":["import type {\n RlyFreshnessState,\n RlyPerson,\n RlyReleasePresentation,\n RlyReleaseRelaySymbolIndices,\n RlyService,\n RlyStage\n} from \"@knpkv/rly/patterns\"\nimport type { RlyStateTone } from \"@knpkv/rly/primitives\"\nimport * as DateTime from \"effect/DateTime\"\n\nimport type { PortfolioReleaseRole, PortfolioSnapshot } from \"../../api/portfolio.js\"\nimport type { PluginHealth } from \"../../domain/freshness.js\"\nimport type {\n ReadinessFinding,\n ReadinessProgress,\n ReadinessStages,\n ReadinessVerdict\n} from \"../../domain/readiness/model.js\"\nimport type { ReleaseLifecycle } from \"../../domain/release.js\"\n\nconst READINESS_STAGES: ReadonlyArray<string> = [\"Build\", \"Verify\", \"Production\"]\n\nconst lifecyclePresentation: Readonly<\n Record<ReleaseLifecycle, { readonly label: string; readonly tone: RlyStateTone }>\n> = {\n assembling: { label: \"Assembling\", tone: \"progress\" },\n candidate: { label: \"Candidate\", tone: \"neutral\" },\n deploying: { label: \"Deploying\", tone: \"progress\" },\n released: { label: \"Released\", tone: \"positive\" },\n cancelled: { label: \"Cancelled\", tone: \"caution\" }\n}\n\nconst releaseRoleLabels: Readonly<Record<PortfolioReleaseRole, string>> = {\n \"release-owner\": \"Release owner\",\n \"release-approver\": \"Release approver\"\n}\n\ninterface SourceHealthPresentation {\n readonly label: string\n readonly message: string | null\n readonly tone: RlyStateTone\n readonly isUnhealthy: boolean\n}\n\nconst DISABLED_SOURCE_HEALTH = {\n label: \"Disabled\",\n message: \"This source connection is disabled.\",\n tone: \"neutral\",\n isUnhealthy: true\n} satisfies SourceHealthPresentation\n\nexport interface PortfolioSourcePresentation {\n readonly displayName: string\n readonly freshness: RlyFreshnessState\n readonly freshnessDateTime: string | null\n readonly freshnessTime: string | null\n readonly healthLabel: string\n readonly healthTone: RlyStateTone\n readonly service: RlyService | null\n readonly warning: string | null\n}\n\nexport interface PortfolioReleasePresentation {\n readonly collaborators: ReadonlyArray<RlyPerson>\n readonly collaboratorCount: number\n readonly facts: ReadonlyArray<{ readonly id: string; readonly label: string; readonly value: string }>\n readonly id: PortfolioSnapshot[\"releases\"][number][\"releaseId\"]\n readonly lifecycleLabel: string\n readonly lifecycleTone: RlyStateTone\n readonly readinessVerdict: ReadinessVerdict | \"unknown\"\n readonly readinessReason: string\n readonly release: RlyReleasePresentation\n readonly relay: {\n readonly algorithm: string\n readonly codename: string\n readonly symbolIndices: RlyReleaseRelaySymbolIndices\n }\n readonly serviceName: string\n readonly source: PortfolioSourcePresentation\n readonly stages: ReadonlyArray<RlyStage>\n readonly targetEnvironmentIds: PortfolioSnapshot[\"releases\"][number][\"targetEnvironmentIds\"]\n readonly version: string\n}\n\nexport interface PortfolioPresentation {\n readonly generatedAt: string\n readonly generatedTime: string\n readonly releases: ReadonlyArray<PortfolioReleasePresentation>\n readonly workspaceId: PortfolioSnapshot[\"workspaceId\"]\n}\n\nconst serviceForProvider = (providerId: PortfolioSnapshot[\"plugins\"][number][\"providerId\"]): RlyService => {\n switch (providerId) {\n case \"codecommit\":\n return \"codecommit\"\n case \"codepipeline\":\n return \"codepipeline\"\n case \"jira\":\n return \"jira\"\n case \"confluence\":\n return \"confluence\"\n case \"clockify\":\n return \"clockify\"\n }\n}\n\nconst healthPresentation = (health: PluginHealth | null): SourceHealthPresentation => {\n if (health === null) {\n return { label: \"Not checked\", message: null, tone: \"neutral\", isUnhealthy: false }\n }\n switch (health._tag) {\n case \"healthy\":\n return { label: \"Healthy\", message: null, tone: \"positive\", isUnhealthy: false }\n case \"degraded\":\n return { label: \"Degraded\", message: health.safeMessage, tone: \"caution\", isUnhealthy: true }\n case \"unavailable\":\n return { label: \"Unavailable\", message: health.safeMessage, tone: \"critical\", isUnhealthy: true }\n case \"disabled\":\n return DISABLED_SOURCE_HEALTH\n }\n}\n\nconst freshnessState = (freshness: PortfolioSnapshot[\"releases\"][number][\"freshness\"]): RlyFreshnessState => {\n switch (freshness._tag) {\n case \"current\":\n return freshness.provenance._tag === \"cache\" ? \"cached\" : \"current\"\n case \"stale\":\n return \"stale\"\n case \"missing\":\n return \"missing\"\n case \"unavailable\":\n return \"unavailable\"\n }\n}\n\nconst sourceConnectionId = (\n freshness: PortfolioSnapshot[\"releases\"][number][\"freshness\"]\n): PortfolioSnapshot[\"plugins\"][number][\"pluginConnectionId\"] =>\n freshness.provenance._tag === \"none\"\n ? freshness.provenance.pluginConnectionId\n : freshness.provenance.sourceRevision.pluginConnectionId\n\nconst freshnessTimestamp = (\n release: PortfolioSnapshot[\"releases\"][number]\n): PortfolioSnapshot[\"releases\"][number][\"updatedAt\"] | null =>\n release.freshness.sourceObservedAt ?? release.freshness.synchronizedAt\n\nconst formattedTime = (timestamp: PortfolioSnapshot[\"generatedAt\"]): string =>\n DateTime.formatUtc(timestamp, {\n day: \"2-digit\",\n hour: \"2-digit\",\n hour12: false,\n locale: \"en-GB\",\n minute: \"2-digit\",\n month: \"short\",\n timeZoneName: \"short\"\n })\n\nconst sourcePresentation = (\n release: PortfolioSnapshot[\"releases\"][number],\n plugins: PortfolioSnapshot[\"plugins\"]\n): PortfolioSourcePresentation => {\n const connectionId = sourceConnectionId(release.freshness)\n const plugin = plugins.find(({ pluginConnectionId }) => pluginConnectionId === connectionId)\n const health = plugin === undefined\n ? {\n label: \"Unavailable\",\n message: \"The source connection is missing from the current portfolio snapshot.\",\n tone: \"critical\",\n isUnhealthy: true\n } satisfies SourceHealthPresentation\n : plugin.isEnabled\n ? healthPresentation(plugin.health)\n : DISABLED_SOURCE_HEALTH\n const freshness = freshnessState(release.freshness)\n const timestamp = freshnessTimestamp(release)\n const hasStaleFacts = release.freshness._tag === \"stale\"\n const warning = hasStaleFacts\n ? health.message ?? \"The source is not current. Preserved facts remain visible from the last successful sync.\"\n : health.isUnhealthy\n ? health.message ?? \"The source needs attention. Saved release facts remain visible.\"\n : null\n\n return {\n displayName: plugin?.displayName ?? \"Source connection unavailable\",\n freshness,\n freshnessDateTime: timestamp === null ? null : DateTime.formatIso(timestamp),\n freshnessTime: timestamp === null ? null : formattedTime(timestamp),\n healthLabel: health.label,\n healthTone: health.tone,\n service: plugin === undefined ? null : serviceForProvider(plugin.providerId),\n warning\n }\n}\n\nconst stageStateLabel = (state: string): string =>\n state.split(\"-\").map((part) => `${part.charAt(0).toLocaleUpperCase(\"en-US\")}${part.slice(1)}`).join(\" \")\n\nconst progressReason = (progress: ReadinessProgress | null): string | undefined => {\n if (progress === null) return undefined\n return progress._tag === \"count\"\n ? `${String(progress.completed)} of ${String(progress.total)} complete`\n : `${String(progress.value)}% complete`\n}\n\nconst buildTone = (state: ReadinessStages[\"build\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"queued\":\n case \"running\":\n return \"progress\"\n case \"succeeded\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst verifyTone = (state: ReadinessStages[\"verify\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"pending\":\n return \"progress\"\n case \"passed\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst productionTone = (state: ReadinessStages[\"production\"][\"state\"]): RlyStateTone => {\n switch (state) {\n case \"failed\":\n case \"rolled-back\":\n return \"critical\"\n case \"held\":\n return \"caution\"\n case \"deploying\":\n case \"waiting\":\n return \"progress\"\n case \"succeeded\":\n return \"positive\"\n case \"not-started\":\n return \"neutral\"\n }\n}\n\nconst unevaluatedStages = (): ReadonlyArray<RlyStage> =>\n READINESS_STAGES.map((name) => ({\n id: name.toLocaleLowerCase(\"en-US\"),\n name,\n reason: \"No readiness evidence has been evaluated yet.\",\n state: \"Not evaluated\",\n tone: \"neutral\"\n }))\n\nconst readinessStage = (\n id: string,\n name: string,\n state: string,\n tone: RlyStateTone,\n progress: ReadinessProgress | null\n): RlyStage => {\n const reason = progressReason(progress)\n const stage = { id, name, state: stageStateLabel(state), tone }\n return reason === undefined ? stage : { ...stage, reason }\n}\n\nconst readinessStages = (stages: ReadinessStages): ReadonlyArray<RlyStage> => [\n readinessStage(\"build\", \"Build\", stages.build.state, buildTone(stages.build.state), stages.build.progress),\n readinessStage(\"verify\", \"Verify\", stages.verify.state, verifyTone(stages.verify.state), stages.verify.progress),\n readinessStage(\n \"production\",\n \"Production\",\n stages.production.state,\n productionTone(stages.production.state),\n stages.production.progress\n )\n]\n\nconst verdictPresentation: Readonly<\n Record<ReadinessVerdict, { readonly label: string; readonly tone: RlyStateTone }>\n> = {\n blocked: { label: \"Can't ship\", tone: \"critical\" },\n ready: { label: \"Can ship\", tone: \"positive\" },\n deploying: { label: \"Deploying\", tone: \"progress\" },\n building: { label: \"Building\", tone: \"progress\" },\n shipped: { label: \"Shipped\", tone: \"positive\" },\n held: { label: \"Needs links\", tone: \"caution\" }\n}\n\nconst findingReason = (finding: ReadinessFinding): string => {\n switch (finding.code) {\n case \"input-incomplete\":\n return \"Readiness inputs are incomplete.\"\n case \"relationship-missing\":\n return \"A required delivery link is missing.\"\n case \"relationship-unverified\":\n return \"A delivery link still needs verification.\"\n case \"relationship-rejected\":\n return \"A required delivery link was rejected.\"\n case \"relationship-superseded\":\n return \"A delivery link points to superseded work.\"\n case \"approval-missing\":\n return \"A required approval is missing.\"\n case \"approval-pending\":\n return \"A required approval is pending.\"\n case \"approval-rejected\":\n return \"A required approval was rejected.\"\n case \"approval-expired\":\n return \"A required approval expired.\"\n case \"check-missing\":\n return \"A required check is missing.\"\n case \"check-pending\":\n return \"Required checks are still running.\"\n case \"check-failed\":\n return \"A required check failed.\"\n case \"check-cancelled\":\n return \"A required check was cancelled.\"\n case \"execution-missing\":\n return \"A required pipeline run is missing.\"\n case \"execution-pending\":\n return \"A required pipeline is still running.\"\n case \"execution-failed\":\n return \"A required pipeline failed.\"\n case \"execution-stopped\":\n return \"A required pipeline was stopped.\"\n case \"documentation-missing\":\n return \"Required release documentation is missing.\"\n case \"documentation-draft\":\n return \"Release documentation is still a draft.\"\n case \"documentation-stale\":\n return \"Release documentation is stale.\"\n case \"documentation-superseded\":\n return \"Release documentation was superseded.\"\n case \"deployment-missing\":\n return \"Production deployment evidence is missing.\"\n case \"deployment-failed\":\n return \"Production deployment failed.\"\n case \"deployment-rolled-back\":\n return \"Production deployment was rolled back.\"\n case \"source-stale\":\n return \"A required source is stale.\"\n case \"source-missing\":\n return \"A required source is missing.\"\n case \"source-unavailable\":\n return \"A required source is unavailable.\"\n case \"plugin-degraded\":\n return \"A connected service is degraded.\"\n case \"evidence-expired\":\n return \"Required evidence has expired.\"\n }\n}\n\nconst verdictReason = (verdict: ReadinessVerdict): string => {\n switch (verdict) {\n case \"blocked\":\n return \"Required release evidence failed.\"\n case \"ready\":\n return \"All required evidence is verified.\"\n case \"deploying\":\n return \"Production rollout is in progress.\"\n case \"building\":\n return \"Build and verification are in progress.\"\n case \"shipped\":\n return \"Production deployment is verified.\"\n case \"held\":\n return \"Waiting for required delivery links.\"\n }\n}\n\nconst releasePresentation = (\n release: PortfolioSnapshot[\"releases\"][number],\n plugins: PortfolioSnapshot[\"plugins\"]\n): PortfolioReleasePresentation => {\n const lifecycle = lifecyclePresentation[release.lifecycle]\n const source = sourcePresentation(release, plugins)\n const collaborators = release.collaborators.map(({ avatarFallback, displayName, personId, role }) => ({\n avatarFallback,\n id: `${personId}:${role}`,\n name: displayName,\n role: releaseRoleLabels[role]\n }))\n const owner = collaborators.find(({ role }) => role === releaseRoleLabels[\"release-owner\"])\n const approver = collaborators.find(({ role }) => role === releaseRoleLabels[\"release-approver\"])\n const facts = [\n { id: \"service\", label: \"Service\", value: release.serviceName },\n { id: \"issues\", label: \"Jira\", value: String(release.relationships.issues) },\n { id: \"pull-requests\", label: \"PRs\", value: String(release.relationships.pullRequests) },\n { id: \"pipelines\", label: \"Pipelines\", value: String(release.relationships.pipelineExecutions) },\n { id: \"gaps\", label: \"Gaps\", value: String(release.readiness?.gapCount ?? 0) }\n ]\n const readiness = release.readiness\n const readinessReason = readiness === null\n ? \"No readiness evidence has been evaluated yet.\"\n : readiness.authority === \"pending\"\n ? \"Evidence changed. Rechecking this release.\"\n : readiness.primaryFinding === null\n ? verdictReason(readiness.verdict)\n : findingReason(readiness.primaryFinding)\n const verdict = readiness === null ? null : verdictPresentation[readiness.verdict]\n return {\n collaborators,\n collaboratorCount: release.collaboratorCount,\n facts,\n id: release.releaseId,\n lifecycleLabel: lifecycle.label,\n lifecycleTone: lifecycle.tone,\n readinessVerdict: readiness?.verdict ?? \"unknown\",\n readinessReason,\n release: {\n algorithm: release.relay.algorithm,\n ...(approver === undefined ? {} : { approver }),\n codename: release.relay.codename,\n facts,\n freshness: source.freshness,\n ...(source.freshnessDateTime === null || source.freshnessTime === null\n ? {}\n : { freshnessDateTime: source.freshnessDateTime, freshnessTime: source.freshnessTime }),\n id: release.releaseId,\n ...(owner === undefined ? {} : { owner }),\n reason: readinessReason,\n state: readiness?.verdict ?? \"unknown\",\n symbolIndices: release.relay.symbolIndices,\n tone: verdict?.tone ?? \"neutral\",\n verdict: verdict?.label ?? \"Readiness not evaluated\",\n version: release.version\n },\n relay: {\n algorithm: release.relay.algorithm,\n codename: release.relay.codename,\n symbolIndices: release.relay.symbolIndices\n },\n serviceName: release.serviceName,\n source,\n stages: readiness === null ? unevaluatedStages() : readinessStages(readiness.stages),\n targetEnvironmentIds: release.targetEnvironmentIds,\n version: release.version\n }\n}\n\n/** Map one authoritative API snapshot into explicit rly component props without deriving readiness. */\nexport const presentPortfolio = (snapshot: PortfolioSnapshot): PortfolioPresentation => ({\n generatedAt: DateTime.formatIso(snapshot.generatedAt),\n generatedTime: formattedTime(snapshot.generatedAt),\n releases: snapshot.releases.map((release) => releasePresentation(release, snapshot.plugins)),\n workspaceId: snapshot.workspaceId\n})\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{K as e}from"./client-P7FaPwzJ.js";import{n as t}from"./workspaceEntityPaths-xAl2jRFm.js";var n=e=>{switch(e){case`change-owner`:case`issue-owner`:case`page-owner`:return`Owner`;case`issue-assignee`:return`Assignee`;case`author`:return`Author`;case`operator`:return`Operator`;default:return i(e)}},r=e=>{switch(e){case`issue`:return`jira`;case`pull-request`:return`codecommit`;case`page`:return`confluence`;case`pipeline-execution`:case`deployment`:return`codepipeline`;case`time-entry`:return`clockify`}},i=e=>e.split(`-`).map(e=>`${e.charAt(0).toLocaleUpperCase(`en-US`)}${e.slice(1)}`).join(` `),a=e=>{switch(e._tag){case`issue`:return e.status;case`pull-request`:switch(e.reviewState){case`changes-requested`:return`Changes requested`;case`approved`:return`Approved`;case`merged`:return`Merged`;case`not-requested`:case`requested`:return e.lifecycle!==null&&e.lifecycle!==void 0?i(e.lifecycle):e.reviewState===`requested`?`Review requested`:`Review not requested`}case`page`:case`pipeline-execution`:case`deployment`:return i(e.status);case`time-entry`:return i(e.approvalState)}},o=e=>{let t=e.trim().toLocaleLowerCase(`en-US`);return t===`blocked`||t===`rejected`?{statusGroup:`failed`,tone:`critical`}:t===`closed`||t===`done`||t===`resolved`?{statusGroup:`done`,tone:`positive`}:{statusGroup:`active`,tone:`progress`}},s=(e,t)=>{if(e===`issue`)return o(t);let n=t.toLocaleLowerCase(`en-US`);return[`blocked`,`changes requested`,`failed`,`rejected`,`rolled back`,`stopped`,`superseded`].some(e=>n.includes(e))?{statusGroup:`failed`,tone:`critical`}:[`approved`,`closed`,`current`,`done`,`merged`,`not required`,`ready`,`resolved`,`succeeded`].some(e=>n.includes(e))?{statusGroup:`done`,tone:`positive`}:{statusGroup:`active`,tone:`progress`}},c=(e,t,n)=>`/w/${e}/releases/${t}?object=${encodeURIComponent(n)}#release-work`,l=(i,o,c)=>{let l=o.projection,u=a(l.details),d=o.releaseIds.filter(e=>c===void 0||c.has(e)),f=o.releaseIds.length===1&&!o.releaseMembershipsTruncated?d[0]??null:null,p=(o.owners??[]).map(({avatarFallback:e,displayName:t,personId:r,roles:i})=>({avatarFallback:e,id:r,name:t,role:[...new Set(i.map(n))].join(` · `),roles:i}));return{entityId:l.entityId,freshness:e(o.recordedAt),href:t(i,l.entityId),key:l.displayKey,kind:l.entityType,owner:p.length===0?`Unassigned`:p.map(({name:e})=>e).join(`, `),owners:p,ownersTruncated:o.ownersTruncated??!1,releaseId:f,releaseIds:o.releaseIds,releaseMembershipsTruncated:o.releaseMembershipsTruncated,routableReleaseIds:d,service:r(l.entityType),status:u,...s(l.entityType,u),title:l.title}},u=(e,t,n)=>t.items.filter(({projection:e})=>e.entityState===`present`).map(t=>l(e,t,n)).sort((e,t)=>e.service.localeCompare(t.service)||e.key.localeCompare(t.key));export{c as a,s as i,r as n,a as r,u as t};
2
+ //# sourceMappingURL=presentWorkspaceItems-IAp5SnpB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentWorkspaceItems-IAp5SnpB.js","names":[],"sources":["../../../src/client/items/presentWorkspaceItems.ts"],"sourcesContent":["import type { RlyService } from \"@knpkv/rly/patterns\"\nimport type { RlyStateTone } from \"@knpkv/rly/primitives\"\nimport * as DateTime from \"effect/DateTime\"\n\nimport type {\n ReleaseDeliveryGraphInspection,\n WorkspaceEntityOwner,\n WorkspaceEntityProjectionIndex\n} from \"../../api/deliveryGraph.js\"\nimport type { Role } from \"../../domain/actors.js\"\nimport type {\n DeliveryEntityDetails,\n DeliveryEntityKind,\n DeliveryEntityStatusGroup\n} from \"../../domain/deliveryGraph.js\"\nimport type { EntityId, ReleaseId, WorkspaceId } from \"../../domain/identifiers.js\"\nimport { workspaceEntityPath } from \"../workspaceEntityPaths.js\"\n\nexport type WorkspaceItemStatus = DeliveryEntityStatusGroup\n\nexport interface WorkspaceItemPresentation {\n readonly entityId: EntityId\n readonly freshness: string\n readonly href: string\n readonly key: string\n readonly kind: DeliveryEntityKind\n readonly owner: string\n readonly owners: ReadonlyArray<WorkspaceItemOwnerPresentation>\n readonly ownersTruncated: boolean\n readonly releaseId: ReleaseId | null\n readonly releaseIds: ReadonlyArray<ReleaseId>\n readonly releaseMembershipsTruncated: boolean\n readonly routableReleaseIds: ReadonlyArray<ReleaseId>\n readonly service: RlyService\n readonly status: string\n readonly statusGroup: WorkspaceItemStatus\n readonly title: string\n readonly tone: RlyStateTone\n}\n\nexport interface WorkspaceItemOwnerPresentation {\n readonly avatarFallback: string\n readonly id: string\n readonly name: string\n readonly role: string\n readonly roles: ReadonlyArray<Role>\n}\n\nconst collaboratorRoleLabel = (role: Role): string => {\n switch (role) {\n case \"change-owner\":\n case \"issue-owner\":\n case \"page-owner\":\n return \"Owner\"\n case \"issue-assignee\":\n return \"Assignee\"\n case \"author\":\n return \"Author\"\n case \"operator\":\n return \"Operator\"\n default:\n return titleCase(role)\n }\n}\n\nexport const serviceFor = (kind: DeliveryEntityKind): RlyService => {\n switch (kind) {\n case \"issue\":\n return \"jira\"\n case \"pull-request\":\n return \"codecommit\"\n case \"page\":\n return \"confluence\"\n case \"pipeline-execution\":\n case \"deployment\":\n return \"codepipeline\"\n case \"time-entry\":\n return \"clockify\"\n }\n}\n\nconst titleCase = (value: string): string =>\n value.split(\"-\").map((part) => `${part.charAt(0).toLocaleUpperCase(\"en-US\")}${part.slice(1)}`).join(\" \")\n\nexport const statusFor = (details: DeliveryEntityDetails): string => {\n switch (details._tag) {\n case \"issue\":\n return details.status\n case \"pull-request\": {\n switch (details.reviewState) {\n case \"changes-requested\":\n return \"Changes requested\"\n case \"approved\":\n return \"Approved\"\n case \"merged\":\n return \"Merged\"\n case \"not-requested\":\n case \"requested\":\n if (details.lifecycle !== null && details.lifecycle !== undefined) return titleCase(details.lifecycle)\n return details.reviewState === \"requested\" ? \"Review requested\" : \"Review not requested\"\n }\n }\n case \"page\":\n case \"pipeline-execution\":\n case \"deployment\":\n return titleCase(details.status)\n case \"time-entry\":\n return titleCase(details.approvalState)\n }\n}\n\nconst issueStatusPresentation = (status: string): Pick<WorkspaceItemPresentation, \"statusGroup\" | \"tone\"> => {\n const value = status.trim().toLocaleLowerCase(\"en-US\")\n if (value === \"blocked\" || value === \"rejected\") return { statusGroup: \"failed\", tone: \"critical\" }\n if (value === \"closed\" || value === \"done\" || value === \"resolved\") {\n return { statusGroup: \"done\", tone: \"positive\" }\n }\n return { statusGroup: \"active\", tone: \"progress\" }\n}\n\nexport const statusPresentation = (\n kind: DeliveryEntityKind,\n status: string\n): Pick<WorkspaceItemPresentation, \"statusGroup\" | \"tone\"> => {\n if (kind === \"issue\") return issueStatusPresentation(status)\n const value = status.toLocaleLowerCase(\"en-US\")\n if (\n [\"blocked\", \"changes requested\", \"failed\", \"rejected\", \"rolled back\", \"stopped\", \"superseded\"].some((part) =>\n value.includes(part)\n )\n ) return { statusGroup: \"failed\", tone: \"critical\" }\n if (\n [\"approved\", \"closed\", \"current\", \"done\", \"merged\", \"not required\", \"ready\", \"resolved\", \"succeeded\"]\n .some((part) => value.includes(part))\n ) return { statusGroup: \"done\", tone: \"positive\" }\n return { statusGroup: \"active\", tone: \"progress\" }\n}\n\nexport const workspaceItemReleaseHref = (\n workspaceId: WorkspaceId,\n releaseId: ReleaseId,\n entityId: EntityId\n): string => `/w/${workspaceId}/releases/${releaseId}?object=${encodeURIComponent(entityId)}#release-work`\n\nconst presentProjection = (\n workspaceId: WorkspaceId,\n entry: ReleaseDeliveryGraphInspection[\"entityProjections\"][number] & {\n readonly canonicalReleaseId: ReleaseId | null\n readonly owners?: ReadonlyArray<WorkspaceEntityOwner>\n readonly ownersTruncated?: boolean\n readonly releaseIds: ReadonlyArray<ReleaseId>\n readonly releaseMembershipsTruncated: boolean\n },\n routableReleaseIds?: ReadonlySet<ReleaseId>\n): WorkspaceItemPresentation => {\n const projection = entry.projection\n const status = statusFor(projection.details)\n const routableMemberships = entry.releaseIds.filter(\n (releaseId) => routableReleaseIds === undefined || routableReleaseIds.has(releaseId)\n )\n const releaseId = entry.releaseIds.length === 1 && !entry.releaseMembershipsTruncated\n ? (routableMemberships[0] ?? null)\n : null\n const owners: ReadonlyArray<WorkspaceItemOwnerPresentation> = (entry.owners ?? []).map(({\n avatarFallback,\n displayName,\n personId,\n roles\n }) => ({\n avatarFallback,\n id: personId,\n name: displayName,\n role: [...new Set(roles.map(collaboratorRoleLabel))].join(\" · \"),\n roles\n }))\n return {\n entityId: projection.entityId,\n freshness: DateTime.formatIso(entry.recordedAt),\n href: workspaceEntityPath(workspaceId, projection.entityId),\n key: projection.displayKey,\n kind: projection.entityType,\n owner: owners.length === 0 ? \"Unassigned\" : owners.map(({ name }) => name).join(\", \"),\n owners,\n ownersTruncated: entry.ownersTruncated ?? false,\n releaseId,\n releaseIds: entry.releaseIds,\n releaseMembershipsTruncated: entry.releaseMembershipsTruncated,\n routableReleaseIds: routableMemberships,\n service: serviceFor(projection.entityType),\n status,\n ...statusPresentation(projection.entityType, status),\n title: projection.title\n }\n}\n\n/** Present one server-authoritative workspace entity index. */\nexport const presentWorkspaceEntityIndex = (\n workspaceId: WorkspaceId,\n index: WorkspaceEntityProjectionIndex,\n routableReleaseIds?: ReadonlySet<ReleaseId>\n): ReadonlyArray<WorkspaceItemPresentation> =>\n index.items\n .filter(({ projection }) => projection.entityState === \"present\")\n .map((entry) => presentProjection(workspaceId, entry, routableReleaseIds))\n .sort((left, right) => left.service.localeCompare(right.service) || left.key.localeCompare(right.key))\n\n/** Present bounded release slices as one deduplicated release-linked item index. */\nexport const presentWorkspaceItems = (\n workspaceId: WorkspaceId,\n inspections: ReadonlyArray<ReleaseDeliveryGraphInspection>\n): ReadonlyArray<WorkspaceItemPresentation> => {\n const items = new Map<EntityId, {\n readonly entry: ReleaseDeliveryGraphInspection[\"entityProjections\"][number]\n readonly releaseIds: Set<ReleaseId>\n }>()\n const canonicalInspections = [...inspections].sort((left, right) => left.releaseId.localeCompare(right.releaseId))\n for (const inspection of canonicalInspections) {\n for (const entry of inspection.entityProjections) {\n const projection = entry.projection\n if (projection.entityState !== \"present\") continue\n const existing = items.get(projection.entityId)\n if (existing !== undefined) {\n existing.releaseIds.add(inspection.releaseId)\n continue\n }\n items.set(projection.entityId, { entry, releaseIds: new Set([inspection.releaseId]) })\n }\n }\n return [...items.values()].map(({ entry, releaseIds: membershipSet }) => {\n const releaseIds = [...membershipSet].sort((left, right) => left.localeCompare(right))\n return presentProjection(workspaceId, {\n ...entry,\n canonicalReleaseId: releaseIds[0] ?? null,\n releaseIds,\n releaseMembershipsTruncated: false\n })\n }).sort((left, right) => left.service.localeCompare(right.service) || left.key.localeCompare(right.key))\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{i as e,o as t}from"./releasePaths-s-fSbIM-.js";import{A as n,D as r,Ft as i,P as a,Pt as o,j as s,w as c,x as l}from"./client-OmvShYdC.js";var u=s({hash:n,pathname:n,search:n}),d=s({_tag:r(`release-origin/v1`),origin:u,releaseId:l,workspaceId:c}),f=(e,t,n)=>{let r=a(d)(e);return o(r)?null:x(r.value,t,n).isStored?r.value:null},p=e=>{let t=a(c)(e);return i(t)?t.value:null},m=e=>{let t=a(l)(e);return i(t)?t.value:null},h=e=>({relay:`release-${e}-relay`,verdict:`release-${e}-verdict`,version:`release-${e}-version`}),g=({hash:e,pathname:t,search:n})=>({hash:e,pathname:t,search:n}),_=e=>e.length<=2048&&(e.length===0||e.startsWith(`?`)),v=e=>e.length<=1024&&(e.length===0||e.startsWith(`#`)),y=(n,r)=>n===e(r)||n===t(r),b=(e,t,n)=>({_tag:`release-origin/v1`,origin:n,releaseId:t,workspaceId:e}),x=(t,n,r)=>{let i={hash:``,pathname:e(n),search:``},s=a(d)(t);if(o(s))return{isStored:!1,origin:i};let c=s.value,l=c.workspaceId===n&&c.releaseId===r,u=y(c.origin.pathname,n);return l&&u&&_(c.origin.search)&&v(c.origin.hash)?{isStored:!0,origin:c.origin}:{isStored:!1,origin:i}},S=(e,t,n)=>x(e,t,n).origin,C=({hash:e,pathname:t,search:n})=>`${t}${n}${e}`;export{S as a,h as c,b as i,x as l,m as n,g as o,p as r,C as s,d as t,f as u};
2
- //# sourceMappingURL=releaseRoutes-DSWkTh-E.js.map
1
+ import{i as e,o as t}from"./releasePaths-s-fSbIM-.js";import{A as n,D as r,Ft as i,P as a,Pt as o,j as s,w as c,x as l}from"./client-P7FaPwzJ.js";var u=s({hash:n,pathname:n,search:n}),d=s({_tag:r(`release-origin/v1`),origin:u,releaseId:l,workspaceId:c}),f=(e,t,n)=>{let r=a(d)(e);return o(r)?null:x(r.value,t,n).isStored?r.value:null},p=e=>{let t=a(c)(e);return i(t)?t.value:null},m=e=>{let t=a(l)(e);return i(t)?t.value:null},h=e=>({relay:`release-${e}-relay`,verdict:`release-${e}-verdict`,version:`release-${e}-version`}),g=({hash:e,pathname:t,search:n})=>({hash:e,pathname:t,search:n}),_=e=>e.length<=2048&&(e.length===0||e.startsWith(`?`)),v=e=>e.length<=1024&&(e.length===0||e.startsWith(`#`)),y=(n,r)=>n===e(r)||n===t(r),b=(e,t,n)=>({_tag:`release-origin/v1`,origin:n,releaseId:t,workspaceId:e}),x=(t,n,r)=>{let i={hash:``,pathname:e(n),search:``},s=a(d)(t);if(o(s))return{isStored:!1,origin:i};let c=s.value,l=c.workspaceId===n&&c.releaseId===r,u=y(c.origin.pathname,n);return l&&u&&_(c.origin.search)&&v(c.origin.hash)?{isStored:!0,origin:c.origin}:{isStored:!1,origin:i}},S=(e,t,n)=>x(e,t,n).origin,C=({hash:e,pathname:t,search:n})=>`${t}${n}${e}`;export{S as a,h as c,b as i,x as l,m as n,g as o,p as r,C as s,d as t,f as u};
2
+ //# sourceMappingURL=releaseRoutes-CfaNElmG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"releaseRoutes-DSWkTh-E.js","names":[],"sources":["../../../src/client/releases/releaseRoutes.ts"],"sourcesContent":["import type { RlyReleaseTransitionNames } from \"@knpkv/rly/patterns\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\n\nimport {\n ReleaseId,\n type ReleaseId as ReleaseIdType,\n WorkspaceId,\n type WorkspaceId as WorkspaceIdType\n} from \"../../domain/identifiers.js\"\nimport { releaseParentPath, workspaceItemsPath } from \"./releasePaths.js\"\n\nexport {\n releaseActiveWorkPath,\n releaseAgentPath,\n releaseFullPath,\n releaseParentPath,\n releasePreviewPath,\n workspaceItemsPath\n} from \"./releasePaths.js\"\n\n/** Exact in-application location preserved while release routes are open. */\nexport interface ReleaseOrigin {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n}\n\n/** Router state carried from a release activation through preview and full view. */\nexport interface ReleaseRouteState {\n readonly _tag: \"release-origin/v1\"\n readonly origin: ReleaseOrigin\n readonly releaseId: ReleaseIdType\n readonly workspaceId: WorkspaceIdType\n}\n\n/** Decoded release origin plus whether it came from trusted versioned history state. */\nexport interface ResolvedReleaseOrigin {\n readonly isStored: boolean\n readonly origin: ReleaseOrigin\n}\n\ninterface LocationParts {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n}\n\nconst ReleaseOriginSchema = Schema.Struct({\n hash: Schema.String,\n pathname: Schema.String,\n search: Schema.String\n})\n\n/** Bounded decoder shared by nested client routes that need to retain a release activation. */\nexport const ReleaseRouteStateSchema = Schema.Struct({\n _tag: Schema.Literal(\"release-origin/v1\"),\n origin: ReleaseOriginSchema,\n releaseId: ReleaseId,\n workspaceId: WorkspaceId\n})\n\n/** Retain only safe state for the exact release target, dropping foreign or malformed history data. */\nexport const retainReleaseRouteState = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ReleaseRouteState | null => {\n const decoded = Schema.decodeUnknownOption(ReleaseRouteStateSchema)(state)\n if (Option.isNone(decoded)) return null\n return resolveReleaseOrigin(decoded.value, workspaceId, releaseId).isStored ? decoded.value : null\n}\n\n/** Decode one workspace route segment at the browser boundary. */\nexport const decodeWorkspaceRouteId = (value: unknown): WorkspaceIdType | null => {\n const decoded = Schema.decodeUnknownOption(WorkspaceId)(value)\n return Option.isSome(decoded) ? decoded.value : null\n}\n\n/** Decode one release route segment at the browser boundary. */\nexport const decodeReleaseRouteId = (value: unknown): ReleaseIdType | null => {\n const decoded = Schema.decodeUnknownOption(ReleaseId)(value)\n return Option.isSome(decoded) ? decoded.value : null\n}\n\n/** Stable, collision-free geometry names for one release's orchestrated route transition. */\nexport const releaseTransitionNames = (releaseId: ReleaseIdType): RlyReleaseTransitionNames => ({\n relay: `release-${releaseId}-relay`,\n verdict: `release-${releaseId}-verdict`,\n version: `release-${releaseId}-version`\n})\n\n/** Capture a location without retaining mutable router objects. */\nexport const releaseOriginFromLocation = ({ hash, pathname, search }: LocationParts): ReleaseOrigin => ({\n hash,\n pathname,\n search\n})\n\nconst isSearch = (search: string): boolean => search.length <= 2_048 && (search.length === 0 || search.startsWith(\"?\"))\n\nconst isHash = (hash: string): boolean => hash.length <= 1_024 && (hash.length === 0 || hash.startsWith(\"#\"))\n\nconst isRecognizedOriginPath = (pathname: string, workspaceId: WorkspaceIdType): boolean =>\n pathname === releaseParentPath(workspaceId) || pathname === workspaceItemsPath(workspaceId)\n\n/** Construct bounded route state without retaining session or snapshot data. */\nexport const makeReleaseRouteState = (\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType,\n origin: ReleaseOrigin\n): ReleaseRouteState => ({ _tag: \"release-origin/v1\", origin, releaseId, workspaceId })\n\n/** Decode history state without permitting external or malformed navigation targets. */\nexport const resolveReleaseOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ResolvedReleaseOrigin => {\n const fallback = { hash: \"\", pathname: releaseParentPath(workspaceId), search: \"\" }\n const decoded = Schema.decodeUnknownOption(ReleaseRouteStateSchema)(state)\n if (Option.isNone(decoded)) return { isStored: false, origin: fallback }\n const routeState = decoded.value\n const isMatchingTarget = routeState.workspaceId === workspaceId && routeState.releaseId === releaseId\n const isRecognizedOrigin = isRecognizedOriginPath(routeState.origin.pathname, workspaceId)\n return isMatchingTarget && isRecognizedOrigin && isSearch(routeState.origin.search) && isHash(routeState.origin.hash)\n ? { isStored: true, origin: routeState.origin }\n : { isStored: false, origin: fallback }\n}\n\n/** Decode the exact safe origin or return the release's semantic parent. */\nexport const readReleaseOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ReleaseOrigin => resolveReleaseOrigin(state, workspaceId, releaseId).origin\n\n/** Serialize an exact origin for React Router navigation. */\nexport const releaseOriginHref = ({ hash, pathname, search }: ReleaseOrigin): string => `${pathname}${search}${hash}`\n"],"mappings":"kJAgDA,IAAM,EAAsB,EAAc,CACxC,KAAM,EACN,SAAU,EACV,OAAQ,CACV,CAAC,EAGY,EAA0B,EAAc,CACnD,KAAM,EAAe,mBAAmB,EACxC,OAAQ,EACR,UAAW,EACX,YAAa,CACf,CAAC,EAGY,GACX,EACA,EACA,IAC6B,CAC7B,IAAM,EAAU,EAA2B,CAAuB,CAAC,CAAC,CAAK,EAEzE,OADI,EAAc,CAAO,EAAU,KAC5B,EAAqB,EAAQ,MAAO,EAAa,CAAS,CAAC,CAAC,SAAW,EAAQ,MAAQ,IAChG,EAGa,EAA0B,GAA2C,CAChF,IAAM,EAAU,EAA2B,CAAW,CAAC,CAAC,CAAK,EAC7D,OAAO,EAAc,CAAO,EAAI,EAAQ,MAAQ,IAClD,EAGa,EAAwB,GAAyC,CAC5E,IAAM,EAAU,EAA2B,CAAS,CAAC,CAAC,CAAK,EAC3D,OAAO,EAAc,CAAO,EAAI,EAAQ,MAAQ,IAClD,EAGa,EAA0B,IAAyD,CAC9F,MAAO,WAAW,EAAU,QAC5B,QAAS,WAAW,EAAU,UAC9B,QAAS,WAAW,EAAU,SAChC,GAGa,GAA6B,CAAE,OAAM,WAAU,aAA4C,CACtG,OACA,WACA,QACF,GAEM,EAAY,GAA4B,EAAO,QAAU,OAAU,EAAO,SAAW,GAAK,EAAO,WAAW,GAAG,GAE/G,EAAU,GAA0B,EAAK,QAAU,OAAU,EAAK,SAAW,GAAK,EAAK,WAAW,GAAG,GAErG,GAA0B,EAAkB,IAChD,IAAa,EAAkB,CAAW,GAAK,IAAa,EAAmB,CAAW,EAG/E,GACX,EACA,EACA,KACuB,CAAE,KAAM,oBAAqB,SAAQ,YAAW,aAAY,GAGxE,GACX,EACA,EACA,IAC0B,CAC1B,IAAM,EAAW,CAAE,KAAM,GAAI,SAAU,EAAkB,CAAW,EAAG,OAAQ,EAAG,EAC5E,EAAU,EAA2B,CAAuB,CAAC,CAAC,CAAK,EACzE,GAAI,EAAc,CAAO,EAAG,MAAO,CAAE,SAAU,GAAO,OAAQ,CAAS,EACvE,IAAM,EAAa,EAAQ,MACrB,EAAmB,EAAW,cAAgB,GAAe,EAAW,YAAc,EACtF,EAAqB,EAAuB,EAAW,OAAO,SAAU,CAAW,EACzF,OAAO,GAAoB,GAAsB,EAAS,EAAW,OAAO,MAAM,GAAK,EAAO,EAAW,OAAO,IAAI,EAChH,CAAE,SAAU,GAAM,OAAQ,EAAW,MAAO,EAC5C,CAAE,SAAU,GAAO,OAAQ,CAAS,CAC1C,EAGa,GACX,EACA,EACA,IACkB,EAAqB,EAAO,EAAa,CAAS,CAAC,CAAC,OAG3D,GAAqB,CAAE,OAAM,WAAU,YAAoC,GAAG,IAAW,IAAS"}
1
+ {"version":3,"file":"releaseRoutes-CfaNElmG.js","names":[],"sources":["../../../src/client/releases/releaseRoutes.ts"],"sourcesContent":["import type { RlyReleaseTransitionNames } from \"@knpkv/rly/patterns\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\n\nimport {\n ReleaseId,\n type ReleaseId as ReleaseIdType,\n WorkspaceId,\n type WorkspaceId as WorkspaceIdType\n} from \"../../domain/identifiers.js\"\nimport { releaseParentPath, workspaceItemsPath } from \"./releasePaths.js\"\n\nexport {\n releaseActiveWorkPath,\n releaseAgentPath,\n releaseFullPath,\n releaseParentPath,\n releasePreviewPath,\n workspaceItemsPath\n} from \"./releasePaths.js\"\n\n/** Exact in-application location preserved while release routes are open. */\nexport interface ReleaseOrigin {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n}\n\n/** Router state carried from a release activation through preview and full view. */\nexport interface ReleaseRouteState {\n readonly _tag: \"release-origin/v1\"\n readonly origin: ReleaseOrigin\n readonly releaseId: ReleaseIdType\n readonly workspaceId: WorkspaceIdType\n}\n\n/** Decoded release origin plus whether it came from trusted versioned history state. */\nexport interface ResolvedReleaseOrigin {\n readonly isStored: boolean\n readonly origin: ReleaseOrigin\n}\n\ninterface LocationParts {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n}\n\nconst ReleaseOriginSchema = Schema.Struct({\n hash: Schema.String,\n pathname: Schema.String,\n search: Schema.String\n})\n\n/** Bounded decoder shared by nested client routes that need to retain a release activation. */\nexport const ReleaseRouteStateSchema = Schema.Struct({\n _tag: Schema.Literal(\"release-origin/v1\"),\n origin: ReleaseOriginSchema,\n releaseId: ReleaseId,\n workspaceId: WorkspaceId\n})\n\n/** Retain only safe state for the exact release target, dropping foreign or malformed history data. */\nexport const retainReleaseRouteState = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ReleaseRouteState | null => {\n const decoded = Schema.decodeUnknownOption(ReleaseRouteStateSchema)(state)\n if (Option.isNone(decoded)) return null\n return resolveReleaseOrigin(decoded.value, workspaceId, releaseId).isStored ? decoded.value : null\n}\n\n/** Decode one workspace route segment at the browser boundary. */\nexport const decodeWorkspaceRouteId = (value: unknown): WorkspaceIdType | null => {\n const decoded = Schema.decodeUnknownOption(WorkspaceId)(value)\n return Option.isSome(decoded) ? decoded.value : null\n}\n\n/** Decode one release route segment at the browser boundary. */\nexport const decodeReleaseRouteId = (value: unknown): ReleaseIdType | null => {\n const decoded = Schema.decodeUnknownOption(ReleaseId)(value)\n return Option.isSome(decoded) ? decoded.value : null\n}\n\n/** Stable, collision-free geometry names for one release's orchestrated route transition. */\nexport const releaseTransitionNames = (releaseId: ReleaseIdType): RlyReleaseTransitionNames => ({\n relay: `release-${releaseId}-relay`,\n verdict: `release-${releaseId}-verdict`,\n version: `release-${releaseId}-version`\n})\n\n/** Capture a location without retaining mutable router objects. */\nexport const releaseOriginFromLocation = ({ hash, pathname, search }: LocationParts): ReleaseOrigin => ({\n hash,\n pathname,\n search\n})\n\nconst isSearch = (search: string): boolean => search.length <= 2_048 && (search.length === 0 || search.startsWith(\"?\"))\n\nconst isHash = (hash: string): boolean => hash.length <= 1_024 && (hash.length === 0 || hash.startsWith(\"#\"))\n\nconst isRecognizedOriginPath = (pathname: string, workspaceId: WorkspaceIdType): boolean =>\n pathname === releaseParentPath(workspaceId) || pathname === workspaceItemsPath(workspaceId)\n\n/** Construct bounded route state without retaining session or snapshot data. */\nexport const makeReleaseRouteState = (\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType,\n origin: ReleaseOrigin\n): ReleaseRouteState => ({ _tag: \"release-origin/v1\", origin, releaseId, workspaceId })\n\n/** Decode history state without permitting external or malformed navigation targets. */\nexport const resolveReleaseOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ResolvedReleaseOrigin => {\n const fallback = { hash: \"\", pathname: releaseParentPath(workspaceId), search: \"\" }\n const decoded = Schema.decodeUnknownOption(ReleaseRouteStateSchema)(state)\n if (Option.isNone(decoded)) return { isStored: false, origin: fallback }\n const routeState = decoded.value\n const isMatchingTarget = routeState.workspaceId === workspaceId && routeState.releaseId === releaseId\n const isRecognizedOrigin = isRecognizedOriginPath(routeState.origin.pathname, workspaceId)\n return isMatchingTarget && isRecognizedOrigin && isSearch(routeState.origin.search) && isHash(routeState.origin.hash)\n ? { isStored: true, origin: routeState.origin }\n : { isStored: false, origin: fallback }\n}\n\n/** Decode the exact safe origin or return the release's semantic parent. */\nexport const readReleaseOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n releaseId: ReleaseIdType\n): ReleaseOrigin => resolveReleaseOrigin(state, workspaceId, releaseId).origin\n\n/** Serialize an exact origin for React Router navigation. */\nexport const releaseOriginHref = ({ hash, pathname, search }: ReleaseOrigin): string => `${pathname}${search}${hash}`\n"],"mappings":"kJAgDA,IAAM,EAAsB,EAAc,CACxC,KAAM,EACN,SAAU,EACV,OAAQ,CACV,CAAC,EAGY,EAA0B,EAAc,CACnD,KAAM,EAAe,mBAAmB,EACxC,OAAQ,EACR,UAAW,EACX,YAAa,CACf,CAAC,EAGY,GACX,EACA,EACA,IAC6B,CAC7B,IAAM,EAAU,EAA2B,CAAuB,CAAC,CAAC,CAAK,EAEzE,OADI,EAAc,CAAO,EAAU,KAC5B,EAAqB,EAAQ,MAAO,EAAa,CAAS,CAAC,CAAC,SAAW,EAAQ,MAAQ,IAChG,EAGa,EAA0B,GAA2C,CAChF,IAAM,EAAU,EAA2B,CAAW,CAAC,CAAC,CAAK,EAC7D,OAAO,EAAc,CAAO,EAAI,EAAQ,MAAQ,IAClD,EAGa,EAAwB,GAAyC,CAC5E,IAAM,EAAU,EAA2B,CAAS,CAAC,CAAC,CAAK,EAC3D,OAAO,EAAc,CAAO,EAAI,EAAQ,MAAQ,IAClD,EAGa,EAA0B,IAAyD,CAC9F,MAAO,WAAW,EAAU,QAC5B,QAAS,WAAW,EAAU,UAC9B,QAAS,WAAW,EAAU,SAChC,GAGa,GAA6B,CAAE,OAAM,WAAU,aAA4C,CACtG,OACA,WACA,QACF,GAEM,EAAY,GAA4B,EAAO,QAAU,OAAU,EAAO,SAAW,GAAK,EAAO,WAAW,GAAG,GAE/G,EAAU,GAA0B,EAAK,QAAU,OAAU,EAAK,SAAW,GAAK,EAAK,WAAW,GAAG,GAErG,GAA0B,EAAkB,IAChD,IAAa,EAAkB,CAAW,GAAK,IAAa,EAAmB,CAAW,EAG/E,GACX,EACA,EACA,KACuB,CAAE,KAAM,oBAAqB,SAAQ,YAAW,aAAY,GAGxE,GACX,EACA,EACA,IAC0B,CAC1B,IAAM,EAAW,CAAE,KAAM,GAAI,SAAU,EAAkB,CAAW,EAAG,OAAQ,EAAG,EAC5E,EAAU,EAA2B,CAAuB,CAAC,CAAC,CAAK,EACzE,GAAI,EAAc,CAAO,EAAG,MAAO,CAAE,SAAU,GAAO,OAAQ,CAAS,EACvE,IAAM,EAAa,EAAQ,MACrB,EAAmB,EAAW,cAAgB,GAAe,EAAW,YAAc,EACtF,EAAqB,EAAuB,EAAW,OAAO,SAAU,CAAW,EACzF,OAAO,GAAoB,GAAsB,EAAS,EAAW,OAAO,MAAM,GAAK,EAAO,EAAW,OAAO,IAAI,EAChH,CAAE,SAAU,GAAM,OAAQ,EAAW,MAAO,EAC5C,CAAE,SAAU,GAAO,OAAQ,CAAS,CAC1C,EAGa,GACX,EACA,EACA,IACkB,EAAqB,EAAO,EAAa,CAAS,CAAC,CAAC,OAG3D,GAAqB,CAAE,OAAM,WAAU,YAAoC,GAAG,IAAW,IAAS"}
@@ -1,2 +1,2 @@
1
- import{A as e,j as t,u as n}from"./client-OmvShYdC.js";t({providerId:n,displayName:e,description:e}).annotate({identifier:`FirstPartyServiceIdentity`});var r={codecommit:{providerId:`codecommit`,displayName:`CodeCommit`,description:`Read pull requests from one AWS CodeCommit repository.`},codepipeline:{providerId:`codepipeline`,displayName:`CodePipeline`,description:`Read pipeline and execution state from AWS CodePipeline.`},jira:{providerId:`jira`,displayName:`Jira`,description:`Read delivery issues from Jira Cloud.`},confluence:{providerId:`confluence`,displayName:`Confluence`,description:`Read release documentation from Confluence Cloud.`},clockify:{providerId:`clockify`,displayName:`Clockify`,description:`Read bounded time-entry evidence from Clockify.`}},i=[r.codecommit,r.codepipeline,r.jira,r.confluence,r.clockify],a=e=>i.find(({providerId:t})=>t===e)?.providerId??null,o=e=>`/pair?service=${e}`,s=(e,t)=>a(e.get(t)),c=e=>`/services?enable=${e}`,l=(e,t=null,n=null)=>{let r=new URLSearchParams({enable:e.includes(`jira`)?`jira`:`confluence`});t!==null&&r.set(`atlassianProfile`,t),n!==null&&r.set(`atlassianSite`,n);for(let t of e)r.append(`atlassianProvider`,t);return`/services?${r.toString()}`},u=e=>{let t=e.getAll(`atlassianSite`);if(t.length!==1)return null;let[n]=t;return n!==void 0&&n.length>0&&n.length<=512&&n.trim()===n?n:null},d=e=>{let t=e.getAll(`atlassianProfile`);if(t.length!==1)return null;let[n]=t;return n!==void 0&&n.length>0&&n.length<=500&&n.trim()===n?n:null},f=e=>{let t=e.getAll(`atlassianProvider`);return t.length===1&&t[0]===`jira`?[`jira`]:t.length===1&&t[0]===`confluence`?[`confluence`]:t.length===2&&t.includes(`jira`)&&t.includes(`confluence`)?[`jira`,`confluence`]:null};export{s as a,i as c,u as i,d as n,o,f as r,c as s,l as t};
2
- //# sourceMappingURL=serviceOnboarding-B9pvdyWf.js.map
1
+ import{A as e,j as t,u as n}from"./client-P7FaPwzJ.js";t({providerId:n,displayName:e,description:e}).annotate({identifier:`FirstPartyServiceIdentity`});var r={codecommit:{providerId:`codecommit`,displayName:`CodeCommit`,description:`Read pull requests from one AWS CodeCommit repository.`},codepipeline:{providerId:`codepipeline`,displayName:`CodePipeline`,description:`Read pipeline and execution state from AWS CodePipeline.`},jira:{providerId:`jira`,displayName:`Jira`,description:`Read delivery issues from Jira Cloud.`},confluence:{providerId:`confluence`,displayName:`Confluence`,description:`Read release documentation from Confluence Cloud.`},clockify:{providerId:`clockify`,displayName:`Clockify`,description:`Read bounded time-entry evidence from Clockify.`}},i=[r.codecommit,r.codepipeline,r.jira,r.confluence,r.clockify],a=e=>i.find(({providerId:t})=>t===e)?.providerId??null,o=e=>`/pair?service=${e}`,s=(e,t)=>a(e.get(t)),c=e=>`/services?enable=${e}`,l=(e,t=null,n=null)=>{let r=new URLSearchParams({enable:e.includes(`jira`)?`jira`:`confluence`});t!==null&&r.set(`atlassianProfile`,t),n!==null&&r.set(`atlassianSite`,n);for(let t of e)r.append(`atlassianProvider`,t);return`/services?${r.toString()}`},u=e=>{let t=e.getAll(`atlassianSite`);if(t.length!==1)return null;let[n]=t;return n!==void 0&&n.length>0&&n.length<=512&&n.trim()===n?n:null},d=e=>{let t=e.getAll(`atlassianProfile`);if(t.length!==1)return null;let[n]=t;return n!==void 0&&n.length>0&&n.length<=500&&n.trim()===n?n:null},f=e=>{let t=e.getAll(`atlassianProvider`);return t.length===1&&t[0]===`jira`?[`jira`]:t.length===1&&t[0]===`confluence`?[`confluence`]:t.length===2&&t.includes(`jira`)&&t.includes(`confluence`)?[`jira`,`confluence`]:null};export{s as a,i as c,u as i,d as n,o,f as r,c as s,l as t};
2
+ //# sourceMappingURL=serviceOnboarding-BZ2U_Ugw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"serviceOnboarding-B9pvdyWf.js","names":[],"sources":["../../../src/domain/firstPartyServices.ts","../../../src/client/services/serviceOnboarding.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\"\n\nimport { ProviderId } from \"./sourceRevision.js\"\n\n/** Safe provider identity rendered before a browser has access to workspace connections. */\nexport const FirstPartyServiceIdentity = Schema.Struct({\n providerId: ProviderId,\n displayName: Schema.String,\n description: Schema.String\n}).annotate({ identifier: \"FirstPartyServiceIdentity\" })\n\n/** Decoded safe first-party provider identity. */\nexport type FirstPartyServiceIdentity = typeof FirstPartyServiceIdentity.Type\n\n/** Stable provider lookup shared by public previews and the server-owned setup catalog. */\nexport const firstPartyServiceIdentityByProvider = {\n codecommit: {\n providerId: \"codecommit\",\n displayName: \"CodeCommit\",\n description: \"Read pull requests from one AWS CodeCommit repository.\"\n },\n codepipeline: {\n providerId: \"codepipeline\",\n displayName: \"CodePipeline\",\n description: \"Read pipeline and execution state from AWS CodePipeline.\"\n },\n jira: {\n providerId: \"jira\",\n displayName: \"Jira\",\n description: \"Read delivery issues from Jira Cloud.\"\n },\n confluence: {\n providerId: \"confluence\",\n displayName: \"Confluence\",\n description: \"Read release documentation from Confluence Cloud.\"\n },\n clockify: {\n providerId: \"clockify\",\n displayName: \"Clockify\",\n description: \"Read bounded time-entry evidence from Clockify.\"\n }\n} satisfies Readonly<Record<typeof ProviderId.Type, FirstPartyServiceIdentity>>\n\n/** Stable first-party services available to every Control Center installation. */\nexport const firstPartyServiceIdentities = [\n firstPartyServiceIdentityByProvider.codecommit,\n firstPartyServiceIdentityByProvider.codepipeline,\n firstPartyServiceIdentityByProvider.jira,\n firstPartyServiceIdentityByProvider.confluence,\n firstPartyServiceIdentityByProvider.clockify\n] satisfies ReadonlyArray<FirstPartyServiceIdentity>\n","import type { AtlassianOAuthProviderIntent } from \"../../api/plugins.js\"\nimport { firstPartyServiceIdentities } from \"../../domain/firstPartyServices.js\"\nimport type { ProviderId } from \"../../domain/sourceRevision.js\"\n\nconst providerFrom = (value: string | null): ProviderId | null =>\n firstPartyServiceIdentities.find(({ providerId }) => providerId === value)?.providerId ?? null\n\n/** Pairing route that remembers which service the person chose to enable. */\nexport const servicePairingPath = (providerId: ProviderId): string => `/pair?service=${providerId}`\n\n/** Service selected by an untrusted browser query parameter. */\nexport const selectedServiceProvider = (searchParams: URLSearchParams, key: \"enable\" | \"service\"): ProviderId | null =>\n providerFrom(searchParams.get(key))\n\n/** Post-pairing destination that opens the selected service setup in place. */\nexport const serviceSetupPath = (providerId: ProviderId): string => `/services?enable=${providerId}`\n\n/** Atlassian setup destination that preserves the initiating products and, when present, the completed profile. */\nexport const atlassianOAuthSetupPath = (\n providers: AtlassianOAuthProviderIntent,\n profileId: string | null = null,\n preferredSiteId: string | null = null\n): string => {\n const searchParams = new URLSearchParams({ enable: providers.includes(\"jira\") ? \"jira\" : \"confluence\" })\n if (profileId !== null) searchParams.set(\"atlassianProfile\", profileId)\n if (preferredSiteId !== null) searchParams.set(\"atlassianSite\", preferredSiteId)\n for (const provider of providers) searchParams.append(\"atlassianProvider\", provider)\n return `/services?${searchParams.toString()}`\n}\n\n/** Atlassian site pinned by a bounded, untrusted browser query parameter. */\nexport const selectedAtlassianOAuthSiteId = (searchParams: URLSearchParams): string | null => {\n const sites = searchParams.getAll(\"atlassianSite\")\n if (sites.length !== 1) return null\n const [siteId] = sites\n return siteId !== undefined && siteId.length > 0 && siteId.length <= 512 && siteId.trim() === siteId ? siteId : null\n}\n\n/** Completed Atlassian profile selected by a bounded, untrusted browser query parameter. */\nexport const selectedAtlassianOAuthProfileId = (searchParams: URLSearchParams): string | null => {\n const profiles = searchParams.getAll(\"atlassianProfile\")\n if (profiles.length !== 1) return null\n const [profileId] = profiles\n return profileId !== undefined && profileId.length > 0 && profileId.length <= 500 && profileId.trim() === profileId\n ? profileId\n : null\n}\n\n/** Atlassian product intent selected by repeated, untrusted browser query parameters. */\nexport const selectedAtlassianOAuthProviders = (\n searchParams: URLSearchParams\n): AtlassianOAuthProviderIntent | null => {\n const providers = searchParams.getAll(\"atlassianProvider\")\n if (providers.length === 1 && providers[0] === \"jira\") return [\"jira\"]\n if (providers.length === 1 && providers[0] === \"confluence\") return [\"confluence\"]\n if (providers.length === 2 && providers.includes(\"jira\") && providers.includes(\"confluence\")) {\n return [\"jira\", \"confluence\"]\n }\n return null\n}\n"],"mappings":"uDAKyC,EAAc,CACrD,WAAY,EACZ,YAAa,EACb,YAAa,CACf,CAAC,CAAC,CAAC,SAAS,CAAE,WAAY,2BAA4B,CAAC,EAMvD,IAAa,EAAsC,CACjD,WAAY,CACV,WAAY,aACZ,YAAa,aACb,YAAa,wDACf,EACA,aAAc,CACZ,WAAY,eACZ,YAAa,eACb,YAAa,0DACf,EACA,KAAM,CACJ,WAAY,OACZ,YAAa,OACb,YAAa,uCACf,EACA,WAAY,CACV,WAAY,aACZ,YAAa,aACb,YAAa,mDACf,EACA,SAAU,CACR,WAAY,WACZ,YAAa,WACb,YAAa,iDACf,CACF,EAGa,EAA8B,CACzC,EAAoC,WACpC,EAAoC,aACpC,EAAoC,KACpC,EAAoC,WACpC,EAAoC,QACtC,EC9CM,EAAgB,GACpB,EAA4B,MAAM,CAAE,gBAAiB,IAAe,CAAK,CAAC,EAAE,YAAc,KAG/E,EAAsB,GAAmC,iBAAiB,IAG1E,GAA2B,EAA+B,IACrE,EAAa,EAAa,IAAI,CAAG,CAAC,EAGvB,EAAoB,GAAmC,oBAAoB,IAG3E,GACX,EACA,EAA2B,KAC3B,EAAiC,OACtB,CACX,IAAM,EAAe,IAAI,gBAAgB,CAAE,OAAQ,EAAU,SAAS,MAAM,EAAI,OAAS,YAAa,CAAC,EACnG,IAAc,MAAM,EAAa,IAAI,mBAAoB,CAAS,EAClE,IAAoB,MAAM,EAAa,IAAI,gBAAiB,CAAe,EAC/E,IAAK,IAAM,KAAY,EAAW,EAAa,OAAO,oBAAqB,CAAQ,EACnF,MAAO,aAAa,EAAa,SAAS,GAC5C,EAGa,EAAgC,GAAiD,CAC5F,IAAM,EAAQ,EAAa,OAAO,eAAe,EACjD,GAAI,EAAM,SAAW,EAAG,OAAO,KAC/B,GAAM,CAAC,GAAU,EACjB,OAAO,IAAW,IAAA,IAAa,EAAO,OAAS,GAAK,EAAO,QAAU,KAAO,EAAO,KAAK,IAAM,EAAS,EAAS,IAClH,EAGa,EAAmC,GAAiD,CAC/F,IAAM,EAAW,EAAa,OAAO,kBAAkB,EACvD,GAAI,EAAS,SAAW,EAAG,OAAO,KAClC,GAAM,CAAC,GAAa,EACpB,OAAO,IAAc,IAAA,IAAa,EAAU,OAAS,GAAK,EAAU,QAAU,KAAO,EAAU,KAAK,IAAM,EACtG,EACA,IACN,EAGa,EACX,GACwC,CACxC,IAAM,EAAY,EAAa,OAAO,mBAAmB,EAMzD,OALI,EAAU,SAAW,GAAK,EAAU,KAAO,OAAe,CAAC,MAAM,EACjE,EAAU,SAAW,GAAK,EAAU,KAAO,aAAqB,CAAC,YAAY,EAC7E,EAAU,SAAW,GAAK,EAAU,SAAS,MAAM,GAAK,EAAU,SAAS,YAAY,EAClF,CAAC,OAAQ,YAAY,EAEvB,IACT"}
1
+ {"version":3,"file":"serviceOnboarding-BZ2U_Ugw.js","names":[],"sources":["../../../src/domain/firstPartyServices.ts","../../../src/client/services/serviceOnboarding.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\"\n\nimport { ProviderId } from \"./sourceRevision.js\"\n\n/** Safe provider identity rendered before a browser has access to workspace connections. */\nexport const FirstPartyServiceIdentity = Schema.Struct({\n providerId: ProviderId,\n displayName: Schema.String,\n description: Schema.String\n}).annotate({ identifier: \"FirstPartyServiceIdentity\" })\n\n/** Decoded safe first-party provider identity. */\nexport type FirstPartyServiceIdentity = typeof FirstPartyServiceIdentity.Type\n\n/** Stable provider lookup shared by public previews and the server-owned setup catalog. */\nexport const firstPartyServiceIdentityByProvider = {\n codecommit: {\n providerId: \"codecommit\",\n displayName: \"CodeCommit\",\n description: \"Read pull requests from one AWS CodeCommit repository.\"\n },\n codepipeline: {\n providerId: \"codepipeline\",\n displayName: \"CodePipeline\",\n description: \"Read pipeline and execution state from AWS CodePipeline.\"\n },\n jira: {\n providerId: \"jira\",\n displayName: \"Jira\",\n description: \"Read delivery issues from Jira Cloud.\"\n },\n confluence: {\n providerId: \"confluence\",\n displayName: \"Confluence\",\n description: \"Read release documentation from Confluence Cloud.\"\n },\n clockify: {\n providerId: \"clockify\",\n displayName: \"Clockify\",\n description: \"Read bounded time-entry evidence from Clockify.\"\n }\n} satisfies Readonly<Record<typeof ProviderId.Type, FirstPartyServiceIdentity>>\n\n/** Stable first-party services available to every Control Center installation. */\nexport const firstPartyServiceIdentities = [\n firstPartyServiceIdentityByProvider.codecommit,\n firstPartyServiceIdentityByProvider.codepipeline,\n firstPartyServiceIdentityByProvider.jira,\n firstPartyServiceIdentityByProvider.confluence,\n firstPartyServiceIdentityByProvider.clockify\n] satisfies ReadonlyArray<FirstPartyServiceIdentity>\n","import type { AtlassianOAuthProviderIntent } from \"../../api/plugins.js\"\nimport { firstPartyServiceIdentities } from \"../../domain/firstPartyServices.js\"\nimport type { ProviderId } from \"../../domain/sourceRevision.js\"\n\nconst providerFrom = (value: string | null): ProviderId | null =>\n firstPartyServiceIdentities.find(({ providerId }) => providerId === value)?.providerId ?? null\n\n/** Pairing route that remembers which service the person chose to enable. */\nexport const servicePairingPath = (providerId: ProviderId): string => `/pair?service=${providerId}`\n\n/** Service selected by an untrusted browser query parameter. */\nexport const selectedServiceProvider = (searchParams: URLSearchParams, key: \"enable\" | \"service\"): ProviderId | null =>\n providerFrom(searchParams.get(key))\n\n/** Post-pairing destination that opens the selected service setup in place. */\nexport const serviceSetupPath = (providerId: ProviderId): string => `/services?enable=${providerId}`\n\n/** Atlassian setup destination that preserves the initiating products and, when present, the completed profile. */\nexport const atlassianOAuthSetupPath = (\n providers: AtlassianOAuthProviderIntent,\n profileId: string | null = null,\n preferredSiteId: string | null = null\n): string => {\n const searchParams = new URLSearchParams({ enable: providers.includes(\"jira\") ? \"jira\" : \"confluence\" })\n if (profileId !== null) searchParams.set(\"atlassianProfile\", profileId)\n if (preferredSiteId !== null) searchParams.set(\"atlassianSite\", preferredSiteId)\n for (const provider of providers) searchParams.append(\"atlassianProvider\", provider)\n return `/services?${searchParams.toString()}`\n}\n\n/** Atlassian site pinned by a bounded, untrusted browser query parameter. */\nexport const selectedAtlassianOAuthSiteId = (searchParams: URLSearchParams): string | null => {\n const sites = searchParams.getAll(\"atlassianSite\")\n if (sites.length !== 1) return null\n const [siteId] = sites\n return siteId !== undefined && siteId.length > 0 && siteId.length <= 512 && siteId.trim() === siteId ? siteId : null\n}\n\n/** Completed Atlassian profile selected by a bounded, untrusted browser query parameter. */\nexport const selectedAtlassianOAuthProfileId = (searchParams: URLSearchParams): string | null => {\n const profiles = searchParams.getAll(\"atlassianProfile\")\n if (profiles.length !== 1) return null\n const [profileId] = profiles\n return profileId !== undefined && profileId.length > 0 && profileId.length <= 500 && profileId.trim() === profileId\n ? profileId\n : null\n}\n\n/** Atlassian product intent selected by repeated, untrusted browser query parameters. */\nexport const selectedAtlassianOAuthProviders = (\n searchParams: URLSearchParams\n): AtlassianOAuthProviderIntent | null => {\n const providers = searchParams.getAll(\"atlassianProvider\")\n if (providers.length === 1 && providers[0] === \"jira\") return [\"jira\"]\n if (providers.length === 1 && providers[0] === \"confluence\") return [\"confluence\"]\n if (providers.length === 2 && providers.includes(\"jira\") && providers.includes(\"confluence\")) {\n return [\"jira\", \"confluence\"]\n }\n return null\n}\n"],"mappings":"uDAKyC,EAAc,CACrD,WAAY,EACZ,YAAa,EACb,YAAa,CACf,CAAC,CAAC,CAAC,SAAS,CAAE,WAAY,2BAA4B,CAAC,EAMvD,IAAa,EAAsC,CACjD,WAAY,CACV,WAAY,aACZ,YAAa,aACb,YAAa,wDACf,EACA,aAAc,CACZ,WAAY,eACZ,YAAa,eACb,YAAa,0DACf,EACA,KAAM,CACJ,WAAY,OACZ,YAAa,OACb,YAAa,uCACf,EACA,WAAY,CACV,WAAY,aACZ,YAAa,aACb,YAAa,mDACf,EACA,SAAU,CACR,WAAY,WACZ,YAAa,WACb,YAAa,iDACf,CACF,EAGa,EAA8B,CACzC,EAAoC,WACpC,EAAoC,aACpC,EAAoC,KACpC,EAAoC,WACpC,EAAoC,QACtC,EC9CM,EAAgB,GACpB,EAA4B,MAAM,CAAE,gBAAiB,IAAe,CAAK,CAAC,EAAE,YAAc,KAG/E,EAAsB,GAAmC,iBAAiB,IAG1E,GAA2B,EAA+B,IACrE,EAAa,EAAa,IAAI,CAAG,CAAC,EAGvB,EAAoB,GAAmC,oBAAoB,IAG3E,GACX,EACA,EAA2B,KAC3B,EAAiC,OACtB,CACX,IAAM,EAAe,IAAI,gBAAgB,CAAE,OAAQ,EAAU,SAAS,MAAM,EAAI,OAAS,YAAa,CAAC,EACnG,IAAc,MAAM,EAAa,IAAI,mBAAoB,CAAS,EAClE,IAAoB,MAAM,EAAa,IAAI,gBAAiB,CAAe,EAC/E,IAAK,IAAM,KAAY,EAAW,EAAa,OAAO,oBAAqB,CAAQ,EACnF,MAAO,aAAa,EAAa,SAAS,GAC5C,EAGa,EAAgC,GAAiD,CAC5F,IAAM,EAAQ,EAAa,OAAO,eAAe,EACjD,GAAI,EAAM,SAAW,EAAG,OAAO,KAC/B,GAAM,CAAC,GAAU,EACjB,OAAO,IAAW,IAAA,IAAa,EAAO,OAAS,GAAK,EAAO,QAAU,KAAO,EAAO,KAAK,IAAM,EAAS,EAAS,IAClH,EAGa,EAAmC,GAAiD,CAC/F,IAAM,EAAW,EAAa,OAAO,kBAAkB,EACvD,GAAI,EAAS,SAAW,EAAG,OAAO,KAClC,GAAM,CAAC,GAAa,EACpB,OAAO,IAAc,IAAA,IAAa,EAAU,OAAS,GAAK,EAAU,QAAU,KAAO,EAAU,KAAK,IAAM,EACtG,EACA,IACN,EAGa,EACX,GACwC,CACxC,IAAM,EAAY,EAAa,OAAO,mBAAmB,EAMzD,OALI,EAAU,SAAW,GAAK,EAAU,KAAO,OAAe,CAAC,MAAM,EACjE,EAAU,SAAW,GAAK,EAAU,KAAO,aAAqB,CAAC,YAAY,EAC7E,EAAU,SAAW,GAAK,EAAU,SAAS,MAAM,GAAK,EAAU,SAAS,YAAY,EAClF,CAAC,OAAQ,YAAY,EAEvB,IACT"}
@@ -1,2 +1,2 @@
1
- import{a as e,n as t}from"./jsx-runtime-n5LQ9ujS.js";import{dt as n,i as r,pt as i,st as a,t as o,zt as s}from"./client-OmvShYdC.js";import{t as c}from"./presentWorkspaceItems-BU__8LcC.js";var l=e(t(),1),u=s(`UnauthorizedApiError`),d={load:(e,t)=>i(a(function*(){return yield*(yield*o()).deliveryGraph.workspaceEntityProjections({query:{...t.query.length===0?{}:{q:t.query},...t.owner===`all`?{}:{owner:t.owner},...t.service===`all`?{}:{service:t.service},...t.status===`all`?{}:{status:t.status},...t.type===`all`?{}:{type:t.type}}})}).pipe(n(r)),{signal:e})},f=(e,t,n,r,i,a,o=d)=>{let[s,f]=(0,l.useState)(0),[p,m]=(0,l.useState)({_tag:`idle`}),h=[...t].join(`:`),g=(0,l.useMemo)(()=>({owner:n.owner,query:n.query,service:n.service,status:n.status,type:n.type}),[n.owner,n.query,n.service,n.status,n.type]),_=`${e}|${h}|${r}|${`${g.query}|${g.owner}|${g.service}|${g.status}|${g.type}`}`;(0,l.useEffect)(()=>{if(i===null){m({_tag:`idle`});return}let n=new AbortController;return m(e=>e._tag===`ready`&&e.sessionKey===i?e:{_tag:`loading`,scopeKey:_,sessionKey:i}),o.load(n.signal,g).then(r=>{n.signal.aborted||m({_tag:`ready`,items:c(e,r,t),matchedCount:r.matchedCount,ownerOptions:r.ownerOptions,ownerOptionsTruncated:r.ownerOptionsTruncated,refreshing:!1,scopeKey:_,sessionKey:i,totalCount:r.totalCount,truncated:r.truncated})},e=>{n.signal.aborted||(u(e)&&a(i),m({_tag:`failed`,scopeKey:_,sessionKey:i}))}),()=>n.abort()},[a,g,s,t,_,i,o,e]);let v=i===null?{_tag:`idle`}:p._tag===`idle`||p.sessionKey!==i?{_tag:`loading`,scopeKey:_,sessionKey:i}:p._tag===`ready`&&p.scopeKey!==_?{...p,refreshing:!0}:p._tag!==`ready`&&p.scopeKey!==_?{_tag:`loading`,scopeKey:_,sessionKey:i}:p;return{retry:(0,l.useCallback)(()=>f(e=>e+1),[]),state:v}};export{f as n,d as t};
2
- //# sourceMappingURL=useWorkspaceItems-D0FuHGto.js.map
1
+ import{a as e,n as t}from"./jsx-runtime-n5LQ9ujS.js";import{dt as n,i as r,pt as i,st as a,t as o,zt as s}from"./client-P7FaPwzJ.js";import{t as c}from"./presentWorkspaceItems-IAp5SnpB.js";var l=e(t(),1),u=s(`UnauthorizedApiError`),d={load:(e,t)=>i(a(function*(){return yield*(yield*o()).deliveryGraph.workspaceEntityProjections({query:{...t.query.length===0?{}:{q:t.query},...t.owner===`all`?{}:{owner:t.owner},...t.service===`all`?{}:{service:t.service},...t.status===`all`?{}:{status:t.status},...t.type===`all`?{}:{type:t.type}}})}).pipe(n(r)),{signal:e})},f=(e,t,n,r,i,a,o=d)=>{let[s,f]=(0,l.useState)(0),[p,m]=(0,l.useState)({_tag:`idle`}),h=[...t].join(`:`),g=(0,l.useMemo)(()=>({owner:n.owner,query:n.query,service:n.service,status:n.status,type:n.type}),[n.owner,n.query,n.service,n.status,n.type]),_=`${e}|${h}|${r}|${`${g.query}|${g.owner}|${g.service}|${g.status}|${g.type}`}`;(0,l.useEffect)(()=>{if(i===null){m({_tag:`idle`});return}let n=new AbortController;return m(e=>e._tag===`ready`&&e.sessionKey===i?e:{_tag:`loading`,scopeKey:_,sessionKey:i}),o.load(n.signal,g).then(r=>{n.signal.aborted||m({_tag:`ready`,items:c(e,r,t),matchedCount:r.matchedCount,ownerOptions:r.ownerOptions,ownerOptionsTruncated:r.ownerOptionsTruncated,refreshing:!1,scopeKey:_,sessionKey:i,totalCount:r.totalCount,truncated:r.truncated})},e=>{n.signal.aborted||(u(e)&&a(i),m({_tag:`failed`,scopeKey:_,sessionKey:i}))}),()=>n.abort()},[a,g,s,t,_,i,o,e]);let v=i===null?{_tag:`idle`}:p._tag===`idle`||p.sessionKey!==i?{_tag:`loading`,scopeKey:_,sessionKey:i}:p._tag===`ready`&&p.scopeKey!==_?{...p,refreshing:!0}:p._tag!==`ready`&&p.scopeKey!==_?{_tag:`loading`,scopeKey:_,sessionKey:i}:p;return{retry:(0,l.useCallback)(()=>f(e=>e+1),[]),state:v}};export{f as n,d as t};
2
+ //# sourceMappingURL=useWorkspaceItems-DQTYaq1R.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWorkspaceItems-D0FuHGto.js","names":[],"sources":["../../../src/client/items/useWorkspaceItems.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\"\nimport { useCallback, useEffect, useMemo, useState } from \"react\"\n\nimport { makeControlCenterApiClient } from \"../../api/client.js\"\nimport type { WorkspaceEntityProjectionIndex } from \"../../api/deliveryGraph.js\"\nimport type {\n DeliveryEntityKind,\n DeliveryEntityService,\n DeliveryEntityStatusGroup\n} from \"../../domain/deliveryGraph.js\"\nimport type { PersonId, ReleaseId, WorkspaceId } from \"../../domain/identifiers.js\"\nimport { presentWorkspaceEntityIndex, type WorkspaceItemPresentation } from \"./presentWorkspaceItems.js\"\n\nexport type WorkspaceItemsState =\n | { readonly _tag: \"idle\" }\n | { readonly _tag: \"loading\"; readonly scopeKey: string; readonly sessionKey: string }\n | { readonly _tag: \"failed\"; readonly scopeKey: string; readonly sessionKey: string }\n | {\n readonly _tag: \"ready\"\n readonly items: ReadonlyArray<WorkspaceItemPresentation>\n readonly matchedCount: number\n readonly ownerOptions: WorkspaceEntityProjectionIndex[\"ownerOptions\"]\n readonly ownerOptionsTruncated: boolean\n readonly refreshing: boolean\n readonly scopeKey: string\n readonly sessionKey: string\n readonly truncated: boolean\n readonly totalCount: number\n }\n\nexport interface WorkspaceItemsQuery {\n readonly owner: PersonId | \"all\"\n readonly query: string\n readonly service: DeliveryEntityService | \"all\"\n readonly status: DeliveryEntityStatusGroup | \"all\"\n readonly type: DeliveryEntityKind | \"all\"\n}\n\nexport interface WorkspaceItemsTransport {\n readonly load: (signal: AbortSignal, query: WorkspaceItemsQuery) => Promise<WorkspaceEntityProjectionIndex>\n}\n\nconst isUnauthorizedFailure = Predicate.isTagged(\"UnauthorizedApiError\")\n\n/** Generated-client transport for the authenticated workspace entity index. */\nexport const browserWorkspaceItemsTransport: WorkspaceItemsTransport = {\n load: (signal, query) =>\n Effect.runPromise(\n Effect.gen(function*() {\n const client = yield* makeControlCenterApiClient()\n return yield* client.deliveryGraph.workspaceEntityProjections({\n query: {\n ...(query.query.length === 0 ? {} : { q: query.query }),\n ...(query.owner === \"all\" ? {} : { owner: query.owner }),\n ...(query.service === \"all\" ? {} : { service: query.service }),\n ...(query.status === \"all\" ? {} : { status: query.status }),\n ...(query.type === \"all\" ? {} : { type: query.type })\n }\n })\n }).pipe(Effect.provide(FetchHttpClient.layer)),\n { signal }\n )\n}\n\n/** Keep one bounded workspace entity index scoped to the exact browser session. */\nexport const useWorkspaceItems = (\n workspaceId: WorkspaceId,\n routableReleaseIds: ReadonlySet<ReleaseId>,\n filters: WorkspaceItemsQuery,\n refreshKey: string,\n sessionKey: string | null,\n onSessionExpired: (sessionKey: string) => void,\n transport: WorkspaceItemsTransport = browserWorkspaceItemsTransport\n): { readonly retry: () => void; readonly state: WorkspaceItemsState } => {\n const [requestRevision, setRequestRevision] = useState(0)\n const [state, setState] = useState<WorkspaceItemsState>({ _tag: \"idle\" })\n const releaseScopeKey = [...routableReleaseIds].join(\":\")\n const query = useMemo<WorkspaceItemsQuery>(() => ({\n owner: filters.owner,\n query: filters.query,\n service: filters.service,\n status: filters.status,\n type: filters.type\n }), [filters.owner, filters.query, filters.service, filters.status, filters.type])\n const filterScopeKey = `${query.query}|${query.owner}|${query.service}|${query.status}|${query.type}`\n const scopeKey = `${workspaceId}|${releaseScopeKey}|${refreshKey}|${filterScopeKey}`\n\n useEffect(() => {\n if (sessionKey === null) {\n setState({ _tag: \"idle\" })\n return\n }\n const abort = new AbortController()\n setState((current) =>\n current._tag === \"ready\" && current.sessionKey === sessionKey\n ? current\n : { _tag: \"loading\", scopeKey, sessionKey }\n )\n transport.load(abort.signal, query).then(\n (index) => {\n if (abort.signal.aborted) return\n setState({\n _tag: \"ready\",\n items: presentWorkspaceEntityIndex(workspaceId, index, routableReleaseIds),\n matchedCount: index.matchedCount,\n ownerOptions: index.ownerOptions,\n ownerOptionsTruncated: index.ownerOptionsTruncated,\n refreshing: false,\n scopeKey,\n sessionKey,\n totalCount: index.totalCount,\n truncated: index.truncated\n })\n },\n (failure) => {\n if (abort.signal.aborted) return\n if (isUnauthorizedFailure(failure)) onSessionExpired(sessionKey)\n setState({ _tag: \"failed\", scopeKey, sessionKey })\n }\n )\n return () => abort.abort()\n }, [onSessionExpired, query, requestRevision, routableReleaseIds, scopeKey, sessionKey, transport, workspaceId])\n\n const currentState: WorkspaceItemsState = sessionKey === null\n ? { _tag: \"idle\" }\n : state._tag === \"idle\" || state.sessionKey !== sessionKey\n ? { _tag: \"loading\", scopeKey, sessionKey }\n : state._tag === \"ready\" && state.scopeKey !== scopeKey\n ? { ...state, refreshing: true }\n : state._tag !== \"ready\" && state.scopeKey !== scopeKey\n ? { _tag: \"loading\", scopeKey, sessionKey }\n : state\n\n return {\n retry: useCallback(() => setRequestRevision((revision) => revision + 1), []),\n state: currentState\n }\n}\n"],"mappings":"4MA4CM,EAAwB,EAAmB,sBAAsB,EAG1D,EAA0D,CACrE,MAAO,EAAQ,IACb,EACE,EAAW,WAAY,CAErB,OAAO,OAAO,MADQ,EAA2B,EAAA,CAC5B,cAAc,2BAA2B,CAC5D,MAAO,CACL,GAAI,EAAM,MAAM,SAAW,EAAI,CAAC,EAAI,CAAE,EAAG,EAAM,KAAM,EACrD,GAAI,EAAM,QAAU,MAAQ,CAAC,EAAI,CAAE,MAAO,EAAM,KAAM,EACtD,GAAI,EAAM,UAAY,MAAQ,CAAC,EAAI,CAAE,QAAS,EAAM,OAAQ,EAC5D,GAAI,EAAM,SAAW,MAAQ,CAAC,EAAI,CAAE,OAAQ,EAAM,MAAO,EACzD,GAAI,EAAM,OAAS,MAAQ,CAAC,EAAI,CAAE,KAAM,EAAM,IAAK,CACrD,CACF,CAAC,CACH,CAAC,CAAC,CAAC,KAAK,EAAe,CAAqB,CAAC,EAC7C,CAAE,QAAO,CACX,CACJ,EAGa,GACX,EACA,EACA,EACA,EACA,EACA,EACA,EAAqC,IACmC,CACxE,GAAM,CAAC,EAAiB,IAAA,EAAA,EAAA,SAAA,CAA+B,CAAC,EAClD,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAA0C,CAAE,KAAM,MAAO,CAAC,EAClE,EAAkB,CAAC,GAAG,CAAkB,CAAC,CAAC,KAAK,GAAG,EAClD,GAAA,EAAA,EAAA,QAAA,MAA4C,CAChD,MAAO,EAAQ,MACf,MAAO,EAAQ,MACf,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,KAAM,EAAQ,IAChB,GAAI,CAAC,EAAQ,MAAO,EAAQ,MAAO,EAAQ,QAAS,EAAQ,OAAQ,EAAQ,IAAI,CAAC,EAE3E,EAAW,GAAG,EAAY,GAAG,EAAgB,GAAG,EAAW,GAAG,GAD1C,EAAM,MAAM,GAAG,EAAM,MAAM,GAAG,EAAM,QAAQ,GAAG,EAAM,OAAO,GAAG,EAAM,UAG/F,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,IAAe,KAAM,CACvB,EAAS,CAAE,KAAM,MAAO,CAAC,EACzB,MACF,CACA,IAAM,EAAQ,IAAI,gBA4BlB,OA3BA,EAAU,GACR,EAAQ,OAAS,SAAW,EAAQ,aAAe,EAC/C,EACA,CAAE,KAAM,UAAW,WAAU,YAAW,CAC9C,EACA,EAAU,KAAK,EAAM,OAAQ,CAAK,CAAC,CAAC,KACjC,GAAU,CACL,EAAM,OAAO,SACjB,EAAS,CACP,KAAM,QACN,MAAO,EAA4B,EAAa,EAAO,CAAkB,EACzE,aAAc,EAAM,aACpB,aAAc,EAAM,aACpB,sBAAuB,EAAM,sBAC7B,WAAY,GACZ,WACA,aACA,WAAY,EAAM,WAClB,UAAW,EAAM,SACnB,CAAC,CACH,EACC,GAAY,CACP,EAAM,OAAO,UACb,EAAsB,CAAO,GAAG,EAAiB,CAAU,EAC/D,EAAS,CAAE,KAAM,SAAU,WAAU,YAAW,CAAC,EACnD,CACF,MACa,EAAM,MAAM,CAC3B,EAAG,CAAC,EAAkB,EAAO,EAAiB,EAAoB,EAAU,EAAY,EAAW,CAAW,CAAC,EAE/G,IAAM,EAAoC,IAAe,KACrD,CAAE,KAAM,MAAO,EACf,EAAM,OAAS,QAAU,EAAM,aAAe,EAC9C,CAAE,KAAM,UAAW,WAAU,YAAW,EACxC,EAAM,OAAS,SAAW,EAAM,WAAa,EAC7C,CAAE,GAAG,EAAO,WAAY,EAAK,EAC7B,EAAM,OAAS,SAAW,EAAM,WAAa,EAC7C,CAAE,KAAM,UAAW,WAAU,YAAW,EACxC,EAEJ,MAAO,CACL,OAAA,EAAA,EAAA,YAAA,KAAyB,EAAoB,GAAa,EAAW,CAAC,EAAG,CAAC,CAAC,EAC3E,MAAO,CACT,CACF"}
1
+ {"version":3,"file":"useWorkspaceItems-DQTYaq1R.js","names":[],"sources":["../../../src/client/items/useWorkspaceItems.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\"\nimport { useCallback, useEffect, useMemo, useState } from \"react\"\n\nimport { makeControlCenterApiClient } from \"../../api/client.js\"\nimport type { WorkspaceEntityProjectionIndex } from \"../../api/deliveryGraph.js\"\nimport type {\n DeliveryEntityKind,\n DeliveryEntityService,\n DeliveryEntityStatusGroup\n} from \"../../domain/deliveryGraph.js\"\nimport type { PersonId, ReleaseId, WorkspaceId } from \"../../domain/identifiers.js\"\nimport { presentWorkspaceEntityIndex, type WorkspaceItemPresentation } from \"./presentWorkspaceItems.js\"\n\nexport type WorkspaceItemsState =\n | { readonly _tag: \"idle\" }\n | { readonly _tag: \"loading\"; readonly scopeKey: string; readonly sessionKey: string }\n | { readonly _tag: \"failed\"; readonly scopeKey: string; readonly sessionKey: string }\n | {\n readonly _tag: \"ready\"\n readonly items: ReadonlyArray<WorkspaceItemPresentation>\n readonly matchedCount: number\n readonly ownerOptions: WorkspaceEntityProjectionIndex[\"ownerOptions\"]\n readonly ownerOptionsTruncated: boolean\n readonly refreshing: boolean\n readonly scopeKey: string\n readonly sessionKey: string\n readonly truncated: boolean\n readonly totalCount: number\n }\n\nexport interface WorkspaceItemsQuery {\n readonly owner: PersonId | \"all\"\n readonly query: string\n readonly service: DeliveryEntityService | \"all\"\n readonly status: DeliveryEntityStatusGroup | \"all\"\n readonly type: DeliveryEntityKind | \"all\"\n}\n\nexport interface WorkspaceItemsTransport {\n readonly load: (signal: AbortSignal, query: WorkspaceItemsQuery) => Promise<WorkspaceEntityProjectionIndex>\n}\n\nconst isUnauthorizedFailure = Predicate.isTagged(\"UnauthorizedApiError\")\n\n/** Generated-client transport for the authenticated workspace entity index. */\nexport const browserWorkspaceItemsTransport: WorkspaceItemsTransport = {\n load: (signal, query) =>\n Effect.runPromise(\n Effect.gen(function*() {\n const client = yield* makeControlCenterApiClient()\n return yield* client.deliveryGraph.workspaceEntityProjections({\n query: {\n ...(query.query.length === 0 ? {} : { q: query.query }),\n ...(query.owner === \"all\" ? {} : { owner: query.owner }),\n ...(query.service === \"all\" ? {} : { service: query.service }),\n ...(query.status === \"all\" ? {} : { status: query.status }),\n ...(query.type === \"all\" ? {} : { type: query.type })\n }\n })\n }).pipe(Effect.provide(FetchHttpClient.layer)),\n { signal }\n )\n}\n\n/** Keep one bounded workspace entity index scoped to the exact browser session. */\nexport const useWorkspaceItems = (\n workspaceId: WorkspaceId,\n routableReleaseIds: ReadonlySet<ReleaseId>,\n filters: WorkspaceItemsQuery,\n refreshKey: string,\n sessionKey: string | null,\n onSessionExpired: (sessionKey: string) => void,\n transport: WorkspaceItemsTransport = browserWorkspaceItemsTransport\n): { readonly retry: () => void; readonly state: WorkspaceItemsState } => {\n const [requestRevision, setRequestRevision] = useState(0)\n const [state, setState] = useState<WorkspaceItemsState>({ _tag: \"idle\" })\n const releaseScopeKey = [...routableReleaseIds].join(\":\")\n const query = useMemo<WorkspaceItemsQuery>(() => ({\n owner: filters.owner,\n query: filters.query,\n service: filters.service,\n status: filters.status,\n type: filters.type\n }), [filters.owner, filters.query, filters.service, filters.status, filters.type])\n const filterScopeKey = `${query.query}|${query.owner}|${query.service}|${query.status}|${query.type}`\n const scopeKey = `${workspaceId}|${releaseScopeKey}|${refreshKey}|${filterScopeKey}`\n\n useEffect(() => {\n if (sessionKey === null) {\n setState({ _tag: \"idle\" })\n return\n }\n const abort = new AbortController()\n setState((current) =>\n current._tag === \"ready\" && current.sessionKey === sessionKey\n ? current\n : { _tag: \"loading\", scopeKey, sessionKey }\n )\n transport.load(abort.signal, query).then(\n (index) => {\n if (abort.signal.aborted) return\n setState({\n _tag: \"ready\",\n items: presentWorkspaceEntityIndex(workspaceId, index, routableReleaseIds),\n matchedCount: index.matchedCount,\n ownerOptions: index.ownerOptions,\n ownerOptionsTruncated: index.ownerOptionsTruncated,\n refreshing: false,\n scopeKey,\n sessionKey,\n totalCount: index.totalCount,\n truncated: index.truncated\n })\n },\n (failure) => {\n if (abort.signal.aborted) return\n if (isUnauthorizedFailure(failure)) onSessionExpired(sessionKey)\n setState({ _tag: \"failed\", scopeKey, sessionKey })\n }\n )\n return () => abort.abort()\n }, [onSessionExpired, query, requestRevision, routableReleaseIds, scopeKey, sessionKey, transport, workspaceId])\n\n const currentState: WorkspaceItemsState = sessionKey === null\n ? { _tag: \"idle\" }\n : state._tag === \"idle\" || state.sessionKey !== sessionKey\n ? { _tag: \"loading\", scopeKey, sessionKey }\n : state._tag === \"ready\" && state.scopeKey !== scopeKey\n ? { ...state, refreshing: true }\n : state._tag !== \"ready\" && state.scopeKey !== scopeKey\n ? { _tag: \"loading\", scopeKey, sessionKey }\n : state\n\n return {\n retry: useCallback(() => setRequestRevision((revision) => revision + 1), []),\n state: currentState\n }\n}\n"],"mappings":"4MA4CM,EAAwB,EAAmB,sBAAsB,EAG1D,EAA0D,CACrE,MAAO,EAAQ,IACb,EACE,EAAW,WAAY,CAErB,OAAO,OAAO,MADQ,EAA2B,EAAA,CAC5B,cAAc,2BAA2B,CAC5D,MAAO,CACL,GAAI,EAAM,MAAM,SAAW,EAAI,CAAC,EAAI,CAAE,EAAG,EAAM,KAAM,EACrD,GAAI,EAAM,QAAU,MAAQ,CAAC,EAAI,CAAE,MAAO,EAAM,KAAM,EACtD,GAAI,EAAM,UAAY,MAAQ,CAAC,EAAI,CAAE,QAAS,EAAM,OAAQ,EAC5D,GAAI,EAAM,SAAW,MAAQ,CAAC,EAAI,CAAE,OAAQ,EAAM,MAAO,EACzD,GAAI,EAAM,OAAS,MAAQ,CAAC,EAAI,CAAE,KAAM,EAAM,IAAK,CACrD,CACF,CAAC,CACH,CAAC,CAAC,CAAC,KAAK,EAAe,CAAqB,CAAC,EAC7C,CAAE,QAAO,CACX,CACJ,EAGa,GACX,EACA,EACA,EACA,EACA,EACA,EACA,EAAqC,IACmC,CACxE,GAAM,CAAC,EAAiB,IAAA,EAAA,EAAA,SAAA,CAA+B,CAAC,EAClD,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAA0C,CAAE,KAAM,MAAO,CAAC,EAClE,EAAkB,CAAC,GAAG,CAAkB,CAAC,CAAC,KAAK,GAAG,EAClD,GAAA,EAAA,EAAA,QAAA,MAA4C,CAChD,MAAO,EAAQ,MACf,MAAO,EAAQ,MACf,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,KAAM,EAAQ,IAChB,GAAI,CAAC,EAAQ,MAAO,EAAQ,MAAO,EAAQ,QAAS,EAAQ,OAAQ,EAAQ,IAAI,CAAC,EAE3E,EAAW,GAAG,EAAY,GAAG,EAAgB,GAAG,EAAW,GAAG,GAD1C,EAAM,MAAM,GAAG,EAAM,MAAM,GAAG,EAAM,QAAQ,GAAG,EAAM,OAAO,GAAG,EAAM,UAG/F,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,IAAe,KAAM,CACvB,EAAS,CAAE,KAAM,MAAO,CAAC,EACzB,MACF,CACA,IAAM,EAAQ,IAAI,gBA4BlB,OA3BA,EAAU,GACR,EAAQ,OAAS,SAAW,EAAQ,aAAe,EAC/C,EACA,CAAE,KAAM,UAAW,WAAU,YAAW,CAC9C,EACA,EAAU,KAAK,EAAM,OAAQ,CAAK,CAAC,CAAC,KACjC,GAAU,CACL,EAAM,OAAO,SACjB,EAAS,CACP,KAAM,QACN,MAAO,EAA4B,EAAa,EAAO,CAAkB,EACzE,aAAc,EAAM,aACpB,aAAc,EAAM,aACpB,sBAAuB,EAAM,sBAC7B,WAAY,GACZ,WACA,aACA,WAAY,EAAM,WAClB,UAAW,EAAM,SACnB,CAAC,CACH,EACC,GAAY,CACP,EAAM,OAAO,UACb,EAAsB,CAAO,GAAG,EAAiB,CAAU,EAC/D,EAAS,CAAE,KAAM,SAAU,WAAU,YAAW,CAAC,EACnD,CACF,MACa,EAAM,MAAM,CAC3B,EAAG,CAAC,EAAkB,EAAO,EAAiB,EAAoB,EAAU,EAAY,EAAW,CAAW,CAAC,EAE/G,IAAM,EAAoC,IAAe,KACrD,CAAE,KAAM,MAAO,EACf,EAAM,OAAS,QAAU,EAAM,aAAe,EAC9C,CAAE,KAAM,UAAW,WAAU,YAAW,EACxC,EAAM,OAAS,SAAW,EAAM,WAAa,EAC7C,CAAE,GAAG,EAAO,WAAY,EAAK,EAC7B,EAAM,OAAS,SAAW,EAAM,WAAa,EAC7C,CAAE,KAAM,UAAW,WAAU,YAAW,EACxC,EAEJ,MAAO,CACL,OAAA,EAAA,EAAA,YAAA,KAAyB,EAAoB,GAAa,EAAW,CAAC,EAAG,CAAC,CAAC,EAC3E,MAAO,CACT,CACF"}
@@ -0,0 +1,2 @@
1
+ import{n as e}from"./releasePaths-s-fSbIM-.js";import{a as t}from"./index-DpW_ckau.js";import{A as n,D as r,Ft as i,P as a,Pt as o,j as s,k as c,m as l,w as u,x as d}from"./client-P7FaPwzJ.js";import{t as f,u as p}from"./releaseRoutes-CfaNElmG.js";import{t as m}from"./workspaceEntityPaths-xAl2jRFm.js";var h=s({hash:n,pathname:n,search:n,state:c(f)}),g=s({entityOrigin:s({_tag:r(`entity-origin/v2`),entityId:l,origin:h,workspaceId:u})}),_=e=>{if(e.includes(`?`)||e.includes(`#`))return null;let t=e.split(`/`);if(t.length!==5||t[0]!==``||t[1]!==`w`||t[3]!==`items`)return null;let n=a(u)(t[2]),r=a(l)(t[4]);return i(n)&&i(r)?{entityId:r.value,workspaceId:n.value}:null},v=e=>{let t=a(l)(e);return i(t)?t.value:null},y=e=>({hash:``,pathname:m(e),search:``,state:null}),b=e=>e.length<=2048&&(e.length===0||e.startsWith(`?`)),x=e=>e.length<=1024&&(e.length===0||e.startsWith(`#`)),S=(e,t)=>{if(e.length!==5&&e.length!==6||e[0]!==``||e[1]!==`w`||e[2]!==t||e[3]!==`releases`)return null;let n=a(d)(e[4]);return o(n)||e.length===6&&e[5]!==`preview`?null:{releaseId:n.value,workspaceId:t}},C=(e,t)=>{if(e.length>2048)return!1;let n=e.split(`/`);return n[0]!==``||n[1]!==`w`||n[2]!==t?!1:n.length===4?[`overview`,`work`,`items`,`timeline`].includes(n[3]??``):S(n,t)!==null},w=({hash:e,pathname:t,search:n,state:r})=>{let o=a(u)(t.split(`/`)[2]),s=i(o)?S(t.split(`/`),o.value):null;return{hash:e,pathname:t,search:n,state:s===null?null:p(r,s.workspaceId,s.releaseId)}},T=(e,t)=>{if(!C(e.pathname,t)||!b(e.search)||!x(e.hash))return!1;if(e.state===null)return!0;let n=S(e.pathname.split(`/`),t);return n!==null&&p(e.state,t,n.releaseId)!==null},E=(e,t)=>{let n=a(g)(e);if(o(n))return null;let r=n.value.entityOrigin;return r.workspaceId===t&&T(r.origin,t)?r.origin:null},D=(e,t,n,r)=>({entityOrigin:{_tag:`entity-origin/v2`,entityId:n,origin:E(e,t)??(T(r,t)?r:y(t)),workspaceId:t}}),O=(e,t,n)=>{let r=y(t),i=a(g)(e);if(o(i))return{isStored:!1,origin:r};let s=i.value.entityOrigin;return s.workspaceId===t&&s.entityId===n&&T(s.origin,t)?{isStored:!0,origin:s.origin}:{isStored:!1,origin:r}},k=({hash:e,pathname:t,search:n})=>`${t}${n}${e}`,A=(n,r,i,a,o)=>{let s=S(n.pathname.split(`/`),r),c=a.canonicalReleaseId,l=s===null?a.releaseMembershipsTruncated||a.releaseIds.length!==1||a.releaseIds[0]!==c||c===null||!o.has(c)?null:e(r,c):!a.releaseMembershipsTruncated&&a.releaseIds.includes(s.releaseId)&&o.has(s.releaseId)?e(r,s.releaseId):null;if(l!==null)return l;let u=_(i.pathname);if(u===null||u.workspaceId!==r)return t(i.pathname,i.search,i.hash);let d=new URLSearchParams(n.pathname===m(r)?n.search:``);return d.set(`object`,u.entityId),t(m(r),`?${d.toString()}`,`#item-details`)};export{A as a,O as i,w as n,k as o,D as r,_ as s,v as t};
2
+ //# sourceMappingURL=workspaceEntityRoutes-Biw67kRR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaceEntityRoutes-Biw67kRR.js","names":[],"sources":["../../../src/client/items/workspaceEntityRoutes.ts"],"sourcesContent":["import * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\n\nimport {\n EntityId,\n type EntityId as EntityIdType,\n ReleaseId,\n type ReleaseId as ReleaseIdType,\n WorkspaceId,\n type WorkspaceId as WorkspaceIdType\n} from \"../../domain/identifiers.js\"\nimport { contextualAgentPath } from \"../contextualAgentPath.js\"\nimport { releaseAgentPath } from \"../releases/releasePaths.js\"\nimport { type ReleaseRouteState, ReleaseRouteStateSchema, retainReleaseRouteState } from \"../releases/releaseRoutes.js\"\nimport { workspaceEntityParentPath } from \"../workspaceEntityPaths.js\"\n\nexport { workspaceEntityParentPath, workspaceEntityPath } from \"../workspaceEntityPaths.js\"\n\n/** Exact in-application location preserved while a canonical entity route is open. */\nexport interface WorkspaceEntityOrigin {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n readonly state: ReleaseRouteState | null\n}\n\n/** Versioned, bounded router state for one canonical entity activation. */\nexport interface WorkspaceEntityRouteState {\n readonly entityOrigin: {\n readonly _tag: \"entity-origin/v2\"\n readonly entityId: EntityIdType\n readonly origin: WorkspaceEntityOrigin\n readonly workspaceId: WorkspaceIdType\n }\n}\n\n/** Decoded entity origin plus whether it came from trusted matching history state. */\nexport interface ResolvedWorkspaceEntityOrigin {\n readonly isStored: boolean\n readonly origin: WorkspaceEntityOrigin\n}\n\n/** Decoded target of one exact canonical workspace entity href. */\nexport interface WorkspaceEntityTarget {\n readonly entityId: EntityIdType\n readonly workspaceId: WorkspaceIdType\n}\n\n/** Release membership evidence used to select an unambiguous entity-owned agent thread. */\nexport interface WorkspaceEntityReleaseContext {\n readonly canonicalReleaseId: ReleaseIdType | null\n readonly releaseIds: ReadonlyArray<ReleaseIdType>\n readonly releaseMembershipsTruncated: boolean\n}\n\ninterface LocationParts {\n readonly hash: string\n readonly pathname: string\n readonly search: string\n readonly state?: unknown\n}\n\nconst WorkspaceEntityOriginSchema = Schema.Struct({\n hash: Schema.String,\n pathname: Schema.String,\n search: Schema.String,\n state: Schema.NullOr(ReleaseRouteStateSchema)\n})\n\nconst WorkspaceEntityRouteStateSchema = Schema.Struct({\n entityOrigin: Schema.Struct({\n _tag: Schema.Literal(\"entity-origin/v2\"),\n entityId: EntityId,\n origin: WorkspaceEntityOriginSchema,\n workspaceId: WorkspaceId\n })\n})\n\n/** Decode only an exact canonical entity path, rejecting query, hash, and extra path material. */\nexport const workspaceEntityTargetFromHref = (href: string): WorkspaceEntityTarget | null => {\n if (href.includes(\"?\") || href.includes(\"#\")) return null\n const parts = href.split(\"/\")\n if (parts.length !== 5 || parts[0] !== \"\" || parts[1] !== \"w\" || parts[3] !== \"items\") return null\n const workspaceId = Schema.decodeUnknownOption(WorkspaceId)(parts[2])\n const entityId = Schema.decodeUnknownOption(EntityId)(parts[4])\n return Option.isSome(workspaceId) && Option.isSome(entityId)\n ? { entityId: entityId.value, workspaceId: workspaceId.value }\n : null\n}\n\n/** Decode one entity route segment at the browser boundary. */\nexport const decodeEntityRouteId = (value: unknown): EntityIdType | null => {\n const decoded = Schema.decodeUnknownOption(EntityId)(value)\n return Option.isSome(decoded) ? decoded.value : null\n}\n\nconst fallbackOrigin = (workspaceId: WorkspaceIdType): WorkspaceEntityOrigin => ({\n hash: \"\",\n pathname: workspaceEntityParentPath(workspaceId),\n search: \"\",\n state: null\n})\n\nconst isSearch = (search: string): boolean => search.length <= 2_048 && (search.length === 0 || search.startsWith(\"?\"))\n\nconst isHash = (hash: string): boolean => hash.length <= 1_024 && (hash.length === 0 || hash.startsWith(\"#\"))\n\nconst releaseOriginTarget = (\n parts: ReadonlyArray<string>,\n workspaceId: WorkspaceIdType\n): { readonly releaseId: ReleaseIdType; readonly workspaceId: WorkspaceIdType } | null => {\n if (parts.length !== 5 && parts.length !== 6) return null\n if (parts[0] !== \"\" || parts[1] !== \"w\" || parts[2] !== workspaceId || parts[3] !== \"releases\") return null\n const releaseId = Schema.decodeUnknownOption(ReleaseId)(parts[4])\n if (Option.isNone(releaseId) || (parts.length === 6 && parts[5] !== \"preview\")) return null\n return { releaseId: releaseId.value, workspaceId }\n}\n\nconst isRecognizedOriginPath = (pathname: string, workspaceId: WorkspaceIdType): boolean => {\n if (pathname.length > 2_048) return false\n const parts = pathname.split(\"/\")\n if (parts[0] !== \"\" || parts[1] !== \"w\" || parts[2] !== workspaceId) return false\n if (parts.length === 4) return [\"overview\", \"work\", \"items\", \"timeline\"].includes(parts[3] ?? \"\")\n return releaseOriginTarget(parts, workspaceId) !== null\n}\n\n/** Capture an exact location while retaining only matching, bounded release origin state. */\nexport const entityOriginFromLocation = ({\n hash,\n pathname,\n search,\n state\n}: LocationParts): WorkspaceEntityOrigin => {\n const workspaceId = Schema.decodeUnknownOption(WorkspaceId)(pathname.split(\"/\")[2])\n const target = Option.isSome(workspaceId) ? releaseOriginTarget(pathname.split(\"/\"), workspaceId.value) : null\n return {\n hash,\n pathname,\n search,\n state: target === null ? null : retainReleaseRouteState(state, target.workspaceId, target.releaseId)\n }\n}\n\n/** Confirm an origin is bounded and belongs to a supported page in the exact workspace. */\nexport const isSafeWorkspaceEntityOrigin = (\n origin: WorkspaceEntityOrigin,\n workspaceId: WorkspaceIdType\n): boolean => {\n if (!isRecognizedOriginPath(origin.pathname, workspaceId) || !isSearch(origin.search) || !isHash(origin.hash)) {\n return false\n }\n if (origin.state === null) return true\n const target = releaseOriginTarget(origin.pathname.split(\"/\"), workspaceId)\n return target !== null && retainReleaseRouteState(origin.state, workspaceId, target.releaseId) !== null\n}\n\nconst reusableStoredOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType\n): WorkspaceEntityOrigin | null => {\n const decoded = Schema.decodeUnknownOption(WorkspaceEntityRouteStateSchema)(state)\n if (Option.isNone(decoded)) return null\n const marker = decoded.value.entityOrigin\n return marker.workspaceId === workspaceId && isSafeWorkspaceEntityOrigin(marker.origin, workspaceId)\n ? marker.origin\n : null\n}\n\n/**\n * Construct bounded entity route state.\n *\n * A previously validated entity origin is carried through related-entity navigation so the\n * shell's explicit Back action still returns to the root activation surface.\n */\nexport const makeWorkspaceEntityRouteState = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n entityId: EntityIdType,\n origin: WorkspaceEntityOrigin\n): WorkspaceEntityRouteState => ({\n entityOrigin: {\n _tag: \"entity-origin/v2\",\n entityId,\n origin: reusableStoredOrigin(state, workspaceId) ??\n (isSafeWorkspaceEntityOrigin(origin, workspaceId) ? origin : fallbackOrigin(workspaceId)),\n workspaceId\n }\n})\n\n/** Decode safe history state for the exact workspace/entity pair or use the Items parent. */\nexport const resolveWorkspaceEntityOrigin = (\n state: unknown,\n workspaceId: WorkspaceIdType,\n entityId: EntityIdType\n): ResolvedWorkspaceEntityOrigin => {\n const fallback = fallbackOrigin(workspaceId)\n const decoded = Schema.decodeUnknownOption(WorkspaceEntityRouteStateSchema)(state)\n if (Option.isNone(decoded)) return { isStored: false, origin: fallback }\n const marker = decoded.value.entityOrigin\n return marker.workspaceId === workspaceId &&\n marker.entityId === entityId &&\n isSafeWorkspaceEntityOrigin(marker.origin, workspaceId)\n ? { isStored: true, origin: marker.origin }\n : { isStored: false, origin: fallback }\n}\n\n/** Serialize an exact validated origin for React Router navigation. */\nexport const workspaceEntityOriginHref = ({\n hash,\n pathname,\n search\n}: WorkspaceEntityOrigin): string => `${pathname}${search}${hash}`\n\n/** Resolve a release-owned agent thread only when the stored origin is an exact release route. */\nexport const workspaceEntityOriginAgentPath = (\n origin: WorkspaceEntityOrigin,\n workspaceId: WorkspaceIdType,\n routableReleaseIds: ReadonlySet<ReleaseIdType>\n): string | null => {\n const target = releaseOriginTarget(origin.pathname.split(\"/\"), workspaceId)\n return target === null || !routableReleaseIds.has(target.releaseId)\n ? null\n : releaseAgentPath(target.workspaceId, target.releaseId)\n}\n\n/** Keep entity actions in a release-owned thread, falling back to the current-page context. */\nexport const workspaceEntityAgentPath = (\n origin: WorkspaceEntityOrigin,\n workspaceId: WorkspaceIdType,\n current: Pick<LocationParts, \"hash\" | \"pathname\" | \"search\">,\n releaseContext: WorkspaceEntityReleaseContext,\n routableReleaseIds: ReadonlySet<ReleaseIdType>\n): string => {\n const originRelease = releaseOriginTarget(origin.pathname.split(\"/\"), workspaceId)\n const canonicalReleaseId = releaseContext.canonicalReleaseId\n const releasePath = originRelease === null\n ? releaseContext.releaseMembershipsTruncated ||\n releaseContext.releaseIds.length !== 1 ||\n releaseContext.releaseIds[0] !== canonicalReleaseId ||\n canonicalReleaseId === null ||\n !routableReleaseIds.has(canonicalReleaseId)\n ? null\n : releaseAgentPath(workspaceId, canonicalReleaseId)\n : !releaseContext.releaseMembershipsTruncated &&\n releaseContext.releaseIds.includes(originRelease.releaseId) &&\n routableReleaseIds.has(originRelease.releaseId)\n ? releaseAgentPath(workspaceId, originRelease.releaseId)\n : null\n if (releasePath !== null) return releasePath\n\n const entity = workspaceEntityTargetFromHref(current.pathname)\n if (entity === null || entity.workspaceId !== workspaceId) {\n return contextualAgentPath(current.pathname, current.search, current.hash)\n }\n const itemSearch = new URLSearchParams(\n origin.pathname === workspaceEntityParentPath(workspaceId) ? origin.search : \"\"\n )\n itemSearch.set(\"object\", entity.entityId)\n return contextualAgentPath(\n workspaceEntityParentPath(workspaceId),\n `?${itemSearch.toString()}`,\n \"#item-details\"\n )\n}\n"],"mappings":"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"}
@@ -370,7 +370,8 @@
370
370
  "npm:effect/dist/DateTime.js",
371
371
  "src/client/items/presentWorkspaceItems.ts",
372
372
  "src/client/workspaceEntityPaths.ts",
373
- "src/client/entities/presentWorkspaceIssue.ts"
373
+ "src/client/entities/presentWorkspaceIssue.ts",
374
+ "src/client/entities/presentWorkspacePullRequest.ts"
374
375
  ],
375
376
  "isEntry": false
376
377
  },
@@ -383,6 +384,15 @@
383
384
  ],
384
385
  "isEntry": false
385
386
  },
387
+ {
388
+ "dynamicImports": [],
389
+ "id": "src/client/entities/presentWorkspacePullRequest.ts",
390
+ "imports": [
391
+ "npm:effect/dist/DateTime.js",
392
+ "npm:effect/dist/Option.js"
393
+ ],
394
+ "isEntry": false
395
+ },
386
396
  {
387
397
  "dynamicImports": [],
388
398
  "id": "src/client/entities/useWorkspaceEntity.ts",
@@ -422,12 +432,12 @@
422
432
  "npm:react/index.js",
423
433
  "npm:react-router/dist/production/index.js",
424
434
  "src/client/BrowserSession.tsx",
425
- "src/client/contextualAgentPath.ts",
426
435
  "src/client/items/workspaceEntityRoutes.ts",
427
436
  "src/client/entities/presentWorkspaceEntity.ts",
428
437
  "src/client/entities/WorkspaceEntityLink.tsx",
429
438
  "src/client/entities/WorkspaceEntityRoute.module.css",
430
439
  "src/client/entities/WorkspaceIssueDetails.tsx",
440
+ "src/client/entities/WorkspacePullRequestDetails.tsx",
431
441
  "src/client/entities/useWorkspaceEntity.ts",
432
442
  "npm:react/jsx-runtime.js"
433
443
  ],
@@ -451,6 +461,24 @@
451
461
  ],
452
462
  "isEntry": false
453
463
  },
464
+ {
465
+ "dynamicImports": [],
466
+ "id": "src/client/entities/WorkspacePullRequestDetails.module.css",
467
+ "imports": [],
468
+ "isEntry": false
469
+ },
470
+ {
471
+ "dynamicImports": [],
472
+ "id": "src/client/entities/WorkspacePullRequestDetails.tsx",
473
+ "imports": [
474
+ "workspace:rly/dist/patterns/index.js",
475
+ "workspace:rly/dist/primitives/index.js",
476
+ "src/client/entities/WorkspacePullRequestDetails.module.css",
477
+ "src/client/entities/WorkspaceRichText.tsx",
478
+ "npm:react/jsx-runtime.js"
479
+ ],
480
+ "isEntry": false
481
+ },
454
482
  {
455
483
  "dynamicImports": [],
456
484
  "id": "src/client/entities/WorkspaceRichText.tsx",
@@ -580,6 +608,8 @@
580
608
  "npm:effect/dist/Option.js",
581
609
  "npm:effect/dist/Schema.js",
582
610
  "src/domain/identifiers.ts",
611
+ "src/client/contextualAgentPath.ts",
612
+ "src/client/releases/releasePaths.ts",
583
613
  "src/client/releases/releaseRoutes.ts",
584
614
  "src/client/workspaceEntityPaths.ts"
585
615
  ],
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="color-scheme" content="light dark" />
7
7
  <title>Control Center</title>
8
- <script type="module" crossorigin src="/assets/index-DLoOV6Hp.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DpW_ckau.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-n5LQ9ujS.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/Dialog-DwSIPb-B-DVn37afM.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/releasePaths-s-fSbIM-.js">