@gscdump/analysis 0.6.3 → 0.7.5

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,220 +1,14 @@
1
- import { createEngine as createBrowserQuerySource } from "@gscdump/engine-wasm";
2
- import { between, clicks, date, extractDateRange, extractMetricFilters, extractSpecialOperatorFilters, gsc, page, query } from "gscdump/query";
3
- import { daysAgo, googleSearchConsole } from "gscdump";
4
- import { assertDimensionsSupported, getDimensionFilters, getFilterDimensions, isSqlQuerySource, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, metricValue } from "@gscdump/engine/resolver";
5
- import { buildLogicalPlan } from "gscdump/query/plan";
1
+ import { AnalyzerCapabilityError, defineAnalyzer, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
2
+ import { canProxyToGsc } from "@gscdump/engine-gsc-api";
3
+ import { between, date, extractDateRange, gsc, page, query } from "gscdump/query";
4
+ import { queryRows, typedQuery } from "@gscdump/engine/source";
5
+ import { num } from "@gscdump/engine/analysis-types";
6
+ import { comparisonOf, periodOf } from "@gscdump/engine/period";
6
7
  import { enumeratePartitions } from "@gscdump/engine/planner";
7
8
  import { METRIC_EXPR } from "@gscdump/engine/sql-fragments";
8
- import { createEngine as createSqliteQuerySource } from "@gscdump/engine-sqlite";
9
- var AnalyzerCapabilityError = class extends Error {
10
- constructor(tool, missing) {
11
- super(`analyzer "${tool}" requires capabilities [${missing.join(", ")}] not provided by source`);
12
- this.tool = tool;
13
- this.missing = missing;
14
- this.name = "AnalyzerCapabilityError";
15
- }
16
- };
17
- function sourceCapabilities(source) {
18
- const caps = /* @__PURE__ */ new Set();
19
- if (source.executeSql) caps.add("executeSql");
20
- if (source.capabilities.fileSets) caps.add("partitionedParquet");
21
- if (source.capabilities.regex) caps.add("regex");
22
- if (source.capabilities.windowTotals) caps.add("windowTotals");
23
- if (source.capabilities.comparisonJoin) caps.add("comparisonJoin");
24
- if (source.capabilities.attachedTables) caps.add("attachedTables");
25
- return caps;
26
- }
27
- function assertSatisfies(analyzer, caps) {
28
- const missing = analyzer.requires.filter((c) => !caps.has(c));
29
- if (missing.length > 0) throw new AnalyzerCapabilityError(analyzer.id, missing);
30
- }
31
- async function runAnalyzerFromSource(source, params, registry) {
32
- const caps = sourceCapabilities(source);
33
- const analyzer = registry.resolveAnalyzer(params.type, caps.has("executeSql") || caps.has("attachedTables"));
34
- if (!analyzer) throw new AnalyzerCapabilityError(params.type, ["executeSql"]);
35
- assertSatisfies(analyzer, caps);
36
- const plan = analyzer.build(params);
37
- if (plan.kind === "rows") return runRowsPlanAgainstSource(source, analyzer, plan, params);
38
- return runSqlPlanAgainstSource(source, analyzer, plan, params);
39
- }
40
- async function runRowsPlanAgainstSource(source, analyzer, plan, params) {
41
- const entries = Object.entries(plan.queries);
42
- const resolved = await Promise.all(entries.map(async ([k, q]) => [k, await source.queryRows(q.state)]));
43
- const rowMap = Object.fromEntries(resolved);
44
- const { results, meta } = analyzer.reduce(rowMap, { params });
45
- return {
46
- results,
47
- meta: {
48
- tool: params.type,
49
- ...meta
50
- }
51
- };
52
- }
53
- function fileSetsFor(plan) {
54
- const fileSets = { FILES: plan.current };
55
- if (plan.previous) fileSets.FILES_PREV = plan.previous;
56
- if (plan.extraFiles) for (const [key, fs] of Object.entries(plan.extraFiles)) fileSets[`FILES_${key}`] = fs;
57
- return fileSets;
58
- }
59
- async function runSqlPlanAgainstSource(source, analyzer, plan, params) {
60
- if (!source.executeSql) throw new AnalyzerCapabilityError(analyzer.id, ["executeSql"]);
61
- if (plan.requiresAttachedTables && !source.capabilities.attachedTables) throw new AnalyzerCapabilityError(analyzer.id, ["attachedTables"]);
62
- const fileSets = source.capabilities.fileSets ? fileSetsFor(plan) : void 0;
63
- const rows = await source.executeSql(plan.sql, plan.params, fileSets ? { fileSets } : void 0);
64
- const extras = {};
65
- if (plan.extraQueries) for (const q of plan.extraQueries) {
66
- const extraRows = await source.executeSql(q.sql, q.params, fileSets ? { fileSets } : void 0);
67
- extras[q.name] = extraRows;
68
- }
69
- const { results, meta } = analyzer.reduce(rows, {
70
- params,
71
- extras
72
- });
73
- const sourceMeta = source.capabilities.localSource ? { source: "local" } : {};
74
- return {
75
- results,
76
- meta: {
77
- tool: params.type,
78
- ...sourceMeta,
79
- ...meta
80
- }
81
- };
82
- }
83
9
  async function analyzeFromSource(source, params, registry) {
84
10
  return runAnalyzerFromSource(source, params, registry);
85
11
  }
86
- async function collectRows(gen) {
87
- const out = [];
88
- for await (const batch of gen) out.push(...batch);
89
- return out;
90
- }
91
- async function fetchGscTopN(opts) {
92
- const { client, siteUrl, dimension, range, orderByClicksDesc, limit, sliceTop } = opts;
93
- let builder = gsc.select(dimension).where(between(date, range.start, range.end));
94
- if (orderByClicksDesc) builder = builder.orderBy(clicks, "desc");
95
- if (typeof limit === "number") builder = builder.limit(limit);
96
- const mapped = (await collectRows(client.query(siteUrl, builder))).map((r) => {
97
- const row = r;
98
- const key = row[dimension.dimension];
99
- if (typeof key !== "string" || !key) return null;
100
- const impressions = Number(row.impressions ?? 0);
101
- const position = Number(row.position ?? 0);
102
- return {
103
- key,
104
- clicks: Number(row.clicks ?? 0),
105
- impressions,
106
- sum_position: position * impressions
107
- };
108
- }).filter((x) => x != null);
109
- if (!orderByClicksDesc) mapped.sort((a, b) => b.clicks - a.clicks);
110
- return typeof sliceTop === "number" ? mapped.slice(0, sliceTop) : mapped;
111
- }
112
- async function fetchGscDaily(opts) {
113
- const { client, siteUrl, range } = opts;
114
- const builder = gsc.select(date).where(between(date, range.start, range.end));
115
- return (await collectRows(client.query(siteUrl, builder))).map((r) => {
116
- const row = r;
117
- if (!row.date) return null;
118
- const impressions = row.impressions ?? 0;
119
- return {
120
- date: Date.parse(`${row.date}T00:00:00Z`),
121
- clicks: row.clicks ?? 0,
122
- impressions,
123
- sum_position: (row.position ?? 0) * impressions,
124
- anonymizedImpressionsPct: 0
125
- };
126
- }).filter((x) => x != null).sort((a, b) => a.date - b.date);
127
- }
128
- const METRIC_NAMES = [
129
- "clicks",
130
- "impressions",
131
- "ctr",
132
- "position"
133
- ];
134
- function isMetricDimension$2(dim) {
135
- return METRIC_NAMES.includes(dim);
136
- }
137
- function applyBuilderStatePostProcessing(rows, state) {
138
- const dimensionFilters = getDimensionFilters(state.filter, isMetricDimension$2);
139
- const metricFilters = extractMetricFilters(state.filter);
140
- const specialFilters = extractSpecialOperatorFilters(state.filter);
141
- const ordered = [...rows.filter((row) => {
142
- if (!dimensionFilters.every((filter) => matchesDimensionFilter(row, filter))) return false;
143
- if (!metricFilters.every((filter) => matchesMetricFilter(row, filter))) return false;
144
- if (specialFilters.some((filter) => filter.operator === "topLevel") && !matchesTopLevelPage(row)) return false;
145
- return true;
146
- })].sort((a, b) => {
147
- const column = state.orderBy?.column ?? "clicks";
148
- const dir = state.orderBy?.dir ?? "desc";
149
- const left = column === "date" ? String(a.date ?? "") : metricValue(a, column);
150
- const right = column === "date" ? String(b.date ?? "") : metricValue(b, column);
151
- if (left === right) return 0;
152
- if (dir === "asc") return left < right ? -1 : 1;
153
- return left > right ? -1 : 1;
154
- });
155
- const offset = Math.max(0, Number(state.startRow ?? 0));
156
- const limit = Math.max(0, Number((state.rowLimit ?? ordered.length) || 0));
157
- return ordered.slice(offset, offset + limit);
158
- }
159
- const GSC_API_CAPABILITIES = {
160
- regex: true,
161
- multiDataset: false,
162
- comparisonJoin: false,
163
- windowTotals: false
164
- };
165
- function isMetricDimension$1(dim) {
166
- return [
167
- "clicks",
168
- "impressions",
169
- "ctr",
170
- "position"
171
- ].includes(dim);
172
- }
173
- function builderFromState(state) {
174
- return { getState: () => state };
175
- }
176
- function createGscApiQuerySource(options) {
177
- const { client, siteUrl } = options;
178
- return {
179
- name: "gsc-api",
180
- capabilities: GSC_API_CAPABILITIES,
181
- async queryRows(state) {
182
- buildLogicalPlan(state, GSC_API_CAPABILITIES);
183
- const filterDims = getFilterDimensions(state.filter, isMetricDimension$1);
184
- assertDimensionsSupported([...state.dimensions, ...filterDims], "api", "gsc-api query source");
185
- return applyBuilderStatePostProcessing(await collectRows(client.query(siteUrl, builderFromState(state))), state);
186
- }
187
- };
188
- }
189
- const PRO_ONLY_DIMENSIONS = new Set(["queryCanonical", "page_keywords"]);
190
- function canProxyToGsc(state) {
191
- if (state.dimensions.some((d) => PRO_ONLY_DIMENSIONS.has(d))) return false;
192
- if (extractMetricFilters(state.filter).length > 0) return false;
193
- if (extractSpecialOperatorFilters(state.filter).length > 0) return false;
194
- return true;
195
- }
196
- function createLiveGscSource(opts) {
197
- let clientPromise = null;
198
- function getClient() {
199
- if (!clientPromise) clientPromise = opts.getAccessToken().then((accessToken) => googleSearchConsole({ accessToken }));
200
- return clientPromise;
201
- }
202
- return {
203
- name: "gsc-api",
204
- capabilities: {
205
- regex: true,
206
- multiDataset: false,
207
- comparisonJoin: false,
208
- windowTotals: false
209
- },
210
- async queryRows(state) {
211
- return createGscApiQuerySource({
212
- client: await getClient(),
213
- siteUrl: opts.siteUrl
214
- }).queryRows(state);
215
- }
216
- };
217
- }
218
12
  function createCompositeSource(opts) {
219
13
  const { engine, live, site } = opts;
220
14
  function rangeCovered(state) {
@@ -231,56 +25,6 @@ function createCompositeSource(opts) {
231
25
  executeSql: engine.executeSql
232
26
  };
233
27
  }
234
- function isMetricDimension(dim) {
235
- return [
236
- "clicks",
237
- "impressions",
238
- "ctr",
239
- "position"
240
- ].includes(dim);
241
- }
242
- const ENGINE_QUERY_CAPABILITIES = {
243
- regex: true,
244
- multiDataset: false,
245
- comparisonJoin: false,
246
- windowTotals: false
247
- };
248
- const ENGINE_SOURCE_CAPABILITIES = {
249
- ...ENGINE_QUERY_CAPABILITIES,
250
- fileSets: true,
251
- localSource: true
252
- };
253
- function createEngineQuerySource(options) {
254
- const { engine, ctx } = options;
255
- return {
256
- name: "engine",
257
- capabilities: ENGINE_SOURCE_CAPABILITIES,
258
- async queryRows(state) {
259
- const filterDims = getFilterDimensions(state.filter, isMetricDimension);
260
- assertDimensionsSupported([...state.dimensions, ...filterDims], "stored", "engine query source");
261
- if (state.dimensions.includes("queryCanonical") || filterDims.includes("queryCanonical")) throw new Error("engine query source does not support queryCanonical; use browser/sqlite query sources for derived dimensions");
262
- return (await engine.query(ctx, state)).rows;
263
- },
264
- async executeSql(sql, params, opts) {
265
- const fileSets = opts?.fileSets;
266
- if (!fileSets?.FILES) throw new Error("engine query source: executeSql requires opts.fileSets with a FILES entry");
267
- const { rows } = await engine.runSQL({
268
- ctx,
269
- table: fileSets.FILES.table,
270
- fileSets,
271
- sql,
272
- params: params ?? []
273
- });
274
- return rows;
275
- }
276
- };
277
- }
278
- async function runAnalyzerWithEngine(deps, ctx, params, registry) {
279
- return runAnalyzerFromSource(createEngineQuerySource({
280
- engine: deps.engine,
281
- ctx
282
- }), params, registry);
283
- }
284
28
  const IN_MEMORY_DEFAULT_CAPABILITIES = {
285
29
  regex: true,
286
30
  multiDataset: true,
@@ -306,114 +50,6 @@ function pagesQueryState(period, limit = DEFAULT_LIMIT$1) {
306
50
  function datesQueryState(period, limit = DEFAULT_LIMIT$1) {
307
51
  return gsc.select(date).where(between(date, period.startDate, period.endDate)).limit(limit).getState();
308
52
  }
309
- const DEFAULT_SQL_REQUIRES = ["executeSql", "partitionedParquet"];
310
- function defineAnalyzer(opts) {
311
- const { id, reduce, reduceSql, reduceRows, buildSql, buildRows, sqlRequires = DEFAULT_SQL_REQUIRES, rowsRequires = [] } = opts;
312
- const sqlReducer = reduceSql ?? reduce;
313
- const rowsReducer = reduceRows ?? reduce;
314
- if (buildSql && !sqlReducer) throw new Error(`defineAnalyzer(${id}): buildSql requires reduce or reduceSql`);
315
- if (buildRows && !rowsReducer) throw new Error(`defineAnalyzer(${id}): buildRows requires reduce or reduceRows`);
316
- const wrap = (fn) => (rows, params, ctx) => {
317
- return fn(Array.isArray(rows) ? rows : pickSingle(rows) ?? rows, params, ctx);
318
- };
319
- return {
320
- id,
321
- sql: buildSql && sqlReducer ? {
322
- id,
323
- requires: sqlRequires,
324
- build(params) {
325
- const spec = buildSql(params);
326
- return {
327
- kind: "sql",
328
- sql: spec.sql,
329
- params: spec.params,
330
- current: spec.current,
331
- previous: spec.previous,
332
- extraFiles: spec.extraFiles,
333
- extraQueries: spec.extraQueries,
334
- requiresAttachedTables: spec.requiresAttachedTables
335
- };
336
- },
337
- reduce(rows, ctx) {
338
- const { results, meta } = wrap(sqlReducer)(rows, ctx.params, { extras: ctx.extras });
339
- return {
340
- results,
341
- meta
342
- };
343
- }
344
- } : void 0,
345
- rows: buildRows && rowsReducer ? {
346
- id,
347
- requires: rowsRequires,
348
- build(params) {
349
- const queries = buildRows(params);
350
- return {
351
- kind: "rows",
352
- queries: Object.fromEntries(Object.entries(queries).map(([k, state]) => [k, { state }]))
353
- };
354
- },
355
- reduce(rows, ctx) {
356
- const { results, meta } = wrap(rowsReducer)(rows, ctx.params, {});
357
- return {
358
- results,
359
- meta
360
- };
361
- }
362
- } : void 0
363
- };
364
- }
365
- function pickSingle(rows) {
366
- const keys = Object.keys(rows);
367
- return keys.length === 1 ? rows[keys[0]] : void 0;
368
- }
369
- function defaultEndDate() {
370
- return daysAgo(3);
371
- }
372
- function defaultStartDate() {
373
- return daysAgo(31);
374
- }
375
- function periodOf(params) {
376
- return {
377
- startDate: params.startDate || defaultStartDate(),
378
- endDate: params.endDate || defaultEndDate()
379
- };
380
- }
381
- function comparisonOf(params) {
382
- if (!params.prevStartDate || !params.prevEndDate) throw new Error(`${params.type} analysis requires prevStartDate and prevEndDate`);
383
- return {
384
- current: periodOf(params),
385
- previous: {
386
- startDate: params.prevStartDate,
387
- endDate: params.prevEndDate
388
- }
389
- };
390
- }
391
- function num(v) {
392
- if (typeof v === "number") return v;
393
- if (typeof v === "bigint") return Number(v);
394
- if (v == null) return 0;
395
- return Number(v);
396
- }
397
- function buildPeriodMap(rows, key, value, filter) {
398
- const out = /* @__PURE__ */ new Map();
399
- for (const row of rows) {
400
- if (filter && !filter(row)) continue;
401
- out.set(key(row), value(row));
402
- }
403
- return out;
404
- }
405
- function createSorter(getValue, defaultMetric, defaultOrder = "desc") {
406
- return (items, sortBy = defaultMetric, sortOrder = defaultOrder) => {
407
- const mult = sortOrder === "desc" ? -1 : 1;
408
- return [...items].sort((a, b) => (getValue(a, sortBy) - getValue(b, sortBy)) * mult);
409
- };
410
- }
411
- function createMetricSorter(defaultMetric, orderByMetric) {
412
- return (items, sortBy = defaultMetric) => {
413
- const mult = orderByMetric[sortBy] === "desc" ? -1 : 1;
414
- return [...items].sort((a, b) => (a[sortBy] - b[sortBy]) * mult);
415
- };
416
- }
417
53
  function escapeRegexAlt(s) {
418
54
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
419
55
  }
@@ -939,6 +575,26 @@ defineAnalyzer({
939
575
  };
940
576
  }
941
577
  });
578
+ function buildPeriodMap(rows, key, value, filter) {
579
+ const out = /* @__PURE__ */ new Map();
580
+ for (const row of rows) {
581
+ if (filter && !filter(row)) continue;
582
+ out.set(key(row), value(row));
583
+ }
584
+ return out;
585
+ }
586
+ function createSorter(getValue, defaultMetric, defaultOrder = "desc") {
587
+ return (items, sortBy = defaultMetric, sortOrder = defaultOrder) => {
588
+ const mult = sortOrder === "desc" ? -1 : 1;
589
+ return [...items].sort((a, b) => (getValue(a, sortBy) - getValue(b, sortBy)) * mult);
590
+ };
591
+ }
592
+ function createMetricSorter(defaultMetric, orderByMetric) {
593
+ return (items, sortBy = defaultMetric) => {
594
+ const mult = orderByMetric[sortBy] === "desc" ? -1 : 1;
595
+ return [...items].sort((a, b) => (a[sortBy] - b[sortBy]) * mult);
596
+ };
597
+ }
942
598
  const sortResults$2 = createMetricSorter("lostClicks", {
943
599
  lostClicks: "desc",
944
600
  declinePercent: "desc",
@@ -1769,23 +1425,6 @@ function analyzeStrikingDistance(keywords, options = {}) {
1769
1425
  }
1770
1426
  return sortResults(results, sortBy, sortOrder);
1771
1427
  }
1772
- function typedQuery(state) {
1773
- return { state };
1774
- }
1775
- function isTypedQuery(value) {
1776
- return "state" in value;
1777
- }
1778
- async function queryRows(source, query) {
1779
- const state = isTypedQuery(query) ? query.state : query;
1780
- return await source.queryRows(state);
1781
- }
1782
- async function queryComparisonRows(source, current, previous) {
1783
- const [currentRows, previousRows] = await Promise.all([queryRows(source, current), queryRows(source, previous)]);
1784
- return {
1785
- current: currentRows,
1786
- previous: previousRows
1787
- };
1788
- }
1789
1428
  function keywordQuery(period, limit) {
1790
1429
  return typedQuery(keywordsQueryState(period, limit));
1791
1430
  }
@@ -1907,4 +1546,4 @@ async function analyzeDecayFromSource(source, periods, options) {
1907
1546
  async function analyzeMoversFromSource(source, periods, options) {
1908
1547
  return runPortableAnalyzer(source, PORTABLE_ANALYZERS.movers, periods, options);
1909
1548
  }
1910
- export { AnalyzerCapabilityError, ENGINE_QUERY_CAPABILITIES, GSC_API_CAPABILITIES, IN_MEMORY_DEFAULT_CAPABILITIES, analyzeBrandSegmentationFromSource, analyzeClusteringFromSource, analyzeDecayFromSource, analyzeFromSource, analyzeKeywordConcentrationFromSource, analyzeMoversFromSource, analyzeOpportunityFromSource, analyzePageConcentrationFromSource, analyzeSeasonalityFromSource, analyzeStrikingDistanceFromSource, canProxyToGsc, collectRows as collectGscRows, createBrowserQuerySource, createCompositeSource, createEngineQuerySource, createGscApiQuerySource, createInMemoryQuerySource, createLiveGscSource, createSqliteQuerySource, fetchGscDaily, fetchGscTopN, isSqlQuerySource, queryAnalyticsFromSource, queryComparisonFromSource, queryComparisonRows, queryRows, runAnalyzerWithEngine, typedQuery };
1549
+ export { AnalyzerCapabilityError, IN_MEMORY_DEFAULT_CAPABILITIES, analyzeBrandSegmentationFromSource, analyzeClusteringFromSource, analyzeDecayFromSource, analyzeFromSource, analyzeKeywordConcentrationFromSource, analyzeMoversFromSource, analyzeOpportunityFromSource, analyzePageConcentrationFromSource, analyzeSeasonalityFromSource, analyzeStrikingDistanceFromSource, createCompositeSource, createInMemoryQuerySource, queryAnalyticsFromSource, queryComparisonFromSource };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/analysis",
3
3
  "type": "module",
4
- "version": "0.6.3",
4
+ "version": "0.7.5",
5
5
  "description": "GSC analyzers — striking-distance, opportunity, movers, decay, brand, clustering, concentration, seasonality. Pure row-based + DuckDB-native.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -23,31 +23,43 @@
23
23
  "exports": {
24
24
  ".": {
25
25
  "types": "./dist/index.d.mts",
26
- "import": "./dist/index.mjs"
26
+ "import": "./dist/index.mjs",
27
+ "default": "./dist/index.mjs"
27
28
  },
28
29
  "./analyzer": {
29
30
  "types": "./dist/analyzer/index.d.mts",
30
- "import": "./dist/analyzer/index.mjs"
31
+ "import": "./dist/analyzer/index.mjs",
32
+ "default": "./dist/analyzer/index.mjs"
31
33
  },
32
34
  "./registry": {
33
35
  "types": "./dist/default-registry.d.mts",
34
- "import": "./dist/default-registry.mjs"
36
+ "import": "./dist/default-registry.mjs",
37
+ "default": "./dist/default-registry.mjs"
35
38
  },
36
39
  "./query": {
37
40
  "types": "./dist/query/index.d.mts",
38
- "import": "./dist/query/index.mjs"
41
+ "import": "./dist/query/index.mjs",
42
+ "default": "./dist/query/index.mjs"
39
43
  },
40
44
  "./source": {
41
45
  "types": "./dist/source/index.d.mts",
42
- "import": "./dist/source/index.mjs"
46
+ "import": "./dist/source/index.mjs",
47
+ "default": "./dist/source/index.mjs"
43
48
  },
44
49
  "./semantic": {
45
50
  "types": "./dist/semantic/index.d.mts",
46
- "import": "./dist/semantic/index.mjs"
51
+ "import": "./dist/semantic/index.mjs",
52
+ "default": "./dist/semantic/index.mjs"
47
53
  },
48
- "./period": {
49
- "types": "./dist/period/index.d.mts",
50
- "import": "./dist/period/index.mjs"
54
+ "./routing": {
55
+ "types": "./dist/routing/index.d.mts",
56
+ "import": "./dist/routing/index.mjs",
57
+ "default": "./dist/routing/index.mjs"
58
+ },
59
+ "./rollups": {
60
+ "types": "./dist/rollups.d.mts",
61
+ "import": "./dist/rollups.mjs",
62
+ "default": "./dist/rollups.mjs"
51
63
  }
52
64
  },
53
65
  "main": "./dist/index.mjs",
@@ -68,11 +80,9 @@
68
80
  },
69
81
  "dependencies": {
70
82
  "drizzle-orm": "^0.45.2",
71
- "@gscdump/engine-duckdb-node": "0.6.3",
72
- "@gscdump/engine-sqlite": "0.6.3",
73
- "@gscdump/engine-wasm": "0.6.3",
74
- "gscdump": "0.6.3",
75
- "@gscdump/engine": "0.6.3"
83
+ "@gscdump/engine": "0.7.5",
84
+ "@gscdump/engine-gsc-api": "0.7.5",
85
+ "gscdump": "0.7.5"
76
86
  },
77
87
  "devDependencies": {
78
88
  "vitest": "^4.1.5"
@@ -1,57 +0,0 @@
1
- import { AnalysisParams } from "gscdump/contracts";
2
- type WindowPreset = 'last-7d' | 'last-28d' | 'last-30d' | 'last-90d' | 'last-180d' | 'last-365d' | 'mtd' | 'ytd' | 'custom';
3
- type ComparisonMode = 'none' | 'prev-period' | 'yoy';
4
- interface ResolveWindowOptions {
5
- preset: WindowPreset;
6
- comparison?: ComparisonMode;
7
- anchor?: string;
8
- start?: string;
9
- end?: string;
10
- }
11
- interface ResolvedWindow {
12
- start: string;
13
- end: string;
14
- days: number;
15
- comparison?: {
16
- start: string;
17
- end: string;
18
- };
19
- }
20
- interface AnalysisPeriod {
21
- startDate: string;
22
- endDate: string;
23
- }
24
- interface ComparisonPeriod {
25
- current: AnalysisPeriod;
26
- previous: AnalysisPeriod;
27
- }
28
- declare function defaultEndDate(): string;
29
- declare function defaultStartDate(): string;
30
- declare function periodOf(params: AnalysisParams): AnalysisPeriod;
31
- declare function comparisonOf(params: AnalysisParams): ComparisonPeriod;
32
- declare function resolveWindow(opts: ResolveWindowOptions): ResolvedWindow;
33
- /** Convert a ResolvedWindow into the AnalysisPeriod / ComparisonPeriod shape. */
34
- declare function windowToPeriod(w: ResolvedWindow): AnalysisPeriod;
35
- declare function windowToComparisonPeriod(w: ResolvedWindow): ComparisonPeriod | undefined;
36
- interface PadTimeseriesOptions<T> {
37
- /** ISO date (YYYY-MM-DD), inclusive lower bound. */
38
- startDate: string;
39
- /** ISO date (YYYY-MM-DD), inclusive upper bound. */
40
- endDate: string;
41
- /**
42
- * Row to insert for missing dates. Defaults to `{ clicks: 0, impressions: 0, ctr: 0, position: 0 }`.
43
- * The `date` field is set automatically.
44
- */
45
- fill?: Omit<T, 'date'>;
46
- /** Row-field that carries the ISO date. Defaults to `date`. */
47
- dateKey?: string;
48
- }
49
- type DateRowShape = Record<string, unknown> & {
50
- date?: unknown;
51
- };
52
- /**
53
- * Pad rows so every calendar day in `[startDate, endDate]` appears at least
54
- * once. Existing dates keep all their rows (grouped timeseries safe).
55
- */
56
- declare function padTimeseries<T extends DateRowShape = DateRowShape>(rows: readonly T[], options: PadTimeseriesOptions<T>): T[];
57
- export { AnalysisPeriod, ComparisonMode, ComparisonPeriod, PadTimeseriesOptions, ResolveWindowOptions, ResolvedWindow, WindowPreset, comparisonOf, defaultEndDate, defaultStartDate, padTimeseries, periodOf, resolveWindow, windowToComparisonPeriod, windowToPeriod };