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 +47 -123
- package/dist/core/result.d.mts +1 -10
- package/dist/core/result.mjs +1 -10
- package/dist/index.d.mts +212 -87
- package/dist/index.mjs +132 -233
- package/dist/query/index.d.mts +11 -8
- package/dist/query/index.mjs +33 -22
- package/dist/query/plan.d.mts +4 -1
- package/dist/query/plan.mjs +6 -0
- package/package.json +4 -25
- package/dist/api/index.d.mts +0 -748
- package/dist/api/index.mjs +0 -2989
package/README.md
CHANGED
|
@@ -4,15 +4,7 @@
|
|
|
4
4
|
[](https://npm.chart.dev/gscdump)
|
|
5
5
|
[](https://github.com/harlan-zw/gscdump/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
+
## Query Search Analytics
|
|
65
18
|
|
|
66
|
-
|
|
19
|
+
Create a client with an access token, an OAuth-like client, or refresh-token credentials:
|
|
67
20
|
|
|
68
21
|
```ts
|
|
69
|
-
import {
|
|
22
|
+
import { googleSearchConsole } from 'gscdump'
|
|
70
23
|
|
|
71
|
-
const
|
|
72
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
90
|
-
.select(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
##
|
|
140
|
-
|
|
141
|
-
**Sites:** `fetchSites`, `fetchSitesWithSitemaps`, `fetchSitemaps`, `getSitemap`, `submitSitemap`, `deleteSitemap`, `inspectUrl`, `batchInspectUrls`
|
|
142
|
-
|
|
143
|
-
**Indexing:** `requestIndexing`, `getIndexingMetadata`, `batchRequestIndexing`
|
|
60
|
+
## Other Google resources
|
|
144
61
|
|
|
145
|
-
|
|
62
|
+
The same client exposes the Google resources owned by this package:
|
|
146
63
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
64
|
+
```ts
|
|
65
|
+
const sitemapList = await client.sitemaps.list(siteUrl)
|
|
66
|
+
const inspection = await client.inspect(siteUrl, 'https://example.com/docs')
|
|
150
67
|
|
|
151
|
-
|
|
68
|
+
await client.indexing.publish(
|
|
69
|
+
'https://example.com/jobs/frontend-engineer',
|
|
70
|
+
'URL_UPDATED',
|
|
71
|
+
)
|
|
72
|
+
```
|
|
152
73
|
|
|
153
|
-
|
|
74
|
+
The package root also exports batch and projection helpers such as `fetchSitesWithSitemaps`, `batchInspectUrlsFlatSettled`, `inspectUrlFlat`, and `batchRequestIndexing`.
|
|
154
75
|
|
|
155
|
-
|
|
76
|
+
## Public subpaths
|
|
156
77
|
|
|
157
|
-
|
|
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
|
-
|
|
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
|
|
package/dist/core/result.d.mts
CHANGED
|
@@ -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,
|
|
20
|
+
export { Err, Ok, Result, err, isErr, isOk, ok, unwrapResult };
|
package/dist/core/result.mjs
CHANGED
|
@@ -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,
|
|
23
|
+
export { err, isErr, isOk, ok, unwrapResult };
|