@cloudcreate/adsense-check 1.0.1 → 1.1.0

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/dist/cli.js CHANGED
@@ -1,108 +1,189 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- check
4
- } from "./chunk-V2YZ36NU.js";
3
+ BrowserManager,
4
+ analyzeSiteTopic,
5
+ check,
6
+ detectSiteType,
7
+ fetchPage,
8
+ getSupportedLangs,
9
+ isValidLang,
10
+ t
11
+ } from "./chunk-GW4SHZYX.js";
5
12
 
6
13
  // src/cli.ts
7
14
  import "dotenv/config";
8
15
  import { Command } from "commander";
9
16
  import chalk2 from "chalk";
10
17
  import { mkdirSync, writeFileSync } from "fs";
11
- import { join, dirname } from "path";
12
- import { fileURLToPath } from "url";
18
+ import { join } from "path";
13
19
 
14
20
  // src/reporter.ts
15
21
  import chalk from "chalk";
16
22
  import figures from "figures";
17
- var STATUS_ICONS = {
23
+ var ICONS = {
18
24
  pass: chalk.green(figures.tick),
19
25
  warn: chalk.yellow(figures.warning),
20
26
  fail: chalk.red(figures.cross),
21
27
  skip: chalk.gray("-")
22
28
  };
23
- var STATUS_LABELS = {
29
+ var LABELS = {
24
30
  pass: chalk.green("PASS"),
25
31
  warn: chalk.yellow("WARN"),
26
32
  fail: chalk.red("FAIL"),
27
33
  skip: chalk.gray("SKIP")
28
34
  };
29
- function getStatusSummary(report) {
30
- if (report.failed > 0) {
31
- return chalk.red.bold(`NOT READY \u2014 ${report.failed} \u9879\u5931\u8D25\u9700\u8981\u4FEE\u590D`);
32
- }
33
- if (report.warned > 0) {
34
- return chalk.yellow.bold(`MOSTLY READY \u2014 \u4FEE\u590D ${report.warned} \u9879\u8B66\u544A\u540E\u53EF\u63D0\u4EA4\u5BA1\u6838`);
35
- }
36
- return chalk.green.bold("READY \u2014 \u53EF\u4EE5\u63D0\u4EA4 AdSense \u5BA1\u6838");
35
+ function renderBar(score, max, width = 20) {
36
+ const ratio = max > 0 ? score / max : 0;
37
+ const filled = Math.round(ratio * width);
38
+ const empty = width - filled;
39
+ const color = ratio >= 0.8 ? chalk.green : ratio >= 0.5 ? chalk.yellow : chalk.red;
40
+ return color("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
41
+ }
42
+ function categoryScore(cat) {
43
+ if (cat.items.length === 0) return 100;
44
+ const earned = cat.items.reduce((s, i) => {
45
+ if (i.status === "pass") return s + 100;
46
+ if (i.status === "warn") return s + 40;
47
+ return s;
48
+ }, 0);
49
+ return Math.round(earned / cat.items.length);
37
50
  }
38
51
  function renderTerminalReport(report) {
39
- const lines = [];
52
+ const lang = report.lang;
53
+ const typeKey = `detector.type.${report.siteType}`;
54
+ const typeLabel = t(typeKey, lang);
55
+ const confidenceLabel = report.siteTypeConfidence === "high" ? "" : ` (${report.siteTypeConfidence})`;
56
+ const lines = [
57
+ "",
58
+ chalk.bold.cyan(` ${t("report.title", lang)}`),
59
+ chalk.gray(` URL: ${report.url}`),
60
+ chalk.gray(` Time: ${report.timestamp}`),
61
+ chalk.gray(` Site type: ${typeLabel}${confidenceLabel}`)
62
+ ];
63
+ if (report.siteTopic) {
64
+ lines.push(chalk.gray(` Topic: ${report.siteTopic.topic} \u2014 ${report.siteTopic.description}`));
65
+ }
66
+ if (report.samplingInfo) {
67
+ const s = report.samplingInfo;
68
+ const confColor = s.confidence === "high" ? chalk.green : s.confidence === "medium" ? chalk.yellow : chalk.red;
69
+ lines.push(chalk.gray(` Pages: ${s.totalDiscovered} total, ${s.recentCount} recent (6mo), ${s.sampledCount} sampled (${s.samplePct}%) ${confColor(s.confidence + " confidence")}`));
70
+ }
71
+ if (report.siteType === "unsupported") {
72
+ lines.push("");
73
+ lines.push(chalk.red.bold(` ${t("topic.unsupported_warning", lang, { type: report.siteTopic?.topic ?? "unknown" })}`));
74
+ }
40
75
  lines.push("");
41
- lines.push(chalk.bold.cyan(" AdSense Checklist Report"));
42
- lines.push(chalk.gray(` Website: ${report.url}`));
43
- lines.push(chalk.gray(` Checked: ${report.timestamp}`));
76
+ const scoreColor = report.compositeScore >= 80 ? chalk.green.bold : report.compositeScore >= 50 ? chalk.yellow.bold : chalk.red.bold;
77
+ lines.push(chalk.bold(` ${t("report.composite_score", lang)}: `) + scoreColor(`${report.compositeScore}/100`));
44
78
  lines.push("");
45
- for (const category of report.categories) {
46
- lines.push(chalk.bold(` ${category.name}`));
47
- for (const item of category.items) {
48
- const icon = STATUS_ICONS[item.status];
49
- const label = STATUS_LABELS[item.status];
50
- lines.push(` ${icon} [${label}] ${item.message}`);
51
- if (item.detail) {
52
- lines.push(chalk.gray(` ${item.detail}`));
53
- }
79
+ const hardColor = report.hardStatus === "ready" ? chalk.green : report.hardStatus === "warn" ? chalk.yellow : chalk.red;
80
+ const hardLabel = report.hardStatus === "ready" ? "PASS" : report.hardStatus === "warn" ? "WARN" : "FAIL";
81
+ lines.push(chalk.bold(` \u250C\u2500 ${t("report.hard_requirements", lang)} `) + chalk.gray("\u2500".repeat(Math.max(0, 40 - t("report.hard_requirements", lang).length))) + ` ${hardColor.bold(hardLabel)}`);
82
+ for (const cat of report.hardCategories) {
83
+ const catScore = categoryScore(cat);
84
+ const catIcon = cat.items.every((i) => i.status === "pass") ? ICONS.pass : cat.items.some((i) => i.status === "fail") ? ICONS.fail : ICONS.warn;
85
+ for (const item of cat.items) {
86
+ lines.push(` \u2502 ${ICONS[item.status]} ${chalk.bold(item.name.padEnd(16))} ${item.message}`);
87
+ }
88
+ }
89
+ const hardStatusKey = `report.hard.${report.hardStatus}`;
90
+ const hardWarnCount = report.hardCategories.flatMap((c) => c.items).filter((i) => i.status === "warn").length;
91
+ const hardFailCount = report.hardCategories.flatMap((c) => c.items).filter((i) => i.status === "fail").length;
92
+ const hardStatusMsg = report.hardStatus === "ready" ? t(hardStatusKey, lang) : t(hardStatusKey, lang, { count: report.hardStatus === "fail" ? hardFailCount : hardWarnCount });
93
+ lines.push(chalk.gray(` \u2502`));
94
+ lines.push(` \u2514\u2500 ${t("report.score", lang)}: ${hardStatusMsg}`);
95
+ lines.push("");
96
+ lines.push(chalk.bold(` \u250C\u2500 ${t("report.soft_scoring", lang)} `) + chalk.gray("\u2500".repeat(Math.max(0, 40 - t("report.soft_scoring", lang).length))) + ` ${scoreColor(report.softScore + "/100")}`);
97
+ for (const cat of report.softCategories) {
98
+ const score = categoryScore(cat);
99
+ const bar = renderBar(score, 100);
100
+ const pct = `${score}%`;
101
+ lines.push(` \u2502 ${bar} ${pct.padStart(4)} ${cat.name}`);
102
+ }
103
+ if (report.warningPenalty > 0) {
104
+ lines.push(chalk.gray(` \u2502`));
105
+ lines.push(chalk.yellow(` \u2502 \u26A0 ${t("report.warning_ratio", lang, { count: report.warned, total: report.totalChecks, pct: Math.round(report.warningRatio * 100) })} \u2192 ${t("report.warning_penalty", lang, { points: report.warningPenalty })}`));
106
+ }
107
+ lines.push(chalk.gray(` \u2502`));
108
+ const hardContrib = Math.round(report.hardStatus === "ready" ? 100 * 0.4 : report.hardCategories.flatMap((c) => c.items).filter((i) => i.status === "pass").length / Math.max(1, report.hardCategories.flatMap((c) => c.items).length) * 100 * 0.4);
109
+ const softContrib = Math.round(report.softScore * 0.6);
110
+ lines.push(chalk.gray(` \u2502 Hard ${Math.round(hardContrib)}% \xD7 0.4 + Soft ${report.softScore}% \xD7 0.6 - Penalty ${report.warningPenalty} = ${report.compositeScore}`));
111
+ lines.push(chalk.gray(` \u2514\u2500`));
112
+ lines.push("");
113
+ if (report.categoryScores.length > 0) {
114
+ for (const cs of report.categoryScores) {
115
+ const bar = renderBar(cs.score, cs.maxScore);
116
+ const pct = cs.maxScore > 0 ? Math.round(cs.score / cs.maxScore * 100) : 0;
117
+ lines.push(` ${bar} ${pct}% ${cs.name}`);
118
+ }
119
+ lines.push("");
120
+ }
121
+ for (const cat of report.categories) {
122
+ lines.push(chalk.bold(` ${cat.name}`));
123
+ for (const item of cat.items) {
124
+ lines.push(` ${ICONS[item.status]} [${LABELS[item.status]}] ${item.message}`);
125
+ if (item.detail) lines.push(chalk.gray(` ${item.detail}`));
54
126
  }
55
127
  lines.push("");
56
128
  }
57
129
  if (report.pages.length > 0) {
58
- lines.push(chalk.bold(" Page Details"));
59
- lines.push(chalk.gray(` (${report.pages.length} pages analyzed)`));
130
+ lines.push(chalk.bold(` ${t("report.page_details", lang)}`));
131
+ lines.push(chalk.gray(` (${t("report.pages", lang, { count: report.pages.length })})`));
60
132
  lines.push("");
61
- const problemPages = report.pages.filter(
62
- (p) => p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass"
63
- );
64
- const okPages = report.pages.filter(
65
- (p) => p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass")
66
- );
67
- for (const page of problemPages) {
68
- renderPageDetail(lines, page, true);
69
- }
70
- if (okPages.length > 0) {
71
- lines.push(chalk.gray(` + ${okPages.length} \u4E2A\u9875\u9762\u65E0\u95EE\u9898`));
72
- lines.push("");
73
- }
133
+ const problems = report.pages.filter((p) => p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass");
134
+ const ok = report.pages.filter((p) => p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass"));
135
+ for (const p of problems) renderPage(lines, p, lang);
136
+ if (ok.length > 0) lines.push(chalk.gray(` ${t("report.pages_ok", lang, { count: ok.length })}`));
137
+ lines.push("");
138
+ }
139
+ lines.push(chalk.bold(` ${t("report.score", lang)}: `) + `${report.score}/${report.totalChecks}`);
140
+ if (report.hardStatus === "fail") {
141
+ lines.push(chalk.red.bold(` ${t("report.notready", lang, { count: hardFailCount })}`));
142
+ } else if (report.hardStatus === "warn") {
143
+ lines.push(chalk.yellow.bold(` ${t("report.hard.warn", lang, { count: hardWarnCount })}`));
144
+ } else if (report.warned > 0) {
145
+ lines.push(chalk.yellow.bold(` ${t("report.mostly", lang, { count: report.warned })}`));
146
+ } else {
147
+ lines.push(chalk.green.bold(` ${t("report.ready", lang)}`));
148
+ }
149
+ const hasAi = report.categories.some((c) => c.group === "soft" && c.name.includes("AI"));
150
+ if (!hasAi) {
151
+ lines.push("");
152
+ lines.push(chalk.cyan(` \u{1F4A1} ${t("ai.suggest_enable", lang)}`));
74
153
  }
75
- lines.push(chalk.bold(" Score: ") + `${report.score}/${report.totalChecks}`);
76
- lines.push(` Status: ${getStatusSummary(report)}`);
77
154
  lines.push("");
78
155
  return lines.join("\n");
79
156
  }
80
- function renderPageDetail(lines, page, verbose) {
81
- const path = safePath(page.url);
82
- const statusIcon = STATUS_ICONS[page.contentStatus];
157
+ var PAGE_TYPE_ICONS = {
158
+ homepage: chalk.cyan("*"),
159
+ content: chalk.green("A"),
160
+ game_detail: chalk.blue("G"),
161
+ required: chalk.yellow("!"),
162
+ listing: chalk.gray("L"),
163
+ utility: chalk.gray("#"),
164
+ unknown: chalk.gray("?")
165
+ };
166
+ function renderPage(lines, page, lang) {
167
+ const path = (() => {
168
+ try {
169
+ return new URL(page.url).pathname;
170
+ } catch {
171
+ return page.url;
172
+ }
173
+ })();
83
174
  const ratioColor = page.contentRatio >= 50 ? chalk.green : page.contentRatio >= 30 ? chalk.yellow : chalk.red;
84
- lines.push(` ${statusIcon} ${chalk.bold(path)}`);
175
+ const scoreColor = page.score >= 80 ? chalk.green : page.score >= 50 ? chalk.yellow : chalk.red;
176
+ const typeIcon = PAGE_TYPE_ICONS[page.pageType] || chalk.gray("?");
177
+ lines.push(` ${ICONS[page.contentStatus]} ${typeIcon} ${chalk.bold(path)} ${scoreColor(page.score + "/100")}`);
85
178
  lines.push(chalk.gray(` ${page.title}`));
86
- lines.push(` \u6B63\u6587 ${ratioColor(page.contentRatio + "%")} (${page.contentChars}/${page.totalChars} \u5B57)`);
87
- for (const issue of page.issues) {
88
- lines.push(chalk.yellow(` ! ${issue}`));
89
- }
179
+ lines.push(` ${t("report.content_label", lang)} ${ratioColor(page.contentRatio + "%")} (${page.contentChars}/${page.totalChars})`);
180
+ for (const issue of page.issues) lines.push(chalk.yellow(` ! ${issue}`));
90
181
  if (page.ai) {
91
- const aiIcon = STATUS_ICONS[page.ai.status];
92
- lines.push(` ${aiIcon} AI: ${truncate(page.ai.assessment, 80)}`);
93
- for (const s of page.ai.suggestions.slice(0, 2)) {
94
- lines.push(chalk.gray(` \u2192 ${truncate(s, 70)}`));
95
- }
182
+ lines.push(` ${ICONS[page.ai.status]} AI: ${truncate(page.ai.assessment, 80)}`);
183
+ for (const s of page.ai.suggestions.slice(0, 2)) lines.push(chalk.gray(` -> ${truncate(s, 70)}`));
96
184
  }
97
185
  lines.push("");
98
186
  }
99
- function safePath(url) {
100
- try {
101
- return new URL(url).pathname;
102
- } catch {
103
- return url;
104
- }
105
- }
106
187
  function truncate(s, max) {
107
188
  return s.length > max ? s.slice(0, max - 1) + "..." : s;
108
189
  }
@@ -111,11 +192,10 @@ function renderJsonReport(report) {
111
192
  }
112
193
 
113
194
  // src/cli.ts
114
- var __dirname = dirname(fileURLToPath(import.meta.url));
115
195
  function formatTimestamp() {
116
196
  const d = /* @__PURE__ */ new Date();
117
- const pad = (n) => String(n).padStart(2, "0");
118
- return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
197
+ const p = (n) => String(n).padStart(2, "0");
198
+ return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
119
199
  }
120
200
  function getDomain(url) {
121
201
  try {
@@ -125,53 +205,100 @@ function getDomain(url) {
125
205
  }
126
206
  }
127
207
  var program = new Command();
128
- program.name("adsense-check").description("Check if a website meets Google AdSense review requirements").version("1.0.0").argument("<url>", "Website URL to check").option("-j, --json", "Output as JSON to stdout").option("-d, --depth <number>", "Number of internal pages to crawl", "10").option("-s, --skip-ai", "Skip AI content analysis", false).option("-t, --timeout <ms>", "Page load timeout in milliseconds", "30000").option("--api-key <key>", "AI API key (or set AI_API_KEY in .env)").option("-o, --output <dir>", "Report output directory", "tmp").option("--no-save", "Skip auto-saving report files").action(async (url, opts) => {
208
+ program.name("adsense-check").description("Check if a website meets Google AdSense review requirements").version("1.0.0").argument("<url>", "Website URL to check").option("-j, --json", "Output JSON to stdout").option("-n, --max-crawl <number>", "Total page crawl limit (Phase 1 + 2)", "50").option("-m, --page-limit <number>", "Max structural pages to crawl (Phase 1)", "50").option("-c, --content-limit <number>", "Max content pages to crawl (Phase 2)", "20").option("--sample-min <number>", "Min content pages to sample", "20").option("--sample-ratio <ratio>", "Content page sampling ratio (0-1)", "0.2").option("--ai", "Enable AI content quality analysis", false).option("-t, --timeout <ms>", "Page load timeout", "30000").option("--api-key <key>", "AI API key").option("-o, --output <dir>", "Report output directory", "tmp").option("--no-save", "Skip auto-saving report").option("-l, --lang <lang>", `Output language (${getSupportedLangs().join("|")})`, "en").option("--type <type>", "Force site type (content|tool|game), skip auto-detection").option("--detect-only", "Only detect site type/topic, skip full check").action(async (url, opts) => {
129
209
  try {
130
210
  new URL(url);
131
211
  } catch {
132
212
  console.error(chalk2.red(`Error: Invalid URL "${url}"`));
133
213
  process.exit(1);
134
214
  }
135
- if (!url.startsWith("http")) {
136
- url = "https://" + url;
215
+ if (!url.startsWith("http")) url = "https://" + url;
216
+ const lang = isValidLang(opts.lang) ? opts.lang : "en";
217
+ const validTypes = ["content", "tool", "game"];
218
+ const siteType = validTypes.includes(opts.type) ? opts.type : void 0;
219
+ if (opts.detectOnly) {
220
+ const browser = new BrowserManager();
221
+ try {
222
+ process.stderr.write(chalk2.cyan(`\u25CF Detecting site type for ${url}...
223
+ `));
224
+ const page = await browser.newPage();
225
+ const data = await fetchPage(page, url, parseInt(opts.timeout, 10));
226
+ await page.close();
227
+ const domResult = detectSiteType([data.signals], data.navText + " " + data.footerText, siteType);
228
+ process.stderr.write(chalk2.gray(` DOM detection: ${domResult.type} (${domResult.confidence})
229
+ `));
230
+ const apiKey = opts.apiKey || process.env.AI_API_KEY;
231
+ if (opts.ai && apiKey) {
232
+ process.stderr.write(chalk2.gray(" AI: analyzing topic...\n"));
233
+ const topic = await analyzeSiteTopic(
234
+ { title: data.title, text: data.text, navText: data.navText + " " + data.footerText },
235
+ lang,
236
+ apiKey
237
+ );
238
+ console.log(JSON.stringify({
239
+ domType: domResult.type,
240
+ domConfidence: domResult.confidence,
241
+ aiType: topic.type,
242
+ topic: topic.topic,
243
+ description: topic.description,
244
+ confidence: topic.confidence,
245
+ reasoning: topic.reasoning
246
+ }, null, 2));
247
+ } else {
248
+ console.log(JSON.stringify({
249
+ type: domResult.type,
250
+ confidence: domResult.confidence,
251
+ signals: domResult.signals
252
+ }, null, 2));
253
+ }
254
+ await browser.close();
255
+ process.exit(0);
256
+ } catch (err) {
257
+ await browser.close();
258
+ console.error(chalk2.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
259
+ process.exit(2);
260
+ }
137
261
  }
138
- const spinner = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
139
- let frame = 0;
140
- const interval = setInterval(() => {
141
- process.stderr.write(`\r${chalk2.cyan(spinner[frame++ % spinner.length])} Checking ${url}...`);
142
- }, 80);
262
+ process.stderr.write(chalk2.cyan(`\u25CF Checking ${url}...
263
+ `));
143
264
  try {
265
+ let lastProgress = "";
144
266
  const report = await check({
145
267
  url,
146
- depth: parseInt(opts.depth, 10),
147
- skipAi: opts.skipAi,
268
+ maxCrawl: parseInt(opts.maxCrawl, 10),
269
+ maxPages: parseInt(opts.pageLimit, 10),
270
+ maxContent: parseInt(opts.contentLimit, 10),
271
+ sampleMin: parseInt(opts.sampleMin, 10),
272
+ sampleRatio: parseFloat(opts.sampleRatio),
273
+ siteType,
274
+ skipAi: !opts.ai,
148
275
  timeout: parseInt(opts.timeout, 10),
149
- apiKey: opts.apiKey
276
+ apiKey: opts.apiKey,
277
+ lang,
278
+ onProgress: (msg) => {
279
+ lastProgress = msg;
280
+ const line = `\r${chalk2.cyan("\u25CF")} ${chalk2.gray(msg)}`;
281
+ process.stderr.write(line + " ".repeat(Math.max(0, 60 - msg.length)));
282
+ }
150
283
  });
151
- clearInterval(interval);
152
- process.stderr.write("\r" + " ".repeat(60) + "\r");
153
- if (opts.json) {
154
- console.log(renderJsonReport(report));
155
- } else {
156
- console.log(renderTerminalReport(report));
157
- }
284
+ process.stderr.write("\r" + " ".repeat(80) + "\r");
285
+ if (opts.json) console.log(renderJsonReport(report));
286
+ else console.log(renderTerminalReport(report));
158
287
  if (opts.save !== false) {
159
288
  const ts = formatTimestamp();
160
289
  const domain = getDomain(url);
161
290
  const outDir = join(process.cwd(), opts.output);
162
291
  try {
163
292
  mkdirSync(outDir, { recursive: true });
164
- const jsonPath = join(outDir, `${domain}-${ts}.json`);
165
- writeFileSync(jsonPath, renderJsonReport(report), "utf-8");
166
- console.log(chalk2.gray(` Report saved: ${jsonPath}`));
167
- } catch (saveErr) {
168
- console.error(chalk2.yellow(` Warning: Failed to save report: ${saveErr instanceof Error ? saveErr.message : String(saveErr)}`));
293
+ const path = join(outDir, `${domain}-${ts}.json`);
294
+ writeFileSync(path, renderJsonReport(report), "utf-8");
295
+ console.log(chalk2.gray(` ${t("report.saved", lang)}: ${path}`));
296
+ } catch {
169
297
  }
170
298
  }
171
299
  process.exit(report.failed > 0 ? 1 : 0);
172
300
  } catch (err) {
173
- clearInterval(interval);
174
- process.stderr.write("\r" + " ".repeat(60) + "\r");
301
+ process.stderr.write("\r" + " ".repeat(80) + "\r");
175
302
  console.error(chalk2.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
176
303
  process.exit(2);
177
304
  }
package/dist/index.d.ts CHANGED
@@ -1,32 +1,65 @@
1
1
  type CheckStatus = 'pass' | 'warn' | 'fail' | 'skip';
2
+ type Lang = string;
3
+ type SiteType = 'content' | 'tool' | 'game' | 'unsupported';
4
+ type PageType = 'homepage' | 'content' | 'game_detail' | 'required' | 'listing' | 'utility' | 'unknown';
2
5
  interface CheckItem {
3
6
  name: string;
4
7
  status: CheckStatus;
5
8
  message: string;
6
9
  detail?: string;
7
10
  }
11
+ type CheckGroup = 'hard' | 'soft';
8
12
  interface CheckCategory {
9
13
  name: string;
10
14
  items: CheckItem[];
15
+ group?: CheckGroup;
11
16
  }
12
17
  interface PageDetail {
13
18
  url: string;
14
19
  title: string;
20
+ pageType: PageType;
15
21
  totalChars: number;
16
22
  contentChars: number;
17
23
  contentRatio: number;
18
24
  contentStatus: CheckStatus;
19
25
  issues: string[];
26
+ score: number;
27
+ relevance?: 'relevant' | 'tangential' | 'off-topic';
20
28
  ai?: {
21
29
  status: CheckStatus;
22
30
  assessment: string;
23
31
  suggestions: string[];
24
32
  };
25
33
  }
34
+ interface SiteTopic {
35
+ type: SiteType;
36
+ topic: string;
37
+ description: string;
38
+ confidence: 'high' | 'medium' | 'low';
39
+ reasoning: string;
40
+ }
41
+ interface CategoryScore {
42
+ name: string;
43
+ score: number;
44
+ maxScore: number;
45
+ }
26
46
  interface CheckReport {
27
47
  url: string;
28
48
  timestamp: string;
49
+ lang: Lang;
50
+ siteType: SiteType;
51
+ siteTypeConfidence: 'high' | 'medium' | 'low';
52
+ siteTopic?: SiteTopic;
53
+ samplingInfo?: {
54
+ totalDiscovered: number;
55
+ recentCount: number;
56
+ sampledCount: number;
57
+ samplePct: number;
58
+ confidence: 'high' | 'medium' | 'low';
59
+ };
29
60
  categories: CheckCategory[];
61
+ hardCategories: CheckCategory[];
62
+ softCategories: CheckCategory[];
30
63
  score: number;
31
64
  totalChecks: number;
32
65
  passed: number;
@@ -34,13 +67,26 @@ interface CheckReport {
34
67
  failed: number;
35
68
  skipped: number;
36
69
  pages: PageDetail[];
70
+ compositeScore: number;
71
+ categoryScores: CategoryScore[];
72
+ hardStatus: 'ready' | 'warn' | 'fail';
73
+ softScore: number;
74
+ warningRatio: number;
75
+ warningPenalty: number;
37
76
  }
38
77
  interface CheckOptions {
39
78
  url: string;
40
- depth?: number;
79
+ maxCrawl?: number;
80
+ maxPages?: number;
81
+ maxContent?: number;
82
+ sampleMin?: number;
83
+ sampleRatio?: number;
84
+ siteType?: SiteType;
41
85
  skipAi?: boolean;
42
86
  timeout?: number;
43
87
  apiKey?: string;
88
+ lang?: Lang;
89
+ onProgress?: (message: string) => void;
44
90
  }
45
91
 
46
92
  declare function check(options: CheckOptions): Promise<CheckReport>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  check
3
- } from "./chunk-V2YZ36NU.js";
3
+ } from "./chunk-GW4SHZYX.js";
4
4
  export {
5
5
  check
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudcreate/adsense-check",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Check if a website meets Google AdSense review requirements",
5
5
  "homepage": "https://cloudcreate.ai",
6
6
  "repository": {