synapse-react-client 4.0.17 → 4.0.18

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 (225) hide show
  1. package/README.md +5 -3
  2. package/dist/SWC.index.d.ts.map +1 -1
  3. package/dist/SWC.index.js +1 -1
  4. package/dist/SWC.index.js.map +1 -1
  5. package/dist/assets/homepage/BlinkingLiveIcon.d.ts +1 -1
  6. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.d.ts.map +1 -1
  7. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js.map +1 -1
  8. package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
  9. package/dist/components/AccessRequirementList/RequirementItem/CertificationRequirement.js.map +1 -1
  10. package/dist/components/AccessRequirementList/RequirementItem/SelfSignAccessRequirementItem.js.map +1 -1
  11. package/dist/components/AccessRequirementList/RequirementItem/UnmanagedACTAccessRequirementItem.js.map +1 -1
  12. package/dist/components/AccessRequirementList/RequirementItem/ValidationRequirement.js.map +1 -1
  13. package/dist/components/AccessTokenPage/CreateAccessTokenModal.d.ts.map +1 -1
  14. package/dist/components/AccessTokenPage/CreateAccessTokenModal.js +56 -54
  15. package/dist/components/AccessTokenPage/CreateAccessTokenModal.js.map +1 -1
  16. package/dist/components/Authentication/PasswordField.d.ts +1 -1
  17. package/dist/components/Authentication/PasswordField.d.ts.map +1 -1
  18. package/dist/components/Authentication/PasswordField.js.map +1 -1
  19. package/dist/components/Authentication/TwoFactorEnrollmentForm.d.ts.map +1 -1
  20. package/dist/components/Authentication/TwoFactorEnrollmentForm.js +1 -2
  21. package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
  22. package/dist/components/Authentication/UsernamePasswordForm.d.ts.map +1 -1
  23. package/dist/components/Authentication/UsernamePasswordForm.js +32 -29
  24. package/dist/components/Authentication/UsernamePasswordForm.js.map +1 -1
  25. package/dist/components/CardContainer/CardLink.d.ts +2 -2
  26. package/dist/components/CardContainer/CardLink.d.ts.map +1 -1
  27. package/dist/components/CardDeck/CardDeckCardProps.d.ts.map +1 -1
  28. package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
  29. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.d.ts.map +1 -1
  30. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js +106 -103
  31. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
  32. package/dist/components/ChallengeTeamWizard/MembershipRequestForm.d.ts.map +1 -1
  33. package/dist/components/ChallengeTeamWizard/MembershipRequestForm.js.map +1 -1
  34. package/dist/components/ChallengeTeamWizard/useCreateAndRegisterChallengeTeam.d.ts.map +1 -1
  35. package/dist/components/ChallengeTeamWizard/useCreateAndRegisterChallengeTeam.js.map +1 -1
  36. package/dist/components/CreateProjectModal/CreateProjectModal.d.ts.map +1 -1
  37. package/dist/components/CreateProjectModal/CreateProjectModal.js +4 -4
  38. package/dist/components/CreateProjectModal/CreateProjectModal.js.map +1 -1
  39. package/dist/components/CreateTableViewWizard/TableNameForm.js +6 -6
  40. package/dist/components/CreateTableViewWizard/TableNameForm.js.map +1 -1
  41. package/dist/components/CrispChat/CrispChat.js +4 -4
  42. package/dist/components/CrispChat/CrispChat.js.map +1 -1
  43. package/dist/components/DataGrid/utils/json-rx/JsonRxResponseComplete.js.map +1 -1
  44. package/dist/components/DownloadCart/RequestDownloadCard.js +14 -14
  45. package/dist/components/DownloadCart/RequestDownloadCard.js.map +1 -1
  46. package/dist/components/EntityAclEditor/EntityAclEditor.d.ts.map +1 -1
  47. package/dist/components/EntityAclEditor/EntityAclEditor.js.map +1 -1
  48. package/dist/components/EntityBadgeIcons/EntityBadgeIcons.d.ts.map +1 -1
  49. package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
  50. package/dist/components/EntityCitation/EntityCitationUtils.js.map +1 -1
  51. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js.map +1 -1
  52. package/dist/components/EntityFinder/EntityFinder.js.map +1 -1
  53. package/dist/components/EntityFinder/EntityIdTextField.d.ts +17 -0
  54. package/dist/components/EntityFinder/EntityIdTextField.d.ts.map +1 -0
  55. package/dist/components/EntityFinder/EntityIdTextField.js +35 -0
  56. package/dist/components/EntityFinder/EntityIdTextField.js.map +1 -0
  57. package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptions.d.ts.map +1 -1
  58. package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptions.js +22 -16
  59. package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptions.js.map +1 -1
  60. package/dist/components/FeaturedDataTabs/FacetPlotsCardGrid.js.map +1 -1
  61. package/dist/components/Forum/ForumThreadEditor.js.map +1 -1
  62. package/dist/components/FullWidthAlert/FullWidthAlert.d.ts.map +1 -1
  63. package/dist/components/FullWidthAlert/FullWidthAlert.js.map +1 -1
  64. package/dist/components/GenericCard/PortalDOI/PortalDOIUtils.d.ts.map +1 -1
  65. package/dist/components/GenericCard/PortalDOI/PortalDOIUtils.js.map +1 -1
  66. package/dist/components/Markdown/widget/MarkdownSynapsePlot.js.map +1 -1
  67. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.d.ts.map +1 -1
  68. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js.map +1 -1
  69. package/dist/components/PortalAclEditor/PortalAclEditor.d.ts.map +1 -1
  70. package/dist/components/PortalAclEditor/PortalAclEditor.js.map +1 -1
  71. package/dist/components/PortalHomePageHeader/PortalHomePageHeader.d.ts +1 -1
  72. package/dist/components/PortalHomePageHeader/PortalHomePageHeader.d.ts.map +1 -1
  73. package/dist/components/PortalHomePageHeader/PortalHomePageHeader.js +1 -1
  74. package/dist/components/PortalHomePageHeader/PortalHomePageHeader.js.map +1 -1
  75. package/dist/components/PortalList/CreatePortalModal.d.ts.map +1 -1
  76. package/dist/components/PortalList/CreatePortalModal.js +34 -33
  77. package/dist/components/PortalList/CreatePortalModal.js.map +1 -1
  78. package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.d.ts.map +1 -1
  79. package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
  80. package/dist/components/QueryWrapperPlotNav/RowSetView.d.ts.map +1 -1
  81. package/dist/components/QueryWrapperPlotNav/RowSetView.js.map +1 -1
  82. package/dist/components/Resources/Resources.Desktop.js.map +1 -1
  83. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js.map +1 -1
  84. package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.d.ts.map +1 -1
  85. package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
  86. package/dist/components/SetBasicAccessRequirementFields/SetBasicAccessRequirementFields.d.ts.map +1 -1
  87. package/dist/components/SetBasicAccessRequirementFields/SetBasicAccessRequirementFields.js.map +1 -1
  88. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts.map +1 -1
  89. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
  90. package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
  91. package/dist/components/SynapsePortalBanners/SynapsePortalBanners.d.ts.map +1 -1
  92. package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js +1 -1
  93. package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
  94. package/dist/components/SynapseTable/SearchV2.js.map +1 -1
  95. package/dist/components/SynapseTable/SynapseTable.js.map +1 -1
  96. package/dist/components/SynapseTable/SynapseTableUtils.d.ts.map +1 -1
  97. package/dist/components/SynapseTable/SynapseTableUtils.js.map +1 -1
  98. package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
  99. package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
  100. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.d.ts.map +1 -1
  101. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
  102. package/dist/components/TanStackTable/TanStackTableUtils.js.map +1 -1
  103. package/dist/components/TextField/TextField.css +1 -1
  104. package/dist/components/TextField/TextField.d.ts +14 -3
  105. package/dist/components/TextField/TextField.d.ts.map +1 -1
  106. package/dist/components/TextField/TextField.js +41 -43
  107. package/dist/components/TextField/TextField.js.map +1 -1
  108. package/dist/components/TextField/TextField.module.js +3 -4
  109. package/dist/components/TextField/TextField.module.js.map +1 -1
  110. package/dist/components/TextField/TextField.module.scss +18 -10
  111. package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.d.ts.map +1 -1
  112. package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js +20 -16
  113. package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js.map +1 -1
  114. package/dist/components/UserCardList/UserCardList.d.ts.map +1 -1
  115. package/dist/components/UserCardList/UserCardList.js.map +1 -1
  116. package/dist/components/UserCardList/UserCardListGroups/UserCardListGroups.Desktop.js.map +1 -1
  117. package/dist/components/UserOrTeamBadge/useUserOrTeam.js.map +1 -1
  118. package/dist/components/UserSearchBox/UserSearchBox.d.ts.map +1 -1
  119. package/dist/components/UserSearchBox/UserSearchBox.js.map +1 -1
  120. package/dist/components/WikiMarkdownEditorButton/WikiMarkdownEditorButton.js.map +1 -1
  121. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
  122. package/dist/components/doi/CreateOrUpdateDoiModal.js +13 -13
  123. package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
  124. package/dist/components/entity/page/title_bar/TitleBarProperties.js +23 -23
  125. package/dist/components/entity/page/title_bar/TitleBarProperties.js.map +1 -1
  126. package/dist/components/entity/page/title_bar/useGetMentions.js.map +1 -1
  127. package/dist/components/index.js +17 -17
  128. package/dist/components/styled/StyledFormControl.d.ts +15 -0
  129. package/dist/components/styled/StyledFormControl.d.ts.map +1 -1
  130. package/dist/components/styled/StyledFormControl.js +1 -0
  131. package/dist/components/styled/StyledFormControl.js.map +1 -1
  132. package/dist/components/styled/index.js +3 -3
  133. package/dist/components/utils/ConditionalWrapper.d.ts.map +1 -1
  134. package/dist/components/utils/ConditionalWrapper.js.map +1 -1
  135. package/dist/components/widgets/ElementWithTooltip.js.map +1 -1
  136. package/dist/components/widgets/facet-nav/FacetNavPanel.d.ts.map +1 -1
  137. package/dist/components/widgets/facet-nav/FacetNavPanel.js +69 -62
  138. package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
  139. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.d.ts +1 -0
  140. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.d.ts.map +1 -1
  141. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js +27 -26
  142. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
  143. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterDropdown.d.ts +1 -0
  144. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterDropdown.d.ts.map +1 -1
  145. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterDropdown.js +2 -1
  146. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterDropdown.js.map +1 -1
  147. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.d.ts +1 -0
  148. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.d.ts.map +1 -1
  149. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.js +44 -43
  150. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.js.map +1 -1
  151. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.d.ts +13 -0
  152. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.d.ts.map +1 -0
  153. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js +398 -0
  154. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js.map +1 -0
  155. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts.map +1 -1
  156. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +49 -19
  157. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
  158. package/dist/features/entity/metadata-task/components/MetadataTasksPage.d.ts.map +1 -1
  159. package/dist/features/entity/metadata-task/components/MetadataTasksPage.js +46 -25
  160. package/dist/features/entity/metadata-task/components/MetadataTasksPage.js.map +1 -1
  161. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.d.ts +0 -1
  162. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.d.ts.map +1 -1
  163. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js +20 -22
  164. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js.map +1 -1
  165. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts.map +1 -1
  166. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js +25 -27
  167. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js.map +1 -1
  168. package/dist/features/entity/metadata-task/utils/constants.d.ts +26 -0
  169. package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -1
  170. package/dist/features/entity/metadata-task/utils/constants.js +2 -2
  171. package/dist/features/entity/metadata-task/utils/constants.js.map +1 -1
  172. package/dist/index.js +17 -17
  173. package/dist/mockServiceWorker.js +6 -8
  174. package/dist/mocks/curation/mockCurationTask.d.ts +2 -2
  175. package/dist/mocks/curation/mockCurationTask.d.ts.map +1 -1
  176. package/dist/mocks/curation/mockCurationTask.js +9 -5
  177. package/dist/mocks/curation/mockCurationTask.js.map +1 -1
  178. package/dist/mocks/msw/handlers/featureFlagHandlers.d.ts +1 -1
  179. package/dist/mocks/msw/handlers/featureFlagHandlers.d.ts.map +1 -1
  180. package/dist/mocks/msw/handlers/featureFlagHandlers.js +1 -1
  181. package/dist/mocks/msw/handlers/featureFlagHandlers.js.map +1 -1
  182. package/dist/style/components/_cards.scss +0 -3
  183. package/dist/style/components/_data-grid-extra.scss +2 -2
  184. package/dist/style/components/_login.scss +6 -2
  185. package/dist/style/main.css +1 -1
  186. package/dist/synapse-client/SynapseClient.d.ts +2 -1
  187. package/dist/synapse-client/SynapseClient.d.ts.map +1 -1
  188. package/dist/synapse-client/SynapseClient.js.map +1 -1
  189. package/dist/synapse-queries/QueryFilterUtils.js.map +1 -1
  190. package/dist/synapse-queries/curation/task/useCurationTask.d.ts +1 -0
  191. package/dist/synapse-queries/curation/task/useCurationTask.d.ts.map +1 -1
  192. package/dist/synapse-queries/curation/task/useCurationTask.js +16 -6
  193. package/dist/synapse-queries/curation/task/useCurationTask.js.map +1 -1
  194. package/dist/synapse-queries/dataaccess/useAccessRequirements.d.ts.map +1 -1
  195. package/dist/synapse-queries/dataaccess/useAccessRequirements.js.map +1 -1
  196. package/dist/synapse-queries/entity/useGetQueryResultBundle.js.map +1 -1
  197. package/dist/synapse-queries/featureflags/useGetFeatureFlag.d.ts +1 -1
  198. package/dist/synapse-queries/featureflags/useGetFeatureFlag.d.ts.map +1 -1
  199. package/dist/synapse-queries/featureflags/useGetFeatureFlag.js.map +1 -1
  200. package/dist/synapse-queries/file/useFiles.d.ts.map +1 -1
  201. package/dist/synapse-queries/file/useFiles.js.map +1 -1
  202. package/dist/synapse-queries/termsOfService/useTermsOfService.d.ts.map +1 -1
  203. package/dist/synapse-queries/termsOfService/useTermsOfService.js.map +1 -1
  204. package/dist/synapse-queries/user/useGetUserChallenges.d.ts.map +1 -1
  205. package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
  206. package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
  207. package/dist/theme/palette/Palettes.d.ts +2 -2
  208. package/dist/theme/palette/Palettes.d.ts.map +1 -1
  209. package/dist/theme/palette/Palettes.js +2 -2
  210. package/dist/theme/palette/Palettes.js.map +1 -1
  211. package/dist/tsconfig.build.tsbuildinfo +1 -1
  212. package/dist/types/ThemeTypes.d.ts +5 -6
  213. package/dist/utils/AppUtils/session/SynapseSessionManager.d.ts.map +1 -1
  214. package/dist/utils/AppUtils/session/SynapseSessionManager.js.map +1 -1
  215. package/dist/utils/featureflag/FeatureFlags.d.ts +12 -0
  216. package/dist/utils/featureflag/FeatureFlags.d.ts.map +1 -0
  217. package/dist/utils/featureflag/FeatureFlags.js +8 -0
  218. package/dist/utils/featureflag/FeatureFlags.js.map +1 -0
  219. package/dist/utils/functions/SanitizeHtmlUtils.js.map +1 -1
  220. package/dist/utils/functions/queryUtils.js.map +1 -1
  221. package/dist/utils/hooks/useGetInfoFromIds.d.ts.map +1 -1
  222. package/dist/utils/hooks/useGetInfoFromIds.js.map +1 -1
  223. package/dist/utils/slots/SlotUtils.d.ts.map +1 -1
  224. package/dist/utils/slots/SlotUtils.js.map +1 -1
  225. package/package.json +7 -8
@@ -40,12 +40,12 @@ declare module '@mui/material/styles' {
40
40
  styledBackground?: string
41
41
  }
42
42
 
43
- interface TypographyVariants
44
- extends RecordWithCustomVariantKeys<React.CSSProperties> {}
43
+ interface TypographyVariants extends RecordWithCustomVariantKeys<React.CSSProperties> {}
45
44
 
46
45
  // allow configuration using `createTheme`
47
- interface TypographyVariantsOptions
48
- extends RecordWithCustomVariantKeys<React.CSSProperties | undefined> {}
46
+ interface TypographyVariantsOptions extends RecordWithCustomVariantKeys<
47
+ React.CSSProperties | undefined
48
+ > {}
49
49
 
50
50
  interface Palette {
51
51
  tertiary: Palette['primary']
@@ -84,6 +84,5 @@ declare module '@mui/material' {
84
84
 
85
85
  // Update the Typography's variant prop options
86
86
  declare module '@mui/material/Typography' {
87
- interface TypographyPropsVariantOverrides
88
- extends RecordWithCustomVariantKeys<true> {}
87
+ interface TypographyPropsVariantOverrides extends RecordWithCustomVariantKeys<true> {}
89
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseSessionManager.d.ts","sourceRoot":"","sources":["../../../../src/utils/AppUtils/session/SynapseSessionManager.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,+BAA+B,EAC/B,cAAc,EAGf,MAAM,kCAAkC,CAAA;AAIzC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,eAAe,EAAE,OAAO,CAAA;IACxB,qBAAqB,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG;IACzC,+GAA+G;IAC/G,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;gIAC4H;IAC5H,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+EAA+E;IAC/E,+BAA+B,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5C;sGACkG;IAClG,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAUD;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAGe;gBAElB,OAAO,GAAE,4BAAiC;IAWtD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAO3C;;;;;OAKG;IACH,WAAW,IAAI,YAAY;IAI3B;;;OAGG;IACH,iBAAiB,IAAI,YAAY;IAIjC;;;OAGG;IACH,UAAU,CACR,OAAO,EAAE,IAAI,CAAC,4BAA4B,EAAE,cAAc,GAAG,QAAQ,CAAC,GACrE,IAAI;IASP;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,OAAO,IAAI,IAAI;IAOf;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAmFrC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnC;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAS1D;;;OAGG;WACU,aAAa,CACxB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,+BAA+B,GAAG,IAAI,CAAC;IA6BlD;;OAEG;WACU,yBAAyB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC;IAQ1B;;;OAGG;YACW,sBAAsB;IAOpC,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,UAAU;CAKnB"}
1
+ {"version":3,"file":"SynapseSessionManager.d.ts","sourceRoot":"","sources":["../../../../src/utils/AppUtils/session/SynapseSessionManager.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,+BAA+B,EAC/B,cAAc,EAGf,MAAM,kCAAkC,CAAA;AAIzC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,eAAe,EAAE,OAAO,CAAA;IACxB,qBAAqB,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG;IACzC,+GAA+G;IAC/G,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;gIAC4H;IAC5H,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+EAA+E;IAC/E,+BAA+B,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5C;sGACkG;IAClG,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAUD;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAGe;gBAElB,OAAO,GAAE,4BAAiC;IAWtD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAO3C;;;;;OAKG;IACH,WAAW,IAAI,YAAY;IAI3B;;;OAGG;IACH,iBAAiB,IAAI,YAAY;IAIjC;;;OAGG;IACH,UAAU,CACR,OAAO,EAAE,IAAI,CAAC,4BAA4B,EAAE,cAAc,GAAG,QAAQ,CAAC,GACrE,IAAI;IASP;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,OAAO,IAAI,IAAI;IAOf;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAkFrC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnC;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAS1D;;;OAGG;WACU,aAAa,CACxB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,+BAA+B,GAAG,IAAI,CAAC;IA6BlD;;OAEG;WACU,yBAAyB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC;IAQ1B;;;OAGG;YACW,sBAAsB;IAOpC,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,UAAU;CAKnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseSessionManager.js","names":[],"sources":["../../../../src/utils/AppUtils/session/SynapseSessionManager.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport {\n BackendDestinationEnum,\n getEndpoint,\n} from '@/utils/functions/getEndpoint'\nimport {\n OAuthTokenIntrospectionResponse,\n RealmPrincipal,\n SynapseClient as SynapseOpenApiClient,\n SynapseClientError,\n} from '@sage-bionetworks/synapse-client'\n\nconst REFRESH_INTERVAL_MS = 60_000\n\nexport type SessionState = {\n token: string | undefined\n realmId: string | undefined\n userId: string | undefined\n isAuthenticated: boolean\n hasInitializedSession: boolean\n}\n\nexport type SynapseSessionManagerOptions = {\n /** The realm that an unauthenticated user should be signed in to. Defaults to \"0\", the public Synapse realm */\n defaultRealm?: string\n /** If defined, specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated.\n * If the elapsed time is greater than this value, the session will be cleared and the user will have to re-authenticate. */\n maxAge?: number\n /** Called if the user is not authenticated when the session is initialized. */\n onMissingExpectedAuthentication?: () => void\n /** Called when a stored token fails validation (e.g. expired). Called after the session manager signs out.\n * If not provided, the session manager will call `window.location.reload()` after signing out. */\n onSessionInvalid?: () => void\n}\n\nconst INITIAL_STATE: SessionState = {\n token: undefined,\n realmId: undefined,\n userId: undefined,\n isAuthenticated: false,\n hasInitializedSession: false,\n}\n\n/**\n * Framework-agnostic session manager for Synapse applications.\n *\n * Handles the full session lifecycle: token retrieval from browser cookies,\n * token validation via OAuth2 introspection, periodic refresh, and sign-out.\n *\n * Can be used directly from any JavaScript context (GWT/JsInterop, vanilla JS, etc.)\n * or wrapped by a framework-specific adapter (e.g. a React hook via `useSyncExternalStore`).\n *\n * Implements the external store contract: use {@link subscribe} and {@link getSnapshot}\n * to integrate with React's `useSyncExternalStore`, or any other subscription-based framework.\n */\nexport class SynapseSessionManager {\n private state: SessionState = INITIAL_STATE\n private listeners = new Set<() => void>()\n private intervalId: ReturnType<typeof setInterval> | null = null\n private options: Required<\n Pick<SynapseSessionManagerOptions, 'defaultRealm'>\n > &\n SynapseSessionManagerOptions\n\n constructor(options: SynapseSessionManagerOptions = {}) {\n this.options = {\n ...options,\n defaultRealm: options.defaultRealm ?? '0',\n }\n // Bind methods so they can be passed directly to useSyncExternalStore\n this.subscribe = this.subscribe.bind(this)\n this.getSnapshot = this.getSnapshot.bind(this)\n this.getServerSnapshot = this.getServerSnapshot.bind(this)\n }\n\n /**\n * Subscribe to state changes. The listener is called whenever session state is updated.\n * @returns an unsubscribe function.\n *\n * Compatible with React's `useSyncExternalStore(manager.subscribe, manager.getSnapshot)`.\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /**\n * Get the current session state snapshot. Returns a stable reference that only\n * changes when the state is updated.\n *\n * Compatible with React's `useSyncExternalStore(manager.subscribe, manager.getSnapshot)`.\n */\n getSnapshot(): SessionState {\n return this.state\n }\n\n /**\n * Get the server-side (SSR) snapshot: always returns the unauthenticated initial state.\n * Required by React's `useSyncExternalStore` when rendering on the server.\n */\n getServerSnapshot(): SessionState {\n return INITIAL_STATE\n }\n\n /**\n * Update mutable options. Values are read at `refreshSession`/`clearSession` call time,\n * so changes take effect on the next refresh cycle.\n */\n setOptions(\n options: Pick<SynapseSessionManagerOptions, 'defaultRealm' | 'maxAge'>,\n ): void {\n if (options.defaultRealm !== undefined) {\n this.options.defaultRealm = options.defaultRealm\n }\n if (options.maxAge !== undefined) {\n this.options.maxAge = options.maxAge\n }\n }\n\n /**\n * Start the session manager: performs an initial refresh and sets up the periodic refresh interval.\n */\n start(): void {\n this.refreshSession()\n this.intervalId = setInterval(() => {\n this.refreshSession()\n }, REFRESH_INTERVAL_MS)\n }\n\n /**\n * Stop the periodic refresh and clean up resources.\n */\n dispose(): void {\n if (this.intervalId !== null) {\n clearInterval(this.intervalId)\n this.intervalId = null\n }\n }\n\n /**\n * Refresh the session by reading the stored token, validating it, and updating state.\n * If no token is stored, initializes an anonymous session.\n * If the token is invalid, signs out and triggers onSessionInvalid (or reloads the page).\n */\n async refreshSession(): Promise<void> {\n let token = await SynapseSessionManager.getStoredToken()\n\n if (!token) {\n token = await this.initAnonymousUserState()\n }\n\n const introspectionResponse = await SynapseSessionManager.validateToken(\n token,\n this.options.maxAge,\n )\n if (introspectionResponse === null) {\n // Token was invalid (e.g. expired). Clear from browser storage and get a fresh anonymous token.\n await SynapseClient.signOut(this.options.defaultRealm)\n if (this.options.onSessionInvalid) {\n this.options.onSessionInvalid()\n } else {\n window.location.reload()\n }\n return\n }\n\n const currentUserId = introspectionResponse.sub\n\n let realmPrincipals: RealmPrincipal | undefined\n try {\n realmPrincipals = await SynapseSessionManager.getCurrentRealmPrincipals(\n token,\n )\n } catch (error) {\n // Special handling for Terms of Service and 2FA errors - allow initialization to continue\n // so the app can prompt the user to complete the required action\n const isTosError =\n error instanceof SynapseClientError &&\n error.reason?.toLowerCase().includes('terms of service')\n const isTwoFaError =\n error instanceof SynapseClientError &&\n error.errorResponse &&\n 'errorCode' in error.errorResponse &&\n error.errorResponse.errorCode === 'TWO_FA_ENABLED_REQUIRED'\n\n if (isTosError || isTwoFaError) {\n const errorType = isTosError\n ? 'Terms of Service not accepted'\n : 'Two factor authentication required'\n console.warn(\n `${errorType}. Session will be initialized without realm principals.`,\n )\n // Continue initialization with partial data - the app's flow will handle this\n this.updateState({\n token,\n hasInitializedSession: true,\n realmId: this.options.defaultRealm,\n userId: currentUserId,\n isAuthenticated: true, // User has a valid token, even if ToS/2FA not complete\n })\n return\n }\n // For any other error, re-throw to be handled by caller\n console.error('Error fetching realm principals: ', error)\n throw error\n }\n\n const isAuthenticated = currentUserId !== realmPrincipals.anonymousUser\n\n /*\n NOTE: It's possible for a user to possess an anonymous token from a different realm (e.g. if they started on an app with a default realm, \n then navigated to a different app with a different default realm). Currently, we will do nothing, so the user will stay in the realm in which they started.\n We may consider different approaches in the future as new requirements arise, such as\n - Replacing the anonymous token with a new one from the default realm (could cause issues if two apps with different realms are used in different tabs)\n - Storing anonymous tokens per-realm\n - Adding UI to choose a realm or account to use\n */\n\n this.updateState({\n token,\n hasInitializedSession: true,\n realmId: realmPrincipals.realmId,\n userId: currentUserId,\n isAuthenticated,\n })\n }\n\n /**\n * Clear the current session by signing out and initializing an anonymous session.\n * Does NOT handle navigation/reload — the caller is responsible for that.\n */\n async clearSession(): Promise<void> {\n await this.initAnonymousUserState()\n await this.refreshSession()\n }\n\n /**\n * Attempt to get the stored access token from the browser cookie.\n * Returns the token string, or undefined if not found or on error.\n */\n static async getStoredToken(): Promise<string | undefined> {\n try {\n return await SynapseClient.getAccessTokenFromCookie()\n } catch (e) {\n console.error('Unable to get the access token: ', e)\n return undefined\n }\n }\n\n /**\n * Validate the token by calling the token introspection service.\n * @return the introspection response if valid, or null if the token is invalid\n */\n static async validateToken(\n accessToken: string,\n maxAge?: number,\n ): Promise<OAuthTokenIntrospectionResponse | null> {\n const synapseClient = new SynapseOpenApiClient({\n accessToken,\n basePath: getEndpoint(BackendDestinationEnum.REPO_ENDPOINT),\n })\n try {\n const response =\n await synapseClient.openIDConnectServicesClient.postAuthV1Oauth2Introspect(\n {\n oAuthTokenIntrospectionRequest: {\n token: accessToken,\n max_age: maxAge,\n },\n },\n )\n if (!response.active) {\n throw new Error('Token is not active.')\n }\n return response\n } catch (e) {\n console.error(\n 'Error validating the access token. Clearing session. Error: ',\n (e as Error).message,\n )\n }\n\n return null\n }\n\n /**\n * Get the realm principals for the current token.\n */\n static async getCurrentRealmPrincipals(\n accessToken: string,\n ): Promise<RealmPrincipal> {\n const synapseClient = new SynapseOpenApiClient({\n accessToken,\n basePath: getEndpoint(BackendDestinationEnum.REPO_ENDPOINT),\n })\n return synapseClient.realmServicesClient.getRepoV1RealmPrincipals()\n }\n\n /**\n * Initialize the session for an anonymous user.\n * @return the new anonymous token.\n */\n private async initAnonymousUserState(): Promise<string> {\n if (this.options.onMissingExpectedAuthentication) {\n this.options.onMissingExpectedAuthentication()\n }\n return SynapseClient.signOut(this.options.defaultRealm)\n }\n\n private updateState(newState: SessionState): void {\n this.state = newState\n this.emitChange()\n }\n\n private emitChange(): void {\n for (const listener of this.listeners) {\n listener()\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SynapseSessionManager.js","names":[],"sources":["../../../../src/utils/AppUtils/session/SynapseSessionManager.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport {\n BackendDestinationEnum,\n getEndpoint,\n} from '@/utils/functions/getEndpoint'\nimport {\n OAuthTokenIntrospectionResponse,\n RealmPrincipal,\n SynapseClient as SynapseOpenApiClient,\n SynapseClientError,\n} from '@sage-bionetworks/synapse-client'\n\nconst REFRESH_INTERVAL_MS = 60_000\n\nexport type SessionState = {\n token: string | undefined\n realmId: string | undefined\n userId: string | undefined\n isAuthenticated: boolean\n hasInitializedSession: boolean\n}\n\nexport type SynapseSessionManagerOptions = {\n /** The realm that an unauthenticated user should be signed in to. Defaults to \"0\", the public Synapse realm */\n defaultRealm?: string\n /** If defined, specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated.\n * If the elapsed time is greater than this value, the session will be cleared and the user will have to re-authenticate. */\n maxAge?: number\n /** Called if the user is not authenticated when the session is initialized. */\n onMissingExpectedAuthentication?: () => void\n /** Called when a stored token fails validation (e.g. expired). Called after the session manager signs out.\n * If not provided, the session manager will call `window.location.reload()` after signing out. */\n onSessionInvalid?: () => void\n}\n\nconst INITIAL_STATE: SessionState = {\n token: undefined,\n realmId: undefined,\n userId: undefined,\n isAuthenticated: false,\n hasInitializedSession: false,\n}\n\n/**\n * Framework-agnostic session manager for Synapse applications.\n *\n * Handles the full session lifecycle: token retrieval from browser cookies,\n * token validation via OAuth2 introspection, periodic refresh, and sign-out.\n *\n * Can be used directly from any JavaScript context (GWT/JsInterop, vanilla JS, etc.)\n * or wrapped by a framework-specific adapter (e.g. a React hook via `useSyncExternalStore`).\n *\n * Implements the external store contract: use {@link subscribe} and {@link getSnapshot}\n * to integrate with React's `useSyncExternalStore`, or any other subscription-based framework.\n */\nexport class SynapseSessionManager {\n private state: SessionState = INITIAL_STATE\n private listeners = new Set<() => void>()\n private intervalId: ReturnType<typeof setInterval> | null = null\n private options: Required<\n Pick<SynapseSessionManagerOptions, 'defaultRealm'>\n > &\n SynapseSessionManagerOptions\n\n constructor(options: SynapseSessionManagerOptions = {}) {\n this.options = {\n ...options,\n defaultRealm: options.defaultRealm ?? '0',\n }\n // Bind methods so they can be passed directly to useSyncExternalStore\n this.subscribe = this.subscribe.bind(this)\n this.getSnapshot = this.getSnapshot.bind(this)\n this.getServerSnapshot = this.getServerSnapshot.bind(this)\n }\n\n /**\n * Subscribe to state changes. The listener is called whenever session state is updated.\n * @returns an unsubscribe function.\n *\n * Compatible with React's `useSyncExternalStore(manager.subscribe, manager.getSnapshot)`.\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /**\n * Get the current session state snapshot. Returns a stable reference that only\n * changes when the state is updated.\n *\n * Compatible with React's `useSyncExternalStore(manager.subscribe, manager.getSnapshot)`.\n */\n getSnapshot(): SessionState {\n return this.state\n }\n\n /**\n * Get the server-side (SSR) snapshot: always returns the unauthenticated initial state.\n * Required by React's `useSyncExternalStore` when rendering on the server.\n */\n getServerSnapshot(): SessionState {\n return INITIAL_STATE\n }\n\n /**\n * Update mutable options. Values are read at `refreshSession`/`clearSession` call time,\n * so changes take effect on the next refresh cycle.\n */\n setOptions(\n options: Pick<SynapseSessionManagerOptions, 'defaultRealm' | 'maxAge'>,\n ): void {\n if (options.defaultRealm !== undefined) {\n this.options.defaultRealm = options.defaultRealm\n }\n if (options.maxAge !== undefined) {\n this.options.maxAge = options.maxAge\n }\n }\n\n /**\n * Start the session manager: performs an initial refresh and sets up the periodic refresh interval.\n */\n start(): void {\n this.refreshSession()\n this.intervalId = setInterval(() => {\n this.refreshSession()\n }, REFRESH_INTERVAL_MS)\n }\n\n /**\n * Stop the periodic refresh and clean up resources.\n */\n dispose(): void {\n if (this.intervalId !== null) {\n clearInterval(this.intervalId)\n this.intervalId = null\n }\n }\n\n /**\n * Refresh the session by reading the stored token, validating it, and updating state.\n * If no token is stored, initializes an anonymous session.\n * If the token is invalid, signs out and triggers onSessionInvalid (or reloads the page).\n */\n async refreshSession(): Promise<void> {\n let token = await SynapseSessionManager.getStoredToken()\n\n if (!token) {\n token = await this.initAnonymousUserState()\n }\n\n const introspectionResponse = await SynapseSessionManager.validateToken(\n token,\n this.options.maxAge,\n )\n if (introspectionResponse === null) {\n // Token was invalid (e.g. expired). Clear from browser storage and get a fresh anonymous token.\n await SynapseClient.signOut(this.options.defaultRealm)\n if (this.options.onSessionInvalid) {\n this.options.onSessionInvalid()\n } else {\n window.location.reload()\n }\n return\n }\n\n const currentUserId = introspectionResponse.sub\n\n let realmPrincipals: RealmPrincipal | undefined\n try {\n realmPrincipals =\n await SynapseSessionManager.getCurrentRealmPrincipals(token)\n } catch (error) {\n // Special handling for Terms of Service and 2FA errors - allow initialization to continue\n // so the app can prompt the user to complete the required action\n const isTosError =\n error instanceof SynapseClientError &&\n error.reason?.toLowerCase().includes('terms of service')\n const isTwoFaError =\n error instanceof SynapseClientError &&\n error.errorResponse &&\n 'errorCode' in error.errorResponse &&\n error.errorResponse.errorCode === 'TWO_FA_ENABLED_REQUIRED'\n\n if (isTosError || isTwoFaError) {\n const errorType = isTosError\n ? 'Terms of Service not accepted'\n : 'Two factor authentication required'\n console.warn(\n `${errorType}. Session will be initialized without realm principals.`,\n )\n // Continue initialization with partial data - the app's flow will handle this\n this.updateState({\n token,\n hasInitializedSession: true,\n realmId: this.options.defaultRealm,\n userId: currentUserId,\n isAuthenticated: true, // User has a valid token, even if ToS/2FA not complete\n })\n return\n }\n // For any other error, re-throw to be handled by caller\n console.error('Error fetching realm principals: ', error)\n throw error\n }\n\n const isAuthenticated = currentUserId !== realmPrincipals.anonymousUser\n\n /*\n NOTE: It's possible for a user to possess an anonymous token from a different realm (e.g. if they started on an app with a default realm, \n then navigated to a different app with a different default realm). Currently, we will do nothing, so the user will stay in the realm in which they started.\n We may consider different approaches in the future as new requirements arise, such as\n - Replacing the anonymous token with a new one from the default realm (could cause issues if two apps with different realms are used in different tabs)\n - Storing anonymous tokens per-realm\n - Adding UI to choose a realm or account to use\n */\n\n this.updateState({\n token,\n hasInitializedSession: true,\n realmId: realmPrincipals.realmId,\n userId: currentUserId,\n isAuthenticated,\n })\n }\n\n /**\n * Clear the current session by signing out and initializing an anonymous session.\n * Does NOT handle navigation/reload — the caller is responsible for that.\n */\n async clearSession(): Promise<void> {\n await this.initAnonymousUserState()\n await this.refreshSession()\n }\n\n /**\n * Attempt to get the stored access token from the browser cookie.\n * Returns the token string, or undefined if not found or on error.\n */\n static async getStoredToken(): Promise<string | undefined> {\n try {\n return await SynapseClient.getAccessTokenFromCookie()\n } catch (e) {\n console.error('Unable to get the access token: ', e)\n return undefined\n }\n }\n\n /**\n * Validate the token by calling the token introspection service.\n * @return the introspection response if valid, or null if the token is invalid\n */\n static async validateToken(\n accessToken: string,\n maxAge?: number,\n ): Promise<OAuthTokenIntrospectionResponse | null> {\n const synapseClient = new SynapseOpenApiClient({\n accessToken,\n basePath: getEndpoint(BackendDestinationEnum.REPO_ENDPOINT),\n })\n try {\n const response =\n await synapseClient.openIDConnectServicesClient.postAuthV1Oauth2Introspect(\n {\n oAuthTokenIntrospectionRequest: {\n token: accessToken,\n max_age: maxAge,\n },\n },\n )\n if (!response.active) {\n throw new Error('Token is not active.')\n }\n return response\n } catch (e) {\n console.error(\n 'Error validating the access token. Clearing session. Error: ',\n (e as Error).message,\n )\n }\n\n return null\n }\n\n /**\n * Get the realm principals for the current token.\n */\n static async getCurrentRealmPrincipals(\n accessToken: string,\n ): Promise<RealmPrincipal> {\n const synapseClient = new SynapseOpenApiClient({\n accessToken,\n basePath: getEndpoint(BackendDestinationEnum.REPO_ENDPOINT),\n })\n return synapseClient.realmServicesClient.getRepoV1RealmPrincipals()\n }\n\n /**\n * Initialize the session for an anonymous user.\n * @return the new anonymous token.\n */\n private async initAnonymousUserState(): Promise<string> {\n if (this.options.onMissingExpectedAuthentication) {\n this.options.onMissingExpectedAuthentication()\n }\n return SynapseClient.signOut(this.options.defaultRealm)\n }\n\n private updateState(newState: SessionState): void {\n this.state = newState\n this.emitChange()\n }\n\n private emitChange(): void {\n for (const listener of this.listeners) {\n listener()\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export type FeatureFlags = Record<FeatureFlagEnum, boolean>;
2
+ export declare enum FeatureFlagEnum {
3
+ PORTAL_SEARCH_HEADER = "PORTAL_SEARCH_HEADER",
4
+ DESCRIPTION_FIELD = "DESCRIPTION_FIELD",
5
+ REACT_ENTITY_ACL_EDITOR = "REACT_ENTITY_ACL_EDITOR",
6
+ HOMEPAGE_CHATBOT = "HOMEPAGE_CHATBOT",
7
+ WEBHOOKS_UI = "WEBHOOKS_UI",
8
+ CRISP_CHAT = "CRISP_CHAT",
9
+ PORTAL_CHAT = "PORTAL_CHAT",
10
+ ADKP_HOMEPAGE_V2 = "ADKP_HOMEPAGE_V2"
11
+ }
12
+ //# sourceMappingURL=FeatureFlags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeatureFlags.d.ts","sourceRoot":"","sources":["../../../src/utils/featureflag/FeatureFlags.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;AAE3D,oBAAY,eAAe;IAEzB,oBAAoB,yBAAyB;IAG7C,iBAAiB,sBAAsB;IAGvC,uBAAuB,4BAA4B;IAGnD,gBAAgB,qBAAqB;IAGrC,WAAW,gBAAgB;IAG3B,UAAU,eAAe;IAGzB,WAAW,gBAAgB;IAG3B,gBAAgB,qBAAqB;CACtC"}
@@ -0,0 +1,8 @@
1
+ //#region src/utils/featureflag/FeatureFlags.ts
2
+ var e = /* @__PURE__ */ function(e) {
3
+ return e.PORTAL_SEARCH_HEADER = "PORTAL_SEARCH_HEADER", e.DESCRIPTION_FIELD = "DESCRIPTION_FIELD", e.REACT_ENTITY_ACL_EDITOR = "REACT_ENTITY_ACL_EDITOR", e.HOMEPAGE_CHATBOT = "HOMEPAGE_CHATBOT", e.WEBHOOKS_UI = "WEBHOOKS_UI", e.CRISP_CHAT = "CRISP_CHAT", e.PORTAL_CHAT = "PORTAL_CHAT", e.ADKP_HOMEPAGE_V2 = "ADKP_HOMEPAGE_V2", e;
4
+ }({});
5
+ //#endregion
6
+ export { e as FeatureFlagEnum };
7
+
8
+ //# sourceMappingURL=FeatureFlags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeatureFlags.js","names":[],"sources":["../../../src/utils/featureflag/FeatureFlags.ts"],"sourcesContent":["export type FeatureFlags = Record<FeatureFlagEnum, boolean>\n\nexport enum FeatureFlagEnum {\n // If enabled, show the new header component that uses the HeaderSearchBox component\n PORTAL_SEARCH_HEADER = 'PORTAL_SEARCH_HEADER',\n\n // If enabled, allow viewing & editing the 'description' string field on entities\n DESCRIPTION_FIELD = 'DESCRIPTION_FIELD',\n\n // If enabled, use the re-implemented ACL Editor for entities\n REACT_ENTITY_ACL_EDITOR = 'REACT_ENTITY_ACL_EDITOR',\n\n // If enabled, show the chatbot entrypoint from the new Synapse Homepage\n HOMEPAGE_CHATBOT = 'HOMEPAGE_CHATBOT',\n\n // If enabled, account settings will link to a page to manage webhooks\n WEBHOOKS_UI = 'WEBHOOKS_UI',\n\n // If enabled, load the Crisp chat widget\n CRISP_CHAT = 'CRISP_CHAT',\n\n // If enabled, show the SynapseChat dialog in portals\n PORTAL_CHAT = 'PORTAL_CHAT',\n\n // If enabled, show the new version of the ADKP homepage\n ADKP_HOMEPAGE_V2 = 'ADKP_HOMEPAGE_V2',\n}\n"],"mappings":";AAEA,IAAY,IAAL,yBAAA,GAAA;QAEL,EAAA,uBAAuB,wBAGvB,EAAA,oBAAoB,qBAGpB,EAAA,0BAA0B,2BAG1B,EAAA,mBAAmB,oBAGnB,EAAA,cAAc,eAGd,EAAA,aAAa,cAGb,EAAA,cAAc,eAGd,EAAA,mBAAmB;KACpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"SanitizeHtmlUtils.js","names":[],"sources":["../../../src/utils/functions/SanitizeHtmlUtils.ts"],"sourcesContent":["import DOMPurify, { clearWindow, removeAllHooks } from 'isomorphic-dompurify'\nimport xssLib from 'xss'\nimport type { IFilterXSSOptions } from 'xss'\n\n// xss is a CJS-only module; pull runtime values from the default import to\n// avoid \"named export not found\" errors in Vite dev mode (native ESM).\nconst { safeAttrValue, escapeAttrValue } = xssLib as unknown as {\n safeAttrValue: typeof import('xss')['safeAttrValue']\n escapeAttrValue: typeof import('xss')['escapeAttrValue']\n}\n\n// allow list used by both the xss config and DOMPurify config\nconst xssWhiteList: XSS.IWhiteList = {\n a: ['target', 'href', 'title', 'rel'],\n abbr: ['title'],\n address: [],\n area: ['shape', 'coords', 'href', 'alt'],\n article: [],\n aside: [],\n audio: ['autoplay', 'controls', 'loop', 'preload', 'src'],\n b: [],\n bdi: ['dir'],\n bdo: ['dir'],\n big: [],\n blockquote: ['cite'],\n body: [],\n br: [],\n button: ['class'],\n caption: [],\n center: [],\n cite: [],\n code: [],\n col: ['align', 'valign', 'span', 'width'],\n colgroup: ['align', 'valign', 'span', 'width'],\n dd: [],\n del: ['datetime'],\n details: ['open'],\n div: ['class'],\n dl: [],\n dt: [],\n em: [],\n font: ['color', 'size', 'face'],\n footer: [],\n g: [],\n h1: ['toc'],\n h2: ['toc'],\n h3: ['toc'],\n h4: ['toc'],\n h5: ['toc'],\n h6: ['toc'],\n head: [],\n header: [],\n hr: [],\n html: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n ins: ['datetime'],\n li: ['class'],\n mark: [],\n nav: [],\n noscript: [],\n ol: ['class'],\n p: [],\n pre: [],\n s: [],\n section: [],\n small: [],\n span: ['data-widgetparams', 'data-widget-type', 'class', 'id'],\n sub: [],\n summary: [],\n sup: [],\n strong: [],\n svg: [],\n table: ['width', 'border', 'align', 'valign', 'class'],\n tbody: ['align', 'valign'],\n td: ['width', 'rowspan', 'colspan', 'align', 'valign', 'style'],\n tfoot: ['align', 'valign'],\n th: ['width', 'rowspan', 'colspan', 'align', 'valign', 'class', 'style'],\n thead: ['class', 'align', 'valign'],\n tr: ['rowspan', 'align', 'valign'],\n tt: [],\n u: [],\n ul: ['class'],\n video: ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width'],\n}\n\n// PORTALS-1450: including 'style' in the allow-list will cause string values to come through, which crashes the app when used (because it uses jsx).\nexport const xssOptions: IFilterXSSOptions = {\n whiteList: xssWhiteList,\n stripIgnoreTagBody: true, // filter out all tags not in the whitelist\n allowCommentTag: false,\n css: {\n whiteList: {\n // SWC-7015 - Allow text-align property in style attr to support GFM table syntax\n // The 'style' attribute is used to set text alignment for table cells, so explicitly allow it here\n 'text-align': true,\n },\n },\n onIgnoreTag: function (tag, html, options) {\n if (tag === '!doctype') {\n // do not filter doctype\n return html\n }\n return undefined\n },\n safeAttrValue: function (tag, name, value, cssFilter) {\n // Apply default safeAttrValue filtering:\n value = safeAttrValue(tag, name, value, cssFilter)\n // If the tag is an image and the attribute is 'src', only allow data:image URIs or http(s) URLs\n if (tag === 'img' && name === 'src') {\n if (\n !(\n value &&\n (value.startsWith('data:image/') || value.startsWith('http'))\n )\n ) {\n return ''\n }\n }\n value = escapeAttrValue(value)\n return value\n },\n}\n\nconst ALLOWED_TAGS: string[] = Object.keys(xssWhiteList as object)\nconst ALLOWED_ATTR: string[] = [\n ...new Set(\n Object.values(xssWhiteList as object).flat() as string[], // Flatten all attributes into a single array\n ),\n]\n\nconst ALLOWED_STYLES: Record<string, boolean> = {\n 'text-align': true,\n}\n// Regex to allow only secure image sources\nconst ALLOWED_URI_REGEXP: RegExp = /^(https?:|data:image\\/)/\n\nconst domPurifyConfig = {\n ALLOWED_TAGS,\n ALLOWED_ATTR,\n KEEP_CONTENT: true,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n}\n\nfunction configureDomPurify() {\n // Configure DOMPurify with TypeScript support\n DOMPurify.setConfig(domPurifyConfig)\n\n // Hook: Sanitize attributes (equivalent to safeAttrValue)\n DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {\n const { attrName, attrValue } = data\n\n // Ensure only safe src values for images\n if (node.nodeName.toLowerCase() === 'img' && attrName === 'src') {\n if (!ALLOWED_URI_REGEXP.test(attrValue)) {\n data.keepAttr = false // Remove unsafe src values\n }\n }\n\n if (attrName === 'data-widgetparams' || attrName === 'data-widget-type') {\n data.attrValue = attrValue\n } else if (attrName === 'style') {\n const safeStyles = attrValue\n .split(';')\n .map(style => style.trim())\n .filter(style => {\n const [property] = style.split(':').map(s => s.trim())\n return ALLOWED_STYLES[property] || false // Only allow explicitly listed styles\n })\n .join('; ')\n data.attrValue = safeStyles\n } else {\n data.attrValue = DOMPurify.sanitize(attrValue, domPurifyConfig)\n }\n })\n\n // Hook: Prevent removal of `!doctype` (equivalent to onIgnoreTag)\n DOMPurify.addHook('uponSanitizeElement', (node, data) => {\n if (data.tagName === '!doctype') {\n // Reinsert the doctype manually after sanitization\n if (node.parentNode) {\n node.parentNode.insertBefore(\n document.implementation.createDocumentType('html', '', ''),\n node,\n )\n }\n }\n })\n}\n\nexport function sanitize(input: string): string {\n configureDomPurify()\n const result = DOMPurify.sanitize(input, domPurifyConfig)\n // clearWindow clears memory usage in Node.js, noop in browser:\n clearWindow()\n removeAllHooks()\n return result\n}\n"],"mappings":";;;AAMA,IAAM,EAAE,kBAAe,uBAAoB,GAMrC,IAA+B;CACnC,GAAG;EAAC;EAAU;EAAQ;EAAS;EAAM;CACrC,MAAM,CAAC,QAAQ;CACf,SAAS,EAAE;CACX,MAAM;EAAC;EAAS;EAAU;EAAQ;EAAM;CACxC,SAAS,EAAE;CACX,OAAO,EAAE;CACT,OAAO;EAAC;EAAY;EAAY;EAAQ;EAAW;EAAM;CACzD,GAAG,EAAE;CACL,KAAK,CAAC,MAAM;CACZ,KAAK,CAAC,MAAM;CACZ,KAAK,EAAE;CACP,YAAY,CAAC,OAAO;CACpB,MAAM,EAAE;CACR,IAAI,EAAE;CACN,QAAQ,CAAC,QAAQ;CACjB,SAAS,EAAE;CACX,QAAQ,EAAE;CACV,MAAM,EAAE;CACR,MAAM,EAAE;CACR,KAAK;EAAC;EAAS;EAAU;EAAQ;EAAQ;CACzC,UAAU;EAAC;EAAS;EAAU;EAAQ;EAAQ;CAC9C,IAAI,EAAE;CACN,KAAK,CAAC,WAAW;CACjB,SAAS,CAAC,OAAO;CACjB,KAAK,CAAC,QAAQ;CACd,IAAI,EAAE;CACN,IAAI,EAAE;CACN,IAAI,EAAE;CACN,MAAM;EAAC;EAAS;EAAQ;EAAO;CAC/B,QAAQ,EAAE;CACV,GAAG,EAAE;CACL,IAAI,CAAC,MAAM;CACX,IAAI,CAAC,MAAM;CACX,IAAI,CAAC,MAAM;CACX,IAAI,CAAC,MAAM;CACX,IAAI,CAAC,MAAM;CACX,IAAI,CAAC,MAAM;CACX,MAAM,EAAE;CACR,QAAQ,EAAE;CACV,IAAI,EAAE;CACN,MAAM,EAAE;CACR,GAAG,EAAE;CACL,KAAK;EAAC;EAAO;EAAO;EAAS;EAAS;EAAS;CAC/C,KAAK,CAAC,WAAW;CACjB,IAAI,CAAC,QAAQ;CACb,MAAM,EAAE;CACR,KAAK,EAAE;CACP,UAAU,EAAE;CACZ,IAAI,CAAC,QAAQ;CACb,GAAG,EAAE;CACL,KAAK,EAAE;CACP,GAAG,EAAE;CACL,SAAS,EAAE;CACX,OAAO,EAAE;CACT,MAAM;EAAC;EAAqB;EAAoB;EAAS;EAAK;CAC9D,KAAK,EAAE;CACP,SAAS,EAAE;CACX,KAAK,EAAE;CACP,QAAQ,EAAE;CACV,KAAK,EAAE;CACP,OAAO;EAAC;EAAS;EAAU;EAAS;EAAU;EAAQ;CACtD,OAAO,CAAC,SAAS,SAAS;CAC1B,IAAI;EAAC;EAAS;EAAW;EAAW;EAAS;EAAU;EAAQ;CAC/D,OAAO,CAAC,SAAS,SAAS;CAC1B,IAAI;EAAC;EAAS;EAAW;EAAW;EAAS;EAAU;EAAS;EAAQ;CACxE,OAAO;EAAC;EAAS;EAAS;EAAS;CACnC,IAAI;EAAC;EAAW;EAAS;EAAS;CAClC,IAAI,EAAE;CACN,GAAG,EAAE;CACL,IAAI,CAAC,QAAQ;CACb,OAAO;EAAC;EAAY;EAAY;EAAQ;EAAW;EAAO;EAAU;EAAQ;CAC7E,EAGY,IAAgC;CAC3C,WAAW;CACX,oBAAoB;CACpB,iBAAiB;CACjB,KAAK,EACH,WAAW,EAGT,cAAc,IACf,EACF;CACD,aAAa,SAAU,GAAK,GAAM,GAAS;AACzC,MAAI,MAAQ,WAEV,QAAO;;CAIX,eAAe,SAAU,GAAK,GAAM,GAAO,GAAW;AAepD,SAbA,IAAQ,EAAc,GAAK,GAAM,GAAO,EAAU,EAE9C,MAAQ,SAAS,MAAS,SAE1B,EACE,MACC,EAAM,WAAW,cAAc,IAAI,EAAM,WAAW,OAAO,KAGvD,MAGX,IAAQ,EAAgB,EAAM,EACvB;;CAEV,EAEK,IAAyB,OAAO,KAAK,EAAuB,EAC5D,IAAyB,CAC7B,GAAG,IAAI,IACL,OAAO,OAAO,EAAuB,CAAC,MAAM,CAC7C,CACF,EAEK,IAA0C,EAC9C,cAAc,IACf,EAEK,IAA6B,2BAE7B,IAAkB;CACtB;CACA;CACA,cAAc;CACd,qBAAqB;CACrB,qBAAqB;CACtB;AAED,SAAS,IAAqB;AAiC5B,CA/BA,EAAU,UAAU,EAAgB,EAGpC,EAAU,QAAQ,0BAA0B,GAAM,MAAS;EACzD,IAAM,EAAE,aAAU,iBAAc;AAShC,EANI,EAAK,SAAS,aAAa,KAAK,SAAS,MAAa,UACnD,EAAmB,KAAK,EAAU,KACrC,EAAK,WAAW,MAIhB,MAAa,uBAAuB,MAAa,qBACnD,EAAK,YAAY,IACR,MAAa,UAStB,EAAK,YARc,EAChB,MAAM,IAAI,CACV,KAAI,MAAS,EAAM,MAAM,CAAC,CAC1B,QAAO,MAAS;GACf,IAAM,CAAC,KAAY,EAAM,MAAM,IAAI,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC;AACtD,UAAO,EAAe,MAAa;IACnC,CACD,KAAK,KACS,GAEjB,EAAK,YAAY,EAAU,SAAS,GAAW,EAAgB;GAEjE,EAGF,EAAU,QAAQ,wBAAwB,GAAM,MAAS;AACvD,EAAI,EAAK,YAAY,cAEf,EAAK,cACP,EAAK,WAAW,aACd,SAAS,eAAe,mBAAmB,QAAQ,IAAI,GAAG,EAC1D,EACD;GAGL;;AAGJ,SAAgB,EAAS,GAAuB;AAC9C,IAAoB;CACpB,IAAM,IAAS,EAAU,SAAS,GAAO,EAAgB;AAIzD,QAFA,GAAa,EACb,GAAgB,EACT"}
1
+ {"version":3,"file":"SanitizeHtmlUtils.js","names":[],"sources":["../../../src/utils/functions/SanitizeHtmlUtils.ts"],"sourcesContent":["import DOMPurify, { clearWindow, removeAllHooks } from 'isomorphic-dompurify'\nimport xssLib from 'xss'\nimport type { IFilterXSSOptions } from 'xss'\n\n// xss is a CJS-only module; pull runtime values from the default import to\n// avoid \"named export not found\" errors in Vite dev mode (native ESM).\nconst { safeAttrValue, escapeAttrValue } = xssLib as unknown as {\n safeAttrValue: (typeof import('xss'))['safeAttrValue']\n escapeAttrValue: (typeof import('xss'))['escapeAttrValue']\n}\n\n// allow list used by both the xss config and DOMPurify config\nconst xssWhiteList: XSS.IWhiteList = {\n a: ['target', 'href', 'title', 'rel'],\n abbr: ['title'],\n address: [],\n area: ['shape', 'coords', 'href', 'alt'],\n article: [],\n aside: [],\n audio: ['autoplay', 'controls', 'loop', 'preload', 'src'],\n b: [],\n bdi: ['dir'],\n bdo: ['dir'],\n big: [],\n blockquote: ['cite'],\n body: [],\n br: [],\n button: ['class'],\n caption: [],\n center: [],\n cite: [],\n code: [],\n col: ['align', 'valign', 'span', 'width'],\n colgroup: ['align', 'valign', 'span', 'width'],\n dd: [],\n del: ['datetime'],\n details: ['open'],\n div: ['class'],\n dl: [],\n dt: [],\n em: [],\n font: ['color', 'size', 'face'],\n footer: [],\n g: [],\n h1: ['toc'],\n h2: ['toc'],\n h3: ['toc'],\n h4: ['toc'],\n h5: ['toc'],\n h6: ['toc'],\n head: [],\n header: [],\n hr: [],\n html: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n ins: ['datetime'],\n li: ['class'],\n mark: [],\n nav: [],\n noscript: [],\n ol: ['class'],\n p: [],\n pre: [],\n s: [],\n section: [],\n small: [],\n span: ['data-widgetparams', 'data-widget-type', 'class', 'id'],\n sub: [],\n summary: [],\n sup: [],\n strong: [],\n svg: [],\n table: ['width', 'border', 'align', 'valign', 'class'],\n tbody: ['align', 'valign'],\n td: ['width', 'rowspan', 'colspan', 'align', 'valign', 'style'],\n tfoot: ['align', 'valign'],\n th: ['width', 'rowspan', 'colspan', 'align', 'valign', 'class', 'style'],\n thead: ['class', 'align', 'valign'],\n tr: ['rowspan', 'align', 'valign'],\n tt: [],\n u: [],\n ul: ['class'],\n video: ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width'],\n}\n\n// PORTALS-1450: including 'style' in the allow-list will cause string values to come through, which crashes the app when used (because it uses jsx).\nexport const xssOptions: IFilterXSSOptions = {\n whiteList: xssWhiteList,\n stripIgnoreTagBody: true, // filter out all tags not in the whitelist\n allowCommentTag: false,\n css: {\n whiteList: {\n // SWC-7015 - Allow text-align property in style attr to support GFM table syntax\n // The 'style' attribute is used to set text alignment for table cells, so explicitly allow it here\n 'text-align': true,\n },\n },\n onIgnoreTag: function (tag, html, options) {\n if (tag === '!doctype') {\n // do not filter doctype\n return html\n }\n return undefined\n },\n safeAttrValue: function (tag, name, value, cssFilter) {\n // Apply default safeAttrValue filtering:\n value = safeAttrValue(tag, name, value, cssFilter)\n // If the tag is an image and the attribute is 'src', only allow data:image URIs or http(s) URLs\n if (tag === 'img' && name === 'src') {\n if (\n !(\n value &&\n (value.startsWith('data:image/') || value.startsWith('http'))\n )\n ) {\n return ''\n }\n }\n value = escapeAttrValue(value)\n return value\n },\n}\n\nconst ALLOWED_TAGS: string[] = Object.keys(xssWhiteList as object)\nconst ALLOWED_ATTR: string[] = [\n ...new Set(\n Object.values(xssWhiteList as object).flat() as string[], // Flatten all attributes into a single array\n ),\n]\n\nconst ALLOWED_STYLES: Record<string, boolean> = {\n 'text-align': true,\n}\n// Regex to allow only secure image sources\nconst ALLOWED_URI_REGEXP: RegExp = /^(https?:|data:image\\/)/\n\nconst domPurifyConfig = {\n ALLOWED_TAGS,\n ALLOWED_ATTR,\n KEEP_CONTENT: true,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n}\n\nfunction configureDomPurify() {\n // Configure DOMPurify with TypeScript support\n DOMPurify.setConfig(domPurifyConfig)\n\n // Hook: Sanitize attributes (equivalent to safeAttrValue)\n DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {\n const { attrName, attrValue } = data\n\n // Ensure only safe src values for images\n if (node.nodeName.toLowerCase() === 'img' && attrName === 'src') {\n if (!ALLOWED_URI_REGEXP.test(attrValue)) {\n data.keepAttr = false // Remove unsafe src values\n }\n }\n\n if (attrName === 'data-widgetparams' || attrName === 'data-widget-type') {\n data.attrValue = attrValue\n } else if (attrName === 'style') {\n const safeStyles = attrValue\n .split(';')\n .map(style => style.trim())\n .filter(style => {\n const [property] = style.split(':').map(s => s.trim())\n return ALLOWED_STYLES[property] || false // Only allow explicitly listed styles\n })\n .join('; ')\n data.attrValue = safeStyles\n } else {\n data.attrValue = DOMPurify.sanitize(attrValue, domPurifyConfig)\n }\n })\n\n // Hook: Prevent removal of `!doctype` (equivalent to onIgnoreTag)\n DOMPurify.addHook('uponSanitizeElement', (node, data) => {\n if (data.tagName === '!doctype') {\n // Reinsert the doctype manually after sanitization\n if (node.parentNode) {\n node.parentNode.insertBefore(\n document.implementation.createDocumentType('html', '', ''),\n node,\n )\n }\n }\n })\n}\n\nexport function sanitize(input: string): string {\n configureDomPurify()\n const result = DOMPurify.sanitize(input, domPurifyConfig)\n // clearWindow clears memory usage in Node.js, noop in browser:\n clearWindow()\n removeAllHooks()\n return result\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"queryUtils.js","names":[],"sources":["../../../src/utils/functions/queryUtils.ts"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n LockedColumn,\n UniqueFacetIdentifier,\n} from '@/utils/types'\nimport {\n ColumnModel,\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResult,\n JsonSubColumnModel,\n Query,\n QueryBundleRequest,\n QueryResultBundle,\n SelectColumn,\n Table,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual, isEqualWith, isMatch, isNil } from 'lodash-es'\nimport * as SynapseConstants from '../SynapseConstants'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '../SynapseConstants'\nimport {\n hasFilesInView,\n isDataset,\n isDatasetCollection,\n isEntityView,\n isFileView,\n} from './EntityTypeUtils'\n\n/**\n * Retrieve the index of a column using the column name\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getFieldIndex = (name?: string, result?: QueryResultBundle) => {\n if (name == undefined) {\n return -1\n }\n return (\n result?.selectColumns?.findIndex(el => {\n return el.name === name\n }) ?? -1\n )\n}\n\n/**\n * Retrieve the index of a column using the header column name found inthe query results.\n * Ignores case.\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getHeaderIndex = (\n name: string,\n result: QueryResultBundle | undefined,\n) => {\n const nameLowercase = name.toLowerCase()\n return (\n result?.queryResult?.queryResults.headers.findIndex(el => {\n return el.name.toLowerCase() === nameLowercase\n }) ?? -1\n )\n}\n\n/**\n * Returns the indices of the selectColumns with the specified type\n * @param columnType\n * @param selectColumns\n */\nexport function getTypeIndices(\n columnType: ColumnTypeEnum,\n selectColumns: SelectColumn[] = [],\n): number[] {\n return selectColumns.reduce((prev: number[], curr, index) => {\n if (curr.columnType === columnType) {\n return [...prev, index]\n }\n return prev\n }, [])\n}\n\nexport const hasFacetedSelectColumn = (\n facets?: FacetColumnResult[],\n selectColumns?: SelectColumn[],\n): boolean => {\n /**\n * Facets are available iff\n * * there is at least one facet AND\n * * each facet has a corresponding columnModel in the selectColumns AND\n * * each facets has a valid value other than the null/not set value\n */\n if (facets == null || selectColumns == null) {\n return false\n }\n\n if (facets.length === 0 || selectColumns.length === 0) {\n return false\n }\n\n const facetsWithValuesAndColumnModels = facets.filter(facet => {\n return (\n !isSingleNotSetValue(facet) &&\n selectColumns.find(model => model.name === facet.columnName)\n )\n })\n\n return facetsWithValuesAndColumnModels.length > 0\n}\n\nexport const isSingleNotSetValue = (facet: FacetColumnResult): boolean => {\n return (\n facet.facetType === 'enumeration' &&\n facet.facetValues.length == 1 &&\n facet.facetValues[0].value == SynapseConstants.VALUE_NOT_SET\n )\n}\n\n// TODO Instead of removing the facet from the data, the facet renderers should just be aware of what to hide\nexport function removeLockedColumnFromFacetData(\n data?: QueryResultBundle,\n lockedColumn?: LockedColumn,\n): QueryResultBundle | undefined {\n const lockedColumnName = lockedColumn?.columnName\n if (lockedColumnName && data) {\n // for details page, return data without the \"locked\" facet\n const dataCopy: QueryResultBundle = cloneDeep(data)\n const facets = dataCopy.facets?.filter(\n item => item.columnName.toLowerCase() !== lockedColumnName.toLowerCase(),\n )\n dataCopy.facets = facets\n return dataCopy\n } else {\n // for other pages, just return the data\n return data\n }\n}\n\n/**\n * Returns true iff the query has filters applied that can be reset.\n * This includes facet filters and additional filters that are not applied to a locked column.\n */\nexport function hasResettableFilters(\n query: Query,\n lockedColumn?: LockedColumn,\n): boolean {\n const hasFacetFilters =\n Array.isArray(query.selectedFacets) &&\n query.selectedFacets.filter(\n facet =>\n facet.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase(),\n ).length > 0\n const hasAdditionalFilters =\n Array.isArray(query.additionalFilters) &&\n query.additionalFilters.filter(queryFilter =>\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ? queryFilter.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase()\n : true,\n ).length > 0\n\n return hasFacetFilters || hasAdditionalFilters\n}\n\n/**\n * Returns true iff a table query can be added to the download list.\n * @param entity\n * @param entityColumnId\n */\nexport function canTableQueryBeAddedToDownloadList<T extends Table = Table>(\n entity?: T,\n entityColumnId?: string,\n) {\n const viewCannotIncludeFiles =\n // EntityViews without the file bit mask cannot contain files\n (entity && isEntityView(entity) && !hasFilesInView(entity)) ||\n // DatasetCollections cannot contain files\n (entity && isDatasetCollection(entity))\n\n if (viewCannotIncludeFiles) {\n return false\n }\n\n return Boolean(\n entityColumnId ||\n (entity &&\n ((isEntityView(entity) && isFileView(entity)) || isDataset(entity))),\n )\n}\n\n/**\n * Returns true if the queries necessarily return the same result set, i.e. if they have identical parameters other than\n * limit, offset, sort.\n *\n * Note that this does not query the actual tables, so this only captures the semantic equivalence of the queries.\n */\nexport function queryRequestsHaveSameTotalResults(\n request1: Query,\n request2: Query,\n) {\n const clone1 = cloneDeep(request1)\n delete clone1.limit\n delete clone1.offset\n delete clone1.sort\n const clone2 = cloneDeep(request2)\n delete clone2.limit\n delete clone2.offset\n delete clone2.sort\n return isEqualWith(clone1, clone2, (value1, value2) => {\n if (isNil(value1) && isNil(value2)) {\n // Consider all nil (e.g. undefined/null) values equal\n return true\n }\n return isEqual(value1, value2)\n })\n}\n\n/**\n * Remove null/empty values from the query parameters where an undefined value is equivalent.\n *\n * This will ensure a query object is as simple as possible for URL search parameters and also increases the\n * likelihood of a cache hit.\n * @param q\n */\nexport function removeEmptyQueryParams(q: Query) {\n const query = cloneDeep(q)\n\n if (query.limit == null) {\n delete query.limit\n }\n if (query.offset == null || query.offset == 0) {\n delete query.offset\n }\n\n if (query.sort == null || query.sort.length == 0) {\n delete query.sort\n }\n\n if (query.selectedFacets == null || query.selectedFacets.length == 0) {\n delete query.selectedFacets\n }\n\n if (query.additionalFilters == null || query.additionalFilters.length == 0) {\n delete query.additionalFilters\n }\n\n return query\n}\n\n/**\n * Given a FacetColumnResult and a set of ColumnModels, return the ColumnModel that\n * matches the FacetColumnResult.\n * @param facet\n * @param columnModels\n */\nexport function getCorrespondingColumnForFacet(\n facet: FacetColumnResult,\n columnModels: ColumnModel[],\n): ColumnModel | JsonSubColumnModel | undefined {\n let columnModel: ColumnModel | JsonSubColumnModel | undefined =\n columnModels.find(model => model.name === facet.columnName)\n if (facet.jsonPath && columnModel && columnModel.jsonSubColumns) {\n columnModel = columnModel.jsonSubColumns.find(\n cm => cm.jsonPath === facet.jsonPath,\n )\n }\n return columnModel\n}\n\n/**\n * Given a set of FacetColumnRequests, return the FacetColumnRequest that matches the given UniqueFacetIdentifier.\n * @param facetDefinition\n * @param selectedFacets\n */\nexport function getCorrespondingSelectedFacet(\n facetDefinition: UniqueFacetIdentifier,\n selectedFacets?: FacetColumnRequest[],\n): FacetColumnRequest | undefined {\n return selectedFacets?.find(selectedFacet =>\n facetObjectMatchesDefinition(facetDefinition, selectedFacet),\n )\n}\n\nexport function facetObjectMatchesDefinition(\n facetDefinition:\n | UniqueFacetIdentifier\n | FacetColumnRequest\n | FacetColumnResult,\n facetObject: UniqueFacetIdentifier | FacetColumnRequest | FacetColumnResult,\n) {\n return isMatch(\n {\n columnName: facetDefinition.columnName,\n jsonPath: facetDefinition.jsonPath,\n },\n { columnName: facetObject.columnName, jsonPath: facetObject.jsonPath },\n )\n}\n\nexport function partitionQueryBundleRequestIntoRowsAndMetadata(\n queryBundleRequest: QueryBundleRequest,\n): {\n rowDataRequest: QueryBundleRequest\n queryMetadataRequest: QueryBundleRequest\n} {\n const rowDataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n // Get just the rows (if they were originally requested)\n partMask: queryBundleRequest.partMask & BUNDLE_MASK_QUERY_RESULTS,\n }\n\n const queryMetadataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n query: {\n ...queryBundleRequest.query,\n // Remove query fields that don't affect the results.\n offset: undefined,\n limit: undefined,\n sort: undefined,\n },\n // Bitwise remove the query result flag from the mask\n partMask: queryBundleRequest.partMask & ~BUNDLE_MASK_QUERY_RESULTS,\n }\n\n return {\n rowDataRequest,\n queryMetadataRequest,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"queryUtils.js","names":[],"sources":["../../../src/utils/functions/queryUtils.ts"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n LockedColumn,\n UniqueFacetIdentifier,\n} from '@/utils/types'\nimport {\n ColumnModel,\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResult,\n JsonSubColumnModel,\n Query,\n QueryBundleRequest,\n QueryResultBundle,\n SelectColumn,\n Table,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual, isEqualWith, isMatch, isNil } from 'lodash-es'\nimport * as SynapseConstants from '../SynapseConstants'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '../SynapseConstants'\nimport {\n hasFilesInView,\n isDataset,\n isDatasetCollection,\n isEntityView,\n isFileView,\n} from './EntityTypeUtils'\n\n/**\n * Retrieve the index of a column using the column name\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getFieldIndex = (name?: string, result?: QueryResultBundle) => {\n if (name == undefined) {\n return -1\n }\n return (\n result?.selectColumns?.findIndex(el => {\n return el.name === name\n }) ?? -1\n )\n}\n\n/**\n * Retrieve the index of a column using the header column name found inthe query results.\n * Ignores case.\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getHeaderIndex = (\n name: string,\n result: QueryResultBundle | undefined,\n) => {\n const nameLowercase = name.toLowerCase()\n return (\n result?.queryResult?.queryResults.headers.findIndex(el => {\n return el.name.toLowerCase() === nameLowercase\n }) ?? -1\n )\n}\n\n/**\n * Returns the indices of the selectColumns with the specified type\n * @param columnType\n * @param selectColumns\n */\nexport function getTypeIndices(\n columnType: ColumnTypeEnum,\n selectColumns: SelectColumn[] = [],\n): number[] {\n return selectColumns.reduce((prev: number[], curr, index) => {\n if (curr.columnType === columnType) {\n return [...prev, index]\n }\n return prev\n }, [])\n}\n\nexport const hasFacetedSelectColumn = (\n facets?: FacetColumnResult[],\n selectColumns?: SelectColumn[],\n): boolean => {\n /**\n * Facets are available iff\n * * there is at least one facet AND\n * * each facet has a corresponding columnModel in the selectColumns AND\n * * each facets has a valid value other than the null/not set value\n */\n if (facets == null || selectColumns == null) {\n return false\n }\n\n if (facets.length === 0 || selectColumns.length === 0) {\n return false\n }\n\n const facetsWithValuesAndColumnModels = facets.filter(facet => {\n return (\n !isSingleNotSetValue(facet) &&\n selectColumns.find(model => model.name === facet.columnName)\n )\n })\n\n return facetsWithValuesAndColumnModels.length > 0\n}\n\nexport const isSingleNotSetValue = (facet: FacetColumnResult): boolean => {\n return (\n facet.facetType === 'enumeration' &&\n facet.facetValues.length == 1 &&\n facet.facetValues[0].value == SynapseConstants.VALUE_NOT_SET\n )\n}\n\n// TODO Instead of removing the facet from the data, the facet renderers should just be aware of what to hide\nexport function removeLockedColumnFromFacetData(\n data?: QueryResultBundle,\n lockedColumn?: LockedColumn,\n): QueryResultBundle | undefined {\n const lockedColumnName = lockedColumn?.columnName\n if (lockedColumnName && data) {\n // for details page, return data without the \"locked\" facet\n const dataCopy: QueryResultBundle = cloneDeep(data)\n const facets = dataCopy.facets?.filter(\n item => item.columnName.toLowerCase() !== lockedColumnName.toLowerCase(),\n )\n dataCopy.facets = facets\n return dataCopy\n } else {\n // for other pages, just return the data\n return data\n }\n}\n\n/**\n * Returns true iff the query has filters applied that can be reset.\n * This includes facet filters and additional filters that are not applied to a locked column.\n */\nexport function hasResettableFilters(\n query: Query,\n lockedColumn?: LockedColumn,\n): boolean {\n const hasFacetFilters =\n Array.isArray(query.selectedFacets) &&\n query.selectedFacets.filter(\n facet =>\n facet.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase(),\n ).length > 0\n const hasAdditionalFilters =\n Array.isArray(query.additionalFilters) &&\n query.additionalFilters.filter(queryFilter =>\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ? queryFilter.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase()\n : true,\n ).length > 0\n\n return hasFacetFilters || hasAdditionalFilters\n}\n\n/**\n * Returns true iff a table query can be added to the download list.\n * @param entity\n * @param entityColumnId\n */\nexport function canTableQueryBeAddedToDownloadList<T extends Table = Table>(\n entity?: T,\n entityColumnId?: string,\n) {\n const viewCannotIncludeFiles =\n // EntityViews without the file bit mask cannot contain files\n (entity && isEntityView(entity) && !hasFilesInView(entity)) ||\n // DatasetCollections cannot contain files\n (entity && isDatasetCollection(entity))\n\n if (viewCannotIncludeFiles) {\n return false\n }\n\n return Boolean(\n entityColumnId ||\n (entity &&\n ((isEntityView(entity) && isFileView(entity)) || isDataset(entity))),\n )\n}\n\n/**\n * Returns true if the queries necessarily return the same result set, i.e. if they have identical parameters other than\n * limit, offset, sort.\n *\n * Note that this does not query the actual tables, so this only captures the semantic equivalence of the queries.\n */\nexport function queryRequestsHaveSameTotalResults(\n request1: Query,\n request2: Query,\n) {\n const clone1 = cloneDeep(request1)\n delete clone1.limit\n delete clone1.offset\n delete clone1.sort\n const clone2 = cloneDeep(request2)\n delete clone2.limit\n delete clone2.offset\n delete clone2.sort\n return isEqualWith(clone1, clone2, (value1, value2) => {\n if (isNil(value1) && isNil(value2)) {\n // Consider all nil (e.g. undefined/null) values equal\n return true\n }\n return isEqual(value1, value2)\n })\n}\n\n/**\n * Remove null/empty values from the query parameters where an undefined value is equivalent.\n *\n * This will ensure a query object is as simple as possible for URL search parameters and also increases the\n * likelihood of a cache hit.\n * @param q\n */\nexport function removeEmptyQueryParams(q: Query) {\n const query = cloneDeep(q)\n\n if (query.limit == null) {\n delete query.limit\n }\n if (query.offset == null || query.offset == 0) {\n delete query.offset\n }\n\n if (query.sort == null || query.sort.length == 0) {\n delete query.sort\n }\n\n if (query.selectedFacets == null || query.selectedFacets.length == 0) {\n delete query.selectedFacets\n }\n\n if (query.additionalFilters == null || query.additionalFilters.length == 0) {\n delete query.additionalFilters\n }\n\n return query\n}\n\n/**\n * Given a FacetColumnResult and a set of ColumnModels, return the ColumnModel that\n * matches the FacetColumnResult.\n * @param facet\n * @param columnModels\n */\nexport function getCorrespondingColumnForFacet(\n facet: FacetColumnResult,\n columnModels: ColumnModel[],\n): ColumnModel | JsonSubColumnModel | undefined {\n let columnModel: ColumnModel | JsonSubColumnModel | undefined =\n columnModels.find(model => model.name === facet.columnName)\n if (facet.jsonPath && columnModel && columnModel.jsonSubColumns) {\n columnModel = columnModel.jsonSubColumns.find(\n cm => cm.jsonPath === facet.jsonPath,\n )\n }\n return columnModel\n}\n\n/**\n * Given a set of FacetColumnRequests, return the FacetColumnRequest that matches the given UniqueFacetIdentifier.\n * @param facetDefinition\n * @param selectedFacets\n */\nexport function getCorrespondingSelectedFacet(\n facetDefinition: UniqueFacetIdentifier,\n selectedFacets?: FacetColumnRequest[],\n): FacetColumnRequest | undefined {\n return selectedFacets?.find(selectedFacet =>\n facetObjectMatchesDefinition(facetDefinition, selectedFacet),\n )\n}\n\nexport function facetObjectMatchesDefinition(\n facetDefinition:\n | UniqueFacetIdentifier\n | FacetColumnRequest\n | FacetColumnResult,\n facetObject: UniqueFacetIdentifier | FacetColumnRequest | FacetColumnResult,\n) {\n return isMatch(\n {\n columnName: facetDefinition.columnName,\n jsonPath: facetDefinition.jsonPath,\n },\n { columnName: facetObject.columnName, jsonPath: facetObject.jsonPath },\n )\n}\n\nexport function partitionQueryBundleRequestIntoRowsAndMetadata(\n queryBundleRequest: QueryBundleRequest,\n): {\n rowDataRequest: QueryBundleRequest\n queryMetadataRequest: QueryBundleRequest\n} {\n const rowDataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n // Get just the rows (if they were originally requested)\n partMask: queryBundleRequest.partMask & BUNDLE_MASK_QUERY_RESULTS,\n }\n\n const queryMetadataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n query: {\n ...queryBundleRequest.query,\n // Remove query fields that don't affect the results.\n offset: undefined,\n limit: undefined,\n sort: undefined,\n },\n // Bitwise remove the query result flag from the mask\n partMask: queryBundleRequest.partMask & ~BUNDLE_MASK_QUERY_RESULTS,\n }\n\n return {\n rowDataRequest,\n queryMetadataRequest,\n }\n}\n"],"mappings":";;;;;;AAmCA,IAAa,KAAiB,GAAe,MACvC,KAAQ,OACH,KAGP,GAAQ,eAAe,WAAU,MACxB,EAAG,SAAS,EACnB,IAAI,IAWG,KACX,GACA,MACG;CACH,IAAM,IAAgB,EAAK,aAAa;AACxC,QACE,GAAQ,aAAa,aAAa,QAAQ,WAAU,MAC3C,EAAG,KAAK,aAAa,KAAK,EACjC,IAAI;;AASV,SAAgB,EACd,GACA,IAAgC,EAAE,EACxB;AACV,QAAO,EAAc,QAAQ,GAAgB,GAAM,MAC7C,EAAK,eAAe,IACf,CAAC,GAAG,GAAM,EAAM,GAElB,GACN,EAAE,CAAC;;AAGR,IAAa,KACX,GACA,MAQI,KAAU,QAAQ,KAAiB,QAInC,EAAO,WAAW,KAAK,EAAc,WAAW,IAC3C,KAG+B,EAAO,QAAO,MAElD,CAAC,EAAoB,EAAM,IAC3B,EAAc,MAAK,MAAS,EAAM,SAAS,EAAM,WAAW,CAIzD,CAAgC,SAAS,GAGrC,KAAuB,MAEhC,EAAM,cAAc,iBACpB,EAAM,YAAY,UAAU,KAC5B,EAAM,YAAY,GAAG,SAAS;AAKlC,SAAgB,EACd,GACA,GAC+B;CAC/B,IAAM,IAAmB,GAAc;AACvC,KAAI,KAAoB,GAAM;EAE5B,IAAM,IAA8B,EAAU,EAAK;AAKnD,SADA,EAAS,SAHM,EAAS,QAAQ,QAC9B,MAAQ,EAAK,WAAW,aAAa,KAAK,EAAiB,aAAa,CACzE,EAEM;OAGP,QAAO;;AAQX,SAAgB,EACd,GACA,GACS;CACT,IAAM,IACJ,MAAM,QAAQ,EAAM,eAAe,IACnC,EAAM,eAAe,QACnB,MACE,EAAM,WAAW,aAAa,KAC9B,GAAc,YAAY,aAAa,CAC1C,CAAC,SAAS,GACP,IACJ,MAAM,QAAQ,EAAM,kBAAkB,IACtC,EAAM,kBAAkB,QAAO,MAC7B,EAA+B,EAAY,IAC3C,EAAsC,EAAY,GAC9C,EAAY,WAAW,aAAa,KACpC,GAAc,YAAY,aAAa,GACvC,GACL,CAAC,SAAS;AAEb,QAAO,KAAmB;;AAQ5B,SAAgB,EACd,GACA,GACA;AAWA,QARG,KAAU,EAAa,EAAO,IAAI,CAAC,EAAe,EAAO,IAEzD,KAAU,EAAoB,EAAO,GAG/B,KAGF,GACL,KACC,MACG,EAAa,EAAO,IAAI,EAAW,EAAO,IAAK,EAAU,EAAO;;AAUxE,SAAgB,EACd,GACA,GACA;CACA,IAAM,IAAS,EAAU,EAAS;AAGlC,CAFA,OAAO,EAAO,OACd,OAAO,EAAO,QACd,OAAO,EAAO;CACd,IAAM,IAAS,EAAU,EAAS;AAIlC,QAHA,OAAO,EAAO,OACd,OAAO,EAAO,QACd,OAAO,EAAO,MACP,EAAY,GAAQ,IAAS,GAAQ,MACtC,EAAM,EAAO,IAAI,EAAM,EAAO,GAEzB,KAEF,EAAQ,GAAQ,EAAO,CAC9B;;AAUJ,SAAgB,EAAuB,GAAU;CAC/C,IAAM,IAAQ,EAAU,EAAE;AAqB1B,QAnBI,EAAM,SACR,OAAO,EAAM,QAEX,EAAM,UAAU,QAAQ,EAAM,UAAU,MAC1C,OAAO,EAAM,SAGX,EAAM,QAAQ,QAAQ,EAAM,KAAK,UAAU,MAC7C,OAAO,EAAM,OAGX,EAAM,kBAAkB,QAAQ,EAAM,eAAe,UAAU,MACjE,OAAO,EAAM,iBAGX,EAAM,qBAAqB,QAAQ,EAAM,kBAAkB,UAAU,MACvE,OAAO,EAAM,mBAGR;;AAST,SAAgB,EACd,GACA,GAC8C;CAC9C,IAAI,IACF,EAAa,MAAK,MAAS,EAAM,SAAS,EAAM,WAAW;AAM7D,QALI,EAAM,YAAY,KAAe,EAAY,mBAC/C,IAAc,EAAY,eAAe,MACvC,MAAM,EAAG,aAAa,EAAM,SAC7B,GAEI;;AAQT,SAAgB,EACd,GACA,GACgC;AAChC,QAAO,GAAgB,MAAK,MAC1B,EAA6B,GAAiB,EAAc,CAC7D;;AAGH,SAAgB,EACd,GAIA,GACA;AACA,QAAO,EACL;EACE,YAAY,EAAgB;EAC5B,UAAU,EAAgB;EAC3B,EACD;EAAE,YAAY,EAAY;EAAY,UAAU,EAAY;EAAU,CACvE;;AAGH,SAAgB,EACd,GAIA;AAoBA,QAAO;EACL,gBAAA;GAnBA,GAAG;GAEH,UAAU,EAAmB,WAAA;GAiB7B;EACA,sBAAA;GAdA,GAAG;GACH,OAAO;IACL,GAAG,EAAmB;IAEtB,QAAQ,KAAA;IACR,OAAO,KAAA;IACP,MAAM,KAAA;IACP;GAED,UAAU,EAAmB,WAAW;GAKxC;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGetInfoFromIds.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EACZ,UAAU,EAGV,eAAe,EAChB,MAAM,iCAAiC,CAAA;AAOxC,MAAM,MAAM,sBAAsB,CAChC,CAAC,SAAS,YAAY,GAAG,eAAe,GAAG,UAAU,EACrD,SAAS,GAAG,CAAC,SAAS,YAAY,GAC9B,eAAe,GACf,CAAC,SAAS,eAAe,GACzB,cAAc,GACd,CAAC,SAAS,UAAU,GACpB,kBAAkB,GAClB,KAAK,IACP;IACF,GAAG,EAAE,MAAM,EAAE,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AA4FD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,CAAC,SAAS,YAAY,GAAG,eAAe,GAAG,UAAU,EACrD,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAgHvC"}
1
+ {"version":3,"file":"useGetInfoFromIds.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EACZ,UAAU,EAGV,eAAe,EAChB,MAAM,iCAAiC,CAAA;AAOxC,MAAM,MAAM,sBAAsB,CAChC,CAAC,SAAS,YAAY,GAAG,eAAe,GAAG,UAAU,EACrD,SAAS,GAAG,CAAC,SAAS,YAAY,GAC9B,eAAe,GACf,CAAC,SAAS,eAAe,GACvB,cAAc,GACd,CAAC,SAAS,UAAU,GAClB,kBAAkB,GAClB,KAAK,IACX;IACF,GAAG,EAAE,MAAM,EAAE,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AA4FD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,CAAC,SAAS,YAAY,GAAG,eAAe,GAAG,UAAU,EACrD,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAgHvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGetInfoFromIds.js","names":[],"sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"sourcesContent":["import {\n getEntityHeaders,\n getEvaluations,\n getGroupHeadersBatch,\n} from '@/synapse-client'\nimport {\n EntityHeader,\n Evaluation,\n Reference,\n ReferenceList,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { chunk, uniq, without } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport { useSynapseContext } from '../context/SynapseContext'\nimport { SynapseConstants } from '../index'\n\nexport type UseGetInfoFromIdsProps<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n THookType = T extends EntityHeader\n ? 'ENTITY_HEADER'\n : T extends UserGroupHeader\n ? 'USER_PROFILE'\n : T extends Evaluation\n ? 'EVALUATION_QUEUE'\n : never,\n> = {\n ids: string[]\n type: THookType\n}\n\ntype LookupRequestType = string | Reference\n\nconst UserGroupHeaderTemplate: UserGroupHeader = {\n ownerId: '', // A foreign key to the ID of the 'principal' object for the user\n firstName: 'Unknown', // This person's given name (forename)\n lastName: 'Unknown', // This person's family name (surname)\n userName: 'Unknown', // A name chosen by the user that uniquely identifies them\n isIndividual: false,\n}\n\nconst entityHeaderTemplate: EntityHeader = {\n name: 'Unknown', //\tThe name of the entity\n id: 'unknown', //\tThe id of the entity\n type: 'org.sagebionetworks.repo.model.FileEntity', //\tThe type of the entity\n versionNumber: 0, //\tThe version number of the entity\n versionLabel: 'placeholder', //\tThe user defined version label of the entity\n benefactorId: 0, //\tThe ID of the entity that this Entity's ACL is inherited from.\n createdOn: 'null', //\tThe date this entity was created.\n modifiedOn: 'null', //\tThe date this entity was last modified.\n createdBy: 'null', //\tThe ID of the user that created this entity.\n modifiedBy: 'null', //\tThe ID of the user that last modified this entity.\n isLatestVersion: true, // If this version is the latest version of the entity\n}\n\nconst evaluationTemplate: Evaluation = {\n id: 'unknown', // The unique immutable ID for this Evaluation.\n etag: 'Unknown', // Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. The eTag changes every time an Evaluation is updated; it is used to detect when a client's copy of an Evaluation is out-of-date.\n name: 'Unknown', // The name of this Evaluation\n description: 'Unknown', // A text description of this Evaluation.\n ownerId: 'null', // The ID of the Synapse user who created this Evaluation.\n createdOn: 'null', //\tThe date on which Evaluation was created.\n contentSource: 'Unknown', // The Synapse ID of the Entity to which this Evaluation belongs, e.g. a reference to a Synapse project.\n submissionInstructionsMessage: 'Unknown', // Message to display to users detailing acceptable formatting for Submissions to this Evaluation.\n submissionReceiptMessage: 'Unknown', // Message to display to users upon successful submission to this Evaluation.\n}\n\nconst getEntityHeaderItems = async (\n lookupList: ReferenceList,\n accessToken: string | undefined,\n): Promise<EntityHeader[]> => {\n const newData = await getEntityHeaders(lookupList, accessToken)\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item.targetId) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...entityHeaderTemplate,\n id: item.targetId,\n name: `${item.targetId}`,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\nconst getUserGroupHeaderItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<UserGroupHeader[]> => {\n const newData = (await getGroupHeadersBatch(lookupList, accessToken)).children\n const notFound = lookupList.filter(\n item => newData.map(item => item.ownerId).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...UserGroupHeaderTemplate,\n ownerId: item,\n name: `Unknown User (${item})`,\n }))\n\n return [...newData, ...notFoundPlaceholders]\n}\n\nconst getEvaluationItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<Evaluation[]> => {\n const newData = await getEvaluations(\n { evaluationIds: lookupList },\n accessToken,\n )\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...evaluationTemplate,\n id: item,\n name: item,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\n/**\n * React hook to get user profiles or entities or evaluation queues. Utilizes a custom cache in sessionStorage.\n *\n * For fetching just one entity or usergroup, see useGetEntity/useGetUserGroupHeader hooks powered by react-query\n * @returns\n */\nexport default function useGetInfoFromIds<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n>(props: UseGetInfoFromIdsProps<T>): T[] {\n const { ids, type } = props\n const { accessToken } = useSynapseContext()\n\n const [data, setData] = useState<Array<T>>([])\n\n const idKey = (type === 'USER_PROFILE' ? 'ownerId' : 'id') as keyof T\n\n const getStorageKey = (type: UseGetInfoFromIdsProps<T>['type']) => {\n switch (type) {\n case 'USER_PROFILE':\n return SynapseConstants.USER_PROFILE_STORAGE_KEY\n case 'ENTITY_HEADER':\n return SynapseConstants.ENTITY_HEADER_STORAGE_KEY\n case 'EVALUATION_QUEUE':\n return SynapseConstants.EVALUATIONS_STORAGE_KEY\n default:\n return ''\n }\n }\n const storageKey: string = getStorageKey(type)\n\n // look at current list of data, see if incoming ids has new data,\n // if so grab those ids\n const curList = data.map(el => el[idKey]) as string[]\n const incomingList = ids.filter(el => el !== SynapseConstants.VALUE_NOT_SET)\n const newValues = uniq(without(incomingList, ...curList))\n\n useEffect(() => {\n const saveToSessionStorage = (data: T[]) => {\n if (!data.length) {\n return\n }\n //get what's there\n const dataInStorage = sessionStorage.getItem(storageKey)\n try {\n const dataInStorageAsObjectArr: T[] = dataInStorage\n ? JSON.parse(dataInStorage)\n : []\n //get an array of ids for items already in storage\n const ids = dataInStorageAsObjectArr.map(item => item[idKey])\n //push all the new data if ids are new\n for (const dataObject of data) {\n if (!ids.includes(dataObject[idKey])) {\n dataInStorageAsObjectArr.push(dataObject)\n }\n }\n sessionStorage.setItem(\n storageKey,\n JSON.stringify(dataInStorageAsObjectArr),\n )\n } catch (e) {\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n }\n }\n saveToSessionStorage(data)\n }, [data, idKey, storageKey])\n\n // Alina TODO: check if the items are already in Local Storage before making server call.\n\n // Michael TODO: There's a bug where the data held in useGetInfoFromIds will be stale if the user token changes,\n // this can be solved by using the useCompare hook on the token to track when it changes\n useDeepCompareEffect(() => {\n let isCancelled = false\n const getData = async () => {\n if (newValues.length > 0) {\n try {\n const newIds = Array.from<string>(newValues)\n const newReferences: LookupRequestType[] =\n type === 'ENTITY_HEADER'\n ? newIds.map(el => ({ targetId: el }))\n : newIds\n const newReferencesChunks = chunk(newReferences, 45)\n const totalData: T[] = []\n for (const newReferences of newReferencesChunks) {\n let newData: T[] = []\n switch (type) {\n case 'USER_PROFILE':\n newData = (await getUserGroupHeaderItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n case 'ENTITY_HEADER':\n newData = (await getEntityHeaderItems(\n newReferences as ReferenceList,\n accessToken,\n )) as T[]\n break\n case 'EVALUATION_QUEUE':\n newData = (await getEvaluationItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n }\n totalData.push(...newData)\n }\n if (!isCancelled) {\n setData(oldData => oldData.concat(...totalData))\n }\n } catch (error) {\n console.error('Error on data retrieval', error)\n }\n }\n }\n getData()\n return () => {\n isCancelled = true\n }\n }, [accessToken, type, newValues])\n return data\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useGetInfoFromIds.js","names":[],"sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"sourcesContent":["import {\n getEntityHeaders,\n getEvaluations,\n getGroupHeadersBatch,\n} from '@/synapse-client'\nimport {\n EntityHeader,\n Evaluation,\n Reference,\n ReferenceList,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { chunk, uniq, without } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport { useSynapseContext } from '../context/SynapseContext'\nimport { SynapseConstants } from '../index'\n\nexport type UseGetInfoFromIdsProps<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n THookType = T extends EntityHeader\n ? 'ENTITY_HEADER'\n : T extends UserGroupHeader\n ? 'USER_PROFILE'\n : T extends Evaluation\n ? 'EVALUATION_QUEUE'\n : never,\n> = {\n ids: string[]\n type: THookType\n}\n\ntype LookupRequestType = string | Reference\n\nconst UserGroupHeaderTemplate: UserGroupHeader = {\n ownerId: '', // A foreign key to the ID of the 'principal' object for the user\n firstName: 'Unknown', // This person's given name (forename)\n lastName: 'Unknown', // This person's family name (surname)\n userName: 'Unknown', // A name chosen by the user that uniquely identifies them\n isIndividual: false,\n}\n\nconst entityHeaderTemplate: EntityHeader = {\n name: 'Unknown', //\tThe name of the entity\n id: 'unknown', //\tThe id of the entity\n type: 'org.sagebionetworks.repo.model.FileEntity', //\tThe type of the entity\n versionNumber: 0, //\tThe version number of the entity\n versionLabel: 'placeholder', //\tThe user defined version label of the entity\n benefactorId: 0, //\tThe ID of the entity that this Entity's ACL is inherited from.\n createdOn: 'null', //\tThe date this entity was created.\n modifiedOn: 'null', //\tThe date this entity was last modified.\n createdBy: 'null', //\tThe ID of the user that created this entity.\n modifiedBy: 'null', //\tThe ID of the user that last modified this entity.\n isLatestVersion: true, // If this version is the latest version of the entity\n}\n\nconst evaluationTemplate: Evaluation = {\n id: 'unknown', // The unique immutable ID for this Evaluation.\n etag: 'Unknown', // Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. The eTag changes every time an Evaluation is updated; it is used to detect when a client's copy of an Evaluation is out-of-date.\n name: 'Unknown', // The name of this Evaluation\n description: 'Unknown', // A text description of this Evaluation.\n ownerId: 'null', // The ID of the Synapse user who created this Evaluation.\n createdOn: 'null', //\tThe date on which Evaluation was created.\n contentSource: 'Unknown', // The Synapse ID of the Entity to which this Evaluation belongs, e.g. a reference to a Synapse project.\n submissionInstructionsMessage: 'Unknown', // Message to display to users detailing acceptable formatting for Submissions to this Evaluation.\n submissionReceiptMessage: 'Unknown', // Message to display to users upon successful submission to this Evaluation.\n}\n\nconst getEntityHeaderItems = async (\n lookupList: ReferenceList,\n accessToken: string | undefined,\n): Promise<EntityHeader[]> => {\n const newData = await getEntityHeaders(lookupList, accessToken)\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item.targetId) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...entityHeaderTemplate,\n id: item.targetId,\n name: `${item.targetId}`,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\nconst getUserGroupHeaderItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<UserGroupHeader[]> => {\n const newData = (await getGroupHeadersBatch(lookupList, accessToken)).children\n const notFound = lookupList.filter(\n item => newData.map(item => item.ownerId).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...UserGroupHeaderTemplate,\n ownerId: item,\n name: `Unknown User (${item})`,\n }))\n\n return [...newData, ...notFoundPlaceholders]\n}\n\nconst getEvaluationItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<Evaluation[]> => {\n const newData = await getEvaluations(\n { evaluationIds: lookupList },\n accessToken,\n )\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...evaluationTemplate,\n id: item,\n name: item,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\n/**\n * React hook to get user profiles or entities or evaluation queues. Utilizes a custom cache in sessionStorage.\n *\n * For fetching just one entity or usergroup, see useGetEntity/useGetUserGroupHeader hooks powered by react-query\n * @returns\n */\nexport default function useGetInfoFromIds<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n>(props: UseGetInfoFromIdsProps<T>): T[] {\n const { ids, type } = props\n const { accessToken } = useSynapseContext()\n\n const [data, setData] = useState<Array<T>>([])\n\n const idKey = (type === 'USER_PROFILE' ? 'ownerId' : 'id') as keyof T\n\n const getStorageKey = (type: UseGetInfoFromIdsProps<T>['type']) => {\n switch (type) {\n case 'USER_PROFILE':\n return SynapseConstants.USER_PROFILE_STORAGE_KEY\n case 'ENTITY_HEADER':\n return SynapseConstants.ENTITY_HEADER_STORAGE_KEY\n case 'EVALUATION_QUEUE':\n return SynapseConstants.EVALUATIONS_STORAGE_KEY\n default:\n return ''\n }\n }\n const storageKey: string = getStorageKey(type)\n\n // look at current list of data, see if incoming ids has new data,\n // if so grab those ids\n const curList = data.map(el => el[idKey]) as string[]\n const incomingList = ids.filter(el => el !== SynapseConstants.VALUE_NOT_SET)\n const newValues = uniq(without(incomingList, ...curList))\n\n useEffect(() => {\n const saveToSessionStorage = (data: T[]) => {\n if (!data.length) {\n return\n }\n //get what's there\n const dataInStorage = sessionStorage.getItem(storageKey)\n try {\n const dataInStorageAsObjectArr: T[] = dataInStorage\n ? JSON.parse(dataInStorage)\n : []\n //get an array of ids for items already in storage\n const ids = dataInStorageAsObjectArr.map(item => item[idKey])\n //push all the new data if ids are new\n for (const dataObject of data) {\n if (!ids.includes(dataObject[idKey])) {\n dataInStorageAsObjectArr.push(dataObject)\n }\n }\n sessionStorage.setItem(\n storageKey,\n JSON.stringify(dataInStorageAsObjectArr),\n )\n } catch (e) {\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n }\n }\n saveToSessionStorage(data)\n }, [data, idKey, storageKey])\n\n // Alina TODO: check if the items are already in Local Storage before making server call.\n\n // Michael TODO: There's a bug where the data held in useGetInfoFromIds will be stale if the user token changes,\n // this can be solved by using the useCompare hook on the token to track when it changes\n useDeepCompareEffect(() => {\n let isCancelled = false\n const getData = async () => {\n if (newValues.length > 0) {\n try {\n const newIds = Array.from<string>(newValues)\n const newReferences: LookupRequestType[] =\n type === 'ENTITY_HEADER'\n ? newIds.map(el => ({ targetId: el }))\n : newIds\n const newReferencesChunks = chunk(newReferences, 45)\n const totalData: T[] = []\n for (const newReferences of newReferencesChunks) {\n let newData: T[] = []\n switch (type) {\n case 'USER_PROFILE':\n newData = (await getUserGroupHeaderItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n case 'ENTITY_HEADER':\n newData = (await getEntityHeaderItems(\n newReferences as ReferenceList,\n accessToken,\n )) as T[]\n break\n case 'EVALUATION_QUEUE':\n newData = (await getEvaluationItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n }\n totalData.push(...newData)\n }\n if (!isCancelled) {\n setData(oldData => oldData.concat(...totalData))\n }\n } catch (error) {\n console.error('Error on data retrieval', error)\n }\n }\n }\n getData()\n return () => {\n isCancelled = true\n }\n }, [accessToken, type, newValues])\n return data\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SlotUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/slots/SlotUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,SAAS,MAAM,EAAE,WAAW,GAAG,OAAO,EACvE,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,WAAW,KAAK,MAAM,CAAC,EACzD,UAAU,EAAE,WAAW,GACtB,MAAM,CAER;AAED,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAC9C,KAAK,iBAAiB,CAAC,KAAK,SAAS,GAAG,EAAE,IAAI,CAC5C,GAAG,IAAI,EAAE,KAAK,KACX,eAAe,CAAA;AAGpB,KAAK,SAAS,CAAC,KAAK,SAAS,GAAG,EAAE,IAAI,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;AAGhF,KAAK,WAAW,CACd,KAAK,SAAS,GAAG,EAAE,EACnB,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,IACzB,EAAE,SAAS,iBAAiB,CAAC,KAAK,CAAC,GACnC,iBAAiB,CAAC,KAAK,CAAC,GACxB,EAAE,SAAS,iBAAiB,CAAC,KAAK,CAAC,GACnC,iBAAiB,CAAC,KAAK,CAAC,GACxB,eAAe,CAAA;AAEnB;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,SAAS,GAAG,EAAE,EACnB,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAa5D"}
1
+ {"version":3,"file":"SlotUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/slots/SlotUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,SAAS,MAAM,EAAE,WAAW,GAAG,OAAO,EACvE,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,WAAW,KAAK,MAAM,CAAC,EACzD,UAAU,EAAE,WAAW,GACtB,MAAM,CAER;AAED,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAC9C,KAAK,iBAAiB,CAAC,KAAK,SAAS,GAAG,EAAE,IAAI,CAC5C,GAAG,IAAI,EAAE,KAAK,KACX,eAAe,CAAA;AAGpB,KAAK,SAAS,CAAC,KAAK,SAAS,GAAG,EAAE,IAAI,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;AAGhF,KAAK,WAAW,CACd,KAAK,SAAS,GAAG,EAAE,EACnB,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,IAE3B,EAAE,SAAS,iBAAiB,CAAC,KAAK,CAAC,GAC/B,iBAAiB,CAAC,KAAK,CAAC,GACxB,EAAE,SAAS,iBAAiB,CAAC,KAAK,CAAC,GACjC,iBAAiB,CAAC,KAAK,CAAC,GACxB,eAAe,CAAA;AAEvB;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,SAAS,GAAG,EAAE,EACnB,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAC3B,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAa5D"}
@@ -1 +1 @@
1
- {"version":3,"file":"SlotUtils.js","names":[],"sources":["../../../src/utils/slots/SlotUtils.ts"],"sourcesContent":["import { merge } from 'lodash-es'\n\n/**\n * Gets or calculates the slot props for a component.\n *\n * Our component 'slots' are heavily inspired by MUI's slots and generally matches their design patterns.\n * See https://mui.com/x/common-concepts/custom-components/\n * @param propsOrFn\n * @param ownerState\n */\nexport function getSlotProps<TProps extends object, TOwnerState = unknown>(\n propsOrFn: TProps | ((ownerState: TOwnerState) => TProps),\n ownerState: TOwnerState,\n): TProps {\n return typeof propsOrFn === 'function' ? propsOrFn(ownerState) : propsOrFn\n}\n\ntype SlotPropsObject = Record<string, unknown>\ntype SlotPropsFunction<TArgs extends any[]> = (\n ...args: TArgs\n) => SlotPropsObject\n\n// A type that can be either an object or a function returning an object\ntype SlotProps<TArgs extends any[]> = SlotPropsObject | SlotPropsFunction<TArgs>\n\n// Helper type to determine return type based on input types\ntype MergeReturn<\n TArgs extends any[],\n T1 extends SlotProps<TArgs>,\n T2 extends SlotProps<TArgs>,\n> = T1 extends SlotPropsFunction<TArgs>\n ? SlotPropsFunction<TArgs>\n : T2 extends SlotPropsFunction<TArgs>\n ? SlotPropsFunction<TArgs>\n : SlotPropsObject\n\n/**\n * Merges two slot props objects or functions so that a complex hierarchy of components can safely expose slots of\n * internal components as their own.\n * @param slotProps1\n * @param slotProps2\n */\nexport function mergeSlotProps<\n TArgs extends any[],\n T1 extends SlotProps<TArgs>,\n T2 extends SlotProps<TArgs>,\n>(slotProps1: T1, slotProps2: T2): MergeReturn<TArgs, T1, T2> {\n if (typeof slotProps1 === 'function' || typeof slotProps2 === 'function') {\n const slotProps1Fn: SlotPropsFunction<TArgs> =\n typeof slotProps1 === 'function' ? slotProps1 : () => slotProps1\n const slotProps2Fn: SlotPropsFunction<TArgs> =\n typeof slotProps2 === 'function' ? slotProps2 : () => slotProps2\n\n return function (...args: TArgs) {\n return merge(slotProps1Fn(...args), slotProps2Fn(...args))\n } as MergeReturn<TArgs, T1, T2>\n } else {\n return merge(slotProps1, slotProps2) as MergeReturn<TArgs, T1, T2>\n }\n}\n"],"mappings":";;AAUA,SAAgB,EACd,GACA,GACQ;AACR,QAAO,OAAO,KAAc,aAAa,EAAU,EAAW,GAAG;;AA4BnE,SAAgB,EAId,GAAgB,GAA4C;AAC5D,KAAI,OAAO,KAAe,cAAc,OAAO,KAAe,YAAY;EACxE,IAAM,IACJ,OAAO,KAAe,aAAa,UAAmB,GAClD,IACJ,OAAO,KAAe,aAAa,UAAmB;AAExD,SAAO,SAAU,GAAG,GAAa;AAC/B,UAAO,EAAM,EAAa,GAAG,EAAK,EAAE,EAAa,GAAG,EAAK,CAAC;;OAG5D,QAAO,EAAM,GAAY,EAAW"}
1
+ {"version":3,"file":"SlotUtils.js","names":[],"sources":["../../../src/utils/slots/SlotUtils.ts"],"sourcesContent":["import { merge } from 'lodash-es'\n\n/**\n * Gets or calculates the slot props for a component.\n *\n * Our component 'slots' are heavily inspired by MUI's slots and generally matches their design patterns.\n * See https://mui.com/x/common-concepts/custom-components/\n * @param propsOrFn\n * @param ownerState\n */\nexport function getSlotProps<TProps extends object, TOwnerState = unknown>(\n propsOrFn: TProps | ((ownerState: TOwnerState) => TProps),\n ownerState: TOwnerState,\n): TProps {\n return typeof propsOrFn === 'function' ? propsOrFn(ownerState) : propsOrFn\n}\n\ntype SlotPropsObject = Record<string, unknown>\ntype SlotPropsFunction<TArgs extends any[]> = (\n ...args: TArgs\n) => SlotPropsObject\n\n// A type that can be either an object or a function returning an object\ntype SlotProps<TArgs extends any[]> = SlotPropsObject | SlotPropsFunction<TArgs>\n\n// Helper type to determine return type based on input types\ntype MergeReturn<\n TArgs extends any[],\n T1 extends SlotProps<TArgs>,\n T2 extends SlotProps<TArgs>,\n> =\n T1 extends SlotPropsFunction<TArgs>\n ? SlotPropsFunction<TArgs>\n : T2 extends SlotPropsFunction<TArgs>\n ? SlotPropsFunction<TArgs>\n : SlotPropsObject\n\n/**\n * Merges two slot props objects or functions so that a complex hierarchy of components can safely expose slots of\n * internal components as their own.\n * @param slotProps1\n * @param slotProps2\n */\nexport function mergeSlotProps<\n TArgs extends any[],\n T1 extends SlotProps<TArgs>,\n T2 extends SlotProps<TArgs>,\n>(slotProps1: T1, slotProps2: T2): MergeReturn<TArgs, T1, T2> {\n if (typeof slotProps1 === 'function' || typeof slotProps2 === 'function') {\n const slotProps1Fn: SlotPropsFunction<TArgs> =\n typeof slotProps1 === 'function' ? slotProps1 : () => slotProps1\n const slotProps2Fn: SlotPropsFunction<TArgs> =\n typeof slotProps2 === 'function' ? slotProps2 : () => slotProps2\n\n return function (...args: TArgs) {\n return merge(slotProps1Fn(...args), slotProps2Fn(...args))\n } as MergeReturn<TArgs, T1, T2>\n } else {\n return merge(slotProps1, slotProps2) as MergeReturn<TArgs, T1, T2>\n }\n}\n"],"mappings":";;AAUA,SAAgB,EACd,GACA,GACQ;AACR,QAAO,OAAO,KAAc,aAAa,EAAU,EAAW,GAAG;;AA6BnE,SAAgB,EAId,GAAgB,GAA4C;AAC5D,KAAI,OAAO,KAAe,cAAc,OAAO,KAAe,YAAY;EACxE,IAAM,IACJ,OAAO,KAAe,aAAa,UAAmB,GAClD,IACJ,OAAO,KAAe,aAAa,UAAmB;AAExD,SAAO,SAAU,GAAG,GAAa;AAC/B,UAAO,EAAM,EAAa,GAAG,EAAK,EAAE,EAAa,GAAG,EAAK,CAAC;;OAG5D,QAAO,EAAM,GAAY,EAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synapse-react-client",
3
- "version": "4.0.17",
3
+ "version": "4.0.18",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -117,11 +117,11 @@
117
117
  "use-deep-compare-effect": "^1.8.1",
118
118
  "xss": "^1.0.15",
119
119
  "zod": "^3.24.4",
120
- "@sage-bionetworks/synapse-client": "0.0.17",
121
- "@sage-bionetworks/synapse-types": "0.0.6",
120
+ "@sage-bionetworks/synapse-client": "0.0.18",
122
121
  "@sage-bionetworks/markdown-it-container": "4.0.1",
123
- "markdown-it-synapse": "2.0.0",
124
- "markdown-it-synapse-table": "1.0.8"
122
+ "@sage-bionetworks/synapse-types": "0.0.7",
123
+ "markdown-it-synapse-table": "1.0.8",
124
+ "markdown-it-synapse": "2.0.0"
125
125
  },
126
126
  "peerDependencies": {
127
127
  "@sage-bionetworks/aridhia-client": "*",
@@ -197,7 +197,6 @@
197
197
  "p-limit": "^6.2.0",
198
198
  "path-browserify": "^1.0.1",
199
199
  "postcss-normalize": "^10.0.1",
200
- "prettier": "^2.8.8",
201
200
  "process": "^0.11.10",
202
201
  "react": "^19.2.5",
203
202
  "react-dom": "^19.2.5",
@@ -211,7 +210,7 @@
211
210
  "typescript": "6.0.3",
212
211
  "util": "^0.12.5",
213
212
  "vite": "^8.0.8",
214
- "vite-plugin-node-polyfills": "^0.26.0",
213
+ "vite-plugin-node-polyfills": "^0.28.0",
215
214
  "vite-plugin-static-copy": "^4.0.1",
216
215
  "vitest": "^4.1.4",
217
216
  "vitest-when": "^0.10.0",
@@ -257,7 +256,7 @@
257
256
  "build": "vite build && tsc -p tsconfig.build.json",
258
257
  "storybook:build": "pnpm storybook build",
259
258
  "type-check": "tsc --build",
260
- "lint": "eslint src",
259
+ "lint": "oxlint src",
261
260
  "watch:js": "tsc --watch"
262
261
  }
263
262
  }