@jango-blockchained/hoox-shared 1.0.8

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 (115) hide show
  1. package/README.md +27 -0
  2. package/dist/analytics.d.ts +22 -0
  3. package/dist/analytics.d.ts.map +1 -0
  4. package/dist/analytics.js +34 -0
  5. package/dist/api-client.d.ts +34 -0
  6. package/dist/api-client.d.ts.map +1 -0
  7. package/dist/api-client.js +124 -0
  8. package/dist/colors.d.ts +34 -0
  9. package/dist/colors.d.ts.map +1 -0
  10. package/dist/colors.js +42 -0
  11. package/dist/config.d.ts +24 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +91 -0
  14. package/dist/d1/index.d.ts +5 -0
  15. package/dist/d1/index.d.ts.map +1 -0
  16. package/dist/d1/index.js +316 -0
  17. package/dist/d1/repository.d.ts +77 -0
  18. package/dist/d1/repository.d.ts.map +1 -0
  19. package/dist/d1/schemas.d.ts +85 -0
  20. package/dist/d1/schemas.d.ts.map +1 -0
  21. package/dist/errors.d.ts +39 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +106 -0
  24. package/dist/exchange-client.d.ts +84 -0
  25. package/dist/exchange-client.d.ts.map +1 -0
  26. package/dist/exchange-client.js +104 -0
  27. package/dist/format-time.d.ts +24 -0
  28. package/dist/format-time.d.ts.map +1 -0
  29. package/dist/format-time.js +57 -0
  30. package/dist/formatters.d.ts +32 -0
  31. package/dist/formatters.d.ts.map +1 -0
  32. package/dist/formatters.js +155 -0
  33. package/dist/health.d.ts +19 -0
  34. package/dist/health.d.ts.map +1 -0
  35. package/dist/health.js +117 -0
  36. package/dist/index.d.ts +45 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +2581 -0
  39. package/dist/kvKeys.d.ts +72 -0
  40. package/dist/kvKeys.d.ts.map +1 -0
  41. package/dist/kvKeys.js +107 -0
  42. package/dist/kvUtils.d.ts +26 -0
  43. package/dist/kvUtils.d.ts.map +1 -0
  44. package/dist/kvUtils.js +127 -0
  45. package/dist/legal.d.ts +11 -0
  46. package/dist/legal.d.ts.map +1 -0
  47. package/dist/legal.js +40 -0
  48. package/dist/middleware/auth.d.ts +59 -0
  49. package/dist/middleware/auth.d.ts.map +1 -0
  50. package/dist/middleware/cors.d.ts +28 -0
  51. package/dist/middleware/cors.d.ts.map +1 -0
  52. package/dist/middleware/index.d.ts +10 -0
  53. package/dist/middleware/index.d.ts.map +1 -0
  54. package/dist/middleware/index.js +375 -0
  55. package/dist/middleware/logger.d.ts +16 -0
  56. package/dist/middleware/logger.d.ts.map +1 -0
  57. package/dist/middleware/rate-limit.d.ts +24 -0
  58. package/dist/middleware/rate-limit.d.ts.map +1 -0
  59. package/dist/middleware/security-headers.d.ts +39 -0
  60. package/dist/middleware/security-headers.d.ts.map +1 -0
  61. package/dist/middleware/validate.d.ts +21 -0
  62. package/dist/middleware/validate.d.ts.map +1 -0
  63. package/dist/router.d.ts +11 -0
  64. package/dist/router.d.ts.map +1 -0
  65. package/dist/router.js +123 -0
  66. package/dist/schemas/index.d.ts +4 -0
  67. package/dist/schemas/index.d.ts.map +1 -0
  68. package/dist/schemas/index.js +656 -0
  69. package/dist/schemas/registry.d.ts +8 -0
  70. package/dist/schemas/registry.d.ts.map +1 -0
  71. package/dist/schemas/types.d.ts +79 -0
  72. package/dist/schemas/types.d.ts.map +1 -0
  73. package/dist/schemas/validators.d.ts +35 -0
  74. package/dist/schemas/validators.d.ts.map +1 -0
  75. package/dist/service-bindings.d.ts +32 -0
  76. package/dist/service-bindings.d.ts.map +1 -0
  77. package/dist/service-bindings.js +31 -0
  78. package/dist/session.d.ts +46 -0
  79. package/dist/session.d.ts.map +1 -0
  80. package/dist/session.js +79 -0
  81. package/dist/sse.d.ts +26 -0
  82. package/dist/sse.d.ts.map +1 -0
  83. package/dist/sse.js +91 -0
  84. package/dist/stores/config-store.d.ts +87 -0
  85. package/dist/stores/config-store.d.ts.map +1 -0
  86. package/dist/stores/config-store.js +132 -0
  87. package/dist/stores/service-store.d.ts +206 -0
  88. package/dist/stores/service-store.d.ts.map +1 -0
  89. package/dist/stores/service-store.js +359 -0
  90. package/dist/stores/ui-store.d.ts +63 -0
  91. package/dist/stores/ui-store.d.ts.map +1 -0
  92. package/dist/stores/ui-store.js +62 -0
  93. package/dist/types/errors.d.ts +16 -0
  94. package/dist/types/errors.d.ts.map +1 -0
  95. package/dist/types/errors.js +1 -0
  96. package/dist/types/router.d.ts +22 -0
  97. package/dist/types/router.d.ts.map +1 -0
  98. package/dist/types/router.js +1 -0
  99. package/dist/types.d.ts +317 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +70 -0
  102. package/dist/wizard/engine.d.ts +42 -0
  103. package/dist/wizard/engine.d.ts.map +1 -0
  104. package/dist/wizard/index.d.ts +7 -0
  105. package/dist/wizard/index.d.ts.map +1 -0
  106. package/dist/wizard/index.js +519 -0
  107. package/dist/wizard/persistence.d.ts +14 -0
  108. package/dist/wizard/persistence.d.ts.map +1 -0
  109. package/dist/wizard/presets.d.ts +34 -0
  110. package/dist/wizard/presets.d.ts.map +1 -0
  111. package/dist/wizard/provisioner.d.ts +11 -0
  112. package/dist/wizard/provisioner.d.ts.map +1 -0
  113. package/dist/wizard/types.d.ts +72 -0
  114. package/dist/wizard/types.d.ts.map +1 -0
  115. package/package.json +83 -0
@@ -0,0 +1,206 @@
1
+ import type { WorkerInfo, Trade, Alert, LogEntry, SystemMetrics, ConnectionStatus } from "../types";
2
+ export interface ServiceState {
3
+ workers: WorkerInfo[];
4
+ tradeStream: Trade[];
5
+ alerts: Alert[];
6
+ logs: LogEntry[];
7
+ metrics: SystemMetrics | null;
8
+ connectionStatus: ConnectionStatus;
9
+ lastUpdated: number;
10
+ selectedWorkerId: string | null;
11
+ /** Number of consecutive failed reconnection attempts */
12
+ retryCount: number;
13
+ /** Last connection error message (for display) */
14
+ lastError: string | null;
15
+ /** Timestamp (ms) of the last successful API fetch */
16
+ lastSuccessfulFetch: number;
17
+ /** Current backoff delay in ms (0 when not reconnecting) */
18
+ reconnectDelay: number;
19
+ /** Timestamp when the connection was lost (for downtime calculation) */
20
+ disconnectedAt: number | null;
21
+ }
22
+ interface ServiceActions {
23
+ fetchWorkers: () => Promise<void>;
24
+ streamTrades: () => Promise<void>;
25
+ streamLogs: () => Promise<void>;
26
+ addAlert: (alert: Alert) => void;
27
+ addAlerts: (alerts: Alert[]) => void;
28
+ setConnectionStatus: (status: ConnectionStatus) => void;
29
+ selectWorker: (id: string | null) => void;
30
+ setWorkers: (workers: WorkerInfo[]) => void;
31
+ pushTrade: (trade: Trade) => void;
32
+ pushLog: (log: LogEntry) => void;
33
+ setMetrics: (metrics: SystemMetrics) => void;
34
+ /** Called on successful API response — resets error state */
35
+ handleConnectionSuccess: () => void;
36
+ /** Called on API failure — increments retry, applies backoff */
37
+ handleConnectionFailure: (errorMessage: string) => void;
38
+ /** Reset all retry/reconnect state (on full reconnect) */
39
+ resetRetries: () => void;
40
+ /** Force a connection retry from offline state */
41
+ forceRetry: () => void;
42
+ }
43
+ export declare const useServiceStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ServiceState & ServiceActions>, "setState"> & {
44
+ setState(nextStateOrUpdater: (ServiceState & ServiceActions) | Partial<ServiceState & ServiceActions> | ((state: {
45
+ workers: {
46
+ id: string;
47
+ name: string;
48
+ status: import("..").WorkerStatus;
49
+ uptime: number;
50
+ cpu: number;
51
+ memory: number;
52
+ requests: number;
53
+ durableObjectCount: number;
54
+ edgeCount: number;
55
+ version?: string | undefined;
56
+ lastDeployed?: number | undefined;
57
+ }[];
58
+ tradeStream: {
59
+ id: string;
60
+ symbol: string;
61
+ side: import("..").TradeSide;
62
+ price: number;
63
+ quantity: number;
64
+ timestamp: number;
65
+ exchange: string;
66
+ strategy?: string | undefined;
67
+ pnl?: number | undefined;
68
+ }[];
69
+ alerts: {
70
+ id: string;
71
+ type: string;
72
+ severity: import("..").AlertSeverity;
73
+ message: string;
74
+ timestamp: number;
75
+ workerId?: string | undefined;
76
+ acknowledged: boolean;
77
+ source?: string | undefined;
78
+ }[];
79
+ logs: {
80
+ id: string;
81
+ level: import("..").LogLevel;
82
+ message: string;
83
+ timestamp: number;
84
+ workerId?: string | undefined;
85
+ source?: string | undefined;
86
+ metadata?: {
87
+ [x: string]: unknown;
88
+ } | undefined;
89
+ }[];
90
+ metrics: {
91
+ totalWorkers: number;
92
+ onlineWorkers: number;
93
+ totalPnl: number;
94
+ activeStrategies: number;
95
+ dailyTrades: number;
96
+ aiCalls: number;
97
+ uptime: number;
98
+ lastUpdated: number;
99
+ } | null;
100
+ connectionStatus: ConnectionStatus;
101
+ lastUpdated: number;
102
+ selectedWorkerId: string | null;
103
+ retryCount: number;
104
+ lastError: string | null;
105
+ lastSuccessfulFetch: number;
106
+ reconnectDelay: number;
107
+ disconnectedAt: number | null;
108
+ fetchWorkers: () => Promise<void>;
109
+ streamTrades: () => Promise<void>;
110
+ streamLogs: () => Promise<void>;
111
+ addAlert: (alert: Alert) => void;
112
+ addAlerts: (alerts: Alert[]) => void;
113
+ setConnectionStatus: (status: ConnectionStatus) => void;
114
+ selectWorker: (id: string | null) => void;
115
+ setWorkers: (workers: WorkerInfo[]) => void;
116
+ pushTrade: (trade: Trade) => void;
117
+ pushLog: (log: LogEntry) => void;
118
+ setMetrics: (metrics: SystemMetrics) => void;
119
+ handleConnectionSuccess: () => void;
120
+ handleConnectionFailure: (errorMessage: string) => void;
121
+ resetRetries: () => void;
122
+ forceRetry: () => void;
123
+ }) => void), shouldReplace?: false): void;
124
+ setState(nextStateOrUpdater: (ServiceState & ServiceActions) | ((state: {
125
+ workers: {
126
+ id: string;
127
+ name: string;
128
+ status: import("..").WorkerStatus;
129
+ uptime: number;
130
+ cpu: number;
131
+ memory: number;
132
+ requests: number;
133
+ durableObjectCount: number;
134
+ edgeCount: number;
135
+ version?: string | undefined;
136
+ lastDeployed?: number | undefined;
137
+ }[];
138
+ tradeStream: {
139
+ id: string;
140
+ symbol: string;
141
+ side: import("..").TradeSide;
142
+ price: number;
143
+ quantity: number;
144
+ timestamp: number;
145
+ exchange: string;
146
+ strategy?: string | undefined;
147
+ pnl?: number | undefined;
148
+ }[];
149
+ alerts: {
150
+ id: string;
151
+ type: string;
152
+ severity: import("..").AlertSeverity;
153
+ message: string;
154
+ timestamp: number;
155
+ workerId?: string | undefined;
156
+ acknowledged: boolean;
157
+ source?: string | undefined;
158
+ }[];
159
+ logs: {
160
+ id: string;
161
+ level: import("..").LogLevel;
162
+ message: string;
163
+ timestamp: number;
164
+ workerId?: string | undefined;
165
+ source?: string | undefined;
166
+ metadata?: {
167
+ [x: string]: unknown;
168
+ } | undefined;
169
+ }[];
170
+ metrics: {
171
+ totalWorkers: number;
172
+ onlineWorkers: number;
173
+ totalPnl: number;
174
+ activeStrategies: number;
175
+ dailyTrades: number;
176
+ aiCalls: number;
177
+ uptime: number;
178
+ lastUpdated: number;
179
+ } | null;
180
+ connectionStatus: ConnectionStatus;
181
+ lastUpdated: number;
182
+ selectedWorkerId: string | null;
183
+ retryCount: number;
184
+ lastError: string | null;
185
+ lastSuccessfulFetch: number;
186
+ reconnectDelay: number;
187
+ disconnectedAt: number | null;
188
+ fetchWorkers: () => Promise<void>;
189
+ streamTrades: () => Promise<void>;
190
+ streamLogs: () => Promise<void>;
191
+ addAlert: (alert: Alert) => void;
192
+ addAlerts: (alerts: Alert[]) => void;
193
+ setConnectionStatus: (status: ConnectionStatus) => void;
194
+ selectWorker: (id: string | null) => void;
195
+ setWorkers: (workers: WorkerInfo[]) => void;
196
+ pushTrade: (trade: Trade) => void;
197
+ pushLog: (log: LogEntry) => void;
198
+ setMetrics: (metrics: SystemMetrics) => void;
199
+ handleConnectionSuccess: () => void;
200
+ handleConnectionFailure: (errorMessage: string) => void;
201
+ resetRetries: () => void;
202
+ forceRetry: () => void;
203
+ }) => void), shouldReplace: true): void;
204
+ }>;
205
+ export {};
206
+ //# sourceMappingURL=service-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-store.d.ts","sourceRoot":"","sources":["../../src/stores/service-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,359 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // src/api-client.ts
19
+ var exports_api_client = {};
20
+ __export(exports_api_client, {
21
+ hooxFetch: () => hooxFetch,
22
+ WorkerAPIError: () => WorkerAPIError
23
+ });
24
+ function isRetryableStatus(status) {
25
+ if (status === 401 || status === 403 || status === 429)
26
+ return false;
27
+ if (status >= 500)
28
+ return true;
29
+ if (status === 408)
30
+ return true;
31
+ return false;
32
+ }
33
+ function isNetworkError(error) {
34
+ if (error instanceof Error) {
35
+ const msg = error.message.toLowerCase();
36
+ return error.name === "AbortError" || error.name === "TimeoutError" || msg.includes("fetch") || msg.includes("network") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("dns") || msg.includes("timeout");
37
+ }
38
+ return false;
39
+ }
40
+ function sleep(ms) {
41
+ return new Promise((resolve) => setTimeout(resolve, ms));
42
+ }
43
+ async function hooxFetch(path, options) {
44
+ let lastError;
45
+ for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
46
+ const controller = new AbortController;
47
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
48
+ try {
49
+ const url = `${API_BASE}${path}`;
50
+ const headers = {
51
+ "Content-Type": "application/json",
52
+ ...API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {},
53
+ ...options?.headers
54
+ };
55
+ const response = await fetch(url, {
56
+ ...options,
57
+ headers,
58
+ signal: controller.signal
59
+ });
60
+ if (response.status === 429) {
61
+ throw new WorkerAPIError("API rate limited \u2014 backing off", {
62
+ status: 429,
63
+ retryable: false
64
+ });
65
+ }
66
+ if (response.status === 401 || response.status === 403) {
67
+ throw new WorkerAPIError(`Authentication failed (HTTP ${response.status})`, { status: response.status, retryable: false });
68
+ }
69
+ if (!response.ok) {
70
+ if (attempt < MAX_RETRIES && isRetryableStatus(response.status)) {
71
+ const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
72
+ await sleep(delay);
73
+ continue;
74
+ }
75
+ throw new WorkerAPIError(`HTTP ${response.status}: ${response.statusText}`, {
76
+ status: response.status,
77
+ retryable: isRetryableStatus(response.status)
78
+ });
79
+ }
80
+ return await response.json();
81
+ } catch (error) {
82
+ lastError = error;
83
+ if (error instanceof WorkerAPIError && !error.retryable) {
84
+ throw error;
85
+ }
86
+ if (attempt < MAX_RETRIES && isNetworkError(error)) {
87
+ const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
88
+ await sleep(delay);
89
+ continue;
90
+ }
91
+ if (!(error instanceof WorkerAPIError)) {
92
+ throw new WorkerAPIError(`Network request failed: ${error.message}`, { cause: error, retryable: false, status: 0 });
93
+ }
94
+ throw error;
95
+ } finally {
96
+ clearTimeout(timeoutId);
97
+ }
98
+ }
99
+ throw new WorkerAPIError("Max retries exhausted", {
100
+ cause: lastError,
101
+ retryable: false
102
+ });
103
+ }
104
+ var API_BASE, API_TOKEN, FETCH_TIMEOUT_MS = 5000, MAX_RETRIES = 3, BASE_RETRY_DELAY_MS = 1000, MAX_RETRY_DELAY_MS = 16000, WorkerAPIError;
105
+ var init_api_client = __esm(() => {
106
+ API_BASE = process.env.HOOX_API_URL || "http://localhost:8787";
107
+ API_TOKEN = process.env.HOOX_API_TOKEN || "";
108
+ WorkerAPIError = class WorkerAPIError extends Error {
109
+ status;
110
+ retryable;
111
+ constructor(message, options) {
112
+ super(message, { cause: options?.cause });
113
+ this.name = "WorkerAPIError";
114
+ this.status = options?.status ?? 0;
115
+ this.retryable = options?.retryable ?? false;
116
+ }
117
+ };
118
+ });
119
+
120
+ // src/sse.ts
121
+ var exports_sse = {};
122
+ __export(exports_sse, {
123
+ subscribeSSE: () => subscribeSSE
124
+ });
125
+ function subscribeSSE(path, callback, onStatus) {
126
+ let aborted = false;
127
+ const run = async () => {
128
+ let attempt = 0;
129
+ while (!aborted && attempt < MAX_RECONNECT_ATTEMPTS) {
130
+ try {
131
+ const url = `${API_BASE2}${path}`;
132
+ const response = await fetch(url, {
133
+ headers: {
134
+ Accept: "text/event-stream",
135
+ ...API_TOKEN2 ? { Authorization: `Bearer ${API_TOKEN2}` } : {}
136
+ }
137
+ });
138
+ if (!response.ok || !response.body) {
139
+ throw new Error(`SSE connection failed: HTTP ${response.status}`);
140
+ }
141
+ attempt = 0;
142
+ onStatus?.("connected");
143
+ const reader = response.body.getReader();
144
+ const decoder = new TextDecoder;
145
+ let buffer = "";
146
+ while (!aborted) {
147
+ const { done, value } = await reader.read();
148
+ if (done)
149
+ break;
150
+ buffer += decoder.decode(value, { stream: true });
151
+ const lines = buffer.split(`
152
+ `);
153
+ buffer = lines.pop() || "";
154
+ for (const line of lines) {
155
+ const trimmed = line.trim();
156
+ if (trimmed.startsWith("data: ")) {
157
+ try {
158
+ const parsed = JSON.parse(trimmed.slice(6));
159
+ callback(parsed);
160
+ } catch {}
161
+ }
162
+ }
163
+ }
164
+ } catch (error) {
165
+ if (aborted)
166
+ break;
167
+ onStatus?.("reconnecting");
168
+ const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, attempt), RECONNECT_MAX_MS);
169
+ attempt++;
170
+ await new Promise((resolve) => setTimeout(resolve, delay));
171
+ }
172
+ }
173
+ if (!aborted) {
174
+ onStatus?.("disconnected");
175
+ }
176
+ };
177
+ run();
178
+ return {
179
+ abort: () => {
180
+ aborted = true;
181
+ }
182
+ };
183
+ }
184
+ var API_BASE2, API_TOKEN2, RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
185
+ var init_sse = __esm(() => {
186
+ API_BASE2 = process.env.HOOX_API_URL || "http://localhost:8787";
187
+ API_TOKEN2 = process.env.HOOX_API_TOKEN || "";
188
+ });
189
+
190
+ // src/stores/service-store.ts
191
+ import { create } from "zustand";
192
+ import { immer } from "zustand/middleware/immer";
193
+ var BACKOFF_SEQUENCE = [1000, 2000, 4000, 8000, 16000];
194
+ var MAX_RETRIES2 = 5;
195
+ function ringPush(arr, item, max) {
196
+ const next = [...arr, item];
197
+ return next.length > max ? next.slice(next.length - max) : next;
198
+ }
199
+ var MAX_TRADES = 500;
200
+ var MAX_ALERTS = 100;
201
+ var MAX_LOGS = 1000;
202
+ var initialState = {
203
+ workers: [],
204
+ tradeStream: [],
205
+ alerts: [],
206
+ logs: [],
207
+ metrics: null,
208
+ connectionStatus: "offline",
209
+ lastUpdated: 0,
210
+ selectedWorkerId: null,
211
+ retryCount: 0,
212
+ lastError: null,
213
+ lastSuccessfulFetch: 0,
214
+ reconnectDelay: 0,
215
+ disconnectedAt: null
216
+ };
217
+ function getBackoffDelay(retryCount) {
218
+ if (retryCount <= 0)
219
+ return 0;
220
+ const index = Math.min(retryCount - 1, BACKOFF_SEQUENCE.length - 1);
221
+ return BACKOFF_SEQUENCE[index];
222
+ }
223
+ var useServiceStore = create()(immer((set, get) => ({
224
+ ...initialState,
225
+ fetchWorkers: async () => {
226
+ const { hooxFetch: hooxFetch2 } = await Promise.resolve().then(() => (init_api_client(), exports_api_client));
227
+ try {
228
+ const data = await hooxFetch2("/workers");
229
+ set((state) => {
230
+ state.workers = data;
231
+ state.lastUpdated = Date.now();
232
+ state.lastSuccessfulFetch = Date.now();
233
+ if (state.connectionStatus === "polling" || state.connectionStatus === "reconnecting" || state.connectionStatus === "offline") {
234
+ state.connectionStatus = "connected";
235
+ state.retryCount = 0;
236
+ state.lastError = null;
237
+ state.reconnectDelay = 0;
238
+ state.disconnectedAt = null;
239
+ }
240
+ });
241
+ } catch (error) {
242
+ const msg = error instanceof Error ? error.message : "Unknown fetch error";
243
+ set((state) => {
244
+ if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
245
+ state.connectionStatus = "reconnecting";
246
+ state.retryCount = state.retryCount + 1;
247
+ state.lastError = msg;
248
+ state.disconnectedAt = state.disconnectedAt ?? Date.now();
249
+ if (state.retryCount >= MAX_RETRIES2) {
250
+ state.connectionStatus = "offline";
251
+ state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
252
+ } else {
253
+ state.reconnectDelay = getBackoffDelay(state.retryCount);
254
+ }
255
+ }
256
+ if (state.connectionStatus === "reconnecting") {
257
+ state.lastError = msg;
258
+ }
259
+ });
260
+ }
261
+ },
262
+ streamTrades: async () => {
263
+ const { subscribeSSE: subscribeSSE2 } = await Promise.resolve().then(() => (init_sse(), exports_sse));
264
+ try {
265
+ await subscribeSSE2("/trades/stream", (trade) => {
266
+ get().pushTrade(trade);
267
+ });
268
+ } catch {}
269
+ },
270
+ streamLogs: async () => {
271
+ const { subscribeSSE: subscribeSSE2 } = await Promise.resolve().then(() => (init_sse(), exports_sse));
272
+ try {
273
+ await subscribeSSE2("/logs/stream", (log) => {
274
+ get().pushLog(log);
275
+ });
276
+ } catch {}
277
+ },
278
+ addAlert: (alert) => set((state) => {
279
+ state.alerts = ringPush(state.alerts, alert, MAX_ALERTS);
280
+ }),
281
+ addAlerts: (alerts) => set((state) => {
282
+ const merged = [...state.alerts, ...alerts];
283
+ state.alerts = merged.length > MAX_ALERTS ? merged.slice(merged.length - MAX_ALERTS) : merged;
284
+ }),
285
+ setConnectionStatus: (status) => set((state) => {
286
+ if (status === "connected") {
287
+ state.retryCount = 0;
288
+ state.lastError = null;
289
+ state.reconnectDelay = 0;
290
+ state.disconnectedAt = null;
291
+ }
292
+ if (status === "offline" || status === "reconnecting") {
293
+ state.disconnectedAt = state.disconnectedAt ?? Date.now();
294
+ }
295
+ state.connectionStatus = status;
296
+ }),
297
+ selectWorker: (id) => set((state) => {
298
+ state.selectedWorkerId = id;
299
+ }),
300
+ setWorkers: (workers) => set((state) => {
301
+ state.workers = workers;
302
+ state.lastUpdated = Date.now();
303
+ }),
304
+ pushTrade: (trade) => set((state) => {
305
+ state.tradeStream = ringPush(state.tradeStream, trade, MAX_TRADES);
306
+ }),
307
+ pushLog: (log) => set((state) => {
308
+ state.logs = ringPush(state.logs, log, MAX_LOGS);
309
+ }),
310
+ setMetrics: (metrics) => set((state) => {
311
+ state.metrics = metrics;
312
+ state.lastUpdated = metrics.lastUpdated;
313
+ }),
314
+ handleConnectionSuccess: () => set((state) => {
315
+ state.lastSuccessfulFetch = Date.now();
316
+ state.retryCount = 0;
317
+ state.lastError = null;
318
+ state.reconnectDelay = 0;
319
+ state.disconnectedAt = null;
320
+ if (state.connectionStatus === "reconnecting" || state.connectionStatus === "polling") {
321
+ state.connectionStatus = "connected";
322
+ }
323
+ }),
324
+ handleConnectionFailure: (errorMessage) => set((state) => {
325
+ state.lastError = errorMessage;
326
+ state.disconnectedAt = state.disconnectedAt ?? Date.now();
327
+ if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
328
+ state.connectionStatus = "reconnecting";
329
+ state.retryCount = 1;
330
+ state.reconnectDelay = getBackoffDelay(1);
331
+ } else if (state.connectionStatus === "reconnecting") {
332
+ state.retryCount = state.retryCount + 1;
333
+ if (state.retryCount >= MAX_RETRIES2) {
334
+ state.connectionStatus = "offline";
335
+ state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
336
+ state.reconnectDelay = 0;
337
+ } else {
338
+ state.reconnectDelay = getBackoffDelay(state.retryCount);
339
+ }
340
+ }
341
+ }),
342
+ resetRetries: () => set((state) => {
343
+ state.retryCount = 0;
344
+ state.lastError = null;
345
+ state.reconnectDelay = 0;
346
+ state.disconnectedAt = null;
347
+ }),
348
+ forceRetry: () => set((state) => {
349
+ if (state.connectionStatus === "offline") {
350
+ state.connectionStatus = "polling";
351
+ state.retryCount = 0;
352
+ state.lastError = null;
353
+ state.reconnectDelay = 0;
354
+ }
355
+ })
356
+ })));
357
+ export {
358
+ useServiceStore
359
+ };
@@ -0,0 +1,63 @@
1
+ import type { ViewId, ModalState } from "../types";
2
+ export interface UIState {
3
+ activeView: ViewId;
4
+ sidebarExpanded: boolean;
5
+ modal: ModalState | null;
6
+ commandPaletteOpen: boolean;
7
+ previousView: ViewId | null;
8
+ }
9
+ interface UIActions {
10
+ setView: (view: ViewId) => void;
11
+ toggleSidebar: () => void;
12
+ openPalette: () => void;
13
+ closePalette: () => void;
14
+ showModal: (modal: ModalState) => void;
15
+ dismissModal: () => void;
16
+ goBack: () => void;
17
+ }
18
+ export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<UIState & UIActions>, "setState"> & {
19
+ setState(nextStateOrUpdater: (UIState & UIActions) | Partial<UIState & UIActions> | ((state: {
20
+ activeView: ViewId;
21
+ sidebarExpanded: boolean;
22
+ modal: {
23
+ type: "confirm" | "alert" | "prompt" | "custom";
24
+ title: string;
25
+ message?: string | undefined;
26
+ data?: unknown;
27
+ onConfirm?: (() => void) | undefined;
28
+ onCancel?: (() => void) | undefined;
29
+ } | null;
30
+ commandPaletteOpen: boolean;
31
+ previousView: ViewId | null;
32
+ setView: (view: ViewId) => void;
33
+ toggleSidebar: () => void;
34
+ openPalette: () => void;
35
+ closePalette: () => void;
36
+ showModal: (modal: ModalState) => void;
37
+ dismissModal: () => void;
38
+ goBack: () => void;
39
+ }) => void), shouldReplace?: false): void;
40
+ setState(nextStateOrUpdater: (UIState & UIActions) | ((state: {
41
+ activeView: ViewId;
42
+ sidebarExpanded: boolean;
43
+ modal: {
44
+ type: "confirm" | "alert" | "prompt" | "custom";
45
+ title: string;
46
+ message?: string | undefined;
47
+ data?: unknown;
48
+ onConfirm?: (() => void) | undefined;
49
+ onCancel?: (() => void) | undefined;
50
+ } | null;
51
+ commandPaletteOpen: boolean;
52
+ previousView: ViewId | null;
53
+ setView: (view: ViewId) => void;
54
+ toggleSidebar: () => void;
55
+ openPalette: () => void;
56
+ closePalette: () => void;
57
+ showModal: (modal: ModalState) => void;
58
+ dismissModal: () => void;
59
+ goBack: () => void;
60
+ }) => void), shouldReplace: true): void;
61
+ }>;
62
+ export {};
63
+ //# sourceMappingURL=ui-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-store.d.ts","sourceRoot":"","sources":["../../src/stores/ui-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAInD,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAID,UAAU,SAAS;IACjB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAgBD,eAAO,MAAM,UAAU;;oBAjCT,MAAM;yBACD,OAAO;;;;;;;;;4BAEJ,OAAO;sBACb,MAAM,GAAG,IAAI;iBAMlB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;uBAChB,MAAM,IAAI;qBACZ,MAAM,IAAI;sBACT,MAAM,IAAI;mBACb,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;sBACxB,MAAM,IAAI;gBAChB,MAAM,IAAI;;;oBAhBN,MAAM;yBACD,OAAO;;;;;;;;;4BAEJ,OAAO;sBACb,MAAM,GAAG,IAAI;iBAMlB,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;uBAChB,MAAM,IAAI;qBACZ,MAAM,IAAI;sBACT,MAAM,IAAI;mBACb,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;sBACxB,MAAM,IAAI;gBAChB,MAAM,IAAI;;EAiEnB,CAAC"}