@oh-my-pi/omp-stats 18.14.2 → 18.16.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/omp-stats",
4
- "version": "18.14.2",
4
+ "version": "18.16.0",
5
5
  "description": "Local observability dashboard for omp AI usage statistics",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -42,9 +42,9 @@
42
42
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
43
43
  },
44
44
  "dependencies": {
45
- "@oh-my-pi/pi-ai": "18.14.2",
46
- "@oh-my-pi/pi-catalog": "18.14.2",
47
- "@oh-my-pi/pi-utils": "18.14.2",
45
+ "@oh-my-pi/pi-ai": "18.16.0",
46
+ "@oh-my-pi/pi-catalog": "18.16.0",
47
+ "@oh-my-pi/pi-utils": "18.16.0",
48
48
  "lucide-react": "^1.24.0",
49
49
  "react": "19.2.7",
50
50
  "react-dom": "19.2.7"
@@ -89,10 +89,6 @@
89
89
  "types": "./dist/types/client/*.d.ts",
90
90
  "import": "./src/client/*.ts"
91
91
  },
92
- "./client/components/*": {
93
- "types": "./dist/types/client/components/*.d.ts",
94
- "import": "./src/client/components/*.ts"
95
- },
96
92
  "./*.js": "./src/*.ts"
97
93
  }
98
94
  }
@@ -15,6 +15,7 @@
15
15
  * can resolve to that same object; an unchanged reference does not re-render.
16
16
  */
17
17
 
18
+ import { toError } from "@oh-my-pi/pi-utils/type-guards";
18
19
  import { startTransition, useCallback, useEffect, useReducer, useRef, useState } from "react";
19
20
  import { currentLiveVersion, useLiveVersion } from "./live";
20
21
  import { cachedEntry, type Inflight, inflightRequest, loadQuery, type QueryFetcher, releaseQuery } from "./query-store";
@@ -60,6 +61,7 @@ export function useQuery<T>(key: readonly unknown[], fetcher: QueryFetcher<T>, o
60
61
  const version = useLiveVersion(enabled);
61
62
 
62
63
  const [, rerender] = useReducer((n: number) => n + 1, 0);
64
+ const [settled, recheck] = useReducer((n: number) => n + 1, 0);
63
65
  const [error, setError] = useState<{ key: string; error: Error } | null>(null);
64
66
  const [refreshing, setRefreshing] = useState(false);
65
67
 
@@ -82,8 +84,10 @@ export function useQuery<T>(key: readonly unknown[], fetcher: QueryFetcher<T>, o
82
84
  waiting.current = { key: targetKey, request };
83
85
  // Join the new request before leaving the old one, so a shared request isn't aborted in between.
84
86
  if (current) releaseQuery(current.key, current.request);
87
+ let succeeded = false;
85
88
  request.promise
86
89
  .then(data => {
90
+ succeeded = true;
87
91
  if (keyRef.current !== targetKey) return;
88
92
  // Revalidated to the very same object: nothing on screen changes.
89
93
  if (data === shownBefore) {
@@ -97,11 +101,15 @@ export function useQuery<T>(key: readonly unknown[], fetcher: QueryFetcher<T>, o
97
101
  })
98
102
  .catch((err: unknown) => {
99
103
  if (keyRef.current !== targetKey || request.controller.signal.aborted) return;
100
- setError({ key: targetKey, error: err instanceof Error ? err : new Error(String(err)) });
104
+ setError({ key: targetKey, error: toError(err) });
101
105
  })
102
106
  .finally(() => {
103
- if (waiting.current?.request === request) waiting.current = null;
104
- if (keyRef.current === targetKey) setRefreshing(false);
107
+ if (waiting.current?.request !== request) return;
108
+ waiting.current = null;
109
+ if (keyRef.current !== targetKey) return;
110
+ setRefreshing(false);
111
+ // A newer live version may have arrived while this older request was in flight.
112
+ if (succeeded && (cachedEntry(targetKey)?.version ?? -1) < currentLiveVersion()) recheck();
105
113
  });
106
114
  }, []);
107
115
 
@@ -128,7 +136,7 @@ export function useQuery<T>(key: readonly unknown[], fetcher: QueryFetcher<T>, o
128
136
  }
129
137
  const timer = setTimeout(() => run(keyString), wait);
130
138
  return () => clearTimeout(timer);
131
- }, [keyString, enabled, version, run, leave]);
139
+ }, [keyString, enabled, version, settled, run, leave]);
132
140
 
133
141
  useEffect(() => {
134
142
  if (!enabled || !pollMs) return;
@@ -4,6 +4,7 @@
4
4
  * + text blocks, tool args/result, user text, subagent task text.
5
5
  */
6
6
 
7
+ import { toError } from "@oh-my-pi/pi-utils/type-guards";
7
8
  import { Check, Copy, X } from "lucide-react";
8
9
  import { type ReactNode, useEffect, useRef, useState } from "react";
9
10
  import { getSessionEntryDetail } from "../api";
@@ -112,7 +113,7 @@ export function SpanDrawer({
112
113
  })
113
114
  .catch(err => {
114
115
  if (controller.signal.aborted) return;
115
- setError(err instanceof Error ? err : new Error(String(err)));
116
+ setError(toError(err));
116
117
  })
117
118
  .finally(() => {
118
119
  if (!controller.signal.aborted) setLoading(false);