@metamask-previews/perps-controller 11.0.0-preview-9efd0b754 → 12.0.0-preview-4e64b66e8

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 (134) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/dist/PerpsController-method-action-types.cjs.map +1 -1
  3. package/dist/PerpsController-method-action-types.d.cts +36 -1
  4. package/dist/PerpsController-method-action-types.d.cts.map +1 -1
  5. package/dist/PerpsController-method-action-types.d.mts +36 -1
  6. package/dist/PerpsController-method-action-types.d.mts.map +1 -1
  7. package/dist/PerpsController-method-action-types.mjs.map +1 -1
  8. package/dist/PerpsController.cjs +600 -119
  9. package/dist/PerpsController.cjs.map +1 -1
  10. package/dist/PerpsController.d.cts +35 -4
  11. package/dist/PerpsController.d.cts.map +1 -1
  12. package/dist/PerpsController.d.mts +35 -4
  13. package/dist/PerpsController.d.mts.map +1 -1
  14. package/dist/PerpsController.mjs +603 -122
  15. package/dist/PerpsController.mjs.map +1 -1
  16. package/dist/constants/eventNames.cjs +3 -0
  17. package/dist/constants/eventNames.cjs.map +1 -1
  18. package/dist/constants/eventNames.d.cts +1 -0
  19. package/dist/constants/eventNames.d.cts.map +1 -1
  20. package/dist/constants/eventNames.d.mts +1 -0
  21. package/dist/constants/eventNames.d.mts.map +1 -1
  22. package/dist/constants/eventNames.mjs +3 -0
  23. package/dist/constants/eventNames.mjs.map +1 -1
  24. package/dist/constants/hyperLiquidConfig.cjs +14 -2
  25. package/dist/constants/hyperLiquidConfig.cjs.map +1 -1
  26. package/dist/constants/hyperLiquidConfig.d.cts +7 -0
  27. package/dist/constants/hyperLiquidConfig.d.cts.map +1 -1
  28. package/dist/constants/hyperLiquidConfig.d.mts +7 -0
  29. package/dist/constants/hyperLiquidConfig.d.mts.map +1 -1
  30. package/dist/constants/hyperLiquidConfig.mjs +11 -0
  31. package/dist/constants/hyperLiquidConfig.mjs.map +1 -1
  32. package/dist/constants/perpsConfig.cjs +19 -2
  33. package/dist/constants/perpsConfig.cjs.map +1 -1
  34. package/dist/constants/perpsConfig.d.cts +39 -7
  35. package/dist/constants/perpsConfig.d.cts.map +1 -1
  36. package/dist/constants/perpsConfig.d.mts +39 -7
  37. package/dist/constants/perpsConfig.d.mts.map +1 -1
  38. package/dist/constants/perpsConfig.mjs +18 -1
  39. package/dist/constants/perpsConfig.mjs.map +1 -1
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +3 -3
  42. package/dist/index.d.cts.map +1 -1
  43. package/dist/index.d.mts +3 -3
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/providers/AggregatedPerpsProvider.cjs +16 -0
  47. package/dist/providers/AggregatedPerpsProvider.cjs.map +1 -1
  48. package/dist/providers/AggregatedPerpsProvider.d.cts +3 -1
  49. package/dist/providers/AggregatedPerpsProvider.d.cts.map +1 -1
  50. package/dist/providers/AggregatedPerpsProvider.d.mts +3 -1
  51. package/dist/providers/AggregatedPerpsProvider.d.mts.map +1 -1
  52. package/dist/providers/AggregatedPerpsProvider.mjs +16 -0
  53. package/dist/providers/AggregatedPerpsProvider.mjs.map +1 -1
  54. package/dist/providers/HyperLiquidProvider.cjs +325 -59
  55. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  56. package/dist/providers/HyperLiquidProvider.d.cts +27 -1
  57. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  58. package/dist/providers/HyperLiquidProvider.d.mts +27 -1
  59. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  60. package/dist/providers/HyperLiquidProvider.mjs +326 -60
  61. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  62. package/dist/services/HyperLiquidClientService.cjs +74 -25
  63. package/dist/services/HyperLiquidClientService.cjs.map +1 -1
  64. package/dist/services/HyperLiquidClientService.d.cts.map +1 -1
  65. package/dist/services/HyperLiquidClientService.d.mts.map +1 -1
  66. package/dist/services/HyperLiquidClientService.mjs +74 -25
  67. package/dist/services/HyperLiquidClientService.mjs.map +1 -1
  68. package/dist/services/HyperLiquidSubscriptionService.cjs +16 -5
  69. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  70. package/dist/services/HyperLiquidSubscriptionService.d.cts +1 -1
  71. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  72. package/dist/services/HyperLiquidSubscriptionService.d.mts +1 -1
  73. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  74. package/dist/services/HyperLiquidSubscriptionService.mjs +16 -5
  75. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  76. package/dist/services/MarketDataService.cjs +49 -5
  77. package/dist/services/MarketDataService.cjs.map +1 -1
  78. package/dist/services/MarketDataService.d.cts +2 -2
  79. package/dist/services/MarketDataService.d.cts.map +1 -1
  80. package/dist/services/MarketDataService.d.mts +2 -2
  81. package/dist/services/MarketDataService.d.mts.map +1 -1
  82. package/dist/services/MarketDataService.mjs +49 -5
  83. package/dist/services/MarketDataService.mjs.map +1 -1
  84. package/dist/services/RewardsIntegrationService.cjs +330 -68
  85. package/dist/services/RewardsIntegrationService.cjs.map +1 -1
  86. package/dist/services/RewardsIntegrationService.d.cts +61 -5
  87. package/dist/services/RewardsIntegrationService.d.cts.map +1 -1
  88. package/dist/services/RewardsIntegrationService.d.mts +61 -5
  89. package/dist/services/RewardsIntegrationService.d.mts.map +1 -1
  90. package/dist/services/RewardsIntegrationService.mjs +330 -68
  91. package/dist/services/RewardsIntegrationService.mjs.map +1 -1
  92. package/dist/services/ServiceContext.cjs.map +1 -1
  93. package/dist/services/ServiceContext.d.cts +17 -1
  94. package/dist/services/ServiceContext.d.cts.map +1 -1
  95. package/dist/services/ServiceContext.d.mts +17 -1
  96. package/dist/services/ServiceContext.d.mts.map +1 -1
  97. package/dist/services/ServiceContext.mjs.map +1 -1
  98. package/dist/services/TerminalMarketService.cjs +422 -7
  99. package/dist/services/TerminalMarketService.cjs.map +1 -1
  100. package/dist/services/TerminalMarketService.d.cts +10 -1
  101. package/dist/services/TerminalMarketService.d.cts.map +1 -1
  102. package/dist/services/TerminalMarketService.d.mts +10 -1
  103. package/dist/services/TerminalMarketService.d.mts.map +1 -1
  104. package/dist/services/TerminalMarketService.mjs +423 -8
  105. package/dist/services/TerminalMarketService.mjs.map +1 -1
  106. package/dist/services/TradingService.cjs +52 -28
  107. package/dist/services/TradingService.cjs.map +1 -1
  108. package/dist/services/TradingService.d.cts.map +1 -1
  109. package/dist/services/TradingService.d.mts.map +1 -1
  110. package/dist/services/TradingService.mjs +52 -28
  111. package/dist/services/TradingService.mjs.map +1 -1
  112. package/dist/types/index.cjs.map +1 -1
  113. package/dist/types/index.d.cts +156 -7
  114. package/dist/types/index.d.cts.map +1 -1
  115. package/dist/types/index.d.mts +156 -7
  116. package/dist/types/index.d.mts.map +1 -1
  117. package/dist/types/index.mjs.map +1 -1
  118. package/dist/utils/marketUtils.cjs +16 -1
  119. package/dist/utils/marketUtils.cjs.map +1 -1
  120. package/dist/utils/marketUtils.d.cts +1 -0
  121. package/dist/utils/marketUtils.d.cts.map +1 -1
  122. package/dist/utils/marketUtils.d.mts +1 -0
  123. package/dist/utils/marketUtils.d.mts.map +1 -1
  124. package/dist/utils/marketUtils.mjs +14 -0
  125. package/dist/utils/marketUtils.mjs.map +1 -1
  126. package/dist/utils/perpsDiskPersistence.cjs +19 -12
  127. package/dist/utils/perpsDiskPersistence.cjs.map +1 -1
  128. package/dist/utils/perpsDiskPersistence.d.cts +5 -1
  129. package/dist/utils/perpsDiskPersistence.d.cts.map +1 -1
  130. package/dist/utils/perpsDiskPersistence.d.mts +5 -1
  131. package/dist/utils/perpsDiskPersistence.d.mts.map +1 -1
  132. package/dist/utils/perpsDiskPersistence.mjs +19 -12
  133. package/dist/utils/perpsDiskPersistence.mjs.map +1 -1
  134. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import type { GetMarketDataWithPricesParams, MarketTypeFilter, PerpsMarketData } from "../types/index.cjs";
2
2
  import type { CandleData } from "../types/perps-types.cjs";
3
+ export declare function clonePerpsMarketData(markets: PerpsMarketData[]): PerpsMarketData[];
3
4
  /**
4
5
  * Whether a market is a HIP-3 (non-main-DEX) market. A `marketSource` DEX id
5
6
  * marks a HIP-3 market even when the `isHip3` flag is unset (e.g. partial route
@@ -1 +1 @@
1
- {"version":3,"file":"marketUtils.d.cts","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,eAAe,EAChB,2BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAe,iCAAgC;AAUvE;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,WACf,KAAK,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KACvD,OAAiE,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAkBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAQ7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,EAC1B,MAAM,CAAC,EAAE,6BAA6B,GACrC,eAAe,EAAE,CA6BnB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACG,CAAC;AAE9C,eAAO,MAAM,qBAAqB,YAAa,MAAM,KAAG,OA4BvD,CAAC;AAEF,eAAO,MAAM,oBAAoB,YAAa,MAAM,KAAG,oBActD,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,MAAM,WACL,oBAAoB,KAC5B,OAMF,CAAC;AAEF,eAAO,MAAM,mBAAmB,WACtB,MAAM,OACT,MAAM,GAAG,IAAI,eACL,OAAO,2BACK,qBAAqB,EAAE,2BACvB,qBAAqB,EAAE,KAC/C,OA2BF,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAWtD,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAAM,GAAG,IAW/D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,YAC3B,sBAAsB,KAC9B,MA2CF,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAClB,UAAU,GAAG,IAAI,KAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CA2B7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,YACtB,eAAe,EAAE,eACb,MAAM,KAClB,eAAe,EAYjB,CAAC"}
1
+ {"version":3,"file":"marketUtils.d.cts","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,eAAe,EAChB,2BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAe,iCAAgC;AAGvE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EAAE,GACzB,eAAe,EAAE,CAanB;AASD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,WACf,KAAK,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KACvD,OAAiE,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAkBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAQ7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,EAC1B,MAAM,CAAC,EAAE,6BAA6B,GACrC,eAAe,EAAE,CA6BnB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACG,CAAC;AAE9C,eAAO,MAAM,qBAAqB,YAAa,MAAM,KAAG,OA4BvD,CAAC;AAEF,eAAO,MAAM,oBAAoB,YAAa,MAAM,KAAG,oBActD,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,MAAM,WACL,oBAAoB,KAC5B,OAMF,CAAC;AAEF,eAAO,MAAM,mBAAmB,WACtB,MAAM,OACT,MAAM,GAAG,IAAI,eACL,OAAO,2BACK,qBAAqB,EAAE,2BACvB,qBAAqB,EAAE,KAC/C,OA2BF,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAWtD,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAAM,GAAG,IAW/D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,YAC3B,sBAAsB,KAC9B,MA2CF,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAClB,UAAU,GAAG,IAAI,KAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CA2B7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,YACtB,eAAe,EAAE,eACb,MAAM,KAClB,eAAe,EAYjB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { GetMarketDataWithPricesParams, MarketTypeFilter, PerpsMarketData } from "../types/index.mjs";
2
2
  import type { CandleData } from "../types/perps-types.mjs";
3
+ export declare function clonePerpsMarketData(markets: PerpsMarketData[]): PerpsMarketData[];
3
4
  /**
4
5
  * Whether a market is a HIP-3 (non-main-DEX) market. A `marketSource` DEX id
5
6
  * marks a HIP-3 market even when the `isHip3` flag is unset (e.g. partial route
@@ -1 +1 @@
1
- {"version":3,"file":"marketUtils.d.mts","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,eAAe,EAChB,2BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAe,iCAAgC;AAUvE;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,WACf,KAAK,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KACvD,OAAiE,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAkBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAQ7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,EAC1B,MAAM,CAAC,EAAE,6BAA6B,GACrC,eAAe,EAAE,CA6BnB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACG,CAAC;AAE9C,eAAO,MAAM,qBAAqB,YAAa,MAAM,KAAG,OA4BvD,CAAC;AAEF,eAAO,MAAM,oBAAoB,YAAa,MAAM,KAAG,oBActD,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,MAAM,WACL,oBAAoB,KAC5B,OAMF,CAAC;AAEF,eAAO,MAAM,mBAAmB,WACtB,MAAM,OACT,MAAM,GAAG,IAAI,eACL,OAAO,2BACK,qBAAqB,EAAE,2BACvB,qBAAqB,EAAE,KAC/C,OA2BF,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAWtD,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAAM,GAAG,IAW/D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,YAC3B,sBAAsB,KAC9B,MA2CF,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAClB,UAAU,GAAG,IAAI,KAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CA2B7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,YACtB,eAAe,EAAE,eACb,MAAM,KAClB,eAAe,EAYjB,CAAC"}
1
+ {"version":3,"file":"marketUtils.d.mts","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,eAAe,EAChB,2BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAe,iCAAgC;AAGvE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EAAE,GACzB,eAAe,EAAE,CAanB;AASD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,WACf,KAAK,eAAe,EAAE,QAAQ,GAAG,cAAc,CAAC,KACvD,OAAiE,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAkBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAQ7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,EAC1B,MAAM,CAAC,EAAE,6BAA6B,GACrC,eAAe,EAAE,CA6BnB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACG,CAAC;AAE9C,eAAO,MAAM,qBAAqB,YAAa,MAAM,KAAG,OA4BvD,CAAC;AAEF,eAAO,MAAM,oBAAoB,YAAa,MAAM,KAAG,oBActD,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,MAAM,WACL,oBAAoB,KAC5B,OAMF,CAAC;AAEF,eAAO,MAAM,mBAAmB,WACtB,MAAM,OACT,MAAM,GAAG,IAAI,eACL,OAAO,2BACK,qBAAqB,EAAE,2BACvB,qBAAqB,EAAE,KAC/C,OA2BF,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAWtD,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAAM,GAAG,IAW/D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,YAC3B,sBAAsB,KAC9B,MA2CF,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAClB,UAAU,GAAG,IAAI,KAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CA2B7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,YACtB,eAAe,EAAE,eACb,MAAM,KAClB,eAAe,EAYjB,CAAC"}
@@ -1,4 +1,18 @@
1
1
  import { sortMarkets } from "./sortMarkets.mjs";
2
+ export function clonePerpsMarketData(markets) {
3
+ return markets.map((market) => ({
4
+ ...market,
5
+ ...(market.keywords && { keywords: [...market.keywords] }),
6
+ ...(market.tags && { tags: [...market.tags] }),
7
+ ...(market.categories && { categories: [...market.categories] }),
8
+ ...(market.trend && {
9
+ trend: market.trend.map(([timestamp, price]) => [
10
+ timestamp,
11
+ price,
12
+ ]),
13
+ }),
14
+ }));
15
+ }
2
16
  // ============================================================================
3
17
  // Market category classification (pure functions)
4
18
  // No service dependencies — pure data transformations that can be tested and
@@ -1 +1 @@
1
- {"version":3,"file":"marketUtils.mjs","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GetMarketDataWithPricesParams,\n MarketTypeFilter,\n PerpsMarketData,\n} from '../types/index.js';\nimport type { CandleData, CandleStick } from '../types/perps-types.js';\nimport { sortMarkets } from './sortMarkets.js';\n\n// ============================================================================\n// Market category classification (pure functions)\n// No service dependencies — pure data transformations that can be tested and\n// reused independently. `matchesCategory` and `getMarketTypeFilter` share the\n// same category model.\n// ============================================================================\n\n/**\n * Whether a market is a HIP-3 (non-main-DEX) market. A `marketSource` DEX id\n * marks a HIP-3 market even when the `isHip3` flag is unset (e.g. partial route\n * params), so both signals are checked. Used as the single HIP-3 signal so the\n * classifiers stay consistent.\n *\n * @param market - The market data to test.\n * @returns True if the market is HIP-3.\n */\nexport const isHip3Market = (\n market: Pick<PerpsMarketData, 'isHip3' | 'marketSource'>,\n): boolean => Boolean(market.isHip3) || Boolean(market.marketSource);\n\n/**\n * Returns true when a market matches the given UI filter category.\n *\n * @param market - The market data to test.\n * @param category - The filter category to test against.\n * @returns Whether the market matches the category.\n */\nexport function matchesCategory(\n market: PerpsMarketData,\n category: MarketTypeFilter,\n): boolean {\n switch (category) {\n case 'all':\n return true;\n case 'new':\n // Explicitly flagged, or an uncategorized HIP-3 market (kept in sync with\n // getMarketTypeFilter's 'new' bucket).\n return (\n market.isNewMarket === true ||\n (isHip3Market(market) && market.marketType === undefined)\n );\n case 'crypto':\n // Main-DEX markets, plus HIP-3 assets explicitly typed as CryptoCurrency.\n return !isHip3Market(market) || market.marketType === 'crypto';\n default:\n // Every other filter is a 1:1 data-model category match.\n return market.marketType !== undefined && market.marketType === category;\n }\n}\n\n/**\n * Resolve the user-facing category bucket for a market — one of `crypto`,\n * `stock`, `pre-ipo`, `index`, `etf`, `commodity`, `forex`, or `new`. Data-model\n * categories map 1:1. A market with no data-model category is `crypto` when it\n * is main-DEX, or `new` when it is an uncategorized HIP-3 market (`isHip3`, or a\n * `marketSource` DEX id when `isHip3` is unset, e.g. minimal route params).\n * Never returns the `all` sentinel.\n *\n * Centralised as the single source of truth so consumers (e.g. category\n * shortcuts, related markets) share one classification instead of re-deriving\n * it per client and drifting as new categories are added.\n *\n * @param market - The market data to classify.\n * @returns The market type filter bucket.\n */\nexport function getMarketTypeFilter(market: PerpsMarketData): MarketTypeFilter {\n const { marketType } = market;\n if (marketType) {\n return marketType;\n }\n // No data-model category: an uncategorized HIP-3 market is the 'new' bucket;\n // otherwise it's a main-DEX crypto market.\n return isHip3Market(market) ? 'new' : 'crypto';\n}\n\n/**\n * Applies optional category filtering, sorting, and limit to a list of markets.\n *\n * @param markets - Source market array.\n * @param params - Optional filter/sort/limit params.\n * @returns Filtered, sorted, and/or sliced market array.\n */\nexport function applyMarketFilters(\n markets: PerpsMarketData[],\n params?: GetMarketDataWithPricesParams,\n): PerpsMarketData[] {\n let result = markets;\n\n if (params?.categories?.length) {\n const { categories } = params;\n result = result.filter((market) =>\n // A market is included if it matches ANY of the requested categories.\n categories.some((category) => matchesCategory(market, category)),\n );\n }\n\n if (params?.excludeSymbols?.length) {\n const excluded = new Set(params.excludeSymbols);\n result = result.filter((market) => !excluded.has(market.symbol));\n }\n\n if (params?.sortBy) {\n result = sortMarkets({\n markets: result,\n sortBy: params.sortBy,\n direction: params.direction,\n });\n }\n\n if (params?.limit !== undefined) {\n result = result.slice(0, params.limit);\n }\n\n return result;\n}\n\n/**\n * Maximum length for market filter patterns (prevents DoS attacks)\n */\nexport const MAX_MARKET_PATTERN_LENGTH = 100;\n\nexport type MarketPatternMatcher = RegExp | string;\n\nexport type CompiledMarketPattern = {\n pattern: string;\n matcher: MarketPatternMatcher;\n};\n\nexport const escapeRegex = (str: string): string =>\n str.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n\nexport const validateMarketPattern = (pattern: string): boolean => {\n if (!pattern || pattern.trim().length === 0) {\n throw new Error('Market pattern cannot be empty');\n }\n\n const normalizedPattern = pattern.trim();\n\n if (normalizedPattern.length > MAX_MARKET_PATTERN_LENGTH) {\n throw new Error(\n `Market pattern exceeds maximum length (${MAX_MARKET_PATTERN_LENGTH} chars): ${normalizedPattern}`,\n );\n }\n\n const dangerousChars = /[\\\\()[\\]{}^$+?.|]/u;\n if (dangerousChars.test(normalizedPattern)) {\n throw new Error(\n `Market pattern contains invalid regex characters: ${normalizedPattern}`,\n );\n }\n\n const validPattern = /^[a-zA-Z0-9:_\\-*]+$/u;\n if (!validPattern.test(normalizedPattern)) {\n throw new Error(\n `Market pattern contains invalid characters: ${normalizedPattern}`,\n );\n }\n\n return true;\n};\n\nexport const compileMarketPattern = (pattern: string): MarketPatternMatcher => {\n const normalizedPattern = pattern.trim();\n validateMarketPattern(normalizedPattern);\n\n if (normalizedPattern.endsWith(':*')) {\n const prefix = normalizedPattern.slice(0, -2);\n return new RegExp(`^${escapeRegex(prefix)}:`, 'u');\n }\n\n if (!normalizedPattern.includes(':')) {\n return new RegExp(`^${escapeRegex(normalizedPattern)}:`, 'u');\n }\n\n return normalizedPattern;\n};\n\nexport const matchesMarketPattern = (\n symbol: string,\n matcher: MarketPatternMatcher,\n): boolean => {\n if (typeof matcher === 'string') {\n return symbol === matcher;\n }\n\n return matcher.test(symbol);\n};\n\nexport const shouldIncludeMarket = (\n symbol: string,\n dex: string | null,\n hip3Enabled: boolean,\n compiledEnabledPatterns: CompiledMarketPattern[],\n compiledBlockedPatterns: CompiledMarketPattern[],\n): boolean => {\n if (dex === null) {\n return true;\n }\n\n if (!hip3Enabled) {\n return false;\n }\n\n if (compiledEnabledPatterns.length > 0) {\n const whitelisted = compiledEnabledPatterns.some(({ matcher }) =>\n matchesMarketPattern(symbol, matcher),\n );\n if (!whitelisted) {\n return false;\n }\n }\n\n if (compiledBlockedPatterns.length === 0) {\n return true;\n }\n\n const blacklisted = compiledBlockedPatterns.some(({ matcher }) =>\n matchesMarketPattern(symbol, matcher),\n );\n\n return !blacklisted;\n};\n\nexport const getPerpsDisplaySymbol = (symbol: string): string => {\n if (!symbol || typeof symbol !== 'string') {\n return symbol;\n }\n\n const colonIndex = symbol.indexOf(':');\n if (colonIndex > 0 && colonIndex < symbol.length - 1) {\n return symbol.substring(colonIndex + 1);\n }\n\n return symbol;\n};\n\nexport const getPerpsDexFromSymbol = (symbol: string): string | null => {\n if (!symbol || typeof symbol !== 'string') {\n return null;\n }\n\n const colonIndex = symbol.indexOf(':');\n if (colonIndex > 0 && colonIndex < symbol.length - 1) {\n return symbol.substring(0, colonIndex);\n }\n\n return null;\n};\n\ntype FundingCountdownParams = {\n nextFundingTime?: number;\n fundingIntervalHours?: number;\n};\n\nexport const calculateFundingCountdown = (\n params?: FundingCountdownParams,\n): string => {\n const now = new Date();\n const nowMs = now.getTime();\n\n if (params?.nextFundingTime && params.nextFundingTime > nowMs) {\n const msUntilFunding = params.nextFundingTime - nowMs;\n const hoursUntilFunding = msUntilFunding / (1000 * 60 * 60);\n\n if (hoursUntilFunding <= 1.1) {\n const totalSeconds = Math.floor(msUntilFunding / 1000);\n\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n const formattedHours = String(hours).padStart(2, '0');\n const formattedMinutes = String(minutes).padStart(2, '0');\n const formattedSeconds = String(seconds).padStart(2, '0');\n\n return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;\n }\n }\n\n const utcMinutes = now.getUTCMinutes();\n const utcSeconds = now.getUTCSeconds();\n\n const minutesUntilNextHour = 59 - utcMinutes;\n const secondsUntilNextHour = 60 - utcSeconds;\n\n const finalSeconds = secondsUntilNextHour === 60 ? 0 : secondsUntilNextHour;\n const finalMinutes =\n secondsUntilNextHour === 60\n ? minutesUntilNextHour + 1\n : minutesUntilNextHour;\n\n const finalHours = finalMinutes === 60 ? 1 : 0;\n const adjustedMinutes = finalMinutes === 60 ? 0 : finalMinutes;\n\n const formattedHours = String(finalHours).padStart(2, '0');\n const formattedMinutes = String(adjustedMinutes).padStart(2, '0');\n const formattedSeconds = String(finalSeconds).padStart(2, '0');\n\n return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;\n};\n\nexport const calculate24hHighLow = (\n candleData: CandleData | null,\n): { high: number; low: number } => {\n if (!candleData?.candles || candleData.candles.length === 0) {\n return { high: 0, low: 0 };\n }\n\n const now = Date.now();\n const twentyFourHoursAgo = now - 24 * 60 * 60 * 1000;\n\n let last24hCandles = candleData.candles.filter(\n (candle: CandleStick) => candle.time >= twentyFourHoursAgo,\n );\n\n if (last24hCandles.length === 0) {\n last24hCandles = [...candleData.candles];\n }\n\n const highs = last24hCandles.map((candle: CandleStick) =>\n parseFloat(candle.high),\n );\n const lows = last24hCandles.map((candle: CandleStick) =>\n parseFloat(candle.low),\n );\n\n return {\n high: Math.max(...highs),\n low: Math.min(...lows),\n };\n};\n\nexport const filterMarketsByQuery = (\n markets: PerpsMarketData[],\n searchQuery: string,\n): PerpsMarketData[] => {\n if (!searchQuery?.trim()) {\n return markets;\n }\n\n const lowerQuery = searchQuery.toLowerCase().trim();\n\n return markets.filter(\n (market) =>\n market.symbol?.toLowerCase().includes(lowerQuery) ||\n market.name?.toLowerCase().includes(lowerQuery),\n );\n};\n"]}
1
+ {"version":3,"file":"marketUtils.mjs","sourceRoot":"","sources":["../../src/utils/marketUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GetMarketDataWithPricesParams,\n MarketTypeFilter,\n PerpsMarketData,\n} from '../types/index.js';\nimport type { CandleData, CandleStick } from '../types/perps-types.js';\nimport { sortMarkets } from './sortMarkets.js';\n\nexport function clonePerpsMarketData(\n markets: PerpsMarketData[],\n): PerpsMarketData[] {\n return markets.map((market) => ({\n ...market,\n ...(market.keywords && { keywords: [...market.keywords] }),\n ...(market.tags && { tags: [...market.tags] }),\n ...(market.categories && { categories: [...market.categories] }),\n ...(market.trend && {\n trend: market.trend.map(([timestamp, price]): [number, string] => [\n timestamp,\n price,\n ]),\n }),\n }));\n}\n\n// ============================================================================\n// Market category classification (pure functions)\n// No service dependencies — pure data transformations that can be tested and\n// reused independently. `matchesCategory` and `getMarketTypeFilter` share the\n// same category model.\n// ============================================================================\n\n/**\n * Whether a market is a HIP-3 (non-main-DEX) market. A `marketSource` DEX id\n * marks a HIP-3 market even when the `isHip3` flag is unset (e.g. partial route\n * params), so both signals are checked. Used as the single HIP-3 signal so the\n * classifiers stay consistent.\n *\n * @param market - The market data to test.\n * @returns True if the market is HIP-3.\n */\nexport const isHip3Market = (\n market: Pick<PerpsMarketData, 'isHip3' | 'marketSource'>,\n): boolean => Boolean(market.isHip3) || Boolean(market.marketSource);\n\n/**\n * Returns true when a market matches the given UI filter category.\n *\n * @param market - The market data to test.\n * @param category - The filter category to test against.\n * @returns Whether the market matches the category.\n */\nexport function matchesCategory(\n market: PerpsMarketData,\n category: MarketTypeFilter,\n): boolean {\n switch (category) {\n case 'all':\n return true;\n case 'new':\n // Explicitly flagged, or an uncategorized HIP-3 market (kept in sync with\n // getMarketTypeFilter's 'new' bucket).\n return (\n market.isNewMarket === true ||\n (isHip3Market(market) && market.marketType === undefined)\n );\n case 'crypto':\n // Main-DEX markets, plus HIP-3 assets explicitly typed as CryptoCurrency.\n return !isHip3Market(market) || market.marketType === 'crypto';\n default:\n // Every other filter is a 1:1 data-model category match.\n return market.marketType !== undefined && market.marketType === category;\n }\n}\n\n/**\n * Resolve the user-facing category bucket for a market — one of `crypto`,\n * `stock`, `pre-ipo`, `index`, `etf`, `commodity`, `forex`, or `new`. Data-model\n * categories map 1:1. A market with no data-model category is `crypto` when it\n * is main-DEX, or `new` when it is an uncategorized HIP-3 market (`isHip3`, or a\n * `marketSource` DEX id when `isHip3` is unset, e.g. minimal route params).\n * Never returns the `all` sentinel.\n *\n * Centralised as the single source of truth so consumers (e.g. category\n * shortcuts, related markets) share one classification instead of re-deriving\n * it per client and drifting as new categories are added.\n *\n * @param market - The market data to classify.\n * @returns The market type filter bucket.\n */\nexport function getMarketTypeFilter(market: PerpsMarketData): MarketTypeFilter {\n const { marketType } = market;\n if (marketType) {\n return marketType;\n }\n // No data-model category: an uncategorized HIP-3 market is the 'new' bucket;\n // otherwise it's a main-DEX crypto market.\n return isHip3Market(market) ? 'new' : 'crypto';\n}\n\n/**\n * Applies optional category filtering, sorting, and limit to a list of markets.\n *\n * @param markets - Source market array.\n * @param params - Optional filter/sort/limit params.\n * @returns Filtered, sorted, and/or sliced market array.\n */\nexport function applyMarketFilters(\n markets: PerpsMarketData[],\n params?: GetMarketDataWithPricesParams,\n): PerpsMarketData[] {\n let result = markets;\n\n if (params?.categories?.length) {\n const { categories } = params;\n result = result.filter((market) =>\n // A market is included if it matches ANY of the requested categories.\n categories.some((category) => matchesCategory(market, category)),\n );\n }\n\n if (params?.excludeSymbols?.length) {\n const excluded = new Set(params.excludeSymbols);\n result = result.filter((market) => !excluded.has(market.symbol));\n }\n\n if (params?.sortBy) {\n result = sortMarkets({\n markets: result,\n sortBy: params.sortBy,\n direction: params.direction,\n });\n }\n\n if (params?.limit !== undefined) {\n result = result.slice(0, params.limit);\n }\n\n return result;\n}\n\n/**\n * Maximum length for market filter patterns (prevents DoS attacks)\n */\nexport const MAX_MARKET_PATTERN_LENGTH = 100;\n\nexport type MarketPatternMatcher = RegExp | string;\n\nexport type CompiledMarketPattern = {\n pattern: string;\n matcher: MarketPatternMatcher;\n};\n\nexport const escapeRegex = (str: string): string =>\n str.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n\nexport const validateMarketPattern = (pattern: string): boolean => {\n if (!pattern || pattern.trim().length === 0) {\n throw new Error('Market pattern cannot be empty');\n }\n\n const normalizedPattern = pattern.trim();\n\n if (normalizedPattern.length > MAX_MARKET_PATTERN_LENGTH) {\n throw new Error(\n `Market pattern exceeds maximum length (${MAX_MARKET_PATTERN_LENGTH} chars): ${normalizedPattern}`,\n );\n }\n\n const dangerousChars = /[\\\\()[\\]{}^$+?.|]/u;\n if (dangerousChars.test(normalizedPattern)) {\n throw new Error(\n `Market pattern contains invalid regex characters: ${normalizedPattern}`,\n );\n }\n\n const validPattern = /^[a-zA-Z0-9:_\\-*]+$/u;\n if (!validPattern.test(normalizedPattern)) {\n throw new Error(\n `Market pattern contains invalid characters: ${normalizedPattern}`,\n );\n }\n\n return true;\n};\n\nexport const compileMarketPattern = (pattern: string): MarketPatternMatcher => {\n const normalizedPattern = pattern.trim();\n validateMarketPattern(normalizedPattern);\n\n if (normalizedPattern.endsWith(':*')) {\n const prefix = normalizedPattern.slice(0, -2);\n return new RegExp(`^${escapeRegex(prefix)}:`, 'u');\n }\n\n if (!normalizedPattern.includes(':')) {\n return new RegExp(`^${escapeRegex(normalizedPattern)}:`, 'u');\n }\n\n return normalizedPattern;\n};\n\nexport const matchesMarketPattern = (\n symbol: string,\n matcher: MarketPatternMatcher,\n): boolean => {\n if (typeof matcher === 'string') {\n return symbol === matcher;\n }\n\n return matcher.test(symbol);\n};\n\nexport const shouldIncludeMarket = (\n symbol: string,\n dex: string | null,\n hip3Enabled: boolean,\n compiledEnabledPatterns: CompiledMarketPattern[],\n compiledBlockedPatterns: CompiledMarketPattern[],\n): boolean => {\n if (dex === null) {\n return true;\n }\n\n if (!hip3Enabled) {\n return false;\n }\n\n if (compiledEnabledPatterns.length > 0) {\n const whitelisted = compiledEnabledPatterns.some(({ matcher }) =>\n matchesMarketPattern(symbol, matcher),\n );\n if (!whitelisted) {\n return false;\n }\n }\n\n if (compiledBlockedPatterns.length === 0) {\n return true;\n }\n\n const blacklisted = compiledBlockedPatterns.some(({ matcher }) =>\n matchesMarketPattern(symbol, matcher),\n );\n\n return !blacklisted;\n};\n\nexport const getPerpsDisplaySymbol = (symbol: string): string => {\n if (!symbol || typeof symbol !== 'string') {\n return symbol;\n }\n\n const colonIndex = symbol.indexOf(':');\n if (colonIndex > 0 && colonIndex < symbol.length - 1) {\n return symbol.substring(colonIndex + 1);\n }\n\n return symbol;\n};\n\nexport const getPerpsDexFromSymbol = (symbol: string): string | null => {\n if (!symbol || typeof symbol !== 'string') {\n return null;\n }\n\n const colonIndex = symbol.indexOf(':');\n if (colonIndex > 0 && colonIndex < symbol.length - 1) {\n return symbol.substring(0, colonIndex);\n }\n\n return null;\n};\n\ntype FundingCountdownParams = {\n nextFundingTime?: number;\n fundingIntervalHours?: number;\n};\n\nexport const calculateFundingCountdown = (\n params?: FundingCountdownParams,\n): string => {\n const now = new Date();\n const nowMs = now.getTime();\n\n if (params?.nextFundingTime && params.nextFundingTime > nowMs) {\n const msUntilFunding = params.nextFundingTime - nowMs;\n const hoursUntilFunding = msUntilFunding / (1000 * 60 * 60);\n\n if (hoursUntilFunding <= 1.1) {\n const totalSeconds = Math.floor(msUntilFunding / 1000);\n\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n const formattedHours = String(hours).padStart(2, '0');\n const formattedMinutes = String(minutes).padStart(2, '0');\n const formattedSeconds = String(seconds).padStart(2, '0');\n\n return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;\n }\n }\n\n const utcMinutes = now.getUTCMinutes();\n const utcSeconds = now.getUTCSeconds();\n\n const minutesUntilNextHour = 59 - utcMinutes;\n const secondsUntilNextHour = 60 - utcSeconds;\n\n const finalSeconds = secondsUntilNextHour === 60 ? 0 : secondsUntilNextHour;\n const finalMinutes =\n secondsUntilNextHour === 60\n ? minutesUntilNextHour + 1\n : minutesUntilNextHour;\n\n const finalHours = finalMinutes === 60 ? 1 : 0;\n const adjustedMinutes = finalMinutes === 60 ? 0 : finalMinutes;\n\n const formattedHours = String(finalHours).padStart(2, '0');\n const formattedMinutes = String(adjustedMinutes).padStart(2, '0');\n const formattedSeconds = String(finalSeconds).padStart(2, '0');\n\n return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;\n};\n\nexport const calculate24hHighLow = (\n candleData: CandleData | null,\n): { high: number; low: number } => {\n if (!candleData?.candles || candleData.candles.length === 0) {\n return { high: 0, low: 0 };\n }\n\n const now = Date.now();\n const twentyFourHoursAgo = now - 24 * 60 * 60 * 1000;\n\n let last24hCandles = candleData.candles.filter(\n (candle: CandleStick) => candle.time >= twentyFourHoursAgo,\n );\n\n if (last24hCandles.length === 0) {\n last24hCandles = [...candleData.candles];\n }\n\n const highs = last24hCandles.map((candle: CandleStick) =>\n parseFloat(candle.high),\n );\n const lows = last24hCandles.map((candle: CandleStick) =>\n parseFloat(candle.low),\n );\n\n return {\n high: Math.max(...highs),\n low: Math.min(...lows),\n };\n};\n\nexport const filterMarketsByQuery = (\n markets: PerpsMarketData[],\n searchQuery: string,\n): PerpsMarketData[] => {\n if (!searchQuery?.trim()) {\n return markets;\n }\n\n const lowerQuery = searchQuery.toLowerCase().trim();\n\n return markets.filter(\n (market) =>\n market.symbol?.toLowerCase().includes(lowerQuery) ||\n market.name?.toLowerCase().includes(lowerQuery),\n );\n};\n"]}
@@ -127,16 +127,12 @@ exports.persistMarketEntriesToDisk = persistMarketEntriesToDisk;
127
127
  * @param diskCache - Disk cache instance from controller infrastructure.
128
128
  * @param entries - Pre-assembled user cache entries to persist.
129
129
  */
130
- function persistUserEntriesToDisk(diskCache, entries) {
130
+ async function persistUserEntriesToDisk(diskCache, entries) {
131
131
  if (entries.length === 0) {
132
132
  return;
133
133
  }
134
134
  const payload = entries.length === 1 ? entries[0] : { entries };
135
- diskCache
136
- .setItem(perpsConfig_js_1.PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload))
137
- .catch(() => {
138
- // Disk persistence is best-effort and must never block preload.
139
- });
135
+ await diskCache.setItem(perpsConfig_js_1.PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload));
140
136
  }
141
137
  exports.persistUserEntriesToDisk = persistUserEntriesToDisk;
142
138
  /**
@@ -181,12 +177,21 @@ function hydrateFromDiskSync(diskCache, currentMarketCache, currentUserCache, st
181
177
  if (entry.providerNetworkKey && Array.isArray(entry.data)) {
182
178
  const existing = currentMarketCache[entry.providerNetworkKey];
183
179
  if (!existing || existing.timestamp < entry.timestamp) {
184
- const strippedData = entry.data.map((market) => ({
185
- ...market,
186
- price: perpsConfig_js_1.PERPS_CONSTANTS.FallbackPriceDisplay,
187
- change24h: perpsConfig_js_1.PERPS_CONSTANTS.FallbackDataDisplay,
188
- change24hPercent: perpsConfig_js_1.PERPS_CONSTANTS.FallbackPercentageDisplay,
189
- }));
180
+ const strippedData = entry.data.map((market) => {
181
+ const structuralMarket = { ...market };
182
+ if (structuralMarket.dataSource ===
183
+ 'terminal-global-snapshot-mark') {
184
+ delete structuralMarket.trend;
185
+ }
186
+ delete structuralMarket.dataSource;
187
+ delete structuralMarket.sourceExpiresAt;
188
+ return {
189
+ ...structuralMarket,
190
+ price: perpsConfig_js_1.PERPS_CONSTANTS.FallbackPriceDisplay,
191
+ change24h: perpsConfig_js_1.PERPS_CONSTANTS.FallbackDataDisplay,
192
+ change24hPercent: perpsConfig_js_1.PERPS_CONSTANTS.FallbackPercentageDisplay,
193
+ };
194
+ });
190
195
  marketUpdates[entry.providerNetworkKey] = {
191
196
  data: strippedData,
192
197
  // Disk-hydrated market snapshots are only for structural
@@ -222,6 +227,8 @@ function hydrateFromDiskSync(diskCache, currentMarketCache, currentUserCache, st
222
227
  accountState: entry.accountState,
223
228
  timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),
224
229
  address: entry.address,
230
+ hip3ConfigVersion: entry.hip3ConfigVersion,
231
+ dexes: entry.dexes,
225
232
  };
226
233
  userPositions += entry.positions.length;
227
234
  userOrders += entry.orders.length;
@@ -1 +1 @@
1
- {"version":3,"file":"perpsDiskPersistence.cjs","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":";;;AAAA,iEAMqC;AAQrC;;;;;GAKG;AACH,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAmC3C;;;;;;;;;GASG;AACH,SAAgB,sBAAsB,CACpC,OAA0B,EAC1B,cAAsB,EACtB,SAAkB,EAClB,GAAW;IAEX,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAgC,CAAC;QAC7D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,gCAAe,CAAC,eAAe,CAAC;YACxE,MAAM,GAAG,GAAG,IAAA,sCAAqB,EAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;oBACpB,kBAAkB,EAAE,GAAG;oBACvB,IAAI,EAAE,CAAC,MAAM,CAAC;oBACd,SAAS,EAAE,GAAG;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QAClD,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IACzD,CAAC;IACD,OAAO;QACL,kBAAkB,EAAE,IAAA,sCAAqB,EACvC,cAAc,IAAI,gCAAe,CAAC,eAAe,EACjD,SAAS,CACV;QACD,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,GAAG;KACf,CAAC;AACJ,CAAC;AAjCD,wDAiCC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAClC,SAAqB,EACrB,MAAe,EACf,YAAiC,EACjC,OAAe,EACf,cAAsB,EACtB,SAAkB,EAClB,GAAW;IAEX,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC3D,MAAM,WAAW,GAAG,CAAC,UAAkB,EAAsB,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAA,sCAAqB,EAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YACzD,IAAI,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG;oBACN,kBAAkB,EAAE,GAAG;oBACvB,OAAO;oBACP,SAAS,EAAE,EAAE;oBACb,MAAM,EAAE,EAAE;oBACV,YAAY,EAAE,IAAI;oBAClB,SAAS,EAAE,GAAG;iBACf,CAAC;gBACF,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,WAAW,CACT,QAAQ,CAAC,UAAU,IAAI,gCAAe,CAAC,eAAe,CACvD,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,WAAW,CACT,KAAK,CAAC,UAAU,IAAI,gCAAe,CAAC,eAAe,CACpD,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,WAAW,CACT,YAAY,CAAC,UAAU,IAAI,gCAAe,CAAC,eAAe,CAC3D,CAAC,YAAY,GAAG,YAAY,CAAC;QAChC,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACtD,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YAC1B,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YACvB,KAAK,CAAC,YAAY,KAAK,IAAI,CAC9B,CAAC;QACF,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IACzD,CAAC;IACD,OAAO;QACL,kBAAkB,EAAE,IAAA,sCAAqB,EACvC,cAAc,IAAI,gCAAe,CAAC,eAAe,EACjD,SAAS,CACV;QACD,OAAO;QACP,SAAS;QACT,MAAM;QACN,YAAY;QACZ,SAAS,EAAE,GAAG;KACf,CAAC;AACJ,CAAC;AA/DD,oDA+DC;AAED;;;;;GAKG;AACH,SAAgB,0BAA0B,CACxC,SAAyB,EACzB,OAA+B;IAE/B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IAChE,SAAS;SACN,OAAO,CAAC,yCAAwB,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;SAC1D,KAAK,CAAC,GAAG,EAAE;QACV,gEAAgE;IAClE,CAAC,CAAC,CAAC;AACP,CAAC;AAbD,gEAaC;AAED;;;;;GAKG;AACH,SAAgB,wBAAwB,CACtC,SAAyB,EACzB,OAA6B;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IAChE,SAAS;SACN,OAAO,CAAC,2CAA0B,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;SAC5D,KAAK,CAAC,GAAG,EAAE;QACV,gEAAgE;IAClE,CAAC,CAAC,CAAC;AACP,CAAC;AAbD,4DAaC;AAuBD;;;;;;;;;;;;;GAaG;AACH,SAAgB,mBAAmB,CACjC,SAAyB,EACzB,kBAAyD,EACzD,gBAAuD,EACvD,YAAoB;IAEpB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,aAAa,GAA2C,EAAE,CAAC;IACjE,MAAM,WAAW,GAAyC,EAAE,CAAC;IAC7D,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC3B,OAAO;YACL,aAAa;YACb,WAAW;YACX,KAAK,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE;SACjE,CAAC;IACJ,CAAC;IAED,MAAM,sBAAsB,GAC1B,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,GAAG,+BAA+B,GAAG,CAAC,CAAC;IAElE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,yCAAwB,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC,2CAA0B,CAAC,CAAC;QAElE,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAEG,CAAC;gBACxC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAE,MAAgC,CAAC,OAAO,CAAC;oBACtE,CAAC,CAAE,MAA8C,CAAC,OAAO;oBACzD,CAAC,CAAC,CAAC,MAA8B,CAAC,CAAC;gBAErC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC5B,IAAI,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;wBAC9D,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;4BACtD,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gCAC/C,GAAG,MAAM;gCACT,KAAK,EAAE,gCAAe,CAAC,oBAAoB;gCAC3C,SAAS,EAAE,gCAAe,CAAC,mBAAmB;gCAC9C,gBAAgB,EAAE,gCAAe,CAAC,yBAAyB;6BAC5D,CAAC,CAAC,CAAC;4BACJ,aAAa,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG;gCACxC,IAAI,EAAE,YAAY;gCAClB,yDAAyD;gCACzD,yDAAyD;gCACzD,yCAAyC;gCACzC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,sBAAsB,CAAC;6BAC7D,CAAC;4BACF,WAAW,IAAI,YAAY,CAAC,MAAM,CAAC;wBACrC,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,iCAAiC;YACnC,CAAC;QACH,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAEI,CAAC;gBACtC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAE,MAAgC,CAAC,OAAO,CAAC;oBACtE,CAAC,CAAE,MAA4C,CAAC,OAAO;oBACvD,CAAC,CAAC,CAAC,MAA4B,CAAC,CAAC;gBAEnC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC5B,IAAI,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;wBAC9C,8DAA8D;wBAC9D,iEAAiE;wBACjE,mEAAmE;wBACnE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;wBAC5D,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;4BACtD,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG;gCACtC,SAAS,EAAE,KAAK,CAAC,SAAS;gCAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;gCACpB,YAAY,EAAE,KAAK,CAAC,YAAY;gCAChC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,sBAAsB,CAAC;gCAC5D,OAAO,EAAE,KAAK,CAAC,OAAO;6BACvB,CAAC;4BACF,aAAa,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC;4BACxC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;wBACpC,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,iCAAiC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IAED,OAAO;QACL,aAAa;QACb,WAAW;QACX,KAAK,EAAE;YACL,WAAW;YACX,aAAa;YACb,UAAU;YACV,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACnC;KACF,CAAC;AACJ,CAAC;AA7GD,kDA6GC","sourcesContent":["import {\n buildProviderCacheKey,\n PERPS_CONSTANTS,\n PERPS_DISK_CACHE_MARKETS,\n PERPS_DISK_CACHE_USER_DATA,\n PROVIDER_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n AccountState,\n Order,\n PerpsMarketData,\n Position,\n} from '../types/index.js';\n\n/**\n * Multiplier applied to staleGuardMs (preloadGuardMs, currently 30s) to compute\n * the staleness cap for disk-hydrated timestamps. A factor of 10 ensures hydrated\n * data is always TTL-expired so the stream manager overwrites it with live data,\n * while still being recent enough for a useful first paint.\n */\nconst DISK_HYDRATION_STALENESS_FACTOR = 10;\n\n/** Minimal disk cache interface required by persistence utilities. */\nexport type PerpsDiskCache = {\n getItem(key: string): Promise<string | null>;\n getItemSync?(key: string): string | null;\n setItem(key: string, value: string): Promise<void>;\n};\n\n/** Shape of a single market entry persisted to disk cache. */\nexport type DiskCacheMarketEntry = {\n providerNetworkKey: string;\n data: PerpsMarketData[];\n timestamp: number;\n};\n\n/** Shape of a single user-data entry persisted to disk cache. */\nexport type DiskCacheUserEntry = {\n providerNetworkKey: string;\n address: string;\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n};\n\n/** Disk payload shape — either a single entry or a multi-provider wrapper. */\nexport type DiskCacheMarketPayload =\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n\nexport type DiskCacheUserPayload =\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n\n/**\n * Build the disk-cache payload for market data.\n * In aggregated mode, groups markets by provider into separate entries.\n *\n * @param markets - Current market data snapshot.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildMarketDataPayload(\n markets: PerpsMarketData[],\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheMarketPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheMarketEntry>();\n for (const market of markets) {\n const providerId = market.providerId ?? PROVIDER_CONFIG.DefaultProvider;\n const key = buildProviderCacheKey(providerId, isTestnet);\n const existing = entriesByKey.get(key);\n if (existing) {\n existing.data.push(market);\n } else {\n entriesByKey.set(key, {\n providerNetworkKey: key,\n data: [market],\n timestamp: now,\n });\n }\n }\n const entries = Array.from(entriesByKey.values());\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n data: markets,\n timestamp: now,\n };\n}\n\n/**\n * Build the disk-cache payload for user data (positions, orders, account).\n * In aggregated mode, groups entries by provider.\n *\n * @param positions - Current positions snapshot.\n * @param orders - Current orders snapshot.\n * @param accountState - Current account state snapshot.\n * @param address - EVM account address.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildUserDataPayload(\n positions: Position[],\n orders: Order[],\n accountState: AccountState | null,\n address: string,\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheUserPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheUserEntry>();\n const ensureEntry = (providerId: string): DiskCacheUserEntry => {\n const key = buildProviderCacheKey(providerId, isTestnet);\n let entry = entriesByKey.get(key);\n if (!entry) {\n entry = {\n providerNetworkKey: key,\n address,\n positions: [],\n orders: [],\n accountState: null,\n timestamp: now,\n };\n entriesByKey.set(key, entry);\n }\n return entry;\n };\n\n for (const position of positions) {\n ensureEntry(\n position.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).positions.push(position);\n }\n for (const order of orders) {\n ensureEntry(\n order.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).orders.push(order);\n }\n if (accountState) {\n ensureEntry(\n accountState.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).accountState = accountState;\n }\n\n const entries = Array.from(entriesByKey.values()).filter(\n (entry) =>\n entry.positions.length > 0 ||\n entry.orders.length > 0 ||\n entry.accountState !== null,\n );\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n address,\n positions,\n orders,\n accountState,\n timestamp: now,\n };\n}\n\n/**\n * Write market entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled market cache entries to persist.\n */\nexport function persistMarketEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheMarketEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_MARKETS, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/**\n * Write user data entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled user cache entries to persist.\n */\nexport function persistUserEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheUserEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/** Computed updates returned by hydrateFromDiskSync. */\nexport type HydrateFromDiskResult = {\n marketUpdates: Record<string, { data: PerpsMarketData[]; timestamp: number }>;\n userUpdates: Record<\n string,\n {\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n address: string;\n }\n >;\n stats: {\n marketCount: number;\n userPositions: number;\n userOrders: number;\n durationMs: number;\n };\n};\n\n/**\n * Read disk-persisted cache snapshots and compute the state updates to apply.\n * Returns plain objects rather than mutating state directly, so the caller\n * can apply all changes in a single batched this.update() call.\n *\n * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs\n * in the past so the stream manager always overwrites disk data with fresh live data.\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param currentMarketCache - Current cachedMarketDataByProvider state.\n * @param currentUserCache - Current cachedUserDataByProvider state.\n * @param staleGuardMs - preloadGuardMs constant from the controller.\n * @returns Updates to apply plus stats for debug logging.\n */\nexport function hydrateFromDiskSync(\n diskCache: PerpsDiskCache,\n currentMarketCache: Record<string, { timestamp: number }>,\n currentUserCache: Record<string, { timestamp: number }>,\n staleGuardMs: number,\n): HydrateFromDiskResult {\n const hydrateT0 = Date.now();\n const marketUpdates: HydrateFromDiskResult['marketUpdates'] = {};\n const userUpdates: HydrateFromDiskResult['userUpdates'] = {};\n let marketCount = 0;\n let userPositions = 0;\n let userOrders = 0;\n\n if (!diskCache.getItemSync) {\n return {\n marketUpdates,\n userUpdates,\n stats: { marketCount, userPositions, userOrders, durationMs: 0 },\n };\n }\n\n const staleHydratedTimestamp =\n Date.now() - staleGuardMs * DISK_HYDRATION_STALENESS_FACTOR - 1;\n\n try {\n const marketsRaw = diskCache.getItemSync(PERPS_DISK_CACHE_MARKETS);\n const userRaw = diskCache.getItemSync(PERPS_DISK_CACHE_USER_DATA);\n\n if (marketsRaw) {\n try {\n const parsed = JSON.parse(marketsRaw) as\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheMarketEntry[] }).entries\n : [parsed as DiskCacheMarketEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && Array.isArray(entry.data)) {\n const existing = currentMarketCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n const strippedData = entry.data.map((market) => ({\n ...market,\n price: PERPS_CONSTANTS.FallbackPriceDisplay,\n change24h: PERPS_CONSTANTS.FallbackDataDisplay,\n change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,\n }));\n marketUpdates[entry.providerNetworkKey] = {\n data: strippedData,\n // Disk-hydrated market snapshots are only for structural\n // first paint. Keep them TTL-stale so the stream manager\n // still fetches fresh prices on connect.\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n };\n marketCount += strippedData.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n\n if (userRaw) {\n try {\n const parsed = JSON.parse(userRaw) as\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheUserEntry[] }).entries\n : [parsed as DiskCacheUserEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && entry.address) {\n // Skip address check here — accounts may not be loaded yet at\n // constructor time. getCachedUserDataForActiveProvider validates\n // the address at read time, so stale-account data is never served.\n const existing = currentUserCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n userUpdates[entry.providerNetworkKey] = {\n positions: entry.positions,\n orders: entry.orders,\n accountState: entry.accountState,\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n address: entry.address,\n };\n userPositions += entry.positions.length;\n userOrders += entry.orders.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n } catch {\n // Disk read failure — non-critical\n }\n\n return {\n marketUpdates,\n userUpdates,\n stats: {\n marketCount,\n userPositions,\n userOrders,\n durationMs: Date.now() - hydrateT0,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"perpsDiskPersistence.cjs","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n buildProviderCacheKey,\n PERPS_CONSTANTS,\n PERPS_DISK_CACHE_MARKETS,\n PERPS_DISK_CACHE_USER_DATA,\n PROVIDER_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n AccountState,\n Order,\n PerpsMarketData,\n Position,\n} from '../types/index.js';\n\n/**\n * Multiplier applied to staleGuardMs (preloadGuardMs, currently 30s) to compute\n * the staleness cap for disk-hydrated timestamps. A factor of 10 ensures hydrated\n * data is always TTL-expired so the stream manager overwrites it with live data,\n * while still being recent enough for a useful first paint.\n */\nconst DISK_HYDRATION_STALENESS_FACTOR = 10;\n\n/** Minimal disk cache interface required by persistence utilities. */\nexport type PerpsDiskCache = {\n getItem(key: string): Promise<string | null>;\n getItemSync?(key: string): string | null;\n setItem(key: string, value: string): Promise<void>;\n};\n\n/** Shape of a single market entry persisted to disk cache. */\nexport type DiskCacheMarketEntry = {\n providerNetworkKey: string;\n data: PerpsMarketData[];\n timestamp: number;\n};\n\n/** Shape of a single user-data entry persisted to disk cache. */\nexport type DiskCacheUserEntry = {\n providerNetworkKey: string;\n address: string;\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n hip3ConfigVersion?: number;\n dexes?: string[];\n};\n\n/** Disk payload shape — either a single entry or a multi-provider wrapper. */\nexport type DiskCacheMarketPayload =\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n\nexport type DiskCacheUserPayload =\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n\n/**\n * Build the disk-cache payload for market data.\n * In aggregated mode, groups markets by provider into separate entries.\n *\n * @param markets - Current market data snapshot.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildMarketDataPayload(\n markets: PerpsMarketData[],\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheMarketPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheMarketEntry>();\n for (const market of markets) {\n const providerId = market.providerId ?? PROVIDER_CONFIG.DefaultProvider;\n const key = buildProviderCacheKey(providerId, isTestnet);\n const existing = entriesByKey.get(key);\n if (existing) {\n existing.data.push(market);\n } else {\n entriesByKey.set(key, {\n providerNetworkKey: key,\n data: [market],\n timestamp: now,\n });\n }\n }\n const entries = Array.from(entriesByKey.values());\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n data: markets,\n timestamp: now,\n };\n}\n\n/**\n * Build the disk-cache payload for user data (positions, orders, account).\n * In aggregated mode, groups entries by provider.\n *\n * @param positions - Current positions snapshot.\n * @param orders - Current orders snapshot.\n * @param accountState - Current account state snapshot.\n * @param address - EVM account address.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildUserDataPayload(\n positions: Position[],\n orders: Order[],\n accountState: AccountState | null,\n address: string,\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheUserPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheUserEntry>();\n const ensureEntry = (providerId: string): DiskCacheUserEntry => {\n const key = buildProviderCacheKey(providerId, isTestnet);\n let entry = entriesByKey.get(key);\n if (!entry) {\n entry = {\n providerNetworkKey: key,\n address,\n positions: [],\n orders: [],\n accountState: null,\n timestamp: now,\n };\n entriesByKey.set(key, entry);\n }\n return entry;\n };\n\n for (const position of positions) {\n ensureEntry(\n position.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).positions.push(position);\n }\n for (const order of orders) {\n ensureEntry(\n order.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).orders.push(order);\n }\n if (accountState) {\n ensureEntry(\n accountState.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).accountState = accountState;\n }\n\n const entries = Array.from(entriesByKey.values()).filter(\n (entry) =>\n entry.positions.length > 0 ||\n entry.orders.length > 0 ||\n entry.accountState !== null,\n );\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n address,\n positions,\n orders,\n accountState,\n timestamp: now,\n };\n}\n\n/**\n * Write market entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled market cache entries to persist.\n */\nexport function persistMarketEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheMarketEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_MARKETS, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/**\n * Write user data entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled user cache entries to persist.\n */\nexport async function persistUserEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheUserEntry[],\n): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n await diskCache.setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload));\n}\n\n/** Computed updates returned by hydrateFromDiskSync. */\nexport type HydrateFromDiskResult = {\n marketUpdates: Record<string, { data: PerpsMarketData[]; timestamp: number }>;\n userUpdates: Record<\n string,\n {\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n address: string;\n hip3ConfigVersion?: number;\n dexes?: string[];\n }\n >;\n stats: {\n marketCount: number;\n userPositions: number;\n userOrders: number;\n durationMs: number;\n };\n};\n\n/**\n * Read disk-persisted cache snapshots and compute the state updates to apply.\n * Returns plain objects rather than mutating state directly, so the caller\n * can apply all changes in a single batched this.update() call.\n *\n * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs\n * in the past so the stream manager always overwrites disk data with fresh live data.\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param currentMarketCache - Current cachedMarketDataByProvider state.\n * @param currentUserCache - Current cachedUserDataByProvider state.\n * @param staleGuardMs - preloadGuardMs constant from the controller.\n * @returns Updates to apply plus stats for debug logging.\n */\nexport function hydrateFromDiskSync(\n diskCache: PerpsDiskCache,\n currentMarketCache: Record<string, { timestamp: number }>,\n currentUserCache: Record<string, { timestamp: number }>,\n staleGuardMs: number,\n): HydrateFromDiskResult {\n const hydrateT0 = Date.now();\n const marketUpdates: HydrateFromDiskResult['marketUpdates'] = {};\n const userUpdates: HydrateFromDiskResult['userUpdates'] = {};\n let marketCount = 0;\n let userPositions = 0;\n let userOrders = 0;\n\n if (!diskCache.getItemSync) {\n return {\n marketUpdates,\n userUpdates,\n stats: { marketCount, userPositions, userOrders, durationMs: 0 },\n };\n }\n\n const staleHydratedTimestamp =\n Date.now() - staleGuardMs * DISK_HYDRATION_STALENESS_FACTOR - 1;\n\n try {\n const marketsRaw = diskCache.getItemSync(PERPS_DISK_CACHE_MARKETS);\n const userRaw = diskCache.getItemSync(PERPS_DISK_CACHE_USER_DATA);\n\n if (marketsRaw) {\n try {\n const parsed = JSON.parse(marketsRaw) as\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheMarketEntry[] }).entries\n : [parsed as DiskCacheMarketEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && Array.isArray(entry.data)) {\n const existing = currentMarketCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n const strippedData = entry.data.map((market) => {\n const structuralMarket = { ...market };\n if (\n structuralMarket.dataSource ===\n 'terminal-global-snapshot-mark'\n ) {\n delete structuralMarket.trend;\n }\n delete structuralMarket.dataSource;\n delete structuralMarket.sourceExpiresAt;\n return {\n ...structuralMarket,\n price: PERPS_CONSTANTS.FallbackPriceDisplay,\n change24h: PERPS_CONSTANTS.FallbackDataDisplay,\n change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,\n };\n });\n marketUpdates[entry.providerNetworkKey] = {\n data: strippedData,\n // Disk-hydrated market snapshots are only for structural\n // first paint. Keep them TTL-stale so the stream manager\n // still fetches fresh prices on connect.\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n };\n marketCount += strippedData.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n\n if (userRaw) {\n try {\n const parsed = JSON.parse(userRaw) as\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheUserEntry[] }).entries\n : [parsed as DiskCacheUserEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && entry.address) {\n // Skip address check here — accounts may not be loaded yet at\n // constructor time. getCachedUserDataForActiveProvider validates\n // the address at read time, so stale-account data is never served.\n const existing = currentUserCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n userUpdates[entry.providerNetworkKey] = {\n positions: entry.positions,\n orders: entry.orders,\n accountState: entry.accountState,\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n address: entry.address,\n hip3ConfigVersion: entry.hip3ConfigVersion,\n dexes: entry.dexes,\n };\n userPositions += entry.positions.length;\n userOrders += entry.orders.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n } catch {\n // Disk read failure — non-critical\n }\n\n return {\n marketUpdates,\n userUpdates,\n stats: {\n marketCount,\n userPositions,\n userOrders,\n durationMs: Date.now() - hydrateT0,\n },\n };\n}\n"]}
@@ -19,6 +19,8 @@ export type DiskCacheUserEntry = {
19
19
  orders: Order[];
20
20
  accountState: AccountState | null;
21
21
  timestamp: number;
22
+ hip3ConfigVersion?: number;
23
+ dexes?: string[];
22
24
  };
23
25
  /** Disk payload shape — either a single entry or a multi-provider wrapper. */
24
26
  export type DiskCacheMarketPayload = DiskCacheMarketEntry | {
@@ -65,7 +67,7 @@ export declare function persistMarketEntriesToDisk(diskCache: PerpsDiskCache, en
65
67
  * @param diskCache - Disk cache instance from controller infrastructure.
66
68
  * @param entries - Pre-assembled user cache entries to persist.
67
69
  */
68
- export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): void;
70
+ export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): Promise<void>;
69
71
  /** Computed updates returned by hydrateFromDiskSync. */
70
72
  export type HydrateFromDiskResult = {
71
73
  marketUpdates: Record<string, {
@@ -78,6 +80,8 @@ export type HydrateFromDiskResult = {
78
80
  accountState: AccountState | null;
79
81
  timestamp: number;
80
82
  address: string;
83
+ hip3ConfigVersion?: number;
84
+ dexes?: string[];
81
85
  }>;
82
86
  stats: {
83
87
  marketCount: number;
@@ -1 +1 @@
1
- {"version":3,"file":"perpsDiskPersistence.d.cts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"perpsDiskPersistence.d.cts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,8 @@ export type DiskCacheUserEntry = {
19
19
  orders: Order[];
20
20
  accountState: AccountState | null;
21
21
  timestamp: number;
22
+ hip3ConfigVersion?: number;
23
+ dexes?: string[];
22
24
  };
23
25
  /** Disk payload shape — either a single entry or a multi-provider wrapper. */
24
26
  export type DiskCacheMarketPayload = DiskCacheMarketEntry | {
@@ -65,7 +67,7 @@ export declare function persistMarketEntriesToDisk(diskCache: PerpsDiskCache, en
65
67
  * @param diskCache - Disk cache instance from controller infrastructure.
66
68
  * @param entries - Pre-assembled user cache entries to persist.
67
69
  */
68
- export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): void;
70
+ export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): Promise<void>;
69
71
  /** Computed updates returned by hydrateFromDiskSync. */
70
72
  export type HydrateFromDiskResult = {
71
73
  marketUpdates: Record<string, {
@@ -78,6 +80,8 @@ export type HydrateFromDiskResult = {
78
80
  accountState: AccountState | null;
79
81
  timestamp: number;
80
82
  address: string;
83
+ hip3ConfigVersion?: number;
84
+ dexes?: string[];
81
85
  }>;
82
86
  stats: {
83
87
  marketCount: number;
@@ -1 +1 @@
1
- {"version":3,"file":"perpsDiskPersistence.d.mts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"perpsDiskPersistence.d.mts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,YAAY,EACZ,KAAK,EACL,eAAe,EACf,QAAQ,EACT,2BAA0B;AAU3B,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD,CAAC;AAEF,8DAA8D;AAC9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAC9B,oBAAoB,GACpB;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB;IAAE,OAAO,EAAE,kBAAkB,EAAE,CAAA;CAAE,CAAC;AAEtC;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EAAE,EAC1B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,OAAO,EAClB,GAAG,EAAE,MAAM,GACV,sBAAsB,CA4BxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,KAAK,EAAE,EACf,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,OAAO,EAClB,GAAG,EAAE,MAAM,GACV,oBAAoB,CAuDtB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,oBAAoB,EAAE,GAC9B,IAAI,CAUN;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,kBAAkB,EAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wDAAwD;AACxD,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,eAAe,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,WAAW,EAAE,MAAM,CACjB,MAAM,EACN;QACE,SAAS,EAAE,QAAQ,EAAE,CAAC;QACtB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CACF,CAAC;IACF,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,cAAc,EACzB,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACzD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACvD,YAAY,EAAE,MAAM,GACnB,qBAAqB,CAqHvB"}
@@ -121,16 +121,12 @@ export function persistMarketEntriesToDisk(diskCache, entries) {
121
121
  * @param diskCache - Disk cache instance from controller infrastructure.
122
122
  * @param entries - Pre-assembled user cache entries to persist.
123
123
  */
124
- export function persistUserEntriesToDisk(diskCache, entries) {
124
+ export async function persistUserEntriesToDisk(diskCache, entries) {
125
125
  if (entries.length === 0) {
126
126
  return;
127
127
  }
128
128
  const payload = entries.length === 1 ? entries[0] : { entries };
129
- diskCache
130
- .setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload))
131
- .catch(() => {
132
- // Disk persistence is best-effort and must never block preload.
133
- });
129
+ await diskCache.setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload));
134
130
  }
135
131
  /**
136
132
  * Read disk-persisted cache snapshots and compute the state updates to apply.
@@ -174,12 +170,21 @@ export function hydrateFromDiskSync(diskCache, currentMarketCache, currentUserCa
174
170
  if (entry.providerNetworkKey && Array.isArray(entry.data)) {
175
171
  const existing = currentMarketCache[entry.providerNetworkKey];
176
172
  if (!existing || existing.timestamp < entry.timestamp) {
177
- const strippedData = entry.data.map((market) => ({
178
- ...market,
179
- price: PERPS_CONSTANTS.FallbackPriceDisplay,
180
- change24h: PERPS_CONSTANTS.FallbackDataDisplay,
181
- change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,
182
- }));
173
+ const strippedData = entry.data.map((market) => {
174
+ const structuralMarket = { ...market };
175
+ if (structuralMarket.dataSource ===
176
+ 'terminal-global-snapshot-mark') {
177
+ delete structuralMarket.trend;
178
+ }
179
+ delete structuralMarket.dataSource;
180
+ delete structuralMarket.sourceExpiresAt;
181
+ return {
182
+ ...structuralMarket,
183
+ price: PERPS_CONSTANTS.FallbackPriceDisplay,
184
+ change24h: PERPS_CONSTANTS.FallbackDataDisplay,
185
+ change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,
186
+ };
187
+ });
183
188
  marketUpdates[entry.providerNetworkKey] = {
184
189
  data: strippedData,
185
190
  // Disk-hydrated market snapshots are only for structural
@@ -215,6 +220,8 @@ export function hydrateFromDiskSync(diskCache, currentMarketCache, currentUserCa
215
220
  accountState: entry.accountState,
216
221
  timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),
217
222
  address: entry.address,
223
+ hip3ConfigVersion: entry.hip3ConfigVersion,
224
+ dexes: entry.dexes,
218
225
  };
219
226
  userPositions += entry.positions.length;
220
227
  userOrders += entry.orders.length;
@@ -1 +1 @@
1
- {"version":3,"file":"perpsDiskPersistence.mjs","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n buildProviderCacheKey,\n PERPS_CONSTANTS,\n PERPS_DISK_CACHE_MARKETS,\n PERPS_DISK_CACHE_USER_DATA,\n PROVIDER_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n AccountState,\n Order,\n PerpsMarketData,\n Position,\n} from '../types/index.js';\n\n/**\n * Multiplier applied to staleGuardMs (preloadGuardMs, currently 30s) to compute\n * the staleness cap for disk-hydrated timestamps. A factor of 10 ensures hydrated\n * data is always TTL-expired so the stream manager overwrites it with live data,\n * while still being recent enough for a useful first paint.\n */\nconst DISK_HYDRATION_STALENESS_FACTOR = 10;\n\n/** Minimal disk cache interface required by persistence utilities. */\nexport type PerpsDiskCache = {\n getItem(key: string): Promise<string | null>;\n getItemSync?(key: string): string | null;\n setItem(key: string, value: string): Promise<void>;\n};\n\n/** Shape of a single market entry persisted to disk cache. */\nexport type DiskCacheMarketEntry = {\n providerNetworkKey: string;\n data: PerpsMarketData[];\n timestamp: number;\n};\n\n/** Shape of a single user-data entry persisted to disk cache. */\nexport type DiskCacheUserEntry = {\n providerNetworkKey: string;\n address: string;\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n};\n\n/** Disk payload shape — either a single entry or a multi-provider wrapper. */\nexport type DiskCacheMarketPayload =\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n\nexport type DiskCacheUserPayload =\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n\n/**\n * Build the disk-cache payload for market data.\n * In aggregated mode, groups markets by provider into separate entries.\n *\n * @param markets - Current market data snapshot.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildMarketDataPayload(\n markets: PerpsMarketData[],\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheMarketPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheMarketEntry>();\n for (const market of markets) {\n const providerId = market.providerId ?? PROVIDER_CONFIG.DefaultProvider;\n const key = buildProviderCacheKey(providerId, isTestnet);\n const existing = entriesByKey.get(key);\n if (existing) {\n existing.data.push(market);\n } else {\n entriesByKey.set(key, {\n providerNetworkKey: key,\n data: [market],\n timestamp: now,\n });\n }\n }\n const entries = Array.from(entriesByKey.values());\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n data: markets,\n timestamp: now,\n };\n}\n\n/**\n * Build the disk-cache payload for user data (positions, orders, account).\n * In aggregated mode, groups entries by provider.\n *\n * @param positions - Current positions snapshot.\n * @param orders - Current orders snapshot.\n * @param accountState - Current account state snapshot.\n * @param address - EVM account address.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildUserDataPayload(\n positions: Position[],\n orders: Order[],\n accountState: AccountState | null,\n address: string,\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheUserPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheUserEntry>();\n const ensureEntry = (providerId: string): DiskCacheUserEntry => {\n const key = buildProviderCacheKey(providerId, isTestnet);\n let entry = entriesByKey.get(key);\n if (!entry) {\n entry = {\n providerNetworkKey: key,\n address,\n positions: [],\n orders: [],\n accountState: null,\n timestamp: now,\n };\n entriesByKey.set(key, entry);\n }\n return entry;\n };\n\n for (const position of positions) {\n ensureEntry(\n position.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).positions.push(position);\n }\n for (const order of orders) {\n ensureEntry(\n order.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).orders.push(order);\n }\n if (accountState) {\n ensureEntry(\n accountState.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).accountState = accountState;\n }\n\n const entries = Array.from(entriesByKey.values()).filter(\n (entry) =>\n entry.positions.length > 0 ||\n entry.orders.length > 0 ||\n entry.accountState !== null,\n );\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n address,\n positions,\n orders,\n accountState,\n timestamp: now,\n };\n}\n\n/**\n * Write market entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled market cache entries to persist.\n */\nexport function persistMarketEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheMarketEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_MARKETS, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/**\n * Write user data entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled user cache entries to persist.\n */\nexport function persistUserEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheUserEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/** Computed updates returned by hydrateFromDiskSync. */\nexport type HydrateFromDiskResult = {\n marketUpdates: Record<string, { data: PerpsMarketData[]; timestamp: number }>;\n userUpdates: Record<\n string,\n {\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n address: string;\n }\n >;\n stats: {\n marketCount: number;\n userPositions: number;\n userOrders: number;\n durationMs: number;\n };\n};\n\n/**\n * Read disk-persisted cache snapshots and compute the state updates to apply.\n * Returns plain objects rather than mutating state directly, so the caller\n * can apply all changes in a single batched this.update() call.\n *\n * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs\n * in the past so the stream manager always overwrites disk data with fresh live data.\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param currentMarketCache - Current cachedMarketDataByProvider state.\n * @param currentUserCache - Current cachedUserDataByProvider state.\n * @param staleGuardMs - preloadGuardMs constant from the controller.\n * @returns Updates to apply plus stats for debug logging.\n */\nexport function hydrateFromDiskSync(\n diskCache: PerpsDiskCache,\n currentMarketCache: Record<string, { timestamp: number }>,\n currentUserCache: Record<string, { timestamp: number }>,\n staleGuardMs: number,\n): HydrateFromDiskResult {\n const hydrateT0 = Date.now();\n const marketUpdates: HydrateFromDiskResult['marketUpdates'] = {};\n const userUpdates: HydrateFromDiskResult['userUpdates'] = {};\n let marketCount = 0;\n let userPositions = 0;\n let userOrders = 0;\n\n if (!diskCache.getItemSync) {\n return {\n marketUpdates,\n userUpdates,\n stats: { marketCount, userPositions, userOrders, durationMs: 0 },\n };\n }\n\n const staleHydratedTimestamp =\n Date.now() - staleGuardMs * DISK_HYDRATION_STALENESS_FACTOR - 1;\n\n try {\n const marketsRaw = diskCache.getItemSync(PERPS_DISK_CACHE_MARKETS);\n const userRaw = diskCache.getItemSync(PERPS_DISK_CACHE_USER_DATA);\n\n if (marketsRaw) {\n try {\n const parsed = JSON.parse(marketsRaw) as\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheMarketEntry[] }).entries\n : [parsed as DiskCacheMarketEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && Array.isArray(entry.data)) {\n const existing = currentMarketCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n const strippedData = entry.data.map((market) => ({\n ...market,\n price: PERPS_CONSTANTS.FallbackPriceDisplay,\n change24h: PERPS_CONSTANTS.FallbackDataDisplay,\n change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,\n }));\n marketUpdates[entry.providerNetworkKey] = {\n data: strippedData,\n // Disk-hydrated market snapshots are only for structural\n // first paint. Keep them TTL-stale so the stream manager\n // still fetches fresh prices on connect.\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n };\n marketCount += strippedData.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n\n if (userRaw) {\n try {\n const parsed = JSON.parse(userRaw) as\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheUserEntry[] }).entries\n : [parsed as DiskCacheUserEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && entry.address) {\n // Skip address check here — accounts may not be loaded yet at\n // constructor time. getCachedUserDataForActiveProvider validates\n // the address at read time, so stale-account data is never served.\n const existing = currentUserCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n userUpdates[entry.providerNetworkKey] = {\n positions: entry.positions,\n orders: entry.orders,\n accountState: entry.accountState,\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n address: entry.address,\n };\n userPositions += entry.positions.length;\n userOrders += entry.orders.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n } catch {\n // Disk read failure — non-critical\n }\n\n return {\n marketUpdates,\n userUpdates,\n stats: {\n marketCount,\n userPositions,\n userOrders,\n durationMs: Date.now() - hydrateT0,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"perpsDiskPersistence.mjs","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n buildProviderCacheKey,\n PERPS_CONSTANTS,\n PERPS_DISK_CACHE_MARKETS,\n PERPS_DISK_CACHE_USER_DATA,\n PROVIDER_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n AccountState,\n Order,\n PerpsMarketData,\n Position,\n} from '../types/index.js';\n\n/**\n * Multiplier applied to staleGuardMs (preloadGuardMs, currently 30s) to compute\n * the staleness cap for disk-hydrated timestamps. A factor of 10 ensures hydrated\n * data is always TTL-expired so the stream manager overwrites it with live data,\n * while still being recent enough for a useful first paint.\n */\nconst DISK_HYDRATION_STALENESS_FACTOR = 10;\n\n/** Minimal disk cache interface required by persistence utilities. */\nexport type PerpsDiskCache = {\n getItem(key: string): Promise<string | null>;\n getItemSync?(key: string): string | null;\n setItem(key: string, value: string): Promise<void>;\n};\n\n/** Shape of a single market entry persisted to disk cache. */\nexport type DiskCacheMarketEntry = {\n providerNetworkKey: string;\n data: PerpsMarketData[];\n timestamp: number;\n};\n\n/** Shape of a single user-data entry persisted to disk cache. */\nexport type DiskCacheUserEntry = {\n providerNetworkKey: string;\n address: string;\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n hip3ConfigVersion?: number;\n dexes?: string[];\n};\n\n/** Disk payload shape — either a single entry or a multi-provider wrapper. */\nexport type DiskCacheMarketPayload =\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n\nexport type DiskCacheUserPayload =\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n\n/**\n * Build the disk-cache payload for market data.\n * In aggregated mode, groups markets by provider into separate entries.\n *\n * @param markets - Current market data snapshot.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildMarketDataPayload(\n markets: PerpsMarketData[],\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheMarketPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheMarketEntry>();\n for (const market of markets) {\n const providerId = market.providerId ?? PROVIDER_CONFIG.DefaultProvider;\n const key = buildProviderCacheKey(providerId, isTestnet);\n const existing = entriesByKey.get(key);\n if (existing) {\n existing.data.push(market);\n } else {\n entriesByKey.set(key, {\n providerNetworkKey: key,\n data: [market],\n timestamp: now,\n });\n }\n }\n const entries = Array.from(entriesByKey.values());\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n data: markets,\n timestamp: now,\n };\n}\n\n/**\n * Build the disk-cache payload for user data (positions, orders, account).\n * In aggregated mode, groups entries by provider.\n *\n * @param positions - Current positions snapshot.\n * @param orders - Current orders snapshot.\n * @param accountState - Current account state snapshot.\n * @param address - EVM account address.\n * @param activeProvider - The active provider id (may be \"aggregated\").\n * @param isTestnet - Global testnet flag.\n * @param now - Timestamp to stamp entries with.\n * @returns Payload ready for JSON serialization.\n */\nexport function buildUserDataPayload(\n positions: Position[],\n orders: Order[],\n accountState: AccountState | null,\n address: string,\n activeProvider: string,\n isTestnet: boolean,\n now: number,\n): DiskCacheUserPayload {\n if (activeProvider === 'aggregated') {\n const entriesByKey = new Map<string, DiskCacheUserEntry>();\n const ensureEntry = (providerId: string): DiskCacheUserEntry => {\n const key = buildProviderCacheKey(providerId, isTestnet);\n let entry = entriesByKey.get(key);\n if (!entry) {\n entry = {\n providerNetworkKey: key,\n address,\n positions: [],\n orders: [],\n accountState: null,\n timestamp: now,\n };\n entriesByKey.set(key, entry);\n }\n return entry;\n };\n\n for (const position of positions) {\n ensureEntry(\n position.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).positions.push(position);\n }\n for (const order of orders) {\n ensureEntry(\n order.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).orders.push(order);\n }\n if (accountState) {\n ensureEntry(\n accountState.providerId ?? PROVIDER_CONFIG.DefaultProvider,\n ).accountState = accountState;\n }\n\n const entries = Array.from(entriesByKey.values()).filter(\n (entry) =>\n entry.positions.length > 0 ||\n entry.orders.length > 0 ||\n entry.accountState !== null,\n );\n return entries.length === 1 ? entries[0] : { entries };\n }\n return {\n providerNetworkKey: buildProviderCacheKey(\n activeProvider ?? PROVIDER_CONFIG.DefaultProvider,\n isTestnet,\n ),\n address,\n positions,\n orders,\n accountState,\n timestamp: now,\n };\n}\n\n/**\n * Write market entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled market cache entries to persist.\n */\nexport function persistMarketEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheMarketEntry[],\n): void {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n diskCache\n .setItem(PERPS_DISK_CACHE_MARKETS, JSON.stringify(payload))\n .catch(() => {\n // Disk persistence is best-effort and must never block preload.\n });\n}\n\n/**\n * Write user data entries to disk (best-effort, non-blocking).\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param entries - Pre-assembled user cache entries to persist.\n */\nexport async function persistUserEntriesToDisk(\n diskCache: PerpsDiskCache,\n entries: DiskCacheUserEntry[],\n): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n const payload = entries.length === 1 ? entries[0] : { entries };\n await diskCache.setItem(PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload));\n}\n\n/** Computed updates returned by hydrateFromDiskSync. */\nexport type HydrateFromDiskResult = {\n marketUpdates: Record<string, { data: PerpsMarketData[]; timestamp: number }>;\n userUpdates: Record<\n string,\n {\n positions: Position[];\n orders: Order[];\n accountState: AccountState | null;\n timestamp: number;\n address: string;\n hip3ConfigVersion?: number;\n dexes?: string[];\n }\n >;\n stats: {\n marketCount: number;\n userPositions: number;\n userOrders: number;\n durationMs: number;\n };\n};\n\n/**\n * Read disk-persisted cache snapshots and compute the state updates to apply.\n * Returns plain objects rather than mutating state directly, so the caller\n * can apply all changes in a single batched this.update() call.\n *\n * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs\n * in the past so the stream manager always overwrites disk data with fresh live data.\n *\n * @param diskCache - Disk cache instance from controller infrastructure.\n * @param currentMarketCache - Current cachedMarketDataByProvider state.\n * @param currentUserCache - Current cachedUserDataByProvider state.\n * @param staleGuardMs - preloadGuardMs constant from the controller.\n * @returns Updates to apply plus stats for debug logging.\n */\nexport function hydrateFromDiskSync(\n diskCache: PerpsDiskCache,\n currentMarketCache: Record<string, { timestamp: number }>,\n currentUserCache: Record<string, { timestamp: number }>,\n staleGuardMs: number,\n): HydrateFromDiskResult {\n const hydrateT0 = Date.now();\n const marketUpdates: HydrateFromDiskResult['marketUpdates'] = {};\n const userUpdates: HydrateFromDiskResult['userUpdates'] = {};\n let marketCount = 0;\n let userPositions = 0;\n let userOrders = 0;\n\n if (!diskCache.getItemSync) {\n return {\n marketUpdates,\n userUpdates,\n stats: { marketCount, userPositions, userOrders, durationMs: 0 },\n };\n }\n\n const staleHydratedTimestamp =\n Date.now() - staleGuardMs * DISK_HYDRATION_STALENESS_FACTOR - 1;\n\n try {\n const marketsRaw = diskCache.getItemSync(PERPS_DISK_CACHE_MARKETS);\n const userRaw = diskCache.getItemSync(PERPS_DISK_CACHE_USER_DATA);\n\n if (marketsRaw) {\n try {\n const parsed = JSON.parse(marketsRaw) as\n | DiskCacheMarketEntry\n | { entries: DiskCacheMarketEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheMarketEntry[] }).entries\n : [parsed as DiskCacheMarketEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && Array.isArray(entry.data)) {\n const existing = currentMarketCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n const strippedData = entry.data.map((market) => {\n const structuralMarket = { ...market };\n if (\n structuralMarket.dataSource ===\n 'terminal-global-snapshot-mark'\n ) {\n delete structuralMarket.trend;\n }\n delete structuralMarket.dataSource;\n delete structuralMarket.sourceExpiresAt;\n return {\n ...structuralMarket,\n price: PERPS_CONSTANTS.FallbackPriceDisplay,\n change24h: PERPS_CONSTANTS.FallbackDataDisplay,\n change24hPercent: PERPS_CONSTANTS.FallbackPercentageDisplay,\n };\n });\n marketUpdates[entry.providerNetworkKey] = {\n data: strippedData,\n // Disk-hydrated market snapshots are only for structural\n // first paint. Keep them TTL-stale so the stream manager\n // still fetches fresh prices on connect.\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n };\n marketCount += strippedData.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n\n if (userRaw) {\n try {\n const parsed = JSON.parse(userRaw) as\n | DiskCacheUserEntry\n | { entries: DiskCacheUserEntry[] };\n const entries = Array.isArray((parsed as { entries?: unknown }).entries)\n ? (parsed as { entries: DiskCacheUserEntry[] }).entries\n : [parsed as DiskCacheUserEntry];\n\n for (const entry of entries) {\n if (entry.providerNetworkKey && entry.address) {\n // Skip address check here — accounts may not be loaded yet at\n // constructor time. getCachedUserDataForActiveProvider validates\n // the address at read time, so stale-account data is never served.\n const existing = currentUserCache[entry.providerNetworkKey];\n if (!existing || existing.timestamp < entry.timestamp) {\n userUpdates[entry.providerNetworkKey] = {\n positions: entry.positions,\n orders: entry.orders,\n accountState: entry.accountState,\n timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),\n address: entry.address,\n hip3ConfigVersion: entry.hip3ConfigVersion,\n dexes: entry.dexes,\n };\n userPositions += entry.positions.length;\n userOrders += entry.orders.length;\n }\n }\n }\n } catch {\n // Corrupt JSON — silently ignore\n }\n }\n } catch {\n // Disk read failure — non-critical\n }\n\n return {\n marketUpdates,\n userUpdates,\n stats: {\n marketCount,\n userPositions,\n userOrders,\n durationMs: Date.now() - hydrateT0,\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/perps-controller",
3
- "version": "11.0.0-preview-9efd0b754",
3
+ "version": "12.0.0-preview-4e64b66e8",
4
4
  "description": "Controller for perpetual trading functionality in MetaMask",
5
5
  "keywords": [
6
6
  "Ethereum",