@gscdump/nuxt 0.22.4 → 1.5.3

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
package/README.md CHANGED
@@ -1,148 +1,21 @@
1
1
  # @gscdump/nuxt
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@gscdump/nuxt?color=yellow)](https://npmjs.com/package/@gscdump/nuxt)
4
- [![npm downloads](https://img.shields.io/npm/dm/@gscdump/nuxt?color=yellow)](https://npm.chart.dev/@gscdump/nuxt)
5
- [![license](https://img.shields.io/github/license/harlan-zw/gscdump?color=yellow)](https://github.com/harlan-zw/gscdump/blob/main/LICENSE)
3
+ Minimal Nuxt integration for gscdump applications.
6
4
 
7
- > Nuxt layer: GSC analytics UI, Nuxt auth/runtime wiring, and DuckDB-WASM integration. It uses `@gscdump/sdk` internally for hosted analytics HTTP reads.
5
+ It owns two stable Nuxt seams:
8
6
 
9
- This package assumes a site is already linked. It does not own partner
10
- onboarding, registration, lifecycle decisions, token exchange, or webhook
11
- subscription management. Use `@gscdump/sdk` from the host app for those
12
- flows, then pass the linked `gscdumpSiteId` into the analytics composables
13
- here.
7
+ - typed `runtimeConfig.public.analytics` defaults
8
+ - build-time analyzer registry injection as `$gscAnalyzers`
14
9
 
15
- Three consumption modes:
16
-
17
- | Mode | Who | What it does |
18
- | --- | --- | --- |
19
- | `origin` | gscdump.com | Reads R2 data directly (binding) + DuckDB service binding. Owns the data. |
20
- | `consumer` | nuxtseo.com | Proxies all reads to an origin over HTTP + an API key. No data locally. |
21
- | `local` | `examples/nuxt-dashboard` | Reads a `gscdump sync` dump from `GSCDUMP_DATA_DIR`. Offline dev. |
22
-
23
- ## Install
24
-
25
- ```bash
26
- pnpm add @gscdump/nuxt
27
- ```
10
+ Views, authentication, data fetching, server routes, and product fallback
11
+ policy stay in the host application. Browser runtime primitives live in
12
+ `@gscdump/engine-duckdb-wasm`; hosted API access lives in `@gscdump/sdk`.
28
13
 
29
14
  ```ts
30
- // nuxt.config.ts
31
15
  export default defineNuxtConfig({
32
- extends: ['@gscdump/nuxt'],
33
- runtimeConfig: {
34
- public: {
35
- analytics: {
36
- apiBase: '', // same-origin by default; set to your analytics origin in consumer mode
37
- },
38
- },
16
+ modules: ['@gscdump/nuxt'],
17
+ gscdumpAnalytics: {
18
+ analyzers: '~/gscAnalyzers',
39
19
  },
40
20
  })
41
21
  ```
42
-
43
- Nuxt auto-imports the layer composables. If you prefer explicit imports, use
44
- per-composable entrypoints:
45
-
46
- ```ts
47
- import { setGscAuth } from '@gscdump/nuxt/composables/useGscAuth'
48
- import { useGscRollup } from '@gscdump/nuxt/composables/useGscRollup'
49
- ```
50
-
51
- The primary query interface is `useGscQuery` / `useGscAnalyzer`.
52
-
53
- ## Register an auth provider
54
-
55
- ```ts
56
- // server/plugins/register-auth.ts
57
- export default defineNitroPlugin((nitro) => {
58
- nitro.hooks.hook('analytics:register-auth-provider', (register) => {
59
- register({
60
- resolve: async (event) => {
61
- const session = await getUserSession(event)
62
- if (!session)
63
- return null
64
- return { userId: session.user.id, siteIds: session.accessibleSiteIds }
65
- },
66
- })
67
- })
68
- })
69
- ```
70
-
71
- ## Data access: which endpoint?
72
-
73
- | Need | Endpoint | Client composable |
74
- | --- | --- | --- |
75
- | Pre-aggregated widgets (top pages, daily totals) | `GET /api/__gsc/sites/:siteId/rollup/:id` | `useGscRollup` / `useGscRollups` / `useGscRollupFanout` |
76
- | Raw rows filtered by dimension/range (`where(eq(page, ...))`) | `POST /api/__gsc/sites/:siteId/rows` | `useGscQuery({ siteId, params })` |
77
- | Analyzer result (striking-distance, CTR anomaly, …) | `POST /api/__gsc/sites/:siteId/analyze` | `useGscAnalyzer(siteId).analyze(...)` |
78
-
79
- Rule of thumb: reach for `/rows` + `useGscQuery` for detail pages that
80
- need a freeform slice of the data. Only fall back to `/analyze` when the
81
- server runs an actual analyzer (anything in the registry); hitting it for
82
- plain row lookups routes through analyzer-gating logic you don't need.
83
-
84
- ## Consumer mode (cross-origin)
85
-
86
- Used when the Nuxt app authenticating the viewer is not the same origin as
87
- the data — e.g. nuxtseo.com pro consuming gscdump.com. Two pieces:
88
-
89
- 1. **Point fetch at the origin**: set `runtimeConfig.public.analytics.apiBase`
90
- in `nuxt.config.ts` (or via `NUXT_PUBLIC_ANALYTICS_API_BASE`). Empty =
91
- same-origin (the default origin-mode shape).
92
-
93
- 2. **Hand the layer per-viewer auth**: register a client plugin that trades
94
- the viewer's host session for an api key, then call
95
- `setGscAuth({ apiKey: key })`. The layer's `useGscFetch`
96
- attaches the header to every `/api/__gsc/*` call automatically. Browser
97
- parquet reads use exact-object URLs minted by the source endpoint; runtime
98
- preflights can include this header, while DuckDB-WASM range reads are
99
- authorized by the token embedded in each URL.
100
-
101
- Wire it from a `'pre'`-enforced client plugin so downstream layer plugins see
102
- the auth on first read. The layer's `setGscAuth` accepts a value, a `Ref`,
103
- or a getter — use a getter when credentials may refresh during the session:
104
-
105
- ```ts
106
- // plugins/00.gscdump-auth.client.ts
107
- export default defineNuxtPlugin({
108
- name: 'gscdump-auth',
109
- enforce: 'pre',
110
- async setup() {
111
- const creds = ref<{ apiKey: string, browserAnalyzerEnabled?: boolean } | null>(null)
112
- creds.value = await $fetch('/api/me/gscdump-credentials').catch(() => null)
113
- setGscAuth(() => ({
114
- apiKey: creds.value?.apiKey ?? null,
115
- browserAnalyzerEnabled: !!creds.value?.browserAnalyzerEnabled,
116
- }))
117
- },
118
- })
119
- ```
120
-
121
- `useGscQuery` mounts that race the credential fetch resolve once the
122
- `'pre'` plugin's `await` settles — Nuxt blocks subsequent plugins on it.
123
-
124
- Onboarding and lifecycle state stay outside this layer. A consumer app should
125
- use `@gscdump/sdk` from its host backend to register users/sites, read
126
- `GET /api/partner/users/:userId/lifecycle`, handle webhook or realtime
127
- invalidation, and decide when a linked site is ready to render. Once it has a
128
- linked site id, this layer handles query reads and analytics UI only.
129
-
130
- ### CORS
131
-
132
- The origin app (`gscdump.com` or your own) must allow the consumer origin
133
- on `/api/__gsc/*`, `/api/r2-data/*`, and any host endpoints the helper
134
- calls. Allow-headers must include `x-api-key`, `Cache-Control`, and `Range`
135
- (DuckDB-WASM uses partial reads); expose `Content-Range`, `Accept-Ranges`,
136
- `Content-Length`. Preflight needs to cover `PATCH` for any settings calls.
137
-
138
- ## Related
139
-
140
- - [`@gscdump/sdk`](../sdk) — framework-agnostic hosted API client used by host onboarding code and by this Nuxt layer internally.
141
- - [`gscdump`](../gscdump) — local package and query builder.
142
- - [`@gscdump/engine`](../engine) — Storage engine consumed in origin mode.
143
- - [`@gscdump/engine-duckdb-wasm`](../engine-duckdb-wasm) — DuckDB-WASM browser runtime used by client-side widgets.
144
- - [`@gscdump/analysis`](../analysis) — Analyzers powering `/analyze` endpoints.
145
-
146
- ## License
147
-
148
- [MIT](../../LICENSE)
@@ -0,0 +1,22 @@
1
+ import { GscdumpAnalyticsRuntimeConfig, GscdumpDefaultEngine, GscdumpRuntimeEnvironment, resolveGscdumpAnalyticsRuntimeConfig } from "./runtime-config.mjs";
2
+ import { NuxtModule } from "@nuxt/schema";
3
+ interface GscdumpNuxtModuleOptions {
4
+ /**
5
+ * Nuxt-resolvable module exporting an analyzer array as `ANALYZERS` or
6
+ * `default`.
7
+ */
8
+ analyzers?: string;
9
+ }
10
+ declare module '@nuxt/schema' {
11
+ interface NuxtConfig {
12
+ gscdumpAnalytics?: GscdumpNuxtModuleOptions;
13
+ }
14
+ interface NuxtOptions {
15
+ gscdumpAnalytics: GscdumpNuxtModuleOptions;
16
+ }
17
+ interface PublicRuntimeConfig {
18
+ analytics: GscdumpAnalyticsRuntimeConfig;
19
+ }
20
+ }
21
+ declare const gscdumpNuxtModule: NuxtModule<GscdumpNuxtModuleOptions>;
22
+ export { type GscdumpAnalyticsRuntimeConfig, type GscdumpDefaultEngine, GscdumpNuxtModuleOptions, type GscdumpRuntimeEnvironment, gscdumpNuxtModule as default, resolveGscdumpAnalyticsRuntimeConfig };
@@ -0,0 +1,30 @@
1
+ import { resolveGscdumpAnalyticsRuntimeConfig } from "./runtime-config.mjs";
2
+ import process from "node:process";
3
+ import { addPluginTemplate, defineNuxtModule } from "@nuxt/kit";
4
+ const gscdumpNuxtModule = defineNuxtModule({
5
+ meta: {
6
+ name: "@gscdump/nuxt",
7
+ configKey: "gscdumpAnalytics",
8
+ compatibility: { nuxt: ">=4.0.0" }
9
+ },
10
+ defaults: {},
11
+ setup(options, nuxt) {
12
+ nuxt.options.runtimeConfig.public.analytics = resolveGscdumpAnalyticsRuntimeConfig(nuxt.options.runtimeConfig.public.analytics, process.env);
13
+ if (!options.analyzers) return;
14
+ addPluginTemplate({
15
+ filename: "gscdump-analyzers.plugin.mjs",
16
+ getContents: () => [
17
+ `import { defineNuxtPlugin } from '#app'`,
18
+ `import * as registry from ${JSON.stringify(options.analyzers)}`,
19
+ `const analyzers = registry.ANALYZERS ?? registry.default`,
20
+ `if (!Array.isArray(analyzers))`,
21
+ ` throw new TypeError('gscdump analyzer registry must export an array')`,
22
+ `export default defineNuxtPlugin(() => ({`,
23
+ ` provide: { gscAnalyzers: analyzers },`,
24
+ `}))`,
25
+ ``
26
+ ].join("\n")
27
+ });
28
+ }
29
+ });
30
+ export { gscdumpNuxtModule as default, resolveGscdumpAnalyticsRuntimeConfig };
@@ -0,0 +1,12 @@
1
+ type GscdumpDefaultEngine = 'auto' | 'client' | 'server';
2
+ interface GscdumpAnalyticsRuntimeConfig {
3
+ apiBase: string;
4
+ duckdbBundleBase: string;
5
+ duckdbThreadBundleBase: string;
6
+ timezone: string;
7
+ toastErrors: boolean;
8
+ defaultEngine: GscdumpDefaultEngine;
9
+ }
10
+ type GscdumpRuntimeEnvironment = Readonly<Record<string, string | undefined>>;
11
+ declare function resolveGscdumpAnalyticsRuntimeConfig(configured: Partial<GscdumpAnalyticsRuntimeConfig> | undefined, environment: GscdumpRuntimeEnvironment): GscdumpAnalyticsRuntimeConfig;
12
+ export { GscdumpAnalyticsRuntimeConfig, GscdumpDefaultEngine, GscdumpRuntimeEnvironment, resolveGscdumpAnalyticsRuntimeConfig };
@@ -0,0 +1,27 @@
1
+ const DEFAULT_ENGINES = /* @__PURE__ */ new Set([
2
+ "auto",
3
+ "client",
4
+ "server"
5
+ ]);
6
+ function defaultEngine(value) {
7
+ return value && DEFAULT_ENGINES.has(value) ? value : "auto";
8
+ }
9
+ function resolveGscdumpAnalyticsRuntimeConfig(configured, environment) {
10
+ const defaults = {
11
+ apiBase: environment.GSCDUMP_ANALYTICS_API_BASE ?? "",
12
+ duckdbBundleBase: environment.GSCDUMP_DUCKDB_BUNDLE_BASE ?? "",
13
+ duckdbThreadBundleBase: environment.GSCDUMP_DUCKDB_THREAD_BUNDLE_BASE ?? "",
14
+ timezone: environment.GSCDUMP_ANALYTICS_TIMEZONE ?? "",
15
+ toastErrors: environment.GSCDUMP_ANALYTICS_TOAST_ERRORS === "true",
16
+ defaultEngine: defaultEngine(environment.GSCDUMP_ANALYTICS_DEFAULT_ENGINE)
17
+ };
18
+ return {
19
+ apiBase: configured?.apiBase ?? defaults.apiBase,
20
+ duckdbBundleBase: configured?.duckdbBundleBase ?? defaults.duckdbBundleBase,
21
+ duckdbThreadBundleBase: configured?.duckdbThreadBundleBase ?? defaults.duckdbThreadBundleBase,
22
+ timezone: configured?.timezone ?? defaults.timezone,
23
+ toastErrors: configured?.toastErrors ?? defaults.toastErrors,
24
+ defaultEngine: configured?.defaultEngine ?? defaults.defaultEngine
25
+ };
26
+ }
27
+ export { resolveGscdumpAnalyticsRuntimeConfig };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@gscdump/nuxt",
3
3
  "type": "module",
4
- "version": "0.22.4",
5
- "description": "Nuxt layer: GSC analytics UI + DuckDB-WASM integration. Frontend-only; server primitives live in @gscdump/analysis, @gscdump/engine-sqlite, @gscdump/cloudflare, and host apps.",
4
+ "version": "1.5.3",
5
+ "description": "Minimal Nuxt integration for gscdump analytics runtime config and analyzer registries.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
8
8
  "email": "harlan@harlanzw.com",
@@ -17,191 +17,41 @@
17
17
  "sideEffects": false,
18
18
  "exports": {
19
19
  ".": {
20
- "types": "./types.ts",
21
- "import": "./nuxt.config.ts",
22
- "default": "./nuxt.config.ts"
23
- },
24
- "./types": {
25
- "types": "./types.ts",
26
- "import": "./types.ts",
27
- "default": "./types.ts"
28
- },
29
- "./module": {
30
- "types": "./module.ts",
31
- "import": "./module.ts",
32
- "default": "./module.ts"
33
- },
34
- "./queries/gsc": {
35
- "types": "./app/queries/gsc.ts",
36
- "import": "./app/queries/gsc.ts",
37
- "default": "./app/queries/gsc.ts"
38
- },
39
- "./composables/useGscAnalytics": {
40
- "types": "./app/composables/useGscAnalytics.ts",
41
- "import": "./app/composables/useGscAnalytics.ts",
42
- "default": "./app/composables/useGscAnalytics.ts"
43
- },
44
- "./composables/useGscAnalyticsClient": {
45
- "types": "./app/composables/useGscAnalyticsClient.ts",
46
- "import": "./app/composables/useGscAnalyticsClient.ts",
47
- "default": "./app/composables/useGscAnalyticsClient.ts"
48
- },
49
- "./composables/useGscAnalyticsConfig": {
50
- "types": "./app/composables/useGscAnalyticsConfig.ts",
51
- "import": "./app/composables/useGscAnalyticsConfig.ts",
52
- "default": "./app/composables/useGscAnalyticsConfig.ts"
53
- },
54
- "./composables/useGscAnalyticsSourceInfo": {
55
- "types": "./app/composables/useGscAnalyticsSourceInfo.ts",
56
- "import": "./app/composables/useGscAnalyticsSourceInfo.ts",
57
- "default": "./app/composables/useGscAnalyticsSourceInfo.ts"
58
- },
59
- "./composables/useGscAnalyzer": {
60
- "types": "./app/composables/useGscAnalyzer.ts",
61
- "import": "./app/composables/useGscAnalyzer.ts",
62
- "default": "./app/composables/useGscAnalyzer.ts"
63
- },
64
- "./composables/useGscAnalyzerBatch": {
65
- "types": "./app/composables/useGscAnalyzerBatch.ts",
66
- "import": "./app/composables/useGscAnalyzerBatch.ts",
67
- "default": "./app/composables/useGscAnalyzerBatch.ts"
68
- },
69
- "./composables/useGscAnalyzerDefs": {
70
- "types": "./app/composables/useGscAnalyzerDefs.ts",
71
- "import": "./app/composables/useGscAnalyzerDefs.ts",
72
- "default": "./app/composables/useGscAnalyzerDefs.ts"
73
- },
74
- "./composables/useGscAuth": {
75
- "types": "./app/composables/useGscAuth.ts",
76
- "import": "./app/composables/useGscAuth.ts",
77
- "default": "./app/composables/useGscAuth.ts"
78
- },
79
- "./composables/useGscConsoleUrl": {
80
- "types": "./app/composables/useGscConsoleUrl.ts",
81
- "import": "./app/composables/useGscConsoleUrl.ts",
82
- "default": "./app/composables/useGscConsoleUrl.ts"
83
- },
84
- "./composables/useGscCountries": {
85
- "types": "./app/composables/useGscCountries.ts",
86
- "import": "./app/composables/useGscCountries.ts",
87
- "default": "./app/composables/useGscCountries.ts"
88
- },
89
- "./composables/useGscCurrentSite": {
90
- "types": "./app/composables/useGscCurrentSite.ts",
91
- "import": "./app/composables/useGscCurrentSite.ts",
92
- "default": "./app/composables/useGscCurrentSite.ts"
93
- },
94
- "./composables/useGscInspectionHistory": {
95
- "types": "./app/composables/useGscInspectionHistory.ts",
96
- "import": "./app/composables/useGscInspectionHistory.ts",
97
- "default": "./app/composables/useGscInspectionHistory.ts"
98
- },
99
- "./composables/useGscInspections": {
100
- "types": "./app/composables/useGscInspections.ts",
101
- "import": "./app/composables/useGscInspections.ts",
102
- "default": "./app/composables/useGscInspections.ts"
103
- },
104
- "./composables/useGscPanelContext": {
105
- "types": "./app/composables/useGscPanelContext.ts",
106
- "import": "./app/composables/useGscPanelContext.ts",
107
- "default": "./app/composables/useGscPanelContext.ts"
108
- },
109
- "./composables/useGscPeriod": {
110
- "types": "./app/composables/useGscPeriod.ts",
111
- "import": "./app/composables/useGscPeriod.ts",
112
- "default": "./app/composables/useGscPeriod.ts"
113
- },
114
- "./composables/useGscQuery": {
115
- "types": "./app/composables/useGscQuery.ts",
116
- "import": "./app/composables/useGscQuery.ts",
117
- "default": "./app/composables/useGscQuery.ts"
118
- },
119
- "./composables/useGscQueryDispatcher": {
120
- "types": "./app/composables/useGscQueryDispatcher.ts",
121
- "import": "./app/composables/useGscQueryDispatcher.ts",
122
- "default": "./app/composables/useGscQueryDispatcher.ts"
123
- },
124
- "./composables/useGscRequestIndexingInspect": {
125
- "types": "./app/composables/useGscRequestIndexingInspect.ts",
126
- "import": "./app/composables/useGscRequestIndexingInspect.ts",
127
- "default": "./app/composables/useGscRequestIndexingInspect.ts"
128
- },
129
- "./composables/useGscResource": {
130
- "types": "./app/composables/useGscResource.ts",
131
- "import": "./app/composables/useGscResource.ts",
132
- "default": "./app/composables/useGscResource.ts"
133
- },
134
- "./composables/useGscRollup": {
135
- "types": "./app/composables/useGscRollup.ts",
136
- "import": "./app/composables/useGscRollup.ts",
137
- "default": "./app/composables/useGscRollup.ts"
138
- },
139
- "./composables/useGscSearchAppearance": {
140
- "types": "./app/composables/useGscSearchAppearance.ts",
141
- "import": "./app/composables/useGscSearchAppearance.ts",
142
- "default": "./app/composables/useGscSearchAppearance.ts"
143
- },
144
- "./composables/useGscSiteAnalyzer": {
145
- "types": "./app/composables/useGscSiteAnalyzer.ts",
146
- "import": "./app/composables/useGscSiteAnalyzer.ts",
147
- "default": "./app/composables/useGscSiteAnalyzer.ts"
148
- },
149
- "./composables/useGscSitemapHistory": {
150
- "types": "./app/composables/useGscSitemapHistory.ts",
151
- "import": "./app/composables/useGscSitemapHistory.ts",
152
- "default": "./app/composables/useGscSitemapHistory.ts"
153
- },
154
- "./composables/useGscSitemaps": {
155
- "types": "./app/composables/useGscSitemaps.ts",
156
- "import": "./app/composables/useGscSitemaps.ts",
157
- "default": "./app/composables/useGscSitemaps.ts"
158
- },
159
- "./composables/useGscTableState": {
160
- "types": "./app/composables/useGscTableState.ts",
161
- "import": "./app/composables/useGscTableState.ts",
162
- "default": "./app/composables/useGscTableState.ts"
20
+ "types": "./dist/module.d.mts",
21
+ "import": "./dist/module.mjs",
22
+ "default": "./dist/module.mjs"
23
+ },
24
+ "./runtime-config": {
25
+ "types": "./dist/runtime-config.d.mts",
26
+ "import": "./dist/runtime-config.mjs",
27
+ "default": "./dist/runtime-config.mjs"
163
28
  }
164
29
  },
165
- "main": "./nuxt.config.ts",
166
- "types": "./types.ts",
30
+ "main": "./dist/module.mjs",
31
+ "types": "./dist/module.d.mts",
167
32
  "files": [
168
33
  "README.md",
169
- "app",
170
- "module.ts",
171
- "nuxt.config.ts",
172
- "types.ts"
34
+ "dist"
173
35
  ],
36
+ "engines": {
37
+ "node": ">=22"
38
+ },
174
39
  "peerDependencies": {
175
- "nuxt": "^4.4.6",
176
- "vue": "^3.5.34"
40
+ "nuxt": "^4.5.0"
177
41
  },
178
42
  "dependencies": {
179
- "@duckdb/duckdb-wasm": "^1.32.0",
180
- "@nuxt/ui": "^4.8.0",
181
- "@unovis/ts": "^1.6.5",
182
- "@unovis/vue": "^1.6.5",
183
- "@vueuse/core": "^14.3.0",
184
- "@vueuse/nuxt": "^14.3.0",
185
- "defu": "^6.1.7",
186
- "nuxt-use-query": "^0.2.4",
187
- "ofetch": "^1.5.1",
188
- "tailwindcss": "^4.3.0",
189
- "zod": "^4.4.3",
190
- "@gscdump/analysis": "0.22.4",
191
- "@gscdump/contracts": "0.22.4",
192
- "@gscdump/engine": "0.22.4",
193
- "@gscdump/engine-duckdb-wasm": "0.22.4",
194
- "@gscdump/sdk": "0.22.4",
195
- "gscdump": "0.22.4"
43
+ "@nuxt/kit": "4.5.0",
44
+ "@nuxt/schema": "4.5.0"
196
45
  },
197
46
  "devDependencies": {
198
- "@nuxt/kit": "^4.4.6",
199
- "nuxt": "^4.4.6",
200
- "typescript": "^6.0.3",
201
- "vue": "^3.5.34"
47
+ "nuxt": "^4.5.0",
48
+ "obuild": "^0.4.38",
49
+ "typescript": "^7.0.2",
50
+ "vitest": "^4.1.10"
202
51
  },
203
52
  "scripts": {
204
- "build": "nuxt prepare",
205
- "typecheck": "nuxt typecheck"
53
+ "build": "obuild",
54
+ "typecheck": "tsc --noEmit",
55
+ "test": "vitest"
206
56
  }
207
57
  }
@@ -1,2 +0,0 @@
1
- @import "tailwindcss";
2
- @import "@nuxt/ui";
@@ -1,94 +0,0 @@
1
- <script setup lang="ts">
2
- // Unified shell for `/analyze` analyzer tabs. Drives the chrome that 11
3
- // per-analyzer sections used to repeat: stat tiles, body, caption. The body
4
- // component + tile projection + caption come from `def.capabilities.panel`;
5
- // the shell owns the loading/error/empty gating unless `panel.ownsLifecycle`
6
- // is set (pipeline panels manage their own phase state internally).
7
-
8
- import type {
9
- GscAnalyzerDefinition,
10
- GscAnalyzerPanelResult,
11
- } from '../composables/useGscAnalyzerDefs'
12
-
13
- interface Props {
14
- def: GscAnalyzerDefinition
15
- rows: unknown[]
16
- meta: Record<string, unknown> | null
17
- queryMs?: number | null
18
- loading?: boolean
19
- error?: string | null
20
- /** Forwarded to the body component as `range`. Optional. */
21
- range?: { start: string, end: string } | null
22
- }
23
-
24
- const props = defineProps<Props>()
25
-
26
- const panel = computed(() => props.def.capabilities?.panel)
27
-
28
- const tiles = computed(() => {
29
- const p = panel.value
30
- if (!p?.summarize)
31
- return []
32
- const result: GscAnalyzerPanelResult = {
33
- results: props.rows,
34
- meta: props.meta ?? {},
35
- queryMs: props.queryMs ?? null,
36
- }
37
- return p.summarize(result)
38
- })
39
-
40
- const showBody = computed(() => {
41
- if (!panel.value)
42
- return false
43
- if (panel.value.ownsLifecycle)
44
- return true
45
- return !props.loading && !props.error && props.rows.length > 0
46
- })
47
-
48
- const bodyProps = computed(() => ({
49
- rows: props.rows,
50
- meta: props.meta ?? {},
51
- range: props.range ?? null,
52
- }))
53
- </script>
54
-
55
- <template>
56
- <section v-if="panel" class="gsc-analyzer-panel">
57
- <div v-if="tiles.length > 0" class="gsc-analyzer-panel__tiles">
58
- <div v-for="(t, i) in tiles" :key="`${t.label}-${i}`" class="gsc-analyzer-panel__tile">
59
- <span class="gsc-analyzer-panel__tile-label">{{ t.label }}</span>
60
- <span class="gsc-analyzer-panel__tile-value" :style="t.valueColor ? { color: t.valueColor } : undefined">{{ t.value }}</span>
61
- </div>
62
- </div>
63
-
64
- <template v-if="!panel.ownsLifecycle">
65
- <div v-if="error" class="gsc-analyzer-panel__error">
66
- {{ error }}
67
- </div>
68
- <div v-else-if="loading" class="gsc-analyzer-panel__loading">
69
- Running analyzer…
70
- </div>
71
- <div v-else-if="rows.length === 0" class="gsc-analyzer-panel__empty">
72
- No rows.
73
- </div>
74
- </template>
75
-
76
- <component :is="panel.component" v-if="showBody" v-bind="bodyProps" />
77
-
78
- <p v-if="panel.caption" class="gsc-analyzer-panel__caption">
79
- <slot name="caption">{{ panel.caption }}</slot>
80
- </p>
81
- </section>
82
- </template>
83
-
84
- <style scoped>
85
- .gsc-analyzer-panel { background: #fff; border: 1px solid #ececef; border-top: 0; border-radius: 0 0 6px 6px; padding: 1rem 1rem 0.9rem; }
86
- .gsc-analyzer-panel__tiles { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 0.85rem; padding: 0 0.15rem; }
87
- .gsc-analyzer-panel__tile { display: flex; flex-direction: column; gap: 0.1rem; }
88
- .gsc-analyzer-panel__tile-label { font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; color: #888; }
89
- .gsc-analyzer-panel__tile-value { font-size: 1.3rem; font-weight: 600; color: #1d1d1f; font-variant-numeric: tabular-nums; }
90
- .gsc-analyzer-panel__caption { margin: 0.8rem 0 0; font-size: 0.78rem; color: #666; font-style: italic; text-align: center; }
91
- .gsc-analyzer-panel__caption :deep(code) { background: #f0f0f3; padding: 0.05rem 0.3rem; border-radius: 3px; font-size: 0.74rem; color: #4c3ca0; }
92
- .gsc-analyzer-panel__error { padding: 1rem 1.25rem; color: #c00; background: #fff5f5; font-family: ui-monospace, monospace; font-size: 0.82rem; white-space: pre-wrap; }
93
- .gsc-analyzer-panel__loading, .gsc-analyzer-panel__empty { padding: 2.5rem; text-align: center; color: var(--ui-text-dimmed); font-size: 0.9rem; }
94
- </style>
@@ -1,46 +0,0 @@
1
- <script setup lang="ts">
2
- // Surfaces the trailing-28d impression-weighted anonymization % from the
3
- // daily_totals rollup. GSC anonymises queries for low-volume or sensitive
4
- // terms, so query-grained breakdowns (keywords, pages-by-query) undercount
5
- // impressions by this amount. Hidden when data isn't ready or the gap is
6
- // negligible (<1%).
7
-
8
- import { useGscRollup } from '../composables/useGscRollup'
9
- import { weightedAnonPct } from '../utils/anonymization'
10
-
11
- interface DailyTotal {
12
- impressions: number
13
- anonymizedImpressionsPct: number
14
- }
15
-
16
- const { siteIdentifier } = defineProps<{
17
- siteIdentifier: string | null | undefined
18
- }>()
19
-
20
- const { data: daily } = useGscRollup<DailyTotal[]>(() => siteIdentifier ?? null, 'daily_totals')
21
-
22
- const formatted = computed(() => {
23
- if (!siteIdentifier)
24
- return null
25
- const pct = weightedAnonPct(daily.value)
26
- if (pct == null)
27
- return null
28
- const percent = pct * 100
29
- if (percent < 1)
30
- return null
31
- return percent.toFixed(percent < 10 ? 1 : 0)
32
- })
33
- </script>
34
-
35
- <template>
36
- <div
37
- v-if="formatted"
38
- class="flex items-start gap-2 border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-xs text-amber-200/90"
39
- >
40
- <UIcon name="i-ph-info" class="mt-0.5 flex-shrink-0 text-amber-400" />
41
- <div>
42
- <span class="font-semibold">~{{ formatted }}% of impressions are anonymised by Google.</span>
43
- Query-grained breakdowns below exclude these, so the sum won't match site-level totals.
44
- </div>
45
- </div>
46
- </template>