@svadmin/ui 0.41.1 → 0.42.1
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { captureAdminContext } from '@svadmin/core';
|
|
2
|
+
import { captureAdminContext, queryKeyMatches } from '@svadmin/core';
|
|
3
3
|
import { useQueryClient } from '@tanstack/svelte-query';
|
|
4
4
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
5
|
|
|
@@ -39,13 +39,19 @@
|
|
|
39
39
|
await taskProvider.cancel(taskId);
|
|
40
40
|
await Promise.all([
|
|
41
41
|
queryClient.invalidateQueries({
|
|
42
|
-
predicate: (query) => query.queryKey
|
|
43
|
-
|
|
42
|
+
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
43
|
+
...adminContext.queryKeyMatcher(),
|
|
44
|
+
kind: 'task',
|
|
45
|
+
action: 'list',
|
|
46
|
+
}),
|
|
44
47
|
}),
|
|
45
48
|
queryClient.invalidateQueries({
|
|
46
|
-
predicate: (query) => query.queryKey
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
50
|
+
...adminContext.queryKeyMatcher(),
|
|
51
|
+
kind: 'task',
|
|
52
|
+
action: 'one',
|
|
53
|
+
id: taskId,
|
|
54
|
+
}),
|
|
49
55
|
}),
|
|
50
56
|
]);
|
|
51
57
|
onSuccess?.();
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
3
4
|
import { fly } from 'svelte/transition';
|
|
4
5
|
import { useQueryClient } from '@tanstack/svelte-query';
|
|
5
6
|
import {
|
|
6
7
|
captureAdminContext,
|
|
7
8
|
getColorTheme,
|
|
8
9
|
getTheme,
|
|
10
|
+
parseQueryKey,
|
|
9
11
|
} from '@svadmin/core';
|
|
10
12
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
11
13
|
|
|
@@ -28,6 +30,17 @@
|
|
|
28
30
|
capabilities: string;
|
|
29
31
|
};
|
|
30
32
|
|
|
33
|
+
type SafeQueryDiagnostic = {
|
|
34
|
+
provider: string;
|
|
35
|
+
resource: string;
|
|
36
|
+
operation: string;
|
|
37
|
+
status: string;
|
|
38
|
+
retries: number;
|
|
39
|
+
duration: string;
|
|
40
|
+
cacheAge: string;
|
|
41
|
+
invalidation: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
31
44
|
const EMPTY_CACHE_DIAGNOSTICS: CacheDiagnostics = {
|
|
32
45
|
queries: { total: 0, fetching: 0, stale: 0, errors: 0 },
|
|
33
46
|
mutations: { total: 0, pending: 0, paused: 0, errors: 0 },
|
|
@@ -42,6 +55,8 @@
|
|
|
42
55
|
let visible = $state(false);
|
|
43
56
|
let collapsed = $state(false);
|
|
44
57
|
let cacheDiagnostics = $state.raw<CacheDiagnostics>(EMPTY_CACHE_DIAGNOSTICS);
|
|
58
|
+
let safeQueryDiagnostics = $state.raw<SafeQueryDiagnostic[]>([]);
|
|
59
|
+
const queryTimings = new SvelteMap<string, { startedAt?: number; duration?: number }>();
|
|
45
60
|
|
|
46
61
|
function toggle() {
|
|
47
62
|
visible = !visible;
|
|
@@ -57,6 +72,7 @@
|
|
|
57
72
|
function refreshCacheDiagnostics() {
|
|
58
73
|
const queries = queryClient.getQueryCache().getAll();
|
|
59
74
|
const mutations = queryClient.getMutationCache().getAll();
|
|
75
|
+
const now = Date.now();
|
|
60
76
|
cacheDiagnostics = {
|
|
61
77
|
queries: {
|
|
62
78
|
total: queries.length,
|
|
@@ -71,12 +87,47 @@
|
|
|
71
87
|
errors: mutations.filter((mutation) => mutation.state.status === 'error').length,
|
|
72
88
|
},
|
|
73
89
|
};
|
|
90
|
+
safeQueryDiagnostics = queries.flatMap((query) => {
|
|
91
|
+
const descriptor = parseQueryKey(query.queryKey);
|
|
92
|
+
if (!descriptor) return [];
|
|
93
|
+
return [{
|
|
94
|
+
provider: descriptor.provider,
|
|
95
|
+
resource: descriptor.kind === 'custom' ? '[custom]' : (descriptor.resource ?? '—'),
|
|
96
|
+
operation: `${descriptor.kind}:${descriptor.action ?? 'call'}`,
|
|
97
|
+
status: query.state.fetchStatus === 'fetching' ? 'fetching' : query.state.status,
|
|
98
|
+
retries: query.state.fetchFailureCount,
|
|
99
|
+
duration: queryTimings.get(query.queryHash)?.duration === undefined
|
|
100
|
+
? '—'
|
|
101
|
+
: `${queryTimings.get(query.queryHash)?.duration}ms`,
|
|
102
|
+
cacheAge: query.state.dataUpdatedAt > 0
|
|
103
|
+
? `${Math.max(0, Math.floor((now - query.state.dataUpdatedAt) / 1000))}s`
|
|
104
|
+
: '—',
|
|
105
|
+
invalidation: query.state.isInvalidated ? 'cache invalidation' : '—',
|
|
106
|
+
}];
|
|
107
|
+
});
|
|
74
108
|
}
|
|
75
109
|
|
|
76
110
|
onMount(() => {
|
|
77
111
|
if (!isDev) return;
|
|
78
112
|
refreshCacheDiagnostics();
|
|
79
|
-
const unsubscribeQueries = queryClient.getQueryCache().subscribe(
|
|
113
|
+
const unsubscribeQueries = queryClient.getQueryCache().subscribe((event) => {
|
|
114
|
+
// 观察器事件会在组件重渲染时成对出现。刷新诊断状态会再次触发渲染,
|
|
115
|
+
// 因此这里只响应真正改变缓存内容或请求状态的事件。
|
|
116
|
+
if (event.type.startsWith('observer')) return;
|
|
117
|
+
if (event.type === 'removed') {
|
|
118
|
+
queryTimings.delete(event.query.queryHash);
|
|
119
|
+
} else if (event.type === 'updated') {
|
|
120
|
+
const current = queryTimings.get(event.query.queryHash) ?? {};
|
|
121
|
+
if (event.action.type === 'fetch') {
|
|
122
|
+
queryTimings.set(event.query.queryHash, { startedAt: Date.now() });
|
|
123
|
+
} else if ((event.action.type === 'success' || event.action.type === 'error') && current.startedAt !== undefined) {
|
|
124
|
+
queryTimings.set(event.query.queryHash, {
|
|
125
|
+
duration: Math.max(0, Date.now() - current.startedAt),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
refreshCacheDiagnostics();
|
|
130
|
+
});
|
|
80
131
|
const unsubscribeMutations = queryClient.getMutationCache().subscribe(refreshCacheDiagnostics);
|
|
81
132
|
return () => {
|
|
82
133
|
unsubscribeQueries();
|
|
@@ -342,6 +393,34 @@
|
|
|
342
393
|
</dl>
|
|
343
394
|
</section>
|
|
344
395
|
|
|
396
|
+
<section class="space-y-1.5 sm:col-span-2">
|
|
397
|
+
<div class="flex items-center justify-between">
|
|
398
|
+
<h4 class="text-xs font-semibold text-foreground">Safe query operations</h4>
|
|
399
|
+
<span class="text-[0.6875rem] text-muted-foreground">IDs, tenant and params hidden</span>
|
|
400
|
+
</div>
|
|
401
|
+
{#if safeQueryDiagnostics.length === 0}
|
|
402
|
+
<div class="rounded-md border px-2.5 py-2 text-[0.6875rem] text-muted-foreground">
|
|
403
|
+
No Query Key v2 operations in cache.
|
|
404
|
+
</div>
|
|
405
|
+
{:else}
|
|
406
|
+
{#each safeQueryDiagnostics as query, index (`${query.provider}:${query.resource}:${query.operation}:${index}`)}
|
|
407
|
+
<div class="rounded-md border px-2.5 py-2" data-testid="devtools-query-operation">
|
|
408
|
+
<div class="flex flex-wrap items-center justify-between gap-2 text-xs">
|
|
409
|
+
<span class="font-medium text-foreground">{query.provider} · {query.resource}</span>
|
|
410
|
+
<Badge variant="outline">{query.operation}</Badge>
|
|
411
|
+
</div>
|
|
412
|
+
<dl class="mt-1 grid grid-cols-5 gap-2 text-[0.6875rem] text-muted-foreground">
|
|
413
|
+
<div><dt>Status</dt><dd class="text-foreground">{query.status}</dd></div>
|
|
414
|
+
<div><dt>Retries</dt><dd class="text-foreground">{query.retries}</dd></div>
|
|
415
|
+
<div><dt>Duration</dt><dd class="text-foreground">{query.duration}</dd></div>
|
|
416
|
+
<div><dt>Cache age</dt><dd class="text-foreground">{query.cacheAge}</dd></div>
|
|
417
|
+
<div><dt>Invalidation</dt><dd class="text-foreground">{query.invalidation}</dd></div>
|
|
418
|
+
</dl>
|
|
419
|
+
</div>
|
|
420
|
+
{/each}
|
|
421
|
+
{/if}
|
|
422
|
+
</section>
|
|
423
|
+
|
|
345
424
|
<p class="text-[0.6875rem] text-muted-foreground sm:col-span-2">
|
|
346
425
|
Query keys, variables, cached records, mutation payloads, and error bodies are intentionally hidden.
|
|
347
426
|
</p>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { captureAdminContext } from '@svadmin/core';
|
|
2
|
+
import { captureAdminContext, queryKeyMatches } from '@svadmin/core';
|
|
3
3
|
import { useQueryClient } from '@tanstack/svelte-query';
|
|
4
4
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
5
|
|
|
@@ -39,13 +39,19 @@
|
|
|
39
39
|
await taskProvider.retry(taskId);
|
|
40
40
|
await Promise.all([
|
|
41
41
|
queryClient.invalidateQueries({
|
|
42
|
-
predicate: (query) => query.queryKey
|
|
43
|
-
|
|
42
|
+
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
43
|
+
...adminContext.queryKeyMatcher(),
|
|
44
|
+
kind: 'task',
|
|
45
|
+
action: 'list',
|
|
46
|
+
}),
|
|
44
47
|
}),
|
|
45
48
|
queryClient.invalidateQueries({
|
|
46
|
-
predicate: (query) => query.queryKey
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
50
|
+
...adminContext.queryKeyMatcher(),
|
|
51
|
+
kind: 'task',
|
|
52
|
+
action: 'one',
|
|
53
|
+
id: taskId,
|
|
54
|
+
}),
|
|
49
55
|
}),
|
|
50
56
|
]);
|
|
51
57
|
onSuccess?.();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.1",
|
|
4
4
|
"description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./components/*": "./dist/components/*"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@svadmin/core": "^0.
|
|
42
|
+
"@svadmin/core": "^0.36.1",
|
|
43
43
|
"@tanstack/svelte-query": "^6.1.38",
|
|
44
44
|
"highlight.js": "^11.11.1",
|
|
45
45
|
"isomorphic-dompurify": "^3.22.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
77
|
"@floating-ui/dom": "^1.8.0",
|
|
78
|
-
"@lucide/svelte": "^1.
|
|
78
|
+
"@lucide/svelte": "^1.31.0",
|
|
79
79
|
"@tanstack/svelte-store": "^0.12.1",
|
|
80
80
|
"@tanstack/svelte-table": "9.1.2",
|
|
81
81
|
"@tanstack/table-core": "9.1.2",
|
|
@@ -89,19 +89,19 @@
|
|
|
89
89
|
"author": "zuohuadong",
|
|
90
90
|
"repository": {
|
|
91
91
|
"type": "git",
|
|
92
|
-
"url": "https://github.com/
|
|
92
|
+
"url": "https://github.com/vibeunion/svadmin.git",
|
|
93
93
|
"directory": "packages/ui"
|
|
94
94
|
},
|
|
95
|
-
"homepage": "https://github.com/
|
|
95
|
+
"homepage": "https://github.com/vibeunion/svadmin#readme",
|
|
96
96
|
"bugs": {
|
|
97
|
-
"url": "https://github.com/
|
|
97
|
+
"url": "https://github.com/vibeunion/svadmin/issues"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@happy-dom/global-registrator": "^20.11.2",
|
|
101
101
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
102
102
|
"@testing-library/dom": "^10.4.1",
|
|
103
103
|
"@testing-library/svelte": "^5.4.2",
|
|
104
|
-
"@testing-library/user-event": "^14.6.
|
|
104
|
+
"@testing-library/user-event": "^14.6.4",
|
|
105
105
|
"happy-dom": "^20.11.2",
|
|
106
106
|
"vitest": "^4.1.10"
|
|
107
107
|
},
|