@metamask/snap-networks-utils 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -1
- package/README.md +4 -10
- package/dist/index.cjs +102 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +51 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +32 -1
- package/dist/index.mjs.map +1 -1
- package/dist/providers/assets/AssetsProvider.cjs +36 -18
- package/dist/providers/assets/AssetsProvider.cjs.map +1 -1
- package/dist/providers/assets/AssetsProvider.d.cts +30 -2
- package/dist/providers/assets/AssetsProvider.d.cts.map +1 -1
- package/dist/providers/assets/AssetsProvider.d.mts +30 -2
- package/dist/providers/assets/AssetsProvider.d.mts.map +1 -1
- package/dist/providers/assets/AssetsProvider.mjs +36 -18
- package/dist/providers/assets/AssetsProvider.mjs.map +1 -1
- package/dist/providers/remote-feature-flags/RemoteFeatureFlagsProvider.cjs +4 -17
- package/dist/providers/remote-feature-flags/RemoteFeatureFlagsProvider.cjs.map +1 -1
- package/dist/providers/remote-feature-flags/RemoteFeatureFlagsProvider.mjs +4 -17
- package/dist/providers/remote-feature-flags/RemoteFeatureFlagsProvider.mjs.map +1 -1
- package/dist/services/analytics/AnalyticsService.cjs +225 -0
- package/dist/services/analytics/AnalyticsService.cjs.map +1 -0
- package/dist/services/analytics/AnalyticsService.d.cts +175 -0
- package/dist/services/analytics/AnalyticsService.d.cts.map +1 -0
- package/dist/services/analytics/AnalyticsService.d.mts +175 -0
- package/dist/services/analytics/AnalyticsService.d.mts.map +1 -0
- package/dist/services/analytics/AnalyticsService.mjs +221 -0
- package/dist/services/analytics/AnalyticsService.mjs.map +1 -0
- package/dist/ui/ActionHeader/ActionHeader.cjs +23 -0
- package/dist/ui/ActionHeader/ActionHeader.cjs.map +1 -0
- package/dist/ui/ActionHeader/ActionHeader.d.cts +27 -0
- package/dist/ui/ActionHeader/ActionHeader.d.cts.map +1 -0
- package/dist/ui/ActionHeader/ActionHeader.d.mts +27 -0
- package/dist/ui/ActionHeader/ActionHeader.d.mts.map +1 -0
- package/dist/ui/ActionHeader/ActionHeader.mjs +19 -0
- package/dist/ui/ActionHeader/ActionHeader.mjs.map +1 -0
- package/dist/ui/Asset/Asset.cjs +68 -0
- package/dist/ui/Asset/Asset.cjs.map +1 -0
- package/dist/ui/Asset/Asset.d.cts +83 -0
- package/dist/ui/Asset/Asset.d.cts.map +1 -0
- package/dist/ui/Asset/Asset.d.mts +83 -0
- package/dist/ui/Asset/Asset.d.mts.map +1 -0
- package/dist/ui/Asset/Asset.mjs +60 -0
- package/dist/ui/Asset/Asset.mjs.map +1 -0
- package/dist/ui/Asset/question-mark.svg +3 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.cjs +57 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.cjs.map +1 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.d.cts +50 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.d.cts.map +1 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.d.mts +50 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.d.mts.map +1 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.mjs +53 -0
- package/dist/ui/EstimatedChanges/EstimatedChanges.mjs.map +1 -0
- package/dist/ui/TransactionAlert/TransactionAlert.cjs +72 -0
- package/dist/ui/TransactionAlert/TransactionAlert.cjs.map +1 -0
- package/dist/ui/TransactionAlert/TransactionAlert.d.cts +58 -0
- package/dist/ui/TransactionAlert/TransactionAlert.d.cts.map +1 -0
- package/dist/ui/TransactionAlert/TransactionAlert.d.mts +58 -0
- package/dist/ui/TransactionAlert/TransactionAlert.d.mts.map +1 -0
- package/dist/ui/TransactionAlert/TransactionAlert.mjs +68 -0
- package/dist/ui/TransactionAlert/TransactionAlert.mjs.map +1 -0
- package/dist/utils/async/async.cjs +95 -0
- package/dist/utils/async/async.cjs.map +1 -0
- package/dist/utils/async/async.d.cts +51 -0
- package/dist/utils/async/async.d.cts.map +1 -0
- package/dist/utils/async/async.d.mts +51 -0
- package/dist/utils/async/async.d.mts.map +1 -0
- package/dist/utils/async/async.mjs +88 -0
- package/dist/utils/async/async.mjs.map +1 -0
- package/dist/utils/buildUrl/buildUrl.cjs +54 -0
- package/dist/utils/buildUrl/buildUrl.cjs.map +1 -0
- package/dist/utils/buildUrl/buildUrl.d.cts +15 -0
- package/dist/utils/buildUrl/buildUrl.d.cts.map +1 -0
- package/dist/utils/buildUrl/buildUrl.d.mts +15 -0
- package/dist/utils/buildUrl/buildUrl.d.mts.map +1 -0
- package/dist/utils/buildUrl/buildUrl.mjs +51 -0
- package/dist/utils/buildUrl/buildUrl.mjs.map +1 -0
- package/dist/utils/cache/InMemoryCache.cjs +134 -0
- package/dist/utils/cache/InMemoryCache.cjs.map +1 -0
- package/dist/utils/cache/InMemoryCache.d.cts +29 -0
- package/dist/utils/cache/InMemoryCache.d.cts.map +1 -0
- package/dist/utils/cache/InMemoryCache.d.mts +29 -0
- package/dist/utils/cache/InMemoryCache.d.mts.map +1 -0
- package/dist/utils/cache/InMemoryCache.mjs +130 -0
- package/dist/utils/cache/InMemoryCache.mjs.map +1 -0
- package/dist/utils/cache/StateCache.cjs +193 -0
- package/dist/utils/cache/StateCache.cjs.map +1 -0
- package/dist/utils/cache/StateCache.d.cts +96 -0
- package/dist/utils/cache/StateCache.d.cts.map +1 -0
- package/dist/utils/cache/StateCache.d.mts +96 -0
- package/dist/utils/cache/StateCache.d.mts.map +1 -0
- package/dist/utils/cache/StateCache.mjs +189 -0
- package/dist/utils/cache/StateCache.mjs.map +1 -0
- package/dist/utils/cache/types.cjs +3 -0
- package/dist/utils/cache/types.cjs.map +1 -0
- package/dist/utils/cache/types.d.cts +99 -0
- package/dist/utils/cache/types.d.cts.map +1 -0
- package/dist/utils/cache/types.d.mts +99 -0
- package/dist/utils/cache/types.d.mts.map +1 -0
- package/dist/utils/cache/types.mjs +2 -0
- package/dist/utils/cache/types.mjs.map +1 -0
- package/dist/utils/cache/useCache.cjs +82 -0
- package/dist/utils/cache/useCache.cjs.map +1 -0
- package/dist/utils/cache/useCache.d.cts +57 -0
- package/dist/utils/cache/useCache.d.cts.map +1 -0
- package/dist/utils/cache/useCache.d.mts +57 -0
- package/dist/utils/cache/useCache.d.mts.map +1 -0
- package/dist/utils/cache/useCache.mjs +78 -0
- package/dist/utils/cache/useCache.mjs.map +1 -0
- package/dist/utils/cache/useCacheUntil.cjs +80 -0
- package/dist/utils/cache/useCacheUntil.cjs.map +1 -0
- package/dist/utils/cache/useCacheUntil.d.cts +54 -0
- package/dist/utils/cache/useCacheUntil.d.cts.map +1 -0
- package/dist/utils/cache/useCacheUntil.d.mts +54 -0
- package/dist/utils/cache/useCacheUntil.d.mts.map +1 -0
- package/dist/utils/cache/useCacheUntil.mjs +76 -0
- package/dist/utils/cache/useCacheUntil.mjs.map +1 -0
- package/dist/utils/config/BaseConfigProvider.cjs +62 -0
- package/dist/utils/config/BaseConfigProvider.cjs.map +1 -0
- package/dist/utils/config/BaseConfigProvider.d.cts +54 -0
- package/dist/utils/config/BaseConfigProvider.d.cts.map +1 -0
- package/dist/utils/config/BaseConfigProvider.d.mts +54 -0
- package/dist/utils/config/BaseConfigProvider.d.mts.map +1 -0
- package/dist/utils/config/BaseConfigProvider.mjs +58 -0
- package/dist/utils/config/BaseConfigProvider.mjs.map +1 -0
- package/dist/utils/config/envStructs.cjs +69 -0
- package/dist/utils/config/envStructs.cjs.map +1 -0
- package/dist/utils/config/envStructs.d.cts +57 -0
- package/dist/utils/config/envStructs.d.cts.map +1 -0
- package/dist/utils/config/envStructs.d.mts +57 -0
- package/dist/utils/config/envStructs.d.mts.map +1 -0
- package/dist/utils/config/envStructs.mjs +61 -0
- package/dist/utils/config/envStructs.mjs.map +1 -0
- package/dist/utils/config/environmentStruct.cjs +19 -0
- package/dist/utils/config/environmentStruct.cjs.map +1 -0
- package/dist/utils/config/environmentStruct.d.cts +20 -0
- package/dist/utils/config/environmentStruct.d.cts.map +1 -0
- package/dist/utils/config/environmentStruct.d.mts +20 -0
- package/dist/utils/config/environmentStruct.d.mts.map +1 -0
- package/dist/utils/config/environmentStruct.mjs +16 -0
- package/dist/utils/config/environmentStruct.mjs.map +1 -0
- package/dist/utils/config/logLevelStruct.cjs +14 -0
- package/dist/utils/config/logLevelStruct.cjs.map +1 -0
- package/dist/utils/config/logLevelStruct.d.cts +17 -0
- package/dist/utils/config/logLevelStruct.d.cts.map +1 -0
- package/dist/utils/config/logLevelStruct.d.mts +17 -0
- package/dist/utils/config/logLevelStruct.d.mts.map +1 -0
- package/dist/utils/config/logLevelStruct.mjs +11 -0
- package/dist/utils/config/logLevelStruct.mjs.map +1 -0
- package/dist/utils/config/parseEnv.cjs +30 -0
- package/dist/utils/config/parseEnv.cjs.map +1 -0
- package/dist/utils/config/parseEnv.d.cts +19 -0
- package/dist/utils/config/parseEnv.d.cts.map +1 -0
- package/dist/utils/config/parseEnv.d.mts +19 -0
- package/dist/utils/config/parseEnv.d.mts.map +1 -0
- package/dist/utils/config/parseEnv.mjs +26 -0
- package/dist/utils/config/parseEnv.mjs.map +1 -0
- package/dist/utils/dedupe/InFlightCoalescer.cjs +28 -0
- package/dist/utils/dedupe/InFlightCoalescer.cjs.map +1 -0
- package/dist/utils/dedupe/InFlightCoalescer.d.cts +12 -0
- package/dist/utils/dedupe/InFlightCoalescer.d.cts.map +1 -0
- package/dist/utils/dedupe/InFlightCoalescer.d.mts +12 -0
- package/dist/utils/dedupe/InFlightCoalescer.d.mts.map +1 -0
- package/dist/utils/dedupe/InFlightCoalescer.mjs +24 -0
- package/dist/utils/dedupe/InFlightCoalescer.mjs.map +1 -0
- package/dist/utils/errors/errors.cjs +44 -0
- package/dist/utils/errors/errors.cjs.map +1 -0
- package/dist/utils/errors/errors.d.cts +37 -0
- package/dist/utils/errors/errors.d.cts.map +1 -0
- package/dist/utils/errors/errors.d.mts +37 -0
- package/dist/utils/errors/errors.d.mts.map +1 -0
- package/dist/utils/errors/errors.mjs +40 -0
- package/dist/utils/errors/errors.mjs.map +1 -0
- package/dist/utils/errors/index.cjs +18 -0
- package/dist/utils/errors/index.cjs.map +1 -0
- package/dist/utils/errors/index.d.cts +10 -0
- package/dist/utils/errors/index.d.cts.map +1 -0
- package/dist/utils/errors/index.d.mts +10 -0
- package/dist/utils/errors/index.d.mts.map +1 -0
- package/dist/utils/errors/index.mjs +6 -0
- package/dist/utils/errors/index.mjs.map +1 -0
- package/dist/utils/errors/snapRpcError.cjs +33 -0
- package/dist/utils/errors/snapRpcError.cjs.map +1 -0
- package/dist/utils/errors/snapRpcError.d.cts +11 -0
- package/dist/utils/errors/snapRpcError.d.cts.map +1 -0
- package/dist/utils/errors/snapRpcError.d.mts +11 -0
- package/dist/utils/errors/snapRpcError.d.mts.map +1 -0
- package/dist/utils/errors/snapRpcError.mjs +30 -0
- package/dist/utils/errors/snapRpcError.mjs.map +1 -0
- package/dist/utils/errors/stringifyReason.cjs +36 -0
- package/dist/utils/errors/stringifyReason.cjs.map +1 -0
- package/dist/utils/errors/stringifyReason.d.cts +15 -0
- package/dist/utils/errors/stringifyReason.d.cts.map +1 -0
- package/dist/utils/errors/stringifyReason.d.mts +15 -0
- package/dist/utils/errors/stringifyReason.d.mts.map +1 -0
- package/dist/utils/errors/stringifyReason.mjs +33 -0
- package/dist/utils/errors/stringifyReason.mjs.map +1 -0
- package/dist/utils/errors/syncError.cjs +98 -0
- package/dist/utils/errors/syncError.cjs.map +1 -0
- package/dist/utils/errors/syncError.d.cts +60 -0
- package/dist/utils/errors/syncError.d.cts.map +1 -0
- package/dist/utils/errors/syncError.d.mts +60 -0
- package/dist/utils/errors/syncError.d.mts.map +1 -0
- package/dist/utils/errors/syncError.mjs +92 -0
- package/dist/utils/errors/syncError.mjs.map +1 -0
- package/dist/utils/errors/trackError.cjs +76 -0
- package/dist/utils/errors/trackError.cjs.map +1 -0
- package/dist/utils/errors/trackError.d.cts +64 -0
- package/dist/utils/errors/trackError.d.cts.map +1 -0
- package/dist/utils/errors/trackError.d.mts +64 -0
- package/dist/utils/errors/trackError.d.mts.map +1 -0
- package/dist/utils/errors/trackError.mjs +72 -0
- package/dist/utils/errors/trackError.mjs.map +1 -0
- package/dist/utils/handlers/wrapSnapHandlers.cjs +57 -0
- package/dist/utils/handlers/wrapSnapHandlers.cjs.map +1 -0
- package/dist/utils/handlers/wrapSnapHandlers.d.cts +74 -0
- package/dist/utils/handlers/wrapSnapHandlers.d.cts.map +1 -0
- package/dist/utils/handlers/wrapSnapHandlers.d.mts +74 -0
- package/dist/utils/handlers/wrapSnapHandlers.d.mts.map +1 -0
- package/dist/utils/handlers/wrapSnapHandlers.mjs +53 -0
- package/dist/utils/handlers/wrapSnapHandlers.mjs.map +1 -0
- package/dist/utils/keyringAccount/keyringAccount.cjs +25 -0
- package/dist/utils/keyringAccount/keyringAccount.cjs.map +1 -0
- package/dist/utils/keyringAccount/keyringAccount.d.cts +29 -0
- package/dist/utils/keyringAccount/keyringAccount.d.cts.map +1 -0
- package/dist/utils/keyringAccount/keyringAccount.d.mts +29 -0
- package/dist/utils/keyringAccount/keyringAccount.d.mts.map +1 -0
- package/dist/utils/keyringAccount/keyringAccount.mjs +22 -0
- package/dist/utils/keyringAccount/keyringAccount.mjs.map +1 -0
- package/dist/utils/logger/Logger.cjs +140 -0
- package/dist/utils/logger/Logger.cjs.map +1 -0
- package/dist/utils/logger/Logger.d.cts +118 -0
- package/dist/utils/logger/Logger.d.cts.map +1 -0
- package/dist/utils/logger/Logger.d.mts +118 -0
- package/dist/utils/logger/Logger.d.mts.map +1 -0
- package/dist/utils/logger/Logger.mjs +136 -0
- package/dist/utils/logger/Logger.mjs.map +1 -0
- package/dist/utils/logger/__mocks__/Logger.cjs +26 -0
- package/dist/utils/logger/__mocks__/Logger.cjs.map +1 -0
- package/dist/utils/logger/__mocks__/Logger.d.cts +3 -0
- package/dist/utils/logger/__mocks__/Logger.d.cts.map +1 -0
- package/dist/utils/logger/__mocks__/Logger.d.mts +3 -0
- package/dist/utils/logger/__mocks__/Logger.d.mts.map +1 -0
- package/dist/utils/logger/__mocks__/Logger.mjs +23 -0
- package/dist/utils/logger/__mocks__/Logger.mjs.map +1 -0
- package/dist/utils/originPermissions/createOriginPermissions.cjs +30 -0
- package/dist/utils/originPermissions/createOriginPermissions.cjs.map +1 -0
- package/dist/utils/originPermissions/createOriginPermissions.d.cts +24 -0
- package/dist/utils/originPermissions/createOriginPermissions.d.cts.map +1 -0
- package/dist/utils/originPermissions/createOriginPermissions.d.mts +24 -0
- package/dist/utils/originPermissions/createOriginPermissions.d.mts.map +1 -0
- package/dist/utils/originPermissions/createOriginPermissions.mjs +26 -0
- package/dist/utils/originPermissions/createOriginPermissions.mjs.map +1 -0
- package/dist/utils/originPermissions/validateOrigin.cjs +23 -0
- package/dist/utils/originPermissions/validateOrigin.cjs.map +1 -0
- package/dist/utils/originPermissions/validateOrigin.d.cts +10 -0
- package/dist/utils/originPermissions/validateOrigin.d.cts.map +1 -0
- package/dist/utils/originPermissions/validateOrigin.d.mts +10 -0
- package/dist/utils/originPermissions/validateOrigin.d.mts.map +1 -0
- package/dist/utils/originPermissions/validateOrigin.mjs +19 -0
- package/dist/utils/originPermissions/validateOrigin.mjs.map +1 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.cjs +89 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.cjs.map +1 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.d.cts +143 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.d.cts.map +1 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.d.mts +143 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.d.mts.map +1 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.mjs +85 -0
- package/dist/utils/proofOfOwnership/proofOfOwnership.mjs.map +1 -0
- package/dist/utils/safeMerge/safeMerge.cjs +26 -0
- package/dist/utils/safeMerge/safeMerge.cjs.map +1 -0
- package/dist/utils/safeMerge/safeMerge.d.cts +17 -0
- package/dist/utils/safeMerge/safeMerge.d.cts.map +1 -0
- package/dist/utils/safeMerge/safeMerge.d.mts +17 -0
- package/dist/utils/safeMerge/safeMerge.d.mts.map +1 -0
- package/dist/utils/safeMerge/safeMerge.mjs +22 -0
- package/dist/utils/safeMerge/safeMerge.mjs.map +1 -0
- package/dist/utils/sanitize/sanitize.cjs +42 -0
- package/dist/utils/sanitize/sanitize.cjs.map +1 -0
- package/dist/utils/sanitize/sanitize.d.cts +15 -0
- package/dist/utils/sanitize/sanitize.d.cts.map +1 -0
- package/dist/utils/sanitize/sanitize.d.mts +15 -0
- package/dist/utils/sanitize/sanitize.d.mts.map +1 -0
- package/dist/utils/sanitize/sanitize.mjs +38 -0
- package/dist/utils/sanitize/sanitize.mjs.map +1 -0
- package/dist/utils/serialization/serialization.cjs +73 -0
- package/dist/utils/serialization/serialization.cjs.map +1 -0
- package/dist/utils/serialization/serialization.d.cts +19 -0
- package/dist/utils/serialization/serialization.d.cts.map +1 -0
- package/dist/utils/serialization/serialization.d.mts +19 -0
- package/dist/utils/serialization/serialization.d.mts.map +1 -0
- package/dist/utils/serialization/serialization.mjs +69 -0
- package/dist/utils/serialization/serialization.mjs.map +1 -0
- package/dist/utils/serialization/types.cjs +3 -0
- package/dist/utils/serialization/types.cjs.map +1 -0
- package/dist/utils/serialization/types.d.cts +8 -0
- package/dist/utils/serialization/types.d.cts.map +1 -0
- package/dist/utils/serialization/types.d.mts +8 -0
- package/dist/utils/serialization/types.d.mts.map +1 -0
- package/dist/utils/serialization/types.mjs +2 -0
- package/dist/utils/serialization/types.mjs.map +1 -0
- package/dist/utils/state/IStateManager.cjs +3 -0
- package/dist/utils/state/IStateManager.cjs.map +1 -0
- package/dist/utils/state/IStateManager.d.cts +69 -0
- package/dist/utils/state/IStateManager.d.cts.map +1 -0
- package/dist/utils/state/IStateManager.d.mts +69 -0
- package/dist/utils/state/IStateManager.d.mts.map +1 -0
- package/dist/utils/state/IStateManager.mjs +2 -0
- package/dist/utils/state/IStateManager.mjs.map +1 -0
- package/dist/utils/state/InMemoryState.cjs +41 -0
- package/dist/utils/state/InMemoryState.cjs.map +1 -0
- package/dist/utils/state/InMemoryState.d.cts +18 -0
- package/dist/utils/state/InMemoryState.d.cts.map +1 -0
- package/dist/utils/state/InMemoryState.d.mts +18 -0
- package/dist/utils/state/InMemoryState.d.mts.map +1 -0
- package/dist/utils/state/InMemoryState.mjs +38 -0
- package/dist/utils/state/InMemoryState.mjs.map +1 -0
- package/dist/utils/state/State.cjs +159 -0
- package/dist/utils/state/State.cjs.map +1 -0
- package/dist/utils/state/State.d.cts +25 -0
- package/dist/utils/state/State.d.cts.map +1 -0
- package/dist/utils/state/State.d.mts +25 -0
- package/dist/utils/state/State.d.mts.map +1 -0
- package/dist/utils/state/State.mjs +156 -0
- package/dist/utils/state/State.mjs.map +1 -0
- package/dist/utils/transactionType/transactionType.cjs +32 -0
- package/dist/utils/transactionType/transactionType.cjs.map +1 -0
- package/dist/utils/transactionType/transactionType.d.cts +17 -0
- package/dist/utils/transactionType/transactionType.d.cts.map +1 -0
- package/dist/utils/transactionType/transactionType.d.mts +17 -0
- package/dist/utils/transactionType/transactionType.d.mts.map +1 -0
- package/dist/utils/transactionType/transactionType.mjs +28 -0
- package/dist/utils/transactionType/transactionType.mjs.map +1 -0
- package/dist/utils/urlStruct/urlStruct.cjs +96 -0
- package/dist/utils/urlStruct/urlStruct.cjs.map +1 -0
- package/dist/utils/urlStruct/urlStruct.d.cts +25 -0
- package/dist/utils/urlStruct/urlStruct.d.cts.map +1 -0
- package/dist/utils/urlStruct/urlStruct.d.mts +25 -0
- package/dist/utils/urlStruct/urlStruct.d.mts.map +1 -0
- package/dist/utils/urlStruct/urlStruct.mjs +93 -0
- package/dist/utils/urlStruct/urlStruct.mjs.map +1 -0
- package/dist/utils/uuidStruct/uuidStruct.cjs +12 -0
- package/dist/utils/uuidStruct/uuidStruct.cjs.map +1 -0
- package/dist/utils/uuidStruct/uuidStruct.d.cts +8 -0
- package/dist/utils/uuidStruct/uuidStruct.d.cts.map +1 -0
- package/dist/utils/uuidStruct/uuidStruct.d.mts +8 -0
- package/dist/utils/uuidStruct/uuidStruct.d.mts.map +1 -0
- package/dist/utils/uuidStruct/uuidStruct.mjs +9 -0
- package/dist/utils/uuidStruct/uuidStruct.mjs.map +1 -0
- package/package.json +33 -13
- package/dist/logger.cjs +0 -57
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.d.cts +0 -30
- package/dist/logger.d.cts.map +0 -1
- package/dist/logger.d.mts +0 -30
- package/dist/logger.d.mts.map +0 -1
- package/dist/logger.mjs +0 -53
- package/dist/logger.mjs.map +0 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StateCache = void 0;
|
|
4
|
+
const utils_1 = require("@metamask/utils");
|
|
5
|
+
/**
|
|
6
|
+
* A cache that wraps any implementation of a state manager to store the cache.
|
|
7
|
+
*
|
|
8
|
+
* It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.
|
|
9
|
+
*
|
|
10
|
+
* By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.
|
|
11
|
+
* This is useful if you want to have multiple independent caches in the same state.
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* {
|
|
15
|
+
* ..., // other state values
|
|
16
|
+
* __cache__default: {
|
|
17
|
+
* key1: value1,
|
|
18
|
+
* key2: value2,
|
|
19
|
+
* },
|
|
20
|
+
* __cache__my-prefix: {
|
|
21
|
+
* key3: value3,
|
|
22
|
+
* key4: value4,
|
|
23
|
+
* },
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const state = new State({}); // Here we use the real snap's state
|
|
30
|
+
* const rootLogger = new Logger({ level: LogLevel.INFO });
|
|
31
|
+
* const cache = new StateCache(state, rootLogger, '__cache__my-prefix');
|
|
32
|
+
*
|
|
33
|
+
* // state looks like this:
|
|
34
|
+
* // {
|
|
35
|
+
* // ..., // other state values
|
|
36
|
+
* // no __cache__my-prefix yet
|
|
37
|
+
* // }
|
|
38
|
+
*
|
|
39
|
+
* await cache.set('key1', 'value1');
|
|
40
|
+
*
|
|
41
|
+
* // state looks like this:
|
|
42
|
+
* // {
|
|
43
|
+
* // ..., // other state values
|
|
44
|
+
* // __cache__my-prefix: {
|
|
45
|
+
* // key1: value1,
|
|
46
|
+
* // },
|
|
47
|
+
* // }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
class StateCache {
|
|
51
|
+
#state;
|
|
52
|
+
#logger;
|
|
53
|
+
prefix;
|
|
54
|
+
constructor(state, logger, prefix = '__cache__default') {
|
|
55
|
+
this.#state = state;
|
|
56
|
+
this.#logger = logger;
|
|
57
|
+
this.prefix = prefix;
|
|
58
|
+
}
|
|
59
|
+
async get(key) {
|
|
60
|
+
const result = await this.mget([key]);
|
|
61
|
+
return result[key];
|
|
62
|
+
}
|
|
63
|
+
async set(key, value, ttlMilliseconds = Number.MAX_SAFE_INTEGER) {
|
|
64
|
+
this.#validateTtlOrThrow(ttlMilliseconds);
|
|
65
|
+
await this.#state.setKey(`${this.prefix}.${key}`, {
|
|
66
|
+
value,
|
|
67
|
+
expiresAt: Math.min(Date.now() + ttlMilliseconds, Number.MAX_SAFE_INTEGER),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
#validateTtlOrThrow(ttlMilliseconds) {
|
|
71
|
+
if (ttlMilliseconds === undefined) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof ttlMilliseconds !== 'number') {
|
|
75
|
+
throw new Error('TTL must be a number');
|
|
76
|
+
}
|
|
77
|
+
if (ttlMilliseconds < 0) {
|
|
78
|
+
throw new Error('TTL must be positive');
|
|
79
|
+
}
|
|
80
|
+
if (ttlMilliseconds > Number.MAX_SAFE_INTEGER) {
|
|
81
|
+
throw new Error('TTL must be less than 2^53 - 1');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async delete(key) {
|
|
85
|
+
const result = await this.mdelete([key]);
|
|
86
|
+
return result[key] ?? false;
|
|
87
|
+
}
|
|
88
|
+
async clear() {
|
|
89
|
+
await this.#state.setKey(this.prefix, {});
|
|
90
|
+
}
|
|
91
|
+
async has(key) {
|
|
92
|
+
const result = await this.get(key);
|
|
93
|
+
return result !== undefined;
|
|
94
|
+
}
|
|
95
|
+
async keys() {
|
|
96
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
97
|
+
return Object.keys(cacheStore ?? {});
|
|
98
|
+
}
|
|
99
|
+
async size() {
|
|
100
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
101
|
+
return Object.keys(cacheStore ?? {}).length;
|
|
102
|
+
}
|
|
103
|
+
async peek(key) {
|
|
104
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
105
|
+
const cacheEntry = cacheStore?.[key];
|
|
106
|
+
return cacheEntry?.value;
|
|
107
|
+
}
|
|
108
|
+
async mget(keys) {
|
|
109
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
110
|
+
// If cache is not initialized, return empty object
|
|
111
|
+
if (!cacheStore) {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
const keysAndValues = Object.entries(cacheStore).filter(([key]) => keys.includes(key));
|
|
115
|
+
const expiredKeys = keysAndValues.filter(([_unused, cacheEntry]) => cacheEntry && cacheEntry.expiresAt < Date.now());
|
|
116
|
+
await this.mdelete(expiredKeys.map(([key]) => key));
|
|
117
|
+
const result = {};
|
|
118
|
+
// First, handle keys that exist in the cache
|
|
119
|
+
keysAndValues.forEach(([key, cacheEntry]) => {
|
|
120
|
+
if (cacheEntry === undefined) {
|
|
121
|
+
this.#logger.info(`[StateCache] ❌ Cache miss for key "${key}"`);
|
|
122
|
+
result[key] = undefined;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (cacheEntry.expiresAt < Date.now()) {
|
|
126
|
+
this.#logger.info(`[StateCache] ⌛ Cache expired for key "${key}"`);
|
|
127
|
+
result[key] = undefined;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this.#logger.info(`[StateCache] 🎉 Cache hit for key "${key}"`);
|
|
131
|
+
result[key] = cacheEntry.value;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// Then, handle keys that don't exist in the cache
|
|
135
|
+
keys.forEach((key) => {
|
|
136
|
+
if (!Object.hasOwn(result, key)) {
|
|
137
|
+
this.#logger.info(`[StateCache] ❌ Cache miss for key "${key}"`);
|
|
138
|
+
result[key] = undefined;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
async mset(entries) {
|
|
144
|
+
if (entries.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (entries.length === 1) {
|
|
148
|
+
(0, utils_1.assert)(entries[0]); // Enforce type narrowing as TS cannot infer that entries[0] is defined
|
|
149
|
+
const { key, value, ttlMilliseconds } = entries[0];
|
|
150
|
+
await this.set(key, value, ttlMilliseconds);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
entries.forEach(({ ttlMilliseconds }) => {
|
|
154
|
+
this.#validateTtlOrThrow(ttlMilliseconds);
|
|
155
|
+
});
|
|
156
|
+
// Using `state.update` is preferred for bulk `set`s, because it's more efficient and atomic.
|
|
157
|
+
await this.#state.update((stateValue) => {
|
|
158
|
+
const cacheStore = stateValue[this.prefix] ?? {};
|
|
159
|
+
entries.forEach(({ key, value, ttlMilliseconds }) => {
|
|
160
|
+
if (value === undefined) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
cacheStore[key] = {
|
|
164
|
+
value,
|
|
165
|
+
expiresAt: Math.min(Date.now() + (ttlMilliseconds ?? Number.MAX_SAFE_INTEGER), Number.MAX_SAFE_INTEGER),
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
stateValue[this.prefix] = cacheStore;
|
|
169
|
+
return stateValue;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async mdelete(keys) {
|
|
173
|
+
const result = {};
|
|
174
|
+
// Using `state.update` is preferred for bulk `delete`s, because it's more efficient and atomic.
|
|
175
|
+
await this.#state.update((stateValue) => {
|
|
176
|
+
const cacheStore = stateValue[this.prefix] ?? {};
|
|
177
|
+
keys.forEach((key) => {
|
|
178
|
+
if (cacheStore[key] === undefined) {
|
|
179
|
+
result[key] = false;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
delete cacheStore[key];
|
|
183
|
+
result[key] = true;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
stateValue[this.prefix] = cacheStore;
|
|
187
|
+
return stateValue;
|
|
188
|
+
});
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.StateCache = StateCache;
|
|
193
|
+
//# sourceMappingURL=StateCache.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateCache.cjs","sourceRoot":"","sources":["../../../src/utils/cache/StateCache.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from '@metamask/utils';\n\nimport type { Logger } from '../logger/Logger';\nimport type { Serializable } from '../serialization/types';\nimport type { CacheEntry, ICache } from './types';\n\n/**\n * The minimal subset of a state manager that {@link StateCache} relies on.\n *\n * Any implementation whose `getKey`, `setKey` and `update` methods match these signatures\n * (such as the `IStateManager` implementations in the network snaps) satisfies this interface structurally.\n */\nexport type CacheStateManager<\n TStateValue extends Record<string, Serializable>,\n> = {\n getKey<TKey extends Serializable>(key: string): Promise<TKey | undefined>;\n setKey(key: string, value: Serializable): Promise<void>;\n update(\n updaterFunction: (state: TStateValue) => TStateValue,\n ): Promise<TStateValue>;\n};\n\n/**\n * The whole cache store.\n */\nexport type CacheStore = Record<string, CacheEntry> | undefined;\n\n/**\n * A prefix for the cache \"location\" in the state. Enforced to start with `__cache__` to avoid collisions with other state values.\n */\nexport type CachePrefix = `__cache__${string}`;\n\n/**\n * Describes the shape of the whole state inside which the cache is stored.\n */\nexport type StateValue = {\n [x: string]: Serializable;\n} & {\n [K in CachePrefix]?: CacheStore;\n};\n\n/**\n * A cache that wraps any implementation of a state manager to store the cache.\n *\n * It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.\n *\n * By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.\n * This is useful if you want to have multiple independent caches in the same state.\n *\n * ```\n * {\n * ..., // other state values\n * __cache__default: {\n * key1: value1,\n * key2: value2,\n * },\n * __cache__my-prefix: {\n * key3: value3,\n * key4: value4,\n * },\n * }\n * ```\n *\n * @example\n * ```ts\n * const state = new State({}); // Here we use the real snap's state\n * const rootLogger = new Logger({ level: LogLevel.INFO });\n * const cache = new StateCache(state, rootLogger, '__cache__my-prefix');\n *\n * // state looks like this:\n * // {\n * // ..., // other state values\n * // no __cache__my-prefix yet\n * // }\n *\n * await cache.set('key1', 'value1');\n *\n * // state looks like this:\n * // {\n * // ..., // other state values\n * // __cache__my-prefix: {\n * // key1: value1,\n * // },\n * // }\n * ```\n */\nexport class StateCache implements ICache<Serializable | undefined> {\n readonly #state: CacheStateManager<StateValue>;\n\n readonly #logger: Logger;\n\n public readonly prefix: CachePrefix;\n\n constructor(\n state: CacheStateManager<StateValue>,\n logger: Logger,\n prefix: CachePrefix = '__cache__default',\n ) {\n this.#state = state;\n this.#logger = logger;\n this.prefix = prefix;\n }\n\n async get(key: string): Promise<Serializable | undefined> {\n const result = await this.mget([key]);\n return result[key];\n }\n\n async set(\n key: string,\n value: Serializable,\n ttlMilliseconds = Number.MAX_SAFE_INTEGER,\n ): Promise<void> {\n this.#validateTtlOrThrow(ttlMilliseconds);\n\n await this.#state.setKey(`${this.prefix}.${key}`, {\n value,\n expiresAt: Math.min(\n Date.now() + ttlMilliseconds,\n Number.MAX_SAFE_INTEGER,\n ),\n });\n }\n\n #validateTtlOrThrow(ttlMilliseconds?: number): void {\n if (ttlMilliseconds === undefined) {\n return;\n }\n\n if (typeof ttlMilliseconds !== 'number') {\n throw new Error('TTL must be a number');\n }\n\n if (ttlMilliseconds < 0) {\n throw new Error('TTL must be positive');\n }\n\n if (ttlMilliseconds > Number.MAX_SAFE_INTEGER) {\n throw new Error('TTL must be less than 2^53 - 1');\n }\n }\n\n async delete(key: string): Promise<boolean> {\n const result = await this.mdelete([key]);\n return result[key] ?? false;\n }\n\n async clear(): Promise<void> {\n await this.#state.setKey(this.prefix, {});\n }\n\n async has(key: string): Promise<boolean> {\n const result = await this.get(key);\n return result !== undefined;\n }\n\n async keys(): Promise<string[]> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n return Object.keys(cacheStore ?? {});\n }\n\n async size(): Promise<number> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n return Object.keys(cacheStore ?? {}).length;\n }\n\n async peek(key: string): Promise<Serializable | undefined> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n const cacheEntry = cacheStore?.[key];\n\n return cacheEntry?.value;\n }\n\n async mget(\n keys: string[],\n ): Promise<Record<string, Serializable | undefined>> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n // If cache is not initialized, return empty object\n if (!cacheStore) {\n return {};\n }\n\n const keysAndValues = Object.entries(cacheStore).filter(([key]) =>\n keys.includes(key),\n );\n\n const expiredKeys = keysAndValues.filter(\n ([_unused, cacheEntry]) =>\n cacheEntry && cacheEntry.expiresAt < Date.now(),\n );\n\n await this.mdelete(expiredKeys.map(([key]) => key));\n\n const result: Record<string, Serializable | undefined> = {};\n\n // First, handle keys that exist in the cache\n keysAndValues.forEach(([key, cacheEntry]) => {\n if (cacheEntry === undefined) {\n this.#logger.info(`[StateCache] ❌ Cache miss for key \"${key}\"`);\n result[key] = undefined;\n return;\n }\n\n if (cacheEntry.expiresAt < Date.now()) {\n this.#logger.info(`[StateCache] ⌛ Cache expired for key \"${key}\"`);\n result[key] = undefined;\n } else {\n this.#logger.info(`[StateCache] 🎉 Cache hit for key \"${key}\"`);\n result[key] = cacheEntry.value;\n }\n });\n\n // Then, handle keys that don't exist in the cache\n keys.forEach((key) => {\n if (!Object.hasOwn(result, key)) {\n this.#logger.info(`[StateCache] ❌ Cache miss for key \"${key}\"`);\n result[key] = undefined;\n }\n });\n\n return result;\n }\n\n async mset(\n entries: { key: string; value: Serializable; ttlMilliseconds?: number }[],\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n if (entries.length === 1) {\n assert(entries[0]); // Enforce type narrowing as TS cannot infer that entries[0] is defined\n const { key, value, ttlMilliseconds } = entries[0];\n await this.set(key, value, ttlMilliseconds);\n return;\n }\n\n entries.forEach(({ ttlMilliseconds }) => {\n this.#validateTtlOrThrow(ttlMilliseconds);\n });\n\n // Using `state.update` is preferred for bulk `set`s, because it's more efficient and atomic.\n await this.#state.update((stateValue) => {\n const cacheStore = stateValue[this.prefix] ?? {};\n entries.forEach(({ key, value, ttlMilliseconds }) => {\n if (value === undefined) {\n return;\n }\n cacheStore[key] = {\n value,\n expiresAt: Math.min(\n Date.now() + (ttlMilliseconds ?? Number.MAX_SAFE_INTEGER),\n Number.MAX_SAFE_INTEGER,\n ),\n };\n });\n stateValue[this.prefix] = cacheStore;\n return stateValue;\n });\n }\n\n async mdelete(keys: string[]): Promise<Record<string, boolean>> {\n const result: Record<string, boolean> = {};\n\n // Using `state.update` is preferred for bulk `delete`s, because it's more efficient and atomic.\n await this.#state.update((stateValue) => {\n const cacheStore = stateValue[this.prefix] ?? {};\n keys.forEach((key) => {\n if (cacheStore[key] === undefined) {\n result[key] = false;\n } else {\n delete cacheStore[key];\n result[key] = true;\n }\n });\n stateValue[this.prefix] = cacheStore;\n return stateValue;\n });\n\n return result;\n }\n}\n"]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Logger } from "../logger/Logger.cjs";
|
|
2
|
+
import type { Serializable } from "../serialization/types.cjs";
|
|
3
|
+
import type { CacheEntry, ICache } from "./types.cjs";
|
|
4
|
+
/**
|
|
5
|
+
* The minimal subset of a state manager that {@link StateCache} relies on.
|
|
6
|
+
*
|
|
7
|
+
* Any implementation whose `getKey`, `setKey` and `update` methods match these signatures
|
|
8
|
+
* (such as the `IStateManager` implementations in the network snaps) satisfies this interface structurally.
|
|
9
|
+
*/
|
|
10
|
+
export type CacheStateManager<TStateValue extends Record<string, Serializable>> = {
|
|
11
|
+
getKey<TKey extends Serializable>(key: string): Promise<TKey | undefined>;
|
|
12
|
+
setKey(key: string, value: Serializable): Promise<void>;
|
|
13
|
+
update(updaterFunction: (state: TStateValue) => TStateValue): Promise<TStateValue>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The whole cache store.
|
|
17
|
+
*/
|
|
18
|
+
export type CacheStore = Record<string, CacheEntry> | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* A prefix for the cache "location" in the state. Enforced to start with `__cache__` to avoid collisions with other state values.
|
|
21
|
+
*/
|
|
22
|
+
export type CachePrefix = `__cache__${string}`;
|
|
23
|
+
/**
|
|
24
|
+
* Describes the shape of the whole state inside which the cache is stored.
|
|
25
|
+
*/
|
|
26
|
+
export type StateValue = {
|
|
27
|
+
[x: string]: Serializable;
|
|
28
|
+
} & {
|
|
29
|
+
[K in CachePrefix]?: CacheStore;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A cache that wraps any implementation of a state manager to store the cache.
|
|
33
|
+
*
|
|
34
|
+
* It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.
|
|
35
|
+
*
|
|
36
|
+
* By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.
|
|
37
|
+
* This is useful if you want to have multiple independent caches in the same state.
|
|
38
|
+
*
|
|
39
|
+
* ```
|
|
40
|
+
* {
|
|
41
|
+
* ..., // other state values
|
|
42
|
+
* __cache__default: {
|
|
43
|
+
* key1: value1,
|
|
44
|
+
* key2: value2,
|
|
45
|
+
* },
|
|
46
|
+
* __cache__my-prefix: {
|
|
47
|
+
* key3: value3,
|
|
48
|
+
* key4: value4,
|
|
49
|
+
* },
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const state = new State({}); // Here we use the real snap's state
|
|
56
|
+
* const rootLogger = new Logger({ level: LogLevel.INFO });
|
|
57
|
+
* const cache = new StateCache(state, rootLogger, '__cache__my-prefix');
|
|
58
|
+
*
|
|
59
|
+
* // state looks like this:
|
|
60
|
+
* // {
|
|
61
|
+
* // ..., // other state values
|
|
62
|
+
* // no __cache__my-prefix yet
|
|
63
|
+
* // }
|
|
64
|
+
*
|
|
65
|
+
* await cache.set('key1', 'value1');
|
|
66
|
+
*
|
|
67
|
+
* // state looks like this:
|
|
68
|
+
* // {
|
|
69
|
+
* // ..., // other state values
|
|
70
|
+
* // __cache__my-prefix: {
|
|
71
|
+
* // key1: value1,
|
|
72
|
+
* // },
|
|
73
|
+
* // }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare class StateCache implements ICache<Serializable | undefined> {
|
|
77
|
+
#private;
|
|
78
|
+
readonly prefix: CachePrefix;
|
|
79
|
+
constructor(state: CacheStateManager<StateValue>, logger: Logger, prefix?: CachePrefix);
|
|
80
|
+
get(key: string): Promise<Serializable | undefined>;
|
|
81
|
+
set(key: string, value: Serializable, ttlMilliseconds?: number): Promise<void>;
|
|
82
|
+
delete(key: string): Promise<boolean>;
|
|
83
|
+
clear(): Promise<void>;
|
|
84
|
+
has(key: string): Promise<boolean>;
|
|
85
|
+
keys(): Promise<string[]>;
|
|
86
|
+
size(): Promise<number>;
|
|
87
|
+
peek(key: string): Promise<Serializable | undefined>;
|
|
88
|
+
mget(keys: string[]): Promise<Record<string, Serializable | undefined>>;
|
|
89
|
+
mset(entries: {
|
|
90
|
+
key: string;
|
|
91
|
+
value: Serializable;
|
|
92
|
+
ttlMilliseconds?: number;
|
|
93
|
+
}[]): Promise<void>;
|
|
94
|
+
mdelete(keys: string[]): Promise<Record<string, boolean>>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=StateCache.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateCache.d.cts","sourceRoot":"","sources":["../../../src/utils/cache/StateCache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,6BAAyB;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,mCAA+B;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAgB;AAElD;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,CAC3B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,IAC9C;IACF,MAAM,CAAC,IAAI,SAAS,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;IAC1E,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CACJ,eAAe,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,GACnD,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,MAAM,EAAE,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CAC3B,GAAG;KACD,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE,UAAU;CAChC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,UAAW,YAAW,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;;IAKjE,SAAgB,MAAM,EAAE,WAAW,CAAC;gBAGlC,KAAK,EAAE,iBAAiB,CAAC,UAAU,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,WAAgC;IAOpC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAKnD,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,eAAe,SAA0B,GACxC,OAAO,CAAC,IAAI,CAAC;IA8BV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAMvB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAOpD,IAAI,CACR,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC,CAAC;IAiD9C,IAAI,CACR,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GACxE,OAAO,CAAC,IAAI,CAAC;IAoCV,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAoBhE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Logger } from "../logger/Logger.mjs";
|
|
2
|
+
import type { Serializable } from "../serialization/types.mjs";
|
|
3
|
+
import type { CacheEntry, ICache } from "./types.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* The minimal subset of a state manager that {@link StateCache} relies on.
|
|
6
|
+
*
|
|
7
|
+
* Any implementation whose `getKey`, `setKey` and `update` methods match these signatures
|
|
8
|
+
* (such as the `IStateManager` implementations in the network snaps) satisfies this interface structurally.
|
|
9
|
+
*/
|
|
10
|
+
export type CacheStateManager<TStateValue extends Record<string, Serializable>> = {
|
|
11
|
+
getKey<TKey extends Serializable>(key: string): Promise<TKey | undefined>;
|
|
12
|
+
setKey(key: string, value: Serializable): Promise<void>;
|
|
13
|
+
update(updaterFunction: (state: TStateValue) => TStateValue): Promise<TStateValue>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The whole cache store.
|
|
17
|
+
*/
|
|
18
|
+
export type CacheStore = Record<string, CacheEntry> | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* A prefix for the cache "location" in the state. Enforced to start with `__cache__` to avoid collisions with other state values.
|
|
21
|
+
*/
|
|
22
|
+
export type CachePrefix = `__cache__${string}`;
|
|
23
|
+
/**
|
|
24
|
+
* Describes the shape of the whole state inside which the cache is stored.
|
|
25
|
+
*/
|
|
26
|
+
export type StateValue = {
|
|
27
|
+
[x: string]: Serializable;
|
|
28
|
+
} & {
|
|
29
|
+
[K in CachePrefix]?: CacheStore;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A cache that wraps any implementation of a state manager to store the cache.
|
|
33
|
+
*
|
|
34
|
+
* It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.
|
|
35
|
+
*
|
|
36
|
+
* By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.
|
|
37
|
+
* This is useful if you want to have multiple independent caches in the same state.
|
|
38
|
+
*
|
|
39
|
+
* ```
|
|
40
|
+
* {
|
|
41
|
+
* ..., // other state values
|
|
42
|
+
* __cache__default: {
|
|
43
|
+
* key1: value1,
|
|
44
|
+
* key2: value2,
|
|
45
|
+
* },
|
|
46
|
+
* __cache__my-prefix: {
|
|
47
|
+
* key3: value3,
|
|
48
|
+
* key4: value4,
|
|
49
|
+
* },
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const state = new State({}); // Here we use the real snap's state
|
|
56
|
+
* const rootLogger = new Logger({ level: LogLevel.INFO });
|
|
57
|
+
* const cache = new StateCache(state, rootLogger, '__cache__my-prefix');
|
|
58
|
+
*
|
|
59
|
+
* // state looks like this:
|
|
60
|
+
* // {
|
|
61
|
+
* // ..., // other state values
|
|
62
|
+
* // no __cache__my-prefix yet
|
|
63
|
+
* // }
|
|
64
|
+
*
|
|
65
|
+
* await cache.set('key1', 'value1');
|
|
66
|
+
*
|
|
67
|
+
* // state looks like this:
|
|
68
|
+
* // {
|
|
69
|
+
* // ..., // other state values
|
|
70
|
+
* // __cache__my-prefix: {
|
|
71
|
+
* // key1: value1,
|
|
72
|
+
* // },
|
|
73
|
+
* // }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare class StateCache implements ICache<Serializable | undefined> {
|
|
77
|
+
#private;
|
|
78
|
+
readonly prefix: CachePrefix;
|
|
79
|
+
constructor(state: CacheStateManager<StateValue>, logger: Logger, prefix?: CachePrefix);
|
|
80
|
+
get(key: string): Promise<Serializable | undefined>;
|
|
81
|
+
set(key: string, value: Serializable, ttlMilliseconds?: number): Promise<void>;
|
|
82
|
+
delete(key: string): Promise<boolean>;
|
|
83
|
+
clear(): Promise<void>;
|
|
84
|
+
has(key: string): Promise<boolean>;
|
|
85
|
+
keys(): Promise<string[]>;
|
|
86
|
+
size(): Promise<number>;
|
|
87
|
+
peek(key: string): Promise<Serializable | undefined>;
|
|
88
|
+
mget(keys: string[]): Promise<Record<string, Serializable | undefined>>;
|
|
89
|
+
mset(entries: {
|
|
90
|
+
key: string;
|
|
91
|
+
value: Serializable;
|
|
92
|
+
ttlMilliseconds?: number;
|
|
93
|
+
}[]): Promise<void>;
|
|
94
|
+
mdelete(keys: string[]): Promise<Record<string, boolean>>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=StateCache.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateCache.d.mts","sourceRoot":"","sources":["../../../src/utils/cache/StateCache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,6BAAyB;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,mCAA+B;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAgB;AAElD;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,CAC3B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,IAC9C;IACF,MAAM,CAAC,IAAI,SAAS,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;IAC1E,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CACJ,eAAe,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,GACnD,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,MAAM,EAAE,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CAC3B,GAAG;KACD,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE,UAAU;CAChC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,UAAW,YAAW,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;;IAKjE,SAAgB,MAAM,EAAE,WAAW,CAAC;gBAGlC,KAAK,EAAE,iBAAiB,CAAC,UAAU,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,WAAgC;IAOpC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAKnD,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,eAAe,SAA0B,GACxC,OAAO,CAAC,IAAI,CAAC;IA8BV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAMvB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAOpD,IAAI,CACR,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC,CAAC;IAiD9C,IAAI,CACR,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GACxE,OAAO,CAAC,IAAI,CAAC;IAoCV,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAoBhE"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { assert } from "@metamask/utils";
|
|
2
|
+
/**
|
|
3
|
+
* A cache that wraps any implementation of a state manager to store the cache.
|
|
4
|
+
*
|
|
5
|
+
* It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.
|
|
6
|
+
*
|
|
7
|
+
* By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.
|
|
8
|
+
* This is useful if you want to have multiple independent caches in the same state.
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* {
|
|
12
|
+
* ..., // other state values
|
|
13
|
+
* __cache__default: {
|
|
14
|
+
* key1: value1,
|
|
15
|
+
* key2: value2,
|
|
16
|
+
* },
|
|
17
|
+
* __cache__my-prefix: {
|
|
18
|
+
* key3: value3,
|
|
19
|
+
* key4: value4,
|
|
20
|
+
* },
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const state = new State({}); // Here we use the real snap's state
|
|
27
|
+
* const rootLogger = new Logger({ level: LogLevel.INFO });
|
|
28
|
+
* const cache = new StateCache(state, rootLogger, '__cache__my-prefix');
|
|
29
|
+
*
|
|
30
|
+
* // state looks like this:
|
|
31
|
+
* // {
|
|
32
|
+
* // ..., // other state values
|
|
33
|
+
* // no __cache__my-prefix yet
|
|
34
|
+
* // }
|
|
35
|
+
*
|
|
36
|
+
* await cache.set('key1', 'value1');
|
|
37
|
+
*
|
|
38
|
+
* // state looks like this:
|
|
39
|
+
* // {
|
|
40
|
+
* // ..., // other state values
|
|
41
|
+
* // __cache__my-prefix: {
|
|
42
|
+
* // key1: value1,
|
|
43
|
+
* // },
|
|
44
|
+
* // }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export class StateCache {
|
|
48
|
+
#state;
|
|
49
|
+
#logger;
|
|
50
|
+
prefix;
|
|
51
|
+
constructor(state, logger, prefix = '__cache__default') {
|
|
52
|
+
this.#state = state;
|
|
53
|
+
this.#logger = logger;
|
|
54
|
+
this.prefix = prefix;
|
|
55
|
+
}
|
|
56
|
+
async get(key) {
|
|
57
|
+
const result = await this.mget([key]);
|
|
58
|
+
return result[key];
|
|
59
|
+
}
|
|
60
|
+
async set(key, value, ttlMilliseconds = Number.MAX_SAFE_INTEGER) {
|
|
61
|
+
this.#validateTtlOrThrow(ttlMilliseconds);
|
|
62
|
+
await this.#state.setKey(`${this.prefix}.${key}`, {
|
|
63
|
+
value,
|
|
64
|
+
expiresAt: Math.min(Date.now() + ttlMilliseconds, Number.MAX_SAFE_INTEGER),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
#validateTtlOrThrow(ttlMilliseconds) {
|
|
68
|
+
if (ttlMilliseconds === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof ttlMilliseconds !== 'number') {
|
|
72
|
+
throw new Error('TTL must be a number');
|
|
73
|
+
}
|
|
74
|
+
if (ttlMilliseconds < 0) {
|
|
75
|
+
throw new Error('TTL must be positive');
|
|
76
|
+
}
|
|
77
|
+
if (ttlMilliseconds > Number.MAX_SAFE_INTEGER) {
|
|
78
|
+
throw new Error('TTL must be less than 2^53 - 1');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async delete(key) {
|
|
82
|
+
const result = await this.mdelete([key]);
|
|
83
|
+
return result[key] ?? false;
|
|
84
|
+
}
|
|
85
|
+
async clear() {
|
|
86
|
+
await this.#state.setKey(this.prefix, {});
|
|
87
|
+
}
|
|
88
|
+
async has(key) {
|
|
89
|
+
const result = await this.get(key);
|
|
90
|
+
return result !== undefined;
|
|
91
|
+
}
|
|
92
|
+
async keys() {
|
|
93
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
94
|
+
return Object.keys(cacheStore ?? {});
|
|
95
|
+
}
|
|
96
|
+
async size() {
|
|
97
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
98
|
+
return Object.keys(cacheStore ?? {}).length;
|
|
99
|
+
}
|
|
100
|
+
async peek(key) {
|
|
101
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
102
|
+
const cacheEntry = cacheStore?.[key];
|
|
103
|
+
return cacheEntry?.value;
|
|
104
|
+
}
|
|
105
|
+
async mget(keys) {
|
|
106
|
+
const cacheStore = await this.#state.getKey(this.prefix);
|
|
107
|
+
// If cache is not initialized, return empty object
|
|
108
|
+
if (!cacheStore) {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
const keysAndValues = Object.entries(cacheStore).filter(([key]) => keys.includes(key));
|
|
112
|
+
const expiredKeys = keysAndValues.filter(([_unused, cacheEntry]) => cacheEntry && cacheEntry.expiresAt < Date.now());
|
|
113
|
+
await this.mdelete(expiredKeys.map(([key]) => key));
|
|
114
|
+
const result = {};
|
|
115
|
+
// First, handle keys that exist in the cache
|
|
116
|
+
keysAndValues.forEach(([key, cacheEntry]) => {
|
|
117
|
+
if (cacheEntry === undefined) {
|
|
118
|
+
this.#logger.info(`[StateCache] ❌ Cache miss for key "${key}"`);
|
|
119
|
+
result[key] = undefined;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (cacheEntry.expiresAt < Date.now()) {
|
|
123
|
+
this.#logger.info(`[StateCache] ⌛ Cache expired for key "${key}"`);
|
|
124
|
+
result[key] = undefined;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.#logger.info(`[StateCache] 🎉 Cache hit for key "${key}"`);
|
|
128
|
+
result[key] = cacheEntry.value;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
// Then, handle keys that don't exist in the cache
|
|
132
|
+
keys.forEach((key) => {
|
|
133
|
+
if (!Object.hasOwn(result, key)) {
|
|
134
|
+
this.#logger.info(`[StateCache] ❌ Cache miss for key "${key}"`);
|
|
135
|
+
result[key] = undefined;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
async mset(entries) {
|
|
141
|
+
if (entries.length === 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (entries.length === 1) {
|
|
145
|
+
assert(entries[0]); // Enforce type narrowing as TS cannot infer that entries[0] is defined
|
|
146
|
+
const { key, value, ttlMilliseconds } = entries[0];
|
|
147
|
+
await this.set(key, value, ttlMilliseconds);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
entries.forEach(({ ttlMilliseconds }) => {
|
|
151
|
+
this.#validateTtlOrThrow(ttlMilliseconds);
|
|
152
|
+
});
|
|
153
|
+
// Using `state.update` is preferred for bulk `set`s, because it's more efficient and atomic.
|
|
154
|
+
await this.#state.update((stateValue) => {
|
|
155
|
+
const cacheStore = stateValue[this.prefix] ?? {};
|
|
156
|
+
entries.forEach(({ key, value, ttlMilliseconds }) => {
|
|
157
|
+
if (value === undefined) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
cacheStore[key] = {
|
|
161
|
+
value,
|
|
162
|
+
expiresAt: Math.min(Date.now() + (ttlMilliseconds ?? Number.MAX_SAFE_INTEGER), Number.MAX_SAFE_INTEGER),
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
stateValue[this.prefix] = cacheStore;
|
|
166
|
+
return stateValue;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async mdelete(keys) {
|
|
170
|
+
const result = {};
|
|
171
|
+
// Using `state.update` is preferred for bulk `delete`s, because it's more efficient and atomic.
|
|
172
|
+
await this.#state.update((stateValue) => {
|
|
173
|
+
const cacheStore = stateValue[this.prefix] ?? {};
|
|
174
|
+
keys.forEach((key) => {
|
|
175
|
+
if (cacheStore[key] === undefined) {
|
|
176
|
+
result[key] = false;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
delete cacheStore[key];
|
|
180
|
+
result[key] = true;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
stateValue[this.prefix] = cacheStore;
|
|
184
|
+
return stateValue;
|
|
185
|
+
});
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=StateCache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateCache.mjs","sourceRoot":"","sources":["../../../src/utils/cache/StateCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,wBAAwB;AAyCzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,OAAO,UAAU;IACZ,MAAM,CAAgC;IAEtC,OAAO,CAAS;IAET,MAAM,CAAc;IAEpC,YACE,KAAoC,EACpC,MAAc,EACd,SAAsB,kBAAkB;QAExC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,GAAG,CACP,GAAW,EACX,KAAmB,EACnB,eAAe,GAAG,MAAM,CAAC,gBAAgB;QAEzC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC;QAE1C,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,EAAE;YAChD,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,CACjB,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,EAC5B,MAAM,CAAC,gBAAgB,CACxB;SACF,CAAC,CAAC;IACL,CAAC;IAED,mBAAmB,CAAC,eAAwB;QAC1C,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QAED,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QAED,IAAI,eAAe,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,MAAM,KAAK,SAAS,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;QAErE,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;QAErE,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;QAErC,OAAO,UAAU,EAAE,KAAK,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,IAAc;QAEd,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;QAErE,mDAAmD;QACnD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAChE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CACnB,CAAC;QAEF,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CACtC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,CACxB,UAAU,IAAI,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAClD,CAAC;QAEF,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAEpD,MAAM,MAAM,GAA6C,EAAE,CAAC;QAE5D,6CAA6C;QAC7C,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE;YAC1C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAC;gBAChE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;gBACxB,OAAO;YACT,CAAC;YAED,IAAI,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,yCAAyC,GAAG,GAAG,CAAC,CAAC;gBACnE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAC;gBAChE,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;YACjC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,kDAAkD;QAClD,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACnB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAC;gBAChE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,OAAyE;QAEzE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,uEAAuE;YAC3F,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACnD,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE;YACtC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,6FAA6F;QAC7F,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACtC,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjD,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE;gBAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,OAAO;gBACT,CAAC;gBACD,UAAU,CAAC,GAAG,CAAC,GAAG;oBAChB,KAAK;oBACL,SAAS,EAAE,IAAI,CAAC,GAAG,CACjB,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,eAAe,IAAI,MAAM,CAAC,gBAAgB,CAAC,EACzD,MAAM,CAAC,gBAAgB,CACxB;iBACF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;YACrC,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAc;QAC1B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,gGAAgG;QAChG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACtC,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBACnB,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;oBAClC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACtB,CAAC;qBAAM,CAAC;oBACN,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;oBACvB,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACrB,CAAC;YACH,CAAC,CAAC,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;YACrC,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import { assert } from '@metamask/utils';\n\nimport type { Logger } from '../logger/Logger';\nimport type { Serializable } from '../serialization/types';\nimport type { CacheEntry, ICache } from './types';\n\n/**\n * The minimal subset of a state manager that {@link StateCache} relies on.\n *\n * Any implementation whose `getKey`, `setKey` and `update` methods match these signatures\n * (such as the `IStateManager` implementations in the network snaps) satisfies this interface structurally.\n */\nexport type CacheStateManager<\n TStateValue extends Record<string, Serializable>,\n> = {\n getKey<TKey extends Serializable>(key: string): Promise<TKey | undefined>;\n setKey(key: string, value: Serializable): Promise<void>;\n update(\n updaterFunction: (state: TStateValue) => TStateValue,\n ): Promise<TStateValue>;\n};\n\n/**\n * The whole cache store.\n */\nexport type CacheStore = Record<string, CacheEntry> | undefined;\n\n/**\n * A prefix for the cache \"location\" in the state. Enforced to start with `__cache__` to avoid collisions with other state values.\n */\nexport type CachePrefix = `__cache__${string}`;\n\n/**\n * Describes the shape of the whole state inside which the cache is stored.\n */\nexport type StateValue = {\n [x: string]: Serializable;\n} & {\n [K in CachePrefix]?: CacheStore;\n};\n\n/**\n * A cache that wraps any implementation of a state manager to store the cache.\n *\n * It is intended to be used with the snap's `State` class, but can be used with any other implementation of the state manager interface. For instance it can be used with an in-memory state implementation for testing purposes.\n *\n * By default, it stores its data in the `__cache__default` property of the state, but you can specify any other prefix you want, provided it starts with `__cache__` to avoid collisions with other state values.\n * This is useful if you want to have multiple independent caches in the same state.\n *\n * ```\n * {\n * ..., // other state values\n * __cache__default: {\n * key1: value1,\n * key2: value2,\n * },\n * __cache__my-prefix: {\n * key3: value3,\n * key4: value4,\n * },\n * }\n * ```\n *\n * @example\n * ```ts\n * const state = new State({}); // Here we use the real snap's state\n * const rootLogger = new Logger({ level: LogLevel.INFO });\n * const cache = new StateCache(state, rootLogger, '__cache__my-prefix');\n *\n * // state looks like this:\n * // {\n * // ..., // other state values\n * // no __cache__my-prefix yet\n * // }\n *\n * await cache.set('key1', 'value1');\n *\n * // state looks like this:\n * // {\n * // ..., // other state values\n * // __cache__my-prefix: {\n * // key1: value1,\n * // },\n * // }\n * ```\n */\nexport class StateCache implements ICache<Serializable | undefined> {\n readonly #state: CacheStateManager<StateValue>;\n\n readonly #logger: Logger;\n\n public readonly prefix: CachePrefix;\n\n constructor(\n state: CacheStateManager<StateValue>,\n logger: Logger,\n prefix: CachePrefix = '__cache__default',\n ) {\n this.#state = state;\n this.#logger = logger;\n this.prefix = prefix;\n }\n\n async get(key: string): Promise<Serializable | undefined> {\n const result = await this.mget([key]);\n return result[key];\n }\n\n async set(\n key: string,\n value: Serializable,\n ttlMilliseconds = Number.MAX_SAFE_INTEGER,\n ): Promise<void> {\n this.#validateTtlOrThrow(ttlMilliseconds);\n\n await this.#state.setKey(`${this.prefix}.${key}`, {\n value,\n expiresAt: Math.min(\n Date.now() + ttlMilliseconds,\n Number.MAX_SAFE_INTEGER,\n ),\n });\n }\n\n #validateTtlOrThrow(ttlMilliseconds?: number): void {\n if (ttlMilliseconds === undefined) {\n return;\n }\n\n if (typeof ttlMilliseconds !== 'number') {\n throw new Error('TTL must be a number');\n }\n\n if (ttlMilliseconds < 0) {\n throw new Error('TTL must be positive');\n }\n\n if (ttlMilliseconds > Number.MAX_SAFE_INTEGER) {\n throw new Error('TTL must be less than 2^53 - 1');\n }\n }\n\n async delete(key: string): Promise<boolean> {\n const result = await this.mdelete([key]);\n return result[key] ?? false;\n }\n\n async clear(): Promise<void> {\n await this.#state.setKey(this.prefix, {});\n }\n\n async has(key: string): Promise<boolean> {\n const result = await this.get(key);\n return result !== undefined;\n }\n\n async keys(): Promise<string[]> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n return Object.keys(cacheStore ?? {});\n }\n\n async size(): Promise<number> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n return Object.keys(cacheStore ?? {}).length;\n }\n\n async peek(key: string): Promise<Serializable | undefined> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n const cacheEntry = cacheStore?.[key];\n\n return cacheEntry?.value;\n }\n\n async mget(\n keys: string[],\n ): Promise<Record<string, Serializable | undefined>> {\n const cacheStore = await this.#state.getKey<CacheStore>(this.prefix);\n\n // If cache is not initialized, return empty object\n if (!cacheStore) {\n return {};\n }\n\n const keysAndValues = Object.entries(cacheStore).filter(([key]) =>\n keys.includes(key),\n );\n\n const expiredKeys = keysAndValues.filter(\n ([_unused, cacheEntry]) =>\n cacheEntry && cacheEntry.expiresAt < Date.now(),\n );\n\n await this.mdelete(expiredKeys.map(([key]) => key));\n\n const result: Record<string, Serializable | undefined> = {};\n\n // First, handle keys that exist in the cache\n keysAndValues.forEach(([key, cacheEntry]) => {\n if (cacheEntry === undefined) {\n this.#logger.info(`[StateCache] ❌ Cache miss for key \"${key}\"`);\n result[key] = undefined;\n return;\n }\n\n if (cacheEntry.expiresAt < Date.now()) {\n this.#logger.info(`[StateCache] ⌛ Cache expired for key \"${key}\"`);\n result[key] = undefined;\n } else {\n this.#logger.info(`[StateCache] 🎉 Cache hit for key \"${key}\"`);\n result[key] = cacheEntry.value;\n }\n });\n\n // Then, handle keys that don't exist in the cache\n keys.forEach((key) => {\n if (!Object.hasOwn(result, key)) {\n this.#logger.info(`[StateCache] ❌ Cache miss for key \"${key}\"`);\n result[key] = undefined;\n }\n });\n\n return result;\n }\n\n async mset(\n entries: { key: string; value: Serializable; ttlMilliseconds?: number }[],\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n if (entries.length === 1) {\n assert(entries[0]); // Enforce type narrowing as TS cannot infer that entries[0] is defined\n const { key, value, ttlMilliseconds } = entries[0];\n await this.set(key, value, ttlMilliseconds);\n return;\n }\n\n entries.forEach(({ ttlMilliseconds }) => {\n this.#validateTtlOrThrow(ttlMilliseconds);\n });\n\n // Using `state.update` is preferred for bulk `set`s, because it's more efficient and atomic.\n await this.#state.update((stateValue) => {\n const cacheStore = stateValue[this.prefix] ?? {};\n entries.forEach(({ key, value, ttlMilliseconds }) => {\n if (value === undefined) {\n return;\n }\n cacheStore[key] = {\n value,\n expiresAt: Math.min(\n Date.now() + (ttlMilliseconds ?? Number.MAX_SAFE_INTEGER),\n Number.MAX_SAFE_INTEGER,\n ),\n };\n });\n stateValue[this.prefix] = cacheStore;\n return stateValue;\n });\n }\n\n async mdelete(keys: string[]): Promise<Record<string, boolean>> {\n const result: Record<string, boolean> = {};\n\n // Using `state.update` is preferred for bulk `delete`s, because it's more efficient and atomic.\n await this.#state.update((stateValue) => {\n const cacheStore = stateValue[this.prefix] ?? {};\n keys.forEach((key) => {\n if (cacheStore[key] === undefined) {\n result[key] = false;\n } else {\n delete cacheStore[key];\n result[key] = true;\n }\n });\n stateValue[this.prefix] = cacheStore;\n return stateValue;\n });\n\n return result;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../src/utils/cache/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Serializable } from '../serialization/types';\n\nexport type TimestampMilliseconds = number;\n\n/**\n * A single cache entry.\n */\nexport type CacheEntry = {\n value: Serializable;\n expiresAt: TimestampMilliseconds;\n};\n\n/**\n * Interface for a generic cache implementation.\n *\n * @template TValue - The type of values stored in the cache\n */\nexport type ICache<TValue> = {\n /**\n * Retrieves a value from the cache by key.\n *\n * @param key - The key to retrieve\n * @returns The value if found, undefined if not found\n */\n get(key: string): Promise<TValue | undefined>;\n\n /**\n * Stores a value in the cache with an optional TTL.\n * - If a value is undefined, it will not be stored in the cache.\n * - If a value is null, it will be stored in the cache.\n *\n * @param key - The key to store the value under\n * @param value - The value to store\n * @param ttlMilliseconds - Optional time-to-live in milliseconds. If not provided, the value will not expire.\n * @throws Error if any entry's ttlMilliseconds is not a number, is negative, or is greater than 2^53 - 1\n */\n set(key: string, value: TValue, ttlMilliseconds?: number): Promise<void>;\n\n /**\n * Removes a value from the cache.\n *\n * @param key - The key to remove\n * @returns true if the key was found and removed, false otherwise\n */\n delete(key: string): Promise<boolean>;\n\n /**\n * Removes all values from the cache.\n */\n clear(): Promise<void>;\n\n /**\n * Checks if a key exists in the cache.\n *\n * @param key - The key to check\n * @returns true if the key exists, false otherwise\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Returns all keys currently in the cache.\n *\n * @returns Array of keys\n */\n keys(): Promise<string[]>;\n\n /**\n * Returns the number of items in the cache.\n *\n * @returns The number of items\n */\n size(): Promise<number>;\n\n /**\n * Retrieves a value from the cache without affecting its TTL or last accessed time.\n *\n * @param key - The key to peek at\n * @returns The value if found, undefined if not found\n */\n peek(key: string): Promise<TValue | undefined>;\n\n /**\n * Retrieves multiple values from the cache in a single operation.\n *\n * @param keys - Array of keys to retrieve\n * @returns Object mapping keys to their values (or undefined if not found)\n */\n mget(keys: string[]): Promise<Record<string, TValue | undefined>>;\n\n /**\n * Stores multiple values in the cache in a single operation.\n * - If a value is undefined, it will not be stored in the cache.\n * - If a value is null, it will be stored in the cache.\n *\n * @param entries - Array of entries to store, each with key, value, and optional TTL (if not provided, the value will not expire)\n * @throws Error if any entry's ttlMilliseconds is not a number, is negative, or is greater than 2^53 - 1\n */\n mset(\n entries: { key: string; value: TValue; ttlMilliseconds?: number }[],\n ): Promise<void>;\n\n /**\n * Removes multiple values from the cache.\n *\n * @param keys - Array of keys to remove\n * @returns An object mapping each key to a boolean indicating whether it was found and removed\n */\n mdelete(keys: string[]): Promise<Record<string, boolean>>;\n};\n"]}
|