@jbrowse/core 5.0.0-beta.7 → 5.0.0-beta.9

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 (379) hide show
  1. package/README.md +427 -130
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.d.ts +3 -2
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +5 -3
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -5
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +8 -9
  6. package/esm/BaseFeatureWidget/BaseFeatureDetail/CoreDetails.js +8 -13
  7. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.d.ts +2 -1
  8. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.js +3 -3
  9. package/esm/BaseFeatureWidget/BaseFeatureDetail/FieldActionsButton.d.ts +8 -0
  10. package/esm/BaseFeatureWidget/BaseFeatureDetail/FieldActionsButton.js +24 -0
  11. package/esm/BaseFeatureWidget/BaseFeatureDetail/SimpleField.d.ts +3 -2
  12. package/esm/BaseFeatureWidget/BaseFeatureDetail/SimpleField.js +5 -3
  13. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.d.ts +1 -0
  14. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.js +1 -4
  15. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.d.ts +16 -0
  16. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +53 -0
  17. package/esm/BaseFeatureWidget/BaseFeatureDetail/util.d.ts +0 -8
  18. package/esm/BaseFeatureWidget/BaseFeatureDetail/util.js +0 -16
  19. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceContents.js +7 -34
  20. package/esm/BaseFeatureWidget/configSchema.d.ts +1 -1
  21. package/esm/BaseFeatureWidget/formatDetails.d.ts +13 -29
  22. package/esm/BaseFeatureWidget/formatDetails.js +46 -79
  23. package/esm/BaseFeatureWidget/index.d.ts +1 -0
  24. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +35 -28
  25. package/esm/BaseFeatureWidget/stateModelFactory.js +94 -102
  26. package/esm/BaseFeatureWidget/types.d.ts +3 -0
  27. package/esm/BaseFeatureWidget/util.d.ts +0 -3
  28. package/esm/BaseFeatureWidget/util.js +0 -17
  29. package/esm/PluginLoader.d.ts +14 -9
  30. package/esm/PluginLoader.js +34 -26
  31. package/esm/PluginManager.d.ts +4 -4
  32. package/esm/PluginManager.js +1 -1
  33. package/esm/ReExports/coreModules.generated.d.ts +2 -0
  34. package/esm/ReExports/coreModules.generated.js +492 -0
  35. package/esm/ReExports/coreWorkerModules.generated.d.ts +2 -0
  36. package/esm/ReExports/coreWorkerModules.generated.js +453 -0
  37. package/esm/ReExports/frameworkModules.d.ts +2 -0
  38. package/esm/ReExports/frameworkModules.js +67 -0
  39. package/esm/ReExports/frameworkShared.d.ts +12 -0
  40. package/esm/ReExports/frameworkShared.js +16 -0
  41. package/esm/ReExports/frameworkWorkerModules.generated.d.ts +2 -0
  42. package/esm/ReExports/frameworkWorkerModules.generated.js +130 -0
  43. package/esm/ReExports/list.js +493 -79
  44. package/esm/ReExports/modules.d.ts +1 -1
  45. package/esm/ReExports/modules.js +10 -87
  46. package/esm/ReExports/registry.d.ts +1 -0
  47. package/esm/ReExports/registry.js +23 -5
  48. package/esm/ReExports/uiStub.d.ts +2 -1
  49. package/esm/ReExports/uiStub.js +24 -2
  50. package/esm/ReExports/workerModules.js +8 -12
  51. package/esm/TextSearch/TextSearchManager.d.ts +3 -12
  52. package/esm/TextSearch/TextSearchManager.js +22 -10
  53. package/esm/assemblyManager/assembly.d.ts +10 -14
  54. package/esm/assemblyManager/assembly.js +10 -14
  55. package/esm/assemblyManager/assemblyConfigSchema.d.ts +23 -81
  56. package/esm/assemblyManager/assemblyManager.d.ts +10 -16
  57. package/esm/assemblyManager/assemblyManager.js +7 -10
  58. package/esm/assemblyManager/loadRefNameMap.js +3 -3
  59. package/esm/configuration/configurationSchema.d.ts +75 -31
  60. package/esm/configuration/configurationSchema.js +74 -40
  61. package/esm/configuration/configurationSchemaUnion.d.ts +39 -0
  62. package/esm/configuration/configurationSchemaUnion.js +59 -0
  63. package/esm/configuration/configurationSlot.d.ts +37 -12
  64. package/esm/configuration/configurationSlot.js +83 -22
  65. package/esm/configuration/formatAboutConfigSchema.d.ts +7 -21
  66. package/esm/configuration/formatDetailsConfigSchema.d.ts +11 -37
  67. package/esm/configuration/formatDetailsConfigSchema.js +10 -13
  68. package/esm/configuration/getConf.d.ts +17 -9
  69. package/esm/configuration/getConf.js +52 -7
  70. package/esm/configuration/index.d.ts +11 -4
  71. package/esm/configuration/index.js +7 -4
  72. package/esm/configuration/mergeFormatCallbacks.d.ts +6 -7
  73. package/esm/configuration/mergeFormatCallbacks.js +13 -10
  74. package/esm/configuration/readConfObject.d.ts +2 -22
  75. package/esm/configuration/readConfObject.js +15 -49
  76. package/esm/configuration/requirements.d.ts +37 -0
  77. package/esm/configuration/requirements.js +88 -0
  78. package/esm/configuration/schemaRegistry.d.ts +21 -1
  79. package/esm/configuration/schemaRegistry.js +11 -0
  80. package/esm/configuration/schemaTypes.d.ts +3 -0
  81. package/esm/configuration/schemaTypes.js +10 -0
  82. package/esm/configuration/slotFacade.d.ts +35 -4
  83. package/esm/configuration/slotFacade.js +96 -9
  84. package/esm/configuration/snapshotPreprocess.d.ts +27 -0
  85. package/esm/configuration/snapshotPreprocess.js +102 -0
  86. package/esm/configuration/types.d.ts +161 -22
  87. package/esm/data_adapters/BaseAdapter/BaseAdapter.d.ts +2 -2
  88. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +11 -2
  89. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +12 -1
  90. package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +2 -3
  91. package/esm/data_adapters/BaseAdapter/featureDensity.js +2 -3
  92. package/esm/data_adapters/BaseAdapter/index.d.ts +22 -3
  93. package/esm/data_adapters/BaseAdapter/types.d.ts +3 -4
  94. package/esm/data_adapters/BaseAdapter/zoomRange.d.ts +10 -0
  95. package/esm/data_adapters/BaseAdapter/zoomRange.js +1 -0
  96. package/esm/data_adapters/CytobandAdapter/configSchema.d.ts +9 -11
  97. package/esm/data_adapters/getSequenceSubAdapter.d.ts +1 -3
  98. package/esm/data_adapters/getSequenceSubAdapter.js +1 -3
  99. package/esm/pluggableElementTypes/AdapterType.d.ts +7 -7
  100. package/esm/pluggableElementTypes/RpcMethodType.d.ts +1 -1
  101. package/esm/pluggableElementTypes/RpcMethodType.js +3 -8
  102. package/esm/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions.d.ts +1 -2
  103. package/esm/pluggableElementTypes/TextSearchAdapterType.d.ts +7 -7
  104. package/esm/pluggableElementTypes/index.d.ts +1 -1
  105. package/esm/pluggableElementTypes/index.js +1 -1
  106. package/esm/pluggableElementTypes/models/BaseConnectionModelFactory.d.ts +17 -7
  107. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +45 -48
  108. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +40 -36
  109. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +10 -13
  110. package/esm/pluggableElementTypes/models/BaseTrackModel.js +6 -6
  111. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +37 -17
  112. package/esm/pluggableElementTypes/models/BaseViewModel.js +25 -10
  113. package/esm/pluggableElementTypes/models/InternetAccountModel.d.ts +26 -16
  114. package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +1 -1
  115. package/esm/pluggableElementTypes/models/baseConnectionConfig.d.ts +22 -18
  116. package/esm/pluggableElementTypes/models/baseConnectionConfig.js +5 -5
  117. package/esm/pluggableElementTypes/models/baseInternetAccountConfig.d.ts +26 -31
  118. package/esm/pluggableElementTypes/models/baseTrackConfig.d.ts +57 -141
  119. package/esm/pluggableElementTypes/models/baseTrackConfig.js +22 -26
  120. package/esm/pluggableElementTypes/models/components/SaveTrackData.js +2 -2
  121. package/esm/pluggableElementTypes/models/components/fetchTrackData.d.ts +3 -4
  122. package/esm/pluggableElementTypes/models/components/fetchTrackData.js +3 -3
  123. package/esm/pluggableElementTypes/models/expandTrackConfigShorthand.d.ts +15 -7
  124. package/esm/pluggableElementTypes/models/expandTrackConfigShorthand.js +38 -29
  125. package/esm/pluggableElementTypes/models/index.d.ts +1 -2
  126. package/esm/pluggableElementTypes/models/index.js +1 -2
  127. package/esm/pluggableElementTypes/models/saveTrackFileTypes/genbank.d.ts +3 -3
  128. package/esm/pluggableElementTypes/models/saveTrackFileTypes/genbank.js +4 -4
  129. package/esm/pluggableElementTypes/models/saveTrackFileTypes/types.d.ts +3 -3
  130. package/esm/rpc/BaseRpcDriver.d.ts +1 -2
  131. package/esm/rpc/BaseRpcDriver.js +23 -30
  132. package/esm/rpc/MainThreadRpcDriver.d.ts +2 -2
  133. package/esm/rpc/MainThreadRpcDriver.js +13 -8
  134. package/esm/rpc/RpcClient.d.ts +17 -6
  135. package/esm/rpc/RpcClient.js +42 -12
  136. package/esm/rpc/RpcManager.d.ts +8 -10
  137. package/esm/rpc/RpcManager.js +1 -1
  138. package/esm/rpc/RpcRegistry.d.ts +8 -2
  139. package/esm/rpc/RpcServer.d.ts +3 -1
  140. package/esm/rpc/RpcServer.js +19 -12
  141. package/esm/rpc/WebWorkerRpcDriver.d.ts +3 -3
  142. package/esm/rpc/WebWorkerRpcDriver.js +9 -47
  143. package/esm/rpc/byteBudget.d.ts +3 -5
  144. package/esm/rpc/byteBudget.js +4 -4
  145. package/esm/rpc/configSchema.d.ts +7 -21
  146. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  147. package/esm/rpc/coreRpcMethods.js +1 -0
  148. package/esm/rpc/methods/CoreEncodeFeatures.d.ts +6 -5
  149. package/esm/rpc/methods/CoreEncodeFeatures.js +26 -31
  150. package/esm/rpc/methods/CoreGetEncodedFeature.d.ts +13 -0
  151. package/esm/rpc/methods/CoreGetEncodedFeature.js +31 -0
  152. package/esm/rpc/methods/CoreGetExportData.js +2 -2
  153. package/esm/rpc/methods/CoreGetFeatureDensity.js +2 -2
  154. package/esm/rpc/methods/CoreGetFeatures.js +2 -2
  155. package/esm/rpc/methods/CoreGetSequence.js +2 -2
  156. package/esm/rpc/methods/layerFeatures.d.ts +26 -0
  157. package/esm/rpc/methods/layerFeatures.js +40 -0
  158. package/esm/svg/SvgExport.js +3 -4
  159. package/esm/svg/SvgThemeProviders.d.ts +12 -0
  160. package/esm/svg/SvgThemeProviders.js +15 -0
  161. package/esm/svg/exportViewSvg.d.ts +32 -0
  162. package/esm/svg/exportViewSvg.js +13 -0
  163. package/esm/svg/normalizeSvgMarkup.d.ts +7 -0
  164. package/esm/svg/normalizeSvgMarkup.js +99 -0
  165. package/esm/svg/serializeSvg.d.ts +7 -0
  166. package/esm/svg/serializeSvg.js +11 -0
  167. package/esm/svg/svgReady.d.ts +8 -7
  168. package/esm/svg/svgReady.js +8 -7
  169. package/esm/svg/trackNames.d.ts +5 -5
  170. package/esm/svg/trackNames.js +6 -6
  171. package/esm/svg/wrapSvgExport.js +6 -74
  172. package/esm/ui/CascadingMenu.js +83 -86
  173. package/esm/ui/ErrorMessageStackTraceDialog.js +0 -5
  174. package/esm/ui/FileSelector/useInternetAccounts.d.ts +156 -96
  175. package/esm/ui/HoverMenu.d.ts +1 -2
  176. package/esm/ui/HoverMenu.js +6 -3
  177. package/esm/ui/JexlFilterDialog.d.ts +13 -10
  178. package/esm/ui/JexlFilterDialog.js +122 -49
  179. package/esm/ui/JexlFilterRowEditors.d.ts +15 -0
  180. package/esm/ui/JexlFilterRowEditors.js +90 -0
  181. package/esm/ui/LazyBaseTooltip.d.ts +4 -0
  182. package/esm/ui/LazyBaseTooltip.js +9 -0
  183. package/esm/ui/LoadingOverlay.js +3 -1
  184. package/esm/ui/MonospaceTextField.d.ts +2 -1
  185. package/esm/ui/MonospaceTextField.js +21 -19
  186. package/esm/ui/PaletteContext.d.ts +7 -9
  187. package/esm/ui/PaletteContext.js +24 -10
  188. package/esm/ui/RefNameAutocomplete/index.d.ts +1 -2
  189. package/esm/ui/RefNameAutocomplete/index.js +1 -1
  190. package/esm/ui/ScrollChrome.d.ts +6 -9
  191. package/esm/ui/ScrollChrome.js +5 -6
  192. package/esm/ui/ScrollEdgeShadow.d.ts +6 -9
  193. package/esm/ui/ScrollEdgeShadow.js +9 -24
  194. package/esm/ui/SettingsChangesTable.js +3 -3
  195. package/esm/ui/VerticalScrollbar.d.ts +13 -34
  196. package/esm/ui/VerticalScrollbar.js +29 -83
  197. package/esm/ui/ViewLoadingScreen.d.ts +2 -2
  198. package/esm/ui/ViewLoadingScreen.js +3 -3
  199. package/esm/ui/colorScale.d.ts +26 -1
  200. package/esm/ui/colorScale.js +28 -5
  201. package/esm/ui/colors.d.ts +49 -1
  202. package/esm/ui/colors.js +139 -4
  203. package/esm/ui/hoverBoxStyle.d.ts +14 -2
  204. package/esm/ui/hoverBoxStyle.js +17 -2
  205. package/esm/ui/index.d.ts +5 -3
  206. package/esm/ui/index.js +9 -7
  207. package/esm/ui/launchViewMenu.js +3 -3
  208. package/esm/ui/legendSpec.d.ts +2 -0
  209. package/esm/ui/legendSpec.js +3 -0
  210. package/esm/ui/palette.d.ts +0 -1
  211. package/esm/ui/palette.js +3 -27
  212. package/esm/ui/replaceViewAction.d.ts +1 -1
  213. package/esm/ui/replaceViewAction.js +1 -1
  214. package/esm/ui/theme.d.ts +1 -1
  215. package/esm/ui/theme.js +6 -40
  216. package/esm/ui/viewHeaderHeight.d.ts +1 -0
  217. package/esm/ui/viewHeaderHeight.js +1 -0
  218. package/esm/util/Base1DUtils.d.ts +1 -1
  219. package/esm/util/Base1DUtils.js +2 -2
  220. package/esm/util/SvgCanvas.d.ts +15 -13
  221. package/esm/util/SvgCanvas.js +72 -53
  222. package/esm/util/aborting.d.ts +39 -0
  223. package/esm/util/aborting.js +84 -0
  224. package/esm/util/adapterMetadata.d.ts +1 -1
  225. package/esm/util/adapterMetadata.js +6 -6
  226. package/esm/util/addAndShowTrack.d.ts +1 -4
  227. package/esm/util/addRelativeUris.d.ts +10 -2
  228. package/esm/util/addRelativeUris.js +22 -7
  229. package/esm/util/addTrackFromWidget.d.ts +2 -5
  230. package/esm/util/animationAllowed.d.ts +9 -0
  231. package/esm/util/animationAllowed.js +13 -0
  232. package/esm/util/applyRowResizeWheel.d.ts +10 -5
  233. package/esm/util/applyRowResizeWheel.js +10 -21
  234. package/esm/util/bandLayout.d.ts +4 -4
  235. package/esm/util/bandLayout.js +4 -4
  236. package/esm/util/bezierConnector.js +5 -4
  237. package/esm/util/bgzfWorkerPool.d.ts +20 -74
  238. package/esm/util/bgzfWorkerPool.js +20 -74
  239. package/esm/util/cachedSetup.d.ts +1 -1
  240. package/esm/util/cachedSetup.js +2 -2
  241. package/esm/util/categoricalField.d.ts +37 -0
  242. package/esm/util/categoricalField.js +52 -0
  243. package/esm/util/color/cssColorsLevel4.d.ts +1 -0
  244. package/esm/util/color/cssColorsLevel4.js +1 -0
  245. package/esm/util/color/index.d.ts +5 -3
  246. package/esm/util/color/index.js +7 -4
  247. package/esm/util/colorBits.d.ts +1 -2
  248. package/esm/util/colorBits.js +17 -39
  249. package/esm/util/colorRamp.d.ts +27 -1
  250. package/esm/util/colorRamp.js +56 -2
  251. package/esm/util/colorSchemes.d.ts +9 -0
  252. package/esm/util/colorSchemes.js +7 -0
  253. package/esm/util/{createStopTokenRotation.d.ts → createAbortRotation.d.ts} +11 -14
  254. package/esm/util/{createStopTokenRotation.js → createAbortRotation.js} +14 -26
  255. package/esm/util/cssColorParse.d.ts +15 -0
  256. package/esm/util/cssColorParse.js +53 -0
  257. package/esm/util/diagonalizeRegions.d.ts +2 -3
  258. package/esm/util/diagonalizeRegions.js +7 -12
  259. package/esm/util/expandLooseSearchIndex.d.ts +7 -0
  260. package/esm/util/expandLooseSearchIndex.js +25 -0
  261. package/esm/util/featureTransforms.d.ts +30 -3
  262. package/esm/util/featureTransforms.js +233 -36
  263. package/esm/util/fetchAndMaybeUnzip.js +5 -6
  264. package/esm/util/fetchContext.d.ts +5 -6
  265. package/esm/util/fetchContext.js +1 -1
  266. package/esm/util/fetchHub.d.ts +3 -1
  267. package/esm/util/fetchHub.js +2 -2
  268. package/esm/util/fetchSeq.d.ts +2 -3
  269. package/esm/util/fetchSeq.js +2 -2
  270. package/esm/util/fieldReader.d.ts +17 -0
  271. package/esm/util/fieldReader.js +62 -0
  272. package/esm/util/groupKeys.d.ts +75 -0
  273. package/esm/util/groupKeys.js +152 -0
  274. package/esm/util/highlights.d.ts +6 -4
  275. package/esm/util/highlights.js +12 -25
  276. package/esm/util/hooks.d.ts +1 -2
  277. package/esm/util/hooks.js +1 -2
  278. package/esm/util/index.d.ts +3 -3
  279. package/esm/util/index.js +4 -3
  280. package/esm/util/indexCandidates.js +1 -1
  281. package/esm/util/installFetch.d.ts +13 -10
  282. package/esm/util/installFetch.js +11 -10
  283. package/esm/util/installPrerequisiteFetch.d.ts +5 -3
  284. package/esm/util/installPrerequisiteFetch.js +3 -4
  285. package/esm/util/io/index.js +12 -12
  286. package/esm/util/isDataCurrent.d.ts +7 -5
  287. package/esm/util/isDataCurrent.js +9 -6
  288. package/esm/util/jexl.js +24 -21
  289. package/esm/util/jexlFilterRows.d.ts +89 -0
  290. package/esm/util/jexlFilterRows.js +252 -0
  291. package/esm/util/jexlFilters.d.ts +7 -5
  292. package/esm/util/jexlStrings.d.ts +3 -4
  293. package/esm/util/jexlStrings.js +2 -3
  294. package/esm/util/legendCandidates.d.ts +31 -27
  295. package/esm/util/legendCandidates.js +79 -53
  296. package/esm/util/localStorage.js +4 -4
  297. package/esm/util/markEncoding.d.ts +32 -16
  298. package/esm/util/markEncoding.js +177 -150
  299. package/esm/util/markEncodingTypes.d.ts +137 -66
  300. package/esm/util/mstUtils.d.ts +19 -22
  301. package/esm/util/mstUtils.js +19 -22
  302. package/esm/util/numericUtils.d.ts +7 -0
  303. package/esm/util/numericUtils.js +16 -0
  304. package/esm/util/numericValue.d.ts +14 -0
  305. package/esm/util/numericValue.js +34 -0
  306. package/esm/util/openFeatureWidget.js +1 -2
  307. package/esm/util/openViews.d.ts +1 -0
  308. package/esm/util/openViews.js +3 -3
  309. package/esm/util/parseLineByLine.d.ts +3 -4
  310. package/esm/util/parseLineByLine.js +7 -13
  311. package/esm/util/progress.d.ts +21 -21
  312. package/esm/util/progress.js +27 -26
  313. package/esm/util/renameRegions.d.ts +1 -2
  314. package/esm/util/renderToStaticMarkup.js +16 -16
  315. package/esm/util/rxjs.d.ts +7 -8
  316. package/esm/util/rxjs.js +12 -15
  317. package/esm/util/simpleFeature.d.ts +0 -1
  318. package/esm/util/svgColorProps.d.ts +11 -0
  319. package/esm/util/svgColorProps.js +28 -1
  320. package/esm/util/tabix.d.ts +3 -4
  321. package/esm/util/tabix.js +6 -7
  322. package/esm/util/thresholdScale.d.ts +54 -0
  323. package/esm/util/thresholdScale.js +112 -0
  324. package/esm/util/timeGate.d.ts +4 -5
  325. package/esm/util/timeGate.js +4 -5
  326. package/esm/util/trackConfigDelta.d.ts +22 -18
  327. package/esm/util/trackConfigDelta.js +40 -38
  328. package/esm/util/tracks.d.ts +27 -4
  329. package/esm/util/tracks.js +116 -43
  330. package/esm/util/translateTranscript.d.ts +44 -0
  331. package/esm/util/translateTranscript.js +102 -0
  332. package/esm/util/types/index.d.ts +19 -7
  333. package/esm/util/types/services.d.ts +1 -1
  334. package/esm/util/unknownSnapshotKeys.d.ts +15 -0
  335. package/esm/util/unknownSnapshotKeys.js +13 -0
  336. package/esm/util/useFeatureSequence.js +4 -4
  337. package/esm/util/useFetch.d.ts +3 -4
  338. package/esm/util/useFetch.js +5 -6
  339. package/esm/util/usePanelVirtualScroll.d.ts +7 -39
  340. package/esm/util/usePanelVirtualScroll.js +9 -40
  341. package/esm/util/useRowVirtualScroll.d.ts +6 -29
  342. package/esm/util/useRowVirtualScroll.js +8 -49
  343. package/esm/util/useVirtualScrollWheel.d.ts +20 -4
  344. package/esm/util/useVirtualScrollWheel.js +40 -84
  345. package/esm/util/viewHighlights.d.ts +3 -0
  346. package/esm/util/viewHighlights.js +5 -0
  347. package/esm/util/viewStatus.d.ts +24 -15
  348. package/esm/util/viewStatus.js +17 -0
  349. package/esm/util/withLaunchInput.d.ts +1 -1
  350. package/esm/util/withLaunchInput.js +0 -4
  351. package/package.json +26 -12
  352. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/DLGAP3.d.ts +0 -80
  353. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/DLGAP3.js +0 -583
  354. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +0 -141
  355. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.js +0 -1116
  356. package/esm/ReExports/BaseFeatureDetails.d.ts +0 -1
  357. package/esm/ReExports/BaseFeatureDetails.js +0 -10
  358. package/esm/ReExports/barrelOnlyNames.d.ts +0 -5
  359. package/esm/ReExports/barrelOnlyNames.js +0 -63
  360. package/esm/ReExports/index.d.ts +0 -1
  361. package/esm/ReExports/index.js +0 -1
  362. package/esm/ReExports/publicTracks.d.ts +0 -1
  363. package/esm/ReExports/publicTracks.js +0 -14
  364. package/esm/ReExports/publicUi.d.ts +0 -4
  365. package/esm/ReExports/publicUi.js +0 -33
  366. package/esm/ReExports/publicUtil.d.ts +0 -1
  367. package/esm/ReExports/publicUtil.js +0 -32
  368. package/esm/ReExports/sharedModules.d.ts +0 -96
  369. package/esm/ReExports/sharedModules.js +0 -66
  370. package/esm/ReExports/workerNamespaceNames.d.ts +0 -11
  371. package/esm/ReExports/workerNamespaceNames.js +0 -275
  372. package/esm/pluggableElementTypes/models/HighlightsMixin.d.ts +0 -51
  373. package/esm/pluggableElementTypes/models/HighlightsMixin.js +0 -78
  374. package/esm/ui/highlightChipReveal.d.ts +0 -32
  375. package/esm/ui/highlightChipReveal.js +0 -39
  376. package/esm/util/adapterConfigKey.d.ts +0 -12
  377. package/esm/util/adapterConfigKey.js +0 -14
  378. package/esm/util/stopToken.d.ts +0 -212
  379. package/esm/util/stopToken.js +0 -438
@@ -1,212 +0,0 @@
1
- import type { TimeGate } from './timeGate.ts';
2
- /**
3
- * Stop tokens cancel long-running work wherever an RPC method runs — a web
4
- * worker, or in-band on the main thread under `MainThreadRpcDriver`.
5
- *
6
- * Stopping a token records its id locally and posts it to every booted worker,
7
- * whose `RpcServer` records it too, so a check is a set lookup — free, exact,
8
- * and asking nothing of the deployment. A worker delivers that message whenever
9
- * it yields, so this covers any check reached after a *macrotask-yielding*
10
- * await; awaiting an already-resolved promise only drains microtasks, which is
11
- * the same distinction `abortBreakPoint` in `aborting.ts` exists for.
12
- *
13
- * A loop that never yields can be interrupted only by something it can read
14
- * *synchronously*, which the message path by definition cannot offer. Two
15
- * mechanisms cover that, in {@link checkStopTokenThrottled}:
16
- *
17
- * - an atomic flag in a `SharedArrayBuffer` token, where the page happens to be
18
- * cross-origin isolated. Cheap, but never assume it: SAB needs COOP/COEP on
19
- * the top-level document, which an embeddable library cannot require of its
20
- * host page. **In practice our deployments do not set those headers, so the
21
- * string path below is not the fallback — it is the path.** Judge any
22
- * cancellation bug by what it does to a string token, and don't spend
23
- * complexity optimizing the SAB branch: a lost cancellation that "only"
24
- * affects string tokens affects everybody.
25
- * - otherwise a revocable blob URL probed by synchronous XHR, throttled because
26
- * it is expensive (https://yoyo-code.com/how-to-stop-synchronous-web-worker/,
27
- * (c) 2022 Matyáš Racek, MIT).
28
- *
29
- * **The blob probe was deleted once and had to be restored — don't repeat it.**
30
- * `website/scripts/cancel-bench.ts` measured it at zero (median 513 ms settle
31
- * either way over a 2000x BAM cancel burst) and that measurement was sound but
32
- * scoped: every loop on the alignments path is already chunked by awaits at
33
- * region granularity, so there was nothing there for an intra-loop probe to
34
- * interrupt. The counter-example is the multi-sample-variant genotype matrix
35
- * fill (`VariantRPC/getGenotypeMatrix.ts`) — millions of cells with no await
36
- * anywhere, where this probe is the only thing that can stop the work, and
37
- * which that bench never exercises. Any future attempt to delete this needs a
38
- * cancel measurement on an await-free workload, not on a pileup. LD's O(n²)
39
- * pair fill was the original example and went with the in-browser estimator.
40
- *
41
- * ## Which path runs where
42
- *
43
- * | realm | isolated? | token | what a non-yielding loop can read |
44
- * | --- | --- | --- | --- |
45
- * | jbrowse-web / jbrowse.org | no, by decision (ADR-056) | string | stopped-id set + blob probe |
46
- * | embedded (`@jbrowse/react-*`) | only if the HOST sets COOP/COEP | string | stopped-id set + blob probe |
47
- * | jbrowse-desktop, packaged + dev | no — measured, see the gate | string | stopped-id set + blob probe |
48
- * | jest | no | string | stopped-id set only |
49
- * | an isolated embedding host | yes | SharedArrayBuffer | atomic read |
50
- *
51
- * The blob probe is the half that needs a worker (`probeBlobUrl` gates on
52
- * `isWebWorker()` and a `blob:` id), which is why jest reads the set alone —
53
- * and why a cancellation that only a probe could catch has no test covering it.
54
- *
55
- * **So nothing we ship mints a `SharedArrayBuffer` token.** The last row is the
56
- * whole of the SAB branch's reach: a host page that isolates itself gets the
57
- * fast path for free, which is why ADR-056 kept the code and why
58
- * `website/scripts/coi-probe.ts` exists to prove it still works. Every SAB arm
59
- * below is marked, and each one is dead in every configuration we control.
60
- *
61
- * Two consequences worth stating, because both have bitten:
62
- *
63
- * - **Judge every cancellation change by the string path.** A bug that "only"
64
- * affects string tokens affects everybody.
65
- * - **Don't spend complexity on the SAB arms** — no caching, no fast paths, no
66
- * cleverness. They are correctness-only, and their cost is that every reader
67
- * of a `StopToken` has to know a token might not be a string.
68
- */
69
- export type StopToken = string | SharedArrayBuffer;
70
- /**
71
- * Narrow an untyped RPC argument to a StopToken, so a caller holding
72
- * `Record<string, unknown>` can check one without a cast.
73
- */
74
- export declare function isStopToken(value: unknown): value is StopToken;
75
- export declare const hasSharedArrayBuffer: boolean;
76
- /**
77
- * Record that a string token has been stopped, and abort any {@link
78
- * stopTokenSignal} taken against it. Called locally by {@link stopStopToken},
79
- * and in a worker by `RpcServer` when the main thread posts a stopped id.
80
- */
81
- export declare function markStopTokenStopped(id: string): void;
82
- type StopTokenBroadcaster = (id: string) => void;
83
- /**
84
- * Register a transport that forwards a stopped token's id to wherever the work
85
- * is running. Drivers owning a worker pool register at construction and drop it
86
- * on destroy; `MainThreadRpcDriver` needs none — its work shares this module
87
- * instance and reads `stoppedIds` directly, which is why main-thread RPC gets
88
- * exact cancellation without any message at all.
89
- */
90
- export declare function registerStopTokenBroadcaster(fn: StopTokenBroadcaster): () => void;
91
- /**
92
- * Mint a token. **The right-hand branch is the one that runs** — see "Which
93
- * path runs where"; `hasSharedArrayBuffer` is false in everything we ship.
94
- */
95
- export declare function createStopToken(): StopToken;
96
- /**
97
- * Stop a token: every check against it now throws, every {@link
98
- * stopTokenSignal} taken against it aborts, and workers are told so their
99
- * in-flight calls do the same.
100
- *
101
- * Call it when an operation ends, not only when cancelling one — a completed
102
- * fetch's token otherwise pins its signal controllers until the next fetch
103
- * supersedes it.
104
- */
105
- export declare function stopStopToken(stopToken?: StopToken): void;
106
- /**
107
- * Whether this token has been stopped, without throwing. Prefer {@link
108
- * checkStopToken} where an abort should unwind the operation; use this to bail
109
- * out cleanly instead of throwing and catching one frame up.
110
- */
111
- export declare function isStopped(stopToken?: StopToken): boolean;
112
- /**
113
- * Throw an AbortError if this token has been stopped. Place at await
114
- * boundaries; this costs an atomic load or a set lookup, never a probe.
115
- */
116
- export declare function checkStopToken(stopToken?: StopToken): void;
117
- /**
118
- * Run an awaited stage under a stop token: checked before it starts, and again
119
- * once it settles. Prefer this to hand-placing {@link checkStopToken} around an
120
- * await — the point is that the check *after* comes with the one before, and the
121
- * one after is always the one that gets left out.
122
- *
123
- * It reads as sufficient to check at the top of an operation, because at the
124
- * time you write it the await below is not yet the slow part. `BaseRpcDriver`
125
- * checked at entry and then awaited `serializeArguments`, which resolves the
126
- * refName map, which downloads the adapter's index — and for an in-memory
127
- * adapter the whole file. A stop landing in that window woke a worker anyway.
128
- *
129
- * {@link updateStatus} and {@link withProgress} in `progress.ts` are this same
130
- * shape for a stage that also has something to say on the status channel; reach
131
- * for one of those when there is a label, and this when there isn't. What you
132
- * should not reach for is a bare `checkStopToken` sprinkled at a boundary you
133
- * happened to think of — the periodic in-loop case is
134
- * {@link checkStopTokenThrottled}, and it is driven by `createProgressReporter`
135
- * for the same reason.
136
- *
137
- * **Checking twice costs nothing, and it is worth knowing why**, because the
138
- * cost of a stop-token check is otherwise easy to remember backwards. The
139
- * expensive one is the synchronous XHR blob probe, which runs at up to ~10ms a
140
- * call — and it is reached ONLY from {@link checkStopTokenThrottled}, behind a
141
- * wall-clock gate with linear backoff, precisely because of that. This path is
142
- * {@link isStopped}: an atomic load for a SAB token, a `Map.has` for a string
143
- * one. So an extra boundary check here is free, and a per-item one in a tight
144
- * loop is not — which is the split the two functions exist to keep.
145
- */
146
- export declare function withStopTokenCheck<T>(stopToken: StopToken | undefined, fn: () => T | Promise<T>): Promise<T>;
147
- export interface StopTokenSignal {
148
- signal: AbortSignal;
149
- dispose: () => void;
150
- }
151
- /**
152
- * An `AbortSignal` wired to a stop token, so a canceled operation drops its
153
- * in-flight HTTP reads at the socket instead of downloading them to completion
154
- * and discarding the result. Call it where the work runs (worker-side for
155
- * worker RPC) and pass `signal` down through `BaseOptions`, which the gmod
156
- * readers already forward to `fetch`.
157
- *
158
- * No polling and no synchronous probe: a string token aborts off the same
159
- * posted id the await-boundary check reads, and a SharedArrayBuffer token off
160
- * `Atomics.waitAsync`, woken by the `Atomics.notify` in {@link stopStopToken}
161
- * — that one lands a turn or two after the notify rather than synchronously
162
- * with it, which costs nothing when the point is to stop a download.
163
- *
164
- * `dispose()` in a `finally` — an undisposed entry pins its controller until
165
- * the token is stopped.
166
- */
167
- export declare function stopTokenSignal(stopToken?: StopToken): StopTokenSignal;
168
- /**
169
- * Run `fn` with an `AbortSignal` wired to this stop token, releasing the signal
170
- * afterwards however `fn` settles. The shape to reach for at an adapter's read
171
- * call — {@link stopTokenSignal} directly is for the rare case that outlives one
172
- * awaited call, and it then owns remembering to dispose.
173
- */
174
- export declare function withStopTokenSignal<T>(stopToken: StopToken | undefined, fn: (signal: AbortSignal) => Promise<T>): Promise<T>;
175
- /**
176
- * Detects synchronously whether a string token has been stopped, for use where
177
- * no message can be delivered. Returns false when it cannot tell.
178
- *
179
- * A seam on the checker rather than a bare call, because the production
180
- * implementation only works in a worker on a `blob:` token, and is therefore
181
- * **inert under jest**. That inertness is not hypothetical: it let a deletion of
182
- * this whole mechanism pass all 6000+ unit tests. Overriding
183
- * `checker.syncProbe` is how a test asserts the loop consults it at all.
184
- */
185
- export type SyncStopProbe = (stopToken: string) => boolean;
186
- export interface StopTokenChecker {
187
- stopToken?: StopToken;
188
- sabView?: Int32Array;
189
- iters: number;
190
- checkInterval: number;
191
- checkDue: TimeGate;
192
- syncProbe: SyncStopProbe;
193
- }
194
- export declare function createStopTokenChecker(stopToken: StopToken | undefined): StopTokenChecker;
195
- /**
196
- * The in-loop counterpart to {@link checkStopToken}, throttled so a
197
- * million-iteration loop doesn't pay per item.
198
- *
199
- * Reads the stopped-id set *and* the synchronous probe, because a loop that
200
- * awaits between items can see the first and a loop that never yields can only
201
- * see the second. Both are behind the same wall-clock gate, so the per-item cost
202
- * is one counter bump.
203
- */
204
- export declare function checkStopTokenThrottled(checker?: StopTokenChecker): void;
205
- /**
206
- * Former name of {@link StopTokenChecker}. Still the primary spelling in several
207
- * in-tree RPC argument types (the three variants methods, `WiggleRPC/types.ts`)
208
- * as well as in external consumers, so it is a live alias rather than a
209
- * deprecation shim — renaming those is a wire-type edit with no behavior in it.
210
- */
211
- export type LastStopTokenCheck = StopTokenChecker;
212
- export {};
@@ -1,438 +0,0 @@
1
- import { makeAbortError } from './aborting.js';
2
- import { isWebWorker } from './isWebWorker.js';
3
- import { nanoid } from './nanoid.js';
4
- import { createTimeGate } from './timeGate.js';
5
- // Atomic flag values stored in the Int32Array view of the SharedArrayBuffer.
6
- // SAB ARM — unreached unless the host page is cross-origin isolated.
7
- const ABORT_FLAG_CLEAR = 0;
8
- const ABORT_FLAG_SET = 1;
9
- // How often the SAB path performs its (cheap) atomic read. SAB ARM.
10
- const SAB_CHECK_EVERY_N_ITERS = 10;
11
- // THE LIVE PATH's throttle. Minimum ms between a string token's in-loop checks
12
- // (also the linear-backoff step), and a cap so cancel latency stays bounded on a long-running loop. The
13
- // gate exists for the synchronous XHR probe, which is far too expensive per
14
- // item; the free set lookup rides along inside it.
15
- const STRING_CHECK_INTERVAL_MS = 50;
16
- const STRING_CHECK_INTERVAL_MAX_MS = 500;
17
- function isSharedArrayBuffer(value) {
18
- return (typeof SharedArrayBuffer !== 'undefined' &&
19
- value instanceof SharedArrayBuffer);
20
- }
21
- // A view is allocated per use rather than cached per buffer. That looks like an
22
- // easy win and is not one: the deployments this ships to are not cross-origin
23
- // isolated, so `hasSharedArrayBuffer` is false and every token is a string —
24
- // caching here would add a module-global WeakMap to speed up the branch that
25
- // never runs. The string path allocates nothing.
26
- function sabView(stopToken) {
27
- return new Int32Array(stopToken);
28
- }
29
- /**
30
- * Narrow an untyped RPC argument to a StopToken, so a caller holding
31
- * `Record<string, unknown>` can check one without a cast.
32
- */
33
- export function isStopToken(value) {
34
- return typeof value === 'string' || isSharedArrayBuffer(value);
35
- }
36
- // Browser support for SharedArrayBuffer requires cross-origin isolation
37
- // headers (COOP/COEP). Exported so diagnostic surfaces (about widget,
38
- // error stack trace) can show whether the page actually got the fast path.
39
- //
40
- // **ISOLATION IS THE QUESTION, not constructibility**, which is what the
41
- // sentence above has always claimed and what a bare `new SharedArrayBuffer(4)`
42
- // never asked. A browser gates the constructor on isolation, so on the web the
43
- // two agree — but a V8 embedder does not, and both of ours are embedders:
44
- //
45
- // - **jest.** Node constructs one unconditionally, so every test ran the SAB
46
- // path while every browser deployment ran the string path. That inverted the
47
- // fidelity — the SAB check reads its atomic once every 10 iterations where a
48
- // string token's time gate fires on the first call — and hid a real bug for
49
- // as long as it existed (`withProgress`'s kickoff `report(0)` throwing past
50
- // `endPhase()` on an already-stopped token, a9e5daba8f). A test suite that
51
- // cannot reach the deployment path by default is the wrong way round.
52
- // - **Electron.** Measured, because the guess went the other way: with
53
- // `nodeIntegration: true` and `contextIsolation: false` it looks as though
54
- // Node's `SharedArrayBuffer` would reach the renderer off a `file://` page.
55
- // It does not — Blink removes the binding and Node integration does not put
56
- // it back. On Electron 44, packaged (`file://`) and dev (`http://localhost`),
57
- // renderer and worker alike, `typeof SharedArrayBuffer` is `'undefined'` and
58
- // `crossOriginIsolated` is false. So jbrowse-desktop was already on the
59
- // string path and this changes nothing for it.
60
- //
61
- // Which leaves jest as the only realm this moves, and no shipped configuration
62
- // on the SAB branch at all unless a host isolates itself.
63
- //
64
- // What this does NOT change is the one configuration the branch is kept for:
65
- // an embedding host that sets COOP/COEP is cross-origin isolated, reports so
66
- // here, and still gets the fast path (ADR-056). `website/scripts/coi-probe.ts`
67
- // serves exactly that page and is the regression check.
68
- export const hasSharedArrayBuffer = (() => {
69
- if (globalThis.crossOriginIsolated !==
70
- true) {
71
- return false;
72
- }
73
- try {
74
- return isSharedArrayBuffer(new SharedArrayBuffer(4));
75
- }
76
- catch {
77
- return false;
78
- }
79
- })();
80
- // ---------------------------------------------------------------------------
81
- // Stopped-id registry (the message path)
82
- // ---------------------------------------------------------------------------
83
- // Retention for a stopped id. An entry is only useful while work carrying that
84
- // token could still check it, and work unwinds within moments of its own
85
- // cancellation, so this is bounded by *age* rather than by a count: a count cap
86
- // silently drops a still-live token's stopped-ness once enough other operations
87
- // have been cancelled, and answering "not stopped" for a stopped token is a lost
88
- // cancellation, not a slow one. Age bounds memory by cancellation *rate*
89
- // instead, and no plausible check arrives minutes after its own stop.
90
- const STOPPED_ID_TTL_MS = 5 * 60 * 1000;
91
- // Sweep only once the map is big enough that retention could matter, so the
92
- // common case is a plain insert.
93
- const STOPPED_ID_SWEEP_AT = 256;
94
- const stoppedIds = new Map();
95
- const signalControllers = new Map();
96
- /**
97
- * Record that a string token has been stopped, and abort any {@link
98
- * stopTokenSignal} taken against it. Called locally by {@link stopStopToken},
99
- * and in a worker by `RpcServer` when the main thread posts a stopped id.
100
- */
101
- export function markStopTokenStopped(id) {
102
- const now = Date.now();
103
- // delete first: `set` on an existing key keeps its original insertion
104
- // position, so a re-stopped id would sit at the front of the iteration order
105
- // carrying a fresh timestamp, and the sweep below would break there forever
106
- stoppedIds.delete(id);
107
- stoppedIds.set(id, now);
108
- if (stoppedIds.size > STOPPED_ID_SWEEP_AT) {
109
- // a Map iterates in insertion order, and timestamps only increase, so this
110
- // can stop at the first entry still inside the window
111
- for (const [key, stoppedAt] of stoppedIds) {
112
- if (now - stoppedAt < STOPPED_ID_TTL_MS) {
113
- break;
114
- }
115
- stoppedIds.delete(key);
116
- }
117
- }
118
- const controllers = signalControllers.get(id);
119
- if (controllers) {
120
- signalControllers.delete(id);
121
- for (const controller of controllers) {
122
- controller.abort(makeAbortError());
123
- }
124
- }
125
- }
126
- const broadcasters = new Set();
127
- /**
128
- * Register a transport that forwards a stopped token's id to wherever the work
129
- * is running. Drivers owning a worker pool register at construction and drop it
130
- * on destroy; `MainThreadRpcDriver` needs none — its work shares this module
131
- * instance and reads `stoppedIds` directly, which is why main-thread RPC gets
132
- * exact cancellation without any message at all.
133
- */
134
- export function registerStopTokenBroadcaster(fn) {
135
- broadcasters.add(fn);
136
- return () => {
137
- broadcasters.delete(fn);
138
- };
139
- }
140
- // ---------------------------------------------------------------------------
141
- // Create / stop
142
- // ---------------------------------------------------------------------------
143
- /**
144
- * Mint a token. **The right-hand branch is the one that runs** — see "Which
145
- * path runs where"; `hasSharedArrayBuffer` is false in everything we ship.
146
- */
147
- export function createStopToken() {
148
- // A fresh SharedArrayBuffer is already zeroed, i.e. ABORT_FLAG_CLEAR.
149
- return hasSharedArrayBuffer ? new SharedArrayBuffer(4) : createStringToken();
150
- }
151
- // One Blob behind every token. Object URLs minted over the same blob are
152
- // distinct ids and revoke independently, so nothing about the probe changes —
153
- // but a zoom mints these continuously and constructing the blob was showing up
154
- // beside `createObjectURL` in a profile.
155
- const emptyBlob = new Blob();
156
- // Uniqueness for the fallback id below, which is the one thing it must have and
157
- // the one thing `nanoid()` alone does not guarantee: it draws from
158
- // `crypto.getRandomValues`, and an environment is free to replace that. Ours
159
- // does — `config/jest/deterministicIds.js` swaps in a counter PRNG and RESETS
160
- // it before every test, so every test drew the same first id. Since a stopped
161
- // id is recorded in a module-global map, one stopped token then read as stopped
162
- // in every later test of the file, which is a live token that throws.
163
- //
164
- // Invisible until it mattered: this fallback serves the realms with no
165
- // `createObjectURL`, jest among them, and jest minted SharedArrayBuffers — whose
166
- // identity is the object — until `hasSharedArrayBuffer` started asking about
167
- // cross-origin isolation. A browser has `createObjectURL` and mints distinct
168
- // ids per call, so nothing here changes for a deployment.
169
- let stringTokenSeq = 0;
170
- function createStringToken() {
171
- // A blob URL serves as both the token's unique id (which is all the message
172
- // path needs) and the thing the sync probe fails against once revoked. Where
173
- // createObjectURL is unavailable a bare id still gives full await-boundary
174
- // cancellation — it just has no sync probe. That is a real degradation but no
175
- // longer the old hole, where such a token could never be cancelled at all.
176
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
177
- return URL.createObjectURL === undefined
178
- ? `${nanoid()}-${++stringTokenSeq}`
179
- : URL.createObjectURL(emptyBlob);
180
- }
181
- /**
182
- * Stop a token: every check against it now throws, every {@link
183
- * stopTokenSignal} taken against it aborts, and workers are told so their
184
- * in-flight calls do the same.
185
- *
186
- * Call it when an operation ends, not only when cancelling one — a completed
187
- * fetch's token otherwise pins its signal controllers until the next fetch
188
- * supersedes it.
189
- */
190
- export function stopStopToken(stopToken) {
191
- if (stopToken !== undefined) {
192
- // SAB ARM — dead in every configuration we ship
193
- if (isSharedArrayBuffer(stopToken)) {
194
- const view = sabView(stopToken);
195
- Atomics.store(view, 0, ABORT_FLAG_SET);
196
- // wakes the waitAsync watcher stopTokenSignal installs; free when there
197
- // is none waiting
198
- Atomics.notify(view, 0);
199
- }
200
- else if (!stoppedIds.has(stopToken)) {
201
- // Guarded, because one token is stopped two or three times over. A fetch's
202
- // `finally` stops it, the rotation's next `begin` stops what it is
203
- // superseding, and `cancel` stops it again — and each repeat re-inserted
204
- // the id and fanned a postMessage out to every worker in the pool for a
205
- // token they had all already been told about. The revoke is idempotent
206
- // but the broadcast is not free.
207
- markStopTokenStopped(stopToken);
208
- for (const broadcast of broadcasters) {
209
- broadcast(stopToken);
210
- }
211
- // revoking is what makes the sync probe start failing, so this is load
212
- // bearing for in-loop cancellation and not just cleanup
213
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
214
- URL.revokeObjectURL?.(stopToken);
215
- }
216
- }
217
- }
218
- /**
219
- * The production {@link SyncStopProbe}: a synchronous XHR against the token's
220
- * blob URL, which succeeds while the URL is live and throws once the main thread
221
- * has revoked it.
222
- *
223
- * Returns false rather than probing when it cannot tell — outside a worker
224
- * (synchronous XHR there is deprecated and would block the UI) or on a
225
- * non-`blob:` id (probing one would 404 and abort on the first check). jsdom is
226
- * not a worker global, so this is inert under jest; see {@link SyncStopProbe}
227
- * for what that cost us and how tests work around it.
228
- */
229
- function probeBlobUrl(stopToken) {
230
- let stopped = false;
231
- if (isWebWorker() && stopToken.startsWith('blob:')) {
232
- const xhr = new XMLHttpRequest();
233
- xhr.open('GET', stopToken, false);
234
- try {
235
- xhr.send(null);
236
- }
237
- catch {
238
- stopped = true;
239
- }
240
- }
241
- return stopped;
242
- }
243
- // ---------------------------------------------------------------------------
244
- // One-shot check (use at async boundaries)
245
- // ---------------------------------------------------------------------------
246
- /**
247
- * Whether this token has been stopped, without throwing. Prefer {@link
248
- * checkStopToken} where an abort should unwind the operation; use this to bail
249
- * out cleanly instead of throwing and catching one frame up.
250
- */
251
- export function isStopped(stopToken) {
252
- // the third arm is the live one; the middle is the SAB one
253
- return stopToken === undefined
254
- ? false
255
- : isSharedArrayBuffer(stopToken)
256
- ? Atomics.load(sabView(stopToken), 0) === ABORT_FLAG_SET
257
- : stoppedIds.has(stopToken);
258
- }
259
- /**
260
- * Throw an AbortError if this token has been stopped. Place at await
261
- * boundaries; this costs an atomic load or a set lookup, never a probe.
262
- */
263
- export function checkStopToken(stopToken) {
264
- if (isStopped(stopToken)) {
265
- throw makeAbortError();
266
- }
267
- }
268
- /**
269
- * Run an awaited stage under a stop token: checked before it starts, and again
270
- * once it settles. Prefer this to hand-placing {@link checkStopToken} around an
271
- * await — the point is that the check *after* comes with the one before, and the
272
- * one after is always the one that gets left out.
273
- *
274
- * It reads as sufficient to check at the top of an operation, because at the
275
- * time you write it the await below is not yet the slow part. `BaseRpcDriver`
276
- * checked at entry and then awaited `serializeArguments`, which resolves the
277
- * refName map, which downloads the adapter's index — and for an in-memory
278
- * adapter the whole file. A stop landing in that window woke a worker anyway.
279
- *
280
- * {@link updateStatus} and {@link withProgress} in `progress.ts` are this same
281
- * shape for a stage that also has something to say on the status channel; reach
282
- * for one of those when there is a label, and this when there isn't. What you
283
- * should not reach for is a bare `checkStopToken` sprinkled at a boundary you
284
- * happened to think of — the periodic in-loop case is
285
- * {@link checkStopTokenThrottled}, and it is driven by `createProgressReporter`
286
- * for the same reason.
287
- *
288
- * **Checking twice costs nothing, and it is worth knowing why**, because the
289
- * cost of a stop-token check is otherwise easy to remember backwards. The
290
- * expensive one is the synchronous XHR blob probe, which runs at up to ~10ms a
291
- * call — and it is reached ONLY from {@link checkStopTokenThrottled}, behind a
292
- * wall-clock gate with linear backoff, precisely because of that. This path is
293
- * {@link isStopped}: an atomic load for a SAB token, a `Map.has` for a string
294
- * one. So an extra boundary check here is free, and a per-item one in a tight
295
- * loop is not — which is the split the two functions exist to keep.
296
- */
297
- export async function withStopTokenCheck(stopToken, fn) {
298
- checkStopToken(stopToken);
299
- const result = await fn();
300
- checkStopToken(stopToken);
301
- return result;
302
- }
303
- /**
304
- * An `AbortSignal` wired to a stop token, so a canceled operation drops its
305
- * in-flight HTTP reads at the socket instead of downloading them to completion
306
- * and discarding the result. Call it where the work runs (worker-side for
307
- * worker RPC) and pass `signal` down through `BaseOptions`, which the gmod
308
- * readers already forward to `fetch`.
309
- *
310
- * No polling and no synchronous probe: a string token aborts off the same
311
- * posted id the await-boundary check reads, and a SharedArrayBuffer token off
312
- * `Atomics.waitAsync`, woken by the `Atomics.notify` in {@link stopStopToken}
313
- * — that one lands a turn or two after the notify rather than synchronously
314
- * with it, which costs nothing when the point is to stop a download.
315
- *
316
- * `dispose()` in a `finally` — an undisposed entry pins its controller until
317
- * the token is stopped.
318
- */
319
- export function stopTokenSignal(stopToken) {
320
- const controller = new AbortController();
321
- let dispose = () => { };
322
- if (isStopped(stopToken)) {
323
- controller.abort(makeAbortError());
324
- }
325
- else if (stopToken !== undefined) {
326
- // SAB ARM — dead in every configuration we ship
327
- if (isSharedArrayBuffer(stopToken)) {
328
- // waitAsync is specified alongside SharedArrayBuffer, but it postdates it
329
- // in browsers, so treat it as optional rather than assumed: without it a
330
- // SAB deployment simply keeps today's behavior of not aborting sockets.
331
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
332
- if (Atomics.waitAsync !== undefined) {
333
- const wait = Atomics.waitAsync(sabView(stopToken), 0, ABORT_FLAG_CLEAR);
334
- let disposed = false;
335
- dispose = () => {
336
- disposed = true;
337
- };
338
- const onWake = () => {
339
- if (!disposed) {
340
- controller.abort(makeAbortError());
341
- }
342
- };
343
- if (wait.async) {
344
- void wait.value.then(onWake);
345
- }
346
- else {
347
- // 'not-equal' — the flag moved between the isStopped read above and
348
- // here, so it is already set
349
- onWake();
350
- }
351
- }
352
- }
353
- else {
354
- const controllers = signalControllers.get(stopToken);
355
- if (controllers) {
356
- controllers.add(controller);
357
- }
358
- else {
359
- signalControllers.set(stopToken, new Set([controller]));
360
- }
361
- dispose = () => {
362
- const set = signalControllers.get(stopToken);
363
- if (set) {
364
- set.delete(controller);
365
- if (set.size === 0) {
366
- signalControllers.delete(stopToken);
367
- }
368
- }
369
- };
370
- }
371
- }
372
- return { signal: controller.signal, dispose };
373
- }
374
- /**
375
- * Run `fn` with an `AbortSignal` wired to this stop token, releasing the signal
376
- * afterwards however `fn` settles. The shape to reach for at an adapter's read
377
- * call — {@link stopTokenSignal} directly is for the rare case that outlives one
378
- * awaited call, and it then owns remembering to dispose.
379
- */
380
- export async function withStopTokenSignal(stopToken, fn) {
381
- const { signal, dispose } = stopTokenSignal(stopToken);
382
- try {
383
- return await fn(signal);
384
- }
385
- finally {
386
- dispose();
387
- }
388
- }
389
- export function createStopTokenChecker(stopToken) {
390
- return {
391
- stopToken,
392
- sabView: stopToken !== undefined && isSharedArrayBuffer(stopToken)
393
- ? sabView(stopToken)
394
- : undefined,
395
- iters: 0,
396
- checkInterval: STRING_CHECK_INTERVAL_MS,
397
- checkDue: createTimeGate(),
398
- syncProbe: probeBlobUrl,
399
- };
400
- }
401
- /**
402
- * The in-loop counterpart to {@link checkStopToken}, throttled so a
403
- * million-iteration loop doesn't pay per item.
404
- *
405
- * Reads the stopped-id set *and* the synchronous probe, because a loop that
406
- * awaits between items can see the first and a loop that never yields can only
407
- * see the second. Both are behind the same wall-clock gate, so the per-item cost
408
- * is one counter bump.
409
- */
410
- export function checkStopTokenThrottled(checker) {
411
- // narrowed into a local because a checker's `sabView` and its token type move
412
- // together but are separate properties, so the branch below can't discriminate
413
- // on `sabView` alone
414
- const stopToken = checker?.stopToken;
415
- if (checker !== undefined && stopToken !== undefined) {
416
- // THE LIVE PATH. Both halves of it: the set lookup catches a token stopped
417
- // through the message path, the probe catches one in a loop that never
418
- // yields to receive that message.
419
- if (typeof stopToken === 'string') {
420
- if (checker.checkDue(checker.checkInterval)) {
421
- if (stoppedIds.has(stopToken) || checker.syncProbe(stopToken)) {
422
- throw makeAbortError();
423
- }
424
- // Linear backoff, capped, so probe cost thins over a long loop while
425
- // cancel latency stays bounded. Advances only past a probe that ran.
426
- checker.checkInterval = Math.min(checker.checkInterval + STRING_CHECK_INTERVAL_MS, STRING_CHECK_INTERVAL_MAX_MS);
427
- }
428
- // SAB ARM, dead in every configuration we ship: a cheap atomic read,
429
- // gated by a small iteration mask. The counter is bumped here rather than
430
- // above the branch because it is the only reader of it.
431
- }
432
- else if (checker.sabView &&
433
- ++checker.iters % SAB_CHECK_EVERY_N_ITERS === 0 &&
434
- Atomics.load(checker.sabView, 0) === ABORT_FLAG_SET) {
435
- throw makeAbortError();
436
- }
437
- }
438
- }