@hoox-sh/hoox-shared 1.2.1 → 1.3.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.
@@ -277,6 +277,23 @@ function ringPush(arr, item, max) {
277
277
  var tradeStreamAbort = null;
278
278
  var logStreamAbort = null;
279
279
  var fetchWorkersGeneration = 0;
280
+ var reconnectTimer = null;
281
+ function clearReconnectTimer() {
282
+ if (reconnectTimer !== null) {
283
+ clearTimeout(reconnectTimer);
284
+ reconnectTimer = null;
285
+ }
286
+ }
287
+ function scheduleReconnect(delayMs) {
288
+ clearReconnectTimer();
289
+ if (delayMs < 0)
290
+ return;
291
+ reconnectTimer = setTimeout(() => {
292
+ reconnectTimer = null;
293
+ scheduledReconnectTick();
294
+ }, delayMs);
295
+ }
296
+ var scheduledReconnectTick = async () => {};
280
297
  var MAX_TRADES = 500;
281
298
  var MAX_ALERTS = 100;
282
299
  var MAX_LOGS = 1000;
@@ -311,6 +328,7 @@ var useServiceStore = create()(immer((set, get) => ({
311
328
  const data = await hooxFetch2("/v1/workers");
312
329
  if (gen !== fetchWorkersGeneration)
313
330
  return;
331
+ clearReconnectTimer();
314
332
  set((state) => {
315
333
  state.workers = data;
316
334
  state.lastUpdated = Date.now();
@@ -336,6 +354,7 @@ var useServiceStore = create()(immer((set, get) => ({
336
354
  timestamp: Date.now(),
337
355
  acknowledged: false
338
356
  });
357
+ let nextDelay = 0;
339
358
  set((state) => {
340
359
  state.lastError = msg;
341
360
  state.disconnectedAt = state.disconnectedAt ?? Date.now();
@@ -344,21 +363,26 @@ var useServiceStore = create()(immer((set, get) => ({
344
363
  state.retryCount = state.retryCount + 1;
345
364
  if (state.retryCount >= MAX_RETRIES2) {
346
365
  state.connectionStatus = "offline";
347
- state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
366
+ state.lastError = `${msg} (gave up after ${MAX_RETRIES2} retries)`;
367
+ state.reconnectDelay = 0;
348
368
  } else {
349
369
  state.reconnectDelay = getBackoffDelay(state.retryCount);
370
+ nextDelay = state.reconnectDelay;
350
371
  }
351
372
  } else if (state.connectionStatus === "reconnecting") {
352
373
  state.retryCount = state.retryCount + 1;
353
374
  if (state.retryCount >= MAX_RETRIES2) {
354
375
  state.connectionStatus = "offline";
355
- state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
376
+ state.lastError = `${msg} (gave up after ${MAX_RETRIES2} retries)`;
356
377
  state.reconnectDelay = 0;
357
378
  } else {
358
379
  state.reconnectDelay = getBackoffDelay(state.retryCount);
380
+ nextDelay = state.reconnectDelay;
359
381
  }
360
382
  }
361
383
  });
384
+ if (nextDelay > 0)
385
+ scheduleReconnect(nextDelay);
362
386
  }
363
387
  },
364
388
  streamTrades: async () => {
@@ -420,6 +444,17 @@ var useServiceStore = create()(immer((set, get) => ({
420
444
  const merged = [...state.alerts, ...alerts];
421
445
  state.alerts = merged.length > MAX_ALERTS ? merged.slice(merged.length - MAX_ALERTS) : merged;
422
446
  }),
447
+ acknowledgeAlert: (id) => set((state) => {
448
+ const hit = state.alerts.find((a) => a.id === id);
449
+ if (hit)
450
+ hit.acknowledged = true;
451
+ }),
452
+ dismissAlert: (id) => set((state) => {
453
+ state.alerts = state.alerts.filter((a) => a.id !== id);
454
+ }),
455
+ clearLogs: () => set((state) => {
456
+ state.logs = [];
457
+ }),
423
458
  setConnectionStatus: (status) => set((state) => {
424
459
  if (status === "connected") {
425
460
  state.retryCount = 0;
@@ -450,49 +485,69 @@ var useServiceStore = create()(immer((set, get) => ({
450
485
  state.metrics = metrics;
451
486
  state.lastUpdated = metrics.lastUpdated;
452
487
  }),
453
- handleConnectionSuccess: () => set((state) => {
454
- state.lastSuccessfulFetch = Date.now();
455
- state.retryCount = 0;
456
- state.lastError = null;
457
- state.lastErrorDetails = null;
458
- state.reconnectDelay = 0;
459
- state.disconnectedAt = null;
460
- state.connectionStatus = "connected";
461
- }),
462
- handleConnectionFailure: (errorMessage) => set((state) => {
463
- state.lastError = errorMessage;
464
- state.disconnectedAt = state.disconnectedAt ?? Date.now();
465
- if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
466
- state.connectionStatus = "reconnecting";
467
- state.retryCount = 1;
468
- state.reconnectDelay = getBackoffDelay(1);
469
- } else if (state.connectionStatus === "reconnecting") {
470
- state.retryCount = state.retryCount + 1;
471
- if (state.retryCount >= MAX_RETRIES2) {
472
- state.connectionStatus = "offline";
473
- state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
474
- state.reconnectDelay = 0;
475
- } else {
476
- state.reconnectDelay = getBackoffDelay(state.retryCount);
488
+ handleConnectionSuccess: () => {
489
+ clearReconnectTimer();
490
+ set((state) => {
491
+ state.lastSuccessfulFetch = Date.now();
492
+ state.retryCount = 0;
493
+ state.lastError = null;
494
+ state.lastErrorDetails = null;
495
+ state.reconnectDelay = 0;
496
+ state.disconnectedAt = null;
497
+ state.connectionStatus = "connected";
498
+ });
499
+ },
500
+ handleConnectionFailure: (errorMessage) => {
501
+ let nextDelay = 0;
502
+ set((state) => {
503
+ state.lastError = errorMessage;
504
+ state.disconnectedAt = state.disconnectedAt ?? Date.now();
505
+ if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
506
+ state.connectionStatus = "reconnecting";
507
+ state.retryCount = 1;
508
+ state.reconnectDelay = getBackoffDelay(1);
509
+ nextDelay = state.reconnectDelay;
510
+ } else if (state.connectionStatus === "reconnecting") {
511
+ state.retryCount = state.retryCount + 1;
512
+ if (state.retryCount >= MAX_RETRIES2) {
513
+ state.connectionStatus = "offline";
514
+ state.lastError = `${errorMessage} (gave up after ${MAX_RETRIES2} retries)`;
515
+ state.reconnectDelay = 0;
516
+ } else {
517
+ state.reconnectDelay = getBackoffDelay(state.retryCount);
518
+ nextDelay = state.reconnectDelay;
519
+ }
477
520
  }
478
- }
479
- }),
480
- resetRetries: () => set((state) => {
481
- state.retryCount = 0;
482
- state.lastError = null;
483
- state.lastErrorDetails = null;
484
- state.reconnectDelay = 0;
485
- state.disconnectedAt = null;
486
- }),
487
- forceRetry: () => set((state) => {
488
- if (state.connectionStatus === "offline") {
489
- state.connectionStatus = "polling";
521
+ });
522
+ if (nextDelay > 0)
523
+ scheduleReconnect(nextDelay);
524
+ },
525
+ resetRetries: () => {
526
+ clearReconnectTimer();
527
+ set((state) => {
490
528
  state.retryCount = 0;
491
529
  state.lastError = null;
492
530
  state.lastErrorDetails = null;
493
531
  state.reconnectDelay = 0;
532
+ state.disconnectedAt = null;
533
+ });
534
+ },
535
+ forceRetry: () => {
536
+ clearReconnectTimer();
537
+ const wasOffline = get().connectionStatus === "offline";
538
+ set((state) => {
539
+ if (state.connectionStatus === "offline") {
540
+ state.connectionStatus = "polling";
541
+ state.retryCount = 0;
542
+ state.lastError = null;
543
+ state.lastErrorDetails = null;
544
+ state.reconnectDelay = 0;
545
+ }
546
+ });
547
+ if (wasOffline) {
548
+ scheduleReconnect(0);
494
549
  }
495
- }),
550
+ },
496
551
  setLastErrorDetails: (details) => set((state) => {
497
552
  state.lastErrorDetails = details;
498
553
  state.lastError = details === null ? null : summarizeCliError(details);
@@ -515,6 +570,12 @@ var useServiceStore = create()(immer((set, get) => ({
515
570
  }, MAX_ALERTS);
516
571
  })
517
572
  })));
573
+ scheduledReconnectTick = async () => {
574
+ const status = useServiceStore.getState().connectionStatus;
575
+ if (status === "reconnecting" || status === "polling") {
576
+ await useServiceStore.getState().fetchWorkers();
577
+ }
578
+ };
518
579
  export {
519
580
  useServiceStore
520
581
  };
@@ -9,6 +9,11 @@ export interface UIState {
9
9
  modal: ModalState | null;
10
10
  commandPaletteOpen: boolean;
11
11
  previousView: ViewId | null;
12
+ /**
13
+ * Whether the status-bar CLI/connection diagnostics panel is expanded.
14
+ * Shared so palette / Ctrl+Shift+D / mouse can all toggle it.
15
+ */
16
+ statusErrorExpanded: boolean;
12
17
  }
13
18
  interface UIActions {
14
19
  setView: (view: ViewId) => void;
@@ -18,6 +23,9 @@ interface UIActions {
18
23
  showModal: (modal: ModalState) => void;
19
24
  dismissModal: () => void;
20
25
  goBack: () => void;
26
+ /** Toggle status-bar error diagnostics panel. */
27
+ toggleStatusErrorExpanded: () => void;
28
+ setStatusErrorExpanded: (expanded: boolean) => void;
21
29
  }
22
30
  export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<UIState & UIActions>, "setState"> & {
23
31
  setState(nextStateOrUpdater: (UIState & UIActions) | Partial<UIState & UIActions> | ((state: {
@@ -33,6 +41,7 @@ export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zu
33
41
  } | null;
34
42
  commandPaletteOpen: boolean;
35
43
  previousView: ViewId | null;
44
+ statusErrorExpanded: boolean;
36
45
  setView: (view: ViewId) => void;
37
46
  toggleSidebar: () => void;
38
47
  openPalette: () => void;
@@ -40,6 +49,8 @@ export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zu
40
49
  showModal: (modal: ModalState) => void;
41
50
  dismissModal: () => void;
42
51
  goBack: () => void;
52
+ toggleStatusErrorExpanded: () => void;
53
+ setStatusErrorExpanded: (expanded: boolean) => void;
43
54
  }) => void), shouldReplace?: false): void;
44
55
  setState(nextStateOrUpdater: (UIState & UIActions) | ((state: {
45
56
  activeView: ViewId;
@@ -54,6 +65,7 @@ export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zu
54
65
  } | null;
55
66
  commandPaletteOpen: boolean;
56
67
  previousView: ViewId | null;
68
+ statusErrorExpanded: boolean;
57
69
  setView: (view: ViewId) => void;
58
70
  toggleSidebar: () => void;
59
71
  openPalette: () => void;
@@ -61,6 +73,8 @@ export declare const useUIStore: import("zustand").UseBoundStore<Omit<import("zu
61
73
  showModal: (modal: ModalState) => void;
62
74
  dismissModal: () => void;
63
75
  goBack: () => void;
76
+ toggleStatusErrorExpanded: () => void;
77
+ setStatusErrorExpanded: (expanded: boolean) => void;
64
78
  }) => void), shouldReplace: true): void;
65
79
  }>;
66
80
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ui-store.d.ts","sourceRoot":"","sources":["../../src/stores/ui-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,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"}
1
+ {"version":3,"file":"ui-store.d.ts","sourceRoot":"","sources":["../../src/stores/ui-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,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;IAC5B;;;OAGG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;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;IACnB,iDAAiD;IACjD,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,sBAAsB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAiBD,eAAO,MAAM,UAAU;;oBA1CT,MAAM;yBACD,OAAO;;;;;;;;;4BAEJ,OAAO;sBACb,MAAM,GAAG,IAAI;6BAKN,OAAO;iBAMnB,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;mCAES,MAAM,IAAI;gCACb,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI;;;oBAxBvC,MAAM;yBACD,OAAO;;;;;;;;;4BAEJ,OAAO;sBACb,MAAM,GAAG,IAAI;6BAKN,OAAO;iBAMnB,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;mCAES,MAAM,IAAI;gCACb,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI;;EA4EpD,CAAC"}
@@ -24,7 +24,8 @@ var initialState = {
24
24
  sidebarExpanded: true,
25
25
  modal: null,
26
26
  commandPaletteOpen: false,
27
- previousView: null
27
+ previousView: null,
28
+ statusErrorExpanded: false
28
29
  };
29
30
  var useUIStore = create()(immer((set) => ({
30
31
  ...initialState,
@@ -55,6 +56,12 @@ var useUIStore = create()(immer((set) => ({
55
56
  state.activeView = state.previousView;
56
57
  state.previousView = null;
57
58
  }
59
+ }),
60
+ toggleStatusErrorExpanded: () => set((state) => {
61
+ state.statusErrorExpanded = !state.statusErrorExpanded;
62
+ }),
63
+ setStatusErrorExpanded: (expanded) => set((state) => {
64
+ state.statusErrorExpanded = expanded;
58
65
  })
59
66
  })));
60
67
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoox-sh/hoox-shared",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Shared utilities, types, and stores for HOOX CLI, TUI, and Cloudflare Workers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",