@gscdump/nuxt 0.22.4 → 1.5.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.
Files changed (66) hide show
  1. package/README.md +10 -137
  2. package/dist/module.d.mts +22 -0
  3. package/dist/module.mjs +30 -0
  4. package/dist/runtime-config.d.mts +12 -0
  5. package/dist/runtime-config.mjs +27 -0
  6. package/package.json +26 -176
  7. package/app/assets/css/main.css +0 -2
  8. package/app/components/GscAnalyzerPanel.vue +0 -94
  9. package/app/components/GscAnonymizationBanner.vue +0 -46
  10. package/app/components/GscBootProgress.vue +0 -297
  11. package/app/components/GscCommandPalette.vue +0 -77
  12. package/app/components/GscDashboardPage.vue +0 -26
  13. package/app/components/GscEngineBadge.vue +0 -28
  14. package/app/components/GscHero.vue +0 -214
  15. package/app/components/GscLazyTopResult.vue +0 -47
  16. package/app/components/GscPerformanceChart.vue +0 -532
  17. package/app/components/GscPositionDistributionChart.vue +0 -63
  18. package/app/components/GscQueryLabel.vue +0 -63
  19. package/app/components/GscSitePageHeader.vue +0 -40
  20. package/app/components/GscSourceDebugPanel.vue +0 -196
  21. package/app/components/GscSyncStatusCell.vue +0 -54
  22. package/app/components/GscTopRollupCard.vue +0 -90
  23. package/app/composables/_useGscBackfill.ts +0 -119
  24. package/app/composables/_useGscQueryDispatcher.ts +0 -123
  25. package/app/composables/_useGscResource.ts +0 -202
  26. package/app/composables/_useGscSharedSiteResource.ts +0 -136
  27. package/app/composables/useGscAnalytics.ts +0 -206
  28. package/app/composables/useGscAnalyticsClient.ts +0 -9
  29. package/app/composables/useGscAnalyticsConfig.ts +0 -8
  30. package/app/composables/useGscAnalyticsSourceInfo.ts +0 -165
  31. package/app/composables/useGscAnalyzer.ts +0 -460
  32. package/app/composables/useGscAnalyzerBatch.ts +0 -106
  33. package/app/composables/useGscAnalyzerDefs.ts +0 -119
  34. package/app/composables/useGscAuth.ts +0 -115
  35. package/app/composables/useGscConsoleUrl.ts +0 -45
  36. package/app/composables/useGscCountries.ts +0 -46
  37. package/app/composables/useGscCurrentSite.ts +0 -36
  38. package/app/composables/useGscInspectionHistory.ts +0 -42
  39. package/app/composables/useGscInspections.ts +0 -52
  40. package/app/composables/useGscPanelContext.ts +0 -31
  41. package/app/composables/useGscPeriod.ts +0 -243
  42. package/app/composables/useGscQuery.ts +0 -301
  43. package/app/composables/useGscQueryDispatcher.ts +0 -14
  44. package/app/composables/useGscRequestIndexingInspect.ts +0 -29
  45. package/app/composables/useGscResource.ts +0 -11
  46. package/app/composables/useGscRollup.ts +0 -237
  47. package/app/composables/useGscSearchAppearance.ts +0 -46
  48. package/app/composables/useGscSiteAnalyzer.ts +0 -456
  49. package/app/composables/useGscSitemapHistory.ts +0 -41
  50. package/app/composables/useGscSitemaps.ts +0 -45
  51. package/app/composables/useGscTableState.ts +0 -119
  52. package/app/plugins/analytics.ts +0 -57
  53. package/app/queries/gsc.ts +0 -163
  54. package/app/utils/anonymization.ts +0 -24
  55. package/app/utils/country-names.ts +0 -56
  56. package/app/utils/duckdb-wasm.ts +0 -166
  57. package/app/utils/gsc-constants.ts +0 -10
  58. package/app/utils/gsc-error.ts +0 -58
  59. package/app/utils/gsc-fetch.ts +0 -94
  60. package/app/utils/gsc-filters.ts +0 -32
  61. package/app/utils/gsc-rows.ts +0 -72
  62. package/app/utils/gsc-rpc.ts +0 -18
  63. package/app/utils/position.ts +0 -7
  64. package/module.ts +0 -81
  65. package/nuxt.config.ts +0 -61
  66. package/types.ts +0 -115
@@ -1,106 +0,0 @@
1
- // Run N analyzers in parallel against a single analyzer runner, with per-id
2
- // status tracking, optional concurrency cap, and stale-token discard so
3
- // late-completing tasks from a superseded `run()` don't overwrite fresher state.
4
- //
5
- // Caller owns: result shaping (summarize, scoring), the trigger watcher,
6
- // and which ids to run.
7
-
8
- import type { MaybeRefOrGetter, Ref } from '@vue/runtime-core'
9
-
10
- export type GscAnalyzerBatchStatus = 'idle' | 'pending' | 'running' | 'done' | 'error' | 'skipped'
11
-
12
- export interface GscAnalyzerBatchEntry<TResult> {
13
- status: GscAnalyzerBatchStatus
14
- result: TResult | null
15
- error: Error | null
16
- }
17
-
18
- export interface GscAnalyzerBatchRunner {
19
- analyze: (params: { type: string, startDate?: string, endDate?: string }) => Promise<unknown>
20
- }
21
-
22
- export interface UseGscAnalyzerBatchOptions {
23
- /** Cap parallel analyzer runs. DuckDB-WASM is single-threaded; high parallelism just serializes behind one connection. Default 2. */
24
- concurrency?: number
25
- /** Drop ids before running (e.g. analyzers the current source can't serve). Returning `false` marks them `skipped`. */
26
- filter?: (id: string) => boolean
27
- }
28
-
29
- export interface UseGscAnalyzerBatchReturn<TResult> {
30
- states: Ref<Record<string, GscAnalyzerBatchEntry<TResult>>>
31
- running: Ref<boolean>
32
- run: () => Promise<void>
33
- }
34
-
35
- export function useGscAnalyzerBatch<TResult = unknown>(
36
- runner: GscAnalyzerBatchRunner,
37
- ids: MaybeRefOrGetter<readonly string[]>,
38
- dateRange: MaybeRefOrGetter<{ start: string, end: string }>,
39
- opts: UseGscAnalyzerBatchOptions = {},
40
- ): UseGscAnalyzerBatchReturn<TResult> {
41
- const states = ref<Record<string, GscAnalyzerBatchEntry<TResult>>>({}) as Ref<Record<string, GscAnalyzerBatchEntry<TResult>>>
42
- const running = ref(false)
43
- let token = 0
44
-
45
- function reset(currentIds: readonly string[]): void {
46
- const next: Record<string, GscAnalyzerBatchEntry<TResult>> = {}
47
- for (const id of currentIds)
48
- next[id] = { status: 'pending', result: null, error: null }
49
- states.value = next
50
- }
51
-
52
- async function run(): Promise<void> {
53
- const myToken = ++token
54
- const currentIds = toValue(ids)
55
- const range = toValue(dateRange)
56
- running.value = true
57
- reset(currentIds)
58
-
59
- const filter = opts.filter
60
- const concurrency = Math.max(1, opts.concurrency ?? 2)
61
-
62
- const queue: string[] = []
63
- for (const id of currentIds) {
64
- if (filter && !filter(id))
65
- states.value = { ...states.value, [id]: { status: 'skipped', result: null, error: null } }
66
- else
67
- queue.push(id)
68
- }
69
-
70
- async function runOne(id: string): Promise<void> {
71
- if (token !== myToken)
72
- return
73
- states.value = { ...states.value, [id]: { status: 'running', result: null, error: null } }
74
- try {
75
- const result = await runner.analyze({ type: id, startDate: range.start, endDate: range.end })
76
- if (token !== myToken)
77
- return
78
- states.value = { ...states.value, [id]: { status: 'done', result: result as TResult, error: null } }
79
- }
80
- catch (err) {
81
- if (token !== myToken)
82
- return
83
- const error = err instanceof Error ? err : new Error(String(err))
84
- states.value = { ...states.value, [id]: { status: 'error', result: null, error } }
85
- }
86
- }
87
-
88
- const workers = Array.from({ length: concurrency }, async () => {
89
- while (queue.length) {
90
- if (token !== myToken)
91
- return
92
- const id = queue.shift()
93
- if (!id)
94
- break
95
- await runOne(id)
96
- }
97
- })
98
-
99
- await Promise.all(workers)
100
-
101
- if (token === myToken)
102
- running.value = false
103
- }
104
-
105
- return { states, running, run }
106
- }
@@ -1,119 +0,0 @@
1
- // Registry of analyzer definitions. The layer owns the type shape and the
2
- // reader; hosts provide the array via a Nuxt plugin (`$gscAnalyzers`) or via
3
- // the module's `analyzers` option (which generates the plugin at build time).
4
- //
5
- // Callers:
6
- // - `/analyze` page builds its tab list from `useGscAnalyzerDefs()` and
7
- // renders each tab via `<GscAnalyzerPanel :def>` driven by `panel`.
8
- // - `/insights` page filters via `useGscAnalyzerDefsWithCapability('insightCard')`.
9
- // - `useActionPriority` filters via `useGscAnalyzerDefsWithCapability('actionPriority')`.
10
- //
11
- // Capability slots are typed; e.g. `actionPriority` only accepts the
12
- // `ActionSource` union from `@gscdump/analysis`, so misspelt or unknown
13
- // sources fail at typecheck instead of at runtime in the priority runner.
14
-
15
- import type { ActionSource } from '@gscdump/analysis'
16
- import type { AnalysisTool } from '@gscdump/engine/analysis-types'
17
- import type { Component } from '@vue/runtime-core'
18
-
19
- export type GscAnalyzerKind = 'analyzer' | 'semantic' | 'action'
20
-
21
- export type GscAnalyzerAccent = 'primary' | 'warning' | 'success' | 'error' | 'neutral'
22
-
23
- export interface GscAnalyzerInsightCard {
24
- icon: string
25
- accent: GscAnalyzerAccent
26
- description: string
27
- summarize: (res: { results: unknown[], meta: Record<string, unknown> }) => { headline: string, tagline: string }
28
- }
29
-
30
- export interface GscAnalyzerStatTile {
31
- label: string
32
- value: string | number
33
- /**
34
- * Optional CSS color string for the value (used by the few panels that
35
- * color-code direction, e.g. improved/worsened in change-points).
36
- */
37
- valueColor?: string
38
- }
39
-
40
- export interface GscAnalyzerPanelResult {
41
- results: unknown[]
42
- meta: Record<string, unknown>
43
- queryMs?: number | null
44
- }
45
-
46
- export interface GscAnalyzerPanelSpec {
47
- /**
48
- * Body component. Receives `{ rows, meta, range }` as props. Lazy-import
49
- * via `defineAsyncComponent(() => import(...))` to preserve route-level
50
- * codesplitting — pages that don't render the analyzer never pay for its
51
- * chart code.
52
- */
53
- component: Component
54
- /** Project a result to the header stat tiles. Empty array = no tiles. */
55
- summarize?: (res: GscAnalyzerPanelResult) => GscAnalyzerStatTile[]
56
- /** Italic footer caption explaining the underlying method. */
57
- caption?: string
58
- /**
59
- * When true, the shell skips its loading/error/empty gating and renders
60
- * the body component immediately — the panel manages its own phase state
61
- * (pipeline panels: action priority, content gap).
62
- */
63
- ownsLifecycle?: boolean
64
- }
65
-
66
- export interface GscAnalyzerCapabilities {
67
- /** Opt into the `/insights` curated grid by providing a card config. */
68
- insightCard?: GscAnalyzerInsightCard
69
- /** Opt into `useActionPriority`. The value is the typed `ActionSource` slug. */
70
- actionPriority?: ActionSource
71
- /**
72
- * Opt into the unified `/analyze` panel renderer. Without this, the tab
73
- * falls back to the generic table renderer in `<GscAnalyzerPanel>`.
74
- */
75
- panel?: GscAnalyzerPanelSpec
76
- }
77
-
78
- export type GscAnalyzerCapability = keyof GscAnalyzerCapabilities
79
-
80
- export interface GscAnalyzerDefinition {
81
- /** Stable analyzer id. Must match an `AnalysisTool` slug for built-in analyzers. */
82
- id: AnalysisTool | (string & {})
83
- label: string
84
- kind: GscAnalyzerKind
85
- /** Reads the per-query table; sums silently drop GSC-anonymized impressions. */
86
- isQueryGrained?: boolean
87
- /** Capability opt-ins. Each key gates a downstream consumer. */
88
- capabilities?: GscAnalyzerCapabilities
89
- }
90
-
91
- export type GscAnalyzerDefinitionWithCapability<K extends GscAnalyzerCapability>
92
- = GscAnalyzerDefinition & {
93
- capabilities: { [P in K]: NonNullable<GscAnalyzerCapabilities[P]> }
94
- }
95
-
96
- export function defineGscAnalyzer(def: GscAnalyzerDefinition): GscAnalyzerDefinition {
97
- return def
98
- }
99
-
100
- export function useGscAnalyzerDefs(): GscAnalyzerDefinition[] {
101
- return (useNuxtApp().$gscAnalyzers as GscAnalyzerDefinition[] | undefined) ?? []
102
- }
103
-
104
- export function useGscAnalyzerDef(id: string): GscAnalyzerDefinition | undefined {
105
- return useGscAnalyzerDefs().find(a => a.id === id)
106
- }
107
-
108
- /**
109
- * Typed filter that narrows defs to those with `capability` opted in.
110
- * The returned defs have `capabilities[capability]` typed as non-nullable.
111
- */
112
- export function useGscAnalyzerDefsWithCapability<K extends GscAnalyzerCapability>(
113
- capability: K,
114
- ): GscAnalyzerDefinitionWithCapability<K>[] {
115
- return useGscAnalyzerDefs().filter(
116
- (d): d is GscAnalyzerDefinitionWithCapability<K> =>
117
- d.capabilities?.[capability] != null,
118
- )
119
- }
@@ -1,115 +0,0 @@
1
- // Reactive auth primitive for the @gscdump/nuxt layer.
2
- //
3
- // `useState`-backed so the value is SSR-safe and shared across the page tree.
4
- // Hosts call `setGscAuth(getter)` from a `'pre'`-enforced plugin so downstream
5
- // `useGscFetch`, `useGscQuery`, and `useGscAnalyzer` reactively re-read auth
6
- // on every change (no plugin race).
7
- //
8
- // `apiKey` populates `x-api-key` for cross-origin deployments.
9
- // `headers` is an opaque bag for hosts that need additional/alternative
10
- // auth schemes (e.g. `Authorization: Bearer …`, CSRF). Merged after `apiKey`.
11
- // `browserAnalyzerEnabled` is the user-level opt-in for DuckDB-WASM.
12
- //
13
- // `apiBase` is *not* on this state — it's a build-time runtimeConfig value
14
- // (`runtimeConfig.public.analytics.apiBase`). Per-user dynamic apiBase would
15
- // reintroduce a plugin-order race where the SDK client gets a stale prefix.
16
-
17
- import type { Ref } from '@vue/runtime-core'
18
-
19
- export interface GscAuthState {
20
- apiKey: string | null
21
- browserAnalyzerEnabled: boolean
22
- userId?: string | null
23
- /**
24
- * Extra request headers to attach to every `/api/__gsc/*` call and parquet
25
- * fetch. Use for non-`x-api-key` auth schemes. `apiKey` (above) is the
26
- * common case — set both when needed.
27
- */
28
- headers?: Record<string, string>
29
- }
30
-
31
- interface InternalAuthState extends GscAuthState {
32
- /**
33
- * True once the host has called `setGscAuth` at least once. Layer
34
- * consumers (e.g. `useGscQuery` engine derivation) treat unconfigured
35
- * state as "no host wiring" and fall back to legacy behavior, rather
36
- * than honoring the default `browserAnalyzerEnabled: false`.
37
- */
38
- _initialized: boolean
39
- }
40
-
41
- const STATE_KEY = 'gsc:auth'
42
-
43
- const DEFAULT_AUTH: InternalAuthState = {
44
- apiKey: null,
45
- browserAnalyzerEnabled: false,
46
- userId: null,
47
- headers: undefined,
48
- _initialized: false,
49
- }
50
-
51
- function authState(): Ref<InternalAuthState> {
52
- return useState<InternalAuthState>(STATE_KEY, () => ({ ...DEFAULT_AUTH }))
53
- }
54
-
55
- export function useGscAuth(): Readonly<Ref<GscAuthState>> {
56
- return readonly(authState()) as unknown as Readonly<Ref<GscAuthState>>
57
- }
58
-
59
- /**
60
- * Layer-internal: returns the underlying state including `_initialized`.
61
- */
62
- export function _useGscAuthInternal(): Readonly<Ref<InternalAuthState>> {
63
- return readonly(authState()) as Readonly<Ref<InternalAuthState>>
64
- }
65
-
66
- /**
67
- * Set the layer's auth state. Accepts a static value, a ref, or a getter.
68
- * Getter form is reactive — re-runs whenever its source updates.
69
- */
70
- export function setGscAuth(
71
- source: GscAuthState | Ref<GscAuthState> | (() => GscAuthState),
72
- ): void {
73
- const state = authState()
74
- const apply = (v: GscAuthState): void => {
75
- state.value = { ...v, _initialized: true }
76
- }
77
- if (typeof source === 'function') {
78
- watchEffect(() => apply((source as () => GscAuthState)()))
79
- return
80
- }
81
- if (isRef(source)) {
82
- watch(source, v => apply(v), { immediate: true, deep: true })
83
- return
84
- }
85
- apply(source)
86
- }
87
-
88
- /**
89
- * Read auth state from any callsite — composable setup, fetch interceptor,
90
- * worker fetch callback. Resolves the ambient NuxtApp via async-local-storage
91
- * (server) or the global app (client) and reads the `useState` cell directly.
92
- * Returns the default sentinel when no NuxtApp context is available.
93
- */
94
- export function readGscAuth(): GscAuthState {
95
- const nuxt = tryUseNuxtApp()
96
- if (!nuxt)
97
- return { ...DEFAULT_AUTH }
98
- return authState().value
99
- }
100
-
101
- /**
102
- * Resolve the auth headers to attach to a request — `x-api-key` from `apiKey`
103
- * (if set) merged with the opaque `headers` bag. Returns `{}` when no auth
104
- * is wired, signalling callers to fall back to cookie credentials.
105
- */
106
- export function resolveGscAuthHeaders(auth: GscAuthState = readGscAuth()): Record<string, string> {
107
- const out: Record<string, string> = {}
108
- if (auth.apiKey)
109
- out['x-api-key'] = auth.apiKey
110
- if (auth.headers) {
111
- for (const [k, v] of Object.entries(auth.headers))
112
- out[k] = v
113
- }
114
- return out
115
- }
@@ -1,45 +0,0 @@
1
- // Builds `search.google.com/search-console?...` deep-links for a site +
2
- // optional page / query. Portable across consumers — no layer state, pure
3
- // URL string concat.
4
-
5
- export interface GscConsoleUrlOpts {
6
- /** The GSC property — either `sc-domain:example.com` or a URL-prefix. */
7
- siteLabel: string
8
- /** Optional page to open Performance drilldown for. */
9
- page?: string
10
- /** Optional query filter. */
11
- query?: string
12
- /** Resource: `performance`, `url-inspection`, `sitemaps`, … */
13
- resource?: 'performance' | 'url-inspection' | 'sitemaps' | 'index'
14
- }
15
-
16
- export function gscConsoleUrl(opts: GscConsoleUrlOpts): string {
17
- const base = 'https://search.google.com/search-console'
18
- const resource = opts.resource ?? 'performance'
19
- const params = new URLSearchParams()
20
- // Default `sc-domain:` prefix when caller passes a bare hostname.
21
- const siteLabel = /^(?:https?:|sc-domain:)/.test(opts.siteLabel)
22
- ? opts.siteLabel
23
- : `sc-domain:${opts.siteLabel}`
24
- params.set('resource_id', siteLabel)
25
- if (resource === 'url-inspection') {
26
- // Inspect tool expects the absolute URL under `id`, not Performance's
27
- // `page=*<url>` wildcard filter.
28
- if (opts.page)
29
- params.set('id', opts.page)
30
- }
31
- else {
32
- if (opts.page)
33
- params.set('page', `*${opts.page}`)
34
- if (opts.query)
35
- params.set('query', `*${opts.query}`)
36
- }
37
- const path = resource === 'performance'
38
- ? '/performance/search-analytics'
39
- : resource === 'url-inspection'
40
- ? '/inspect'
41
- : resource === 'sitemaps'
42
- ? '/sitemaps'
43
- : '/index'
44
- return `${base}${path}?${params.toString()}`
45
- }
@@ -1,46 +0,0 @@
1
- // Fetches per-country search performance for a site over a date window.
2
- // Server dispatches tier-aware: free → GSC API live, pro → engine parquet.
3
- // The page sees a uniform row shape regardless of which backend served it.
4
-
5
- import type { CountriesResponse, CountryRow, GscApiRange } from '@gscdump/contracts'
6
- import type { ComputedRef, Ref } from '@vue/runtime-core'
7
- import type { GscResourceStatus } from './_useGscResource'
8
- import { gscQueries } from '../queries/gsc'
9
- import { useGscResource } from './_useGscResource'
10
-
11
- export interface UseGscCountriesReturn {
12
- response: Readonly<Ref<CountriesResponse | null>>
13
- rows: ComputedRef<CountryRow[]>
14
- range: ComputedRef<GscApiRange | null>
15
- source: ComputedRef<CountriesResponse['source'] | null>
16
- loading: ComputedRef<boolean>
17
- status: Readonly<Ref<GscResourceStatus>>
18
- error: Readonly<Ref<Error | null>>
19
- refresh: () => Promise<void>
20
- }
21
-
22
- export function useGscCountries(
23
- siteId: MaybeRefOrGetter<string | null | undefined>,
24
- range: MaybeRefOrGetter<{ start: string, end: string } | null | undefined>,
25
- ): UseGscCountriesReturn {
26
- const { data, status, loading, error, refresh } = useGscResource<[string, string, string], CountriesResponse>({
27
- keys: [
28
- siteId,
29
- () => toValue(range)?.start,
30
- () => toValue(range)?.end,
31
- ] as const,
32
- operation: (id, start, end) => gscQueries.countries(id, { start, end }),
33
- isEmpty: r => r.rows.length === 0,
34
- })
35
-
36
- return {
37
- response: data as Readonly<Ref<CountriesResponse | null>>,
38
- rows: computed(() => data.value?.rows ?? []),
39
- range: computed(() => data.value?.range ?? null),
40
- source: computed(() => data.value?.source ?? null),
41
- loading,
42
- status,
43
- error,
44
- refresh,
45
- }
46
- }
@@ -1,36 +0,0 @@
1
- import type { ComputedRef, InjectionKey } from 'vue'
2
-
3
- export interface GscCurrentSiteContext {
4
- siteId: ComputedRef<string>
5
- site: ReturnType<typeof useGscSite>
6
- }
7
-
8
- export const gscCurrentSiteKey: InjectionKey<GscCurrentSiteContext> = Symbol('gsc-current-site')
9
-
10
- function createGscCurrentSiteContext(siteId: MaybeRefOrGetter<string | null | undefined>): GscCurrentSiteContext {
11
- const resolvedSiteId = computed(() => String(toValue(siteId) ?? ''))
12
- return { siteId: resolvedSiteId, site: useGscSite(resolvedSiteId) }
13
- }
14
-
15
- /**
16
- * Provide the current-site context for a dashboard subtree. Hosts with a route
17
- * shape other than `/sites/[id]/*` should call this from their layout/page.
18
- */
19
- export function provideGscCurrentSite(siteId: MaybeRefOrGetter<string | null | undefined>): GscCurrentSiteContext {
20
- const ctx = createGscCurrentSiteContext(siteId)
21
- provide(gscCurrentSiteKey, ctx)
22
- return ctx
23
- }
24
-
25
- /**
26
- * Read the current-site context. Falls back to the historical `/sites/[id]/*`
27
- * route convention for existing hosts that have not added a provider yet.
28
- */
29
- export function useGscCurrentSite(): GscCurrentSiteContext {
30
- const provided = inject(gscCurrentSiteKey, null)
31
- if (provided)
32
- return provided
33
-
34
- const route = useRoute()
35
- return createGscCurrentSiteContext(() => route.params.id as string | undefined)
36
- }
@@ -1,42 +0,0 @@
1
- // Fetches the inspection timeline for a single URL (by hash) from the
2
- // site's history shards. Oldest → newest. 404 / empty history is a normal
3
- // state on the first dashboard visit before a second snapshot runs.
4
-
5
- import type { InspectionHistoryRecord, InspectionHistoryResponse } from '@gscdump/contracts'
6
- import type { ComputedRef, Ref } from '@vue/runtime-core'
7
- import type { GscResourceStatus } from './_useGscResource'
8
- import { gscQueries } from '../queries/gsc'
9
- import { useGscResource } from './_useGscResource'
10
-
11
- export interface UseGscInspectionHistoryReturn {
12
- response: Readonly<Ref<InspectionHistoryResponse | null>>
13
- records: ComputedRef<InspectionHistoryRecord[]>
14
- url: ComputedRef<string | null>
15
- loading: ComputedRef<boolean>
16
- status: Readonly<Ref<GscResourceStatus>>
17
- error: Readonly<Ref<Error | null>>
18
- refresh: () => Promise<void>
19
- }
20
-
21
- export function useGscInspectionHistory(
22
- siteId: MaybeRefOrGetter<string | null | undefined>,
23
- urlHash: MaybeRefOrGetter<string | null | undefined>,
24
- ): UseGscInspectionHistoryReturn {
25
- const { data, status, loading, error, refresh } = useGscResource<[string, string], InspectionHistoryResponse>({
26
- keys: [siteId, urlHash] as const,
27
- operation: (id, hash) => gscQueries.inspectionHistory(id, hash),
28
- })
29
-
30
- const records = computed(() => data.value?.records ?? [])
31
- const url = computed(() => data.value?.url ?? null)
32
-
33
- return {
34
- response: data as Readonly<Ref<InspectionHistoryResponse | null>>,
35
- records,
36
- url,
37
- loading,
38
- status,
39
- error,
40
- refresh,
41
- }
42
- }
@@ -1,52 +0,0 @@
1
- // Fetches the URL-inspection entity index for a site, with PASS/NEUTRAL/FAIL
2
- // counts derived from the records. Split from the previous `useEntity(kind)`
3
- // discriminated API so each entity stays its own typed hook.
4
-
5
- import type { InspectionHistoryRecord, InspectionIndex } from '@gscdump/contracts'
6
- import type { ComputedRef, Ref } from '@vue/runtime-core'
7
- import type { GscResourceStatus } from './_useGscResource'
8
- import { gscQueries } from '../queries/gsc'
9
- import { useGscResource } from './_useGscResource'
10
-
11
- export interface UseGscInspectionsReturn {
12
- index: Readonly<Ref<InspectionIndex | null>>
13
- records: ComputedRef<InspectionHistoryRecord[]>
14
- statusCounts: ComputedRef<{ PASS: number, NEUTRAL: number, FAIL: number, unknown: number }>
15
- loading: ComputedRef<boolean>
16
- status: Readonly<Ref<GscResourceStatus>>
17
- error: Readonly<Ref<Error | null>>
18
- refresh: () => Promise<void>
19
- }
20
-
21
- export function useGscInspections(siteId: MaybeRefOrGetter<string | null | undefined>): UseGscInspectionsReturn {
22
- const { data, status, loading, error, refresh } = useGscResource<[string], InspectionIndex>({
23
- keys: [siteId] as const,
24
- operation: (id: string) => gscQueries.inspections(id),
25
- })
26
-
27
- const records = computed<InspectionHistoryRecord[]>(() =>
28
- data.value ? Object.values(data.value.records) : [],
29
- )
30
-
31
- const statusCounts = computed(() => {
32
- const counts = { PASS: 0, NEUTRAL: 0, FAIL: 0, unknown: 0 }
33
- for (const r of records.value) {
34
- const key = r.indexStatus as keyof typeof counts | undefined
35
- if (key && key in counts)
36
- counts[key]++
37
- else
38
- counts.unknown++
39
- }
40
- return counts
41
- })
42
-
43
- return {
44
- index: data as Readonly<Ref<InspectionIndex | null>>,
45
- records,
46
- statusCounts,
47
- loading,
48
- status,
49
- error,
50
- refresh,
51
- }
52
- }
@@ -1,31 +0,0 @@
1
- // Provide/inject seam for analyzer panels. Pipeline panels (those declared
2
- // with `panel.ownsLifecycle: true`) need access to the analyzer runner +
3
- // long-running composable instances so they can fire `analyze`/`query` and
4
- // keep their phase state alive across tab switches.
5
- //
6
- // `/analyze` provides these once; panels inject. Type-only — runtime values
7
- // are the InjectionKey Symbols themselves.
8
-
9
- import type { AnalysisParams, AnalysisResult } from '@gscdump/engine/analysis-types'
10
- import type { InjectionKey } from '@vue/runtime-core'
11
-
12
- export interface GscPanelRunner {
13
- /** Positional-form SQL runner — `{ rows, queryMs }`. */
14
- query: (sql: string, params?: readonly unknown[]) => Promise<{ rows: Record<string, unknown>[], queryMs: number }>
15
- /** Registry-driven analyzer dispatch over the same per-site DuckDB-WASM runtime. */
16
- analyze: (params: AnalysisParams, opts?: { signal?: AbortSignal }) => Promise<AnalysisResult & { queryMs: number }>
17
- }
18
-
19
- export interface GscPanelRunnerContext {
20
- runner: GscPanelRunner
21
- ready: Ref<boolean>
22
- }
23
-
24
- export const gscPanelRunnerKey: InjectionKey<GscPanelRunnerContext> = Symbol('gscPanelRunnerKey')
25
-
26
- export function useGscPanelRunner(): GscPanelRunnerContext {
27
- const ctx = inject(gscPanelRunnerKey, null)
28
- if (!ctx)
29
- throw new Error('useGscPanelRunner() called outside <GscAnalyzerPanel> context')
30
- return ctx
31
- }