synapse-react-client 4.0.9 → 4.0.11

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 (613) hide show
  1. package/dist/SWC.index.d.ts +1 -0
  2. package/dist/SWC.index.d.ts.map +1 -1
  3. package/dist/SWC.index.js +2 -1
  4. package/dist/SWC.index.js.map +1 -1
  5. package/dist/aridhia-queries/aridhiaTokenExchange.js.map +1 -1
  6. package/dist/aridhia-queries/useGetAridhiaRequests.js.map +1 -1
  7. package/dist/assets/icons/CloudWarning.d.ts +5 -0
  8. package/dist/assets/icons/CloudWarning.d.ts.map +1 -0
  9. package/dist/assets/icons/CloudWarning.js +47 -0
  10. package/dist/assets/icons/CloudWarning.js.map +1 -0
  11. package/dist/assets/icons/TasksIcon.d.ts.map +1 -1
  12. package/dist/assets/icons/TasksIcon.js +6 -10
  13. package/dist/assets/icons/TasksIcon.js.map +1 -1
  14. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.d.ts.map +1 -1
  15. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js +69 -63
  16. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js.map +1 -1
  17. package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js.map +1 -1
  18. package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
  19. package/dist/components/AccessRequirementList/AccessRequirementListUtils.js.map +1 -1
  20. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsEditor.js.map +1 -1
  21. package/dist/components/AccessRequirementList/RequirementItem/SelfSignAccessRequirementItem.js.map +1 -1
  22. package/dist/components/AccessRequirementRelatedProjectsList/AccessRequirementRelatedProjectsList.js.map +1 -1
  23. package/dist/components/AccessTokenPage/AccessTokenCard/AccessTokenCard.js.map +1 -1
  24. package/dist/components/AcknowledgementsPage/StudyAcknowledgements.js.map +1 -1
  25. package/dist/components/AclEditor/PermissionLevelMenu.js.map +1 -1
  26. package/dist/components/AclEditor/ResourceAccessAndUserGroupHeader.js.map +1 -1
  27. package/dist/components/AclEditor/useSortResourceAccessList.js.map +1 -1
  28. package/dist/components/AclEditor/useUpdateAcl.js.map +1 -1
  29. package/dist/components/Aridhia/AridhiaAccessStatus.js.map +1 -1
  30. package/dist/components/Authentication/AuthenticationMethodSelection.d.ts.map +1 -1
  31. package/dist/components/Authentication/AuthenticationMethodSelection.js +38 -37
  32. package/dist/components/Authentication/AuthenticationMethodSelection.js.map +1 -1
  33. package/dist/components/Authentication/Constants.d.ts +1 -0
  34. package/dist/components/Authentication/Constants.d.ts.map +1 -1
  35. package/dist/components/Authentication/Constants.js +2 -2
  36. package/dist/components/Authentication/Constants.js.map +1 -1
  37. package/dist/components/Authentication/LastLoginInfo.js.map +1 -1
  38. package/dist/components/Authentication/RecoveryCodeForm.js.map +1 -1
  39. package/dist/components/Authentication/RecoveryCodeGrid.js.map +1 -1
  40. package/dist/components/Authentication/RegenerateBackupCodesWarning.js.map +1 -1
  41. package/dist/components/Authentication/Reset2FAWarning.js.map +1 -1
  42. package/dist/components/Authentication/StandaloneLoginForm.js +1 -1
  43. package/dist/components/Authentication/TwoFactorBackupCodes.js.map +1 -1
  44. package/dist/components/Authentication/TwoFactorEnrollmentForm.d.ts.map +1 -1
  45. package/dist/components/Authentication/TwoFactorEnrollmentForm.js +2 -1
  46. package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
  47. package/dist/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.js.map +1 -1
  48. package/dist/components/CardContainer/CardContainer.js.map +1 -1
  49. package/dist/components/CardDeck/CardDeck.Mobile.js.map +1 -1
  50. package/dist/components/CardDeck/TableQueryCardDeck.js.map +1 -1
  51. package/dist/components/CertificationQuiz/CertificationQuiz.js.map +1 -1
  52. package/dist/components/ChallengeDataDownload/ChallengeDataDownload.js.map +1 -1
  53. package/dist/components/ChallengeSubmission/ChallengeSubmission.js.map +1 -1
  54. package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js.map +1 -1
  55. package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js.map +1 -1
  56. package/dist/components/ChallengeSubmission/EvaluationQueueList.js.map +1 -1
  57. package/dist/components/ChallengeSubmission/SubmissionDirectoryList.d.ts.map +1 -1
  58. package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js +143 -140
  59. package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
  60. package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js.map +1 -1
  61. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
  62. package/dist/components/ChangePassword/ChangePassword.js.map +1 -1
  63. package/dist/components/ChangePassword/ChangePasswordWithToken.js.map +1 -1
  64. package/dist/components/ChangePassword/useChangePasswordFormState.js +1 -1
  65. package/dist/components/ChangePassword/useChangePasswordFormState.js.map +1 -1
  66. package/dist/components/CitationPopover/CitationPopoverContent.js.map +1 -1
  67. package/dist/components/ColumnFilter/ColumnFilter.js.map +1 -1
  68. package/dist/components/ComponentCollapse.js.map +1 -1
  69. package/dist/components/CookiesNotification/CookiesNotification.js.map +1 -1
  70. package/dist/components/CreateProjectModal/CreateProjectModal.js.map +1 -1
  71. package/dist/components/CreateTableViewWizard/CreateTableViewWizardUtils.js.map +1 -1
  72. package/dist/components/DataGrid/DataGrid.d.ts +0 -1
  73. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
  74. package/dist/components/DataGrid/DataGrid.js +72 -72
  75. package/dist/components/DataGrid/DataGrid.js.map +1 -1
  76. package/dist/components/DataGrid/DataGridWebSocket.d.ts +4 -0
  77. package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
  78. package/dist/components/DataGrid/DataGridWebSocket.js +9 -8
  79. package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
  80. package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
  81. package/dist/components/DataGrid/SynapseGrid.js +326 -268
  82. package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
  83. package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts +2 -0
  84. package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts.map +1 -1
  85. package/dist/components/DataGrid/columns/AutocompleteColumn.js +124 -67
  86. package/dist/components/DataGrid/columns/AutocompleteColumn.js.map +1 -1
  87. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts +2 -1
  88. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts.map +1 -1
  89. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js +126 -122
  90. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js.map +1 -1
  91. package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts +58 -0
  92. package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts.map +1 -0
  93. package/dist/components/DataGrid/columns/useGridAutocompleteState.js +52 -0
  94. package/dist/components/DataGrid/columns/useGridAutocompleteState.js.map +1 -0
  95. package/dist/components/DataGrid/components/ValidationAlert.d.ts +5 -2
  96. package/dist/components/DataGrid/components/ValidationAlert.d.ts.map +1 -1
  97. package/dist/components/DataGrid/components/ValidationAlert.js +429 -24
  98. package/dist/components/DataGrid/components/ValidationAlert.js.map +1 -1
  99. package/dist/components/DataGrid/hooks/useColumnResizeHandles.js.map +1 -1
  100. package/dist/components/DataGrid/hooks/useGetSchemaForGrid.js.map +1 -1
  101. package/dist/components/DataGrid/hooks/useGridUndoRedo.js.map +1 -1
  102. package/dist/components/DataGrid/hooks/useStack.js.map +1 -1
  103. package/dist/components/DataGrid/useCRDTModelView.js.map +1 -1
  104. package/dist/components/DataGrid/useDataGridWebsocket.d.ts +7 -0
  105. package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
  106. package/dist/components/DataGrid/useDataGridWebsocket.js +16 -2
  107. package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
  108. package/dist/components/DataGrid/useInitializeGridConnection.js.map +1 -1
  109. package/dist/components/DataGrid/useMergeGridWithRecordSet.js.map +1 -1
  110. package/dist/components/DataGrid/useMergeGridWithSource.js.map +1 -1
  111. package/dist/components/DataGrid/useMergeGridWithTable.js.map +1 -1
  112. package/dist/components/DataGrid/utils/DataGridUtils.js.map +1 -1
  113. package/dist/components/DataGrid/utils/applyModelChange.d.ts +1 -1
  114. package/dist/components/DataGrid/utils/applyModelChange.d.ts.map +1 -1
  115. package/dist/components/DataGrid/utils/applyModelChange.js +27 -24
  116. package/dist/components/DataGrid/utils/applyModelChange.js.map +1 -1
  117. package/dist/components/DataGrid/utils/columnFactory.d.ts +8 -0
  118. package/dist/components/DataGrid/utils/columnFactory.d.ts.map +1 -1
  119. package/dist/components/DataGrid/utils/columnFactory.js +47 -44
  120. package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
  121. package/dist/components/DataGrid/utils/computeReplicaSelectionModel.js.map +1 -1
  122. package/dist/components/DataGrid/utils/extractColumnValidationMessages.js.map +1 -1
  123. package/dist/components/DataGrid/utils/getCellClassName.d.ts.map +1 -1
  124. package/dist/components/DataGrid/utils/getCellClassName.js +8 -8
  125. package/dist/components/DataGrid/utils/getCellClassName.js.map +1 -1
  126. package/dist/components/DataGrid/utils/getEmptyValue.d.ts +2 -0
  127. package/dist/components/DataGrid/utils/getEmptyValue.d.ts.map +1 -0
  128. package/dist/components/DataGrid/utils/getEmptyValue.js +8 -0
  129. package/dist/components/DataGrid/utils/getEmptyValue.js.map +1 -0
  130. package/dist/components/DataGrid/utils/json-rx/JsonRx.js.map +1 -1
  131. package/dist/components/DataGrid/utils/modelColsToGrid.d.ts.map +1 -1
  132. package/dist/components/DataGrid/utils/modelColsToGrid.js +2 -1
  133. package/dist/components/DataGrid/utils/modelColsToGrid.js.map +1 -1
  134. package/dist/components/DataGrid/utils/modelRowsToGrid.js.map +1 -1
  135. package/dist/components/DataGrid/utils/parseFreeTextUsingJsonSchemaType.js.map +1 -1
  136. package/dist/components/DataGrid/utils/schemaAwarePasteValue.d.ts +32 -0
  137. package/dist/components/DataGrid/utils/schemaAwarePasteValue.d.ts.map +1 -0
  138. package/dist/components/DataGrid/utils/schemaAwarePasteValue.js +22 -0
  139. package/dist/components/DataGrid/utils/schemaAwarePasteValue.js.map +1 -0
  140. package/dist/components/DataGrid/utils/splitPatch.js.map +1 -1
  141. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  142. package/dist/components/DirectDownload/DirectDownload.js.map +1 -1
  143. package/dist/components/DirectDownloadButton.js.map +1 -1
  144. package/dist/components/DownloadCart/CreatePackageV2.js.map +1 -1
  145. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.css +1 -0
  146. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.d.ts.map +1 -1
  147. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js +199 -132
  148. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map +1 -1
  149. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js +22 -0
  150. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js.map +1 -0
  151. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss +170 -0
  152. package/dist/components/DownloadCart/DownloadListActionsRequired.js.map +1 -1
  153. package/dist/components/DownloadCart/DownloadListTable.js.map +1 -1
  154. package/dist/components/DownloadCart/fileNameUtils.js.map +1 -1
  155. package/dist/components/DraggableDialog/DraggableDialog.js.map +1 -1
  156. package/dist/components/DynamicForm/DynamicFormModal.js.map +1 -1
  157. package/dist/components/Ecosystem/TableQueryEcosystem.js.map +1 -1
  158. package/dist/components/EntityAclEditor/EntityAclEditor.d.ts.map +1 -1
  159. package/dist/components/EntityAclEditor/EntityAclEditor.js +103 -103
  160. package/dist/components/EntityAclEditor/EntityAclEditor.js.map +1 -1
  161. package/dist/components/EntityAclEditor/useNotifyNewACLUsers.js.map +1 -1
  162. package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
  163. package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
  164. package/dist/components/EntityDownloadButton/EntityDownloadButton.d.ts.map +1 -1
  165. package/dist/components/EntityDownloadButton/EntityDownloadButton.js +1 -0
  166. package/dist/components/EntityDownloadButton/EntityDownloadButton.js.map +1 -1
  167. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.d.ts.map +1 -1
  168. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js +36 -30
  169. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js.map +1 -1
  170. package/dist/components/EntityFinder/EntityFinder.js.map +1 -1
  171. package/dist/components/EntityFinder/VersionSelectionType.js.map +1 -1
  172. package/dist/components/EntityFinder/details/configurations/EntityChildrenDetails.js.map +1 -1
  173. package/dist/components/EntityFinder/details/configurations/FavoritesDetails.js.map +1 -1
  174. package/dist/components/EntityFinder/details/configurations/ProjectListDetails.js.map +1 -1
  175. package/dist/components/EntityFinder/details/view/DetailsView.js.map +1 -1
  176. package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
  177. package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
  178. package/dist/components/EntityFinder/useEntitySelection.js.map +1 -1
  179. package/dist/components/EntityForm/EntityForm.js.map +1 -1
  180. package/dist/components/EntityHeaderTable/EntityHeaderTable.js.map +1 -1
  181. package/dist/components/EntityHeaderTable/Filter.js.map +1 -1
  182. package/dist/components/EntityHeaderTable/useEntityHeaderTableState.js.map +1 -1
  183. package/dist/components/EntitySubjectsSelector/EntitySubjectsSelector.js.map +1 -1
  184. package/dist/components/EntityTreeTable/components/IdColumnHeader.js.map +1 -1
  185. package/dist/components/EntityTreeTable/hooks/useEntityTreeState.js.map +1 -1
  186. package/dist/components/EntityTreeTable/hooks/useTableColumns.js.map +1 -1
  187. package/dist/components/EntityTreeTable/hooks/useTableData.js.map +1 -1
  188. package/dist/components/EntityTreeTable/hooks/useTreeOperationsWithDirectFetch.js.map +1 -1
  189. package/dist/components/EntityUpload/EntityUpload.js.map +1 -1
  190. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.d.ts.map +1 -1
  191. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js +15 -14
  192. package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js.map +1 -1
  193. package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
  194. package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js.map +1 -1
  195. package/dist/components/FeaturedDataTabs/FacetPlotsCard.js.map +1 -1
  196. package/dist/components/FeaturedDataTabs/QueryPerFacetPlotsCard.js.map +1 -1
  197. package/dist/components/FeaturedDataTabs/SingleQueryFacetPlotsCards.js.map +1 -1
  198. package/dist/components/FeaturedResearch/FeaturedResearch.js.map +1 -1
  199. package/dist/components/FeaturedToolsList/FeaturedToolsList.js.map +1 -1
  200. package/dist/components/FilePreview/FileHandleContentRenderer.js.map +1 -1
  201. package/dist/components/FilePreview/HtmlPreview/HtmlPreview.js.map +1 -1
  202. package/dist/components/FilePreview/PreviewRendererType.js.map +1 -1
  203. package/dist/components/Forum/DiscussionReply.d.ts +1 -0
  204. package/dist/components/Forum/DiscussionReply.d.ts.map +1 -1
  205. package/dist/components/Forum/DiscussionReply.js +19 -19
  206. package/dist/components/Forum/DiscussionReply.js.map +1 -1
  207. package/dist/components/Forum/DiscussionSearchResult.js.map +1 -1
  208. package/dist/components/Forum/DiscussionThread.d.ts +1 -0
  209. package/dist/components/Forum/DiscussionThread.d.ts.map +1 -1
  210. package/dist/components/Forum/DiscussionThread.js +73 -72
  211. package/dist/components/Forum/DiscussionThread.js.map +1 -1
  212. package/dist/components/Forum/ForumTable.js.map +1 -1
  213. package/dist/components/Forum/ForumThreadEditor.js.map +1 -1
  214. package/dist/components/FullTextSearch/FullTextSearchUtils.js.map +1 -1
  215. package/dist/components/GenericCard/BioregistryRules.d.ts.map +1 -1
  216. package/dist/components/GenericCard/BioregistryRules.js +7 -3
  217. package/dist/components/GenericCard/BioregistryRules.js.map +1 -1
  218. package/dist/components/GenericCard/GenericCard.d.ts.map +1 -1
  219. package/dist/components/GenericCard/GenericCard.js +12 -7
  220. package/dist/components/GenericCard/GenericCard.js.map +1 -1
  221. package/dist/components/GenericCard/Linkify.js.map +1 -1
  222. package/dist/components/GenericCard/SynapseCardLabel.js.map +1 -1
  223. package/dist/components/GenericCard/TableRowGenericCard.js +105 -105
  224. package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
  225. package/dist/components/Goals/Goals.Mobile.js.map +1 -1
  226. package/dist/components/Goals/Goals.js.map +1 -1
  227. package/dist/components/GoalsV2/GoalsV2.Mobile.js.map +1 -1
  228. package/dist/components/GoalsV2/GoalsV2.js.map +1 -1
  229. package/dist/components/GoalsV3/GoalsV3.Mobile.js.map +1 -1
  230. package/dist/components/GoalsV3/GoalsV3.js.map +1 -1
  231. package/dist/components/GoogleMap/SynapseUserMarker.js.map +1 -1
  232. package/dist/components/HasAccess/AccessIcon.js.map +1 -1
  233. package/dist/components/HasAccess/useHasAccess.js.map +1 -1
  234. package/dist/components/HeaderCard/HeaderCardV2.js.map +1 -1
  235. package/dist/components/HeaderCard.d.ts +6 -1
  236. package/dist/components/HeaderCard.d.ts.map +1 -1
  237. package/dist/components/HeaderCard.js +107 -76
  238. package/dist/components/HeaderCard.js.map +1 -1
  239. package/dist/components/HexGrid/HexGrid.js.map +1 -1
  240. package/dist/components/IconList.js.map +1 -1
  241. package/dist/components/IconSvg/IconSvg.d.ts.map +1 -1
  242. package/dist/components/IconSvg/IconSvg.js +2 -1
  243. package/dist/components/IconSvg/IconSvg.js.map +1 -1
  244. package/dist/components/ImageCardGridWithLinks/ImageCardGridWithLinks.js.map +1 -1
  245. package/dist/components/ImageFromSynapseTable.js.map +1 -1
  246. package/dist/components/JSONArrayEditor/useParseCsv.js.map +1 -1
  247. package/dist/components/JsonSchemaForm/templates/ArrayFieldDescriptionTemplate.js.map +1 -1
  248. package/dist/components/JsonSchemaForm/templates/ArrayFieldItemTemplate.js.map +1 -1
  249. package/dist/components/JsonSchemaForm/templates/BaseInputTemplate.js.map +1 -1
  250. package/dist/components/JsonSchemaForm/templates/FieldTemplate.js.map +1 -1
  251. package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js.map +1 -1
  252. package/dist/components/Markdown/MarkdownGithub.js.map +1 -1
  253. package/dist/components/Markdown/MarkdownSynapse.js.map +1 -1
  254. package/dist/components/Markdown/MarkdownUtils.js.map +1 -1
  255. package/dist/components/Markdown/SynapseWikiContext.js.map +1 -1
  256. package/dist/components/Markdown/UserMentionModal.js.map +1 -1
  257. package/dist/components/Markdown/widget/MarkdownProvenanceGraph.js.map +1 -1
  258. package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js.map +1 -1
  259. package/dist/components/ModalDownload/ModalDownload.js.map +1 -1
  260. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.d.ts.map +1 -1
  261. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js +45 -39
  262. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js.map +1 -1
  263. package/dist/components/OAuthClientManagement/OAuthManagement.js.map +1 -1
  264. package/dist/components/PageProgress/PageProgress.js.map +1 -1
  265. package/dist/components/Plot/DotPlot.js.map +1 -1
  266. package/dist/components/Plot/Plot.js.map +1 -1
  267. package/dist/components/Plot/SynapsePlot.js.map +1 -1
  268. package/dist/components/Plot/ThemesPlot.js.map +1 -1
  269. package/dist/components/Plot/UpsetPlot.js.map +1 -1
  270. package/dist/components/PortalAclEditor/PortalAclEditor.d.ts.map +1 -1
  271. package/dist/components/PortalAclEditor/PortalAclEditor.js +43 -41
  272. package/dist/components/PortalAclEditor/PortalAclEditor.js.map +1 -1
  273. package/dist/components/PortalFeaturedPartners/PortalFeaturedPartners.js.map +1 -1
  274. package/dist/components/PortalList/CreatePortalModal.js.map +1 -1
  275. package/dist/components/ProgrammaticInstructionsModal/ProgrammaticInstructionsModal.js.map +1 -1
  276. package/dist/components/ProgrammaticTableDownload/ProgrammaticTableDownload.js.map +1 -1
  277. package/dist/components/Programs/Programs.Mobile.js.map +1 -1
  278. package/dist/components/Programs/Programs.js.map +1 -1
  279. package/dist/components/ProvenanceGraph/ProvenanceExternalIcon.js.map +1 -1
  280. package/dist/components/ProvenanceGraph/ProvenanceGraph.js.map +1 -1
  281. package/dist/components/ProvenanceGraph/ProvenanceGraphUtils.js.map +1 -1
  282. package/dist/components/ProvenanceGraph/ProvenanceUtils.js.map +1 -1
  283. package/dist/components/QueryCount/QueryCount.js.map +1 -1
  284. package/dist/components/QueryCountButton/QueryCountButton.js.map +1 -1
  285. package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
  286. package/dist/components/QueryWrapper/QueryWrapper.js.map +1 -1
  287. package/dist/components/QueryWrapper/TableQueryUseQueryOptions.js.map +1 -1
  288. package/dist/components/QueryWrapper/TableRowSelectionState.js.map +1 -1
  289. package/dist/components/QueryWrapper/generateEncodedPathAndQueryForSelectedFacetURL.js.map +1 -1
  290. package/dist/components/QueryWrapper/useGetQueryMetadata.js.map +1 -1
  291. package/dist/components/QueryWrapperErrorBoundary.js.map +1 -1
  292. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
  293. package/dist/components/QueryWrapperPlotNav/UseRowSet.js.map +1 -1
  294. package/dist/components/RecentPublicationsGrid/RecentPublicationsGrid.js.map +1 -1
  295. package/dist/components/ReleaseCard/ReleaseCardUtils.js.map +1 -1
  296. package/dist/components/ResizableContainer/hooks/useResizable.js.map +1 -1
  297. package/dist/components/Resources/Resources.Mobile.js.map +1 -1
  298. package/dist/components/Resources/Resources.js.map +1 -1
  299. package/dist/components/RowDataTable/RowDataTableWithQuery.js.map +1 -1
  300. package/dist/components/SageResourcesPopover/SageResourcesPopover.js.map +1 -1
  301. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js.map +1 -1
  302. package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
  303. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
  304. package/dist/components/SmartLink/SmartButton.js.map +1 -1
  305. package/dist/components/SmartLink/SmartLink.js.map +1 -1
  306. package/dist/components/SourceAppImage.js.map +1 -1
  307. package/dist/components/StandaloneQueryWrapper/StandaloneQueryWrapper.js.map +1 -1
  308. package/dist/components/StatisticsPlot.js.map +1 -1
  309. package/dist/components/StorybookComponentWrapper.js.map +1 -1
  310. package/dist/components/SubsectionRowRenderer/SubsectionRowRenderer.js.map +1 -1
  311. package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js.map +1 -1
  312. package/dist/components/SynapseChat/GridAgentChat.js.map +1 -1
  313. package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
  314. package/dist/components/SynapseChat/SynapseChatMessage.js.map +1 -1
  315. package/dist/components/SynapseChat/extractMessageFromTraceEvent.js.map +1 -1
  316. package/dist/components/SynapseForm/StepsSideNav.js.map +1 -1
  317. package/dist/components/SynapseForm/SummaryTable.js.map +1 -1
  318. package/dist/components/SynapseForm/SynapseForm.js +4 -2
  319. package/dist/components/SynapseForm/SynapseForm.js.map +1 -1
  320. package/dist/components/SynapseForm/SynapseFormWrapper.js.map +1 -1
  321. package/dist/components/SynapseHomepageV2/SynapseByTheNumbersItem.js.map +1 -1
  322. package/dist/components/SynapseHomepageV2/SynapseFeatureItem.js.map +1 -1
  323. package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js.map +1 -1
  324. package/dist/components/SynapseHomepageV2/SynapseHomepageSearch.js.map +1 -1
  325. package/dist/components/SynapseHomepageV2/SynapseInActionItem.js.map +1 -1
  326. package/dist/components/SynapseHomepageV2/SynapsePlans.js.map +1 -1
  327. package/dist/components/SynapseHomepageV2/SynapseTrendingProjects.js.map +1 -1
  328. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts +8 -7
  329. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts.map +1 -1
  330. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js +173 -164
  331. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js.map +1 -1
  332. package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
  333. package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanel.js.map +1 -1
  334. package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanelUtils.js.map +1 -1
  335. package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js.map +1 -1
  336. package/dist/components/SynapseTable/EntityIDColumnCopyIcon.js.map +1 -1
  337. package/dist/components/SynapseTable/NoContentPlaceholderType.js.map +1 -1
  338. package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js.map +1 -1
  339. package/dist/components/SynapseTable/SynapseTableCell/SynapseTableCell.js.map +1 -1
  340. package/dist/components/SynapseTable/SynapseTableRenderers.js.map +1 -1
  341. package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
  342. package/dist/components/SynapseTable/table-top/ColumnSelection.js.map +1 -1
  343. package/dist/components/SynapseTable/table-top/DownloadOptions.js.map +1 -1
  344. package/dist/components/SynapseTable/usePrefetchTableData.js.map +1 -1
  345. package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
  346. package/dist/components/TableColumnSchemaEditor/ColumnModelFormFields/DefaultValueField.js.map +1 -1
  347. package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js.map +1 -1
  348. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts +1 -1
  349. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts.map +1 -1
  350. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.js.map +1 -1
  351. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
  352. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js.map +1 -1
  353. package/dist/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.js.map +1 -1
  354. package/dist/components/TableColumnSchemaEditor/Validators/DatetimeSchema.js.map +1 -1
  355. package/dist/components/TanStackTable/ColumnHeader.d.ts +1 -0
  356. package/dist/components/TanStackTable/ColumnHeader.d.ts.map +1 -1
  357. package/dist/components/TanStackTable/ColumnHeader.js +8 -8
  358. package/dist/components/TanStackTable/ColumnHeader.js.map +1 -1
  359. package/dist/components/TanStackTable/ColumnHeaderEnumFilter.js.map +1 -1
  360. package/dist/components/TanStackTable/TableBody.js.map +1 -1
  361. package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js.map +1 -1
  362. package/dist/components/TextField/TextField.js.map +1 -1
  363. package/dist/components/TimelinePlot/TimelinePhase.js.map +1 -1
  364. package/dist/components/TimelinePlot/TimelinePlot.js.map +1 -1
  365. package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js.map +1 -1
  366. package/dist/components/UserCard/Avatar.js.map +1 -1
  367. package/dist/components/UserCardList/UserCardList.js.map +1 -1
  368. package/dist/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.js.map +1 -1
  369. package/dist/components/UserCardList/UserCardListRotate.js.map +1 -1
  370. package/dist/components/UserOrTeamBadge/useUserOrTeam.js.map +1 -1
  371. package/dist/components/UserProfileLinks/UserProjects.js.map +1 -1
  372. package/dist/components/UserSearchBox/UserSearchBox.js.map +1 -1
  373. package/dist/components/Webhook/WebhookDashboard.js.map +1 -1
  374. package/dist/components/WikiMarkdownEditor/WikiMarkdownEditor.js.map +1 -1
  375. package/dist/components/WikiMarkdownEditorButton/WikiMarkdownEditorButton.js.map +1 -1
  376. package/dist/components/dataaccess/AccessApprovalsTable.js.map +1 -1
  377. package/dist/components/dataaccess/AccessRequestSubmissionTable.js.map +1 -1
  378. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.d.ts.map +1 -1
  379. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js +157 -148
  380. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
  381. package/dist/components/dataaccess/UseAccessRequirementTable.js.map +1 -1
  382. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js.map +1 -1
  383. package/dist/components/doi/CreateOrUpdateDoiModal.d.ts.map +1 -1
  384. package/dist/components/doi/CreateOrUpdateDoiModal.js +20 -19
  385. package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
  386. package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
  387. package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
  388. package/dist/components/entity/page/title_bar/useDataCiteUsage.js.map +1 -1
  389. package/dist/components/entity/page/title_bar/useGetMentions.js.map +1 -1
  390. package/dist/components/error/ErrorPage.js.map +1 -1
  391. package/dist/components/favorites/FavoritesPage.js.map +1 -1
  392. package/dist/components/file/upload/BasicFileHandleUpload.js.map +1 -1
  393. package/dist/components/layout/SWCHeader.d.ts +9 -0
  394. package/dist/components/layout/SWCHeader.d.ts.map +1 -0
  395. package/dist/components/layout/SWCHeader.js +19 -0
  396. package/dist/components/layout/SWCHeader.js.map +1 -0
  397. package/dist/components/layout/SWCPageLayout.d.ts +9 -0
  398. package/dist/components/layout/SWCPageLayout.d.ts.map +1 -0
  399. package/dist/components/layout/SWCPageLayout.js +14 -0
  400. package/dist/components/layout/SWCPageLayout.js.map +1 -0
  401. package/dist/components/menu/ComplexMenu.js.map +1 -1
  402. package/dist/components/row_renderers/utils/ChipContainer.js.map +1 -1
  403. package/dist/components/styled/StyledPopover.js.map +1 -1
  404. package/dist/components/table/CsvPreview/CsvPreview.js +2 -1
  405. package/dist/components/table/CsvPreview/CsvPreview.js.map +1 -1
  406. package/dist/components/table/CsvPreview/CsvPreviewDialog.js.map +1 -1
  407. package/dist/components/trash/TrashCanList.js.map +1 -1
  408. package/dist/components/widgets/FileHandleLink.js.map +1 -1
  409. package/dist/components/widgets/RangeSlider/RangeSlider.js.map +1 -1
  410. package/dist/components/widgets/SynapseVideo.js.map +1 -1
  411. package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
  412. package/dist/components/widgets/facet-nav/PlotsContainer.js.map +1 -1
  413. package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js.map +1 -1
  414. package/dist/components/widgets/facet-nav/useFacetPlots.js.map +1 -1
  415. package/dist/components/widgets/query-filter/CombinedRangeFacetFilter.js.map +1 -1
  416. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
  417. package/dist/components/widgets/query-filter/FacetFilterControls.js.map +1 -1
  418. package/dist/components/widgets/query-filter/RangeFacetFilter.js.map +1 -1
  419. package/dist/components/widgets/query-filter/RangeFacetFilterUI.js.map +1 -1
  420. package/dist/features/curator/GridPage/components/GridPageTitle.d.ts.map +1 -1
  421. package/dist/features/curator/GridPage/components/GridPageTitle.js +23 -30
  422. package/dist/features/curator/GridPage/components/GridPageTitle.js.map +1 -1
  423. package/dist/features/curator/dashboard/CuratorDashboard.d.ts +2 -0
  424. package/dist/features/curator/dashboard/CuratorDashboard.d.ts.map +1 -0
  425. package/dist/features/curator/dashboard/CuratorDashboard.js +45 -0
  426. package/dist/features/curator/dashboard/CuratorDashboard.js.map +1 -0
  427. package/dist/features/curator/dashboard/components/CurationTaskCard.css +1 -0
  428. package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts +9 -0
  429. package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts.map +1 -0
  430. package/dist/features/curator/dashboard/components/CurationTaskCard.js +106 -0
  431. package/dist/features/curator/dashboard/components/CurationTaskCard.js.map +1 -0
  432. package/dist/features/curator/dashboard/components/CurationTaskCard.module.js +12 -0
  433. package/dist/features/curator/dashboard/components/CurationTaskCard.module.js.map +1 -0
  434. package/dist/features/curator/dashboard/components/CurationTaskCard.module.scss +52 -0
  435. package/dist/features/curator/dashboard/components/NextStepButton.css +1 -0
  436. package/dist/features/curator/dashboard/components/NextStepButton.d.ts +14 -0
  437. package/dist/features/curator/dashboard/components/NextStepButton.d.ts.map +1 -0
  438. package/dist/features/curator/dashboard/components/NextStepButton.js +35 -0
  439. package/dist/features/curator/dashboard/components/NextStepButton.js.map +1 -0
  440. package/dist/features/curator/dashboard/components/NextStepButton.module.js +11 -0
  441. package/dist/features/curator/dashboard/components/NextStepButton.module.js.map +1 -0
  442. package/dist/features/curator/dashboard/components/NextStepButton.module.scss +57 -0
  443. package/dist/features/curator/dashboard/components/UserOrTeamChip.css +1 -1
  444. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js +1 -1
  445. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js.map +1 -1
  446. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.scss +5 -5
  447. package/dist/features/curator/dashboard/components/shared.css +1 -0
  448. package/dist/features/curator/dashboard/components/shared.module.js +5 -0
  449. package/dist/features/curator/dashboard/components/shared.module.js.map +1 -0
  450. package/dist/features/curator/dashboard/components/shared.module.scss +8 -0
  451. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts +0 -2
  452. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts.map +1 -1
  453. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +16 -34
  454. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
  455. package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.d.ts.map +1 -1
  456. package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js +1 -1
  457. package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js.map +1 -1
  458. package/dist/features/entity/metadata-task/hooks/useGetOrCreateGridSessionForSource.js.map +1 -1
  459. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js.map +1 -1
  460. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask_legacy.js.map +1 -1
  461. package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js +1 -1
  462. package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js.map +1 -1
  463. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts +10 -0
  464. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts.map +1 -0
  465. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js +37 -0
  466. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js.map +1 -0
  467. package/dist/features/entity/metadata-task/utils/constants.d.ts +5 -0
  468. package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -0
  469. package/dist/features/entity/metadata-task/utils/constants.js +6 -0
  470. package/dist/features/entity/metadata-task/utils/constants.js.map +1 -0
  471. package/dist/mocks/challenge/mockChallenge.js.map +1 -1
  472. package/dist/mocks/entity/mockDataset.js.map +1 -1
  473. package/dist/mocks/entity/mockDatasetCollection.js.map +1 -1
  474. package/dist/mocks/entity/mockFileEntity.js.map +1 -1
  475. package/dist/mocks/entity/mockFileView.js.map +1 -1
  476. package/dist/mocks/entity/mockGeneratedEntityData.js.map +1 -1
  477. package/dist/mocks/entity/mockProject.js.map +1 -1
  478. package/dist/mocks/entity/mockProjectView.js.map +1 -1
  479. package/dist/mocks/entity/mockRootEntity.js.map +1 -1
  480. package/dist/mocks/entity/mockTableEntity.js.map +1 -1
  481. package/dist/mocks/mockWiki.js.map +1 -1
  482. package/dist/mocks/msw/handlers/asyncJobHandlers.js.map +1 -1
  483. package/dist/mocks/msw/handlers/challengeHandlers.js.map +1 -1
  484. package/dist/mocks/msw/handlers/changePasswordHandlers.js.map +1 -1
  485. package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
  486. package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
  487. package/dist/mocks/msw/handlers/fileHandlers.js.map +1 -1
  488. package/dist/mocks/msw/handlers/gridHandlers.js.map +1 -1
  489. package/dist/mocks/msw/handlers/personalAccessTokenHandlers.js.map +1 -1
  490. package/dist/mocks/msw/handlers/subscriptionHandlers.js.map +1 -1
  491. package/dist/mocks/msw/handlers/teamHandlers.js.map +1 -1
  492. package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
  493. package/dist/mocks/msw/handlers/wikiHandlers.js.map +1 -1
  494. package/dist/mocks/provenance/mockActivity.js.map +1 -1
  495. package/dist/mocks/query/mockReleaseCardsTableQueryResultBundle.js.map +1 -1
  496. package/dist/ror-client/index.js.map +1 -1
  497. package/dist/style/components/_cards.scss +4 -0
  498. package/dist/style/components/_data-grid-extra.css +1 -1
  499. package/dist/style/components/_data-grid-extra.scss +2 -0
  500. package/dist/style/main.css +1 -1
  501. package/dist/synapse-client/HttpClient.js.map +1 -1
  502. package/dist/synapse-client/SynapseClient.js.map +1 -1
  503. package/dist/synapse-queries/KeyFactory.d.ts +1 -0
  504. package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
  505. package/dist/synapse-queries/KeyFactory.js +3 -0
  506. package/dist/synapse-queries/KeyFactory.js.map +1 -1
  507. package/dist/synapse-queries/QueryMatching.test-utils.js.map +1 -1
  508. package/dist/synapse-queries/auth/useTwoFactorEnrollment.js.map +1 -1
  509. package/dist/synapse-queries/curation/task/useCurationTask.d.ts +1 -1
  510. package/dist/synapse-queries/curation/task/useCurationTask.d.ts.map +1 -1
  511. package/dist/synapse-queries/curation/task/useCurationTask.js +1 -1
  512. package/dist/synapse-queries/curation/task/useCurationTask.js.map +1 -1
  513. package/dist/synapse-queries/dataaccess/useRestrictionInformation.js.map +1 -1
  514. package/dist/synapse-queries/doi/useDOI.js.map +1 -1
  515. package/dist/synapse-queries/download/useDownloadList.js.map +1 -1
  516. package/dist/synapse-queries/entity/useEntity.js.map +1 -1
  517. package/dist/synapse-queries/entity/useEntityBundle.js.map +1 -1
  518. package/dist/synapse-queries/entity/useExportTableQueryToAnalysisPlatform.js.map +1 -1
  519. package/dist/synapse-queries/entity/useExportToTerra.js.map +1 -1
  520. package/dist/synapse-queries/entity/useGetQueryResultBundle.js.map +1 -1
  521. package/dist/synapse-queries/entity/useSchema.js.map +1 -1
  522. package/dist/synapse-queries/file/UploadToS3.js.map +1 -1
  523. package/dist/synapse-queries/file/useDirectUploadToS3.js.map +1 -1
  524. package/dist/synapse-queries/file/useFiles.js.map +1 -1
  525. package/dist/synapse-queries/forum/useReply.js.map +1 -1
  526. package/dist/synapse-queries/forum/useThread.d.ts +1 -0
  527. package/dist/synapse-queries/forum/useThread.d.ts.map +1 -1
  528. package/dist/synapse-queries/forum/useThread.js +19 -12
  529. package/dist/synapse-queries/forum/useThread.js.map +1 -1
  530. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
  531. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
  532. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
  533. package/dist/synapse-queries/grid/useExportGrid.js.map +1 -1
  534. package/dist/synapse-queries/grid/useGridSession.js.map +1 -1
  535. package/dist/synapse-queries/grid/useImportCsvIntoGrid.js.map +1 -1
  536. package/dist/synapse-queries/subscription/useSubscription.js.map +1 -1
  537. package/dist/synapse-queries/table/useGetCsvPreview.js.map +1 -1
  538. package/dist/synapse-queries/table/useTableUpdateTransaction.js.map +1 -1
  539. package/dist/synapse-queries/team/useTeamMembers.js.map +1 -1
  540. package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
  541. package/dist/synapse-queries/user/useUserBundle.js.map +1 -1
  542. package/dist/synapse-queries/user/useUserGroupHeader.js.map +1 -1
  543. package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
  544. package/dist/theme/ThemeProvider.js.map +1 -1
  545. package/dist/tsconfig.build.tsbuildinfo +1 -1
  546. package/dist/utils/APIConstants.d.ts +1 -0
  547. package/dist/utils/APIConstants.d.ts.map +1 -1
  548. package/dist/utils/APIConstants.js +2 -2
  549. package/dist/utils/APIConstants.js.map +1 -1
  550. package/dist/utils/AppUtils/session/ApplicationSessionManager.d.ts.map +1 -1
  551. package/dist/utils/AppUtils/session/ApplicationSessionManager.js +7 -4
  552. package/dist/utils/AppUtils/session/ApplicationSessionManager.js.map +1 -1
  553. package/dist/utils/AppUtils/session/SynapseSessionManager.js.map +1 -1
  554. package/dist/utils/AppUtils/session/useSessionManager.js.map +1 -1
  555. package/dist/utils/PermissionLevelToAccessType.js.map +1 -1
  556. package/dist/utils/challenge/evaluation/EvaluationUtils.js.map +1 -1
  557. package/dist/utils/context/SynapseContext.js.map +1 -1
  558. package/dist/utils/functions/AccessControlListUtils.d.ts +4 -0
  559. package/dist/utils/functions/AccessControlListUtils.d.ts.map +1 -1
  560. package/dist/utils/functions/AccessControlListUtils.js +12 -1
  561. package/dist/utils/functions/AccessControlListUtils.js.map +1 -1
  562. package/dist/utils/functions/EntityTypeUtils.d.ts.map +1 -1
  563. package/dist/utils/functions/EntityTypeUtils.js +15 -4
  564. package/dist/utils/functions/EntityTypeUtils.js.map +1 -1
  565. package/dist/utils/functions/GridApiUtils.js.map +1 -1
  566. package/dist/utils/functions/QueryFilterUtils.js.map +1 -1
  567. package/dist/utils/functions/RealmUtils.d.ts +4 -0
  568. package/dist/utils/functions/RealmUtils.d.ts.map +1 -1
  569. package/dist/utils/functions/RealmUtils.js +9 -3
  570. package/dist/utils/functions/RealmUtils.js.map +1 -1
  571. package/dist/utils/functions/SanitizeHtmlUtils.js.map +1 -1
  572. package/dist/utils/functions/SanitizeHtmlUtils.test-utils.js.map +1 -1
  573. package/dist/utils/functions/SqlFunctions.js.map +1 -1
  574. package/dist/utils/functions/StringUtils.js.map +1 -1
  575. package/dist/utils/functions/deepLinkingUtils.js.map +1 -1
  576. package/dist/utils/functions/getDataFromFromStorage.js.map +1 -1
  577. package/dist/utils/functions/getEndpoint.js.map +1 -1
  578. package/dist/utils/functions/getUserData.js.map +1 -1
  579. package/dist/utils/functions/queryUtils.js.map +1 -1
  580. package/dist/utils/functions/testDownloadSpeed.js.map +1 -1
  581. package/dist/utils/hooks/useConfirmItems.js.map +1 -1
  582. package/dist/utils/hooks/useCookiePreferences.js.map +1 -1
  583. package/dist/utils/hooks/useCreateShortUrl.js.map +1 -1
  584. package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
  585. package/dist/utils/hooks/useDirectDownloadHandler.js.map +1 -1
  586. package/dist/utils/hooks/useGetGoalData.js.map +1 -1
  587. package/dist/utils/hooks/useGetInfoFromIds.js.map +1 -1
  588. package/dist/utils/hooks/useImageUrlUtils.js.map +1 -1
  589. package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
  590. package/dist/utils/hooks/useImmutableTableQuery/useTableQueryReducer.js.map +1 -1
  591. package/dist/utils/hooks/useIsBot.js.map +1 -1
  592. package/dist/utils/hooks/useListState.js.map +1 -1
  593. package/dist/utils/hooks/useLogin.d.ts.map +1 -1
  594. package/dist/utils/hooks/useLogin.js +53 -52
  595. package/dist/utils/hooks/useLogin.js.map +1 -1
  596. package/dist/utils/hooks/useMutuallyExclusiveState.js.map +1 -1
  597. package/dist/utils/hooks/useOverlay.js.map +1 -1
  598. package/dist/utils/hooks/usePreFetchResource.js.map +1 -1
  599. package/dist/utils/hooks/useQuerySearchParam.js.map +1 -1
  600. package/dist/utils/hooks/useScrollFadeTransition.js.map +1 -1
  601. package/dist/utils/hooks/useSet.js.map +1 -1
  602. package/dist/utils/hooks/useSourceAppConfigs.js.map +1 -1
  603. package/dist/utils/hooks/useTableImageUrl.js.map +1 -1
  604. package/dist/utils/hooks/useUploadFileEntity/useCreatePathsAndGetParentId.js.map +1 -1
  605. package/dist/utils/hooks/useUploadFileEntity/useLinkFileEntityToURL.js.map +1 -1
  606. package/dist/utils/hooks/useUploadFileEntity/usePrepareFileEntityUpload.js.map +1 -1
  607. package/dist/utils/hooks/useUploadFileEntity/useTrackFileUploads.js.map +1 -1
  608. package/dist/utils/hooks/useUploadFileEntity/useUploadFileEntities.js.map +1 -1
  609. package/dist/utils/hooks/useUploadFileEntity/useUploadFiles.js.map +1 -1
  610. package/dist/utils/hooks/useUploadFileEntity/willUploadsExceedStorageLimit.js.map +1 -1
  611. package/dist/utils/html/TargetEnum.js.map +1 -1
  612. package/dist/utils/jsonschema/SchemaAnnotationUtils.js.map +1 -1
  613. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"EntityTypeUtils.js","names":[],"sources":["../../../src/utils/functions/EntityTypeUtils.ts"],"sourcesContent":["import { isTypeViaConcreteTypeFactory } from '@/utils/types/IsType'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n Dataset,\n DATASET_COLLECTION_CONCRETE_TYPE_VALUE,\n DATASET_CONCRETE_TYPE_VALUE,\n DatasetCollection,\n DOCKER_REPOSITORY_CONCRETE_TYPE_VALUE,\n Entity,\n Entity as Entity_OpenAPI,\n ENTITY_CONCRETE_TYPE,\n ENTITY_VIEW_CONCRETE_TYPE_VALUE,\n ENTITY_VIEW_TYPE_MASK_FILE,\n EntityHeader,\n EntityView,\n FILE_ENTITY_CONCRETE_TYPE_VALUE,\n FOLDER_CONCRETE_TYPE_VALUE,\n Hit,\n LINK_CONCRETE_TYPE_VALUE,\n MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE,\n MaterializedView,\n PROJECT_CONCRETE_TYPE_VALUE,\n ProjectHeader,\n SUBMISSION_VIEW_CONCRETE_TYPE_VALUE,\n SubmissionView,\n Table,\n TABLE_CONCRETE_TYPE_VALUES,\n TABLE_ENTITY_CONCRETE_TYPE_VALUE,\n TableEntity,\n VersionableEntity,\n View,\n VIEW_CONCRETE_TYPE_VALUES,\n VIRTUAL_TABLE_CONCRETE_TYPE_VALUE,\n} from '@sage-bionetworks/synapse-types'\n\nexport function getEntityTypeFromHeader(\n header:\n | Pick<EntityHeader, 'name' | 'id' | 'type'>\n | EntityHeader\n | ProjectHeader\n | Hit,\n): EntityType {\n // Hit has the `node_type` field which is what we already want.\n if ((header as Hit).node_type) {\n return (header as Hit).node_type\n }\n // ProjectHeader doesn't have the `type` field, so we can just check that to determine if it's a ProjectHeader\n return (header as EntityHeader).type === undefined\n ? EntityType.project\n : convertToEntityType((header as EntityHeader).type)\n}\n\nexport function isContainerType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n return true\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.file:\n case EntityType.recordset:\n case EntityType.table:\n case EntityType.submissionview:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n case EntityType.materializedview:\n case EntityType.virtualtable:\n return false\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport function isTableType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.file:\n case EntityType.recordset:\n return false\n case EntityType.table:\n case EntityType.submissionview:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n case EntityType.materializedview:\n case EntityType.virtualtable:\n return true\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport function entityTypeToFriendlyName(entityType: EntityType): string {\n switch (entityType) {\n case EntityType.project:\n return 'Project'\n case EntityType.folder:\n return 'Folder'\n case EntityType.file:\n return 'File'\n case EntityType.table:\n return 'Table'\n case EntityType.link:\n return 'Link'\n case EntityType.entityview:\n return 'View'\n case EntityType.dockerrepo:\n return 'Docker Repository'\n case EntityType.submissionview:\n return 'Submission View'\n case EntityType.dataset:\n return 'Dataset'\n case EntityType.datasetcollection:\n return 'Dataset Collection'\n case EntityType.materializedview:\n return 'Materialized View'\n case EntityType.virtualtable:\n return 'Virtual Table'\n case EntityType.recordset:\n return 'Record Set'\n default:\n console.warn('Entity type could not be mapped to name:', entityType)\n return ''\n }\n}\n\nexport function convertToEntityType(\n typeString: string | ENTITY_CONCRETE_TYPE | EntityType,\n): EntityType {\n if (Object.values(EntityType).includes(typeString as EntityType)) {\n return typeString as EntityType\n }\n switch (typeString) {\n case 'org.sagebionetworks.repo.model.Project':\n return EntityType.project\n case 'org.sagebionetworks.repo.model.Folder':\n return EntityType.folder\n case FILE_ENTITY_CONCRETE_TYPE_VALUE:\n return EntityType.file\n case 'org.sagebionetworks.repo.model.Link':\n return EntityType.link\n case 'org.sagebionetworks.repo.model.docker.DockerRepository':\n return EntityType.dockerrepo\n case TABLE_ENTITY_CONCRETE_TYPE_VALUE:\n return EntityType.table\n case 'org.sagebionetworks.repo.model.table.SubmissionView':\n return EntityType.submissionview\n case ENTITY_VIEW_CONCRETE_TYPE_VALUE:\n return EntityType.entityview\n case DATASET_CONCRETE_TYPE_VALUE:\n return EntityType.dataset\n case DATASET_COLLECTION_CONCRETE_TYPE_VALUE:\n return EntityType.datasetcollection\n case MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE:\n return EntityType.materializedview\n case VIRTUAL_TABLE_CONCRETE_TYPE_VALUE:\n return EntityType.virtualtable\n case 'org.sagebionetworks.repo.model.RecordSet':\n return EntityType.recordset\n default:\n throw new Error(`Unknown entity type: ${typeString}`)\n }\n}\n\nexport function convertToConcreteEntityType(\n type: EntityType,\n): Entity_OpenAPI['concreteType'] {\n switch (type) {\n case EntityType.project:\n return 'org.sagebionetworks.repo.model.Project'\n case EntityType.folder:\n return 'org.sagebionetworks.repo.model.Folder'\n case EntityType.file:\n return 'org.sagebionetworks.repo.model.FileEntity'\n case EntityType.link:\n return 'org.sagebionetworks.repo.model.Link'\n case EntityType.dockerrepo:\n return 'org.sagebionetworks.repo.model.docker.DockerRepository'\n case EntityType.table:\n return 'org.sagebionetworks.repo.model.table.TableEntity'\n case EntityType.submissionview:\n return 'org.sagebionetworks.repo.model.table.SubmissionView'\n case EntityType.entityview:\n return 'org.sagebionetworks.repo.model.table.EntityView'\n case EntityType.dataset:\n return 'org.sagebionetworks.repo.model.table.Dataset'\n case EntityType.datasetcollection:\n return 'org.sagebionetworks.repo.model.table.DatasetCollection'\n case EntityType.materializedview:\n return 'org.sagebionetworks.repo.model.table.MaterializedView'\n case EntityType.virtualtable:\n return 'org.sagebionetworks.repo.model.table.VirtualTable'\n case EntityType.recordset:\n return 'org.sagebionetworks.repo.model.RecordSet'\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\n/**\n * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html\n * @param type\n * @returns\n */\nexport function isVersionableEntityType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.submissionview: // SubmissionView implements VersionableEntity, but versions aren't supported\n case EntityType.materializedview: // MaterializedView implements VersionableEntity, but versions aren't supported.\n case EntityType.virtualtable: // VirtualTable implements VersionableEntity, but versions aren't supported.\n return false\n case EntityType.file:\n case EntityType.recordset:\n case EntityType.table:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n return true\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport const isTable = isTypeViaConcreteTypeFactory<Table, Entity>(\n ...TABLE_CONCRETE_TYPE_VALUES,\n)\n\nexport const isView = isTypeViaConcreteTypeFactory<View, Entity>(\n ...VIEW_CONCRETE_TYPE_VALUES,\n)\n\nexport const isTableEntity = isTypeViaConcreteTypeFactory<TableEntity, Entity>(\n TABLE_ENTITY_CONCRETE_TYPE_VALUE,\n)\n\nexport const isSubmissionView = isTypeViaConcreteTypeFactory<\n SubmissionView,\n Entity\n>(SUBMISSION_VIEW_CONCRETE_TYPE_VALUE)\n\nexport const isMaterializedView = isTypeViaConcreteTypeFactory<\n MaterializedView,\n Entity\n>(MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE)\n\nexport const isDataset = isTypeViaConcreteTypeFactory<Dataset, Entity>(\n DATASET_CONCRETE_TYPE_VALUE,\n)\nexport const isDatasetCollection = isTypeViaConcreteTypeFactory<\n DatasetCollection,\n Entity\n>(DATASET_COLLECTION_CONCRETE_TYPE_VALUE)\n\nexport const isEntityRefCollectionView = (\n entity: Entity,\n): entity is Dataset | DatasetCollection =>\n isDataset(entity) || isDatasetCollection(entity)\n\nexport const isEntityView = isTypeViaConcreteTypeFactory<EntityView, Entity>(\n ENTITY_VIEW_CONCRETE_TYPE_VALUE,\n)\n\n/**\n * @param entityView\n * @returns true iff the viewTypeMask allows files to appear in the view\n */\nexport function hasFilesInView(entityView: EntityView) {\n return (entityView.viewTypeMask & ENTITY_VIEW_TYPE_MASK_FILE) != 0\n}\n\n/**\n * @param entityView\n * @returns true iff the viewTypeMask allows only files to appear in the view\n */\nexport function isFileView(entityView: EntityView) {\n return entityView.viewTypeMask === ENTITY_VIEW_TYPE_MASK_FILE\n}\n\nexport function isVersionableEntity(\n entity: Entity,\n): entity is VersionableEntity {\n return isVersionableEntityType(convertToEntityType(entity.concreteType))\n}\n\nexport function getVersionDisplay(entity: Entity): string {\n if (!isVersionableEntity(entity)) {\n console.warn(\"Entity isn't versionable:\", entity)\n return ''\n }\n\n if (entity.isLatestVersion) {\n if (!isTable(entity)) {\n // e.g. Files. Always show the version number\n return `${entity.versionNumber!.toString()} (Current)`\n } else if (isDataset(entity)) {\n return 'Draft'\n } else {\n return 'Current'\n }\n } else {\n return entity.versionNumber!.toString()\n }\n}\n\n/**\n * Given an entityId, returns the entity ID with the `syn` prefix.\n * If the entity already has the `syn` prefix, the entityId will not be changed\n * @param entityId\n */\nexport function normalizeSynPrefix(entityId: string) {\n if (entityId.toLowerCase().startsWith('syn')) {\n return entityId.toLowerCase()\n }\n return `syn${entityId}`\n}\n\n// implemented by https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/Entity.html\nconst allEntityKeys: string[] = [\n 'name',\n 'description',\n 'id',\n 'etag',\n 'createdOn',\n 'modifiedOn',\n 'createdBy',\n 'modifiedBy',\n 'parentId',\n 'concreteType',\n]\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html\nconst versionableKeys: string[] = [\n ...allEntityKeys,\n 'versionNumber',\n 'versionLabel',\n 'versionComment',\n 'isLatestVersion',\n]\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Table.html\nconst tableKeys: string[] = [...versionableKeys, 'columnIds', 'isSearchEnabled']\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/View.html\nconst viewKeys: string[] = [...tableKeys]\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/EntityRefCollectionView.html\nconst entityRefCollectionViewKeys: string[] = [...viewKeys, 'items']\n\n/**\n * A string array of all possible keys used by Synapse in Entity objects (objects that inherit this interface: https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/Entity.html).\n * This object is used to determine which fields are standard and which are annotations,\n * so it's important that this object contains all keys in the objects that implement the linked interface above.\n */\nexport const entityJsonKeys: Record<ENTITY_CONCRETE_TYPE, string[]> = {\n [LINK_CONCRETE_TYPE_VALUE]: [...allEntityKeys, 'linksTo', 'linksToClassName'],\n [DOCKER_REPOSITORY_CONCRETE_TYPE_VALUE]: [\n ...allEntityKeys,\n 'repositoryName',\n 'isManaged',\n ],\n [FILE_ENTITY_CONCRETE_TYPE_VALUE]: [\n ...versionableKeys,\n 'dataFileHandleId',\n 'fileNameOverride',\n ],\n [SUBMISSION_VIEW_CONCRETE_TYPE_VALUE]: [...viewKeys, 'scopeIds'],\n [DATASET_CONCRETE_TYPE_VALUE]: [\n ...entityRefCollectionViewKeys,\n 'size',\n 'checksum',\n 'count',\n ],\n [DATASET_COLLECTION_CONCRETE_TYPE_VALUE]: [...entityRefCollectionViewKeys],\n [ENTITY_VIEW_CONCRETE_TYPE_VALUE]: [\n ...viewKeys,\n 'scopeIds',\n 'viewTypeMask',\n 'type',\n ],\n [TABLE_ENTITY_CONCRETE_TYPE_VALUE]: tableKeys,\n [MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE]: [...tableKeys, 'definingSQL'],\n [VIRTUAL_TABLE_CONCRETE_TYPE_VALUE]: [...tableKeys, 'definingSQL'],\n [FOLDER_CONCRETE_TYPE_VALUE]: allEntityKeys,\n [PROJECT_CONCRETE_TYPE_VALUE]: [...allEntityKeys, 'alias'],\n ['org.sagebionetworks.repo.model.RecordSet']: [\n ...versionableKeys,\n 'upsertKey',\n 'csvDescriptor',\n ],\n 'org.sagebionetworks.repo.model.Preview': [\n /* unused */\n ],\n 'org.sagebionetworks.repo.model.ExampleEntity': [\n /* unused */\n ],\n}\n\ntype EntityTypeGroupKey = 'ALL_TABLES' | 'CONTAINER'\n\nexport const EntityTypeGroup: Record<EntityTypeGroupKey, EntityType[]> = {\n ['ALL_TABLES']: [\n EntityType.table,\n EntityType.entityview,\n EntityType.submissionview,\n EntityType.dataset,\n EntityType.datasetcollection,\n EntityType.materializedview,\n EntityType.virtualtable,\n ],\n ['CONTAINER']: [EntityType.project, EntityType.folder],\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EntityTypeUtils.js","names":[],"sources":["../../../src/utils/functions/EntityTypeUtils.ts"],"sourcesContent":["import { isTypeViaConcreteTypeFactory } from '@/utils/types/IsType'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n Dataset,\n DATASET_COLLECTION_CONCRETE_TYPE_VALUE,\n DATASET_CONCRETE_TYPE_VALUE,\n DatasetCollection,\n DOCKER_REPOSITORY_CONCRETE_TYPE_VALUE,\n Entity,\n Entity as Entity_OpenAPI,\n ENTITY_CONCRETE_TYPE,\n ENTITY_VIEW_CONCRETE_TYPE_VALUE,\n ENTITY_VIEW_TYPE_MASK_FILE,\n EntityHeader,\n EntityView,\n FILE_ENTITY_CONCRETE_TYPE_VALUE,\n FOLDER_CONCRETE_TYPE_VALUE,\n Hit,\n LINK_CONCRETE_TYPE_VALUE,\n MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE,\n MaterializedView,\n PROJECT_CONCRETE_TYPE_VALUE,\n ProjectHeader,\n SUBMISSION_VIEW_CONCRETE_TYPE_VALUE,\n SubmissionView,\n Table,\n TABLE_CONCRETE_TYPE_VALUES,\n TABLE_ENTITY_CONCRETE_TYPE_VALUE,\n TableEntity,\n VersionableEntity,\n View,\n VIEW_CONCRETE_TYPE_VALUES,\n VIRTUAL_TABLE_CONCRETE_TYPE_VALUE,\n} from '@sage-bionetworks/synapse-types'\n\nexport function getEntityTypeFromHeader(\n header:\n | Pick<EntityHeader, 'name' | 'id' | 'type'>\n | EntityHeader\n | ProjectHeader\n | Hit,\n): EntityType {\n // Hit has the `node_type` field which is what we already want.\n if ((header as Hit).node_type) {\n return (header as Hit).node_type\n }\n // ProjectHeader doesn't have the `type` field, so we can just check that to determine if it's a ProjectHeader\n return (header as EntityHeader).type === undefined\n ? EntityType.project\n : convertToEntityType((header as EntityHeader).type)\n}\n\nexport function isContainerType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n return true\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.file:\n case EntityType.recordset:\n case EntityType.table:\n case EntityType.submissionview:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n case EntityType.materializedview:\n case EntityType.virtualtable:\n case EntityType.searchindex:\n return false\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport function isTableType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.file:\n case EntityType.recordset:\n case EntityType.searchindex:\n return false\n case EntityType.table:\n case EntityType.submissionview:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n case EntityType.materializedview:\n case EntityType.virtualtable:\n return true\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport function entityTypeToFriendlyName(entityType: EntityType): string {\n switch (entityType) {\n case EntityType.project:\n return 'Project'\n case EntityType.folder:\n return 'Folder'\n case EntityType.file:\n return 'File'\n case EntityType.table:\n return 'Table'\n case EntityType.link:\n return 'Link'\n case EntityType.entityview:\n return 'View'\n case EntityType.dockerrepo:\n return 'Docker Repository'\n case EntityType.submissionview:\n return 'Submission View'\n case EntityType.dataset:\n return 'Dataset'\n case EntityType.datasetcollection:\n return 'Dataset Collection'\n case EntityType.materializedview:\n return 'Materialized View'\n case EntityType.virtualtable:\n return 'Virtual Table'\n case EntityType.recordset:\n return 'Record Set'\n case EntityType.searchindex:\n return 'Search Index'\n default:\n console.warn('Entity type could not be mapped to name:', entityType)\n return ''\n }\n}\n\nexport function convertToEntityType(\n typeString: string | ENTITY_CONCRETE_TYPE | EntityType,\n): EntityType {\n if (Object.values(EntityType).includes(typeString as EntityType)) {\n return typeString as EntityType\n }\n switch (typeString) {\n case 'org.sagebionetworks.repo.model.Project':\n return EntityType.project\n case 'org.sagebionetworks.repo.model.Folder':\n return EntityType.folder\n case FILE_ENTITY_CONCRETE_TYPE_VALUE:\n return EntityType.file\n case 'org.sagebionetworks.repo.model.Link':\n return EntityType.link\n case 'org.sagebionetworks.repo.model.docker.DockerRepository':\n return EntityType.dockerrepo\n case TABLE_ENTITY_CONCRETE_TYPE_VALUE:\n return EntityType.table\n case 'org.sagebionetworks.repo.model.table.SubmissionView':\n return EntityType.submissionview\n case ENTITY_VIEW_CONCRETE_TYPE_VALUE:\n return EntityType.entityview\n case DATASET_CONCRETE_TYPE_VALUE:\n return EntityType.dataset\n case DATASET_COLLECTION_CONCRETE_TYPE_VALUE:\n return EntityType.datasetcollection\n case MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE:\n return EntityType.materializedview\n case VIRTUAL_TABLE_CONCRETE_TYPE_VALUE:\n return EntityType.virtualtable\n case 'org.sagebionetworks.repo.model.RecordSet':\n return EntityType.recordset\n case 'org.sagebionetworks.repo.model.search.table.SearchIndex':\n return EntityType.searchindex\n default:\n throw new Error(`Unknown entity type: ${typeString}`)\n }\n}\n\nexport function convertToConcreteEntityType(\n type: EntityType,\n): Entity_OpenAPI['concreteType'] {\n switch (type) {\n case EntityType.project:\n return 'org.sagebionetworks.repo.model.Project'\n case EntityType.folder:\n return 'org.sagebionetworks.repo.model.Folder'\n case EntityType.file:\n return 'org.sagebionetworks.repo.model.FileEntity'\n case EntityType.link:\n return 'org.sagebionetworks.repo.model.Link'\n case EntityType.dockerrepo:\n return 'org.sagebionetworks.repo.model.docker.DockerRepository'\n case EntityType.table:\n return 'org.sagebionetworks.repo.model.table.TableEntity'\n case EntityType.submissionview:\n return 'org.sagebionetworks.repo.model.table.SubmissionView'\n case EntityType.entityview:\n return 'org.sagebionetworks.repo.model.table.EntityView'\n case EntityType.dataset:\n return 'org.sagebionetworks.repo.model.table.Dataset'\n case EntityType.datasetcollection:\n return 'org.sagebionetworks.repo.model.table.DatasetCollection'\n case EntityType.materializedview:\n return 'org.sagebionetworks.repo.model.table.MaterializedView'\n case EntityType.virtualtable:\n return 'org.sagebionetworks.repo.model.table.VirtualTable'\n case EntityType.recordset:\n return 'org.sagebionetworks.repo.model.RecordSet'\n case EntityType.searchindex:\n return 'org.sagebionetworks.repo.model.search.table.SearchIndex'\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\n/**\n * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html\n * @param type\n * @returns\n */\nexport function isVersionableEntityType(type: EntityType): boolean {\n switch (type) {\n case EntityType.project:\n case EntityType.folder:\n case EntityType.link:\n case EntityType.dockerrepo:\n case EntityType.submissionview: // SubmissionView implements VersionableEntity, but versions aren't supported\n case EntityType.materializedview: // MaterializedView implements VersionableEntity, but versions aren't supported.\n case EntityType.virtualtable: // VirtualTable implements VersionableEntity, but versions aren't supported.\n case EntityType.searchindex:\n return false\n case EntityType.file:\n case EntityType.recordset:\n case EntityType.table:\n case EntityType.entityview:\n case EntityType.dataset:\n case EntityType.datasetcollection:\n return true\n default:\n throw new Error(`Unknown entity type: ${type}`)\n }\n}\n\nexport const isTable = isTypeViaConcreteTypeFactory<Table, Entity>(\n ...TABLE_CONCRETE_TYPE_VALUES,\n)\n\nexport const isView = isTypeViaConcreteTypeFactory<View, Entity>(\n ...VIEW_CONCRETE_TYPE_VALUES,\n)\n\nexport const isTableEntity = isTypeViaConcreteTypeFactory<TableEntity, Entity>(\n TABLE_ENTITY_CONCRETE_TYPE_VALUE,\n)\n\nexport const isSubmissionView = isTypeViaConcreteTypeFactory<\n SubmissionView,\n Entity\n>(SUBMISSION_VIEW_CONCRETE_TYPE_VALUE)\n\nexport const isMaterializedView = isTypeViaConcreteTypeFactory<\n MaterializedView,\n Entity\n>(MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE)\n\nexport const isDataset = isTypeViaConcreteTypeFactory<Dataset, Entity>(\n DATASET_CONCRETE_TYPE_VALUE,\n)\nexport const isDatasetCollection = isTypeViaConcreteTypeFactory<\n DatasetCollection,\n Entity\n>(DATASET_COLLECTION_CONCRETE_TYPE_VALUE)\n\nexport const isEntityRefCollectionView = (\n entity: Entity,\n): entity is Dataset | DatasetCollection =>\n isDataset(entity) || isDatasetCollection(entity)\n\nexport const isEntityView = isTypeViaConcreteTypeFactory<EntityView, Entity>(\n ENTITY_VIEW_CONCRETE_TYPE_VALUE,\n)\n\n/**\n * @param entityView\n * @returns true iff the viewTypeMask allows files to appear in the view\n */\nexport function hasFilesInView(entityView: EntityView) {\n return (entityView.viewTypeMask & ENTITY_VIEW_TYPE_MASK_FILE) != 0\n}\n\n/**\n * @param entityView\n * @returns true iff the viewTypeMask allows only files to appear in the view\n */\nexport function isFileView(entityView: EntityView) {\n return entityView.viewTypeMask === ENTITY_VIEW_TYPE_MASK_FILE\n}\n\nexport function isVersionableEntity(\n entity: Entity,\n): entity is VersionableEntity {\n return isVersionableEntityType(convertToEntityType(entity.concreteType))\n}\n\nexport function getVersionDisplay(entity: Entity): string {\n if (!isVersionableEntity(entity)) {\n console.warn(\"Entity isn't versionable:\", entity)\n return ''\n }\n\n if (entity.isLatestVersion) {\n if (!isTable(entity)) {\n // e.g. Files. Always show the version number\n return `${entity.versionNumber!.toString()} (Current)`\n } else if (isDataset(entity)) {\n return 'Draft'\n } else {\n return 'Current'\n }\n } else {\n return entity.versionNumber!.toString()\n }\n}\n\n/**\n * Given an entityId, returns the entity ID with the `syn` prefix.\n * If the entity already has the `syn` prefix, the entityId will not be changed\n * @param entityId\n */\nexport function normalizeSynPrefix(entityId: string) {\n if (entityId.toLowerCase().startsWith('syn')) {\n return entityId.toLowerCase()\n }\n return `syn${entityId}`\n}\n\n// implemented by https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/Entity.html\nconst allEntityKeys: string[] = [\n 'name',\n 'description',\n 'id',\n 'etag',\n 'createdOn',\n 'modifiedOn',\n 'createdBy',\n 'modifiedBy',\n 'parentId',\n 'concreteType',\n]\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html\nconst versionableKeys: string[] = [\n ...allEntityKeys,\n 'versionNumber',\n 'versionLabel',\n 'versionComment',\n 'isLatestVersion',\n]\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Table.html\nconst tableKeys: string[] = [...versionableKeys, 'columnIds', 'isSearchEnabled']\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/View.html\nconst viewKeys: string[] = [...tableKeys]\n\n// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/EntityRefCollectionView.html\nconst entityRefCollectionViewKeys: string[] = [...viewKeys, 'items']\n\n/**\n * A string array of all possible keys used by Synapse in Entity objects (objects that inherit this interface: https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/Entity.html).\n * This object is used to determine which fields are standard and which are annotations,\n * so it's important that this object contains all keys in the objects that implement the linked interface above.\n */\nexport const entityJsonKeys: Record<ENTITY_CONCRETE_TYPE, string[]> = {\n [LINK_CONCRETE_TYPE_VALUE]: [...allEntityKeys, 'linksTo', 'linksToClassName'],\n [DOCKER_REPOSITORY_CONCRETE_TYPE_VALUE]: [\n ...allEntityKeys,\n 'repositoryName',\n 'isManaged',\n ],\n [FILE_ENTITY_CONCRETE_TYPE_VALUE]: [\n ...versionableKeys,\n 'dataFileHandleId',\n 'fileNameOverride',\n ],\n [SUBMISSION_VIEW_CONCRETE_TYPE_VALUE]: [...viewKeys, 'scopeIds'],\n [DATASET_CONCRETE_TYPE_VALUE]: [\n ...entityRefCollectionViewKeys,\n 'size',\n 'checksum',\n 'count',\n ],\n [DATASET_COLLECTION_CONCRETE_TYPE_VALUE]: [...entityRefCollectionViewKeys],\n [ENTITY_VIEW_CONCRETE_TYPE_VALUE]: [\n ...viewKeys,\n 'scopeIds',\n 'viewTypeMask',\n 'type',\n ],\n [TABLE_ENTITY_CONCRETE_TYPE_VALUE]: tableKeys,\n [MATERIALIZED_VIEW_CONCRETE_TYPE_VALUE]: [...tableKeys, 'definingSQL'],\n [VIRTUAL_TABLE_CONCRETE_TYPE_VALUE]: [...tableKeys, 'definingSQL'],\n [FOLDER_CONCRETE_TYPE_VALUE]: allEntityKeys,\n [PROJECT_CONCRETE_TYPE_VALUE]: [...allEntityKeys, 'alias'],\n ['org.sagebionetworks.repo.model.RecordSet']: [\n ...versionableKeys,\n 'upsertKey',\n 'csvDescriptor',\n ],\n 'org.sagebionetworks.repo.model.Preview': [\n /* unused */\n ],\n 'org.sagebionetworks.repo.model.ExampleEntity': [\n /* unused */\n ],\n 'org.sagebionetworks.repo.model.search.table.SearchIndex': [\n ...allEntityKeys,\n 'definingSQL',\n 'searchConfigurationId',\n ],\n}\n\ntype EntityTypeGroupKey = 'ALL_TABLES' | 'CONTAINER'\n\nexport const EntityTypeGroup: Record<EntityTypeGroupKey, EntityType[]> = {\n ['ALL_TABLES']: [\n EntityType.table,\n EntityType.entityview,\n EntityType.submissionview,\n EntityType.dataset,\n EntityType.datasetcollection,\n EntityType.materializedview,\n EntityType.virtualtable,\n ],\n ['CONTAINER']: [EntityType.project, EntityType.folder],\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"GridApiUtils.js","names":[],"sources":["../../../src/utils/functions/GridApiUtils.ts"],"sourcesContent":["import { SynapseClient } from '@sage-bionetworks/synapse-client/SynapseClient'\nimport {\n waitForAsyncResult,\n CreateGridRequest,\n CreateGridResponse,\n} from '@sage-bionetworks/synapse-client'\n\nexport default async function startGridSession(\n synapseClient: SynapseClient,\n request: CreateGridRequest,\n) {\n const asyncJobId =\n await synapseClient.gridServicesClient.postRepoV1GridSessionAsyncStart({\n createGridRequest: request,\n })\n if (!asyncJobId.token) {\n throw new Error('No async job token returned from startGridSession')\n }\n // getRepoV1GridSessionAsyncStart does not return 202 processing jobs as expected\n // so use the getAsynchronousJobJobId endpoint instead\n const asyncJobResponse = await waitForAsyncResult(() =>\n synapseClient.asynchronousJobServicesClient.getRepoV1AsynchronousJobJobId({\n jobId: asyncJobId.token!,\n }),\n )\n return asyncJobResponse.responseBody as CreateGridResponse\n}\n"],"mappings":";;AAOA,eAA8B,EAC5B,GACA,GACA;CACA,IAAM,IACJ,MAAM,EAAc,mBAAmB,gCAAgC,EACrE,mBAAmB,GACpB,CAAC;AACJ,KAAI,CAAC,EAAW,MACd,OAAU,MAAM,oDAAoD;AAStE,SALyB,MAAM,QAC7B,EAAc,8BAA8B,8BAA8B,EACxE,OAAO,EAAW,OACnB,CAAC,CACH,EACuB"}
1
+ {"version":3,"file":"GridApiUtils.js","names":[],"sources":["../../../src/utils/functions/GridApiUtils.ts"],"sourcesContent":["import { SynapseClient } from '@sage-bionetworks/synapse-client/SynapseClient'\nimport {\n waitForAsyncResult,\n CreateGridRequest,\n CreateGridResponse,\n} from '@sage-bionetworks/synapse-client'\n\nexport default async function startGridSession(\n synapseClient: SynapseClient,\n request: CreateGridRequest,\n) {\n const asyncJobId =\n await synapseClient.gridServicesClient.postRepoV1GridSessionAsyncStart({\n createGridRequest: request,\n })\n if (!asyncJobId.token) {\n throw new Error('No async job token returned from startGridSession')\n }\n // getRepoV1GridSessionAsyncStart does not return 202 processing jobs as expected\n // so use the getAsynchronousJobJobId endpoint instead\n const asyncJobResponse = await waitForAsyncResult(() =>\n synapseClient.asynchronousJobServicesClient.getRepoV1AsynchronousJobJobId({\n jobId: asyncJobId.token!,\n }),\n )\n return asyncJobResponse.responseBody as CreateGridResponse\n}\n"],"mappings":";;AAOA,eAA8B,EAC5B,GACA,GACA;CACA,IAAM,IACJ,MAAM,EAAc,mBAAmB,gCAAgC,EACrE,mBAAmB,GACpB,CAAC;AACJ,KAAI,CAAC,EAAW,MACd,OAAU,MAAM,oDAAoD;AAStE,SAAO,MALwB,QAC7B,EAAc,8BAA8B,8BAA8B,EACxE,OAAO,EAAW,OACnB,CAAC,CACH,EACuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"QueryFilterUtils.js","names":[],"sources":["../../../src/utils/functions/QueryFilterUtils.ts"],"sourcesContent":["import {\n ColumnSingleValueFilterOperator,\n ColumnSingleValueQueryFilter,\n QueryFilter,\n Row,\n SelectColumn,\n} from '@sage-bionetworks/synapse-types'\n\n/**\n * Given a list of selected rows, return a QueryFilter that will only return those rows\n * @param primaryKeyColumnNames\n * @param selectedRows\n * @param selectColumns\n */\nexport function getPrimaryKeyINFilter(\n primaryKeyColumnNames: string[],\n selectedRows: Row[],\n selectColumns: SelectColumn[],\n): QueryFilter {\n if (!primaryKeyColumnNames || primaryKeyColumnNames.length !== 1) {\n // If the key is undefined, then the user should have never been able to apply a filter\n // TODO: Handling a composite key would be tricky since the QueryFilter API currently only allows you to specify one column\n throw new Error('rowSelectionPrimaryKey must be defined and have length 1')\n }\n // Apply a filter that should only return the selected rows\n const primaryKeyColumnIndex = selectColumns.findIndex(\n sc => sc.name === primaryKeyColumnNames[0],\n )\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: primaryKeyColumnNames[0],\n operator: ColumnSingleValueFilterOperator.IN,\n values: selectedRows.map(row => row.values[primaryKeyColumnIndex]!),\n }\n return filter\n}\n"],"mappings":";;AAcA,SAAgB,EACd,GACA,GACA,GACa;AACb,KAAI,CAAC,KAAyB,EAAsB,WAAW,EAG7D,OAAU,MAAM,2DAA2D;CAG7E,IAAM,IAAwB,EAAc,WAC1C,MAAM,EAAG,SAAS,EAAsB,GACzC;AAQD,QAP6C;EAC3C,cACE;EACF,YAAY,EAAsB;EAClC,UAAU,EAAgC;EAC1C,QAAQ,EAAa,KAAI,MAAO,EAAI,OAAO,GAAwB;EACpE"}
1
+ {"version":3,"file":"QueryFilterUtils.js","names":[],"sources":["../../../src/utils/functions/QueryFilterUtils.ts"],"sourcesContent":["import {\n ColumnSingleValueFilterOperator,\n ColumnSingleValueQueryFilter,\n QueryFilter,\n Row,\n SelectColumn,\n} from '@sage-bionetworks/synapse-types'\n\n/**\n * Given a list of selected rows, return a QueryFilter that will only return those rows\n * @param primaryKeyColumnNames\n * @param selectedRows\n * @param selectColumns\n */\nexport function getPrimaryKeyINFilter(\n primaryKeyColumnNames: string[],\n selectedRows: Row[],\n selectColumns: SelectColumn[],\n): QueryFilter {\n if (!primaryKeyColumnNames || primaryKeyColumnNames.length !== 1) {\n // If the key is undefined, then the user should have never been able to apply a filter\n // TODO: Handling a composite key would be tricky since the QueryFilter API currently only allows you to specify one column\n throw new Error('rowSelectionPrimaryKey must be defined and have length 1')\n }\n // Apply a filter that should only return the selected rows\n const primaryKeyColumnIndex = selectColumns.findIndex(\n sc => sc.name === primaryKeyColumnNames[0],\n )\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: primaryKeyColumnNames[0],\n operator: ColumnSingleValueFilterOperator.IN,\n values: selectedRows.map(row => row.values[primaryKeyColumnIndex]!),\n }\n return filter\n}\n"],"mappings":";;AAcA,SAAgB,EACd,GACA,GACA,GACa;AACb,KAAI,CAAC,KAAyB,EAAsB,WAAW,EAG7D,OAAU,MAAM,2DAA2D;CAG7E,IAAM,IAAwB,EAAc,WAC1C,MAAM,EAAG,SAAS,EAAsB,GACzC;AAQD,QAAO;EANL,cACE;EACF,YAAY,EAAsB;EAClC,UAAU,EAAgC;EAC1C,QAAQ,EAAa,KAAI,MAAO,EAAI,OAAO,GAAwB;EAE9D"}
@@ -3,4 +3,8 @@ import { Realm } from '@sage-bionetworks/synapse-client';
3
3
  * Check if a realm has the ARCUS_BIOSCIENCES identity provider
4
4
  */
5
5
  export declare function hasArcusProvider(realm?: Realm): boolean;
6
+ /**
7
+ * Check if a realm has the SAGE_BIONETWORKS identity provider
8
+ */
9
+ export declare function hasSageBionetworksProvider(realm?: Realm): boolean;
6
10
  //# sourceMappingURL=RealmUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RealmUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/functions/RealmUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,EACN,MAAM,kCAAkC,CAAA;AAEzC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAOvD"}
1
+ {"version":3,"file":"RealmUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/functions/RealmUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,EACN,MAAM,kCAAkC,CAAA;AAEzC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAEvD;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAEjE"}
@@ -1,9 +1,15 @@
1
1
  import { OAuthIdentityProviderProviderEnum as e, instanceOfOAuthIdentityProvider as t } from "@sage-bionetworks/synapse-client";
2
2
  //#region src/utils/functions/RealmUtils.ts
3
- function n(n) {
4
- return n?.identityProvider ? n.identityProvider.some((n) => t(n) && n.provider === e.ARCUS_BIOSCIENCES) : !1;
3
+ function n(t) {
4
+ return i(t, e.ARCUS_BIOSCIENCES);
5
+ }
6
+ function r(t) {
7
+ return i(t, e.SAGE_BIONETWORKS);
8
+ }
9
+ function i(e, n) {
10
+ return e?.identityProvider ? e.identityProvider.some((e) => t(e) && e.provider === n) : !1;
5
11
  }
6
12
  //#endregion
7
- export { n as hasArcusProvider };
13
+ export { n as hasArcusProvider, r as hasSageBionetworksProvider };
8
14
 
9
15
  //# sourceMappingURL=RealmUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RealmUtils.js","names":[],"sources":["../../../src/utils/functions/RealmUtils.ts"],"sourcesContent":["import {\n instanceOfOAuthIdentityProvider,\n OAuthIdentityProviderProviderEnum,\n Realm,\n} from '@sage-bionetworks/synapse-client'\n\n/**\n * Check if a realm has the ARCUS_BIOSCIENCES identity provider\n */\nexport function hasArcusProvider(realm?: Realm): boolean {\n if (!realm?.identityProvider) return false\n return realm.identityProvider.some(\n (provider: any) =>\n instanceOfOAuthIdentityProvider(provider) &&\n provider.provider === OAuthIdentityProviderProviderEnum.ARCUS_BIOSCIENCES,\n )\n}\n"],"mappings":";;AASA,SAAgB,EAAiB,GAAwB;AAEvD,QADK,GAAO,mBACL,EAAM,iBAAiB,MAC3B,MACC,EAAgC,EAAS,IACzC,EAAS,aAAa,EAAkC,kBAC3D,GALoC"}
1
+ {"version":3,"file":"RealmUtils.js","names":[],"sources":["../../../src/utils/functions/RealmUtils.ts"],"sourcesContent":["import {\n instanceOfOAuthIdentityProvider,\n OAuthIdentityProviderProviderEnum,\n Realm,\n} from '@sage-bionetworks/synapse-client'\n\n/**\n * Check if a realm has the ARCUS_BIOSCIENCES identity provider\n */\nexport function hasArcusProvider(realm?: Realm): boolean {\n return hasProvider(realm, OAuthIdentityProviderProviderEnum.ARCUS_BIOSCIENCES)\n}\n\n/**\n * Check if a realm has the SAGE_BIONETWORKS identity provider\n */\nexport function hasSageBionetworksProvider(realm?: Realm): boolean {\n return hasProvider(realm, OAuthIdentityProviderProviderEnum.SAGE_BIONETWORKS)\n}\n\nfunction hasProvider(\n realm?: Realm,\n providerEnum?: OAuthIdentityProviderProviderEnum,\n): boolean {\n if (!realm?.identityProvider) return false\n return realm.identityProvider.some(\n (provider: any) =>\n instanceOfOAuthIdentityProvider(provider) &&\n provider.provider === providerEnum,\n )\n}\n"],"mappings":";;AASA,SAAgB,EAAiB,GAAwB;AACvD,QAAO,EAAY,GAAO,EAAkC,kBAAkB;;AAMhF,SAAgB,EAA2B,GAAwB;AACjE,QAAO,EAAY,GAAO,EAAkC,iBAAiB;;AAG/E,SAAS,EACP,GACA,GACS;AAET,QADK,GAAO,mBACL,EAAM,iBAAiB,MAC3B,MACC,EAAgC,EAAS,IACzC,EAAS,aAAa,EACzB,GALoC"}
@@ -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":"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"}
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":"SanitizeHtmlUtils.test-utils.js","names":[],"sources":["../../../src/utils/functions/SanitizeHtmlUtils.test-utils.ts"],"sourcesContent":["/**\n * Shared test factory for SanitizeHtmlUtils.\n * Imported by both the CSR (jsdom) and SSR (node) test files so test cases\n * are defined once and run in both environments.\n *\n * Uses explicit JSDOM for HTML parsing so the helpers work regardless of\n * whether a global `document` is available.\n */\nimport xss from 'xss'\nimport { JSDOM } from 'jsdom'\nimport { sanitize, xssOptions } from './SanitizeHtmlUtils'\n\nfunction parseHTML(html: string) {\n return new JSDOM(html).window.document.documentElement\n}\n\nexport function runDomPurifyTests() {\n describe('HTML Sanitization - DOMPurify', () => {\n test('Prevents XSS through onload', () => {\n const sanitized = sanitize(\"<span onload='alert('XSS')'>foo</span>\")\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onload')).toBeNull()\n })\n test('Prevents XSS through onclick', () => {\n const sanitized = sanitize(\"<span onclick='alert('XSS')'>foo</span>\")\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onclick')).toBeNull()\n })\n test('Prevents XSS through href', () => {\n const script = 'javascript:alert(1)'\n const sanitized = sanitize(`<a href=\"${script}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n // Verify script was removed\n expect(anchor.getAttribute('href')).toBeNull()\n })\n test('Allows valid link href', () => {\n const validHref = 'https://synapse.org'\n const sanitized = sanitize(`<a href=\"${validHref}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(validHref)\n })\n test('Allows link rel property', () => {\n const href = 'https://synapse.org'\n const rel = 'noopener noreferrer'\n const sanitized = sanitize(`<a href=\"${href}\" rel=\"${rel}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(href)\n expect(anchor.getAttribute('rel')).toEqual(rel)\n })\n\n describe.each(['td', 'th'])('%s cell text alignment', tag => {\n test(`Allows ${tag} with text-align style`, () => {\n const input = `<table><tbody><tr><${tag} style=\"text-align:center\">foo</${tag}></tr></tbody></table>`\n const expected = input\n const sanitized = sanitize(input)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n\n test(`Removes styles other than text-align from ${tag}`, () => {\n // Some other style is applied, which is not allowed (may be a click-jacking attempt!)\n const input = `<table><tbody><tr><${tag} style=\"text-align:center; position: absolute;\">foo</${tag}></tr></tbody></table>`\n // position is not an allowed CSS property\n const expected = `<table><tbody><tr><${tag} style=\"text-align:center\">foo</${tag}></tr></tbody></table>`\n const sanitized = sanitize(input)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n })\n })\n}\n\nexport function runXssTests() {\n describe('HTML Sanitization - xss (legacy)', () => {\n test('Prevents XSS through onload', () => {\n const sanitized = xss(\n \"<span onload='alert('XSS')'>foo</span>\",\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onload')).toBeNull()\n })\n test('Prevents XSS through onclick', () => {\n const sanitized = xss(\n \"<span onclick='alert('XSS')'>foo</span>\",\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onclick')).toBeNull()\n })\n test('Prevents XSS through href', () => {\n const script = 'javascript:alert(1)'\n const sanitized = xss(`<a href=\"${script}\">foo</a>`, xssOptions)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n // Verify script was removed\n expect(anchor.getAttribute('href')).toEqual('')\n })\n test('Allows valid link href', () => {\n const validHref = 'https://synapse.org'\n const sanitized = xss(`<a href=\"${validHref}\">foo</a>`, xssOptions)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(validHref)\n })\n test('Allows link rel property', () => {\n const href = 'https://synapse.org'\n const rel = 'noopener noreferrer'\n const sanitized = xss(\n `<a href=\"${href}\" rel=\"${rel}\">foo</a>`,\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(href)\n expect(anchor.getAttribute('rel')).toEqual(rel)\n })\n\n describe.each(['td', 'th'])('%s cell text alignment', tag => {\n test(`Allows ${tag} with text-align style`, () => {\n const input = `<table><tbody><tr><${tag} style=\"text-align:center;\">foo</${tag}></tr></tbody></table>`\n const expected = input\n const sanitized = xss(input, xssOptions)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n\n test(`Removes styles other than text-align from ${tag}`, () => {\n // Some other style is applied, which is not allowed (may be a click-jacking attempt!)\n const input = `<table><tbody><tr><${tag} style=\"text-align:center; position: absolute;\">foo</${tag}></tr></tbody></table>`\n // position is not an allowed CSS property\n const expected = `<table><tbody><tr><${tag} style=\"text-align:center;\">foo</${tag}></tr></tbody></table>`\n const sanitized = xss(input, xssOptions)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n })\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SanitizeHtmlUtils.test-utils.js","names":[],"sources":["../../../src/utils/functions/SanitizeHtmlUtils.test-utils.ts"],"sourcesContent":["/**\n * Shared test factory for SanitizeHtmlUtils.\n * Imported by both the CSR (jsdom) and SSR (node) test files so test cases\n * are defined once and run in both environments.\n *\n * Uses explicit JSDOM for HTML parsing so the helpers work regardless of\n * whether a global `document` is available.\n */\nimport xss from 'xss'\nimport { JSDOM } from 'jsdom'\nimport { sanitize, xssOptions } from './SanitizeHtmlUtils'\n\nfunction parseHTML(html: string) {\n return new JSDOM(html).window.document.documentElement\n}\n\nexport function runDomPurifyTests() {\n describe('HTML Sanitization - DOMPurify', () => {\n test('Prevents XSS through onload', () => {\n const sanitized = sanitize(\"<span onload='alert('XSS')'>foo</span>\")\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onload')).toBeNull()\n })\n test('Prevents XSS through onclick', () => {\n const sanitized = sanitize(\"<span onclick='alert('XSS')'>foo</span>\")\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onclick')).toBeNull()\n })\n test('Prevents XSS through href', () => {\n const script = 'javascript:alert(1)'\n const sanitized = sanitize(`<a href=\"${script}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n // Verify script was removed\n expect(anchor.getAttribute('href')).toBeNull()\n })\n test('Allows valid link href', () => {\n const validHref = 'https://synapse.org'\n const sanitized = sanitize(`<a href=\"${validHref}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(validHref)\n })\n test('Allows link rel property', () => {\n const href = 'https://synapse.org'\n const rel = 'noopener noreferrer'\n const sanitized = sanitize(`<a href=\"${href}\" rel=\"${rel}\">foo</a>`)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(href)\n expect(anchor.getAttribute('rel')).toEqual(rel)\n })\n\n describe.each(['td', 'th'])('%s cell text alignment', tag => {\n test(`Allows ${tag} with text-align style`, () => {\n const input = `<table><tbody><tr><${tag} style=\"text-align:center\">foo</${tag}></tr></tbody></table>`\n const expected = input\n const sanitized = sanitize(input)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n\n test(`Removes styles other than text-align from ${tag}`, () => {\n // Some other style is applied, which is not allowed (may be a click-jacking attempt!)\n const input = `<table><tbody><tr><${tag} style=\"text-align:center; position: absolute;\">foo</${tag}></tr></tbody></table>`\n // position is not an allowed CSS property\n const expected = `<table><tbody><tr><${tag} style=\"text-align:center\">foo</${tag}></tr></tbody></table>`\n const sanitized = sanitize(input)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n })\n })\n}\n\nexport function runXssTests() {\n describe('HTML Sanitization - xss (legacy)', () => {\n test('Prevents XSS through onload', () => {\n const sanitized = xss(\n \"<span onload='alert('XSS')'>foo</span>\",\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onload')).toBeNull()\n })\n test('Prevents XSS through onclick', () => {\n const sanitized = xss(\n \"<span onclick='alert('XSS')'>foo</span>\",\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const span = html.querySelector('span')!\n expect(span).not.toBeNull()\n expect(span.getAttribute('onclick')).toBeNull()\n })\n test('Prevents XSS through href', () => {\n const script = 'javascript:alert(1)'\n const sanitized = xss(`<a href=\"${script}\">foo</a>`, xssOptions)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n // Verify script was removed\n expect(anchor.getAttribute('href')).toEqual('')\n })\n test('Allows valid link href', () => {\n const validHref = 'https://synapse.org'\n const sanitized = xss(`<a href=\"${validHref}\">foo</a>`, xssOptions)\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(validHref)\n })\n test('Allows link rel property', () => {\n const href = 'https://synapse.org'\n const rel = 'noopener noreferrer'\n const sanitized = xss(\n `<a href=\"${href}\" rel=\"${rel}\">foo</a>`,\n xssOptions,\n )\n const html = parseHTML(sanitized)\n const anchor = html.querySelector('a')!\n expect(anchor).not.toBeNull()\n expect(anchor.getAttribute('href')).toEqual(href)\n expect(anchor.getAttribute('rel')).toEqual(rel)\n })\n\n describe.each(['td', 'th'])('%s cell text alignment', tag => {\n test(`Allows ${tag} with text-align style`, () => {\n const input = `<table><tbody><tr><${tag} style=\"text-align:center;\">foo</${tag}></tr></tbody></table>`\n const expected = input\n const sanitized = xss(input, xssOptions)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n\n test(`Removes styles other than text-align from ${tag}`, () => {\n // Some other style is applied, which is not allowed (may be a click-jacking attempt!)\n const input = `<table><tbody><tr><${tag} style=\"text-align:center; position: absolute;\">foo</${tag}></tr></tbody></table>`\n // position is not an allowed CSS property\n const expected = `<table><tbody><tr><${tag} style=\"text-align:center;\">foo</${tag}></tr></tbody></table>`\n const sanitized = xss(input, xssOptions)\n\n // style should be allowed in this case\n expect(sanitized).toEqual(expected)\n })\n })\n })\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SqlFunctions.js","names":[],"sources":["../../../src/utils/functions/SqlFunctions.ts"],"sourcesContent":["import {\n ColumnModel,\n ColumnMultiValueFunction,\n ColumnMultiValueFunctionQueryFilter,\n ColumnSingleValueFilterOperator,\n ColumnSingleValueQueryFilter,\n QueryFilter,\n Row,\n SelectColumn,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { SYNAPSE_ENTITY_ID_REGEX } from './RegularExpressions'\nimport { splitAndTrim } from './StringUtils'\n\nexport type SQLOperator =\n | ColumnSingleValueFilterOperator\n | ColumnMultiValueFunction\n\nconst WITHOUT_SYN_PREFIX = 3\nexport const QUERY_FILTERS_SESSION_STORAGE_KEY = (key: string) =>\n `${key}-temp-QueryFilter-array`\n\nexport function removePrefixIfSynId(value: string) {\n if (value.match(SYNAPSE_ENTITY_ID_REGEX)) {\n return value.substring(WITHOUT_SYN_PREFIX)\n }\n return value\n}\nexport const getIgnoredQueryFilterSearchParamKey = (\n key: string,\n namespace?: string,\n) => {\n return `__${namespace ?? ''}_${key}`\n}\n\n// Special search parameter key that will automatically apply a FTS search term to a Query Wrapper if present\nexport const SEARCH_TERM = 'SEARCH_TERM'\nexport const SEARCH_ROLE = 'SEARCH_ROLE'\n\n/** @deprecated Use SEARCH_TERM instead */\nexport const FTS_SEARCH_TERM = 'FTS_SEARCH_TERM'\n/** @deprecated Use SEARCH_ROLE instead */\nexport const FTS_SEARCH_ROLE = 'FTS_SEARCH_ROLE'\n\n/**\n * Look in local storage for a set of QueryFilters to apply. In addition, given the search params,\n * generate a set of QueryFilters to narrow the query to view just related data.\n * May return null if a QueryFilter should not be added.\n * @param sql\n * @param searchParams\n * @param operator\n * @returns\n */\nexport const getAdditionalFilters = (\n searchParams?: Record<string, string>,\n operator: SQLOperator = ColumnSingleValueFilterOperator.LIKE,\n sessionStorageKey?: string,\n): QueryFilter[] | undefined => {\n const sessionStorageQueryFiltersString = sessionStorageKey\n ? sessionStorage.getItem(\n QUERY_FILTERS_SESSION_STORAGE_KEY(sessionStorageKey),\n )\n : undefined\n let additionalFilters: QueryFilter[] = []\n if (sessionStorageQueryFiltersString) {\n additionalFilters = JSON.parse(\n sessionStorageQueryFiltersString,\n ) as QueryFilter[]\n }\n if (searchParams) {\n const isQueryWrapperKey = (key: string) =>\n key.startsWith('qw') || key.startsWith('__')\n additionalFilters = additionalFilters.concat(\n Object.keys(searchParams || {})\n .filter(\n key =>\n !isQueryWrapperKey(key) &&\n searchParams[key] != undefined &&\n searchParams[key].trim() != '',\n )\n .map(key => {\n if (key == SEARCH_TERM || key == FTS_SEARCH_TERM) {\n const filter: TextMatchesQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.TextMatchesQueryFilter',\n searchExpression: searchParams[key],\n }\n return filter\n }\n switch (operator) {\n case ColumnSingleValueFilterOperator.EQUAL: {\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n values: [searchParams[key]],\n }\n return filter\n }\n case ColumnSingleValueFilterOperator.IN: {\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n values: splitAndTrim(searchParams[key]),\n }\n return filter\n }\n case ColumnMultiValueFunction.HAS: {\n const filter: ColumnMultiValueFunctionQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n columnName: key,\n function: operator,\n _function: operator,\n values: splitAndTrim(searchParams[key]),\n }\n return filter\n }\n case ColumnSingleValueFilterOperator.LIKE: {\n // If we use a LIKE statement with a synId the backend will look for a string with the first three\n // characters being 'syn', however, it stores synIds without 'syn', so the query will fail\n // The backend usually parses 'syn' out, but not with the LIKE clause since its expecting a regex, so we\n // parse this out. This will cause a bug if something matches the synId regex but is in free text.\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n // Add wildcards around the value\n values: [`%${removePrefixIfSynId(searchParams[key])}%`],\n }\n return filter\n }\n case ColumnMultiValueFunction.HAS_LIKE: {\n const filter: ColumnMultiValueFunctionQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n columnName: key,\n function: operator,\n _function: operator,\n values: searchParams[key].split(',').map(param => {\n // Remove synId prefix for the same reasons as in the LIKE case\n return `%${removePrefixIfSynId(param)}%`\n }),\n }\n return filter\n }\n }\n }),\n )\n }\n\n return additionalFilters.length === 0 ? undefined : additionalFilters\n}\n\n//parses synapse entity id from a sql query string\n//look for a pattern of 'from[some number of spaces]syn[somenumbers]` case insensitive\nexport const parseEntityIdFromSqlStatement = (sql: string): string => {\n const matches = sql.match(/(from)\\s+(syn)\\d+/gi)\n return matches && matches[0] ? matches[0].substr(5).trim() : ''\n}\n\nexport const parseEntityIdAndVersionFromSqlStatement = (\n sql: string,\n): { entityId?: string; versionNumber?: number } => {\n const regex = /from\\s+(syn\\d+)(?:\\.(\\d+))?/i\n const matches = regex.exec(sql)\n if (!matches) {\n return { entityId: undefined, versionNumber: undefined }\n }\n return {\n entityId: matches[1],\n versionNumber: matches[2] ? parseInt(matches[2]) : undefined,\n }\n}\n\nexport const resultToJson = (\n headerColumns: SelectColumn[],\n rowColumns: Row[],\n) => {\n const rows = rowColumns.map(row => row.values)\n const headers = headerColumns.map(column => column.name)\n return rows.map(row => {\n const obj: Record<string, string | null> = {}\n row.forEach((text, cellIndex) => {\n obj[headers[cellIndex]] = text\n })\n return obj\n })\n}\n\n/**\n * Finds the index of the given column name in the selectColumns or columnModels.\n *\n * @returns a non-negative integer if the column is found, or undefined if the column is not found\n * @param columnName\n * @param selectColumns\n * @param columnModels\n */\nexport const getColumnIndex = (\n columnName?: string,\n selectColumns?: SelectColumn[],\n columnModels?: ColumnModel[],\n): number | undefined => {\n if (!columnName) {\n return undefined\n }\n\n const lowerCaseColumnName = columnName.toLowerCase()\n\n if (selectColumns) {\n const findIndexResult = selectColumns.findIndex(\n el => el.name?.toLowerCase() === lowerCaseColumnName,\n )\n if (findIndexResult != -1) {\n return findIndexResult\n }\n }\n\n if (columnModels) {\n const findIndexResult = columnModels.findIndex(\n el => el.name?.toLowerCase() === lowerCaseColumnName,\n )\n if (findIndexResult !== -1) {\n return findIndexResult\n }\n }\n\n return undefined\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SqlFunctions.js","names":[],"sources":["../../../src/utils/functions/SqlFunctions.ts"],"sourcesContent":["import {\n ColumnModel,\n ColumnMultiValueFunction,\n ColumnMultiValueFunctionQueryFilter,\n ColumnSingleValueFilterOperator,\n ColumnSingleValueQueryFilter,\n QueryFilter,\n Row,\n SelectColumn,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { SYNAPSE_ENTITY_ID_REGEX } from './RegularExpressions'\nimport { splitAndTrim } from './StringUtils'\n\nexport type SQLOperator =\n | ColumnSingleValueFilterOperator\n | ColumnMultiValueFunction\n\nconst WITHOUT_SYN_PREFIX = 3\nexport const QUERY_FILTERS_SESSION_STORAGE_KEY = (key: string) =>\n `${key}-temp-QueryFilter-array`\n\nexport function removePrefixIfSynId(value: string) {\n if (value.match(SYNAPSE_ENTITY_ID_REGEX)) {\n return value.substring(WITHOUT_SYN_PREFIX)\n }\n return value\n}\nexport const getIgnoredQueryFilterSearchParamKey = (\n key: string,\n namespace?: string,\n) => {\n return `__${namespace ?? ''}_${key}`\n}\n\n// Special search parameter key that will automatically apply a FTS search term to a Query Wrapper if present\nexport const SEARCH_TERM = 'SEARCH_TERM'\nexport const SEARCH_ROLE = 'SEARCH_ROLE'\n\n/** @deprecated Use SEARCH_TERM instead */\nexport const FTS_SEARCH_TERM = 'FTS_SEARCH_TERM'\n/** @deprecated Use SEARCH_ROLE instead */\nexport const FTS_SEARCH_ROLE = 'FTS_SEARCH_ROLE'\n\n/**\n * Look in local storage for a set of QueryFilters to apply. In addition, given the search params,\n * generate a set of QueryFilters to narrow the query to view just related data.\n * May return null if a QueryFilter should not be added.\n * @param sql\n * @param searchParams\n * @param operator\n * @returns\n */\nexport const getAdditionalFilters = (\n searchParams?: Record<string, string>,\n operator: SQLOperator = ColumnSingleValueFilterOperator.LIKE,\n sessionStorageKey?: string,\n): QueryFilter[] | undefined => {\n const sessionStorageQueryFiltersString = sessionStorageKey\n ? sessionStorage.getItem(\n QUERY_FILTERS_SESSION_STORAGE_KEY(sessionStorageKey),\n )\n : undefined\n let additionalFilters: QueryFilter[] = []\n if (sessionStorageQueryFiltersString) {\n additionalFilters = JSON.parse(\n sessionStorageQueryFiltersString,\n ) as QueryFilter[]\n }\n if (searchParams) {\n const isQueryWrapperKey = (key: string) =>\n key.startsWith('qw') || key.startsWith('__')\n additionalFilters = additionalFilters.concat(\n Object.keys(searchParams || {})\n .filter(\n key =>\n !isQueryWrapperKey(key) &&\n searchParams[key] != undefined &&\n searchParams[key].trim() != '',\n )\n .map(key => {\n if (key == SEARCH_TERM || key == FTS_SEARCH_TERM) {\n const filter: TextMatchesQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.TextMatchesQueryFilter',\n searchExpression: searchParams[key],\n }\n return filter\n }\n switch (operator) {\n case ColumnSingleValueFilterOperator.EQUAL: {\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n values: [searchParams[key]],\n }\n return filter\n }\n case ColumnSingleValueFilterOperator.IN: {\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n values: splitAndTrim(searchParams[key]),\n }\n return filter\n }\n case ColumnMultiValueFunction.HAS: {\n const filter: ColumnMultiValueFunctionQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n columnName: key,\n function: operator,\n _function: operator,\n values: splitAndTrim(searchParams[key]),\n }\n return filter\n }\n case ColumnSingleValueFilterOperator.LIKE: {\n // If we use a LIKE statement with a synId the backend will look for a string with the first three\n // characters being 'syn', however, it stores synIds without 'syn', so the query will fail\n // The backend usually parses 'syn' out, but not with the LIKE clause since its expecting a regex, so we\n // parse this out. This will cause a bug if something matches the synId regex but is in free text.\n const filter: ColumnSingleValueQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnSingleValueQueryFilter',\n columnName: key,\n operator: operator,\n // Add wildcards around the value\n values: [`%${removePrefixIfSynId(searchParams[key])}%`],\n }\n return filter\n }\n case ColumnMultiValueFunction.HAS_LIKE: {\n const filter: ColumnMultiValueFunctionQueryFilter = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n columnName: key,\n function: operator,\n _function: operator,\n values: searchParams[key].split(',').map(param => {\n // Remove synId prefix for the same reasons as in the LIKE case\n return `%${removePrefixIfSynId(param)}%`\n }),\n }\n return filter\n }\n }\n }),\n )\n }\n\n return additionalFilters.length === 0 ? undefined : additionalFilters\n}\n\n//parses synapse entity id from a sql query string\n//look for a pattern of 'from[some number of spaces]syn[somenumbers]` case insensitive\nexport const parseEntityIdFromSqlStatement = (sql: string): string => {\n const matches = sql.match(/(from)\\s+(syn)\\d+/gi)\n return matches && matches[0] ? matches[0].substr(5).trim() : ''\n}\n\nexport const parseEntityIdAndVersionFromSqlStatement = (\n sql: string,\n): { entityId?: string; versionNumber?: number } => {\n const regex = /from\\s+(syn\\d+)(?:\\.(\\d+))?/i\n const matches = regex.exec(sql)\n if (!matches) {\n return { entityId: undefined, versionNumber: undefined }\n }\n return {\n entityId: matches[1],\n versionNumber: matches[2] ? parseInt(matches[2]) : undefined,\n }\n}\n\nexport const resultToJson = (\n headerColumns: SelectColumn[],\n rowColumns: Row[],\n) => {\n const rows = rowColumns.map(row => row.values)\n const headers = headerColumns.map(column => column.name)\n return rows.map(row => {\n const obj: Record<string, string | null> = {}\n row.forEach((text, cellIndex) => {\n obj[headers[cellIndex]] = text\n })\n return obj\n })\n}\n\n/**\n * Finds the index of the given column name in the selectColumns or columnModels.\n *\n * @returns a non-negative integer if the column is found, or undefined if the column is not found\n * @param columnName\n * @param selectColumns\n * @param columnModels\n */\nexport const getColumnIndex = (\n columnName?: string,\n selectColumns?: SelectColumn[],\n columnModels?: ColumnModel[],\n): number | undefined => {\n if (!columnName) {\n return undefined\n }\n\n const lowerCaseColumnName = columnName.toLowerCase()\n\n if (selectColumns) {\n const findIndexResult = selectColumns.findIndex(\n el => el.name?.toLowerCase() === lowerCaseColumnName,\n )\n if (findIndexResult != -1) {\n return findIndexResult\n }\n }\n\n if (columnModels) {\n const findIndexResult = columnModels.findIndex(\n el => el.name?.toLowerCase() === lowerCaseColumnName,\n )\n if (findIndexResult !== -1) {\n return findIndexResult\n }\n }\n\n return undefined\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"StringUtils.js","names":[],"sources":["../../../src/utils/functions/StringUtils.ts"],"sourcesContent":["import { parseSynId, SYNAPSE_ENTITY_ID_REGEX } from './RegularExpressions'\n\n/**\n * Truncates a string to be a specified length, then optionally append a suffix.\n */\nexport function truncateString(str: string, maxLength: number, suffix = '…') {\n if (str.length <= maxLength) {\n return str\n }\n return str.substring(0, maxLength) + suffix\n}\n\nexport function hex2ascii(inputString: string): string {\n const hex: string = inputString.toString()\n let str: string = ''\n for (let n = 0; n < hex.length; n += 2) {\n str += String.fromCharCode(parseInt(hex.substring(n, n + 2), 16))\n }\n return str\n}\n\n/**\n * Returns a hash code from a string. Uses Java's String.hashCode() algorithm: https://devdocs.io/openjdk~8/java/lang/string#hashCode--\n * @param {String} str The string to hash.\n * @return {Number} A 32bit integer\n * @see https://stackoverflow.com/a/8831937\n */\nexport function hashCode(str?: string | null) {\n if (str == null) {\n str = ''\n }\n let hash = 0\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i)\n hash = (hash << 5) - hash + chr\n hash |= 0 // Convert to 32bit integer\n }\n return hash\n}\n\nexport function normalizeNumericId(id: string | number): number {\n if (id == null) {\n return NaN\n }\n if (typeof id === 'number') {\n return id\n }\n if (id.match(SYNAPSE_ENTITY_ID_REGEX)) {\n // parse the ID to remove a possible version suffix ('syn123.4' -> 'syn123')\n const entityId = parseSynId(id)!.targetId\n return parseInt(entityId.substring('syn'.length))\n } else {\n return parseInt(id)\n }\n}\n\n// Used for client-side validation. Note, the server has the final say. Integration tested in ChangePassword.integration.test.tsx\n// See https://github.com/Sage-Bionetworks/Synapse-Repository-Services/blob/develop/services/repository-managers/src/main/java/org/sagebionetworks/repo/manager/password/PasswordValidatorImpl.java#L47 for rules\nexport function validatePassword(newPassword: string) {\n if (newPassword.trim().length < 8) {\n return 'A valid password must be at least 8 characters long'\n }\n const hasLetter = /[a-zA-Z]/.test(newPassword) // Checks for at least one letter\n if (!hasLetter) {\n return 'A valid password must include letters'\n }\n\n const hasNumber = /\\d/.test(newPassword) // Checks for at least one number\n if (!hasNumber) {\n return 'A valid password must include digits (0-9)'\n }\n\n const hasSpecialChar = /[~!@#$%^&*_\\-+=`|\\\\(){}[\\]:;\"'<>,.?/]/.test(\n newPassword,\n ) // Checks for at least one special character\n if (!hasSpecialChar) {\n return 'A valid password must include special characters ~!@#$%^&*_-+=`|\\\\(){}[]:;\"\\'<>,.?/'\n }\n return undefined\n}\n\nexport function copyStringToClipboard(value: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // PORTALS-3571: setTimeout necessary on Safari\n setTimeout(() => {\n // use the Clipboard API\n // https://caniuse.com/mdn-api_clipboard_writetext\n navigator.clipboard.writeText(value).then(resolve).catch(reject)\n }, 0)\n })\n}\n\nexport function replaceFileExtension(\n filePath: string,\n extension: string,\n): string {\n const pathParts = filePath.split(/[\\\\/]/) // split on both '/' and '\\'\n const filename = pathParts[pathParts.length - 1] // last part of path\n const baseName = filename.replace(/\\.[^/.]+$/, '') // remove extension\n return `${baseName}.${extension}`\n}\n\nexport function stringListToArray(input: string): string[] {\n let inputArray: string[]\n\n if (Array.isArray(input)) {\n inputArray = input.map(String)\n } else {\n try {\n const parsed = JSON.parse(input ?? '[]')\n inputArray = Array.isArray(parsed) ? parsed.map(String) : [String(parsed)]\n } catch {\n inputArray = input ? [String(input)] : []\n }\n }\n return inputArray\n}\n\n// Split a value by a separator and remove leading/trailing spaces\nexport function splitAndTrim(value: string, sep: string = ','): string[] {\n return value?.split(sep).map(v => v.trim()) ?? []\n}\n"],"mappings":";;AAKA,SAAgB,EAAe,GAAa,GAAmB,IAAS,KAAK;AAI3E,QAHI,EAAI,UAAU,IACT,IAEF,EAAI,UAAU,GAAG,EAAU,GAAG;;AAGvC,SAAgB,EAAU,GAA6B;CACrD,IAAM,IAAc,EAAY,UAAU,EACtC,IAAc;AAClB,MAAK,IAAI,IAAI,GAAG,IAAI,EAAI,QAAQ,KAAK,EACnC,MAAO,OAAO,aAAa,SAAS,EAAI,UAAU,GAAG,IAAI,EAAE,EAAE,GAAG,CAAC;AAEnE,QAAO;;AAST,SAAgB,EAAS,GAAqB;AAC5C,CACE,MAAM;CAER,IAAI,IAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAM,EAAI,QAAQ,IAAI,GAAK,KAAK;EAC9C,IAAM,IAAM,EAAI,WAAW,EAAE;AAE7B,EADA,KAAQ,KAAQ,KAAK,IAAO,GAC5B,KAAQ;;AAEV,QAAO;;AAGT,SAAgB,EAAmB,GAA6B;AAC9D,KAAI,KAAM,KACR,QAAO;AAET,KAAI,OAAO,KAAO,SAChB,QAAO;AAET,KAAI,EAAG,MAAM,EAAwB,EAAE;EAErC,IAAM,IAAW,EAAW,EAAG,CAAE;AACjC,SAAO,SAAS,EAAS,UAAU,EAAa,CAAC;OAEjD,QAAO,SAAS,EAAG;;AAMvB,SAAgB,EAAiB,GAAqB;AACpD,KAAI,EAAY,MAAM,CAAC,SAAS,EAC9B,QAAO;AAGT,KAAI,CADc,WAAW,KAAK,EAAY,CAE5C,QAAO;AAIT,KAAI,CADc,KAAK,KAAK,EAAY,CAEtC,QAAO;AAMT,KAAI,CAHmB,wCAAwC,KAC7D,EACD,CAEC,QAAO;;AAKX,SAAgB,EAAsB,GAA8B;AAClE,QAAO,IAAI,SAAS,GAAS,MAAW;AAEtC,mBAAiB;AAGf,aAAU,UAAU,UAAU,EAAM,CAAC,KAAK,EAAQ,CAAC,MAAM,EAAO;KAC/D,EAAE;GACL;;AAGJ,SAAgB,EACd,GACA,GACQ;CACR,IAAM,IAAY,EAAS,MAAM,QAAQ;AAGzC,QAAO,GAFU,EAAU,EAAU,SAAS,GACpB,QAAQ,aAAa,GAAG,CAC/B,GAAG;;AAGxB,SAAgB,EAAkB,GAAyB;CACzD,IAAI;AAEJ,KAAI,MAAM,QAAQ,EAAM,CACtB,KAAa,EAAM,IAAI,OAAO;KAE9B,KAAI;EACF,IAAM,IAAS,KAAK,MAAM,KAAS,KAAK;AACxC,MAAa,MAAM,QAAQ,EAAO,GAAG,EAAO,IAAI,OAAO,GAAG,CAAC,OAAO,EAAO,CAAC;SACpE;AACN,MAAa,IAAQ,CAAC,OAAO,EAAM,CAAC,GAAG,EAAE;;AAG7C,QAAO;;AAIT,SAAgB,EAAa,GAAe,IAAc,KAAe;AACvE,QAAO,GAAO,MAAM,EAAI,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,IAAI,EAAE"}
1
+ {"version":3,"file":"StringUtils.js","names":[],"sources":["../../../src/utils/functions/StringUtils.ts"],"sourcesContent":["import { parseSynId, SYNAPSE_ENTITY_ID_REGEX } from './RegularExpressions'\n\n/**\n * Truncates a string to be a specified length, then optionally append a suffix.\n */\nexport function truncateString(str: string, maxLength: number, suffix = '…') {\n if (str.length <= maxLength) {\n return str\n }\n return str.substring(0, maxLength) + suffix\n}\n\nexport function hex2ascii(inputString: string): string {\n const hex: string = inputString.toString()\n let str: string = ''\n for (let n = 0; n < hex.length; n += 2) {\n str += String.fromCharCode(parseInt(hex.substring(n, n + 2), 16))\n }\n return str\n}\n\n/**\n * Returns a hash code from a string. Uses Java's String.hashCode() algorithm: https://devdocs.io/openjdk~8/java/lang/string#hashCode--\n * @param {String} str The string to hash.\n * @return {Number} A 32bit integer\n * @see https://stackoverflow.com/a/8831937\n */\nexport function hashCode(str?: string | null) {\n if (str == null) {\n str = ''\n }\n let hash = 0\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i)\n hash = (hash << 5) - hash + chr\n hash |= 0 // Convert to 32bit integer\n }\n return hash\n}\n\nexport function normalizeNumericId(id: string | number): number {\n if (id == null) {\n return NaN\n }\n if (typeof id === 'number') {\n return id\n }\n if (id.match(SYNAPSE_ENTITY_ID_REGEX)) {\n // parse the ID to remove a possible version suffix ('syn123.4' -> 'syn123')\n const entityId = parseSynId(id)!.targetId\n return parseInt(entityId.substring('syn'.length))\n } else {\n return parseInt(id)\n }\n}\n\n// Used for client-side validation. Note, the server has the final say. Integration tested in ChangePassword.integration.test.tsx\n// See https://github.com/Sage-Bionetworks/Synapse-Repository-Services/blob/develop/services/repository-managers/src/main/java/org/sagebionetworks/repo/manager/password/PasswordValidatorImpl.java#L47 for rules\nexport function validatePassword(newPassword: string) {\n if (newPassword.trim().length < 8) {\n return 'A valid password must be at least 8 characters long'\n }\n const hasLetter = /[a-zA-Z]/.test(newPassword) // Checks for at least one letter\n if (!hasLetter) {\n return 'A valid password must include letters'\n }\n\n const hasNumber = /\\d/.test(newPassword) // Checks for at least one number\n if (!hasNumber) {\n return 'A valid password must include digits (0-9)'\n }\n\n const hasSpecialChar = /[~!@#$%^&*_\\-+=`|\\\\(){}[\\]:;\"'<>,.?/]/.test(\n newPassword,\n ) // Checks for at least one special character\n if (!hasSpecialChar) {\n return 'A valid password must include special characters ~!@#$%^&*_-+=`|\\\\(){}[]:;\"\\'<>,.?/'\n }\n return undefined\n}\n\nexport function copyStringToClipboard(value: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // PORTALS-3571: setTimeout necessary on Safari\n setTimeout(() => {\n // use the Clipboard API\n // https://caniuse.com/mdn-api_clipboard_writetext\n navigator.clipboard.writeText(value).then(resolve).catch(reject)\n }, 0)\n })\n}\n\nexport function replaceFileExtension(\n filePath: string,\n extension: string,\n): string {\n const pathParts = filePath.split(/[\\\\/]/) // split on both '/' and '\\'\n const filename = pathParts[pathParts.length - 1] // last part of path\n const baseName = filename.replace(/\\.[^/.]+$/, '') // remove extension\n return `${baseName}.${extension}`\n}\n\nexport function stringListToArray(input: string): string[] {\n let inputArray: string[]\n\n if (Array.isArray(input)) {\n inputArray = input.map(String)\n } else {\n try {\n const parsed = JSON.parse(input ?? '[]')\n inputArray = Array.isArray(parsed) ? parsed.map(String) : [String(parsed)]\n } catch {\n inputArray = input ? [String(input)] : []\n }\n }\n return inputArray\n}\n\n// Split a value by a separator and remove leading/trailing spaces\nexport function splitAndTrim(value: string, sep: string = ','): string[] {\n return value?.split(sep).map(v => v.trim()) ?? []\n}\n"],"mappings":";;AAKA,SAAgB,EAAe,GAAa,GAAmB,IAAS,KAAK;AAI3E,QAHI,EAAI,UAAU,IACT,IAEF,EAAI,UAAU,GAAG,EAAU,GAAG;;AAGvC,SAAgB,EAAU,GAA6B;CACrD,IAAM,IAAc,EAAY,UAAU,EACtC,IAAc;AAClB,MAAK,IAAI,IAAI,GAAG,IAAI,EAAI,QAAQ,KAAK,EACnC,MAAO,OAAO,aAAa,SAAS,EAAI,UAAU,GAAG,IAAI,EAAE,EAAE,GAAG,CAAC;AAEnE,QAAO;;AAST,SAAgB,EAAS,GAAqB;AAC5C,CACE,MAAM;CAER,IAAI,IAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAM,EAAI,QAAQ,IAAI,GAAK,KAAK;EAC9C,IAAM,IAAM,EAAI,WAAW,EAAE;AAE7B,EADA,KAAQ,KAAQ,KAAK,IAAO,GAC5B,KAAQ;;AAEV,QAAO;;AAGT,SAAgB,EAAmB,GAA6B;AAC9D,KAAI,KAAM,KACR,QAAO;AAET,KAAI,OAAO,KAAO,SAChB,QAAO;AAET,KAAI,EAAG,MAAM,EAAwB,EAAE;EAErC,IAAM,IAAW,EAAW,EAAG,CAAE;AACjC,SAAO,SAAS,EAAS,UAAU,EAAa,CAAC;OAEjD,QAAO,SAAS,EAAG;;AAMvB,SAAgB,EAAiB,GAAqB;AACpD,KAAI,EAAY,MAAM,CAAC,SAAS,EAC9B,QAAO;AAGT,KAAI,CADc,WAAW,KAAK,EAC7B,CACH,QAAO;AAIT,KAAI,CADc,KAAK,KAAK,EACvB,CACH,QAAO;AAMT,KAAI,CAHmB,wCAAwC,KAC7D,EAEG,CACH,QAAO;;AAKX,SAAgB,EAAsB,GAA8B;AAClE,QAAO,IAAI,SAAS,GAAS,MAAW;AAEtC,mBAAiB;AAGf,aAAU,UAAU,UAAU,EAAM,CAAC,KAAK,EAAQ,CAAC,MAAM,EAAO;KAC/D,EAAE;GACL;;AAGJ,SAAgB,EACd,GACA,GACQ;CACR,IAAM,IAAY,EAAS,MAAM,QAAQ;AAGzC,QAAO,GAFU,EAAU,EAAU,SAAS,GACpB,QAAQ,aAAa,GACrC,CAAS,GAAG;;AAGxB,SAAgB,EAAkB,GAAyB;CACzD,IAAI;AAEJ,KAAI,MAAM,QAAQ,EAAM,CACtB,KAAa,EAAM,IAAI,OAAO;KAE9B,KAAI;EACF,IAAM,IAAS,KAAK,MAAM,KAAS,KAAK;AACxC,MAAa,MAAM,QAAQ,EAAO,GAAG,EAAO,IAAI,OAAO,GAAG,CAAC,OAAO,EAAO,CAAC;SACpE;AACN,MAAa,IAAQ,CAAC,OAAO,EAAM,CAAC,GAAG,EAAE;;AAG7C,QAAO;;AAIT,SAAgB,EAAa,GAAe,IAAc,KAAe;AACvE,QAAO,GAAO,MAAM,EAAI,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,IAAI,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"deepLinkingUtils.js","names":[],"sources":["../../../src/utils/functions/deepLinkingUtils.ts"],"sourcesContent":["import { Query, QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { isEqual } from 'lodash-es'\nimport { parseEntityIdFromSqlStatement } from './SqlFunctions'\n\n/**\n * Compresses a string using the browser's CompressionStream API and returns base64.\n * @param str - The string to compress\n * @returns Base64-encoded compressed string\n */\nasync function compressString(str: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(str)\n\n const compressionStream = new CompressionStream('gzip')\n const writer = compressionStream.writable.getWriter()\n\n // Start consuming from the readable side immediately to prevent backpressure deadlock\n const readPromise = new Response(compressionStream.readable).arrayBuffer()\n\n await writer.write(data)\n await writer.close()\n\n const compressedArrayBuffer = await readPromise\n\n // Convert to base64\n const bytes = new Uint8Array(compressedArrayBuffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i])\n }\n return btoa(binary)\n}\n\n/**\n * Decompresses a base64-encoded gzip string using the browser's DecompressionStream API.\n * @param base64Str - The base64-encoded compressed string\n * @returns The decompressed string\n */\nasync function decompressString(base64Str: string): Promise<string> {\n // Convert from base64 to Uint8Array\n const binary = atob(base64Str)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n\n const decompressionStream = new DecompressionStream('gzip')\n const writer = decompressionStream.writable.getWriter()\n\n // Start consuming from the readable side immediately to prevent backpressure deadlock\n const readPromise = new Response(decompressionStream.readable).arrayBuffer()\n\n await writer.write(bytes)\n await writer.close()\n\n const decompressedArrayBuffer = await readPromise\n\n const decoder = new TextDecoder()\n return decoder.decode(decompressedArrayBuffer)\n}\n\n//id consists of a component class/function name and it's index\nfunction getComponentSearchHashId(\n componentName: string,\n componentIndex: number,\n): string {\n return `${componentName}${componentIndex}`\n}\n\n/**\n * Computes the difference between the current query and the initial query.\n * Returns only the fields that differ from the initial query.\n * Dynamically handles all fields in the Query type, making it safe for future changes.\n * @param currentQuery - The current query state\n * @param initQuery - The initial query state\n * @returns A partial Query object containing only the differences, or null if queries are equal\n */\nfunction computeQueryDiff(\n currentQuery: Query,\n initQuery: Query,\n): Partial<Query> | null {\n if (isEqual(currentQuery, initQuery)) {\n return null\n }\n\n const diff: Partial<Query> = {}\n\n // Get the union of keys from both currentQuery and initQuery to handle\n // both additions/changes and removals/omissions\n const allKeys = new Set([\n ...Object.keys(currentQuery),\n ...Object.keys(initQuery),\n ]) as Set<keyof Query>\n\n for (const key of allKeys) {\n if (!isEqual(currentQuery[key], initQuery[key])) {\n // Type assertion needed for dynamic key assignment\n // Include currentQuery[key] even if undefined to represent removals\n ;(diff as any)[key] = currentQuery[key]\n }\n }\n\n return Object.keys(diff).length > 0 ? diff : null\n}\n\n/**\n * Applies a query diff to the initial query to reconstruct the current query.\n * @param initQuery - The initial query state\n * @param diff - The partial query containing only the differences\n * @returns The reconstructed query\n */\nfunction applyQueryDiff(initQuery: Query, diff: Partial<Query>): Query {\n return {\n ...initQuery,\n ...diff,\n }\n}\n\n/**\n * Updates the url with the components' new search params. Stores only the\n * differences between currentQuery and initQuery, compressed with gzip and base64-encoded.\n * If the queries are equal, the search param will be removed.\n * @param componentName\n * @param componentIndex\n * @param currentQuery - The current query state, or null to remove the param\n * @param initQuery - The initial query state to compute diff against\n */\nexport async function updateUrlWithNewSearchParam(\n componentName: string,\n componentIndex: number | undefined,\n currentQuery: Query | null,\n initQuery: Query | null,\n): Promise<void> {\n const componentSearchHashId =\n componentIndex !== undefined\n ? getComponentSearchHashId(componentName, componentIndex)\n : componentName\n\n const currentSearch = new URLSearchParams(window.location.search)\n\n if (currentQuery && initQuery) {\n const diff = computeQueryDiff(currentQuery, initQuery)\n if (diff) {\n // Compress the diff and encode as base64\n const jsonString = JSON.stringify(diff)\n const compressed = await compressString(jsonString)\n currentSearch.set(componentSearchHashId, compressed)\n } else {\n // Queries are equal, remove the param\n currentSearch.delete(componentSearchHashId)\n }\n } else {\n // No current query or no init query, remove the param\n currentSearch.delete(componentSearchHashId)\n }\n\n const searchString = currentSearch.toString()\n window.history.replaceState(\n window.history.state,\n '',\n window.location.pathname +\n (Array.from(currentSearch).length > 0\n ? `?${searchString.toString()}`\n : ''),\n )\n}\n\n/**\n * Retrieves a query request from the URL by applying the stored diff to the initial query.\n * @param componentIndex\n * @param initQuery - The initial query to apply the diff to\n * @returns A partial QueryBundleRequest with the reconstructed query, or undefined if no diff is stored\n */\nexport async function getQueryRequestFromLink(\n componentIndex: number,\n initQuery: Query,\n): Promise<Partial<QueryBundleRequest> | undefined> {\n const paramKey = getComponentSearchHashId('qw', componentIndex)\n const searchParamValue = new URLSearchParams(window.location.search).get(\n paramKey,\n )\n\n let queryRequest: Partial<QueryBundleRequest> | undefined = undefined\n if (searchParamValue) {\n try {\n // Decompress and parse the diff from the URL\n const jsonString = await decompressString(searchParamValue)\n const diff = JSON.parse(jsonString) as Partial<Query>\n // Apply the diff to the init query to reconstruct the current query\n const query = applyQueryDiff(initQuery, diff)\n if (query.sql) {\n queryRequest = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: parseEntityIdFromSqlStatement(query.sql),\n query,\n }\n }\n } catch (error) {\n console.error('Failed to parse query diff from URL:', error)\n }\n }\n\n // check for legacy search param for backward compatibility, but prioritize the new format if both are present\n const legacyParamKey = getComponentSearchHashId(\n 'QueryWrapper',\n componentIndex,\n )\n const legacySearchParamValue = new URLSearchParams(\n window.location.search,\n ).get(legacyParamKey)\n if (legacySearchParamValue && !queryRequest) {\n try {\n const query = JSON.parse(legacySearchParamValue) as Query\n if (query.sql) {\n queryRequest = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: parseEntityIdFromSqlStatement(query.sql),\n query,\n }\n }\n } catch (error) {\n console.error('Failed to parse legacy query from URL:', error)\n }\n }\n return queryRequest\n}\n\n/**\n * Generates a URL with a compressed query diff parameter.\n * @param path - The base path for the URL\n * @param componentIndex - The component index (e.g., 0)\n * @param currentQuery - The query to encode\n * @param initQuery - The initial/default query to compute the diff against\n * @returns A Promise that resolves to the full URL with compressed query parameter\n */\nexport async function generateCompressedQueryURL(\n path: string,\n componentIndex: number,\n currentQuery: Query,\n initQuery: Query,\n): Promise<string> {\n const diff = computeQueryDiff(currentQuery, initQuery)\n if (!diff) {\n // Queries are equal, no need for a parameter\n return path\n }\n\n const jsonString = JSON.stringify(diff)\n const compressed = await compressString(jsonString)\n const paramKey = getComponentSearchHashId('qw', componentIndex)\n\n return `${path}?${paramKey}=${encodeURIComponent(compressed)}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"deepLinkingUtils.js","names":[],"sources":["../../../src/utils/functions/deepLinkingUtils.ts"],"sourcesContent":["import { Query, QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { isEqual } from 'lodash-es'\nimport { parseEntityIdFromSqlStatement } from './SqlFunctions'\n\n/**\n * Compresses a string using the browser's CompressionStream API and returns base64.\n * @param str - The string to compress\n * @returns Base64-encoded compressed string\n */\nasync function compressString(str: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(str)\n\n const compressionStream = new CompressionStream('gzip')\n const writer = compressionStream.writable.getWriter()\n\n // Start consuming from the readable side immediately to prevent backpressure deadlock\n const readPromise = new Response(compressionStream.readable).arrayBuffer()\n\n await writer.write(data)\n await writer.close()\n\n const compressedArrayBuffer = await readPromise\n\n // Convert to base64\n const bytes = new Uint8Array(compressedArrayBuffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i])\n }\n return btoa(binary)\n}\n\n/**\n * Decompresses a base64-encoded gzip string using the browser's DecompressionStream API.\n * @param base64Str - The base64-encoded compressed string\n * @returns The decompressed string\n */\nasync function decompressString(base64Str: string): Promise<string> {\n // Convert from base64 to Uint8Array\n const binary = atob(base64Str)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n\n const decompressionStream = new DecompressionStream('gzip')\n const writer = decompressionStream.writable.getWriter()\n\n // Start consuming from the readable side immediately to prevent backpressure deadlock\n const readPromise = new Response(decompressionStream.readable).arrayBuffer()\n\n await writer.write(bytes)\n await writer.close()\n\n const decompressedArrayBuffer = await readPromise\n\n const decoder = new TextDecoder()\n return decoder.decode(decompressedArrayBuffer)\n}\n\n//id consists of a component class/function name and it's index\nfunction getComponentSearchHashId(\n componentName: string,\n componentIndex: number,\n): string {\n return `${componentName}${componentIndex}`\n}\n\n/**\n * Computes the difference between the current query and the initial query.\n * Returns only the fields that differ from the initial query.\n * Dynamically handles all fields in the Query type, making it safe for future changes.\n * @param currentQuery - The current query state\n * @param initQuery - The initial query state\n * @returns A partial Query object containing only the differences, or null if queries are equal\n */\nfunction computeQueryDiff(\n currentQuery: Query,\n initQuery: Query,\n): Partial<Query> | null {\n if (isEqual(currentQuery, initQuery)) {\n return null\n }\n\n const diff: Partial<Query> = {}\n\n // Get the union of keys from both currentQuery and initQuery to handle\n // both additions/changes and removals/omissions\n const allKeys = new Set([\n ...Object.keys(currentQuery),\n ...Object.keys(initQuery),\n ]) as Set<keyof Query>\n\n for (const key of allKeys) {\n if (!isEqual(currentQuery[key], initQuery[key])) {\n // Type assertion needed for dynamic key assignment\n // Include currentQuery[key] even if undefined to represent removals\n ;(diff as any)[key] = currentQuery[key]\n }\n }\n\n return Object.keys(diff).length > 0 ? diff : null\n}\n\n/**\n * Applies a query diff to the initial query to reconstruct the current query.\n * @param initQuery - The initial query state\n * @param diff - The partial query containing only the differences\n * @returns The reconstructed query\n */\nfunction applyQueryDiff(initQuery: Query, diff: Partial<Query>): Query {\n return {\n ...initQuery,\n ...diff,\n }\n}\n\n/**\n * Updates the url with the components' new search params. Stores only the\n * differences between currentQuery and initQuery, compressed with gzip and base64-encoded.\n * If the queries are equal, the search param will be removed.\n * @param componentName\n * @param componentIndex\n * @param currentQuery - The current query state, or null to remove the param\n * @param initQuery - The initial query state to compute diff against\n */\nexport async function updateUrlWithNewSearchParam(\n componentName: string,\n componentIndex: number | undefined,\n currentQuery: Query | null,\n initQuery: Query | null,\n): Promise<void> {\n const componentSearchHashId =\n componentIndex !== undefined\n ? getComponentSearchHashId(componentName, componentIndex)\n : componentName\n\n const currentSearch = new URLSearchParams(window.location.search)\n\n if (currentQuery && initQuery) {\n const diff = computeQueryDiff(currentQuery, initQuery)\n if (diff) {\n // Compress the diff and encode as base64\n const jsonString = JSON.stringify(diff)\n const compressed = await compressString(jsonString)\n currentSearch.set(componentSearchHashId, compressed)\n } else {\n // Queries are equal, remove the param\n currentSearch.delete(componentSearchHashId)\n }\n } else {\n // No current query or no init query, remove the param\n currentSearch.delete(componentSearchHashId)\n }\n\n const searchString = currentSearch.toString()\n window.history.replaceState(\n window.history.state,\n '',\n window.location.pathname +\n (Array.from(currentSearch).length > 0\n ? `?${searchString.toString()}`\n : ''),\n )\n}\n\n/**\n * Retrieves a query request from the URL by applying the stored diff to the initial query.\n * @param componentIndex\n * @param initQuery - The initial query to apply the diff to\n * @returns A partial QueryBundleRequest with the reconstructed query, or undefined if no diff is stored\n */\nexport async function getQueryRequestFromLink(\n componentIndex: number,\n initQuery: Query,\n): Promise<Partial<QueryBundleRequest> | undefined> {\n const paramKey = getComponentSearchHashId('qw', componentIndex)\n const searchParamValue = new URLSearchParams(window.location.search).get(\n paramKey,\n )\n\n let queryRequest: Partial<QueryBundleRequest> | undefined = undefined\n if (searchParamValue) {\n try {\n // Decompress and parse the diff from the URL\n const jsonString = await decompressString(searchParamValue)\n const diff = JSON.parse(jsonString) as Partial<Query>\n // Apply the diff to the init query to reconstruct the current query\n const query = applyQueryDiff(initQuery, diff)\n if (query.sql) {\n queryRequest = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: parseEntityIdFromSqlStatement(query.sql),\n query,\n }\n }\n } catch (error) {\n console.error('Failed to parse query diff from URL:', error)\n }\n }\n\n // check for legacy search param for backward compatibility, but prioritize the new format if both are present\n const legacyParamKey = getComponentSearchHashId(\n 'QueryWrapper',\n componentIndex,\n )\n const legacySearchParamValue = new URLSearchParams(\n window.location.search,\n ).get(legacyParamKey)\n if (legacySearchParamValue && !queryRequest) {\n try {\n const query = JSON.parse(legacySearchParamValue) as Query\n if (query.sql) {\n queryRequest = {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: parseEntityIdFromSqlStatement(query.sql),\n query,\n }\n }\n } catch (error) {\n console.error('Failed to parse legacy query from URL:', error)\n }\n }\n return queryRequest\n}\n\n/**\n * Generates a URL with a compressed query diff parameter.\n * @param path - The base path for the URL\n * @param componentIndex - The component index (e.g., 0)\n * @param currentQuery - The query to encode\n * @param initQuery - The initial/default query to compute the diff against\n * @returns A Promise that resolves to the full URL with compressed query parameter\n */\nexport async function generateCompressedQueryURL(\n path: string,\n componentIndex: number,\n currentQuery: Query,\n initQuery: Query,\n): Promise<string> {\n const diff = computeQueryDiff(currentQuery, initQuery)\n if (!diff) {\n // Queries are equal, no need for a parameter\n return path\n }\n\n const jsonString = JSON.stringify(diff)\n const compressed = await compressString(jsonString)\n const paramKey = getComponentSearchHashId('qw', componentIndex)\n\n return `${path}?${paramKey}=${encodeURIComponent(compressed)}`\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"getDataFromFromStorage.js","names":[],"sources":["../../../src/utils/functions/getDataFromFromStorage.ts"],"sourcesContent":["import {\n ColumnType,\n ColumnTypeEnum,\n EntityHeader,\n Evaluation,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { SynapseConstants } from '../index'\n\nconst getStoredEntityHeaders = (): EntityHeader[] => {\n try {\n const lookUpEntityHeaders: EntityHeader[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.ENTITY_HEADER_STORAGE_KEY) || '',\n )\n return lookUpEntityHeaders\n } catch (e) {\n return []\n }\n}\n\nconst getStoredUserProfiles = (): UserProfile[] => {\n try {\n const lookUpUserIds: UserProfile[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.USER_PROFILE_STORAGE_KEY) || '',\n )\n return lookUpUserIds\n } catch (e) {\n return []\n }\n}\n\nconst getStoredEvaluation = (): Evaluation[] => {\n try {\n const lookUpEvaluations: UserProfile[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.EVALUATIONS_STORAGE_KEY) || '',\n )\n return lookUpEvaluations\n } catch (e) {\n return []\n }\n}\n\nconst getDisplayValueForEntityColumn = (value: string): string => {\n const entity = getStoredEntityHeaders().find(item => item.id === value)\n return entity?.name ?? value\n}\n\nconst getDisplayValueEvaluationIdColumn = (facetValue: string): string => {\n const evaluation = getStoredEvaluation().find(item => item.id === facetValue)\n return evaluation?.name || facetValue\n}\n\nconst getDisplayValueUserIdColumn = (facetValue: string): string => {\n const userProfile = getStoredUserProfiles().find(\n item => item.ownerId === facetValue,\n )\n return userProfile?.userName || facetValue\n}\n\nexport const getDisplayValue = (value: string, columnType: ColumnType) => {\n if (value === SynapseConstants.VALUE_NOT_SET) {\n return SynapseConstants.FRIENDLY_VALUE_NOT_SET\n }\n\n switch (columnType) {\n case ColumnTypeEnum.ENTITYID:\n case ColumnTypeEnum.ENTITYID_LIST:\n return getDisplayValueForEntityColumn(value)\n case ColumnTypeEnum.USERID:\n case ColumnTypeEnum.USERID_LIST:\n return getDisplayValueUserIdColumn(value)\n case ColumnTypeEnum.EVALUATIONID:\n return getDisplayValueEvaluationIdColumn(value)\n default:\n return value\n }\n}\n"],"mappings":";;;;AASA,IAAM,UAA+C;AACnD,KAAI;AAIF,SAH4C,KAAK,MAC/C,eAAe,QAAQ,8BAA2C,IAAI,GACvE;SAES;AACV,SAAO,EAAE;;GAIP,UAA6C;AACjD,KAAI;AAIF,SAHqC,KAAK,MACxC,eAAe,QAAQ,6BAA0C,IAAI,GACtE;SAES;AACV,SAAO,EAAE;;GAIP,UAA0C;AAC9C,KAAI;AAIF,SAHyC,KAAK,MAC5C,eAAe,QAAQ,iCAAyC,IAAI,GACrE;SAES;AACV,SAAO,EAAE;;GAIP,KAAkC,MACvB,GAAwB,CAAC,MAAK,MAAQ,EAAK,OAAO,EAAM,EACxD,QAAQ,GAGnB,KAAqC,MACtB,GAAqB,CAAC,MAAK,MAAQ,EAAK,OAAO,EAAW,EAC1D,QAAQ,GAGvB,KAA+B,MACf,GAAuB,CAAC,MAC1C,MAAQ,EAAK,YAAY,EAC1B,EACmB,YAAY,GAGrB,KAAmB,GAAe,MAA2B;AACxE,KAAI,MAAU,4CACZ,QAAO;AAGT,SAAQ,GAAR;EACE,KAAK,EAAe;EACpB,KAAK,EAAe,cAClB,QAAO,EAA+B,EAAM;EAC9C,KAAK,EAAe;EACpB,KAAK,EAAe,YAClB,QAAO,EAA4B,EAAM;EAC3C,KAAK,EAAe,aAClB,QAAO,EAAkC,EAAM;EACjD,QACE,QAAO"}
1
+ {"version":3,"file":"getDataFromFromStorage.js","names":[],"sources":["../../../src/utils/functions/getDataFromFromStorage.ts"],"sourcesContent":["import {\n ColumnType,\n ColumnTypeEnum,\n EntityHeader,\n Evaluation,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { SynapseConstants } from '../index'\n\nconst getStoredEntityHeaders = (): EntityHeader[] => {\n try {\n const lookUpEntityHeaders: EntityHeader[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.ENTITY_HEADER_STORAGE_KEY) || '',\n )\n return lookUpEntityHeaders\n } catch (e) {\n return []\n }\n}\n\nconst getStoredUserProfiles = (): UserProfile[] => {\n try {\n const lookUpUserIds: UserProfile[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.USER_PROFILE_STORAGE_KEY) || '',\n )\n return lookUpUserIds\n } catch (e) {\n return []\n }\n}\n\nconst getStoredEvaluation = (): Evaluation[] => {\n try {\n const lookUpEvaluations: UserProfile[] = JSON.parse(\n sessionStorage.getItem(SynapseConstants.EVALUATIONS_STORAGE_KEY) || '',\n )\n return lookUpEvaluations\n } catch (e) {\n return []\n }\n}\n\nconst getDisplayValueForEntityColumn = (value: string): string => {\n const entity = getStoredEntityHeaders().find(item => item.id === value)\n return entity?.name ?? value\n}\n\nconst getDisplayValueEvaluationIdColumn = (facetValue: string): string => {\n const evaluation = getStoredEvaluation().find(item => item.id === facetValue)\n return evaluation?.name || facetValue\n}\n\nconst getDisplayValueUserIdColumn = (facetValue: string): string => {\n const userProfile = getStoredUserProfiles().find(\n item => item.ownerId === facetValue,\n )\n return userProfile?.userName || facetValue\n}\n\nexport const getDisplayValue = (value: string, columnType: ColumnType) => {\n if (value === SynapseConstants.VALUE_NOT_SET) {\n return SynapseConstants.FRIENDLY_VALUE_NOT_SET\n }\n\n switch (columnType) {\n case ColumnTypeEnum.ENTITYID:\n case ColumnTypeEnum.ENTITYID_LIST:\n return getDisplayValueForEntityColumn(value)\n case ColumnTypeEnum.USERID:\n case ColumnTypeEnum.USERID_LIST:\n return getDisplayValueUserIdColumn(value)\n case ColumnTypeEnum.EVALUATIONID:\n return getDisplayValueEvaluationIdColumn(value)\n default:\n return value\n }\n}\n"],"mappings":";;;;AASA,IAAM,UAA+C;AACnD,KAAI;AAIF,SAH4C,KAAK,MAC/C,eAAe,QAAQ,8BAA2C,IAAI,GAEjE;SACG;AACV,SAAO,EAAE;;GAIP,UAA6C;AACjD,KAAI;AAIF,SAHqC,KAAK,MACxC,eAAe,QAAQ,6BAA0C,IAAI,GAEhE;SACG;AACV,SAAO,EAAE;;GAIP,UAA0C;AAC9C,KAAI;AAIF,SAHyC,KAAK,MAC5C,eAAe,QAAQ,iCAAyC,IAAI,GAE/D;SACG;AACV,SAAO,EAAE;;GAIP,KAAkC,MACvB,GAAwB,CAAC,MAAK,MAAQ,EAAK,OAAO,EAC1D,EAAQ,QAAQ,GAGnB,KAAqC,MACtB,GAAqB,CAAC,MAAK,MAAQ,EAAK,OAAO,EAC3D,EAAY,QAAQ,GAGvB,KAA+B,MACf,GAAuB,CAAC,MAC1C,MAAQ,EAAK,YAAY,EAEpB,EAAa,YAAY,GAGrB,KAAmB,GAAe,MAA2B;AACxE,KAAI,MAAU,4CACZ,QAAO;AAGT,SAAQ,GAAR;EACE,KAAK,EAAe;EACpB,KAAK,EAAe,cAClB,QAAO,EAA+B,EAAM;EAC9C,KAAK,EAAe;EACpB,KAAK,EAAe,YAClB,QAAO,EAA4B,EAAM;EAC3C,KAAK,EAAe,aAClB,QAAO,EAAkC,EAAM;EACjD,QACE,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"getEndpoint.js","names":[],"sources":["../../../src/utils/functions/getEndpoint.ts"],"sourcesContent":["import {\n SYNAPSE_BACKEND_DEV_URL,\n SYNAPSE_BACKEND_PRODUCTION_URL,\n SYNAPSE_BACKEND_STAGING_URL,\n} from '../SynapseConstants'\n\nexport enum BackendDestinationEnum {\n REPO_ENDPOINT,\n PORTAL_ENDPOINT,\n}\n\n// Encodes the portal and repo service destinations\nexport type EndpointObject = {\n REPO: string\n PORTAL: string\n}\n\nexport const KNOWN_SYNAPSE_ORG_URLS = [\n 'www.synapse.org',\n 'staging.synapse.org',\n 'tst.synapse.org',\n 'dev.synapse.org',\n '127.0.0.1',\n]\n\nexport const getSynapsePortalEndpoint = (hostname: string): string => {\n return KNOWN_SYNAPSE_ORG_URLS.includes(hostname.toLowerCase())\n ? '/'\n : 'https://www.synapse.org/'\n}\nfunction getDefaultSynapsePortal(): string {\n if (typeof window === 'undefined') return getSynapsePortalEndpoint('')\n return getSynapsePortalEndpoint(window.location.hostname)\n}\n\nexport const PRODUCTION_ENDPOINT_CONFIG: EndpointObject = {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n // Computed lazily via a getter so this object can be imported in Node.js (SSR/pre-render)\n // without crashing on the `window.location.hostname` access.\n get PORTAL() {\n return getDefaultSynapsePortal()\n },\n}\n\n// Given an endpoint will return the specific stack object\nexport const getEndpoint = (endpoint: BackendDestinationEnum): string => {\n let endpoint_config = PRODUCTION_ENDPOINT_CONFIG\n if (typeof window !== 'undefined') {\n // @ts-expect-error if overriding endpoint config\n if (window.SRC_OVERRIDE_ENDPOINT_CONFIG) {\n // @ts-expect-error\n endpoint_config = window.SRC_OVERRIDE_ENDPOINT_CONFIG\n }\n }\n const { PORTAL, REPO } = endpoint_config\n if (!PORTAL || !REPO) {\n throw Error('Error failed to specify endpoints, cannot make call')\n }\n if (endpoint === BackendDestinationEnum.PORTAL_ENDPOINT) {\n return PORTAL\n }\n return REPO\n}\n\nexport type SynapseStack =\n | 'production'\n | 'staging'\n | 'development'\n | 'mock'\n | 'local'\n\nexport const MOCK_REPO_ORIGIN =\n 'https://mock-repo.sagebase.org' satisfies string\nconst MOCK_PORTAL_ORIGIN = 'https://mock-portal.sagebase.org/' satisfies string\n\nexport const STACK_MAP: Record<SynapseStack, EndpointObject> = {\n production: {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n PORTAL: 'https://www.synapse.org/',\n },\n staging: {\n REPO: SYNAPSE_BACKEND_STAGING_URL,\n PORTAL: 'https://staging.synapse.org/',\n },\n development: {\n REPO: SYNAPSE_BACKEND_DEV_URL,\n PORTAL: 'https://dev.synapse.org/',\n },\n mock: {\n REPO: MOCK_REPO_ORIGIN,\n PORTAL: MOCK_PORTAL_ORIGIN,\n },\n local: {\n REPO: 'http://localhost:8080/services-repository-develop-SNAPSHOT',\n PORTAL: 'http://localhost:8888/',\n },\n}\n"],"mappings":";;AAMA,IAAY,IAAL,yBAAA,GAAA;QACL,EAAA,EAAA,gBAAA,KAAA,iBACA,EAAA,EAAA,kBAAA,KAAA;KACD,EAQY,IAAyB;CACpC;CACA;CACA;CACA;CACA;CACD,EAEY,KAA4B,MAChC,EAAuB,SAAS,EAAS,aAAa,CAAC,GAC1D,MACA;AAEN,SAAS,IAAkC;AAEzC,QAD0C,EAAtC,OAAO,SAAW,MAA6C,KACnC,OAAO,SAAS,SADsB;;AAIxE,IAAa,IAA6C;CACxD,MAAM;CAGN,IAAI,SAAS;AACX,SAAO,GAAyB;;CAEnC,EAGY,KAAe,MAA6C;CACvE,IAAI,IAAkB;AACtB,CAAI,OAAO,SAAW,OAEhB,OAAO,iCAET,IAAkB,OAAO;CAG7B,IAAM,EAAE,WAAQ,YAAS;AACzB,KAAI,CAAC,KAAU,CAAC,EACd,OAAM,MAAM,sDAAsD;AAKpE,QAHI,MAAa,EAAuB,kBAC/B,IAEF;GAUI,IACX,kCAGW,IAAkD;CAC7D,YAAY;EACV,MAAM;EACN,QAAQ;EACT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACT;CACD,aAAa;EACX,MAAM;EACN,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,QAjBuB;EAkBxB;CACD,OAAO;EACL,MAAM;EACN,QAAQ;EACT;CACF"}
1
+ {"version":3,"file":"getEndpoint.js","names":[],"sources":["../../../src/utils/functions/getEndpoint.ts"],"sourcesContent":["import {\n SYNAPSE_BACKEND_DEV_URL,\n SYNAPSE_BACKEND_PRODUCTION_URL,\n SYNAPSE_BACKEND_STAGING_URL,\n} from '../SynapseConstants'\n\nexport enum BackendDestinationEnum {\n REPO_ENDPOINT,\n PORTAL_ENDPOINT,\n}\n\n// Encodes the portal and repo service destinations\nexport type EndpointObject = {\n REPO: string\n PORTAL: string\n}\n\nexport const KNOWN_SYNAPSE_ORG_URLS = [\n 'www.synapse.org',\n 'staging.synapse.org',\n 'tst.synapse.org',\n 'dev.synapse.org',\n '127.0.0.1',\n]\n\nexport const getSynapsePortalEndpoint = (hostname: string): string => {\n return KNOWN_SYNAPSE_ORG_URLS.includes(hostname.toLowerCase())\n ? '/'\n : 'https://www.synapse.org/'\n}\nfunction getDefaultSynapsePortal(): string {\n if (typeof window === 'undefined') return getSynapsePortalEndpoint('')\n return getSynapsePortalEndpoint(window.location.hostname)\n}\n\nexport const PRODUCTION_ENDPOINT_CONFIG: EndpointObject = {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n // Computed lazily via a getter so this object can be imported in Node.js (SSR/pre-render)\n // without crashing on the `window.location.hostname` access.\n get PORTAL() {\n return getDefaultSynapsePortal()\n },\n}\n\n// Given an endpoint will return the specific stack object\nexport const getEndpoint = (endpoint: BackendDestinationEnum): string => {\n let endpoint_config = PRODUCTION_ENDPOINT_CONFIG\n if (typeof window !== 'undefined') {\n // @ts-expect-error if overriding endpoint config\n if (window.SRC_OVERRIDE_ENDPOINT_CONFIG) {\n // @ts-expect-error\n endpoint_config = window.SRC_OVERRIDE_ENDPOINT_CONFIG\n }\n }\n const { PORTAL, REPO } = endpoint_config\n if (!PORTAL || !REPO) {\n throw Error('Error failed to specify endpoints, cannot make call')\n }\n if (endpoint === BackendDestinationEnum.PORTAL_ENDPOINT) {\n return PORTAL\n }\n return REPO\n}\n\nexport type SynapseStack =\n | 'production'\n | 'staging'\n | 'development'\n | 'mock'\n | 'local'\n\nexport const MOCK_REPO_ORIGIN =\n 'https://mock-repo.sagebase.org' satisfies string\nconst MOCK_PORTAL_ORIGIN = 'https://mock-portal.sagebase.org/' satisfies string\n\nexport const STACK_MAP: Record<SynapseStack, EndpointObject> = {\n production: {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n PORTAL: 'https://www.synapse.org/',\n },\n staging: {\n REPO: SYNAPSE_BACKEND_STAGING_URL,\n PORTAL: 'https://staging.synapse.org/',\n },\n development: {\n REPO: SYNAPSE_BACKEND_DEV_URL,\n PORTAL: 'https://dev.synapse.org/',\n },\n mock: {\n REPO: MOCK_REPO_ORIGIN,\n PORTAL: MOCK_PORTAL_ORIGIN,\n },\n local: {\n REPO: 'http://localhost:8080/services-repository-develop-SNAPSHOT',\n PORTAL: 'http://localhost:8888/',\n },\n}\n"],"mappings":";;AAMA,IAAY,IAAL,yBAAA,GAAA;QACL,EAAA,EAAA,gBAAA,KAAA,iBACA,EAAA,EAAA,kBAAA,KAAA;KACD,EAQY,IAAyB;CACpC;CACA;CACA;CACA;CACA;CACD,EAEY,KAA4B,MAChC,EAAuB,SAAS,EAAS,aAAa,CAAC,GAC1D,MACA;AAEN,SAAS,IAAkC;AAEzC,QAD0C,EAAtC,OAAO,SAAW,MAA6C,KACnC,OAAO,SAAS,SADsB;;AAIxE,IAAa,IAA6C;CACxD,MAAM;CAGN,IAAI,SAAS;AACX,SAAO,GAAyB;;CAEnC,EAGY,KAAe,MAA6C;CACvE,IAAI,IAAkB;AACtB,CAAI,OAAO,SAAW,OAEhB,OAAO,iCAET,IAAkB,OAAO;CAG7B,IAAM,EAAE,WAAQ,YAAS;AACzB,KAAI,CAAC,KAAU,CAAC,EACd,OAAM,MAAM,sDAAsD;AAKpE,QAHI,MAAa,EAAuB,kBAC/B,IAEF;GAUI,IACX,kCAGW,IAAkD;CAC7D,YAAY;EACV,MAAM;EACN,QAAQ;EACT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACT;CACD,aAAa;EACX,MAAM;EACN,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACT;CACD,OAAO;EACL,MAAM;EACN,QAAQ;EACT;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"getUserData.js","names":[],"sources":["../../../src/utils/functions/getUserData.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { BackendDestinationEnum, getEndpoint } from './getEndpoint'\n\n/*\n Utility functions for UserCards\n*/\n\nexport async function getUserProfileWithProfilePicAttached(\n principalIds: string[],\n) {\n const userProfiles = await SynapseClient.getUserProfiles(principalIds)\n const profilesWithPictures = userProfiles.list.map(profile => {\n if (profile.profilePicureFileHandleId) {\n return {\n ...profile,\n clientPreSignedURL: `${getEndpoint(\n BackendDestinationEnum.REPO_ENDPOINT,\n )}/repo/v1/userProfile/${profile.ownerId}/image/preview?redirect=true`,\n }\n } else {\n return profile\n }\n })\n return { list: profilesWithPictures }\n}\n\nconst COLORS: string[] = [\n 'chocolate',\n 'black',\n 'firebrick',\n 'maroon',\n 'olive',\n 'green',\n 'forestgreen',\n 'darkturquoise',\n 'teal',\n 'blue',\n 'navy',\n 'darkmagenta',\n 'purple',\n 'blue',\n 'orangered',\n 'blueviolet',\n]\n\nconst hash = (userName: string) => {\n const val = userName\n .split('')\n .reduce(\n (prevHash, currVal) =>\n ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0,\n 0,\n )\n return Math.abs(val)\n}\n\nexport const getColor = (userName: string) => {\n const hashedUserName = hash(userName)\n return COLORS[hashedUserName % COLORS.length]\n}\n"],"mappings":";;;AAOA,eAAsB,EACpB,GACA;AAcA,QAAO,EAAE,OAbY,MAAM,EAAc,gBAAgB,EAAa,EAC5B,KAAK,KAAI,MAC7C,EAAQ,4BACH;EACL,GAAG;EACH,oBAAoB,GAAG,EACrB,EAAuB,cACxB,CAAC,uBAAuB,EAAQ,QAAQ;EAC1C,GAEM,EAET,EACmC;;AAGvC,IAAM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,KAAQ,MAAqB;CACjC,IAAM,IAAM,EACT,MAAM,GAAG,CACT,QACE,GAAU,OACP,KAAY,KAAK,IAAW,EAAQ,WAAW,EAAE,GAAI,GACzD,EACD;AACH,QAAO,KAAK,IAAI,EAAI;GAGT,KAAY,MAEhB,EADgB,EAAK,EAAS,GACN,EAAO"}
1
+ {"version":3,"file":"getUserData.js","names":[],"sources":["../../../src/utils/functions/getUserData.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { BackendDestinationEnum, getEndpoint } from './getEndpoint'\n\n/*\n Utility functions for UserCards\n*/\n\nexport async function getUserProfileWithProfilePicAttached(\n principalIds: string[],\n) {\n const userProfiles = await SynapseClient.getUserProfiles(principalIds)\n const profilesWithPictures = userProfiles.list.map(profile => {\n if (profile.profilePicureFileHandleId) {\n return {\n ...profile,\n clientPreSignedURL: `${getEndpoint(\n BackendDestinationEnum.REPO_ENDPOINT,\n )}/repo/v1/userProfile/${profile.ownerId}/image/preview?redirect=true`,\n }\n } else {\n return profile\n }\n })\n return { list: profilesWithPictures }\n}\n\nconst COLORS: string[] = [\n 'chocolate',\n 'black',\n 'firebrick',\n 'maroon',\n 'olive',\n 'green',\n 'forestgreen',\n 'darkturquoise',\n 'teal',\n 'blue',\n 'navy',\n 'darkmagenta',\n 'purple',\n 'blue',\n 'orangered',\n 'blueviolet',\n]\n\nconst hash = (userName: string) => {\n const val = userName\n .split('')\n .reduce(\n (prevHash, currVal) =>\n ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0,\n 0,\n )\n return Math.abs(val)\n}\n\nexport const getColor = (userName: string) => {\n const hashedUserName = hash(userName)\n return COLORS[hashedUserName % COLORS.length]\n}\n"],"mappings":";;;AAOA,eAAsB,EACpB,GACA;AAcA,QAAO,EAAE,OAZoB,MADF,EAAc,gBAAgB,EAAa,EAC5B,KAAK,KAAI,MAC7C,EAAQ,4BACH;EACL,GAAG;EACH,oBAAoB,GAAG,EACrB,EAAuB,cACxB,CAAC,uBAAuB,EAAQ,QAAQ;EAC1C,GAEM,EAGI,EAAsB;;AAGvC,IAAM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,KAAQ,MAAqB;CACjC,IAAM,IAAM,EACT,MAAM,GAAG,CACT,QACE,GAAU,OACP,KAAY,KAAK,IAAW,EAAQ,WAAW,EAAE,GAAI,GACzD,EACD;AACH,QAAO,KAAK,IAAI,EAAI;GAGT,KAAY,MAEhB,EADgB,EAAK,EACd,GAAiB,EAAO"}
@@ -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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"testDownloadSpeed.js","names":[],"sources":["../../../src/utils/functions/testDownloadSpeed.ts"],"sourcesContent":["import {\n getEntity,\n getFileHandleContent,\n getFiles,\n} from '@/synapse-client/SynapseClient'\nimport {\n BatchFileRequest,\n BatchFileResult,\n Entity,\n FileEntity,\n FileHandle,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nconst ESTIMATED_CORS_TIME_MS: number = 200\nconst ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY: string =\n 'ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME'\nconst ESTIMATED_DOWNLOAD_SPEED_KEY: string = 'ESTIMATED_DOWNLOAD_SPEED'\nconst TEST_FILE_ENTITY_ID: string = 'syn12600511'\n/**\n * Return the estimated download speed (bytes/second). Result is cached.\n * Result is crude estimate since it's a single test file (small sample, only ~2MB), but is a valid test (since it's a Synapse file on s3).\n * The intent is to let the user know if the package download will take many hours to download.\n * @param accessToken\n */\nexport const testDownloadSpeed = (accessToken: string): Promise<number> => {\n return new Promise((resolve, reject) => {\n // check cache\n const cachedSpeedExpireTime = localStorage.getItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n )\n const cachedSpeed = localStorage.getItem(ESTIMATED_DOWNLOAD_SPEED_KEY)\n if (cachedSpeedExpireTime && cachedSpeed) {\n // is this value expired?\n const now: number = new Date().getTime()\n if (now < Number(cachedSpeedExpireTime)) {\n resolve(Number(cachedSpeed))\n return\n }\n }\n\n /**\n * 1. Get the test File Entity\n * 2. Get the file handle and presigned URL associated to the latest version of the test File Entity\n * 3. Start the timer and fetch the file content using that presigned URL\n */\n getEntity(accessToken, TEST_FILE_ENTITY_ID)\n .then((entity: Entity) => {\n const fileEntity: FileEntity = entity as FileEntity\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: fileEntity.id!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: fileEntity.dataFileHandleId,\n },\n ]\n const request: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n getFiles(request, accessToken).then((data: BatchFileResult) => {\n const presignedUrl: string = data.requestedFiles[0].preSignedURL!\n // we know this file exists\n const fileHandle: FileHandle = data.requestedFiles[0].fileHandle!\n // start test!\n const startMs = new Date().getTime()\n return getFileHandleContent(fileHandle, presignedUrl).then(() => {\n // console.log(`Transferred ${fileHandle.contentSize/1000000} MB in ${((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS)/1000)} seconds`)\n // bytes/second\n const result =\n fileHandle.contentSize /\n ((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS) / 1000)\n // save result in cache (for 5 minutes)\n const now: number = new Date().getTime()\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n (now + 1000 * 60 * 5).toString(),\n )\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_KEY,\n result.toString(),\n )\n resolve(result)\n })\n })\n })\n .catch(err => reject(err as Error))\n })\n}\n"],"mappings":";;;AAeA,IAAM,IAAiC,KACjC,IACJ,wCACI,IAAuC,4BACvC,IAA8B,eAOvB,KAAqB,MACzB,IAAI,SAAS,GAAS,MAAW;CAEtC,IAAM,IAAwB,aAAa,QACzC,EACD,EACK,IAAc,aAAa,QAAQ,EAA6B;AACtE,KAAI,KAAyB,sBAEP,IAAI,MAAM,EAAC,SAAS,GAC9B,OAAO,EAAsB,EAAE;AACvC,IAAQ,OAAO,EAAY,CAAC;AAC5B;;AASJ,GAAU,GAAa,EAAoB,CACxC,MAAM,MAAmB;EACxB,IAAM,IAAyB;AAc/B,IANkC;GAChC,oBAAoB;GACpB,sBAAsB;GACtB,6BAA6B;GAC7B,gBAXyD,CACzD;IACE,mBAAmB,EAAW;IAC9B,qBAAqB,EAAwB;IAC7C,cAAc,EAAW;IAC1B,CACF;GAMA,EACiB,EAAY,CAAC,MAAM,MAA0B;GAC7D,IAAM,IAAuB,EAAK,eAAe,GAAG,cAE9C,IAAyB,EAAK,eAAe,GAAG,YAEhD,qBAAU,IAAI,MAAM,EAAC,SAAS;AACpC,UAAO,EAAqB,GAAY,EAAa,CAAC,WAAW;IAG/D,IAAM,IACJ,EAAW,iCACT,IAAI,MAAM,EAAC,SAAS,GAAG,IAAU,KAA0B,MAEzD,qBAAc,IAAI,MAAM,EAAC,SAAS;AASxC,IARA,aAAa,QACX,IACC,IAAM,MAAO,KAAK,GAAG,UAAU,CACjC,EACD,aAAa,QACX,GACA,EAAO,UAAU,CAClB,EACD,EAAQ,EAAO;KACf;IACF;GACF,CACD,OAAM,MAAO,EAAO,EAAa,CAAC;EACrC"}
1
+ {"version":3,"file":"testDownloadSpeed.js","names":[],"sources":["../../../src/utils/functions/testDownloadSpeed.ts"],"sourcesContent":["import {\n getEntity,\n getFileHandleContent,\n getFiles,\n} from '@/synapse-client/SynapseClient'\nimport {\n BatchFileRequest,\n BatchFileResult,\n Entity,\n FileEntity,\n FileHandle,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nconst ESTIMATED_CORS_TIME_MS: number = 200\nconst ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY: string =\n 'ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME'\nconst ESTIMATED_DOWNLOAD_SPEED_KEY: string = 'ESTIMATED_DOWNLOAD_SPEED'\nconst TEST_FILE_ENTITY_ID: string = 'syn12600511'\n/**\n * Return the estimated download speed (bytes/second). Result is cached.\n * Result is crude estimate since it's a single test file (small sample, only ~2MB), but is a valid test (since it's a Synapse file on s3).\n * The intent is to let the user know if the package download will take many hours to download.\n * @param accessToken\n */\nexport const testDownloadSpeed = (accessToken: string): Promise<number> => {\n return new Promise((resolve, reject) => {\n // check cache\n const cachedSpeedExpireTime = localStorage.getItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n )\n const cachedSpeed = localStorage.getItem(ESTIMATED_DOWNLOAD_SPEED_KEY)\n if (cachedSpeedExpireTime && cachedSpeed) {\n // is this value expired?\n const now: number = new Date().getTime()\n if (now < Number(cachedSpeedExpireTime)) {\n resolve(Number(cachedSpeed))\n return\n }\n }\n\n /**\n * 1. Get the test File Entity\n * 2. Get the file handle and presigned URL associated to the latest version of the test File Entity\n * 3. Start the timer and fetch the file content using that presigned URL\n */\n getEntity(accessToken, TEST_FILE_ENTITY_ID)\n .then((entity: Entity) => {\n const fileEntity: FileEntity = entity as FileEntity\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: fileEntity.id!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: fileEntity.dataFileHandleId,\n },\n ]\n const request: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n getFiles(request, accessToken).then((data: BatchFileResult) => {\n const presignedUrl: string = data.requestedFiles[0].preSignedURL!\n // we know this file exists\n const fileHandle: FileHandle = data.requestedFiles[0].fileHandle!\n // start test!\n const startMs = new Date().getTime()\n return getFileHandleContent(fileHandle, presignedUrl).then(() => {\n // console.log(`Transferred ${fileHandle.contentSize/1000000} MB in ${((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS)/1000)} seconds`)\n // bytes/second\n const result =\n fileHandle.contentSize /\n ((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS) / 1000)\n // save result in cache (for 5 minutes)\n const now: number = new Date().getTime()\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n (now + 1000 * 60 * 5).toString(),\n )\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_KEY,\n result.toString(),\n )\n resolve(result)\n })\n })\n })\n .catch(err => reject(err as Error))\n })\n}\n"],"mappings":";;;AAeA,IAAM,IAAiC,KACjC,IACJ,wCACI,IAAuC,4BACvC,IAA8B,eAOvB,KAAqB,MACzB,IAAI,SAAS,GAAS,MAAW;CAEtC,IAAM,IAAwB,aAAa,QACzC,EACD,EACK,IAAc,aAAa,QAAQ,EAA6B;AACtE,KAAI,KAAyB,sBAEP,IAAI,MAAM,EAAC,SAC3B,GAAM,OAAO,EAAsB,EAAE;AACvC,IAAQ,OAAO,EAAY,CAAC;AAC5B;;AASJ,GAAU,GAAa,EAAoB,CACxC,MAAM,MAAmB;EACxB,IAAM,IAAyB;AAc/B,IAAS;GALP,oBAAoB;GACpB,sBAAsB;GACtB,6BAA6B;GAC7B,gBAAgB,CAVhB;IACE,mBAAmB,EAAW;IAC9B,qBAAqB,EAAwB;IAC7C,cAAc,EAAW;IAC1B,CAMe;GAET,EAAS,EAAY,CAAC,MAAM,MAA0B;GAC7D,IAAM,IAAuB,EAAK,eAAe,GAAG,cAE9C,IAAyB,EAAK,eAAe,GAAG,YAEhD,qBAAU,IAAI,MAAM,EAAC,SAAS;AACpC,UAAO,EAAqB,GAAY,EAAa,CAAC,WAAW;IAG/D,IAAM,IACJ,EAAW,iCACT,IAAI,MAAM,EAAC,SAAS,GAAG,IAAU,KAA0B,MAEzD,qBAAc,IAAI,MAAM,EAAC,SAAS;AASxC,IARA,aAAa,QACX,IACC,IAAM,MAAO,KAAK,GAAG,UAAU,CACjC,EACD,aAAa,QACX,GACA,EAAO,UAAU,CAClB,EACD,EAAQ,EAAO;KACf;IACF;GACF,CACD,OAAM,MAAO,EAAO,EAAa,CAAC;EACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useConfirmItems.js","names":[],"sources":["../../../src/utils/hooks/useConfirmItems.ts"],"sourcesContent":["import { useReducer } from 'react'\n\ntype UseConfirmItemsReturn<T = unknown> = {\n /** The set of items that are awaiting confirmation */\n pendingItems: T[]\n /** The set of items that have been confirmed */\n confirmedItems: T[]\n /** Adds one or more items to the pending list */\n addItemsPendingConfirmation: (...items: T[]) => void\n /** Confirms one or more items, adding them to the confirmed list and removing them from the pending list\n * @returns the confirmed items */\n confirmItem: (...items: T[]) => { confirmedItems: T[]; pendingItems: T[] }\n /** Remove one or more items that are pending from the list\n * @returns the resulting state of confirmed items and pending items */\n removePendingItems: (...items: T[]) => {\n confirmedItems: T[]\n pendingItems: T[]\n }\n /** Clear all items from the confirmed and pending lists */\n clear: () => void\n}\n\nfunction reducer<T = unknown>(\n state: { pendingItems: T[]; confirmedItems: T[] },\n action:\n | { type: 'addItemsPendingConfirmation'; newItems: T[] }\n | { type: 'confirmItem'; itemsToConfirm: T[] }\n | { type: 'removePendingItems'; itemsToRemove: T[] }\n | { type: 'clear' },\n) {\n switch (action.type) {\n case 'addItemsPendingConfirmation':\n return {\n ...state,\n pendingItems: [...state.pendingItems, ...action.newItems],\n }\n case 'confirmItem': {\n const newConfirmedItems = [\n ...state.confirmedItems,\n ...action.itemsToConfirm,\n ]\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToConfirm.includes(i),\n )\n return {\n confirmedItems: newConfirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'removePendingItems': {\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToRemove.includes(i),\n )\n return {\n confirmedItems: state.confirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'clear':\n return { confirmedItems: [], pendingItems: [] }\n default:\n return state\n }\n}\n\n/**\n * Stateful hook used to track items that need to be confirmed by the user. Methods are provided to support confirming\n * individual items or all items, as well as skipping confirmation for individual items or all items.\n */\nfunction useConfirmItems<T = unknown>(): UseConfirmItemsReturn<T> {\n const [state, dispatch] = useReducer(reducer<T>, {\n pendingItems: [],\n confirmedItems: [],\n })\n\n const addItemsPendingConfirmation = (...items: T[]) => {\n dispatch({ type: 'addItemsPendingConfirmation', newItems: items })\n }\n\n const confirmItem = (...items: T[]) => {\n dispatch({ type: 'confirmItem', itemsToConfirm: items })\n return reducer(state, { type: 'confirmItem', itemsToConfirm: items })\n }\n\n const removePendingItems = (...items: T[]) => {\n dispatch({ type: 'removePendingItems', itemsToRemove: items })\n return reducer(state, { type: 'removePendingItems', itemsToRemove: items })\n }\n\n const clear = () => {\n dispatch({ type: 'clear' })\n }\n\n return {\n pendingItems: state.pendingItems,\n confirmedItems: state.confirmedItems,\n addItemsPendingConfirmation,\n confirmItem,\n removePendingItems,\n clear,\n }\n}\n\nexport default useConfirmItems\n"],"mappings":";;AAsBA,SAAS,EACP,GACA,GAKA;AACA,SAAQ,EAAO,MAAf;EACE,KAAK,8BACH,QAAO;GACL,GAAG;GACH,cAAc,CAAC,GAAG,EAAM,cAAc,GAAG,EAAO,SAAS;GAC1D;EACH,KAAK,cAQH,QAAO;GACL,gBARwB,CACxB,GAAG,EAAM,gBACT,GAAG,EAAO,eACX;GAMC,cALsB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,eAAe,SAAS,EAAE,CACxC;GAIA;EAEH,KAAK,sBAAsB;GACzB,IAAM,IAAkB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,cAAc,SAAS,EAAE,CACvC;AACD,UAAO;IACL,gBAAgB,EAAM;IACtB,cAAc;IACf;;EAEH,KAAK,QACH,QAAO;GAAE,gBAAgB,EAAE;GAAE,cAAc,EAAE;GAAE;EACjD,QACE,QAAO;;;AAQb,SAAS,IAAyD;CAChE,IAAM,CAAC,GAAO,KAAY,EAAW,GAAY;EAC/C,cAAc,EAAE;EAChB,gBAAgB,EAAE;EACnB,CAAC;AAoBF,QAAO;EACL,cAAc,EAAM;EACpB,gBAAgB,EAAM;EACtB,8BArBmC,GAAG,MAAe;AACrD,KAAS;IAAE,MAAM;IAA+B,UAAU;IAAO,CAAC;;EAqBlE,cAlBmB,GAAG,OACtB,EAAS;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC,EACjD,EAAQ,GAAO;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC;EAiBrE,qBAd0B,GAAG,OAC7B,EAAS;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC,EACvD,EAAQ,GAAO;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC;EAa3E,aAVkB;AAClB,KAAS,EAAE,MAAM,SAAS,CAAC;;EAU5B"}
1
+ {"version":3,"file":"useConfirmItems.js","names":[],"sources":["../../../src/utils/hooks/useConfirmItems.ts"],"sourcesContent":["import { useReducer } from 'react'\n\ntype UseConfirmItemsReturn<T = unknown> = {\n /** The set of items that are awaiting confirmation */\n pendingItems: T[]\n /** The set of items that have been confirmed */\n confirmedItems: T[]\n /** Adds one or more items to the pending list */\n addItemsPendingConfirmation: (...items: T[]) => void\n /** Confirms one or more items, adding them to the confirmed list and removing them from the pending list\n * @returns the confirmed items */\n confirmItem: (...items: T[]) => { confirmedItems: T[]; pendingItems: T[] }\n /** Remove one or more items that are pending from the list\n * @returns the resulting state of confirmed items and pending items */\n removePendingItems: (...items: T[]) => {\n confirmedItems: T[]\n pendingItems: T[]\n }\n /** Clear all items from the confirmed and pending lists */\n clear: () => void\n}\n\nfunction reducer<T = unknown>(\n state: { pendingItems: T[]; confirmedItems: T[] },\n action:\n | { type: 'addItemsPendingConfirmation'; newItems: T[] }\n | { type: 'confirmItem'; itemsToConfirm: T[] }\n | { type: 'removePendingItems'; itemsToRemove: T[] }\n | { type: 'clear' },\n) {\n switch (action.type) {\n case 'addItemsPendingConfirmation':\n return {\n ...state,\n pendingItems: [...state.pendingItems, ...action.newItems],\n }\n case 'confirmItem': {\n const newConfirmedItems = [\n ...state.confirmedItems,\n ...action.itemsToConfirm,\n ]\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToConfirm.includes(i),\n )\n return {\n confirmedItems: newConfirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'removePendingItems': {\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToRemove.includes(i),\n )\n return {\n confirmedItems: state.confirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'clear':\n return { confirmedItems: [], pendingItems: [] }\n default:\n return state\n }\n}\n\n/**\n * Stateful hook used to track items that need to be confirmed by the user. Methods are provided to support confirming\n * individual items or all items, as well as skipping confirmation for individual items or all items.\n */\nfunction useConfirmItems<T = unknown>(): UseConfirmItemsReturn<T> {\n const [state, dispatch] = useReducer(reducer<T>, {\n pendingItems: [],\n confirmedItems: [],\n })\n\n const addItemsPendingConfirmation = (...items: T[]) => {\n dispatch({ type: 'addItemsPendingConfirmation', newItems: items })\n }\n\n const confirmItem = (...items: T[]) => {\n dispatch({ type: 'confirmItem', itemsToConfirm: items })\n return reducer(state, { type: 'confirmItem', itemsToConfirm: items })\n }\n\n const removePendingItems = (...items: T[]) => {\n dispatch({ type: 'removePendingItems', itemsToRemove: items })\n return reducer(state, { type: 'removePendingItems', itemsToRemove: items })\n }\n\n const clear = () => {\n dispatch({ type: 'clear' })\n }\n\n return {\n pendingItems: state.pendingItems,\n confirmedItems: state.confirmedItems,\n addItemsPendingConfirmation,\n confirmItem,\n removePendingItems,\n clear,\n }\n}\n\nexport default useConfirmItems\n"],"mappings":";;AAsBA,SAAS,EACP,GACA,GAKA;AACA,SAAQ,EAAO,MAAf;EACE,KAAK,8BACH,QAAO;GACL,GAAG;GACH,cAAc,CAAC,GAAG,EAAM,cAAc,GAAG,EAAO,SAAS;GAC1D;EACH,KAAK,cAQH,QAAO;GACL,gBAAgB,CAPhB,GAAG,EAAM,gBACT,GAAG,EAAO,eAMM;GAChB,cALsB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,eAAe,SAAS,EAAE,CAIzB;GACf;EAEH,KAAK,sBAAsB;GACzB,IAAM,IAAkB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,cAAc,SAAS,EAAE,CACvC;AACD,UAAO;IACL,gBAAgB,EAAM;IACtB,cAAc;IACf;;EAEH,KAAK,QACH,QAAO;GAAE,gBAAgB,EAAE;GAAE,cAAc,EAAE;GAAE;EACjD,QACE,QAAO;;;AAQb,SAAS,IAAyD;CAChE,IAAM,CAAC,GAAO,KAAY,EAAW,GAAY;EAC/C,cAAc,EAAE;EAChB,gBAAgB,EAAE;EACnB,CAAC;AAoBF,QAAO;EACL,cAAc,EAAM;EACpB,gBAAgB,EAAM;EACtB,8BArBmC,GAAG,MAAe;AACrD,KAAS;IAAE,MAAM;IAA+B,UAAU;IAAO,CAAC;;EAqBlE,cAlBmB,GAAG,OACtB,EAAS;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC,EACjD,EAAQ,GAAO;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC;EAiBrE,qBAd0B,GAAG,OAC7B,EAAS;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC,EACvD,EAAQ,GAAO;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC;EAa3E,aAVkB;AAClB,KAAS,EAAE,MAAM,SAAS,CAAC;;EAU5B"}