katman-mcp 0.2.0 → 0.3.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/README.md +48 -42
- package/dist/license.js +201 -12
- package/dist/meta.js +25 -13
- package/dist/plans.js +13 -6
- package/dist/tools/account/account.js +19 -10
- package/dist/tools/account/cli.js +4 -3
- package/dist/tools/account/index.js +1 -1
- package/dist/tools/account/upsell.js +156 -39
- package/dist/tools/crawl/audit/report.js +2 -2
- package/dist/tools/crawl/cli.js +18 -6
- package/dist/tools/crawl/index.js +39 -8
- package/dist/tools/crawl/scan/framework.js +4 -1
- package/dist/tools/method/core/lint.js +15 -1
- package/dist/tools/method/core/prompt.js +2 -1
- package/dist/tools/method/data/_store.js +17 -0
- package/dist/tools/method/data/anatomy.js +12 -141
- package/dist/tools/method/data/appendices.js +22 -202
- package/dist/tools/method/data/cloudflare.js +28 -192
- package/dist/tools/method/data/evidence.js +18 -137
- package/dist/tools/method/data/geo-jobs.js +8 -128
- package/dist/tools/method/data/landing.js +14 -67
- package/dist/tools/method/data/lessons.js +16 -127
- package/dist/tools/method/data/measurement.js +38 -142
- package/dist/tools/method/data/offsite.js +16 -148
- package/dist/tools/method/data/pages.js +16 -167
- package/dist/tools/method/data/prompts.js +8 -633
- package/dist/tools/method/data/publish.js +14 -113
- package/dist/tools/method/data/research.js +14 -131
- package/dist/tools/method/data/roadmap.js +12 -105
- package/dist/tools/method/data/traps.js +6 -74
- package/dist/tools/method/data/verify.js +12 -88
- package/dist/tools/method/data/viral-list.js +12 -39
- package/dist/tools/method/lib/prompt-lookup.js +18 -0
- package/dist/tools/method/mcp/resources.js +4 -3
- package/dist/tools/visibility/index.js +2 -2
- package/dist/tools/visibility/monitoring.js +3 -0
- package/package.json +5 -3
- package/server.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Crawl module: katman_scan_code, katman_audit_site, katman_keywords
|
|
1
|
+
// Crawl module: katman_scan_code, katman_audit_site, katman_keywords and katman_indexnow (Katman Pro). katman_audit_site is
|
|
2
|
+
// counted: it asks katman.pro right before the audit runs (license.ts meterAudit); see auditAndSave().
|
|
2
3
|
// Registration only; the engines live in scan/, audit/, indexnow.ts and keywords.ts (pure or fetcher-injected).
|
|
3
4
|
import { stat } from "node:fs/promises";
|
|
4
5
|
import os from "node:os";
|
|
@@ -6,8 +7,9 @@ import path from "node:path";
|
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import { resolveRoot, rootWarning } from "../../context.js";
|
|
8
9
|
import { loadConfig, loadResearch, logEvent, readJson, saveConfig, saveResearch, stamp, writeJson, writeText } from "../../state.js";
|
|
9
|
-
import { fail, ok } from "../../util.js";
|
|
10
|
-
import { tierNote, withPlan } from "../../license.js";
|
|
10
|
+
import { domainOf, fail, normalizeSiteUrl, ok } from "../../util.js";
|
|
11
|
+
import { licenseOf, meterAudit, statusOf, tierNote, withPlan } from "../../license.js";
|
|
12
|
+
import { auditCountLine, auditCountStructured, auditRefusalStructured, renderAuditRefusal } from "../account/upsell.js";
|
|
11
13
|
import { topIssues, auditMarkdown } from "./audit/report.js";
|
|
12
14
|
import { runAudit } from "./audit/index.js";
|
|
13
15
|
import { countBySeverity } from "./common/findings.js";
|
|
@@ -35,7 +37,22 @@ export async function scanAndSave(root, maxFiles, save = true) {
|
|
|
35
37
|
const markdown = scanMarkdown(run, { savedTo: save ? ".katman/scan.json" : undefined, warning });
|
|
36
38
|
return { run, markdown };
|
|
37
39
|
}
|
|
38
|
-
|
|
40
|
+
/** The audit was not run: this month's audits or sites are used up, or the plan or key stopped being valid. */
|
|
41
|
+
export class AuditRefused extends Error {
|
|
42
|
+
use;
|
|
43
|
+
domain;
|
|
44
|
+
constructor(use, domain) {
|
|
45
|
+
super(`audit refused for ${domain}: ${use.reason ?? "not allowed"}`);
|
|
46
|
+
this.use = use;
|
|
47
|
+
this.domain = domain;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Audit the live site and save the result. With a `meter` (the real tools and the CLI pass one) the audit is counted
|
|
52
|
+
* first, right before it runs and only once the URL is valid; a refusal throws AuditRefused and nothing is fetched.
|
|
53
|
+
* `use` in the result is the meter's answer (the count, or "couldn't be checked").
|
|
54
|
+
*/
|
|
55
|
+
export async function auditAndSave(root, a, onProgress, save = true, meter) {
|
|
39
56
|
const writable = save && !unsafeRoot(root);
|
|
40
57
|
const readable = !unsafeRoot(root);
|
|
41
58
|
const config = readable ? await loadConfig(root) : {};
|
|
@@ -44,6 +61,14 @@ export async function auditAndSave(root, a, onProgress, save = true) {
|
|
|
44
61
|
const siteUrl = a.site_url?.trim() || config.siteUrl || research?.site?.url;
|
|
45
62
|
if (!siteUrl)
|
|
46
63
|
throw new Error("No site URL: pass site_url (e.g. https://example.com), or save it first with katman_start { site_url }.");
|
|
64
|
+
let use;
|
|
65
|
+
if (meter) {
|
|
66
|
+
// An invalid URL throws here, before anything is counted.
|
|
67
|
+
const domain = domainOf(normalizeSiteUrl(siteUrl));
|
|
68
|
+
use = await meter(domain);
|
|
69
|
+
if (!use.allowed)
|
|
70
|
+
throw new AuditRefused(use, domain);
|
|
71
|
+
}
|
|
47
72
|
const run = await runAudit({
|
|
48
73
|
siteUrl,
|
|
49
74
|
maxUrls: a.max_urls ?? config.maxUrls ?? 50,
|
|
@@ -70,7 +95,7 @@ export async function auditAndSave(root, a, onProgress, save = true) {
|
|
|
70
95
|
await logEvent(root, `katman_audit_site ${run.ctx.origin}: ${run.ctx.pages.length} URLs · read ${g.read.pass}/${g.read.total} · quote ${g.quote.pass}/${g.quote.total} · fit ${g.fit.pass}/${g.fit.total} · ${run.findings.length} findings`);
|
|
71
96
|
}
|
|
72
97
|
const markdown = auditMarkdown(run.ctx, run.result, run.findings, { full: false, jsonPath, mdPath });
|
|
73
|
-
return { run, markdown, jsonPath, writable };
|
|
98
|
+
return { run, markdown, jsonPath, writable, use };
|
|
74
99
|
}
|
|
75
100
|
function auditSummary(result, top, jsonPath) {
|
|
76
101
|
return {
|
|
@@ -119,7 +144,7 @@ export function registerCrawlTools(server, ctx) {
|
|
|
119
144
|
}));
|
|
120
145
|
server.registerTool("katman_audit_site", {
|
|
121
146
|
title: "Audit the live site like AI crawlers see it",
|
|
122
|
-
description: "Fetches the live site without JavaScript, like AI crawlers: robots.txt, sitemap, http→https, www/apex, HSTS, compression, soft 404, llms.txt, AI-bot access through the CDN/firewall (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot…), and per page: status, noindex, title/description, canonical, H1, JS-off words and first paragraph, JSON-LD, og:image, hreflang, internal links. Returns gates, top-10 issues (P-codes + acceptance criteria) and the P14 table. Network: the site only, ≤4 requests at a time. Pass urls to re-verify a fix. Writes .katman/audits/<stamp>.json + .katman/audit.md." +
|
|
147
|
+
description: "Fetches the live site without JavaScript, like AI crawlers: robots.txt, sitemap, http→https, www/apex, HSTS, compression, soft 404, llms.txt, AI-bot access through the CDN/firewall (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot…), and per page: status, noindex, title/description, canonical, H1, JS-off words and first paragraph, JSON-LD, og:image, hreflang, internal links. Returns gates, top-10 issues (P-codes + acceptance criteria) and the P14 table. Network: the site only, ≤4 requests at a time. Pass urls to re-verify a fix. Writes .katman/audits/<stamp>.json + .katman/audit.md. Each run counts as one of the plan's audits for the calendar month (Katman Pro, 1 site: 5 audits and 1 site; 3 sites: 30 audits and 3 sites; katman.pro counts them); re-reading .katman/audit.md and every other tool don't count, so don't re-run it without a reason." +
|
|
123
148
|
tierNote("audit"),
|
|
124
149
|
inputSchema: {
|
|
125
150
|
site_url: z.string().optional().describe("Site to audit, e.g. https://example.com (default: .katman/config.json siteUrl or research site.url)."),
|
|
@@ -139,11 +164,17 @@ export function registerCrawlTools(server, ctx) {
|
|
|
139
164
|
void extra.sendNotification({ method: "notifications/progress", params: { progressToken: token, progress: done, total, message } }).catch(() => undefined);
|
|
140
165
|
}
|
|
141
166
|
: undefined;
|
|
142
|
-
const
|
|
167
|
+
const provider = licenseOf(ctx);
|
|
168
|
+
const { run, markdown, jsonPath, writable, use } = await auditAndSave(root, args, onProgress, true, (domain) => meterAudit(provider, domain));
|
|
143
169
|
const note = writable ? "" : `\n\n> Not saved: ${root} is not a project folder (pass project_root to keep the audit in .katman/).`;
|
|
144
|
-
|
|
170
|
+
const count = use ? auditCountLine(use) : undefined;
|
|
171
|
+
return ok(markdown + note + (count ? `\n\n${count}` : ""), { ...auditSummary(run.result, topIssues(run.ctx, run.findings), jsonPath), ...(use ? { auditCount: auditCountStructured(use) } : {}) });
|
|
145
172
|
}
|
|
146
173
|
catch (e) {
|
|
174
|
+
if (e instanceof AuditRefused) {
|
|
175
|
+
const input = { use: e.use, status: await statusOf(licenseOf(ctx)), domain: e.domain, surface: "mcp" };
|
|
176
|
+
return ok(renderAuditRefusal(input), auditRefusalStructured(input));
|
|
177
|
+
}
|
|
147
178
|
return fail(`katman_audit_site failed: ${errText(e)}`);
|
|
148
179
|
}
|
|
149
180
|
}));
|
|
@@ -593,7 +593,10 @@ function detectHosting(p, pkg, builder, wr, findings) {
|
|
|
593
593
|
}
|
|
594
594
|
function staticDirFor(framework, appDir, assetsDir, p) {
|
|
595
595
|
const pre = appDir ? `${appDir}/` : "";
|
|
596
|
-
|
|
596
|
+
// A Worker's assets directory is the folder that gets deployed. Without a build step it is the source folder; with a
|
|
597
|
+
// framework it is the build output (dist/, build/, Hugo's public/…), which the next build wipes, so files go to the
|
|
598
|
+
// framework's own static folder instead.
|
|
599
|
+
if (assetsDir && (framework === "custom-server" || framework === "static-html" || framework === "unknown"))
|
|
597
600
|
return assetsDir;
|
|
598
601
|
switch (framework) {
|
|
599
602
|
case "sveltekit":
|
|
@@ -19,6 +19,7 @@ const SOURCE_MARK_RE = /(source|according to|survey|study|report|data from|measu
|
|
|
19
19
|
const CLAIM_NUM_RE = /(\d+(?:[.,]\d+)?\s?%|%\s?\d+|\b\d+(?:[.,]\d+)?\s?[x×]\b|\b\d{1,3}(?:[.,]\d{3})+\+?|\b\d+\+?\s+(?:users|customers|people|visitors|downloads|companies|teams|businesses|orders|sites|reviews|clients|installs|creators|members|kullanıcı|müşteri|kişi|ziyaretçi|şirket|indirme|yorum|site|üye))/i;
|
|
20
20
|
const FREE_RE = /\b(free|for free|free of charge)\b|ücretsiz|bedava|\bgratis\b|kostenlos|gratuit/gi;
|
|
21
21
|
const CURRENCY_NEAR = /[$€£₺]|\b(usd|eur|gbp|try|tl)\b/i;
|
|
22
|
+
const OFFER_LIMIT_BEFORE = /(?:\b(?:first|next|up to|limited to|max(?:imum)?|only|at most)|\bilk|\ben fazla|\bsadece)\s*$/i;
|
|
22
23
|
function stripQuotes(s) {
|
|
23
24
|
return s.replace(/“[^”]*”|"[^"]*"|«[^»]*»|„[^“]*“/g, " ");
|
|
24
25
|
}
|
|
@@ -227,6 +228,9 @@ export function lintPage(x, c) {
|
|
|
227
228
|
continue;
|
|
228
229
|
if (CURRENCY_NEAR.test(s.slice(Math.max(0, (m.index ?? 0) - 3), (m.index ?? 0) + m[0].length + 3)))
|
|
229
230
|
continue;
|
|
231
|
+
// An offer's limit ("the first 100 customers", "up to 3 sites") is a condition, not a claim that needs a source.
|
|
232
|
+
if (OFFER_LIMIT_BEFORE.test(s.slice(Math.max(0, (m.index ?? 0) - 20), m.index ?? 0)))
|
|
233
|
+
continue;
|
|
230
234
|
if (SOURCE_MARK_RE.test(s) || (ss[i + 1] && SOURCE_MARK_RE.test(ss[i + 1])))
|
|
231
235
|
continue;
|
|
232
236
|
unsourced.push(`“${s.length > 140 ? s.slice(0, 137) + "…" : s}”`);
|
|
@@ -270,12 +274,22 @@ export function lintPage(x, c) {
|
|
|
270
274
|
// "free" next to a tool/resource word (free VAT calculator, free checker, free guide, ücretsiz araç) is a
|
|
271
275
|
// genuinely free side offer, which the method recommends (free tools are link magnets): not a violation.
|
|
272
276
|
const FREE_SIDE_OFFER = /^\W{0,3}(?:\p{L}+[\s-]+){0,3}?(?:tool|tools|finder|checker|calculator|counter|generator|guide|book|ebook|pdf|template|templates|course|webinar|audit|test|trial|plan|tier|version|download|araç|araçlar|araci|aracı|bulucu|hesaplayıcı|sayacı|rehber|kitap|şablon|deneme|sürüm|plan)\b/iu;
|
|
277
|
+
// The same side offer named before the word ("the checker and the guides are free", "AI crawler checker — Free",
|
|
278
|
+
// "check your site first, free"), in the same clause and without a contrast ("our tools are paid, but setup is free").
|
|
279
|
+
const FREE_SIDE_OFFER_BEFORE = /\b(?:tool|tools|finder|checker|checkers|check|calculator|calculators|counter|generator|guide|guides|template|templates|resource|resources|araç|araçlar|araci|aracı|bulucu|hesaplayıcı|sayacı|rehber|rehberler|şablon|kontrol)\b(?:(?!\b(?:but|however|while|whereas|although|except|ama|fakat|ancak)\b)[^.!?;])*$/iu;
|
|
273
280
|
const hits = [];
|
|
274
281
|
for (const seg of [...segments, stripQuotes(x.description ?? "")]) {
|
|
275
282
|
for (const m of seg.matchAll(FREE_RE)) {
|
|
276
|
-
const
|
|
283
|
+
const at = m.index ?? 0;
|
|
284
|
+
const after = seg.slice(at + m[0].length, at + m[0].length + 60);
|
|
277
285
|
if (FREE_SIDE_OFFER.test(after))
|
|
278
286
|
continue;
|
|
287
|
+
if (FREE_SIDE_OFFER_BEFORE.test(seg.slice(Math.max(0, at - 50), at)))
|
|
288
|
+
continue;
|
|
289
|
+
// A question ("Is it free?") asks, it doesn't promise; the answer is checked on its own.
|
|
290
|
+
const end = seg.slice(at).search(/[.!?](?:\s|$)/);
|
|
291
|
+
if (end >= 0 && seg[at + end] === "?")
|
|
292
|
+
continue;
|
|
279
293
|
if (hits.length < 4)
|
|
280
294
|
hits.push(snippet(seg, m.index ?? 0, m[0].length));
|
|
281
295
|
}
|
|
@@ -5,7 +5,8 @@ import { t } from "../lib/l10n.js";
|
|
|
5
5
|
import { appPathsText, knownPublicPaths, pathKnown } from "../lib/routes.js";
|
|
6
6
|
import { loadConfigSafe, loadLatestAudit, loadResearchSafe, loadScan, staticDirFor } from "../lib/state.js";
|
|
7
7
|
import { brandSentence, codeBlock, fillTemplate, hasNotX, isFilled, slugify } from "../lib/text.js";
|
|
8
|
-
import { PLACEHOLDERS, PROMPTS
|
|
8
|
+
import { PLACEHOLDERS, PROMPTS } from "../data/prompts.js";
|
|
9
|
+
import { getPrompt, promptPlaceholders } from "../lib/prompt-lookup.js";
|
|
9
10
|
import { detectLang } from "./questions.js";
|
|
10
11
|
export function siteUrlOf(r, cfg) {
|
|
11
12
|
if (r && isFilled(r.site.url) && /^https?:\/\//i.test(r.site.url))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Where the method data comes from at run time.
|
|
2
|
+
// In the source tree (tests, `npm run dev`) every data module holds the method inline and METHOD_INLINE is true.
|
|
3
|
+
// The published package carries no method text: scripts/postbuild.mjs replaces each data module in dist/ with a stub
|
|
4
|
+
// that exports the same names, empty until katman.pro sends the data for a paid key (POST /api/web/mcp/method, see
|
|
5
|
+
// src/license.ts loadMethod), and sets METHOD_INLINE to false. ES module bindings are live, so every importer sees
|
|
6
|
+
// the values once applyMethod has run.
|
|
7
|
+
export const METHOD_INLINE = false;
|
|
8
|
+
const setters = [];
|
|
9
|
+
/** Called by each stub module to receive its exports. */
|
|
10
|
+
export function onMethod(set) {
|
|
11
|
+
setters.push(set);
|
|
12
|
+
}
|
|
13
|
+
/** Fills every stub module from the data katman.pro sent. */
|
|
14
|
+
export function applyMethod(data) {
|
|
15
|
+
for (const set of setters)
|
|
16
|
+
set(data);
|
|
17
|
+
}
|
|
@@ -1,141 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
tr: "Title önce durum, sonra marka, yaklaşık 60 karakter. Meta açıklama asistanın söylemesini istediğiniz cümle; Google snippet’i çoğunlukla sayfa metninden kurar, bu yüzden aynı cümle 3. blokta da olmalı.",
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
n: 2,
|
|
18
|
-
tags: ["SEO", "GEO"],
|
|
19
|
-
name: { en: "H1", tr: "H1" },
|
|
20
|
-
example: { en: "I type my invoices in by hand and it takes hours", tr: "Faturalarımı elle giriyorum, saatlerimi alıyor" },
|
|
21
|
-
rule: { en: "The H1 is the customer's own sentence. The URL uses the same words.", tr: "H1 müşterinin kendi cümlesi. Adres de aynı kelimelerle." },
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
n: 3,
|
|
25
|
-
tags: ["GEO"],
|
|
26
|
-
name: { en: "The first 1–3 sentences", tr: "İlk 1–3 cümle" },
|
|
27
|
-
example: {
|
|
28
|
-
en: "Upload your invoice. We move the line items into your accounting software in 30 seconds and flag the ones that don't match. Your document is never stored.",
|
|
29
|
-
tr: "Faturanı yükle. Kalemleri 30 saniyede muhasebe programına aktarırız, eşleşmeyenleri işaretleriz. Belgen saklanmaz.",
|
|
30
|
-
},
|
|
31
|
-
rule: {
|
|
32
|
-
en: "The most important block. The assistant takes a sentence for its answer from here: short, concrete, understandable on its own. ChatGPT repeated the sample site's sentence almost word for word. Keep the key claim within the H1 and the first ~200 characters.",
|
|
33
|
-
tr: "Sayfanın en önemli bloğu. Asistan cevabına buradan bir cümle alır: kısa, somut, tek başına anlaşılır. Örnek sitenin cümlesini ChatGPT neredeyse kelimesi kelimesine tekrar etti. Asıl iddiayı H1’de ve ilk ~200 karakterde tutun.",
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
n: 4,
|
|
38
|
-
tags: [],
|
|
39
|
-
name: { en: "Try it now / one clear action", tr: "Hemen deneme / tek net eylem" },
|
|
40
|
-
example: { en: "Drop your invoice · try it free", tr: "Faturanı bırak · ücretsiz dene" },
|
|
41
|
-
rule: {
|
|
42
|
-
en: "The visitor arrives with the situation they told ChatGPT. On the sample site we put a file drop box on the two pages ChatGPT sent people to; 52 of the first 68 generations in week one used the visitor's own file.",
|
|
43
|
-
tr: "Ziyaretçi ChatGPT’ye anlattığı durumla gelir. Örnek sitede ChatGPT’nin gönderdiği iki sayfaya dosya kutusu koyduk; ilk haftadaki 68 ilk üretimin 52’si ziyaretçinin kendi dosyasıyla yapıldı.",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
n: 5,
|
|
48
|
-
tags: ["GEO"],
|
|
49
|
-
name: { en: "Proof: number + date", tr: "Kanıt: sayı + tarih" },
|
|
50
|
-
example: { en: "A verifiable number with its date", tr: "Doğrulanabilir sayı, tarihiyle" },
|
|
51
|
-
rule: {
|
|
52
|
-
en: "A verifiable number, with its date. Don't claim more than your proof: the assistant repeats it and a competitor refutes it.",
|
|
53
|
-
tr: "Doğrulanabilir sayı, tarihiyle. Kanıtından büyük iddia yazmayın; asistan tekrar eder, rakip çürütür.",
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
n: 6,
|
|
58
|
-
tags: ["SEO", "GEO"],
|
|
59
|
-
name: { en: "How it works: 3–5 steps", tr: "Nasıl çalışır: 3–5 adım" },
|
|
60
|
-
example: { en: "3–5 plain-text steps", tr: "3–5 düz metin adım" },
|
|
61
|
-
rule: {
|
|
62
|
-
en: "Plain HTML text; a demo drawn with JavaScript is an empty page to a bot.",
|
|
63
|
-
tr: "Düz HTML metin; JavaScript ile çizilen bir demo bot için boş sayfadır.",
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
n: 7,
|
|
68
|
-
tags: ["GEO"],
|
|
69
|
-
name: { en: "Who it's not for", tr: "Kimler için değil" },
|
|
70
|
-
example: { en: "2–3 bullets", tr: "2–3 madde" },
|
|
71
|
-
rule: {
|
|
72
|
-
en: "Saying who it's not for and naming competitors honestly builds trust; assistants quote one-sided pages less.",
|
|
73
|
-
tr: "“Kime uygun değil” ve rakipleri dürüstçe saymak güven verir; asistan tek taraflı sayfayı daha az alıntılar.",
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
n: 8,
|
|
78
|
-
tags: ["GEO"],
|
|
79
|
-
name: { en: "Alternatives, honestly", tr: "Alternatifler, dürüstçe" },
|
|
80
|
-
example: { en: "Competitor: who it fits", tr: "Rakip: kime uygun" },
|
|
81
|
-
rule: {
|
|
82
|
-
en: "Name the alternatives and say whom each one fits; take competitor facts only from their own pages, with a date.",
|
|
83
|
-
tr: "Alternatifleri sayın, hangisinin kime uygun olduğunu yazın; rakip bilgisini yalnızca kendi sayfasından, tarihiyle alın.",
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
n: 9,
|
|
88
|
-
tags: ["GEO"],
|
|
89
|
-
name: { en: "FAQ: 5–8 questions", tr: "SSS: 5–8 soru" },
|
|
90
|
-
example: { en: "Questions in the customer's words", tr: "Müşterinin ağzından sorular" },
|
|
91
|
-
rule: {
|
|
92
|
-
en: "Questions in the customer's words, answers whose first sentence is the answer. Google no longer shows an FAQ rich result; this block is for the assistant.",
|
|
93
|
-
tr: "Müşterinin ağzından sorular, ilk cümlesi cevap olan yanıtlar. Google’da SSS zengin sonucu yok; bu blok asistan için.",
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
n: 10,
|
|
98
|
-
tags: ["SEO"],
|
|
99
|
-
name: { en: "Related pages", tr: "İlgili sayfalar" },
|
|
100
|
-
example: { en: "3–5 text links", tr: "3–5 metinli link" },
|
|
101
|
-
rule: {
|
|
102
|
-
en: "Internal links with descriptive text; every page gets a link from somewhere.",
|
|
103
|
-
tr: "Metinli iç linkler; her sayfa en az bir yerden link alsın.",
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
n: 11,
|
|
108
|
-
tags: ["SEO", "GEO"],
|
|
109
|
-
name: { en: "Updated date · author · JSON-LD", tr: "Güncellendi · yazar · JSON-LD" },
|
|
110
|
-
example: { en: "Updated: 29 Sep 2026 · Author · JSON-LD", tr: "Güncellendi: 29.09.2026 · Yazar · JSON-LD" },
|
|
111
|
-
rule: {
|
|
112
|
-
en: "A real update date (changed only when the text changes), the author, valid JSON-LD. In the sample site's first version all 85 pages carried the launch date; we fixed it.",
|
|
113
|
-
tr: "Gerçek güncelleme tarihi (yalnızca metin değişince), yazar, geçerli JSON-LD. Örnek sitede ilk sürümde 85 sayfanın hepsi yayın tarihini taşıyordu; düzelttik.",
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
];
|
|
117
|
-
/** The page brief the book tells you to start P6 with. */
|
|
118
|
-
export const PAGE_BRIEF_TEMPLATE = {
|
|
119
|
-
en: `Situation: [the customer's sentence]
|
|
120
|
-
URL: /[words] Title: [situation] — [brand]
|
|
121
|
-
First sentence: [brings] → [we do] → [they get]. [fear is unnecessary].
|
|
122
|
-
Action: [file drop / trial / pricing]
|
|
123
|
-
Proof: [number, source, date]
|
|
124
|
-
Not for: [2–3 bullets]
|
|
125
|
-
Alternatives: [competitor: who it fits]
|
|
126
|
-
FAQ: [5–8 questions, in the customer's words]
|
|
127
|
-
Related pages: [3–5 URLs]`,
|
|
128
|
-
tr: `Durum: [müşterinin cümlesi]
|
|
129
|
-
Adres: /[kelimeler] Title: [durum] — [marka]
|
|
130
|
-
İlk cümle: [getirir] → [yaparız] → [eline geçer]. [korku gereksiz].
|
|
131
|
-
Eylem: [dosya kutusu / deneme / fiyat]
|
|
132
|
-
Kanıt: [sayı, kaynak, tarih]
|
|
133
|
-
Kimler için değil: [2-3 madde]
|
|
134
|
-
Alternatifler: [rakip: kime uygun]
|
|
135
|
-
SSS: [5-8 soru, müşterinin ağzından]
|
|
136
|
-
İlgili sayfalar: [3-5 adres]`,
|
|
137
|
-
};
|
|
138
|
-
export const ANATOMY_EXERCISE = {
|
|
139
|
-
en: "Write your own four-part sentence (brings → we do → gets → fear is unnecessary). Put the same sentence in three places: the first paragraph of your most important page, that page's meta description, and llms.txt.",
|
|
140
|
-
tr: "Kendi dört parçalı cümlenizi yazın (getirir → yaparız → eline geçer → korku gereksiz). Aynı cümleyi üç yere koyun: en önemli sayfanın ilk paragrafı, o sayfanın meta açıklaması, llms.txt.",
|
|
141
|
-
};
|
|
1
|
+
// Filled from katman.pro at run time for a paid key (see _store.js); the package carries no method text.
|
|
2
|
+
import { onMethod } from "./_store.js";
|
|
3
|
+
export let ANATOMY = [];
|
|
4
|
+
export let ANATOMY_EXERCISE = {};
|
|
5
|
+
export let ANATOMY_INTRO = {};
|
|
6
|
+
export let PAGE_BRIEF_TEMPLATE = {};
|
|
7
|
+
onMethod((d) => {
|
|
8
|
+
if (d.ANATOMY !== undefined) ANATOMY = d.ANATOMY;
|
|
9
|
+
if (d.ANATOMY_EXERCISE !== undefined) ANATOMY_EXERCISE = d.ANATOMY_EXERCISE;
|
|
10
|
+
if (d.ANATOMY_INTRO !== undefined) ANATOMY_INTRO = d.ANATOMY_INTRO;
|
|
11
|
+
if (d.PAGE_BRIEF_TEMPLATE !== undefined) PAGE_BRIEF_TEMPLATE = d.PAGE_BRIEF_TEMPLATE;
|
|
12
|
+
});
|
|
@@ -1,202 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Sitemap: https://yourdomain.com/sitemap.xml
|
|
24
|
-
# For assistants: https://yourdomain.com/llms.txt`,
|
|
25
|
-
tr: `# Arama motorlarına, yapay zekâ asistanlarına ve tarayıcılara açığız.
|
|
26
|
-
# Content signals: search=yes, ai-input=yes, ai-train=yes
|
|
27
|
-
# (Yorum olarak yazıldı: kural olarak yazılınca Lighthouse "robots.txt geçersiz" dedi.)
|
|
28
|
-
|
|
29
|
-
User-agent: *
|
|
30
|
-
Allow: /
|
|
31
|
-
Disallow: /app/
|
|
32
|
-
Disallow: /admin/
|
|
33
|
-
Disallow: /api/
|
|
34
|
-
|
|
35
|
-
User-agent: OAI-SearchBot
|
|
36
|
-
Allow: /
|
|
37
|
-
Disallow: /app/
|
|
38
|
-
Disallow: /admin/
|
|
39
|
-
Disallow: /api/
|
|
40
|
-
|
|
41
|
-
# Aynı grup şunlar için de tekrarlanır: ChatGPT-User, GPTBot, Claude-SearchBot,
|
|
42
|
-
# Claude-User, ClaudeBot, PerplexityBot, Perplexity-User, Google-Extended,
|
|
43
|
-
# Applebot, Applebot-Extended, Bingbot, DuckAssistBot, Meta-ExternalAgent, Amazonbot
|
|
44
|
-
|
|
45
|
-
Sitemap: https://alanadiniz.com/sitemap.xml
|
|
46
|
-
# Asistanlar için: https://alanadiniz.com/llms.txt`,
|
|
47
|
-
};
|
|
48
|
-
export const APPENDIX_A_NOTE = {
|
|
49
|
-
en: "A bot that has a group with its own name doesn't read the * group, so repeat the closed paths in every group. Don't write bare prefixes: Disallow: /t closes both /tr and /tools; write /t/. Have your coding tool generate this file with P4.",
|
|
50
|
-
tr: "Bir bot için adıyla yazılmış grup varsa o bot * grubunu okumaz; bu yüzden kapalı yolları her grupta tekrar yazın. Çıplak önek yazmayın: Disallow: /t hem /tr’yi hem /tools’u kapatır, /t/ yazın. Kod aracınıza bu dosyayı P4 ile ürettirin.",
|
|
51
|
-
};
|
|
52
|
-
/** Appendix B — llms.txt skeleton. */
|
|
53
|
-
export const APPENDIX_B_LLMS = {
|
|
54
|
-
en: `# [Brand] [category word]
|
|
55
|
-
|
|
56
|
-
> [Brand] is a [product type] that turns [input] into [output]. [The most important difference, one sentence].
|
|
57
|
-
> Operated by [company name] ([country]).
|
|
58
|
-
|
|
59
|
-
Key facts: [pricing model, lowest price, trial], [cancellation], [usage limits].
|
|
60
|
-
Not to be confused with: [Brand] is not [similarly named product]; it is not a [big company] product.
|
|
61
|
-
Limits of the evidence: [the test behind the claim and its size, stated plainly].
|
|
62
|
-
|
|
63
|
-
## Product
|
|
64
|
-
- [Home](https://yourdomain.com/): what it is
|
|
65
|
-
- [Pricing](https://yourdomain.com/pricing): plans
|
|
66
|
-
- [Situation page](https://yourdomain.com/...): [the four-part sentence]
|
|
67
|
-
|
|
68
|
-
## Guides
|
|
69
|
-
## Comparisons
|
|
70
|
-
## Optional
|
|
71
|
-
- [Terms](...), [Privacy](...)`,
|
|
72
|
-
tr: `# [Marka] [kategori kelimesi]
|
|
73
|
-
|
|
74
|
-
> [Marka], [girdi]yi [çıktı]ya dönüştüren bir [ürün tipi]. [En önemli fark, tek cümle].
|
|
75
|
-
> [Şirket adı] tarafından işletilir ([ülke]).
|
|
76
|
-
|
|
77
|
-
Temel bilgiler: [fiyat modeli, en düşük fiyat, deneme], [iptal], [kullanım sınırları].
|
|
78
|
-
Karıştırılmasın: [Marka], [benzer adlı ürün] değildir; [büyük şirket]in ürünü değildir.
|
|
79
|
-
Kanıtın sınırı: [iddianın dayandığı test ve büyüklüğü, açıkça].
|
|
80
|
-
|
|
81
|
-
## Ürün
|
|
82
|
-
- [Ana sayfa](https://alanadiniz.com/): ne olduğu
|
|
83
|
-
- [Fiyatlar](https://alanadiniz.com/fiyatlandirma): planlar
|
|
84
|
-
- [Durum sayfası](https://alanadiniz.com/...): [dört parçalı cümle]
|
|
85
|
-
|
|
86
|
-
## Rehberler
|
|
87
|
-
## Karşılaştırmalar
|
|
88
|
-
## Optional
|
|
89
|
-
- [Şartlar](...), [Gizlilik](...)`,
|
|
90
|
-
};
|
|
91
|
-
export const APPENDIX_B_NOTE = {
|
|
92
|
-
en: "Keep it short: the sample site's llms.txt grew to 65 KB and is being trimmed. Every figure should come from the same source as the pages.",
|
|
93
|
-
tr: "Kısa tutun: örnek sitenin llms.txt’si 65 KB’a çıktı, inceltilecek. Her rakam sayfalarla aynı kaynaktan gelsin.",
|
|
94
|
-
};
|
|
95
|
-
/** Appendix C — Organization JSON-LD with the not-X note. */
|
|
96
|
-
export const APPENDIX_C_ORG = `{
|
|
97
|
-
"@context": "https://schema.org",
|
|
98
|
-
"@type": "Organization",
|
|
99
|
-
"@id": "https://yourdomain.com/#org",
|
|
100
|
-
"name": "[Brand] [Category]",
|
|
101
|
-
"alternateName": ["[Brand]"],
|
|
102
|
-
"disambiguatingDescription": "A [category] product. Not [similarly named product]; not a [big company] product.",
|
|
103
|
-
"url": "https://yourdomain.com",
|
|
104
|
-
"logo": "https://yourdomain.com/img/logo-512.png",
|
|
105
|
-
"sameAs": ["https://www.youtube.com/@[channel]", "https://www.instagram.com/[account]"],
|
|
106
|
-
"address": { "@type": "PostalAddress", "addressCountry": "TR" }
|
|
107
|
-
}`;
|
|
108
|
-
export const APPENDIX_C_NOTE = {
|
|
109
|
-
en: "On every page also WebSite, BreadcrumbList and, by page type, Article (datePublished, dateModified, author), FAQPage (the real questions and answers on the page), VideoObject (if a video is embedded). Everything in the schema must also be visible on the page.",
|
|
110
|
-
tr: "Her sayfada ayrıca WebSite, BreadcrumbList ve sayfa tipine göre Article (datePublished, dateModified, author), FAQPage (sayfadaki gerçek soru-cevaplar), VideoObject (gömülü video varsa). Schema’daki her bilgi sayfada da görünmeli.",
|
|
111
|
-
};
|
|
112
|
-
/** Appendix D — content rules (+ the 2026 additions). */
|
|
113
|
-
export const CONTENT_RULES = [
|
|
114
|
-
{ en: "The first 1–3 sentences answer the question and can be quoted on their own.", tr: "İlk 1–3 cümle soruyu cevaplar ve tek başına alıntılanabilir." },
|
|
115
|
-
{ en: "Every claim's proof is on the page; if the proof is small, say it's small.", tr: "Her iddianın kanıtı sayfada; kanıt küçükse küçük olduğunu yazın." },
|
|
116
|
-
{ en: "About competitors, write only what their own pages say, with the date.", tr: "Rakipler hakkında yalnızca kendi sayfalarında yazanı, tarihiyle yazın." },
|
|
117
|
-
{ en: "Prices and features come from one source; no two pages contradict each other.", tr: "Fiyat ve özellik tek kaynaktan; iki sayfa birbiriyle çelişmesin." },
|
|
118
|
-
{ en: "Don't call a paid product “free”; don't write headlines that imply another brand's endorsement.", tr: "Ücretli ürüne “ücretsiz” demeyin; başka markanın onayını ima eden başlık yazmayın." },
|
|
119
|
-
{
|
|
120
|
-
en: "Let AI write the draft, but don't publish until it passes a rule script (no made-up numbers, no “best / perfect”, no real people's names) and a human read. katman_lint_content is that rule script.",
|
|
121
|
-
tr: "Taslağı yapay zekâya yazdırın, ama bir kural betiğinden (uydurma sayı, “en iyi / mükemmel”, gerçek kişi adı yok) ve bir insan okumasından geçmeden yayınlamayın. katman_lint_content bu kural betiğidir.",
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
en: "Question sources: Google autocomplete (seed keyword + letter by letter), situation sentences people really ask the assistant, the pages the assistant sends people to in your analytics.",
|
|
125
|
-
tr: "Soru kaynağı: Google’ın otomatik tamamlaması (tohum kelime + harf harf), asistana gerçekten sorulan durum cümleleri, analitikte asistanın indirdiği sayfalar.",
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
en: "Watch the page mix: Perplexity took the sample site for a “template and education site” because it had ~110 template pages against 10 product pages. The mix of your pages decides what the assistant thinks you are.",
|
|
129
|
-
tr: "Sayfa karışımına dikkat: Perplexity örnek sitemizi “şablon ve eğitim sitesi” sandı, çünkü yaklaşık 110 şablon sayfasına karşı 10 ürün sayfası vardı. Sayfalarınızın dağılımı asistanın sizi ne sandığını belirler.",
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
en: "On a new domain, 5–10 pages a week. Don't grow a template whose “crawled – not indexed” rate passes 30%.",
|
|
133
|
-
tr: "Yeni alan adında haftada 5–10 sayfa. “Tarandı, dizine eklenmedi” oranı %30’u geçen şablonu büyütmeyin.",
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
en: "Put the key claim in the H1 and the first ~200 characters. Don't keyword-stuff: keyword-heavy titles and URLs get cited less.",
|
|
137
|
-
tr: "Asıl iddiayı H1’e ve ilk ~200 karaktere koyun. Anahtar kelime doldurmayın: anahtar kelime yüklü başlıklar ve adresler daha az alıntılanıyor.",
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
en: "Never show bots different content than people (cloaking), never hide instructions for AI in the page (prompt injection): Google's spam policy covers both since May 2026.",
|
|
141
|
-
tr: "Botlara insanlardan farklı içerik göstermeyin (cloaking), sayfaya yapay zekâ için gizli talimat koymayın (prompt enjeksiyonu): Google’ın spam politikası Mayıs 2026’dan beri ikisini de kapsıyor.",
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
en: "Real freshness only: update the facts and show the date; faking dates is manipulation.",
|
|
145
|
-
tr: "Yalnızca gerçek tazelik: bilgiyi güncelleyin, tarihi gösterin; tarih uydurmak manipülasyondur.",
|
|
146
|
-
},
|
|
147
|
-
];
|
|
148
|
-
/** Appendix E — the full site audit checklist (what the crawl script checks). */
|
|
149
|
-
export const SITE_AUDIT_CHECKLIST = {
|
|
150
|
-
perUrl: [
|
|
151
|
-
{ en: "Status 200 (no redirect), content-type text/html", tr: "Durum 200 (yönlendirme yok), content-type text/html" },
|
|
152
|
-
{ en: "No noindex in meta robots or X-Robots-Tag (special pages should have it)", tr: "meta robots’ta ve X-Robots-Tag’de noindex yok (özel sayfalarda olmalı)" },
|
|
153
|
-
{ en: "<title> present, unique, not longer than 60–65 characters", tr: "<title> var, benzersiz, 60–65 karakteri geçmiyor" },
|
|
154
|
-
{ en: "Meta description present, unique, 70–160 characters", tr: "Meta açıklama var, benzersiz, 70–160 karakter" },
|
|
155
|
-
{ en: "One H1, no heading level skips (h1 → h2 → h3)", tr: "Tek H1, başlık seviyesi atlaması yok (h1 → h2 → h3)" },
|
|
156
|
-
{ en: "One absolute, self-referencing canonical", tr: "Tek, mutlak, kendini gösteren canonical" },
|
|
157
|
-
{ en: "Reciprocal hreflang pairs, an x-default, a correct <html lang>", tr: "hreflang çiftleri karşılıklı, x-default var, <html lang> doğru" },
|
|
158
|
-
{ en: "JSON-LD parses; the expected types are there", tr: "JSON-LD ayrıştırılabiliyor, beklenen tipler var" },
|
|
159
|
-
{ en: "og:title, og:description, og:image (with width, height, alt), og:url, og:locale, twitter:card", tr: "og:title, og:description, og:image (width, height, alt ile), og:url, og:locale, twitter:card" },
|
|
160
|
-
{ en: "Every <img> has alt, width and height; lazy below the fold; a modern format", tr: "Bütün <img>’lerde alt, width ve height; ekranın altındaysa lazy; modern format" },
|
|
161
|
-
{ en: "No http:// resources (mixed content)", tr: "http:// kaynak yok (karışık içerik)" },
|
|
162
|
-
{ en: "URL: lowercase, hyphens, ASCII, short, no trailing /", tr: "URL: küçük harf, tire, ASCII, kısa, sonda / yok" },
|
|
163
|
-
{ en: "At least 3 internal links point to the page (no orphans)", tr: "Sayfaya gelen iç link en az 3 (yetim yok)" },
|
|
164
|
-
{ en: "Every internal and external link on the page is 200 or a deliberate redirect", tr: "Sayfadaki bütün iç ve dış linkler 200 ya da bilinçli yönlendirme" },
|
|
165
|
-
{ en: "Content is visible with JavaScript off (a sensible word count)", tr: "JavaScript kapalıyken içerik görünüyor (kelime sayısı makul)" },
|
|
166
|
-
],
|
|
167
|
-
siteWide: [
|
|
168
|
-
{ en: "http → https 301, www → bare domain 301, HSTS", tr: "http → https 301, www → yalın alan adı 301, HSTS" },
|
|
169
|
-
{ en: "HTML, CSS and JS compressed (Content-Encoding: br or gzip)", tr: "HTML, CSS ve JS sıkıştırılmış (Content-Encoding: br ya da gzip)" },
|
|
170
|
-
{ en: "Long caching and versioning for static files", tr: "Statik dosyalarda uzun önbellek ve sürümleme" },
|
|
171
|
-
{ en: "robots.txt valid, with a sitemap line", tr: "robots.txt geçerli, sitemap satırı var" },
|
|
172
|
-
{ en: "A missing page returns a real 404", tr: "Olmayan sayfa gerçek 404" },
|
|
173
|
-
{ en: "Lighthouse mobile, 5 pages: performance ≥ 90, the others 100", tr: "Lighthouse mobil, 5 sayfa: performans ≥ 90, diğerleri 100" },
|
|
174
|
-
{ en: "Search Console and Bing Webmaster verified, sitemap submitted, IndexNow working", tr: "Search Console ve Bing Webmaster doğrulandı, sitemap gönderildi, IndexNow çalışıyor" },
|
|
175
|
-
{ en: "AI bots get 200 through the CDN (ch. 6)", tr: "CDN’de yapay zekâ botları 200 alıyor (06. bölüm)" },
|
|
176
|
-
],
|
|
177
|
-
};
|
|
178
|
-
/** Appendix F — glossary. Terms as used in English by Katman. */
|
|
179
|
-
export const GLOSSARY = [
|
|
180
|
-
{ term: { en: "GEO", tr: "GEO" }, def: { en: "Generative engine optimisation: visibility in generative engines (assistants).", tr: "Üretken motorlarda (asistanlarda) görünürlük optimizasyonu." } },
|
|
181
|
-
{ term: { en: "AEO", tr: "AEO" }, def: { en: "Answer engine optimisation: quotable answers for direct-answer boxes and AI Overviews.", tr: "Cevap motoru optimizasyonu; doğrudan cevap kutuları ve AI Overviews için alıntılanabilir cevap." } },
|
|
182
|
-
{ term: { en: "Read / Quote / Fit", tr: "Okunmak / Alıntılanmak / Soruya uymak" }, def: { en: "The three gates every public page must pass: bots can read it, the assistant can quote its first sentences, and it fits the situation the customer describes.", tr: "Her herkese açık sayfanın geçmesi gereken üç kapı: botlar okuyabilir, asistan ilk cümlelerini alıntılayabilir, sayfa müşterinin anlattığı duruma uyar." } },
|
|
183
|
-
{ term: { en: "Situation page", tr: "Durum sayfası" }, def: { en: "A page written for one concrete situation a user describes to the assistant, answering in the first sentence.", tr: "Kullanıcının asistana anlattığı somut bir duruma yazılmış, ilk cümlede cevap veren sayfa." } },
|
|
184
|
-
{ term: { en: "The one sentence", tr: "Tek cümle" }, def: { en: "The brand's four-part description (brings → we do → gets → fear is unnecessary), word for word the same on the page, in the meta description and in llms.txt.", tr: "Markanın dört parçalı tanımı (getirir → yaparız → eline geçer → korku gereksiz); sayfada, meta açıklamada ve llms.txt’de kelimesi kelimesine aynı." } },
|
|
185
|
-
{ term: { en: "Not-X note", tr: "“X değildir” notu" }, def: { en: "A plain statement of what the brand is not confused with (llms.txt, About, FAQ, Organization disambiguatingDescription).", tr: "Markanın neyle karıştırılmaması gerektiğini açıkça söyleyen cümle (llms.txt, Hakkımızda, SSS, Organization disambiguatingDescription)." } },
|
|
186
|
-
{ term: { en: "Question set", tr: "Soru seti" }, def: { en: "12 unbranded questions + 2 diagnostic ones, asked the same way every 14 days.", tr: "12 markasız soru + 2 teşhis sorusu; 14 günde bir aynı şekilde sorulur." } },
|
|
187
|
-
{ term: { en: "OAI-SearchBot", tr: "OAI-SearchBot" }, def: { en: "The bot that fills ChatGPT's search index (doesn't run JavaScript).", tr: "ChatGPT’nin arama dizinini dolduran bot (JavaScript çalıştırmaz)." } },
|
|
188
|
-
{ term: { en: "GPTBot", tr: "GPTBot" }, def: { en: "OpenAI's training-data bot.", tr: "OpenAI’ın eğitim verisi botu." } },
|
|
189
|
-
{ term: { en: "ChatGPT-User, Claude-User", tr: "ChatGPT-User, Claude-User" }, def: { en: "Agents that open a page on the user's behalf during a chat.", tr: "Sohbet sırasında kullanıcı adına sayfa açan ajan." } },
|
|
190
|
-
{ term: { en: "ClaudeBot, Claude-SearchBot", tr: "ClaudeBot, Claude-SearchBot" }, def: { en: "Anthropic's training bot and search bot.", tr: "Anthropic’in eğitim botu ve arama botu." } },
|
|
191
|
-
{ term: { en: "llms.txt", tr: "llms.txt" }, def: { en: "A short Markdown summary of the site for language models (llmstxt.org).", tr: "Sitenin dil modelleri için kısa Markdown özeti (llmstxt.org)." } },
|
|
192
|
-
{ term: { en: "IndexNow", tr: "IndexNow" }, def: { en: "A protocol that tells search engines about changed URLs instantly.", tr: "Değişen adresleri arama motorlarına anında bildiren protokol." } },
|
|
193
|
-
{ term: { en: "hreflang", tr: "hreflang" }, def: { en: "The tag linking a page's language and region versions.", tr: "Bir sayfanın dil ve bölge sürümlerini birbirine bağlayan etiket." } },
|
|
194
|
-
{ term: { en: "Canonical", tr: "Canonical" }, def: { en: "The preferred URL of a piece of content.", tr: "Bir içeriğin tercih edilen adresi." } },
|
|
195
|
-
{ term: { en: "lastmod", tr: "lastmod" }, def: { en: "A page's last real change date in the sitemap.", tr: "Sitemap’te sayfanın son gerçek değişiklik tarihi." } },
|
|
196
|
-
{ term: { en: "Entity", tr: "Varlık (entity)" }, def: { en: "What search engines and models recognise as “who/what is this”: name, definition, distinction.", tr: "Arama motorunun ve modellerin “bu kim, ne” diye tanıdığı şey: ad, tanım, ayrım." } },
|
|
197
|
-
{ term: { en: "Human filter", tr: "İnsan filtresi" }, def: { en: "A behaviour rule that separates bots from people in analytics.", tr: "Botları analitikten ayıran davranış kuralı." } },
|
|
198
|
-
{ term: { en: "Facade", tr: "Kapak (facade)" }, def: { en: "An embed that is just an image until clicked, then loads the real player.", tr: "Tıklanana kadar yalnızca resim olan, tıklanınca gerçek oynatıcıyı yükleyen gömme." } },
|
|
199
|
-
{ term: { en: "CWV", tr: "CWV" }, def: { en: "Core Web Vitals: LCP (largest content), CLS (shift), INP (interaction).", tr: "Core Web Vitals: LCP (en büyük içerik), CLS (kayma), INP (etkileşim)." } },
|
|
200
|
-
{ term: { en: "Soft 404", tr: "Sahte 404 (soft 404)" }, def: { en: "A page that says “page not found” but returns a 200 status code.", tr: "“Sayfa yok” diyen ama 200 durum kodu dönen sayfa." } },
|
|
201
|
-
{ term: { en: "Training / search / user bot", tr: "Eğitim / arama / kullanıcı botu" }, def: { en: "Bots that collect pages to train models, fill an assistant's search index, and open pages during a chat (ch. 6).", tr: "Model eğitmek için toplayan, asistanın arama dizinini dolduran ve sohbet sırasında sayfa açan botlar (06. bölüm)." } },
|
|
202
|
-
];
|
|
1
|
+
// Filled from katman.pro at run time for a paid key (see _store.js); the package carries no method text.
|
|
2
|
+
import { onMethod } from "./_store.js";
|
|
3
|
+
export let APPENDIX_A_NOTE = {};
|
|
4
|
+
export let APPENDIX_A_ROBOTS = {};
|
|
5
|
+
export let APPENDIX_B_LLMS = {};
|
|
6
|
+
export let APPENDIX_B_NOTE = {};
|
|
7
|
+
export let APPENDIX_C_NOTE = {};
|
|
8
|
+
export let APPENDIX_C_ORG = "";
|
|
9
|
+
export let CONTENT_RULES = [];
|
|
10
|
+
export let GLOSSARY = [];
|
|
11
|
+
export let SITE_AUDIT_CHECKLIST = {};
|
|
12
|
+
onMethod((d) => {
|
|
13
|
+
if (d.APPENDIX_A_NOTE !== undefined) APPENDIX_A_NOTE = d.APPENDIX_A_NOTE;
|
|
14
|
+
if (d.APPENDIX_A_ROBOTS !== undefined) APPENDIX_A_ROBOTS = d.APPENDIX_A_ROBOTS;
|
|
15
|
+
if (d.APPENDIX_B_LLMS !== undefined) APPENDIX_B_LLMS = d.APPENDIX_B_LLMS;
|
|
16
|
+
if (d.APPENDIX_B_NOTE !== undefined) APPENDIX_B_NOTE = d.APPENDIX_B_NOTE;
|
|
17
|
+
if (d.APPENDIX_C_NOTE !== undefined) APPENDIX_C_NOTE = d.APPENDIX_C_NOTE;
|
|
18
|
+
if (d.APPENDIX_C_ORG !== undefined) APPENDIX_C_ORG = d.APPENDIX_C_ORG;
|
|
19
|
+
if (d.CONTENT_RULES !== undefined) CONTENT_RULES = d.CONTENT_RULES;
|
|
20
|
+
if (d.GLOSSARY !== undefined) GLOSSARY = d.GLOSSARY;
|
|
21
|
+
if (d.SITE_AUDIT_CHECKLIST !== undefined) SITE_AUDIT_CHECKLIST = d.SITE_AUDIT_CHECKLIST;
|
|
22
|
+
});
|