@metamask-previews/perps-controller 3.0.0-preview-e61cfa5 → 3.1.0-preview-548bdd1d9

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 (90) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/dist/PerpsController-method-action-types.cjs.map +1 -1
  3. package/dist/PerpsController-method-action-types.d.cts +8 -0
  4. package/dist/PerpsController-method-action-types.d.cts.map +1 -1
  5. package/dist/PerpsController-method-action-types.d.mts +8 -0
  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 +117 -29
  9. package/dist/PerpsController.cjs.map +1 -1
  10. package/dist/PerpsController.d.cts +14 -2
  11. package/dist/PerpsController.d.cts.map +1 -1
  12. package/dist/PerpsController.d.mts +14 -2
  13. package/dist/PerpsController.d.mts.map +1 -1
  14. package/dist/PerpsController.mjs +118 -30
  15. package/dist/PerpsController.mjs.map +1 -1
  16. package/dist/constants/eventNames.cjs +1 -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 +1 -0
  23. package/dist/constants/eventNames.mjs.map +1 -1
  24. package/dist/constants/perpsConfig.cjs +46 -1
  25. package/dist/constants/perpsConfig.cjs.map +1 -1
  26. package/dist/constants/perpsConfig.d.cts +35 -0
  27. package/dist/constants/perpsConfig.d.cts.map +1 -1
  28. package/dist/constants/perpsConfig.d.mts +35 -0
  29. package/dist/constants/perpsConfig.d.mts.map +1 -1
  30. package/dist/constants/perpsConfig.mjs +43 -0
  31. package/dist/constants/perpsConfig.mjs.map +1 -1
  32. package/dist/constants/transactionsHistoryConfig.cjs +23 -4
  33. package/dist/constants/transactionsHistoryConfig.cjs.map +1 -1
  34. package/dist/constants/transactionsHistoryConfig.d.cts +23 -4
  35. package/dist/constants/transactionsHistoryConfig.d.cts.map +1 -1
  36. package/dist/constants/transactionsHistoryConfig.d.mts +23 -4
  37. package/dist/constants/transactionsHistoryConfig.d.mts.map +1 -1
  38. package/dist/constants/transactionsHistoryConfig.mjs +23 -4
  39. package/dist/constants/transactionsHistoryConfig.mjs.map +1 -1
  40. package/dist/index.cjs +14 -3
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +3 -1
  43. package/dist/index.d.cts.map +1 -1
  44. package/dist/index.d.mts +3 -1
  45. package/dist/index.d.mts.map +1 -1
  46. package/dist/index.mjs +2 -1
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/providers/HyperLiquidProvider.cjs +83 -27
  49. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  50. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  51. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  52. package/dist/providers/HyperLiquidProvider.mjs +83 -27
  53. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  54. package/dist/services/HyperLiquidSubscriptionService.cjs +6 -0
  55. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  56. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  57. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  58. package/dist/services/HyperLiquidSubscriptionService.mjs +6 -0
  59. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  60. package/dist/types/index.cjs.map +1 -1
  61. package/dist/types/index.d.cts +6 -0
  62. package/dist/types/index.d.cts.map +1 -1
  63. package/dist/types/index.d.mts +6 -0
  64. package/dist/types/index.d.mts.map +1 -1
  65. package/dist/types/index.mjs.map +1 -1
  66. package/dist/utils/index.cjs +2 -0
  67. package/dist/utils/index.cjs.map +1 -1
  68. package/dist/utils/index.d.cts +2 -0
  69. package/dist/utils/index.d.cts.map +1 -1
  70. package/dist/utils/index.d.mts +2 -0
  71. package/dist/utils/index.d.mts.map +1 -1
  72. package/dist/utils/index.mjs +2 -0
  73. package/dist/utils/index.mjs.map +1 -1
  74. package/dist/utils/perpsDiskPersistence.cjs +252 -0
  75. package/dist/utils/perpsDiskPersistence.cjs.map +1 -0
  76. package/dist/utils/perpsDiskPersistence.d.cts +108 -0
  77. package/dist/utils/perpsDiskPersistence.d.cts.map +1 -0
  78. package/dist/utils/perpsDiskPersistence.d.mts +108 -0
  79. package/dist/utils/perpsDiskPersistence.d.mts.map +1 -0
  80. package/dist/utils/perpsDiskPersistence.mjs +244 -0
  81. package/dist/utils/perpsDiskPersistence.mjs.map +1 -0
  82. package/dist/utils/perpsFormatters.cjs +498 -0
  83. package/dist/utils/perpsFormatters.cjs.map +1 -0
  84. package/dist/utils/perpsFormatters.d.cts +202 -0
  85. package/dist/utils/perpsFormatters.d.cts.map +1 -0
  86. package/dist/utils/perpsFormatters.d.mts +202 -0
  87. package/dist/utils/perpsFormatters.d.mts.map +1 -0
  88. package/dist/utils/perpsFormatters.mjs +489 -0
  89. package/dist/utils/perpsFormatters.mjs.map +1 -0
  90. package/package.json +3 -3
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hydrateFromDiskSync = exports.persistUserEntriesToDisk = exports.persistMarketEntriesToDisk = exports.buildUserDataPayload = exports.buildMarketDataPayload = void 0;
4
+ const perpsConfig_1 = require("../constants/perpsConfig.cjs");
5
+ /**
6
+ * Multiplier applied to staleGuardMs (preloadGuardMs, currently 30s) to compute
7
+ * the staleness cap for disk-hydrated timestamps. A factor of 10 ensures hydrated
8
+ * data is always TTL-expired so the stream manager overwrites it with live data,
9
+ * while still being recent enough for a useful first paint.
10
+ */
11
+ const DISK_HYDRATION_STALENESS_FACTOR = 10;
12
+ /**
13
+ * Build the disk-cache payload for market data.
14
+ * In aggregated mode, groups markets by provider into separate entries.
15
+ *
16
+ * @param markets - Current market data snapshot.
17
+ * @param activeProvider - The active provider id (may be "aggregated").
18
+ * @param isTestnet - Global testnet flag.
19
+ * @param now - Timestamp to stamp entries with.
20
+ * @returns Payload ready for JSON serialization.
21
+ */
22
+ function buildMarketDataPayload(markets, activeProvider, isTestnet, now) {
23
+ if (activeProvider === 'aggregated') {
24
+ const entriesByKey = new Map();
25
+ for (const market of markets) {
26
+ const providerId = market.providerId ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider;
27
+ const key = (0, perpsConfig_1.buildProviderCacheKey)(providerId, isTestnet);
28
+ const existing = entriesByKey.get(key);
29
+ if (existing) {
30
+ existing.data.push(market);
31
+ }
32
+ else {
33
+ entriesByKey.set(key, {
34
+ providerNetworkKey: key,
35
+ data: [market],
36
+ timestamp: now,
37
+ });
38
+ }
39
+ }
40
+ const entries = Array.from(entriesByKey.values());
41
+ return entries.length === 1 ? entries[0] : { entries };
42
+ }
43
+ return {
44
+ providerNetworkKey: (0, perpsConfig_1.buildProviderCacheKey)(activeProvider ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider, isTestnet),
45
+ data: markets,
46
+ timestamp: now,
47
+ };
48
+ }
49
+ exports.buildMarketDataPayload = buildMarketDataPayload;
50
+ /**
51
+ * Build the disk-cache payload for user data (positions, orders, account).
52
+ * In aggregated mode, groups entries by provider.
53
+ *
54
+ * @param positions - Current positions snapshot.
55
+ * @param orders - Current orders snapshot.
56
+ * @param accountState - Current account state snapshot.
57
+ * @param address - EVM account address.
58
+ * @param activeProvider - The active provider id (may be "aggregated").
59
+ * @param isTestnet - Global testnet flag.
60
+ * @param now - Timestamp to stamp entries with.
61
+ * @returns Payload ready for JSON serialization.
62
+ */
63
+ function buildUserDataPayload(positions, orders, accountState, address, activeProvider, isTestnet, now) {
64
+ if (activeProvider === 'aggregated') {
65
+ const entriesByKey = new Map();
66
+ const ensureEntry = (providerId) => {
67
+ const key = (0, perpsConfig_1.buildProviderCacheKey)(providerId, isTestnet);
68
+ let entry = entriesByKey.get(key);
69
+ if (!entry) {
70
+ entry = {
71
+ providerNetworkKey: key,
72
+ address,
73
+ positions: [],
74
+ orders: [],
75
+ accountState: null,
76
+ timestamp: now,
77
+ };
78
+ entriesByKey.set(key, entry);
79
+ }
80
+ return entry;
81
+ };
82
+ for (const position of positions) {
83
+ ensureEntry(position.providerId ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider).positions.push(position);
84
+ }
85
+ for (const order of orders) {
86
+ ensureEntry(order.providerId ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider).orders.push(order);
87
+ }
88
+ if (accountState) {
89
+ ensureEntry(accountState.providerId ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider).accountState = accountState;
90
+ }
91
+ const entries = Array.from(entriesByKey.values()).filter((entry) => entry.positions.length > 0 ||
92
+ entry.orders.length > 0 ||
93
+ entry.accountState !== null);
94
+ return entries.length === 1 ? entries[0] : { entries };
95
+ }
96
+ return {
97
+ providerNetworkKey: (0, perpsConfig_1.buildProviderCacheKey)(activeProvider ?? perpsConfig_1.PROVIDER_CONFIG.DefaultProvider, isTestnet),
98
+ address,
99
+ positions,
100
+ orders,
101
+ accountState,
102
+ timestamp: now,
103
+ };
104
+ }
105
+ exports.buildUserDataPayload = buildUserDataPayload;
106
+ /**
107
+ * Write market entries to disk (best-effort, non-blocking).
108
+ *
109
+ * @param diskCache - Disk cache instance from controller infrastructure.
110
+ * @param entries - Pre-assembled market cache entries to persist.
111
+ */
112
+ function persistMarketEntriesToDisk(diskCache, entries) {
113
+ if (entries.length === 0) {
114
+ return;
115
+ }
116
+ const payload = entries.length === 1 ? entries[0] : { entries };
117
+ diskCache
118
+ .setItem(perpsConfig_1.PERPS_DISK_CACHE_MARKETS, JSON.stringify(payload))
119
+ .catch(() => {
120
+ // Disk persistence is best-effort and must never block preload.
121
+ });
122
+ }
123
+ exports.persistMarketEntriesToDisk = persistMarketEntriesToDisk;
124
+ /**
125
+ * Write user data entries to disk (best-effort, non-blocking).
126
+ *
127
+ * @param diskCache - Disk cache instance from controller infrastructure.
128
+ * @param entries - Pre-assembled user cache entries to persist.
129
+ */
130
+ function persistUserEntriesToDisk(diskCache, entries) {
131
+ if (entries.length === 0) {
132
+ return;
133
+ }
134
+ const payload = entries.length === 1 ? entries[0] : { entries };
135
+ diskCache
136
+ .setItem(perpsConfig_1.PERPS_DISK_CACHE_USER_DATA, JSON.stringify(payload))
137
+ .catch(() => {
138
+ // Disk persistence is best-effort and must never block preload.
139
+ });
140
+ }
141
+ exports.persistUserEntriesToDisk = persistUserEntriesToDisk;
142
+ /**
143
+ * Read disk-persisted cache snapshots and compute the state updates to apply.
144
+ * Returns plain objects rather than mutating state directly, so the caller
145
+ * can apply all changes in a single batched this.update() call.
146
+ *
147
+ * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs
148
+ * in the past so the stream manager always overwrites disk data with fresh live data.
149
+ *
150
+ * @param diskCache - Disk cache instance from controller infrastructure.
151
+ * @param currentMarketCache - Current cachedMarketDataByProvider state.
152
+ * @param currentUserCache - Current cachedUserDataByProvider state.
153
+ * @param staleGuardMs - preloadGuardMs constant from the controller.
154
+ * @returns Updates to apply plus stats for debug logging.
155
+ */
156
+ function hydrateFromDiskSync(diskCache, currentMarketCache, currentUserCache, staleGuardMs) {
157
+ const hydrateT0 = Date.now();
158
+ const marketUpdates = {};
159
+ const userUpdates = {};
160
+ let marketCount = 0;
161
+ let userPositions = 0;
162
+ let userOrders = 0;
163
+ if (!diskCache.getItemSync) {
164
+ return {
165
+ marketUpdates,
166
+ userUpdates,
167
+ stats: { marketCount, userPositions, userOrders, durationMs: 0 },
168
+ };
169
+ }
170
+ const staleHydratedTimestamp = Date.now() - staleGuardMs * DISK_HYDRATION_STALENESS_FACTOR - 1;
171
+ try {
172
+ const marketsRaw = diskCache.getItemSync(perpsConfig_1.PERPS_DISK_CACHE_MARKETS);
173
+ const userRaw = diskCache.getItemSync(perpsConfig_1.PERPS_DISK_CACHE_USER_DATA);
174
+ if (marketsRaw) {
175
+ try {
176
+ const parsed = JSON.parse(marketsRaw);
177
+ const entries = Array.isArray(parsed.entries)
178
+ ? parsed.entries
179
+ : [parsed];
180
+ for (const entry of entries) {
181
+ if (entry.providerNetworkKey && Array.isArray(entry.data)) {
182
+ const existing = currentMarketCache[entry.providerNetworkKey];
183
+ if (!existing || existing.timestamp < entry.timestamp) {
184
+ const strippedData = entry.data.map((market) => ({
185
+ ...market,
186
+ price: perpsConfig_1.PERPS_CONSTANTS.FallbackPriceDisplay,
187
+ change24h: perpsConfig_1.PERPS_CONSTANTS.FallbackDataDisplay,
188
+ change24hPercent: perpsConfig_1.PERPS_CONSTANTS.FallbackPercentageDisplay,
189
+ }));
190
+ marketUpdates[entry.providerNetworkKey] = {
191
+ data: strippedData,
192
+ // Disk-hydrated market snapshots are only for structural
193
+ // first paint. Keep them TTL-stale so the stream manager
194
+ // still fetches fresh prices on connect.
195
+ timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),
196
+ };
197
+ marketCount += strippedData.length;
198
+ }
199
+ }
200
+ }
201
+ }
202
+ catch {
203
+ // Corrupt JSON — silently ignore
204
+ }
205
+ }
206
+ if (userRaw) {
207
+ try {
208
+ const parsed = JSON.parse(userRaw);
209
+ const entries = Array.isArray(parsed.entries)
210
+ ? parsed.entries
211
+ : [parsed];
212
+ for (const entry of entries) {
213
+ if (entry.providerNetworkKey && entry.address) {
214
+ // Skip address check here — accounts may not be loaded yet at
215
+ // constructor time. getCachedUserDataForActiveProvider validates
216
+ // the address at read time, so stale-account data is never served.
217
+ const existing = currentUserCache[entry.providerNetworkKey];
218
+ if (!existing || existing.timestamp < entry.timestamp) {
219
+ userUpdates[entry.providerNetworkKey] = {
220
+ positions: entry.positions,
221
+ orders: entry.orders,
222
+ accountState: entry.accountState,
223
+ timestamp: Math.min(entry.timestamp, staleHydratedTimestamp),
224
+ address: entry.address,
225
+ };
226
+ userPositions += entry.positions.length;
227
+ userOrders += entry.orders.length;
228
+ }
229
+ }
230
+ }
231
+ }
232
+ catch {
233
+ // Corrupt JSON — silently ignore
234
+ }
235
+ }
236
+ }
237
+ catch {
238
+ // Disk read failure — non-critical
239
+ }
240
+ return {
241
+ marketUpdates,
242
+ userUpdates,
243
+ stats: {
244
+ marketCount,
245
+ userPositions,
246
+ userOrders,
247
+ durationMs: Date.now() - hydrateT0,
248
+ },
249
+ };
250
+ }
251
+ exports.hydrateFromDiskSync = hydrateFromDiskSync;
252
+ //# sourceMappingURL=perpsDiskPersistence.cjs.map
@@ -0,0 +1 @@
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';\nimport type { AccountState, Order, PerpsMarketData, Position } from '../types';\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"]}
@@ -0,0 +1,108 @@
1
+ import type { AccountState, Order, PerpsMarketData, Position } from "../types/index.cjs";
2
+ /** Minimal disk cache interface required by persistence utilities. */
3
+ export type PerpsDiskCache = {
4
+ getItem(key: string): Promise<string | null>;
5
+ getItemSync?(key: string): string | null;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ };
8
+ /** Shape of a single market entry persisted to disk cache. */
9
+ export type DiskCacheMarketEntry = {
10
+ providerNetworkKey: string;
11
+ data: PerpsMarketData[];
12
+ timestamp: number;
13
+ };
14
+ /** Shape of a single user-data entry persisted to disk cache. */
15
+ export type DiskCacheUserEntry = {
16
+ providerNetworkKey: string;
17
+ address: string;
18
+ positions: Position[];
19
+ orders: Order[];
20
+ accountState: AccountState | null;
21
+ timestamp: number;
22
+ };
23
+ /** Disk payload shape — either a single entry or a multi-provider wrapper. */
24
+ export type DiskCacheMarketPayload = DiskCacheMarketEntry | {
25
+ entries: DiskCacheMarketEntry[];
26
+ };
27
+ export type DiskCacheUserPayload = DiskCacheUserEntry | {
28
+ entries: DiskCacheUserEntry[];
29
+ };
30
+ /**
31
+ * Build the disk-cache payload for market data.
32
+ * In aggregated mode, groups markets by provider into separate entries.
33
+ *
34
+ * @param markets - Current market data snapshot.
35
+ * @param activeProvider - The active provider id (may be "aggregated").
36
+ * @param isTestnet - Global testnet flag.
37
+ * @param now - Timestamp to stamp entries with.
38
+ * @returns Payload ready for JSON serialization.
39
+ */
40
+ export declare function buildMarketDataPayload(markets: PerpsMarketData[], activeProvider: string, isTestnet: boolean, now: number): DiskCacheMarketPayload;
41
+ /**
42
+ * Build the disk-cache payload for user data (positions, orders, account).
43
+ * In aggregated mode, groups entries by provider.
44
+ *
45
+ * @param positions - Current positions snapshot.
46
+ * @param orders - Current orders snapshot.
47
+ * @param accountState - Current account state snapshot.
48
+ * @param address - EVM account address.
49
+ * @param activeProvider - The active provider id (may be "aggregated").
50
+ * @param isTestnet - Global testnet flag.
51
+ * @param now - Timestamp to stamp entries with.
52
+ * @returns Payload ready for JSON serialization.
53
+ */
54
+ export declare function buildUserDataPayload(positions: Position[], orders: Order[], accountState: AccountState | null, address: string, activeProvider: string, isTestnet: boolean, now: number): DiskCacheUserPayload;
55
+ /**
56
+ * Write market entries to disk (best-effort, non-blocking).
57
+ *
58
+ * @param diskCache - Disk cache instance from controller infrastructure.
59
+ * @param entries - Pre-assembled market cache entries to persist.
60
+ */
61
+ export declare function persistMarketEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheMarketEntry[]): void;
62
+ /**
63
+ * Write user data entries to disk (best-effort, non-blocking).
64
+ *
65
+ * @param diskCache - Disk cache instance from controller infrastructure.
66
+ * @param entries - Pre-assembled user cache entries to persist.
67
+ */
68
+ export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): void;
69
+ /** Computed updates returned by hydrateFromDiskSync. */
70
+ export type HydrateFromDiskResult = {
71
+ marketUpdates: Record<string, {
72
+ data: PerpsMarketData[];
73
+ timestamp: number;
74
+ }>;
75
+ userUpdates: Record<string, {
76
+ positions: Position[];
77
+ orders: Order[];
78
+ accountState: AccountState | null;
79
+ timestamp: number;
80
+ address: string;
81
+ }>;
82
+ stats: {
83
+ marketCount: number;
84
+ userPositions: number;
85
+ userOrders: number;
86
+ durationMs: number;
87
+ };
88
+ };
89
+ /**
90
+ * Read disk-persisted cache snapshots and compute the state updates to apply.
91
+ * Returns plain objects rather than mutating state directly, so the caller
92
+ * can apply all changes in a single batched this.update() call.
93
+ *
94
+ * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs
95
+ * in the past so the stream manager always overwrites disk data with fresh live data.
96
+ *
97
+ * @param diskCache - Disk cache instance from controller infrastructure.
98
+ * @param currentMarketCache - Current cachedMarketDataByProvider state.
99
+ * @param currentUserCache - Current cachedUserDataByProvider state.
100
+ * @param staleGuardMs - preloadGuardMs constant from the controller.
101
+ * @returns Updates to apply plus stats for debug logging.
102
+ */
103
+ export declare function hydrateFromDiskSync(diskCache: PerpsDiskCache, currentMarketCache: Record<string, {
104
+ timestamp: number;
105
+ }>, currentUserCache: Record<string, {
106
+ timestamp: number;
107
+ }>, staleGuardMs: number): HydrateFromDiskResult;
108
+ //# sourceMappingURL=perpsDiskPersistence.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perpsDiskPersistence.d.cts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import type { AccountState, Order, PerpsMarketData, Position } from "../types/index.mjs";
2
+ /** Minimal disk cache interface required by persistence utilities. */
3
+ export type PerpsDiskCache = {
4
+ getItem(key: string): Promise<string | null>;
5
+ getItemSync?(key: string): string | null;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ };
8
+ /** Shape of a single market entry persisted to disk cache. */
9
+ export type DiskCacheMarketEntry = {
10
+ providerNetworkKey: string;
11
+ data: PerpsMarketData[];
12
+ timestamp: number;
13
+ };
14
+ /** Shape of a single user-data entry persisted to disk cache. */
15
+ export type DiskCacheUserEntry = {
16
+ providerNetworkKey: string;
17
+ address: string;
18
+ positions: Position[];
19
+ orders: Order[];
20
+ accountState: AccountState | null;
21
+ timestamp: number;
22
+ };
23
+ /** Disk payload shape — either a single entry or a multi-provider wrapper. */
24
+ export type DiskCacheMarketPayload = DiskCacheMarketEntry | {
25
+ entries: DiskCacheMarketEntry[];
26
+ };
27
+ export type DiskCacheUserPayload = DiskCacheUserEntry | {
28
+ entries: DiskCacheUserEntry[];
29
+ };
30
+ /**
31
+ * Build the disk-cache payload for market data.
32
+ * In aggregated mode, groups markets by provider into separate entries.
33
+ *
34
+ * @param markets - Current market data snapshot.
35
+ * @param activeProvider - The active provider id (may be "aggregated").
36
+ * @param isTestnet - Global testnet flag.
37
+ * @param now - Timestamp to stamp entries with.
38
+ * @returns Payload ready for JSON serialization.
39
+ */
40
+ export declare function buildMarketDataPayload(markets: PerpsMarketData[], activeProvider: string, isTestnet: boolean, now: number): DiskCacheMarketPayload;
41
+ /**
42
+ * Build the disk-cache payload for user data (positions, orders, account).
43
+ * In aggregated mode, groups entries by provider.
44
+ *
45
+ * @param positions - Current positions snapshot.
46
+ * @param orders - Current orders snapshot.
47
+ * @param accountState - Current account state snapshot.
48
+ * @param address - EVM account address.
49
+ * @param activeProvider - The active provider id (may be "aggregated").
50
+ * @param isTestnet - Global testnet flag.
51
+ * @param now - Timestamp to stamp entries with.
52
+ * @returns Payload ready for JSON serialization.
53
+ */
54
+ export declare function buildUserDataPayload(positions: Position[], orders: Order[], accountState: AccountState | null, address: string, activeProvider: string, isTestnet: boolean, now: number): DiskCacheUserPayload;
55
+ /**
56
+ * Write market entries to disk (best-effort, non-blocking).
57
+ *
58
+ * @param diskCache - Disk cache instance from controller infrastructure.
59
+ * @param entries - Pre-assembled market cache entries to persist.
60
+ */
61
+ export declare function persistMarketEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheMarketEntry[]): void;
62
+ /**
63
+ * Write user data entries to disk (best-effort, non-blocking).
64
+ *
65
+ * @param diskCache - Disk cache instance from controller infrastructure.
66
+ * @param entries - Pre-assembled user cache entries to persist.
67
+ */
68
+ export declare function persistUserEntriesToDisk(diskCache: PerpsDiskCache, entries: DiskCacheUserEntry[]): void;
69
+ /** Computed updates returned by hydrateFromDiskSync. */
70
+ export type HydrateFromDiskResult = {
71
+ marketUpdates: Record<string, {
72
+ data: PerpsMarketData[];
73
+ timestamp: number;
74
+ }>;
75
+ userUpdates: Record<string, {
76
+ positions: Position[];
77
+ orders: Order[];
78
+ accountState: AccountState | null;
79
+ timestamp: number;
80
+ address: string;
81
+ }>;
82
+ stats: {
83
+ marketCount: number;
84
+ userPositions: number;
85
+ userOrders: number;
86
+ durationMs: number;
87
+ };
88
+ };
89
+ /**
90
+ * Read disk-persisted cache snapshots and compute the state updates to apply.
91
+ * Returns plain objects rather than mutating state directly, so the caller
92
+ * can apply all changes in a single batched this.update() call.
93
+ *
94
+ * All returned timestamps are capped at DISK_HYDRATION_STALENESS_FACTOR * staleGuardMs
95
+ * in the past so the stream manager always overwrites disk data with fresh live data.
96
+ *
97
+ * @param diskCache - Disk cache instance from controller infrastructure.
98
+ * @param currentMarketCache - Current cachedMarketDataByProvider state.
99
+ * @param currentUserCache - Current cachedUserDataByProvider state.
100
+ * @param staleGuardMs - preloadGuardMs constant from the controller.
101
+ * @returns Updates to apply plus stats for debug logging.
102
+ */
103
+ export declare function hydrateFromDiskSync(diskCache: PerpsDiskCache, currentMarketCache: Record<string, {
104
+ timestamp: number;
105
+ }>, currentUserCache: Record<string, {
106
+ timestamp: number;
107
+ }>, staleGuardMs: number): HydrateFromDiskResult;
108
+ //# sourceMappingURL=perpsDiskPersistence.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perpsDiskPersistence.d.mts","sourceRoot":"","sources":["../../src/utils/perpsDiskPersistence.ts"],"names":[],"mappings":"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"}