gscdump 0.40.2 → 1.0.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.
package/README.md CHANGED
@@ -4,15 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/gscdump?color=yellow)](https://npm.chart.dev/gscdump)
5
5
  [![license](https://img.shields.io/github/license/harlan-zw/gscdump?color=yellow)](https://github.com/harlan-zw/gscdump/blob/main/LICENSE)
6
6
 
7
- > Google Search Console API wrapper with typed query builder, streaming pagination, and SEO analysis functions.
8
-
9
- ## Features
10
-
11
- - **Typed Query Builder** - Drizzle-style API with filter constraints narrowing result types
12
- - **Streaming Pagination** - Memory-efficient iteration over large datasets (>25k rows)
13
- - **SEO Analysis** - Pure functions for cannibalization, striking distance, movers & shakers, decay detection
14
- - **Edge-Compatible** - Works in Cloudflare Workers, Deno, and other edge runtimes
15
- - **Full API Coverage** - Sites, sitemaps, indexing, and analytics
7
+ Direct Google Search Console client with a typed query builder, streaming pagination, URL inspection, sitemap, verification, and Indexing API helpers.
16
8
 
17
9
  ## Install
18
10
 
@@ -20,146 +12,78 @@
20
12
  npm install gscdump
21
13
  ```
22
14
 
23
- This package is the edge-safe surface: REST client + query builder + cross-package contracts. For the storage engine (Parquet/DuckDB, planner, adapters), install [`@gscdump/engine`](../engine). For analyzers, install [`@gscdump/analysis`](../analysis).
24
-
25
- ## Usage
26
-
27
- ```ts
28
- import { fetchKeywordsWithComparison, fetchPagesWithComparison } from 'gscdump'
29
- import { daysAgo, today } from 'gscdump/query'
30
-
31
- // Auth accepts token string or object
32
- const auth = 'ya29.xxx...'
33
- // or: { accessToken: 'ya29.xxx...' }
34
-
35
- const range = {
36
- period: { start: daysAgo(28), end: today() },
37
- }
38
-
39
- // Pages with top keyword per page
40
- const pages = await fetchPagesWithComparison(auth, site, range)
41
-
42
- // Keywords with change percentages
43
- const keywords = await fetchKeywordsWithComparison(auth, site, range)
44
- ```
45
-
46
- ### Streaming Large Datasets
47
-
48
- For memory-efficient pagination of large datasets (>25k rows):
49
-
50
- ```ts
51
- import { queryRecursiveStream } from 'gscdump'
52
-
53
- // Stream keyword+page combinations - yields batches as they're fetched
54
- for await (const batch of queryRecursiveStream(client, site, {
55
- dimensions: ['query', 'page'] as const, // as const required for type inference
56
- startDate: '2024-01-01',
57
- endDate: '2024-01-31',
58
- })) {
59
- // batch: { keyword: string, page: string, clicks, impressions, ctr, position }[]
60
- await db.insert(batch)
61
- }
62
- ```
15
+ `gscdump` requires Node 22 or newer. It talks to Google over `fetch`; it does not require the Google API client packages.
63
16
 
64
- ### Typed Query Builder
17
+ ## Query Search Analytics
65
18
 
66
- Drizzle-style query builder with full type safety. Filter constraints flow through to result types.
19
+ Create a client with an access token, an OAuth-like client, or refresh-token credentials:
67
20
 
68
21
  ```ts
69
- import { and, between, contains, country, Country, date, device, Device, eq, gsc, inArray, page } from 'gscdump/query'
22
+ import { googleSearchConsole } from 'gscdump'
70
23
 
71
- const body = gsc
72
- .select('page', 'query', 'device', 'country')
73
- .where(and(
74
- eq(device, Device.MOBILE),
75
- inArray(country, [Country.USA, Country.GBR]),
76
- contains(page, '/blog/'),
77
- between(date, '2024-01-01', '2024-01-31')
78
- ))
79
- .toBody()
80
-
81
- // Use with client.searchAnalytics.query(siteUrl, body)
24
+ const client = googleSearchConsole({ accessToken: 'ya29.xxx' })
25
+ const sites = await client.sites()
82
26
  ```
83
27
 
84
- **With date helpers:**
28
+ Use `gscdump/query` to build a request. `client.query()` paginates through Google's 25,000-row pages and yields each non-empty batch.
85
29
 
86
30
  ```ts
87
- import { and, between, date, daysAgo, gsc, query, regex, today } from 'gscdump/query'
31
+ import { googleSearchConsole } from 'gscdump'
32
+ import { and, between, date, daysAgo, gsc, page, query, today } from 'gscdump/query'
33
+
34
+ const client = googleSearchConsole('ya29.xxx')
35
+ const siteUrl = 'sc-domain:example.com'
88
36
 
89
- const q = gsc
90
- .select('query', 'page')
37
+ const request = gsc
38
+ .select(page, query)
91
39
  .where(and(
92
40
  between(date, daysAgo(28), today()),
93
- regex(query, /how to/)
94
41
  ))
95
- .limit(100)
42
+
43
+ for await (const rows of client.query(siteUrl, request)) {
44
+ for (const row of rows)
45
+ console.log(row.page, row.query, row.clicks)
46
+ }
96
47
  ```
97
48
 
98
- **Operators:**
99
-
100
- | Operator | Narrows Type? | Description |
101
- |----------|---------------|-------------|
102
- | `eq(col, val)` | ✓ | Exact match |
103
- | `ne(col, val)` | ✗ | Not equal |
104
- | `inArray(col, [a, b])` | ✓ | Value in array (becomes `a \| b`) |
105
- | `contains(col, str)` | ✗ | String contains |
106
- | `like(col, '%pattern%')` | ✗ | SQL LIKE pattern |
107
- | `regex(col, /pattern/)` | ✗ | Regex match |
108
- | `notRegex(col, /pattern/)` | ✗ | Regex exclusion |
109
- | `between(col, start, end)` | ✗ | Inclusive range (primarily for date) |
110
- | `gte(col, val)` | ✗ | Greater than or equal |
111
- | `lte(col, val)` | ✗ | Less than or equal |
112
- | `gt(col, val)` | ✗ | Greater than |
113
- | `lt(col, val)` | ✗ | Less than |
114
- | `and(...filters)` | ✓ | Merge constraints |
115
- | `or(...filters)` | ✗ | Any match |
116
- | `not(filter)` | ✗ | Invert filter |
117
-
118
- ### Analysis Functions
119
-
120
- Analysis functions are pure - they operate on typed data arrays and return typed results.
49
+ If you already have a Search Analytics request body, call the direct operation:
121
50
 
122
51
  ```ts
123
- import {
124
- analyzeCannibalization,
125
- analyzeDecay,
126
- analyzeMovers,
127
- } from '@gscdump/analysis'
128
- import { fetchKeywordsWithComparison } from 'gscdump'
129
-
130
- // Fetch data first
131
- const { current, previous } = await fetchKeywordsWithComparison(auth, site, range)
132
-
133
- // Run pure analysis on the data
134
- const movers = analyzeMovers(current, previous)
135
- const decay = analyzeDecay(current, previous)
136
- const cannibalization = analyzeCannibalization(keywordPageData)
52
+ const response = await client.searchAnalytics.query(siteUrl, {
53
+ startDate: '2026-06-01',
54
+ endDate: '2026-06-30',
55
+ dimensions: ['page'],
56
+ rowLimit: 1000,
57
+ })
137
58
  ```
138
59
 
139
- ## Exports
140
-
141
- **Sites:** `fetchSites`, `fetchSitesWithSitemaps`, `fetchSitemaps`, `getSitemap`, `submitSitemap`, `deleteSitemap`, `inspectUrl`, `batchInspectUrls`
142
-
143
- **Indexing:** `requestIndexing`, `getIndexingMetadata`, `batchRequestIndexing`
60
+ ## Other Google resources
144
61
 
145
- **Analytics:** `fetchAnalyticsWithComparison`, `fetchPagesWithComparison`, `fetchKeywordsWithComparison`, `fetchDevicesWithComparison`, `fetchCountriesWithComparison`, `fetchSearchAppearanceWithComparison`, `fetchDates`, `fetchDatesWithComparison`, `fetchPages`, `fetchPage`, `fetchKeyword`
62
+ The same client exposes the Google resources owned by this package:
146
63
 
147
- **Analysis (Pure)** — these live in `@gscdump/analysis`: `analyzeOpportunity`, `analyzeBrandSegmentation`, `analyzeConcentration`, `analyzeDecay`, `analyzeMovers`, `analyzeCannibalization`, `analyzeZeroClick`, `analyzeSeasonality`, `analyzeClustering`
148
-
149
- **Low-level:** `gscClient`, `queryRecursive`, `queryRecursiveStream`, `createQueryBody`, `withPropertyAggregation`, `withSearchAppearance`, `withDataType`, `withFreshData`, `withFinalData`
64
+ ```ts
65
+ const sitemapList = await client.sitemaps.list(siteUrl)
66
+ const inspection = await client.inspect(siteUrl, 'https://example.com/docs')
150
67
 
151
- **Query Builder (`gscdump/query`):** `gsc`, `eq`, `ne`, `and`, `or`, `inArray`, `contains`, `like`, `regex`, `notRegex`, `not`, `between`, `gte`, `gt`, `lte`, `lt`, `page`, `query`, `device`, `country`, `date`, `searchAppearance`, `Device`, `Country`, `today`, `daysAgo`
68
+ await client.indexing.publish(
69
+ 'https://example.com/jobs/frontend-engineer',
70
+ 'URL_UPDATED',
71
+ )
72
+ ```
152
73
 
153
- **Error Utilities:** `isQuotaError`, `isRateLimitError`, `isAuthError`, `getErrorCode`, `getErrorMessage`, `getRetryAfter`, `analyzeGscError`, `formatGscErrorForCli`
74
+ The package root also exports batch and projection helpers such as `fetchSitesWithSitemaps`, `batchInspectUrlsFlatSettled`, `inspectUrlFlat`, and `batchRequestIndexing`.
154
75
 
155
- **Utils:** `formatDateGsc`, `percentDifference`
76
+ ## Public subpaths
156
77
 
157
- ## Related
78
+ - `gscdump/query`: query builder, columns, operators, Pacific date helpers, and logical query plans
79
+ - `gscdump/query/plan`: logical query planning only
80
+ - `gscdump/dates`: explicit UTC and Pacific Search Console date helpers
81
+ - `gscdump/contracts`: Search Analytics request and response contracts
82
+ - `gscdump/result`: `Result` helpers
83
+ - `gscdump/normalize`: URL normalization
84
+ - `gscdump/tenant`: site ID encoding and normalization
158
85
 
159
- - [`@gscdump/cli`](../cli) CLI: `sync`, `query`, `dump`, `analyze`, `mcp`, `store` admin.
160
- - [`@gscdump/engine`](../engine) — Append-only Parquet/DuckDB storage engine.
161
- - [`@gscdump/analysis`](../analysis) — SEO analyzers (row-based + DuckDB-native + D1-ready).
162
- - [`@gscdump/sdk`](../sdk) — Hosted API and ticketed realtime clients.
86
+ Hosted gscdump.com clients live in [`@gscdump/sdk`](../sdk). Storage engines and analyzers live in [`@gscdump/engine`](../engine) and [`@gscdump/analysis`](../analysis).
163
87
 
164
88
  ## License
165
89
 
@@ -11,19 +11,10 @@ declare function ok<A>(value: A): Ok<A>;
11
11
  declare function err<E>(error: E): Err<E>;
12
12
  declare function isOk<A, E>(result: Result<A, E>): result is Ok<A>;
13
13
  declare function isErr<A, E>(result: Result<A, E>): result is Err<E>;
14
- /** Maps the success channel, leaving an `Err` untouched. */
15
- declare function mapResult<A, B, E>(result: Result<A, E>, f: (value: A) => B): Result<B, E>;
16
- /** Maps the error channel, leaving an `Ok` untouched. */
17
- declare function mapErr<A, E, F>(result: Result<A, E>, f: (error: E) => F): Result<A, F>;
18
- /** Folds both channels into a single value. */
19
- declare function matchResult<A, E, R>(result: Result<A, E>, handlers: {
20
- onOk: (value: A) => R;
21
- onErr: (error: E) => R;
22
- }): R;
23
14
  /**
24
15
  * Collapses a `Result` back into the throwing world: returns the value or throws
25
16
  * via `toError`. Used by the throwing wrappers that sit over the `Result` core so
26
17
  * existing call sites keep their `await`/`throw` ergonomics.
27
18
  */
28
19
  declare function unwrapResult<A, E>(result: Result<A, E>, toError: (error: E) => unknown): A;
29
- export { Err, Ok, Result, err, isErr, isOk, mapErr, mapResult, matchResult, ok, unwrapResult };
20
+ export { Err, Ok, Result, err, isErr, isOk, ok, unwrapResult };
@@ -16,17 +16,8 @@ function isOk(result) {
16
16
  function isErr(result) {
17
17
  return !result.ok;
18
18
  }
19
- function mapResult(result, f) {
20
- return result.ok ? ok(f(result.value)) : result;
21
- }
22
- function mapErr(result, f) {
23
- return result.ok ? result : err(f(result.error));
24
- }
25
- function matchResult(result, handlers) {
26
- return result.ok ? handlers.onOk(result.value) : handlers.onErr(result.error);
27
- }
28
19
  function unwrapResult(result, toError) {
29
20
  if (result.ok) return result.value;
30
21
  throw toError(result.error);
31
22
  }
32
- export { err, isErr, isOk, mapErr, mapResult, matchResult, ok, unwrapResult };
23
+ export { err, isErr, isOk, ok, unwrapResult };